| /* d-diagnostics.cc -- D frontend interface to gcc diagnostics. |
| Copyright (C) 2017-2022 Free Software Foundation, Inc. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| |
| #include "dmd/globals.h" |
| #include "dmd/errors.h" |
| |
| #include "tree.h" |
| #include "options.h" |
| #include "diagnostic.h" |
| |
| #include "d-tree.h" |
| |
| |
| /* Rewrite the format string FORMAT to deal with any format extensions not |
| supported by pp_format(). |
| |
| The following format specifiers are handled: |
| `...`: text within backticks gets quoted as '%<...%>'. |
| %-10s: left-justify format flag is removed leaving '%s' remaining. |
| %02x: zero-padding format flag is removed leaving '%x' remaining. |
| %X: uppercase unsigned hexadecimals are rewritten as '%x'. */ |
| |
| static char * |
| expand_d_format (const char *format) |
| { |
| obstack buf; |
| bool inbacktick = false; |
| |
| gcc_obstack_init (&buf); |
| |
| for (const char *p = format; *p;) |
| { |
| while (*p != '\0' && *p != '\\' && *p != '%' && *p != '`') |
| { |
| obstack_1grow (&buf, *p); |
| p++; |
| } |
| |
| if (*p == '\0') |
| break; |
| |
| if (*p == '\\') |
| { |
| if (p[1] == '`') |
| { |
| /* Escaped backtick, don't expand it as a quoted string. */ |
| obstack_1grow (&buf, '`'); |
| p++;; |
| } |
| else |
| obstack_1grow (&buf, *p); |
| |
| p++; |
| continue; |
| } |
| |
| if (*p == '`') |
| { |
| /* Text enclosed by `...` are translated as a quoted string. */ |
| if (inbacktick) |
| { |
| obstack_grow (&buf, "%>", 2); |
| inbacktick = false; |
| } |
| else |
| { |
| obstack_grow (&buf, "%<", 2); |
| inbacktick = true; |
| } |
| p++; |
| continue; |
| } |
| |
| /* Check the conversion specification for unhandled flags. */ |
| obstack_1grow (&buf, *p); |
| p++; |
| |
| Lagain: |
| switch (*p) |
| { |
| case '\0': |
| /* Malformed format string. */ |
| gcc_unreachable (); |
| |
| case '-': |
| /* Remove whitespace formatting. */ |
| p++; |
| while (ISDIGIT (*p)) |
| p++; |
| goto Lagain; |
| |
| case '0': |
| /* Remove zero padding from format string. */ |
| while (ISDIGIT (*p)) |
| p++; |
| goto Lagain; |
| |
| case 'X': |
| /* Hex format only supports lower-case. */ |
| obstack_1grow (&buf, 'x'); |
| p++; |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| gcc_assert (!inbacktick); |
| obstack_1grow (&buf, '\0'); |
| return (char *) obstack_finish (&buf); |
| } |
| |
| /* Rewrite the format string FORMAT to deal with any characters that require |
| escaping before expand_d_format expands it. */ |
| |
| static char * |
| escape_d_format (const char *format) |
| { |
| bool quoted = false; |
| size_t format_len = 0; |
| obstack buf; |
| |
| gcc_obstack_init (&buf); |
| |
| /* If the format string is enclosed by two '`' characters, then don't escape |
| the first and last characters. */ |
| if (*format == '`') |
| { |
| format_len = strlen (format) - 1; |
| if (format_len && format[format_len] == '`') |
| quoted = true; |
| } |
| |
| for (const char *p = format; *p; p++) |
| { |
| switch (*p) |
| { |
| case '%': |
| /* Escape `%' characters so that pp_format does not confuse them |
| for actual format specifiers. */ |
| obstack_1grow (&buf, '%'); |
| break; |
| |
| case '`': |
| /* Escape '`' characters so that expand_d_format does not confuse them |
| for a quoted string. */ |
| if (!quoted || (p != format && p != (format + format_len))) |
| obstack_1grow (&buf, '\\'); |
| break; |
| |
| default: |
| break; |
| } |
| |
| obstack_1grow (&buf, *p); |
| } |
| |
| obstack_1grow (&buf, '\0'); |
| return (char *) obstack_finish (&buf); |
| } |
| |
| /* Helper routine for all error routines. Reports a diagnostic specified by |
| KIND at the explicit location LOC. The message FORMAT comes from the dmd |
| front-end, which does not get translated by the gcc diagnostic routines. */ |
| |
| 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) |
| { |
| va_list argp; |
| va_copy (argp, ap); |
| |
| if (loc.filename || !verbatim) |
| { |
| rich_location rich_loc (line_table, make_location_t (loc)); |
| diagnostic_info diagnostic; |
| char *xformat = expand_d_format (format); |
| |
| diagnostic_set_info_translated (&diagnostic, xformat, &argp, |
| &rich_loc, kind); |
| if (opt != 0) |
| diagnostic.option_index = opt; |
| |
| diagnostic_report_diagnostic (global_dc, &diagnostic); |
| } |
| else |
| { |
| /* Write verbatim messages with no location direct to stream. */ |
| text_info text; |
| text.err_no = errno; |
| text.args_ptr = &argp; |
| text.format_spec = expand_d_format (format); |
| text.x_data = NULL; |
| |
| pp_format_verbatim (global_dc->printer, &text); |
| pp_newline_and_flush (global_dc->printer); |
| } |
| |
| va_end (argp); |
| } |
| |
| /* Print a hard error message with explicit location LOC with an optional |
| message prefix PREFIX1 and PREFIX2, increasing the global or gagged |
| error count. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| verror (const Loc &loc, const char *format, va_list ap, |
| const char *prefix1, const char *prefix2, const char *) |
| { |
| if (!global.gag || global.params.showGaggedErrors) |
| { |
| char *xformat; |
| |
| /* Build string and emit. */ |
| if (prefix2 != NULL) |
| xformat = xasprintf ("%s %s %s", escape_d_format (prefix1), |
| escape_d_format (prefix2), format); |
| else if (prefix1 != NULL) |
| xformat = xasprintf ("%s %s", escape_d_format (prefix1), format); |
| else |
| xformat = xasprintf ("%s", format); |
| |
| d_diagnostic_report_diagnostic (loc, 0, xformat, ap, |
| global.gag ? DK_ANACHRONISM : DK_ERROR, |
| false); |
| free (xformat); |
| } |
| |
| if (global.gag) |
| global.gaggedErrors++; |
| |
| global.errors++; |
| } |
| |
| /* Print supplementary message about the last error with explicit location LOC. |
| This doesn't increase the global error count. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| verrorSupplemental (const Loc &loc, const char *format, va_list ap) |
| { |
| if (global.gag && !global.params.showGaggedErrors) |
| return; |
| |
| d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false); |
| } |
| |
| /* Print a warning message with explicit location LOC, increasing the |
| global warning count. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| vwarning (const Loc &loc, const char *format, va_list ap) |
| { |
| if (!global.gag && global.params.warnings != DIAGNOSTICoff) |
| { |
| /* Warnings don't count if not treated as errors. */ |
| if (global.params.warnings == DIAGNOSTICerror) |
| global.warnings++; |
| |
| d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_WARNING, false); |
| } |
| else if (global.gag) |
| global.gaggedWarnings++; |
| } |
| |
| /* Print supplementary message about the last warning with explicit location |
| LOC. This doesn't increase the global warning count. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| vwarningSupplemental (const Loc &loc, const char *format, va_list ap) |
| { |
| if (global.params.warnings == DIAGNOSTICoff || global.gag) |
| return; |
| |
| d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false); |
| } |
| |
| /* Print a deprecation message with explicit location LOC with an optional |
| message prefix PREFIX1 and PREFIX2, increasing the global warning or |
| error count depending on how deprecations are treated. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| vdeprecation (const Loc &loc, const char *format, va_list ap, |
| const char *prefix1, const char *prefix2) |
| { |
| if (global.params.useDeprecated == DIAGNOSTICerror) |
| verror (loc, format, ap, prefix1, prefix2); |
| else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag) |
| { |
| char *xformat; |
| |
| /* Build string and emit. */ |
| if (prefix2 != NULL) |
| xformat = xasprintf ("%s %s %s", escape_d_format (prefix1), |
| escape_d_format (prefix2), format); |
| else if (prefix1 != NULL) |
| xformat = xasprintf ("%s %s", escape_d_format (prefix1), format); |
| else |
| xformat = xasprintf ("%s", format); |
| |
| d_diagnostic_report_diagnostic (loc, OPT_Wdeprecated, xformat, ap, |
| DK_WARNING, false); |
| free (xformat); |
| } |
| else if (global.gag) |
| global.gaggedWarnings++; |
| } |
| |
| /* Print supplementary message about the last deprecation with explicit |
| location LOC. This does not increase the global error count. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| vdeprecationSupplemental (const Loc &loc, const char *format, va_list ap) |
| { |
| if (global.params.useDeprecated == DIAGNOSTICerror) |
| verrorSupplemental (loc, format, ap); |
| else if (global.params.useDeprecated == DIAGNOSTICinform && !global.gag) |
| d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, false); |
| } |
| |
| /* Print a verbose message with explicit location LOC. */ |
| |
| void ATTRIBUTE_GCC_DIAG(2,0) |
| vmessage (const Loc &loc, const char *format, va_list ap) |
| { |
| d_diagnostic_report_diagnostic (loc, 0, format, ap, DK_NOTE, true); |
| } |
| |
| /* Print a tip message with prefix and highlighing. */ |
| |
| void ATTRIBUTE_GCC_DIAG(1,0) |
| vtip (const char *format, va_list ap) |
| { |
| if (!global.gag) |
| d_diagnostic_report_diagnostic (Loc (), 0, format, ap, DK_DEBUG, true); |
| } |
| |
| /* Call this after printing out fatal error messages to clean up and |
| exit the compiler. */ |
| |
| void |
| fatal (void) |
| { |
| exit (FATAL_EXIT_CODE); |
| } |