1 /** 2 * Functions for raising errors. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/errors.d, _errors.d) 8 * Documentation: https://dlang.org/phobos/dmd_errors.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/errors.d 10 */ 11 12 module dmd.errors; 13 14 import core.stdc.stdarg; 15 import core.stdc.stdio; 16 import core.stdc.stdlib; 17 import core.stdc..string; 18 import dmd.globals; 19 import dmd.root.outbuffer; 20 import dmd.root.rmem; 21 import dmd.root..string; 22 import dmd.console; 23 24 nothrow: 25 26 /** 27 * Color highlighting to classify messages 28 */ 29 enum Classification 30 { 31 error = Color.brightRed, /// for errors 32 gagged = Color.brightBlue, /// for gagged errors 33 warning = Color.brightYellow, /// for warnings 34 deprecation = Color.brightCyan, /// for deprecations 35 tip = Color.brightGreen, /// for tip messages 36 } 37 38 /** 39 * Print an error message, increasing the global error count. 40 * Params: 41 * loc = location of error 42 * format = printf-style format specification 43 * ... = printf-style variadic arguments 44 */ 45 extern (C++) void error(const ref Loc loc, const(char)* format, ...) 46 { 47 va_list ap; 48 va_start(ap, format); 49 verror(loc, format, ap); 50 va_end(ap); 51 } 52 53 /** 54 * Same as above, but allows Loc() literals to be passed. 55 * Params: 56 * loc = location of error 57 * format = printf-style format specification 58 * ... = printf-style variadic arguments 59 */ 60 extern (D) void error(Loc loc, const(char)* format, ...) 61 { 62 va_list ap; 63 va_start(ap, format); 64 verror(loc, format, ap); 65 va_end(ap); 66 } 67 68 /** 69 * Same as above, but takes a filename and line information arguments as separate parameters. 70 * Params: 71 * filename = source file of error 72 * linnum = line in the source file 73 * charnum = column number on the line 74 * format = printf-style format specification 75 * ... = printf-style variadic arguments 76 */ 77 extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...) 78 { 79 const loc = Loc(filename, linnum, charnum); 80 va_list ap; 81 va_start(ap, format); 82 verror(loc, format, ap); 83 va_end(ap); 84 } 85 86 /** 87 * Print additional details about an error message. 88 * Doesn't increase the error count or print an additional error prefix. 89 * Params: 90 * loc = location of error 91 * format = printf-style format specification 92 * ... = printf-style variadic arguments 93 */ 94 extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...) 95 { 96 va_list ap; 97 va_start(ap, format); 98 verrorSupplemental(loc, format, ap); 99 va_end(ap); 100 } 101 102 /** 103 * Print a warning message, increasing the global warning count. 104 * Params: 105 * loc = location of warning 106 * format = printf-style format specification 107 * ... = printf-style variadic arguments 108 */ 109 extern (C++) void warning(const ref Loc loc, const(char)* format, ...) 110 { 111 va_list ap; 112 va_start(ap, format); 113 vwarning(loc, format, ap); 114 va_end(ap); 115 } 116 117 /** 118 * Print additional details about a warning message. 119 * Doesn't increase the warning count or print an additional warning prefix. 120 * Params: 121 * loc = location of warning 122 * format = printf-style format specification 123 * ... = printf-style variadic arguments 124 */ 125 extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...) 126 { 127 va_list ap; 128 va_start(ap, format); 129 vwarningSupplemental(loc, format, ap); 130 va_end(ap); 131 } 132 133 /** 134 * Print a deprecation message, may increase the global warning or error count 135 * depending on whether deprecations are ignored. 136 * Params: 137 * loc = location of deprecation 138 * format = printf-style format specification 139 * ... = printf-style variadic arguments 140 */ 141 extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...) 142 { 143 va_list ap; 144 va_start(ap, format); 145 vdeprecation(loc, format, ap); 146 va_end(ap); 147 } 148 149 /** 150 * Print additional details about a deprecation message. 151 * Doesn't increase the error count, or print an additional deprecation prefix. 152 * Params: 153 * loc = location of deprecation 154 * format = printf-style format specification 155 * ... = printf-style variadic arguments 156 */ 157 extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...) 158 { 159 va_list ap; 160 va_start(ap, format); 161 vdeprecationSupplemental(loc, format, ap); 162 va_end(ap); 163 } 164 165 /** 166 * Print a verbose message. 167 * Doesn't prefix or highlight messages. 168 * Params: 169 * loc = location of message 170 * format = printf-style format specification 171 * ... = printf-style variadic arguments 172 */ 173 extern (C++) void message(const ref Loc loc, const(char)* format, ...) 174 { 175 va_list ap; 176 va_start(ap, format); 177 vmessage(loc, format, ap); 178 va_end(ap); 179 } 180 181 /** 182 * Same as above, but doesn't take a location argument. 183 * Params: 184 * format = printf-style format specification 185 * ... = printf-style variadic arguments 186 */ 187 extern (C++) void message(const(char)* format, ...) 188 { 189 va_list ap; 190 va_start(ap, format); 191 vmessage(Loc.initial, format, ap); 192 va_end(ap); 193 } 194 195 /** 196 * The type of the diagnostic handler 197 * see verrorPrint for arguments 198 * Returns: true if error handling is done, false to continue printing to stderr 199 */ 200 alias DiagnosticHandler = bool delegate(const ref Loc location, Color headerColor, const(char)* header, const(char)* messageFormat, va_list args, const(char)* prefix1, const(char)* prefix2); 201 202 /** 203 * The diagnostic handler. 204 * If non-null it will be called for every diagnostic message issued by the compiler. 205 * If it returns false, the message will be printed to stderr as usual. 206 */ 207 __gshared DiagnosticHandler diagnosticHandler; 208 209 /** 210 * Print a tip message with the prefix and highlighting. 211 * Params: 212 * format = printf-style format specification 213 * ... = printf-style variadic arguments 214 */ 215 extern (C++) void tip(const(char)* format, ...) 216 { 217 va_list ap; 218 va_start(ap, format); 219 vtip(format, ap); 220 va_end(ap); 221 } 222 223 /** 224 * Just print to stderr, doesn't care about gagging. 225 * (format,ap) text within backticks gets syntax highlighted. 226 * Params: 227 * loc = location of error 228 * headerColor = color to set `header` output to 229 * header = title of error message 230 * format = printf-style format specification 231 * ap = printf-style variadic arguments 232 * p1 = additional message prefix 233 * p2 = additional message prefix 234 */ 235 private void verrorPrint(const ref Loc loc, Color headerColor, const(char)* header, 236 const(char)* format, va_list ap, const(char)* p1 = null, const(char)* p2 = null) 237 { 238 if (diagnosticHandler && diagnosticHandler(loc, headerColor, header, format, ap, p1, p2)) 239 return; 240 241 if (global.params.showGaggedErrors && global.gag) 242 fprintf(stderr, "(spec:%d) ", global.gag); 243 Console* con = cast(Console*)global.console; 244 const p = loc.toChars(); 245 if (con) 246 con.setColorBright(true); 247 if (*p) 248 { 249 fprintf(stderr, "%s: ", p); 250 mem.xfree(cast(void*)p); 251 } 252 if (con) 253 con.setColor(headerColor); 254 fputs(header, stderr); 255 if (con) 256 con.resetColor(); 257 OutBuffer tmp; 258 if (p1) 259 { 260 tmp.writestring(p1); 261 tmp.writestring(" "); 262 } 263 if (p2) 264 { 265 tmp.writestring(p2); 266 tmp.writestring(" "); 267 } 268 tmp.vprintf(format, ap); 269 270 if (con && strchr(tmp.peekChars(), '`')) 271 { 272 colorSyntaxHighlight(tmp); 273 writeHighlights(con, tmp); 274 } 275 else 276 fputs(tmp.peekChars(), stderr); 277 fputc('\n', stderr); 278 279 if (global.params.printErrorContext && 280 // ignore invalid files 281 loc != Loc.initial && 282 // ignore mixins for now 283 !loc.filename.strstr(".d-mixin-") && 284 !global.params.mixinOut) 285 { 286 import dmd.filecache : FileCache; 287 auto fllines = FileCache.fileCache.addOrGetFile(loc.filename.toDString()); 288 289 if (loc.linnum - 1 < fllines.lines.length) 290 { 291 auto line = fllines.lines[loc.linnum - 1]; 292 if (loc.charnum < line.length) 293 { 294 fprintf(stderr, "%.*s\n", cast(int)line.length, line.ptr); 295 foreach (_; 1 .. loc.charnum) 296 fputc(' ', stderr); 297 298 fputc('^', stderr); 299 fputc('\n', stderr); 300 } 301 } 302 } 303 fflush(stderr); // ensure it gets written out in case of compiler aborts 304 } 305 306 /** 307 * Same as $(D error), but takes a va_list parameter, and optionally additional message prefixes. 308 * Params: 309 * loc = location of error 310 * format = printf-style format specification 311 * ap = printf-style variadic arguments 312 * p1 = additional message prefix 313 * p2 = additional message prefix 314 * header = title of error message 315 */ 316 extern (C++) void verror(const ref Loc loc, const(char)* format, va_list ap, const(char)* p1 = null, const(char)* p2 = null, const(char)* header = "Error: ") 317 { 318 global.errors++; 319 if (!global.gag) 320 { 321 verrorPrint(loc, Classification.error, header, format, ap, p1, p2); 322 if (global.params.errorLimit && global.errors >= global.params.errorLimit) 323 fatal(); // moderate blizzard of cascading messages 324 } 325 else 326 { 327 if (global.params.showGaggedErrors) 328 verrorPrint(loc, Classification.gagged, header, format, ap, p1, p2); 329 global.gaggedErrors++; 330 } 331 } 332 333 /** 334 * Same as $(D errorSupplemental), but takes a va_list parameter. 335 * Params: 336 * loc = location of error 337 * format = printf-style format specification 338 * ap = printf-style variadic arguments 339 */ 340 extern (C++) void verrorSupplemental(const ref Loc loc, const(char)* format, va_list ap) 341 { 342 Color color; 343 if (global.gag) 344 { 345 if (!global.params.showGaggedErrors) 346 return; 347 color = Classification.gagged; 348 } 349 else 350 color = Classification.error; 351 verrorPrint(loc, color, " ", format, ap); 352 } 353 354 /** 355 * Same as $(D warning), but takes a va_list parameter. 356 * Params: 357 * loc = location of warning 358 * format = printf-style format specification 359 * ap = printf-style variadic arguments 360 */ 361 extern (C++) void vwarning(const ref Loc loc, const(char)* format, va_list ap) 362 { 363 if (global.params.warnings != DiagnosticReporting.off) 364 { 365 if (!global.gag) 366 { 367 verrorPrint(loc, Classification.warning, "Warning: ", format, ap); 368 if (global.params.warnings == DiagnosticReporting.error) 369 global.warnings++; 370 } 371 else 372 { 373 global.gaggedWarnings++; 374 } 375 } 376 } 377 378 /** 379 * Same as $(D warningSupplemental), but takes a va_list parameter. 380 * Params: 381 * loc = location of warning 382 * format = printf-style format specification 383 * ap = printf-style variadic arguments 384 */ 385 extern (C++) void vwarningSupplemental(const ref Loc loc, const(char)* format, va_list ap) 386 { 387 if (global.params.warnings != DiagnosticReporting.off && !global.gag) 388 verrorPrint(loc, Classification.warning, " ", format, ap); 389 } 390 391 /** 392 * Same as $(D deprecation), but takes a va_list parameter, and optionally additional message prefixes. 393 * Params: 394 * loc = location of deprecation 395 * format = printf-style format specification 396 * ap = printf-style variadic arguments 397 * p1 = additional message prefix 398 * p2 = additional message prefix 399 */ 400 extern (C++) void vdeprecation(const ref Loc loc, const(char)* format, va_list ap, const(char)* p1 = null, const(char)* p2 = null) 401 { 402 __gshared const(char)* header = "Deprecation: "; 403 if (global.params.useDeprecated == DiagnosticReporting.error) 404 verror(loc, format, ap, p1, p2, header); 405 else if (global.params.useDeprecated == DiagnosticReporting.inform) 406 { 407 if (!global.gag) 408 { 409 verrorPrint(loc, Classification.deprecation, header, format, ap, p1, p2); 410 } 411 else 412 { 413 global.gaggedWarnings++; 414 } 415 } 416 } 417 418 /** 419 * Same as $(D message), but takes a va_list parameter. 420 * Params: 421 * loc = location of message 422 * format = printf-style format specification 423 * ap = printf-style variadic arguments 424 */ 425 extern (C++) void vmessage(const ref Loc loc, const(char)* format, va_list ap) 426 { 427 const p = loc.toChars(); 428 if (*p) 429 { 430 fprintf(stdout, "%s: ", p); 431 mem.xfree(cast(void*)p); 432 } 433 OutBuffer tmp; 434 tmp.vprintf(format, ap); 435 fputs(tmp.peekChars(), stdout); 436 fputc('\n', stdout); 437 fflush(stdout); // ensure it gets written out in case of compiler aborts 438 } 439 440 /** 441 * Same as $(D tip), but takes a va_list parameter. 442 * Params: 443 * format = printf-style format specification 444 * ap = printf-style variadic arguments 445 */ 446 extern (C++) void vtip(const(char)* format, va_list ap) 447 { 448 if (!global.gag) 449 { 450 Loc loc = Loc.init; 451 verrorPrint(loc, Classification.tip, " Tip: ", format, ap); 452 } 453 } 454 455 /** 456 * Same as $(D deprecationSupplemental), but takes a va_list parameter. 457 * Params: 458 * loc = location of deprecation 459 * format = printf-style format specification 460 * ap = printf-style variadic arguments 461 */ 462 extern (C++) void vdeprecationSupplemental(const ref Loc loc, const(char)* format, va_list ap) 463 { 464 if (global.params.useDeprecated == DiagnosticReporting.error) 465 verrorSupplemental(loc, format, ap); 466 else if (global.params.useDeprecated == DiagnosticReporting.inform && !global.gag) 467 verrorPrint(loc, Classification.deprecation, " ", format, ap); 468 } 469 470 /** 471 * Call this after printing out fatal error messages to clean up and exit 472 * the compiler. 473 */ 474 extern (C++) void fatal() 475 { 476 version (none) 477 { 478 halt(); 479 } 480 exit(EXIT_FAILURE); 481 } 482 483 /** 484 * Try to stop forgetting to remove the breakpoints from 485 * release builds. 486 */ 487 extern (C++) void halt() 488 { 489 assert(0); 490 } 491 492 /** 493 * Scan characters in `buf`. Assume text enclosed by `...` 494 * is D source code, and color syntax highlight it. 495 * Modify contents of `buf` with highlighted result. 496 * Many parallels to ddoc.highlightText(). 497 * Params: 498 * buf = text containing `...` code to highlight 499 */ 500 private void colorSyntaxHighlight(ref OutBuffer buf) 501 { 502 //printf("colorSyntaxHighlight('%.*s')\n", cast(int)buf.length, buf.data); 503 bool inBacktick = false; 504 size_t iCodeStart = 0; 505 size_t offset = 0; 506 for (size_t i = offset; i < buf.length; ++i) 507 { 508 char c = buf[i]; 509 switch (c) 510 { 511 case '`': 512 if (inBacktick) 513 { 514 inBacktick = false; 515 OutBuffer codebuf; 516 codebuf.write(buf[iCodeStart + 1 .. i]); 517 codebuf.writeByte(0); 518 // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL 519 colorHighlightCode(codebuf); 520 buf.remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current ` 521 immutable pre = ""; 522 i = buf.insert(iCodeStart, pre); 523 i = buf.insert(i, codebuf[]); 524 i--; // point to the ending ) so when the for loop does i++, it will see the next character 525 break; 526 } 527 inBacktick = true; 528 iCodeStart = i; 529 break; 530 531 default: 532 break; 533 } 534 } 535 } 536 537 538 /** 539 * Embed these highlighting commands in the text stream. 540 * HIGHLIGHT.Escape indicates a Color follows. 541 */ 542 enum HIGHLIGHT : ubyte 543 { 544 Default = Color.black, // back to whatever the console is set at 545 Escape = '\xFF', // highlight Color follows 546 Identifier = Color.white, 547 Keyword = Color.white, 548 Literal = Color.white, 549 Comment = Color.darkGray, 550 Other = Color.cyan, // other tokens 551 } 552 553 /** 554 * Highlight code for CODE section. 555 * Rewrite the contents of `buf` with embedded highlights. 556 * Analogous to doc.highlightCode2() 557 */ 558 559 private void colorHighlightCode(ref OutBuffer buf) 560 { 561 import dmd.lexer; 562 import dmd.tokens; 563 import dmd.diagnostic : DefaultDiagnosticHandler; 564 565 __gshared int nested; 566 if (nested) 567 { 568 // Should never happen, but don't infinitely recurse if it does 569 --nested; 570 return; 571 } 572 ++nested; 573 574 auto gaggedErrorsSave = global.startGagging(); 575 auto diagnosticHandler = DefaultDiagnosticHandler(); 576 scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, diagnosticHandler.diagnosticHandler); 577 OutBuffer res; 578 const(char)* lastp = cast(char*)buf[].ptr; 579 //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf.data); 580 res.reserve(buf.length); 581 res.writeByte(HIGHLIGHT.Escape); 582 res.writeByte(HIGHLIGHT.Other); 583 while (1) 584 { 585 Token tok; 586 lex.scan(&tok); 587 diagnosticHandler.report(); 588 res.writestring(lastp[0 .. tok.ptr - lastp]); 589 HIGHLIGHT highlight; 590 switch (tok.value) 591 { 592 case TOK.identifier: 593 highlight = HIGHLIGHT.Identifier; 594 break; 595 case TOK.comment: 596 highlight = HIGHLIGHT.Comment; 597 break; 598 case TOK.int32Literal: 599 .. 600 case TOK.dcharLiteral: 601 case TOK.string_: 602 highlight = HIGHLIGHT.Literal; 603 break; 604 default: 605 if (tok.isKeyword()) 606 highlight = HIGHLIGHT.Keyword; 607 break; 608 } 609 if (highlight != HIGHLIGHT.Default) 610 { 611 res.writeByte(HIGHLIGHT.Escape); 612 res.writeByte(highlight); 613 res.writestring(tok.ptr[0 .. lex.p - tok.ptr]); 614 res.writeByte(HIGHLIGHT.Escape); 615 res.writeByte(HIGHLIGHT.Other); 616 } 617 else 618 res.writestring(tok.ptr[0 .. lex.p - tok.ptr]); 619 if (tok.value == TOK.endOfFile) 620 break; 621 lastp = lex.p; 622 } 623 res.writeByte(HIGHLIGHT.Escape); 624 res.writeByte(HIGHLIGHT.Default); 625 //printf("res = '%.*s'\n", cast(int)buf.length, buf.data); 626 buf.setsize(0); 627 buf.write(&res); 628 global.endGagging(gaggedErrorsSave); 629 --nested; 630 } 631 632 /** 633 * Write the buffer contents with embedded highlights to stderr. 634 * Params: 635 * buf = highlighted text 636 */ 637 private void writeHighlights(Console* con, ref const OutBuffer buf) 638 { 639 bool colors; 640 scope (exit) 641 { 642 /* Do not mess up console if highlighting aborts 643 */ 644 if (colors) 645 con.resetColor(); 646 } 647 648 for (size_t i = 0; i < buf.length; ++i) 649 { 650 const c = buf[i]; 651 if (c == HIGHLIGHT.Escape) 652 { 653 const color = buf[++i]; 654 if (color == HIGHLIGHT.Default) 655 { 656 con.resetColor(); 657 colors = false; 658 } 659 else 660 if (color == Color.white) 661 { 662 con.resetColor(); 663 con.setColorBright(true); 664 colors = true; 665 } 666 else 667 { 668 con.setColor(cast(Color)color); 669 colors = true; 670 } 671 } 672 else 673 fputc(c, con.fp); 674 } 675 }