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 1
    va_list ap;
48 1
    va_start(ap, format);
49 1
    verror(loc, format, ap);
50 1
    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 0
    va_list ap;
63 0
    va_start(ap, format);
64 0
    verror(loc, format, ap);
65 0
    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 1
    const loc = Loc(filename, linnum, charnum);
80 1
    va_list ap;
81 1
    va_start(ap, format);
82 1
    verror(loc, format, ap);
83 1
    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 1
    va_list ap;
97 1
    va_start(ap, format);
98 1
    verrorSupplemental(loc, format, ap);
99 1
    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 1
    va_list ap;
112 1
    va_start(ap, format);
113 1
    vwarning(loc, format, ap);
114 1
    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 1
    va_list ap;
128 1
    va_start(ap, format);
129 1
    vwarningSupplemental(loc, format, ap);
130 1
    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 1
    va_list ap;
144 1
    va_start(ap, format);
145 1
    vdeprecation(loc, format, ap);
146 1
    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 1
    va_list ap;
160 1
    va_start(ap, format);
161 1
    vdeprecationSupplemental(loc, format, ap);
162 1
    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 1
    va_list ap;
176 1
    va_start(ap, format);
177 1
    vmessage(loc, format, ap);
178 1
    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 1
    va_list ap;
190 1
    va_start(ap, format);
191 1
    vmessage(Loc.initial, format, ap);
192 1
    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 1
    va_list ap;
218 1
    va_start(ap, format);
219 1
    vtip(format, ap);
220 1
    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 1
    if (diagnosticHandler && diagnosticHandler(loc, headerColor, header, format, ap, p1, p2))
239 0
        return;
240

241 1
    if (global.params.showGaggedErrors && global.gag)
242 1
        fprintf(stderr, "(spec:%d) ", global.gag);
243 1
    Console* con = cast(Console*)global.console;
244 1
    const p = loc.toChars();
245 1
    if (con)
246 1
        con.setColorBright(true);
247 1
    if (*p)
248
    {
249 1
        fprintf(stderr, "%s: ", p);
250 1
        mem.xfree(cast(void*)p);
251
    }
252 1
    if (con)
253 1
        con.setColor(headerColor);
254 1
    fputs(header, stderr);
255 1
    if (con)
256 1
        con.resetColor();
257 1
    OutBuffer tmp;
258 1
    if (p1)
259
    {
260 1
        tmp.writestring(p1);
261 1
        tmp.writestring(" ");
262
    }
263 1
    if (p2)
264
    {
265 1
        tmp.writestring(p2);
266 1
        tmp.writestring(" ");
267
    }
268 1
    tmp.vprintf(format, ap);
269

270 1
    if (con && strchr(tmp.peekChars(), '`'))
271
    {
272 1
        colorSyntaxHighlight(tmp);
273 1
        writeHighlights(con, tmp);
274
    }
275
    else
276 1
        fputs(tmp.peekChars(), stderr);
277 1
    fputc('\n', stderr);
278

279 1
    if (global.params.printErrorContext &&
280
        // ignore invalid files
281 1
        loc != Loc.initial &&
282
        // ignore mixins for now
283 1
        !loc.filename.strstr(".d-mixin-") &&
284 1
        !global.params.mixinOut)
285
    {
286
        import dmd.filecache : FileCache;
287 1
        auto fllines = FileCache.fileCache.addOrGetFile(loc.filename.toDString());
288

289 1
        if (loc.linnum - 1 < fllines.lines.length)
290
        {
291 1
            auto line = fllines.lines[loc.linnum - 1];
292 1
            if (loc.charnum < line.length)
293
            {
294 1
                fprintf(stderr, "%.*s\n", cast(int)line.length, line.ptr);
295 1
                foreach (_; 1 .. loc.charnum)
296 1
                    fputc(' ', stderr);
297

298 1
                fputc('^', stderr);
299 1
                fputc('\n', stderr);
300
            }
301
        }
302
    }
303 1
    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 1
    global.errors++;
319 1
    if (!global.gag)
320
    {
321 1
        verrorPrint(loc, Classification.error, header, format, ap, p1, p2);
322 1
        if (global.params.errorLimit && global.errors >= global.params.errorLimit)
323 1
            fatal(); // moderate blizzard of cascading messages
324
    }
325
    else
