Home | History | Annotate | Line # | Download | only in d
d-diagnostic.cc revision 1.1.1.2
      1 /* d-diagnostics.cc -- D frontend interface to gcc diagnostics.
      2    Copyright (C) 2017-2020 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    The result should be freed by the caller.  */
     42 
     43 static char *
     44 expand_d_format (const char *format)
     45 {
     46   OutBuffer buf;
     47   bool inbacktick = false;
     48 
     49   for (const char *p = format; *p;)
     50     {
     51       while (*p != '\0' && *p != '\\' && *p != '%' && *p != '`')
     52 	{
     53 	  buf.writeByte (*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 	      buf.writeByte ('`');
     66 	      p++;;
     67 	    }
     68 	  else
     69 	    buf.writeByte (*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 	      buf.writestring ("%>");
     81 	      inbacktick = false;
     82 	    }
     83 	  else
     84 	    {
     85 	      buf.writestring ("%<");
     86 	      inbacktick = true;
     87 	    }
     88 	  p++;
     89 	  continue;
     90 	}
     91 
     92       /* Check the conversion specification for unhandled flags.  */
     93       buf.writeByte (*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 	  buf.writeByte ('x');
    119 	  p++;
    120 	  break;
    121 
    122 	default:
    123 	  break;
    124 	}
    125     }
    126 
    127   gcc_assert (!inbacktick);
    128   return buf.extractString ();
    129 }
    130 
    131 /* Rewrite the format string FORMAT to deal with any characters that require
    132    escaping before expand_d_format expands it.  */
    133 
    134 static char *
    135 escape_d_format (const char *format)
    136 {
    137   obstack buf;
    138 
    139   gcc_obstack_init (&buf);
    140 
    141   for (const char *p = format; *p; p++)
    142     {
    143       switch (*p)
    144 	{
    145 	case '%':
    146 	  /* Escape `%' characters so that pp_format does not confuse them
    147 	     for actual format specifiers.  */
    148 	  obstack_1grow (&buf, '%');
    149 	  break;
    150 
    151 	case '`':
    152 	  /* Escape '`' characters so that expand_d_format does not confuse them
    153 	     for a quoted string.  */
    154 	  obstack_1grow (&buf, '\\');
    155 	  break;
    156 
    157 	default:
    158 	  break;
    159 	}
    160 
    161       obstack_1grow (&buf, *p);
    162     }
    163 
    164   obstack_1grow (&buf, '\0');
    165   return (char *) obstack_finish (&buf);
    166 }
    167 
    168 /* Helper routine for all error routines.  Reports a diagnostic specified by
    169    KIND at the explicit location LOC.  The message FORMAT comes from the dmd
    170    front-end, which does not get translated by the gcc diagnostic routines.  */
    171 
    172 static void ATTRIBUTE_GCC_DIAG(3,0)
    173 d_diagnostic_report_diagnostic (const Loc& loc, int opt, const char *format,
    174 				va_list ap, diagnostic_t kind, bool verbatim)
    175 {
    176   va_list argp;
    177   va_copy (argp, ap);
    178 
    179   if (loc.filename || !verbatim)
    180     {
    181       rich_location rich_loc (line_table, make_location_t (loc));
    182       diagnostic_info diagnostic;
    183       char *xformat = expand_d_format (format);
    184 
    185       diagnostic_set_info_translated (&diagnostic, xformat, &argp,
    186 				      &rich_loc, kind);
    187       if (opt != 0)
    188 	diagnostic.option_index = opt;
    189 
    190       diagnostic_report_diagnostic (global_dc, &diagnostic);
    191       free (xformat);
    192     }
    193   else
    194     {
    195       /* Write verbatim messages with no location direct to stream.  */
    196       text_info text;
    197       text.err_no = errno;
    198       text.args_ptr = &argp;
    199       text.format_spec = expand_d_format (format);
    200       text.x_data = NULL;
    201 
    202       pp_format_verbatim (global_dc->printer, &text);
    203       pp_newline_and_flush (global_dc->printer);
    204     }
    205 
    206   va_end (argp);
    207 }
    208 
    209 /* Print a hard error message with explicit location LOC with an optional
    210    message prefix PREFIX1 and PREFIX2, increasing the global or gagged
    211    error count.  */
    212 
    213 void ATTRIBUTE_GCC_DIAG(2,3)
    214 error (const Loc& loc, const char *format, ...)
    215 {
    216   va_list ap;
    217   va_start (ap, format);
    218   verror (loc, format, ap);
    219   va_end (ap);
    220 }
    221 
    222 void ATTRIBUTE_GCC_DIAG(2,0)
    223 verror (const Loc& loc, const char *format, va_list ap,
    224 	const char *prefix1, const char *prefix2, const char *)
    225 {
    226   if (!global.gag || global.params.showGaggedErrors)
    227     {
    228       char *xformat;
    229 
    230       /* Build string and emit.  */
    231       if (prefix2 != NULL)
    232 	xformat = xasprintf ("%s %s %s", escape_d_format (prefix1),
    233 			     escape_d_format (prefix2), format);
    234       else if (prefix1 != NULL)
    235 	xformat = xasprintf ("%s %s", escape_d_format (prefix1), format);
    236       else
    237 	xformat = xasprintf ("%s", format);
    238 
    239       d_diagnostic_report_diagnostic (loc, 0, xformat, ap,
    240 				      global.gag ? DK_ANACHRONISM : DK_ERROR,
    241 				      false);
    242       free (xformat);
    243     }
    244 
    245   if (global.gag)
    246     global.gaggedErrors++;
    247 
    248   global.errors++;
    249 }
    250 
    251 /* Print supplementary message about the last error with explicit location LOC.
    252    This doesn't increase the global error count.  */
    253 
    254 void ATTRIBUTE_GCC_DIAG(2,3)
    255 errorSupplemental (const Loc& loc, const char *format, ...)
    256 {
    257   va_list ap;
    258   va_start (ap, format);
    259   verrorSupplemental (loc, format, ap);
    260   va_end (ap);
    261 }
    262 
    263 void ATTRIBUTE_GCC_DIAG(2,0)
    264 verrorSupplemental (const Loc& loc, const char *format, va_list ap)
    265 {
    266   if (global.gag && !global.params.showGaggedErrors)
    267     return;
    268 
    269   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    270 }
    271 
    272 /* Print a warning message with explicit location LOC, increasing the
    273    global warning count.  */
    274 
    275 void ATTRIBUTE_GCC_DIAG(2,3)
    276 warning (const Loc& loc, const char *format, ...)
    277 {
    278   va_list ap;
    279   va_start (ap, format);
    280   vwarning (loc, format, ap);
    281   va_end (ap);
    282 }
    283 
    284 void ATTRIBUTE_GCC_DIAG(2,0)
    285 vwarning (const Loc& loc, const char *format, va_list ap)
    286 {
    287   if (!global.gag && global.params.warnings != DIAGNOSTICoff)
    288     {
    289       /* Warnings don't count if not treated as errors.  */
    290       if (global.params.warnings == DIAGNOSTICerror)
    291 	global.warnings++;
    292 
    293       d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_WARNING, false);
    294     }
    295 }
    296 
    297 /* Print supplementary message about the last warning with explicit location
    298    LOC.  This doesn't increase the global warning count.  */
    299 
    300 void ATTRIBUTE_GCC_DIAG(2,3)
    301 warningSupplemental (const Loc& loc, const char *format, ...)
    302 {
    303   va_list ap;
    304   va_start (ap, format);
    305   vwarningSupplemental (loc, format, ap);
    306   va_end (ap);
    307 }
    308 
    309 void ATTRIBUTE_GCC_DIAG(2,0)
    310 vwarningSupplemental (const Loc& loc, const char *format, va_list ap)
    311 {
    312   if (global.params.warnings == DIAGNOSTICoff || global.gag)
    313     return;
    314 
    315   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    316 }
    317 
    318 /* Print a deprecation message with explicit location LOC with an optional
    319    message prefix PREFIX1 and PREFIX2, increasing the global warning or
    320    error count depending on how deprecations are treated.  */
    321 
    322 void ATTRIBUTE_GCC_DIAG(2,3)
    323 deprecation (const Loc& loc, const char *format, ...)
    324 {
    325   va_list ap;
    326   va_start (ap, format);
    327   vdeprecation (loc, format, ap);
    328   va_end (ap);
    329 }
    330 
    331 void ATTRIBUTE_GCC_DIAG(2,0)
    332 vdeprecation (const Loc& loc, const char *format, va_list ap,
    333 	      const char *prefix1, const char *prefix2)
    334 {
    335   if (global.params.useDeprecated == DIAGNOSTICerror)
    336     verror (loc, format, ap, prefix1, prefix2);
    337   else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag)
    338     {
    339       char *xformat;
    340 
    341       /* Build string and emit.  */
    342       if (prefix2 != NULL)
    343 	xformat = xasprintf ("%s %s %s", escape_d_format (prefix1),
    344 			     escape_d_format (prefix2), format);
    345       else if (prefix1 != NULL)
    346 	xformat = xasprintf ("%s %s", escape_d_format (prefix1), format);
    347       else
    348 	xformat = xasprintf ("%s", format);
    349 
    350       d_diagnostic_report_diagnostic (loc, OPT_Wdeprecated, xformat, ap,
    351 				      DK_WARNING, false);
    352       free (xformat);
    353     }
    354 }
    355 
    356 /* Print supplementary message about the last deprecation with explicit
    357    location LOC.  This does not increase the global error count.  */
    358 
    359 void ATTRIBUTE_GCC_DIAG(2,3)
    360 deprecationSupplemental (const Loc& loc, const char *format, ...)
    361 {
    362   va_list ap;
    363   va_start (ap, format);
    364   vdeprecationSupplemental (loc, format, ap);
    365   va_end (ap);
    366 }
    367 
    368 void ATTRIBUTE_GCC_DIAG(2,0)
    369 vdeprecationSupplemental (const Loc& loc, const char *format, va_list ap)
    370 {
    371   if (global.params.useDeprecated == DIAGNOSTICerror)
    372     verrorSupplemental (loc, format, ap);
    373   else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag)
    374     d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false);
    375 }
    376 
    377 /* Print a verbose message with explicit location LOC.  */
    378 
    379 void ATTRIBUTE_GCC_DIAG(2, 3)
    380 message (const Loc& loc, const char *format, ...)
    381 {
    382   va_list ap;
    383   va_start (ap, format);
    384   vmessage (loc, format, ap);
    385   va_end (ap);
    386 }
    387 
    388 void ATTRIBUTE_GCC_DIAG(2,0)
    389 vmessage (const Loc& loc, const char *format, va_list ap)
    390 {
    391   d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, true);
    392 }
    393 
    394 /* Same as above, but doesn't take a location argument.  */
    395 
    396 void ATTRIBUTE_GCC_DIAG(1, 2)
    397 message (const char *format, ...)
    398 {
    399   va_list ap;
    400   va_start (ap, format);
    401   vmessage (Loc (), format, ap);
    402   va_end (ap);
    403 }
    404 
    405 /* Call this after printing out fatal error messages to clean up and
    406    exit the compiler.  */
    407 
    408 void
    409 fatal (void)
    410 {
    411   exit (FATAL_EXIT_CODE);
    412 }
    413