1 /**
2  * Functions for raising errors.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://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 public import core.stdc.stdarg;
15 import core.stdc.stdio;
16 import core.stdc.stdlib;
17 import core.stdc.string;
18 import dmd.errorsink;
19 import dmd.globals;
20 import dmd.location;
21 import dmd.common.outbuffer;
22 import dmd.root.rmem;
23 import dmd.root.string;
24 import dmd.console;
25 
26 nothrow:
27 
28 /// Constants used to discriminate kinds of error messages.
29 enum ErrorKind
30 {
31     warning,
32     deprecation,
33     error,
34     tip,
35     message,
36 }
37 
38 /***************************
39  * Error message sink for D compiler.
40  */
41 class ErrorSinkCompiler : ErrorSink
42 {
43   nothrow:
44   extern (C++):
45   override:
46 
47     void error(const ref Loc loc, const(char)* format, ...)
48     {
49         va_list ap;
50         va_start(ap, format);
51         verrorReport(loc, format, ap, ErrorKind.error);
52         va_end(ap);
53     }
54 
55     void errorSupplemental(const ref Loc loc, const(char)* format, ...)
56     {
57         va_list ap;
58         va_start(ap, format);
59         verrorReportSupplemental(loc, format, ap, ErrorKind.error);
60         va_end(ap);
61     }
62 
63     void warning(const ref Loc loc, const(char)* format, ...)
64     {
65         va_list ap;
66         va_start(ap, format);
67         verrorReport(loc, format, ap, ErrorKind.warning);
68         va_end(ap);
69     }
70 
71     void warningSupplemental(const ref Loc loc, const(char)* format, ...)
72     {
73         va_list ap;
74         va_start(ap, format);
75         verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
76         va_end(ap);
77     }
78 
79     void deprecation(const ref Loc loc, const(char)* format, ...)
80     {
81         va_list ap;
82         va_start(ap, format);
83         verrorReport(loc, format, ap, ErrorKind.deprecation);
84         va_end(ap);
85     }
86 
87     void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
88     {
89         va_list ap;
90         va_start(ap, format);
91         verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
92         va_end(ap);
93     }
94 
95     void message(const ref Loc loc, const(char)* format, ...)
96     {
97         va_list ap;
98         va_start(ap, format);
99         verrorReport(loc, format, ap, ErrorKind.message);
100         va_end(ap);
101     }
102 }
103 
104 
105 /**
106  * Color highlighting to classify messages
107  */
108 enum Classification : Color
109 {
110     error = Color.brightRed,          /// for errors
111     gagged = Color.brightBlue,        /// for gagged errors
112     warning = Color.brightYellow,     /// for warnings
113     deprecation = Color.brightCyan,   /// for deprecations
114     tip = Color.brightGreen,          /// for tip messages
115 }
116 
117 
118 static if (__VERSION__ < 2092)
119     private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {}
120 else
121     pragma(printf) private extern (C++) void noop(const ref Loc loc, const(char)* format, ...) {}
122 
123 
124 package auto previewErrorFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow
125 {
126     with (FeatureState) final switch (featureState)
127     {
128         case enabled:
129             return &error;
130 
131         case disabled:
132             return &noop;
133 
134         case default_:
135             return isDeprecated ? &noop : &deprecation;
136     }
137 }
138 
139 package auto previewSupplementalFunc(bool isDeprecated, FeatureState featureState) @safe @nogc pure nothrow
140 {
141     with (FeatureState) final switch (featureState)
142     {
143         case enabled:
144             return &errorSupplemental;
145 
146         case disabled:
147             return &noop;
148 
149         case default_:
150             return isDeprecated ? &noop : &deprecationSupplemental;
151     }
152 }
153 
154 
155 /**
156  * Print an error message, increasing the global error count.
157  * Params:
158  *      loc    = location of error
159  *      format = printf-style format specification
160  *      ...    = printf-style variadic arguments
161  */
162 static if (__VERSION__ < 2092)
163     extern (C++) void error(const ref Loc loc, const(char)* format, ...)
164     {
165         va_list ap;
166         va_start(ap, format);
167         verrorReport(loc, format, ap, ErrorKind.error);
168         va_end(ap);
169     }
170 else
171     pragma(printf) extern (C++) void error(const ref Loc loc, const(char)* format, ...)
172     {
173         va_list ap;
174         va_start(ap, format);
175         verrorReport(loc, format, ap, ErrorKind.error);
176         va_end(ap);
177     }
178 
179 /**
180  * Same as above, but takes a filename and line information arguments as separate parameters.
181  * Params:
182  *      filename = source file of error
183  *      linnum   = line in the source file
184  *      charnum  = column number on the line
185  *      format   = printf-style format specification
186  *      ...      = printf-style variadic arguments
187  */
188 static if (__VERSION__ < 2092)
189     extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...)
190     {
191         const loc = Loc(filename, linnum, charnum);
192         va_list ap;
193         va_start(ap, format);
194         verrorReport(loc, format, ap, ErrorKind.error);
195         va_end(ap);
196     }
197 else
198     pragma(printf) extern (C++) void error(const(char)* filename, uint linnum, uint charnum, const(char)* format, ...)
199     {
200         const loc = Loc(filename, linnum, charnum);
201         va_list ap;
202         va_start(ap, format);
203         verrorReport(loc, format, ap, ErrorKind.error);
204         va_end(ap);
205     }
206 
207 /**
208  * Print additional details about an error message.
209  * Doesn't increase the error count or print an additional error prefix.
210  * Params:
211  *      loc    = location of error
212  *      format = printf-style format specification
213  *      ...    = printf-style variadic arguments
214  */
215 static if (__VERSION__ < 2092)
216     extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...)
217     {
218         va_list ap;
219         va_start(ap, format);
220         verrorReportSupplemental(loc, format, ap, ErrorKind.error);
221         va_end(ap);
222     }
223 else
224     pragma(printf) extern (C++) void errorSupplemental(const ref Loc loc, const(char)* format, ...)
225     {
226         va_list ap;
227         va_start(ap, format);
228         verrorReportSupplemental(loc, format, ap, ErrorKind.error);
229         va_end(ap);
230     }
231 
232 /**
233  * Print a warning message, increasing the global warning count.
234  * Params:
235  *      loc    = location of warning
236  *      format = printf-style format specification
237  *      ...    = printf-style variadic arguments
238  */
239 static if (__VERSION__ < 2092)
240     extern (C++) void warning(const ref Loc loc, const(char)* format, ...)
241     {
242         va_list ap;
243         va_start(ap, format);
244         verrorReport(loc, format, ap, ErrorKind.warning);
245         va_end(ap);
246     }
247 else
248     pragma(printf) extern (C++) void warning(const ref Loc loc, const(char)* format, ...)
249     {
250         va_list ap;
251         va_start(ap, format);
252         verrorReport(loc, format, ap, ErrorKind.warning);
253         va_end(ap);
254     }
255 
256 /**
257  * Print additional details about a warning message.
258  * Doesn't increase the warning count or print an additional warning prefix.
259  * Params:
260  *      loc    = location of warning
261  *      format = printf-style format specification
262  *      ...    = printf-style variadic arguments
263  */
264 static if (__VERSION__ < 2092)
265     extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...)
266     {
267         va_list ap;
268         va_start(ap, format);
269         verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
270         va_end(ap);
271     }
272 else
273     pragma(printf) extern (C++) void warningSupplemental(const ref Loc loc, const(char)* format, ...)
274     {
275         va_list ap;
276         va_start(ap, format);
277         verrorReportSupplemental(loc, format, ap, ErrorKind.warning);
278         va_end(ap);
279     }
280 
281 /**
282  * Print a deprecation message, may increase the global warning or error count
283  * depending on whether deprecations are ignored.
284  * Params:
285  *      loc    = location of deprecation
286  *      format = printf-style format specification
287  *      ...    = printf-style variadic arguments
288  */
289 static if (__VERSION__ < 2092)
290     extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...)
291     {
292         va_list ap;
293         va_start(ap, format);
294         verrorReport(loc, format, ap, ErrorKind.deprecation);
295         va_end(ap);
296     }
297 else
298     pragma(printf) extern (C++) void deprecation(const ref Loc loc, const(char)* format, ...)
299     {
300         va_list ap;
301         va_start(ap, format);
302         verrorReport(loc, format, ap, ErrorKind.deprecation);
303         va_end(ap);
304     }
305 
306 /**
307  * Print additional details about a deprecation message.
308  * Doesn't increase the error count, or print an additional deprecation prefix.
309  * Params:
310  *      loc    = location of deprecation
311  *      format = printf-style format specification
312  *      ...    = printf-style variadic arguments
313  */
314 static if (__VERSION__ < 2092)
315     extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
316     {
317         va_list ap;
318         va_start(ap, format);
319         verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
320         va_end(ap);
321     }
322 else
323     pragma(printf) extern (C++) void deprecationSupplemental(const ref Loc loc, const(char)* format, ...)
324     {
325         va_list ap;
326         va_start(ap, format);
327         verrorReportSupplemental(loc, format, ap, ErrorKind.deprecation);
328         va_end(ap);
329     }
330 
331 /**
332  * Print a verbose message.
333  * Doesn't prefix or highlight messages.
334  * Params:
335  *      loc    = location of message
336  *      format = printf-style format specification
337  *      ...    = printf-style variadic arguments
338  */
339 static if (__VERSION__ < 2092)
340     extern (C++) void message(const ref Loc loc, const(char)* format, ...)
341     {
342         va_list ap;
343         va_start(ap, format);
344         verrorReport(loc, format, ap, ErrorKind.message);
345         va_end(ap);
346     }
347 else
348     pragma(printf) extern (C++) void message(const ref Loc loc, const(char)* format, ...)
349     {
350         va_list ap;
351         va_start(ap, format);
352         verrorReport(loc, format, ap, ErrorKind.message);
353         va_end(ap);
354     }
355 
356 /**
357  * Same as above, but doesn't take a location argument.
358  * Params:
359  *      format = printf-style format specification
360  *      ...    = printf-style variadic arguments
361  */
362 static if (__VERSION__ < 2092)
363     extern (C++) void message(const(char)* format, ...)
364     {
365         va_list ap;
366         va_start(ap, format);
367         verrorReport(Loc.initial, format, ap, ErrorKind.message);
368         va_end(ap);
369     }
370 else
371     pragma(printf) extern (C++) void message(const(char)* format, ...)
372     {
373         va_list ap;
374         va_start(ap, format);
375         verrorReport(Loc.initial, format, ap, ErrorKind.message);
376         va_end(ap);
377     }
378 
379 /**
380  * The type of the diagnostic handler
381  * see verrorReport for arguments
382  * Returns: true if error handling is done, false to continue printing to stderr
383  */
384 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);
385 
386 /**
387  * The diagnostic handler.
388  * If non-null it will be called for every diagnostic message issued by the compiler.
389  * If it returns false, the message will be printed to stderr as usual.
390  */
391 __gshared DiagnosticHandler diagnosticHandler;
392 
393 /**
394  * Print a tip message with the prefix and highlighting.
395  * Params:
396  *      format = printf-style format specification
397  *      ...    = printf-style variadic arguments
398  */
399 static if (__VERSION__ < 2092)
400     extern (C++) void tip(const(char)* format, ...)
401     {
402         va_list ap;
403         va_start(ap, format);
404         verrorReport(Loc.initial, format, ap, ErrorKind.tip);
405         va_end(ap);
406     }
407 else
408     pragma(printf) extern (C++) void tip(const(char)* format, ...)
409     {
410         va_list ap;
411         va_start(ap, format);
412         verrorReport(Loc.initial, format, ap, ErrorKind.tip);
413         va_end(ap);
414     }
415 
416 
417 // Encapsulates an error as described by its location, format message, and kind.
418 private struct ErrorInfo
419 {
420     this(const ref Loc loc, const ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null) @safe @nogc pure nothrow
421     {
422         this.loc = loc;
423         this.p1 = p1;
424         this.p2 = p2;
425         this.kind = kind;
426     }
427 
428     const Loc loc;              // location of error
429     Classification headerColor; // color to set `header` output to
430     const(char)* p1;            // additional message prefix
431     const(char)* p2;            // additional message prefix
432     const ErrorKind kind;       // kind of error being printed
433     bool supplemental;          // true if supplemental error
434 }
435 
436 /**
437  * Implements $(D error), $(D warning), $(D deprecation), $(D message), and
438  * $(D tip). Report a diagnostic error, taking a va_list parameter, and
439  * optionally additional message prefixes. Whether the message gets printed
440  * depends on runtime values of DiagnosticReporting and global gagging.
441  * Params:
442  *      loc         = location of error
443  *      format      = printf-style format specification
444  *      ap          = printf-style variadic arguments
445  *      kind        = kind of error being printed
446  *      p1          = additional message prefix
447  *      p2          = additional message prefix
448  */
449 extern (C++) void verrorReport(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind, const(char)* p1 = null, const(char)* p2 = null)
450 {
451     auto info = ErrorInfo(loc, kind, p1, p2);
452     final switch (info.kind)
453     {
454     case ErrorKind.error:
455         global.errors++;
456         if (!global.gag)
457         {
458             info.headerColor = Classification.error;
459             verrorPrint(format, ap, info);
460             if (global.params.v.errorLimit && global.errors >= global.params.v.errorLimit)
461                 fatal(); // moderate blizzard of cascading messages
462         }
463         else
464         {
465             if (global.params.v.showGaggedErrors)
466             {
467                 info.headerColor = Classification.gagged;
468                 verrorPrint(format, ap, info);
469             }
470             global.gaggedErrors++;
471         }
472         break;
473 
474     case ErrorKind.deprecation:
475         if (global.params.useDeprecated == DiagnosticReporting.error)
476             goto case ErrorKind.error;
477         else if (global.params.useDeprecated == DiagnosticReporting.inform)
478         {
479             if (!global.gag)
480             {
481                 info.headerColor = Classification.deprecation;
482                 verrorPrint(format, ap, info);
483             }
484             else
485             {
486                 global.gaggedWarnings++;
487             }
488         }
489         break;
490 
491     case ErrorKind.warning:
492         if (global.params.warnings != DiagnosticReporting.off)
493         {
494             if (!global.gag)
495             {
496                 info.headerColor = Classification.warning;
497                 verrorPrint(format, ap, info);
498                 if (global.params.warnings == DiagnosticReporting.error)
499                     global.warnings++;
500             }
501             else
502             {
503                 global.gaggedWarnings++;
504             }
505         }
506         break;
507 
508     case ErrorKind.tip:
509         if (!global.gag)
510         {
511             info.headerColor = Classification.tip;
512             verrorPrint(format, ap, info);
513         }
514         break;
515 
516     case ErrorKind.message:
517         const p = info.loc.toChars();
518         if (*p)
519         {
520             fprintf(stdout, "%s: ", p);
521             mem.xfree(cast(void*)p);
522         }
523         OutBuffer tmp;
524         tmp.vprintf(format, ap);
525         fputs(tmp.peekChars(), stdout);
526         fputc('\n', stdout);
527         fflush(stdout);     // ensure it gets written out in case of compiler aborts
528         break;
529     }
530 }
531 
532 /**
533  * Implements $(D errorSupplemental), $(D warningSupplemental), and
534  * $(D deprecationSupplemental). Report an addition diagnostic error, taking a
535  * va_list parameter. Whether the message gets printed depends on runtime
536  * values of DiagnosticReporting and global gagging.
537  * Params:
538  *      loc         = location of error
539  *      format      = printf-style format specification
540  *      ap          = printf-style variadic arguments
541  *      kind        = kind of error being printed
542  */
543 extern (C++) void verrorReportSupplemental(const ref Loc loc, const(char)* format, va_list ap, ErrorKind kind)
544 {
545     auto info = ErrorInfo(loc, kind);
546     info.supplemental = true;
547     switch (info.kind)
548     {
549     case ErrorKind.error:
550         if (global.gag)
551         {
552             if (!global.params.v.showGaggedErrors)
553                 return;
554             info.headerColor = Classification.gagged;
555         }
556         else
557             info.headerColor = Classification.error;
558         verrorPrint(format, ap, info);
559         break;
560 
561     case ErrorKind.deprecation:
562         if (global.params.useDeprecated == DiagnosticReporting.error)
563             goto case ErrorKind.error;
564         else if (global.params.useDeprecated == DiagnosticReporting.inform && !global.gag)
565         {
566             info.headerColor = Classification.deprecation;
567             verrorPrint(format, ap, info);
568         }
569         break;
570 
571     case ErrorKind.warning:
572         if (global.params.warnings != DiagnosticReporting.off && !global.gag)
573         {
574             info.headerColor = Classification.warning;
575             verrorPrint(format, ap, info);
576         }
577         break;
578 
579     default:
580         assert(false, "internal error: unhandled kind in error report");
581     }
582 }
583 
584 /**
585  * Just print to stderr, doesn't care about gagging.
586  * (format,ap) text within backticks gets syntax highlighted.
587  * Params:
588  *      format  = printf-style format specification
589  *      ap      = printf-style variadic arguments
590  *      info    = context of error
591  */
592 private void verrorPrint(const(char)* format, va_list ap, ref ErrorInfo info)
593 {
594     const(char)* header;    // title of error message
595     if (info.supplemental)
596         header = "       ";
597     else
598     {
599         final switch (info.kind)
600         {
601             case ErrorKind.error:       header = "Error: "; break;
602             case ErrorKind.deprecation: header = "Deprecation: "; break;
603             case ErrorKind.warning:     header = "Warning: "; break;
604             case ErrorKind.tip:         header = "  Tip: "; break;
605             case ErrorKind.message:     assert(0);
606         }
607     }
608 
609     if (diagnosticHandler !is null &&
610         diagnosticHandler(info.loc, info.headerColor, header, format, ap, info.p1, info.p2))
611         return;
612 
613     if (global.params.v.showGaggedErrors && global.gag)
614         fprintf(stderr, "(spec:%d) ", global.gag);
615     Console con = cast(Console) global.console;
616     const p = info.loc.toChars();
617     if (con)
618         con.setColorBright(true);
619     if (*p)
620     {
621         fprintf(stderr, "%s: ", p);
622         mem.xfree(cast(void*)p);
623     }
624     if (con)
625         con.setColor(info.headerColor);
626     fputs(header, stderr);
627     if (con)
628         con.resetColor();
629     OutBuffer tmp;
630     if (info.p1)
631     {
632         tmp.writestring(info.p1);
633         tmp.writestring(" ");
634     }
635     if (info.p2)
636     {
637         tmp.writestring(info.p2);
638         tmp.writestring(" ");
639     }
640     tmp.vprintf(format, ap);
641 
642     if (con && strchr(tmp.peekChars(), '`'))
643     {
644         colorSyntaxHighlight(tmp);
645         writeHighlights(con, tmp);
646     }
647     else
648         fputs(tmp.peekChars(), stderr);
649     fputc('\n', stderr);
650 
651     __gshared Loc old_loc;
652     Loc loc = info.loc;
653     if (global.params.v.printErrorContext &&
654         // ignore supplemental messages with same loc
655         (loc != old_loc || !info.supplemental) &&
656         // ignore invalid files
657         loc != Loc.initial &&
658         // ignore mixins for now
659         !loc.filename.strstr(".d-mixin-") &&
660         !global.params.mixinOut.doOutput)
661     {
662         import dmd.root.filename : FileName;
663         const fileName = FileName(loc.filename.toDString);
664         if (auto file = global.fileManager.lookup(fileName))
665         {
666             const(char)[][] lines = global.fileManager.getLines(fileName);
667             if (loc.linnum - 1 < lines.length)
668             {
669                 auto line = lines[loc.linnum - 1];
670                 if (loc.charnum < line.length)
671                 {
672                     fprintf(stderr, "%.*s\n", cast(int)line.length, line.ptr);
673                     // The number of column bytes and the number of display columns
674                     // occupied by a character are not the same for non-ASCII charaters.
675                     // https://issues.dlang.org/show_bug.cgi?id=21849
676                     size_t c = 0;
677                     while (c < loc.charnum - 1)
678                     {
679                         import dmd.root.utf : utf_decodeChar;
680                         dchar u;
681                         const msg = utf_decodeChar(line, c, u);
682                         assert(msg is null, msg);
683                         fputc(' ', stderr);
684                     }
685                     fputc('^', stderr);
686                     fputc('\n', stderr);
687                 }
688             }
689         }
690     }
691     old_loc = loc;
692     fflush(stderr);     // ensure it gets written out in case of compiler aborts
693 }
694 
695 /**
696  * The type of the fatal error handler
697  * Returns: true if error handling is done, false to do exit(EXIT_FAILURE)
698  */
699 alias FatalErrorHandler = bool delegate();
700 
701 /**
702  * The fatal error handler.
703  * If non-null it will be called for every fatal() call issued by the compiler.
704  */
705 __gshared FatalErrorHandler fatalErrorHandler;
706 
707 /**
708  * Call this after printing out fatal error messages to clean up and exit the
709  * compiler. You can also set a fatalErrorHandler to override this behaviour.
710  */
711 extern (C++) void fatal()
712 {
713     if (fatalErrorHandler && fatalErrorHandler())
714         return;
715 
716     exit(EXIT_FAILURE);
717 }
718 
719 /**
720  * Try to stop forgetting to remove the breakpoints from
721  * release builds.
722  */
723 extern (C++) void halt() @safe
724 {
725     assert(0);
726 }
727 
728 /**
729  * Scan characters in `buf`. Assume text enclosed by `...`
730  * is D source code, and color syntax highlight it.
731  * Modify contents of `buf` with highlighted result.
732  * Many parallels to ddoc.highlightText().
733  * Params:
734  *      buf = text containing `...` code to highlight
735  */
736 private void colorSyntaxHighlight(ref OutBuffer buf)
737 {
738     //printf("colorSyntaxHighlight('%.*s')\n", cast(int)buf.length, buf[].ptr);
739     bool inBacktick = false;
740     size_t iCodeStart = 0;
741     size_t offset = 0;
742     for (size_t i = offset; i < buf.length; ++i)
743     {
744         char c = buf[i];
745         switch (c)
746         {
747             case '`':
748                 if (inBacktick)
749                 {
750                     inBacktick = false;
751                     OutBuffer codebuf;
752                     codebuf.write(buf[iCodeStart .. i]);
753                     codebuf.writeByte(0);
754                     // escape the contents, but do not perform highlighting except for DDOC_PSYMBOL
755                     colorHighlightCode(codebuf);
756                     buf.remove(iCodeStart, i - iCodeStart);
757                     immutable pre = "";
758                     i = buf.insert(iCodeStart, pre);
759                     i = buf.insert(i, codebuf[]);
760                     break;
761                 }
762                 inBacktick = true;
763                 iCodeStart = i + 1;
764                 break;
765 
766             default:
767                 break;
768         }
769     }
770 }
771 
772 
773 /**
774  * Embed these highlighting commands in the text stream.
775  * HIGHLIGHT.Escape indicates a Color follows.
776  */
777 enum HIGHLIGHT : ubyte
778 {
779     Default    = Color.black,           // back to whatever the console is set at
780     Escape     = '\xFF',                // highlight Color follows
781     Identifier = Color.white,
782     Keyword    = Color.white,
783     Literal    = Color.white,
784     Comment    = Color.darkGray,
785     Other      = Color.cyan,           // other tokens
786 }
787 
788 /**
789  * Highlight code for CODE section.
790  * Rewrite the contents of `buf` with embedded highlights.
791  * Analogous to doc.highlightCode2()
792  */
793 
794 private void colorHighlightCode(ref OutBuffer buf)
795 {
796     import dmd.lexer;
797     import dmd.tokens;
798 
799     __gshared int nested;
800     if (nested)
801     {
802         // Should never happen, but don't infinitely recurse if it does
803         --nested;
804         return;
805     }
806     ++nested;
807 
808     scope Lexer lex = new Lexer(null, cast(char*)buf[].ptr, 0, buf.length - 1, 0, 1, global.errorSinkNull, &global.compileEnv);
809     OutBuffer res;
810     const(char)* lastp = cast(char*)buf[].ptr;
811     //printf("colorHighlightCode('%.*s')\n", cast(int)(buf.length - 1), buf[].ptr);
812     res.reserve(buf.length);
813     res.writeByte(HIGHLIGHT.Escape);
814     res.writeByte(HIGHLIGHT.Other);
815     while (1)
816     {
817         Token tok;
818         lex.scan(&tok);
819         res.writestring(lastp[0 .. tok.ptr - lastp]);
820         HIGHLIGHT highlight;
821         switch (tok.value)
822         {
823         case TOK.identifier:
824             highlight = HIGHLIGHT.Identifier;
825             break;
826         case TOK.comment:
827             highlight = HIGHLIGHT.Comment;
828             break;
829         case TOK.int32Literal:
830             ..
831         case TOK.dcharLiteral:
832         case TOK.string_:
833             highlight = HIGHLIGHT.Literal;
834             break;
835         default:
836             if (tok.isKeyword())
837                 highlight = HIGHLIGHT.Keyword;
838             break;
839         }
840         if (highlight != HIGHLIGHT.Default)
841         {
842             res.writeByte(HIGHLIGHT.Escape);
843             res.writeByte(highlight);
844             res.writestring(tok.ptr[0 .. lex.p - tok.ptr]);
845             res.writeByte(HIGHLIGHT.Escape);
846             res.writeByte(HIGHLIGHT.Other);
847         }
848         else
849             res.writestring(tok.ptr[0 .. lex.p - tok.ptr]);
850         if (tok.value == TOK.endOfFile)
851             break;
852         lastp = lex.p;
853     }
854     res.writeByte(HIGHLIGHT.Escape);
855     res.writeByte(HIGHLIGHT.Default);
856     //printf("res = '%.*s'\n", cast(int)buf.length, buf[].ptr);
857     buf.setsize(0);
858     buf.write(&res);
859     --nested;
860 }
861 
862 /**
863  * Write the buffer contents with embedded highlights to stderr.
864  * Params:
865  *      buf = highlighted text
866  */
867 private void writeHighlights(Console con, ref const OutBuffer buf)
868 {
869     bool colors;
870     scope (exit)
871     {
872         /* Do not mess up console if highlighting aborts
873          */
874         if (colors)
875             con.resetColor();
876     }
877 
878     for (size_t i = 0; i < buf.length; ++i)
879     {
880         const c = buf[i];
881         if (c == HIGHLIGHT.Escape)
882         {
883             const color = buf[++i];
884             if (color == HIGHLIGHT.Default)
885             {
886                 con.resetColor();
887                 colors = false;
888             }
889             else
890             if (color == Color.white)
891             {
892                 con.resetColor();
893                 con.setColorBright(true);
894                 colors = true;
895             }
896             else
897             {
898                 con.setColor(cast(Color)color);
899                 colors = true;
900             }
901         }
902         else
903             fputc(c, con.fp);
904     }
905 }