326
    {
327 1
        if (global.params.showGaggedErrors)
328 1
            verrorPrint(loc, Classification.gagged, header, format, ap, p1, p2);
329 1
        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 1
    Color color;
343 1
    if (global.gag)
344
    {
345 1
        if (!global.params.showGaggedErrors)
346 1
            return;
347 0
        color = Classification.gagged;
348
    }
349
    else
350 1
        color = Classification.error;
351 1
    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 1
    if (global.params.warnings != DiagnosticReporting.off)
364
    {
365 1
        if (!global.gag)
366
        {
367 1
            verrorPrint(loc, Classification.warning, "Warning: ", format, ap);
368 1
            if (global.params.warnings == DiagnosticReporting.error)
369 1
                global.warnings++;
370
        }
371
        else
372
        {
373 0
            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 1
    if (global.params.warnings != DiagnosticReporting.off && !global.gag)
388 1
        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 1
    __gshared const(char)* header = "Deprecation: ";
403 1
    if (global.params.useDeprecated == DiagnosticReporting.error)
404 1
        verror(loc, format, ap, p1, p2, header);
405 1
    else if (global.params.useDeprecated == DiagnosticReporting.inform)
406
    {
407 1
        if (!global.gag)
408
        {
409 1
            verrorPrint(loc, Classification.deprecation, header, format, ap, p1, p2);
410
        }
411
        else
412
        {
413 1
            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 1
    const p = loc.toChars();
428 1
    if (*p)
429
    {
430 1
        fprintf(stdout, "%s: ", p);
431 1
        mem.xfree(cast(void*)p);
432
    }
433 1
    OutBuffer tmp;
434 1
    tmp.vprintf(format, ap);
435 1
    fputs(tmp.peekChars(), stdout);
436 1
    fputc('\n', stdout);
437 1
    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 1
    if (!global.gag)
449
    {
450 1
        Loc loc = Loc.init;
451 1
        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 1
    if (global.params.useDeprecated == DiagnosticReporting.error)
465 1
        verrorSupplemental(loc, format, ap);
466 0
    else if (global.params.useDeprecated == DiagnosticReporting.inform && !global.gag)
467 0
        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 1
    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 0
    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 1
    bool inBacktick = false;
504 1
    size_t iCodeStart = 0;
505 1
    size_t offset = 0;
506 1
    for (size_t i = offset; i < buf.length; ++i)
507
    {
508 1
        char c = buf[i];
509 1
        switch (c)
510
        {
511 1
            case '`':
512 1
                if (inBacktick)
513
                {
514 1
                    inBacktick = false;
515 1
                    OutBuffer codebuf;
516 1
                    codebuf.write(buf[iCodeStart + 1 .. i]);
517 1
                    codebuf.writeByte(0);
518
                    // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
519 1
                    colorHighlightCode(codebuf);
520 1
                    buf.remove(iCodeStart, i - iCodeStart + 1); // also trimming off the current `
521 1
                    immutable pre = "";
522 1
                    i = buf.insert(iCodeStart, pre);
523 1
                    i = buf.insert(i, codebuf[]);
524 1
                    i--; // point to the ending ) so when the for loop does i++, it will see the next character
525 1
                    break;
526
                }
527 1
                inBacktick = true;
528 1
                iCodeStart = i;
529 1
                break;
530

531 1
            default:
532 1
                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

564 1
    __gshared int nested;
565 1
    if (nested)
566
    {
567
        // Should never happen, but don't infinitely recurse if it does
568 0
        --nested;
569 0
        return;
570
    }
571 1
    ++nested;
572

573 1
    auto gaggedErrorsSave = global.startGagging();
574 1
    scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1);
575 1
    OutBuffer res;
576 1
    const(char)* lastp = cast(char*)buf[].ptr;
577
    //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf.data);
578 1
    res.reserve(buf.length);
579 1
    res.writeByte(HIGHLIGHT.Escape);
580 1
    res.writeByte(HIGHLIGHT.Other);
581 1
    while (1)
582
    {
583 1
        Token tok;
584 1
        lex.scan(&tok);
585 1
        res.writestring(lastp[0 .. tok.ptr - lastp]);
586 1
        HIGHLIGHT highlight;
587 1
        switch (tok.value)
588
        {
589 1
        case TOK.identifier:
590 1
            highlight = HIGHLIGHT.Identifier;
591 1
            break;
592 0
        case TOK.comment:
593 0
            highlight = HIGHLIGHT.Comment;
594 0
            break;
595 0
        case TOK.int32Literal:
596
            ..
597
        case TOK.dcharLiteral:
598 0
        case TOK.string_:
599 0
            highlight = HIGHLIGHT.Literal;
600 0
            break;
601 1
        default:
602 1
            if (tok.isKeyword())
603 0
                highlight = HIGHLIGHT.Keyword;
604 1
            break;
605
        }
606 1
        if (highlight != HIGHLIGHT.Default)
607
        {
608 1
            res.writeByte(HIGHLIGHT.Escape);
609 1
            res.writeByte(highlight);
610 1
            res.writestring(tok.ptr[0 .. lex.p - tok.ptr]);
611 1
            res.writeByte(HIGHLIGHT.Escape);
612 1
            res.writeByte(HIGHLIGHT.Other);
613
        }
614
        else
615 1
            res.writestring(tok.ptr[0 .. lex.p - tok.ptr]);
616 1
        if (tok.value == TOK.endOfFile)
617 1
            break;
618 1
        lastp = lex.p;
619
    }
620 1
    res.writeByte(HIGHLIGHT.Escape);
621 1
    res.writeByte(HIGHLIGHT.Default);
622
    //printf("res = '%.*s'\n", cast(int)buf.length, buf.data);
623 1
    buf.setsize(0);
624 1
    buf.write(&res);
625 1
    global.endGagging(gaggedErrorsSave);
626 1
    --nested;
627
}
628

629
/**
630
 * Write the buffer contents with embedded highlights to stderr.
631
 * Params:
632
 *      buf = highlighted text
633
 */
634
private void writeHighlights(Console* con, ref const OutBuffer buf)
635
{
636 1
    bool colors;
637
    scope (exit)
638
    {
639
        /* Do not mess up console if highlighting aborts
640
         */
641 1
        if (colors)
642 0
            con.resetColor();
643
    }
644

645 1
    for (size_t i = 0; i < buf.length; ++i)
646
    {
647 1
        const c = buf[i];
648 1
        if (c == HIGHLIGHT.Escape)
649
        {
650 1
            const color = buf[++i];
651 1
            if (color == HIGHLIGHT.Default)
652
            {
653 1
                con.resetColor();
654 1
                colors = false;
655
            }
656
            else
657 1
            if (color == Color.white)
658
            {
659 1
                con.resetColor();
660 1
                con.setColorBright(true);
661 1
                colors = true;
662
            }
663
            else
664
            {
665 1
                con.setColor(cast(Color)color);
666 1
                colors = true;
667
            }
668
        }
669
        else
670 1
            fputc(c, con.fp);
671
    }
672
}

Read our documentation on viewing source code .

Loading