1/* d-diagnostics.cc -- D frontend interface to gcc diagnostics.
2   Copyright (C) 2017-2020 Free Software Foundation, Inc.
3
4GCC is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 3, or (at your option)
7any later version.
8
9GCC is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along 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
43static char *
44expand_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
134static char *
135escape_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
172static void ATTRIBUTE_GCC_DIAG(3,0)
173d_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
213void ATTRIBUTE_GCC_DIAG(2,3)
214error (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
222void ATTRIBUTE_GCC_DIAG(2,0)
223verror (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
254void ATTRIBUTE_GCC_DIAG(2,3)
255errorSupplemental (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
263void ATTRIBUTE_GCC_DIAG(2,0)
264verrorSupplemental (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
275void ATTRIBUTE_GCC_DIAG(2,3)
276warning (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
284void ATTRIBUTE_GCC_DIAG(2,0)
285vwarning (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
300void ATTRIBUTE_GCC_DIAG(2,3)
301warningSupplemental (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
309void ATTRIBUTE_GCC_DIAG(2,0)
310vwarningSupplemental (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
322void ATTRIBUTE_GCC_DIAG(2,3)
323deprecation (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
331void ATTRIBUTE_GCC_DIAG(2,0)
332vdeprecation (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
359void ATTRIBUTE_GCC_DIAG(2,3)
360deprecationSupplemental (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
368void ATTRIBUTE_GCC_DIAG(2,0)
369vdeprecationSupplemental (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
379void ATTRIBUTE_GCC_DIAG(2, 3)
380message (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
388void ATTRIBUTE_GCC_DIAG(2,0)
389vmessage (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
396void ATTRIBUTE_GCC_DIAG(1, 2)
397message (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
408void
409fatal (void)
410{
411  exit (FATAL_EXIT_CODE);
412}
413