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