]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/diagnostic-show-locus.c
PR 62314: add ability to add fixit-hints to a diagnostic
[thirdparty/gcc.git] / gcc / diagnostic-show-locus.c
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2015 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30
31 #ifdef HAVE_TERMIOS_H
32 # include <termios.h>
33 #endif
34
35 #ifdef GWINSZ_IN_SYS_IOCTL
36 # include <sys/ioctl.h>
37 #endif
38
39 /* Classes for rendering source code and diagnostics, within an
40 anonymous namespace.
41 The work is done by "class layout", which embeds and uses
42 "class colorizer" and "class layout_range" to get things done. */
43
44 namespace {
45
46 /* The state at a given point of the source code, assuming that we're
47 in a range: which range are we in, and whether we should draw a caret at
48 this point. */
49
50 struct point_state
51 {
52 int range_idx;
53 bool draw_caret_p;
54 };
55
56 /* A class to inject colorization codes when printing the diagnostic locus.
57
58 It has one kind of colorization for each of:
59 - normal text
60 - range 0 (the "primary location")
61 - range 1
62 - range 2
63
64 The class caches the lookup of the color codes for the above.
65
66 The class also has responsibility for tracking which of the above is
67 active, filtering out unnecessary changes. This allows
68 layout::print_source_line and layout::print_annotation_line
69 to simply request a colorization code for *every* character they print,
70 via this class, and have the filtering be done for them here. */
71
72 class colorizer
73 {
74 public:
75 colorizer (diagnostic_context *context,
76 const diagnostic_info *diagnostic);
77 ~colorizer ();
78
79 void set_range (int range_idx) { set_state (range_idx); }
80 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
81 void set_fixit_hint () { set_state (0); }
82
83 private:
84 void set_state (int state);
85 void begin_state (int state);
86 void finish_state (int state);
87
88 private:
89 static const int STATE_NORMAL_TEXT = -1;
90
91 diagnostic_context *m_context;
92 const diagnostic_info *m_diagnostic;
93 int m_current_state;
94 const char *m_caret_cs;
95 const char *m_caret_ce;
96 const char *m_range1_cs;
97 const char *m_range2_cs;
98 const char *m_range_ce;
99 };
100
101 /* A point within a layout_range; similar to an expanded_location,
102 but after filtering on file. */
103
104 class layout_point
105 {
106 public:
107 layout_point (const expanded_location &exploc)
108 : m_line (exploc.line),
109 m_column (exploc.column) {}
110
111 int m_line;
112 int m_column;
113 };
114
115 /* A class for use by "class layout" below: a filtered location_range. */
116
117 class layout_range
118 {
119 public:
120 layout_range (const location_range *loc_range);
121
122 bool contains_point (int row, int column) const;
123
124 layout_point m_start;
125 layout_point m_finish;
126 bool m_show_caret_p;
127 layout_point m_caret;
128 };
129
130 /* A struct for use by layout::print_source_line for telling
131 layout::print_annotation_line the extents of the source line that
132 it printed, so that underlines can be clipped appropriately. */
133
134 struct line_bounds
135 {
136 int m_first_non_ws;
137 int m_last_non_ws;
138 };
139
140 /* A class to control the overall layout when printing a diagnostic.
141
142 The layout is determined within the constructor.
143 It is then printed by repeatedly calling the "print_source_line",
144 "print_annotation_line" and "print_any_fixits" methods.
145
146 We assume we have disjoint ranges. */
147
148 class layout
149 {
150 public:
151 layout (diagnostic_context *context,
152 const diagnostic_info *diagnostic);
153
154 int get_first_line () const { return m_first_line; }
155 int get_last_line () const { return m_last_line; }
156
157 bool print_source_line (int row, line_bounds *lbounds_out);
158 void print_annotation_line (int row, const line_bounds lbounds);
159 void print_any_fixits (int row, const rich_location *richloc);
160
161 private:
162 bool
163 get_state_at_point (/* Inputs. */
164 int row, int column,
165 int first_non_ws, int last_non_ws,
166 /* Outputs. */
167 point_state *out_state);
168
169 int
170 get_x_bound_for_row (int row, int caret_column,
171 int last_non_ws);
172
173 void
174 move_to_column (int *column, int dest_column);
175
176 private:
177 diagnostic_context *m_context;
178 pretty_printer *m_pp;
179 diagnostic_t m_diagnostic_kind;
180 expanded_location m_exploc;
181 colorizer m_colorizer;
182 bool m_colorize_source_p;
183 auto_vec <layout_range> m_layout_ranges;
184 int m_first_line;
185 int m_last_line;
186 int m_x_offset;
187 };
188
189 /* Implementation of "class colorizer". */
190
191 /* The constructor for "colorizer". Lookup and store color codes for the
192 different kinds of things we might need to print. */
193
194 colorizer::colorizer (diagnostic_context *context,
195 const diagnostic_info *diagnostic) :
196 m_context (context),
197 m_diagnostic (diagnostic),
198 m_current_state (STATE_NORMAL_TEXT)
199 {
200 m_caret_ce = colorize_stop (pp_show_color (context->printer));
201 m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
202 m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
203 m_range_ce = colorize_stop (pp_show_color (context->printer));
204 }
205
206 /* The destructor for "colorize". If colorization is on, print a code to
207 turn it off. */
208
209 colorizer::~colorizer ()
210 {
211 finish_state (m_current_state);
212 }
213
214 /* Update state, printing color codes if necessary if there's a state
215 change. */
216
217 void
218 colorizer::set_state (int new_state)
219 {
220 if (m_current_state != new_state)
221 {
222 finish_state (m_current_state);
223 m_current_state = new_state;
224 begin_state (new_state);
225 }
226 }
227
228 /* Turn on any colorization for STATE. */
229
230 void
231 colorizer::begin_state (int state)
232 {
233 switch (state)
234 {
235 case STATE_NORMAL_TEXT:
236 break;
237
238 case 0:
239 /* Make range 0 be the same color as the "kind" text
240 (error vs warning vs note). */
241 pp_string
242 (m_context->printer,
243 colorize_start (pp_show_color (m_context->printer),
244 diagnostic_get_color_for_kind (m_diagnostic->kind)));
245 break;
246
247 case 1:
248 pp_string (m_context->printer, m_range1_cs);
249 break;
250
251 case 2:
252 pp_string (m_context->printer, m_range2_cs);
253 break;
254
255 default:
256 /* We don't expect more than 3 ranges per diagnostic. */
257 gcc_unreachable ();
258 break;
259 }
260 }
261
262 /* Turn off any colorization for STATE. */
263
264 void
265 colorizer::finish_state (int state)
266 {
267 switch (state)
268 {
269 case STATE_NORMAL_TEXT:
270 break;
271
272 case 0:
273 pp_string (m_context->printer, m_caret_ce);
274 break;
275
276 default:
277 /* Within a range. */
278 gcc_assert (state > 0);
279 pp_string (m_context->printer, m_range_ce);
280 break;
281 }
282 }
283
284 /* Implementation of class layout_range. */
285
286 /* The constructor for class layout_range.
287 Initialize various layout_point fields from expanded_location
288 equivalents; we've already filtered on file. */
289
290 layout_range::layout_range (const location_range *loc_range)
291 : m_start (loc_range->m_start),
292 m_finish (loc_range->m_finish),
293 m_show_caret_p (loc_range->m_show_caret_p),
294 m_caret (loc_range->m_caret)
295 {
296 }
297
298 /* Is (column, row) within the given range?
299 We've already filtered on the file.
300
301 Ranges are closed (both limits are within the range).
302
303 Example A: a single-line range:
304 start: (col=22, line=2)
305 finish: (col=38, line=2)
306
307 |00000011111111112222222222333333333344444444444
308 |34567890123456789012345678901234567890123456789
309 --+-----------------------------------------------
310 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
311 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
312 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
313
314 Example B: a multiline range with
315 start: (col=14, line=3)
316 finish: (col=08, line=5)
317
318 |00000011111111112222222222333333333344444444444
319 |34567890123456789012345678901234567890123456789
320 --+-----------------------------------------------
321 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
322 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
323 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
324 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
325 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
326 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
327 --+-----------------------------------------------
328
329 Legend:
330 - 'b' indicates a point *before* the range
331 - 'S' indicates the start of the range
332 - 'w' indicates a point within the range
333 - 'F' indicates the finish of the range (which is
334 within it).
335 - 'a' indicates a subsequent point *after* the range. */
336
337 bool
338 layout_range::contains_point (int row, int column) const
339 {
340 gcc_assert (m_start.m_line <= m_finish.m_line);
341 /* ...but the equivalent isn't true for the columns;
342 consider example B in the comment above. */
343
344 if (row < m_start.m_line)
345 /* Points before the first line of the range are
346 outside it (corresponding to line 01 in example A
347 and lines 01 and 02 in example B above). */
348 return false;
349
350 if (row == m_start.m_line)
351 /* On same line as start of range (corresponding
352 to line 02 in example A and line 03 in example B). */
353 {
354 if (column < m_start.m_column)
355 /* Points on the starting line of the range, but
356 before the column in which it begins. */
357 return false;
358
359 if (row < m_finish.m_line)
360 /* This is a multiline range; the point
361 is within it (corresponds to line 03 in example B
362 from column 14 onwards) */
363 return true;
364 else
365 {
366 /* This is a single-line range. */
367 gcc_assert (row == m_finish.m_line);
368 return column <= m_finish.m_column;
369 }
370 }
371
372 /* The point is in a line beyond that containing the
373 start of the range: lines 03 onwards in example A,
374 and lines 04 onwards in example B. */
375 gcc_assert (row > m_start.m_line);
376
377 if (row > m_finish.m_line)
378 /* The point is beyond the final line of the range
379 (lines 03 onwards in example A, and lines 06 onwards
380 in example B). */
381 return false;
382
383 if (row < m_finish.m_line)
384 {
385 /* The point is in a line that's fully within a multiline
386 range (e.g. line 04 in example B). */
387 gcc_assert (m_start.m_line < m_finish.m_line);
388 return true;
389 }
390
391 gcc_assert (row == m_finish.m_line);
392
393 return column <= m_finish.m_column;
394 }
395
396 /* Given a source line LINE of length LINE_WIDTH, determine the width
397 without any trailing whitespace. */
398
399 static int
400 get_line_width_without_trailing_whitespace (const char *line, int line_width)
401 {
402 int result = line_width;
403 while (result > 0)
404 {
405 char ch = line[result - 1];
406 if (ch == ' ' || ch == '\t')
407 result--;
408 else
409 break;
410 }
411 gcc_assert (result >= 0);
412 gcc_assert (result <= line_width);
413 gcc_assert (result == 0 ||
414 (line[result - 1] != ' '
415 && line[result -1] != '\t'));
416 return result;
417 }
418
419 /* Implementation of class layout. */
420
421 /* Constructor for class layout.
422
423 Filter the ranges from the rich_location to those that we can
424 sanely print, populating m_layout_ranges.
425 Determine the range of lines that we will print.
426 Determine m_x_offset, to ensure that the primary caret
427 will fit within the max_width provided by the diagnostic_context. */
428
429 layout::layout (diagnostic_context * context,
430 const diagnostic_info *diagnostic)
431 : m_context (context),
432 m_pp (context->printer),
433 m_diagnostic_kind (diagnostic->kind),
434 m_exploc (diagnostic->richloc->lazily_expand_location ()),
435 m_colorizer (context, diagnostic),
436 m_colorize_source_p (context->colorize_source_p),
437 m_layout_ranges (rich_location::MAX_RANGES),
438 m_first_line (m_exploc.line),
439 m_last_line (m_exploc.line),
440 m_x_offset (0)
441 {
442 rich_location *richloc = diagnostic->richloc;
443 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
444 {
445 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
446 Ignore any ranges that are awkward to handle. */
447 location_range *loc_range = richloc->get_range (idx);
448
449 /* If any part of the range isn't in the same file as the primary
450 location of this diagnostic, ignore the range. */
451 if (loc_range->m_start.file != m_exploc.file)
452 continue;
453 if (loc_range->m_finish.file != m_exploc.file)
454 continue;
455 if (loc_range->m_show_caret_p)
456 if (loc_range->m_caret.file != m_exploc.file)
457 continue;
458
459 /* Passed all the tests; add the range to m_layout_ranges so that
460 it will be printed. */
461 layout_range ri (loc_range);
462 m_layout_ranges.safe_push (ri);
463
464 /* Update m_first_line/m_last_line if necessary. */
465 if (loc_range->m_start.line < m_first_line)
466 m_first_line = loc_range->m_start.line;
467 if (loc_range->m_finish.line > m_last_line)
468 m_last_line = loc_range->m_finish.line;
469 }
470
471 /* Adjust m_x_offset.
472 Center the primary caret to fit in max_width; all columns
473 will be adjusted accordingly. */
474 int max_width = m_context->caret_max_width;
475 int line_width;
476 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
477 &line_width);
478 if (line && m_exploc.column <= line_width)
479 {
480 int right_margin = CARET_LINE_MARGIN;
481 int column = m_exploc.column;
482 right_margin = MIN (line_width - column, right_margin);
483 right_margin = max_width - right_margin;
484 if (line_width >= max_width && column > right_margin)
485 m_x_offset = column - right_margin;
486 gcc_assert (m_x_offset >= 0);
487 }
488 }
489
490 /* Attempt to print line ROW of source code, potentially colorized at any
491 ranges.
492 Return true if the line was printed, populating *LBOUNDS_OUT.
493 Return false if the source line could not be read, leaving *LBOUNDS_OUT
494 untouched. */
495
496 bool
497 layout::print_source_line (int row, line_bounds *lbounds_out)
498 {
499 int line_width;
500 const char *line = location_get_source_line (m_exploc.file, row,
501 &line_width);
502 if (!line)
503 return false;
504
505 line += m_x_offset;
506
507 m_colorizer.set_normal_text ();
508
509 /* We will stop printing the source line at any trailing
510 whitespace. */
511 line_width = get_line_width_without_trailing_whitespace (line,
512 line_width);
513
514 pp_space (m_pp);
515 int first_non_ws = INT_MAX;
516 int last_non_ws = 0;
517 int column;
518 for (column = 1 + m_x_offset; column <= line_width; column++)
519 {
520 /* Assuming colorization is enabled for the caret and underline
521 characters, we may also colorize the associated characters
522 within the source line.
523
524 For frontends that generate range information, we color the
525 associated characters in the source line the same as the
526 carets and underlines in the annotation line, to make it easier
527 for the reader to see the pertinent code.
528
529 For frontends that only generate carets, we don't colorize the
530 characters above them, since this would look strange (e.g.
531 colorizing just the first character in a token). */
532 if (m_colorize_source_p)
533 {
534 bool in_range_p;
535 point_state state;
536 in_range_p = get_state_at_point (row, column,
537 0, INT_MAX,
538 &state);
539 if (in_range_p)
540 m_colorizer.set_range (state.range_idx);
541 else
542 m_colorizer.set_normal_text ();
543 }
544 char c = *line == '\t' ? ' ' : *line;
545 if (c == '\0')
546 c = ' ';
547 if (c != ' ')
548 {
549 last_non_ws = column;
550 if (first_non_ws == INT_MAX)
551 first_non_ws = column;
552 }
553 pp_character (m_pp, c);
554 line++;
555 }
556 pp_newline (m_pp);
557
558 lbounds_out->m_first_non_ws = first_non_ws;
559 lbounds_out->m_last_non_ws = last_non_ws;
560 return true;
561 }
562
563 /* Print a line consisting of the caret/underlines for the given
564 source line. */
565
566 void
567 layout::print_annotation_line (int row, const line_bounds lbounds)
568 {
569 int x_bound = get_x_bound_for_row (row, m_exploc.column,
570 lbounds.m_last_non_ws);
571
572 pp_space (m_pp);
573 for (int column = 1 + m_x_offset; column < x_bound; column++)
574 {
575 bool in_range_p;
576 point_state state;
577 in_range_p = get_state_at_point (row, column,
578 lbounds.m_first_non_ws,
579 lbounds.m_last_non_ws,
580 &state);
581 if (in_range_p)
582 {
583 /* Within a range. Draw either the caret or an underline. */
584 m_colorizer.set_range (state.range_idx);
585 if (state.draw_caret_p)
586 /* Draw the caret. */
587 pp_character (m_pp, m_context->caret_chars[state.range_idx]);
588 else
589 pp_character (m_pp, '~');
590 }
591 else
592 {
593 /* Not in a range. */
594 m_colorizer.set_normal_text ();
595 pp_character (m_pp, ' ');
596 }
597 }
598 pp_newline (m_pp);
599 }
600
601 /* If there are any fixit hints on source line ROW within RICHLOC, print them.
602 They are printed in order, attempting to combine them onto lines, but
603 starting new lines if necessary. */
604
605 void
606 layout::print_any_fixits (int row, const rich_location *richloc)
607 {
608 int column = 0;
609 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
610 {
611 fixit_hint *hint = richloc->get_fixit_hint (i);
612 if (hint->affects_line_p (m_exploc.file, row))
613 {
614 /* For now we assume each fixit hint can only touch one line. */
615 switch (hint->get_kind ())
616 {
617 case fixit_hint::INSERT:
618 {
619 fixit_insert *insert = static_cast <fixit_insert *> (hint);
620 /* This assumes the insertion just affects one line. */
621 int start_column
622 = LOCATION_COLUMN (insert->get_location ());
623 move_to_column (&column, start_column);
624 m_colorizer.set_fixit_hint ();
625 pp_string (m_pp, insert->get_string ());
626 m_colorizer.set_normal_text ();
627 column += insert->get_length ();
628 }
629 break;
630
631 case fixit_hint::REMOVE:
632 {
633 fixit_remove *remove = static_cast <fixit_remove *> (hint);
634 /* This assumes the removal just affects one line. */
635 source_range src_range = remove->get_range ();
636 int start_column = LOCATION_COLUMN (src_range.m_start);
637 int finish_column = LOCATION_COLUMN (src_range.m_finish);
638 move_to_column (&column, start_column);
639 for (int column = start_column; column <= finish_column; column++)
640 {
641 m_colorizer.set_fixit_hint ();
642 pp_character (m_pp, '-');
643 m_colorizer.set_normal_text ();
644 }
645 }
646 break;
647
648 case fixit_hint::REPLACE:
649 {
650 fixit_replace *replace = static_cast <fixit_replace *> (hint);
651 int start_column
652 = LOCATION_COLUMN (replace->get_range ().m_start);
653 move_to_column (&column, start_column);
654 m_colorizer.set_fixit_hint ();
655 pp_string (m_pp, replace->get_string ());
656 m_colorizer.set_normal_text ();
657 column += replace->get_length ();
658 }
659 break;
660
661 default:
662 gcc_unreachable ();
663 }
664 }
665 }
666 }
667
668 /* Return true if (ROW/COLUMN) is within a range of the layout.
669 If it returns true, OUT_STATE is written to, with the
670 range index, and whether we should draw the caret at
671 (ROW/COLUMN) (as opposed to an underline). */
672
673 bool
674 layout::get_state_at_point (/* Inputs. */
675 int row, int column,
676 int first_non_ws, int last_non_ws,
677 /* Outputs. */
678 point_state *out_state)
679 {
680 layout_range *range;
681 int i;
682 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
683 {
684 if (range->contains_point (row, column))
685 {
686 out_state->range_idx = i;
687
688 /* Are we at the range's caret? is it visible? */
689 out_state->draw_caret_p = false;
690 if (row == range->m_caret.m_line
691 && column == range->m_caret.m_column)
692 out_state->draw_caret_p = range->m_show_caret_p;
693
694 /* Within a multiline range, don't display any underline
695 in any leading or trailing whitespace on a line.
696 We do display carets, however. */
697 if (!out_state->draw_caret_p)
698 if (column < first_non_ws || column > last_non_ws)
699 return false;
700
701 /* We are within a range. */
702 return true;
703 }
704 }
705
706 return false;
707 }
708
709 /* Helper function for use by layout::print_line when printing the
710 annotation line under the source line.
711 Get the column beyond the rightmost one that could contain a caret or
712 range marker, given that we stop rendering at trailing whitespace.
713 ROW is the source line within the given file.
714 CARET_COLUMN is the column of range 0's caret.
715 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
716 character of source (as determined when printing the source line). */
717
718 int
719 layout::get_x_bound_for_row (int row, int caret_column,
720 int last_non_ws_column)
721 {
722 int result = caret_column + 1;
723
724 layout_range *range;
725 int i;
726 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
727 {
728 if (row >= range->m_start.m_line)
729 {
730 if (range->m_finish.m_line == row)
731 {
732 /* On the final line within a range; ensure that
733 we render up to the end of the range. */
734 if (result <= range->m_finish.m_column)
735 result = range->m_finish.m_column + 1;
736 }
737 else if (row < range->m_finish.m_line)
738 {
739 /* Within a multiline range; ensure that we render up to the
740 last non-whitespace column. */
741 if (result <= last_non_ws_column)
742 result = last_non_ws_column + 1;
743 }
744 }
745 }
746
747 return result;
748 }
749
750 /* Given *COLUMN as an x-coordinate, print spaces to position
751 successive output at DEST_COLUMN, printing a newline if necessary,
752 and updating *COLUMN. */
753
754 void
755 layout::move_to_column (int *column, int dest_column)
756 {
757 /* Start a new line if we need to. */
758 if (*column > dest_column)
759 {
760 pp_newline (m_pp);
761 *column = 0;
762 }
763
764 while (*column < dest_column)
765 {
766 pp_space (m_pp);
767 (*column)++;
768 }
769 }
770
771 } /* End of anonymous namespace. */
772
773 /* Print the physical source code corresponding to the location of
774 this diagnostic, with additional annotations. */
775
776 void
777 diagnostic_show_locus (diagnostic_context * context,
778 const diagnostic_info *diagnostic)
779 {
780 if (!context->show_caret
781 || diagnostic_location (diagnostic, 0) <= BUILTINS_LOCATION
782 || diagnostic_location (diagnostic, 0) == context->last_location)
783 return;
784
785 context->last_location = diagnostic_location (diagnostic, 0);
786
787 pp_newline (context->printer);
788
789 const char *saved_prefix = pp_get_prefix (context->printer);
790 pp_set_prefix (context->printer, NULL);
791
792 {
793 layout layout (context, diagnostic);
794 int last_line = layout.get_last_line ();
795 for (int row = layout.get_first_line ();
796 row <= last_line;
797 row++)
798 {
799 /* Print the source line, followed by an annotation line
800 consisting of any caret/underlines, then any fixits.
801 If the source line can't be read, print nothing. */
802 line_bounds lbounds;
803 if (layout.print_source_line (row, &lbounds))
804 {
805 layout.print_annotation_line (row, lbounds);
806 layout.print_any_fixits (row, diagnostic->richloc);
807 }
808 }
809
810 /* The closing scope here leads to the dtor for layout and thus
811 colorizer being called here, which affects the precise
812 place where colorization is turned off in the unittest
813 for colorized output. */
814 }
815
816 pp_set_prefix (context->printer, saved_prefix);
817 }