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