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