xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/d-diagnostic.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
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 *
expand_d_format(const char * format)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 *
escape_d_format(const char * format)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)
d_diagnostic_report_diagnostic(const Loc & loc,int opt,const char * format,va_list ap,diagnostic_t kind,bool verbatim)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)
verror(const Loc & loc,const char * format,va_list ap,const char * prefix1,const char * prefix2,const char *)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)
verrorSupplemental(const Loc & loc,const char * format,va_list ap)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)
vwarning(const Loc & loc,const char * format,va_list ap)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)
vwarningSupplemental(const Loc & loc,const char * format,va_list ap)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)
vdeprecation(const Loc & loc,const char * format,va_list ap,const char * prefix1,const char * prefix2)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)
vdeprecationSupplemental(const Loc & loc,const char * format,va_list ap)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)
vmessage(const Loc & loc,const char * format,va_list ap)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)
vtip(const char * format,va_list ap)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
fatal(void)360 fatal (void)
361 {
362   exit (FATAL_EXIT_CODE);
363 }
364