Home | History | Annotate | Line # | Download | only in d
d-diagnostic.cc revision 1.1.1.3
      1 /* d-diagnostics.cc -- D frontend interface to gcc diagnostics.
      2    Copyright (C) 2017-2022 Free Software Foundation, Inc.
      3 
      4 GCC is free software; you can redistribute it and/or modify
      5 it under the terms of the GNU General Public License as published by
      6 the Free Software Foundation; either version 3, or (at your option)
      7 any later version.
      8 
      9 GCC is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12 GNU General Public License for more details.
     13 
     14 You should have received a copy of the GNU General Public License
     15 along with GCC; see the file COPYING3.  If not see
     16 <http://www.gnu.org/licenses/>.  */
     17 
     18 #include "config.h"
     19 #include "system.h"
     20 #include "coretypes.h"
     21 
     22 #include "dmd/globals.h"
     23 #include "dmd/errors.h"
     24 
     25 #include "tree.h"
     26 #include "options.h"
     27 #include "diagnostic.h"
     28 
     29 #include "d-tree.h"
     30 
     31 
     32 /* Rewrite the format string FORMAT to deal with any format extensions not
     33    supported by pp_format().
     34 
     35    The following format specifiers are handled:
     36    `...`: text within backticks gets quoted as '%<...%>'.
     37    %-10s: left-justify format flag is removed leaving '%s' remaining.
     38    %02x: zero-padding format flag is removed leaving '%x' remaining.
     39    %X: uppercase unsigned hexadecimals are rewritten as '%x'.  */
     40 
     41 static char *
     42 expand_d_format (const char *format)
     43 {
     44   obstack buf;
     45   bool inbacktick = false;
     46 
     47   gcc_obstack_init (&buf);
     48 
     49   for (const char *p = format; *p;)
     50     {
     51       while (*p != '\0' && *p != '\\' && *p != '%' && *p != '`')
     52 	{
     53 	  obstack_1grow (&buf, *p);
     54 	  p++;
     55 	}
     56 
     57       if (*p == '\0')
     58 	break;
     59 
     60       if (*p == '\\')
     61 	{
     62 	  if (p[1] == '`')
     63 	    {
     64 	      /* Escaped backtick, don't expand it as a quoted string.  */
     65 	      obstack_1grow (&buf, '`');
     66 	      p++;;
     67 	    }
     68 	  else
     69 	    obstack_1grow (&buf, *p);
     70 
     71 	  p++;
     72 	  continue;
     73 	}
     74 
     75       if (*p == '`')
     76 	{
     77 	  /* Text enclosed by `...` are translated as a quoted string.  */
     78 	  if (inbacktick)
     79 	    {
     80 	      obstack_grow (&buf, "%>", 2);
     81 	      inbacktick = false;
     82 	    }
     83 	  else
     84 	    {
     85 	      obstack_grow (&buf, "%<", 2);
     86 	      inbacktick = true;
     87 	    }
     88 	  p++;
     89 	  continue;
     90 	}
     91 
     92       /* Check the conversion specification for unhandled flags.  */
     93       obstack_1grow (&buf, *p);
     94       p++;
     95 
     96     Lagain:
     97       switch (*p)
     98 	{
     99 	case '\0':
    100 	  /* Malformed format string.  */
    101 	  gcc_unreachable ();
    102 
    103 	case '-':
    104 	  /* Remove whitespace formatting.  */
    105 	  p++;
    106 	  while (ISDIGIT (*p))
    107 	    p++;
    108 	  goto Lagain;
    109 
    110 	case '0':
    111 	  /* Remove zero padding from format string.  */
    112 	  while (ISDIGIT (*p))
    113 	    p++;
    114 	  goto Lagain;
    115 
    116 	case 'X':
    117 	  /* Hex format only supports lower-case.  */
    118 	  obstack_1grow (&buf, 'x');
    119 	  p++;
    120 	  break;
    121 
    122 	default:
    123 	  break;
    124 	}
    125     }
    126 
    127   gcc_assert (!inbacktick);
    128   obstack_1grow (&buf, '\0');
    129   return (char *) obstack_finish (&buf);
    130 }
    131 
    132 /* Rewrite the format string FORMAT to deal with any characters that require
    133    escaping before expand_d_format expands it.  */
    134 
    135 static char *
    136 escape_d_format (const char *format)
    137 {
    138   bool quoted = false;
    139   size_t format_len = 0;
    140   obstack buf;
    141 
    142   gcc_obstack_init (&buf);
    143 
    144   /* If the format string is enclosed by two '`' characters, then don't escape
    145      the first and last characters.  */
    146   if (*format == '`')
    147     {
    148       format_len = strlen (format) - 1;
    149       if (format_len && format[format_len] == '`')
    150 	quoted = true;
    151     }
    152 
    153   for (const char *p = format; *p; p++)
    154     {
    155       switch (*p)
    156 	{
    157 	case '%':
    158 	  /* Escape `%' characters so that pp_format does not confuse them
    159 	     for actual format specifiers.  */
    160 	  obstack_1grow (&buf, '%');
    161 	  break;
    162 
    163 	case '`':
    164 	  /* Escape '`' characters so that expand_d_format does not confuse them
    165 	     for a quoted string.  */
    166 	  if (!quoted || (p != format && p != (format + format_len)))
    167 	    obstack_1grow (&buf, '\\');
    168 	  break;
    169 
    170 	default:
    171 	  break;
    172 	}
    173 
    174       obstack_1grow (&buf, *p);
    175     }
    176 
    177   obstack_1grow (&buf, '\0');
    178   return (char *) obstack_finish (&buf);
    179 }
    180 
    181 /* Helper routine for all error routines.  Reports a diagnostic specified by
    182    KIND at the explicit location LOC.  The message FORMAT comes from the dmd
    183    front-end, which does not get translated by the gcc diagnostic routines.  */
    184 
    185 static void ATTRIBUTE_GCC_DIAG(3,0)
    186 d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format,
    187 				va_list ap, diagnostic_t kind, bool verbatim)
    188 {
    189   va_list argp;
    190   va_copy (argp, ap);
    191 
    192   if (loc.filename || !verbatim)
    193     {
    194       rich_location rich_loc (line_table, make_location_t (loc));
    195       diagnostic_info diagnostic;
    196       char *xformat = expand_d_format (format);
    197 
    198       diagnostic_set_info_translated (&diagnostic, xformat, &argp,
    199 				      &rich_loc, kind);
    200       if (opt != 0)
    201 	diagnostic.option_index = opt;
    202 
    203       diagnostic_report_diagnostic (global_dc, &diagnostic);
    204     }
    205   else
    206     {
    207       /* Write verbatim messages with no location direct to stream.  */
    208       text_info text;
    209       text.err_no = errno;
    210       text.args_ptr = &argp;
    211       text.format_spec = expand_d_format (format);
    212       text.x_data = NULL;
    213 
    214       pp_format_verbatim (global_dc->printer, &text);
    215       pp_newline_and_flush (global_dc->printer);
    216     }
    217 
    218   va_end (argp);
    219 }
    220 
    221 /* Print a hard error message with explicit location LOC with an optional
    222    message prefix PREFIX1 and PREFIX2, increasing the global or gagged
    223    error count.  */
    224 
    225 void ATTRIBUTE_GCC_DIAG(2,0)
    226 verror (const Loc &loc, const char *format, va_list ap,
    227 	const char *prefix1, const char *prefix2, const char *)
    228 {
    229   if (!global.gag || global.params.showGaggedErrors)
    230     {
    231       char *xformat;
    232 
    233       /* Build string and emit.  */
    234       if (prefix2 != NULL)
    235 	xformat = xasprintf ("%s %s %s", escape_d_format (prefix1),
    236 			     escape_d_format (prefix2), format);
    237       else if (prefix1 != NULL)
    238 	xformat = xasprintf ("%s %s", escape_d_format (prefix1), format);
    239       else
    240 	xformat = xasprintf ("%s", format);
    241 
    242       d_diagnostic_report_diagnostic (loc, 0, xformat, ap,
    243 				      global.gag ? DK_ANACHRONISM : DK_ERROR,
    244 				      false);
    245       free (xformat);
    246     }
    247 
    248   if (global.gag)
    249     global.gaggedErrors++;
    250 
    251   global.errors++;
    252 }
    253 
    254 /* Print supplementary message about the last error with explicit location LOC.
    255    This doesn't increase the global error count.  */
    256 
    257 void ATTRIBUTE_GCC_DIAG(2,0)
    258 verrorSupplemental (const Loc &loc, const char *format, va_list ap)
    259 {
    260   if (global.gag && !global.params.showGaggedErrors)
    261     return;
    262 
    263   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    264 }
    265 
    266 /* Print a warning message with explicit location LOC, increasing the
    267    global warning count.  */
    268 
    269 void ATTRIBUTE_GCC_DIAG(2,0)
    270 vwarning (const Loc &loc, const char *format, va_list ap)
    271 {
    272   if (!global.gag && global.params.warnings != DIAGNOSTICoff)
    273     {
    274       /* Warnings don't count if not treated as errors.  */
    275       if (global.params.warnings == DIAGNOSTICerror)
    276 	global.warnings++;
    277 
    278       d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_WARNING, false);
    279     }
    280   else if (global.gag)
    281     global.gaggedWarnings++;
    282 }
    283 
    284 /* Print supplementary message about the last warning with explicit location
    285    LOC.  This doesn't increase the global warning count.  */
    286 
    287 void ATTRIBUTE_GCC_DIAG(2,0)
    288 vwarningSupplemental (const Loc &loc, const char *format, va_list ap)
    289 {
    290   if (global.params.warnings == DIAGNOSTICoff || global.gag)
    291     return;
    292 
    293   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    294 }
    295 
    296 /* Print a deprecation message with explicit location LOC with an optional
    297    message prefix PREFIX1 and PREFIX2, increasing the global warning or
    298    error count depending on how deprecations are treated.  */
    299 
    300 void ATTRIBUTE_GCC_DIAG(2,0)
    301 vdeprecation (const Loc &loc, const char *format, va_list ap,
    302 	      const char *prefix1, const char *prefix2)
    303 {
    304   if (global.params.useDeprecated == DIAGNOSTICerror)
    305     verror (loc, format, ap, prefix1, prefix2);
    306   else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag)
    307     {
    308       char *xformat;
    309 
    310       /* Build string and emit.  */
    311       if (prefix2 != NULL)
    312 	xformat = xasprintf ("%s %s %s", escape_d_format (prefix1),
    313 			     escape_d_format (prefix2), format);
    314       else if (prefix1 != NULL)
    315 	xformat = xasprintf ("%s %s", escape_d_format (prefix1), format);
    316       else
    317 	xformat = xasprintf ("%s", format);
    318 
    319       d_diagnostic_report_diagnostic (loc, OPT_Wdeprecated, xformat, ap,
    320 				      DK_WARNING, false);
    321       free (xformat);
    322     }
    323   else if (global.gag)
    324     global.gaggedWarnings++;
    325 }
    326 
    327 /* Print supplementary message about the last deprecation with explicit
    328    location LOC.  This does not increase the global error count.  */
    329 
    330 void ATTRIBUTE_GCC_DIAG(2,0)
    331 vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap)
    332 {
    333   if (global.params.useDeprecated == DIAGNOSTICerror)
    334     verrorSupplemental (loc, format, ap);
    335   else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag)
    336     d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    337 }
    338 
    339 /* Print a verbose message with explicit location LOC.  */
    340 
    341 void ATTRIBUTE_GCC_DIAG(2,0)
    342 vmessage (const Loc &loc, const char *format, va_list ap)
    343 {
    344   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, true);
    345 }
    346 
    347 /* Print a tip message with prefix and highlighing.  */
    348 
    349 void ATTRIBUTE_GCC_DIAG(1,0)
    350 vtip (const char *format, va_list ap)
    351 {
    352   if (!global.gag)
    353     d_diagnostic_report_diagnostic (Loc (), 0, format, ap, DK_DEBUG, true);
    354 }
    355 
    356 /* Call this after printing out fatal error messages to clean up and
    357    exit the compiler.  */
    358 
    359 void
    360 fatal (void)
    361 {
    362   exit (FATAL_EXIT_CODE);
    363 }
    364