PR tree-optimization/78496
[official-gcc.git] / gcc / diagnostic-show-locus.c
blobf410a324b4b7e631e8ee93dac603736d55a10c42
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2017 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
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.
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.
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/>. */
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 #include "selftest.h"
32 #ifdef HAVE_TERMIOS_H
33 # include <termios.h>
34 #endif
36 #ifdef GWINSZ_IN_SYS_IOCTL
37 # include <sys/ioctl.h>
38 #endif
40 /* Classes for rendering source code and diagnostics, within an
41 anonymous namespace.
42 The work is done by "class layout", which embeds and uses
43 "class colorizer" and "class layout_range" to get things done. */
45 namespace {
47 /* The state at a given point of the source code, assuming that we're
48 in a range: which range are we in, and whether we should draw a caret at
49 this point. */
51 struct point_state
53 int range_idx;
54 bool draw_caret_p;
57 /* A class to inject colorization codes when printing the diagnostic locus.
59 It has one kind of colorization for each of:
60 - normal text
61 - range 0 (the "primary location")
62 - range 1
63 - range 2
65 The class caches the lookup of the color codes for the above.
67 The class also has responsibility for tracking which of the above is
68 active, filtering out unnecessary changes. This allows
69 layout::print_source_line and layout::print_annotation_line
70 to simply request a colorization code for *every* character they print,
71 via this class, and have the filtering be done for them here. */
73 class colorizer
75 public:
76 colorizer (diagnostic_context *context,
77 diagnostic_t diagnostic_kind);
78 ~colorizer ();
80 void set_range (int range_idx) { set_state (range_idx); }
81 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
82 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
83 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
85 private:
86 void set_state (int state);
87 void begin_state (int state);
88 void finish_state (int state);
89 const char *get_color_by_name (const char *);
91 private:
92 static const int STATE_NORMAL_TEXT = -1;
93 static const int STATE_FIXIT_INSERT = -2;
94 static const int STATE_FIXIT_DELETE = -3;
96 diagnostic_context *m_context;
97 diagnostic_t m_diagnostic_kind;
98 int m_current_state;
99 const char *m_caret;
100 const char *m_range1;
101 const char *m_range2;
102 const char *m_fixit_insert;
103 const char *m_fixit_delete;
104 const char *m_stop_color;
107 /* A point within a layout_range; similar to an expanded_location,
108 but after filtering on file. */
110 class layout_point
112 public:
113 layout_point (const expanded_location &exploc)
114 : m_line (exploc.line),
115 m_column (exploc.column) {}
117 int m_line;
118 int m_column;
121 /* A class for use by "class layout" below: a filtered location_range. */
123 class layout_range
125 public:
126 layout_range (const expanded_location *start_exploc,
127 const expanded_location *finish_exploc,
128 bool show_caret_p,
129 const expanded_location *caret_exploc);
131 bool contains_point (int row, int column) const;
132 bool intersects_line_p (int row) const;
134 layout_point m_start;
135 layout_point m_finish;
136 bool m_show_caret_p;
137 layout_point m_caret;
140 /* A struct for use by layout::print_source_line for telling
141 layout::print_annotation_line the extents of the source line that
142 it printed, so that underlines can be clipped appropriately. */
144 struct line_bounds
146 int m_first_non_ws;
147 int m_last_non_ws;
150 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
151 or "line 23"). During the layout ctor, layout::calculate_line_spans
152 splits the pertinent source lines into a list of disjoint line_span
153 instances (e.g. lines 5-10, lines 15-20, line 23). */
155 struct line_span
157 line_span (linenum_type first_line, linenum_type last_line)
158 : m_first_line (first_line), m_last_line (last_line)
160 gcc_assert (first_line <= last_line);
162 linenum_type get_first_line () const { return m_first_line; }
163 linenum_type get_last_line () const { return m_last_line; }
165 bool contains_line_p (linenum_type line) const
167 return line >= m_first_line && line <= m_last_line;
170 static int comparator (const void *p1, const void *p2)
172 const line_span *ls1 = (const line_span *)p1;
173 const line_span *ls2 = (const line_span *)p2;
174 int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
175 if (first_line_diff)
176 return first_line_diff;
177 return (int)ls1->m_last_line - (int)ls2->m_last_line;
180 linenum_type m_first_line;
181 linenum_type m_last_line;
184 /* A class to control the overall layout when printing a diagnostic.
186 The layout is determined within the constructor.
187 It is then printed by repeatedly calling the "print_source_line",
188 "print_annotation_line" and "print_any_fixits" methods.
190 We assume we have disjoint ranges. */
192 class layout
194 public:
195 layout (diagnostic_context *context,
196 rich_location *richloc,
197 diagnostic_t diagnostic_kind);
199 int get_num_line_spans () const { return m_line_spans.length (); }
200 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
202 bool print_heading_for_line_span_index_p (int line_span_idx) const;
204 expanded_location get_expanded_location (const line_span *) const;
206 void print_line (int row);
208 private:
209 void print_leading_fixits (int row);
210 void print_source_line (int row, const char *line, int line_width,
211 line_bounds *lbounds_out);
212 bool should_print_annotation_line_p (int row) const;
213 void print_annotation_line (int row, const line_bounds lbounds);
214 void print_trailing_fixits (int row);
216 bool annotation_line_showed_range_p (int line, int start_column,
217 int finish_column) const;
218 void show_ruler (int max_column) const;
220 bool validate_fixit_hint_p (const fixit_hint *hint);
222 void calculate_line_spans ();
224 void print_newline ();
226 bool
227 get_state_at_point (/* Inputs. */
228 int row, int column,
229 int first_non_ws, int last_non_ws,
230 /* Outputs. */
231 point_state *out_state);
234 get_x_bound_for_row (int row, int caret_column,
235 int last_non_ws);
237 void
238 move_to_column (int *column, int dest_column);
240 private:
241 diagnostic_context *m_context;
242 pretty_printer *m_pp;
243 diagnostic_t m_diagnostic_kind;
244 expanded_location m_exploc;
245 colorizer m_colorizer;
246 bool m_colorize_source_p;
247 auto_vec <layout_range> m_layout_ranges;
248 auto_vec <const fixit_hint *> m_fixit_hints;
249 auto_vec <line_span> m_line_spans;
250 int m_x_offset;
253 /* Implementation of "class colorizer". */
255 /* The constructor for "colorizer". Lookup and store color codes for the
256 different kinds of things we might need to print. */
258 colorizer::colorizer (diagnostic_context *context,
259 diagnostic_t diagnostic_kind) :
260 m_context (context),
261 m_diagnostic_kind (diagnostic_kind),
262 m_current_state (STATE_NORMAL_TEXT)
264 m_range1 = get_color_by_name ("range1");
265 m_range2 = get_color_by_name ("range2");
266 m_fixit_insert = get_color_by_name ("fixit-insert");
267 m_fixit_delete = get_color_by_name ("fixit-delete");
268 m_stop_color = colorize_stop (pp_show_color (context->printer));
271 /* The destructor for "colorize". If colorization is on, print a code to
272 turn it off. */
274 colorizer::~colorizer ()
276 finish_state (m_current_state);
279 /* Update state, printing color codes if necessary if there's a state
280 change. */
282 void
283 colorizer::set_state (int new_state)
285 if (m_current_state != new_state)
287 finish_state (m_current_state);
288 m_current_state = new_state;
289 begin_state (new_state);
293 /* Turn on any colorization for STATE. */
295 void
296 colorizer::begin_state (int state)
298 switch (state)
300 case STATE_NORMAL_TEXT:
301 break;
303 case STATE_FIXIT_INSERT:
304 pp_string (m_context->printer, m_fixit_insert);
305 break;
307 case STATE_FIXIT_DELETE:
308 pp_string (m_context->printer, m_fixit_delete);
309 break;
311 case 0:
312 /* Make range 0 be the same color as the "kind" text
313 (error vs warning vs note). */
314 pp_string
315 (m_context->printer,
316 colorize_start (pp_show_color (m_context->printer),
317 diagnostic_get_color_for_kind (m_diagnostic_kind)));
318 break;
320 case 1:
321 pp_string (m_context->printer, m_range1);
322 break;
324 case 2:
325 pp_string (m_context->printer, m_range2);
326 break;
328 default:
329 /* For ranges beyond 2, alternate between color 1 and color 2. */
331 gcc_assert (state > 2);
332 pp_string (m_context->printer,
333 state % 2 ? m_range1 : m_range2);
335 break;
339 /* Turn off any colorization for STATE. */
341 void
342 colorizer::finish_state (int state)
344 if (state != STATE_NORMAL_TEXT)
345 pp_string (m_context->printer, m_stop_color);
348 /* Get the color code for NAME (or the empty string if
349 colorization is disabled). */
351 const char *
352 colorizer::get_color_by_name (const char *name)
354 return colorize_start (pp_show_color (m_context->printer), name);
357 /* Implementation of class layout_range. */
359 /* The constructor for class layout_range.
360 Initialize various layout_point fields from expanded_location
361 equivalents; we've already filtered on file. */
363 layout_range::layout_range (const expanded_location *start_exploc,
364 const expanded_location *finish_exploc,
365 bool show_caret_p,
366 const expanded_location *caret_exploc)
367 : m_start (*start_exploc),
368 m_finish (*finish_exploc),
369 m_show_caret_p (show_caret_p),
370 m_caret (*caret_exploc)
374 /* Is (column, row) within the given range?
375 We've already filtered on the file.
377 Ranges are closed (both limits are within the range).
379 Example A: a single-line range:
380 start: (col=22, line=2)
381 finish: (col=38, line=2)
383 |00000011111111112222222222333333333344444444444
384 |34567890123456789012345678901234567890123456789
385 --+-----------------------------------------------
386 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
387 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
388 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
390 Example B: a multiline range with
391 start: (col=14, line=3)
392 finish: (col=08, line=5)
394 |00000011111111112222222222333333333344444444444
395 |34567890123456789012345678901234567890123456789
396 --+-----------------------------------------------
397 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
398 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
399 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
400 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
401 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
402 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
403 --+-----------------------------------------------
405 Legend:
406 - 'b' indicates a point *before* the range
407 - 'S' indicates the start of the range
408 - 'w' indicates a point within the range
409 - 'F' indicates the finish of the range (which is
410 within it).
411 - 'a' indicates a subsequent point *after* the range. */
413 bool
414 layout_range::contains_point (int row, int column) const
416 gcc_assert (m_start.m_line <= m_finish.m_line);
417 /* ...but the equivalent isn't true for the columns;
418 consider example B in the comment above. */
420 if (row < m_start.m_line)
421 /* Points before the first line of the range are
422 outside it (corresponding to line 01 in example A
423 and lines 01 and 02 in example B above). */
424 return false;
426 if (row == m_start.m_line)
427 /* On same line as start of range (corresponding
428 to line 02 in example A and line 03 in example B). */
430 if (column < m_start.m_column)
431 /* Points on the starting line of the range, but
432 before the column in which it begins. */
433 return false;
435 if (row < m_finish.m_line)
436 /* This is a multiline range; the point
437 is within it (corresponds to line 03 in example B
438 from column 14 onwards) */
439 return true;
440 else
442 /* This is a single-line range. */
443 gcc_assert (row == m_finish.m_line);
444 return column <= m_finish.m_column;
448 /* The point is in a line beyond that containing the
449 start of the range: lines 03 onwards in example A,
450 and lines 04 onwards in example B. */
451 gcc_assert (row > m_start.m_line);
453 if (row > m_finish.m_line)
454 /* The point is beyond the final line of the range
455 (lines 03 onwards in example A, and lines 06 onwards
456 in example B). */
457 return false;
459 if (row < m_finish.m_line)
461 /* The point is in a line that's fully within a multiline
462 range (e.g. line 04 in example B). */
463 gcc_assert (m_start.m_line < m_finish.m_line);
464 return true;
467 gcc_assert (row == m_finish.m_line);
469 return column <= m_finish.m_column;
472 /* Does this layout_range contain any part of line ROW? */
474 bool
475 layout_range::intersects_line_p (int row) const
477 gcc_assert (m_start.m_line <= m_finish.m_line);
478 if (row < m_start.m_line)
479 return false;
480 if (row > m_finish.m_line)
481 return false;
482 return true;
485 #if CHECKING_P
487 /* A helper function for testing layout_range. */
489 static layout_range
490 make_range (int start_line, int start_col, int end_line, int end_col)
492 const expanded_location start_exploc
493 = {"test.c", start_line, start_col, NULL, false};
494 const expanded_location finish_exploc
495 = {"test.c", end_line, end_col, NULL, false};
496 return layout_range (&start_exploc, &finish_exploc, false,
497 &start_exploc);
500 /* Selftests for layout_range::contains_point and
501 layout_range::intersects_line_p. */
503 /* Selftest for layout_range, where the layout_range
504 is a range with start==end i.e. a single point. */
506 static void
507 test_layout_range_for_single_point ()
509 layout_range point = make_range (7, 10, 7, 10);
511 /* Tests for layout_range::contains_point. */
513 /* Before the line. */
514 ASSERT_FALSE (point.contains_point (6, 1));
516 /* On the line, but before start. */
517 ASSERT_FALSE (point.contains_point (7, 9));
519 /* At the point. */
520 ASSERT_TRUE (point.contains_point (7, 10));
522 /* On the line, after the point. */
523 ASSERT_FALSE (point.contains_point (7, 11));
525 /* After the line. */
526 ASSERT_FALSE (point.contains_point (8, 1));
528 /* Tests for layout_range::intersects_line_p. */
529 ASSERT_FALSE (point.intersects_line_p (6));
530 ASSERT_TRUE (point.intersects_line_p (7));
531 ASSERT_FALSE (point.intersects_line_p (8));
534 /* Selftest for layout_range, where the layout_range
535 is the single-line range shown as "Example A" above. */
537 static void
538 test_layout_range_for_single_line ()
540 layout_range example_a = make_range (2, 22, 2, 38);
542 /* Tests for layout_range::contains_point. */
544 /* Before the line. */
545 ASSERT_FALSE (example_a.contains_point (1, 1));
547 /* On the line, but before start. */
548 ASSERT_FALSE (example_a.contains_point (2, 21));
550 /* On the line, at the start. */
551 ASSERT_TRUE (example_a.contains_point (2, 22));
553 /* On the line, within the range. */
554 ASSERT_TRUE (example_a.contains_point (2, 23));
556 /* On the line, at the end. */
557 ASSERT_TRUE (example_a.contains_point (2, 38));
559 /* On the line, after the end. */
560 ASSERT_FALSE (example_a.contains_point (2, 39));
562 /* After the line. */
563 ASSERT_FALSE (example_a.contains_point (2, 39));
565 /* Tests for layout_range::intersects_line_p. */
566 ASSERT_FALSE (example_a.intersects_line_p (1));
567 ASSERT_TRUE (example_a.intersects_line_p (2));
568 ASSERT_FALSE (example_a.intersects_line_p (3));
571 /* Selftest for layout_range, where the layout_range
572 is the multi-line range shown as "Example B" above. */
574 static void
575 test_layout_range_for_multiple_lines ()
577 layout_range example_b = make_range (3, 14, 5, 8);
579 /* Tests for layout_range::contains_point. */
581 /* Before first line. */
582 ASSERT_FALSE (example_b.contains_point (1, 1));
584 /* On the first line, but before start. */
585 ASSERT_FALSE (example_b.contains_point (3, 13));
587 /* At the start. */
588 ASSERT_TRUE (example_b.contains_point (3, 14));
590 /* On the first line, within the range. */
591 ASSERT_TRUE (example_b.contains_point (3, 15));
593 /* On an interior line.
594 The column number should not matter; try various boundary
595 values. */
596 ASSERT_TRUE (example_b.contains_point (4, 1));
597 ASSERT_TRUE (example_b.contains_point (4, 7));
598 ASSERT_TRUE (example_b.contains_point (4, 8));
599 ASSERT_TRUE (example_b.contains_point (4, 9));
600 ASSERT_TRUE (example_b.contains_point (4, 13));
601 ASSERT_TRUE (example_b.contains_point (4, 14));
602 ASSERT_TRUE (example_b.contains_point (4, 15));
604 /* On the final line, before the end. */
605 ASSERT_TRUE (example_b.contains_point (5, 7));
607 /* On the final line, at the end. */
608 ASSERT_TRUE (example_b.contains_point (5, 8));
610 /* On the final line, after the end. */
611 ASSERT_FALSE (example_b.contains_point (5, 9));
613 /* After the line. */
614 ASSERT_FALSE (example_b.contains_point (6, 1));
616 /* Tests for layout_range::intersects_line_p. */
617 ASSERT_FALSE (example_b.intersects_line_p (2));
618 ASSERT_TRUE (example_b.intersects_line_p (3));
619 ASSERT_TRUE (example_b.intersects_line_p (4));
620 ASSERT_TRUE (example_b.intersects_line_p (5));
621 ASSERT_FALSE (example_b.intersects_line_p (6));
624 #endif /* #if CHECKING_P */
626 /* Given a source line LINE of length LINE_WIDTH, determine the width
627 without any trailing whitespace. */
629 static int
630 get_line_width_without_trailing_whitespace (const char *line, int line_width)
632 int result = line_width;
633 while (result > 0)
635 char ch = line[result - 1];
636 if (ch == ' ' || ch == '\t')
637 result--;
638 else
639 break;
641 gcc_assert (result >= 0);
642 gcc_assert (result <= line_width);
643 gcc_assert (result == 0 ||
644 (line[result - 1] != ' '
645 && line[result -1] != '\t'));
646 return result;
649 #if CHECKING_P
651 /* A helper function for testing get_line_width_without_trailing_whitespace. */
653 static void
654 assert_eq (const char *line, int expected_width)
656 int actual_value
657 = get_line_width_without_trailing_whitespace (line, strlen (line));
658 ASSERT_EQ (actual_value, expected_width);
661 /* Verify that get_line_width_without_trailing_whitespace is sane for
662 various inputs. It is not required to handle newlines. */
664 static void
665 test_get_line_width_without_trailing_whitespace ()
667 assert_eq ("", 0);
668 assert_eq (" ", 0);
669 assert_eq ("\t", 0);
670 assert_eq ("hello world", 11);
671 assert_eq ("hello world ", 11);
672 assert_eq ("hello world \t\t ", 11);
675 #endif /* #if CHECKING_P */
677 /* Helper function for layout's ctor, for sanitizing locations relative
678 to the primary location within a diagnostic.
680 Compare LOC_A and LOC_B to see if it makes sense to print underlines
681 connecting their expanded locations. Doing so is only guaranteed to
682 make sense if the locations share the same macro expansion "history"
683 i.e. they can be traced through the same macro expansions, eventually
684 reaching an ordinary map.
686 This may be too strong a condition, but it effectively sanitizes
687 PR c++/70105, which has an example of printing an expression where the
688 final location of the expression is in a different macro, which
689 erroneously was leading to hundreds of lines of irrelevant source
690 being printed. */
692 static bool
693 compatible_locations_p (location_t loc_a, location_t loc_b)
695 if (IS_ADHOC_LOC (loc_a))
696 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
697 if (IS_ADHOC_LOC (loc_b))
698 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
700 /* If either location is one of the special locations outside of a
701 linemap, they are only compatible if they are equal. */
702 if (loc_a < RESERVED_LOCATION_COUNT
703 || loc_b < RESERVED_LOCATION_COUNT)
704 return loc_a == loc_b;
706 const line_map *map_a = linemap_lookup (line_table, loc_a);
707 linemap_assert (map_a);
709 const line_map *map_b = linemap_lookup (line_table, loc_b);
710 linemap_assert (map_b);
712 /* Are they within the same map? */
713 if (map_a == map_b)
715 /* Are both within the same macro expansion? */
716 if (linemap_macro_expansion_map_p (map_a))
718 /* Expand each location towards the spelling location, and
719 recurse. */
720 const line_map_macro *macro_map = linemap_check_macro (map_a);
721 source_location loc_a_toward_spelling
722 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
723 macro_map,
724 loc_a);
725 source_location loc_b_toward_spelling
726 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
727 macro_map,
728 loc_b);
729 return compatible_locations_p (loc_a_toward_spelling,
730 loc_b_toward_spelling);
733 /* Otherwise they are within the same ordinary map. */
734 return true;
736 else
738 /* Within different maps. */
740 /* If either is within a macro expansion, they are incompatible. */
741 if (linemap_macro_expansion_map_p (map_a)
742 || linemap_macro_expansion_map_p (map_b))
743 return false;
745 /* Within two different ordinary maps; they are compatible iff they
746 are in the same file. */
747 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
748 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
749 return ord_map_a->to_file == ord_map_b->to_file;
753 /* Implementation of class layout. */
755 /* Constructor for class layout.
757 Filter the ranges from the rich_location to those that we can
758 sanely print, populating m_layout_ranges and m_fixit_hints.
759 Determine the range of lines that we will print, splitting them
760 up into an ordered list of disjoint spans of contiguous line numbers.
761 Determine m_x_offset, to ensure that the primary caret
762 will fit within the max_width provided by the diagnostic_context. */
764 layout::layout (diagnostic_context * context,
765 rich_location *richloc,
766 diagnostic_t diagnostic_kind)
767 : m_context (context),
768 m_pp (context->printer),
769 m_diagnostic_kind (diagnostic_kind),
770 m_exploc (richloc->get_expanded_location (0)),
771 m_colorizer (context, diagnostic_kind),
772 m_colorize_source_p (context->colorize_source_p),
773 m_layout_ranges (richloc->get_num_locations ()),
774 m_fixit_hints (richloc->get_num_fixit_hints ()),
775 m_line_spans (1 + richloc->get_num_locations ()),
776 m_x_offset (0)
778 source_location primary_loc = richloc->get_range (0)->m_loc;
780 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
782 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
783 Ignore any ranges that are awkward to handle. */
784 const location_range *loc_range = richloc->get_range (idx);
786 /* Split the "range" into caret and range information. */
787 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
789 /* Expand the various locations. */
790 expanded_location start
791 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
792 expanded_location finish
793 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
794 expanded_location caret
795 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
797 /* If any part of the range isn't in the same file as the primary
798 location of this diagnostic, ignore the range. */
799 if (start.file != m_exploc.file)
800 continue;
801 if (finish.file != m_exploc.file)
802 continue;
803 if (loc_range->m_show_caret_p)
804 if (caret.file != m_exploc.file)
805 continue;
807 /* Sanitize the caret location for non-primary ranges. */
808 if (m_layout_ranges.length () > 0)
809 if (loc_range->m_show_caret_p)
810 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
811 /* Discard any non-primary ranges that can't be printed
812 sanely relative to the primary location. */
813 continue;
815 /* Everything is now known to be in the correct source file,
816 but it may require further sanitization. */
817 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
819 /* If we have a range that finishes before it starts (perhaps
820 from something built via macro expansion), printing the
821 range is likely to be nonsensical. Also, attempting to do so
822 breaks assumptions within the printing code (PR c/68473).
823 Similarly, don't attempt to print ranges if one or both ends
824 of the range aren't sane to print relative to the
825 primary location (PR c++/70105). */
826 if (start.line > finish.line
827 || !compatible_locations_p (src_range.m_start, primary_loc)
828 || !compatible_locations_p (src_range.m_finish, primary_loc))
830 /* Is this the primary location? */
831 if (m_layout_ranges.length () == 0)
833 /* We want to print the caret for the primary location, but
834 we must sanitize away m_start and m_finish. */
835 ri.m_start = ri.m_caret;
836 ri.m_finish = ri.m_caret;
838 else
839 /* This is a non-primary range; ignore it. */
840 continue;
843 /* Passed all the tests; add the range to m_layout_ranges so that
844 it will be printed. */
845 m_layout_ranges.safe_push (ri);
848 /* Populate m_fixit_hints, filtering to only those that are in the
849 same file. */
850 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
852 const fixit_hint *hint = richloc->get_fixit_hint (i);
853 if (validate_fixit_hint_p (hint))
854 m_fixit_hints.safe_push (hint);
857 /* Populate m_line_spans. */
858 calculate_line_spans ();
860 /* Adjust m_x_offset.
861 Center the primary caret to fit in max_width; all columns
862 will be adjusted accordingly. */
863 int max_width = m_context->caret_max_width;
864 int line_width;
865 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
866 &line_width);
867 if (line && m_exploc.column <= line_width)
869 int right_margin = CARET_LINE_MARGIN;
870 int column = m_exploc.column;
871 right_margin = MIN (line_width - column, right_margin);
872 right_margin = max_width - right_margin;
873 if (line_width >= max_width && column > right_margin)
874 m_x_offset = column - right_margin;
875 gcc_assert (m_x_offset >= 0);
878 if (context->show_ruler_p)
879 show_ruler (m_x_offset + max_width);
882 /* Return true iff we should print a heading when starting the
883 line span with the given index. */
885 bool
886 layout::print_heading_for_line_span_index_p (int line_span_idx) const
888 /* We print a heading for every change of line span, hence for every
889 line span after the initial one. */
890 if (line_span_idx > 0)
891 return true;
893 /* We also do it for the initial span if the primary location of the
894 diagnostic is in a different span. */
895 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
896 return true;
898 return false;
901 /* Get an expanded_location for the first location of interest within
902 the given line_span.
903 Used when printing a heading to indicate a new line span. */
905 expanded_location
906 layout::get_expanded_location (const line_span *line_span) const
908 /* Whenever possible, use the caret location. */
909 if (line_span->contains_line_p (m_exploc.line))
910 return m_exploc;
912 /* Otherwise, use the start of the first range that's present
913 within the line_span. */
914 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
916 const layout_range *lr = &m_layout_ranges[i];
917 if (line_span->contains_line_p (lr->m_start.m_line))
919 expanded_location exploc = m_exploc;
920 exploc.line = lr->m_start.m_line;
921 exploc.column = lr->m_start.m_column;
922 return exploc;
926 /* Otherwise, use the location of the first fixit-hint present within
927 the line_span. */
928 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
930 const fixit_hint *hint = m_fixit_hints[i];
931 location_t loc = hint->get_start_loc ();
932 expanded_location exploc = expand_location (loc);
933 if (line_span->contains_line_p (exploc.line))
934 return exploc;
937 /* It should not be possible to have a line span that didn't
938 contain any of the layout_range or fixit_hint instances. */
939 gcc_unreachable ();
940 return m_exploc;
943 /* Determine if HINT is meaningful to print within this layout. */
945 bool
946 layout::validate_fixit_hint_p (const fixit_hint *hint)
948 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
949 return false;
950 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
951 return false;
953 return true;
956 /* Determine the range of lines affected by HINT.
957 This assumes that HINT has already been filtered by
958 validate_fixit_hint_p, and so affects the correct source file. */
960 static line_span
961 get_line_span_for_fixit_hint (const fixit_hint *hint)
963 gcc_assert (hint);
964 return line_span (LOCATION_LINE (hint->get_start_loc ()),
965 LOCATION_LINE (hint->get_next_loc ()));
968 /* We want to print the pertinent source code at a diagnostic. The
969 rich_location can contain multiple locations. This will have been
970 filtered into m_exploc (the caret for the primary location) and
971 m_layout_ranges, for those ranges within the same source file.
973 We will print a subset of the lines within the source file in question,
974 as a collection of "spans" of lines.
976 This function populates m_line_spans with an ordered, disjoint list of
977 the line spans of interest.
979 For example, if the primary caret location is on line 7, with ranges
980 covering lines 5-6 and lines 9-12:
983 005 |RANGE 0
984 006 |RANGE 0
985 007 |PRIMARY CARET
987 009 |RANGE 1
988 010 |RANGE 1
989 011 |RANGE 1
990 012 |RANGE 1
993 then we want two spans: lines 5-7 and lines 9-12. */
995 void
996 layout::calculate_line_spans ()
998 /* This should only be called once, by the ctor. */
999 gcc_assert (m_line_spans.length () == 0);
1001 /* Populate tmp_spans with individual spans, for each of
1002 m_exploc, and for m_layout_ranges. */
1003 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1004 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1005 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1007 const layout_range *lr = &m_layout_ranges[i];
1008 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1009 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1010 lr->m_finish.m_line));
1013 /* Also add spans for any fix-it hints, in case they cover other lines. */
1014 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1016 const fixit_hint *hint = m_fixit_hints[i];
1017 gcc_assert (hint);
1018 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1021 /* Sort them. */
1022 tmp_spans.qsort(line_span::comparator);
1024 /* Now iterate through tmp_spans, copying into m_line_spans, and
1025 combining where possible. */
1026 gcc_assert (tmp_spans.length () > 0);
1027 m_line_spans.safe_push (tmp_spans[0]);
1028 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1030 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1031 const line_span *next = &tmp_spans[i];
1032 gcc_assert (next->m_first_line >= current->m_first_line);
1033 if (next->m_first_line <= current->m_last_line + 1)
1035 /* We can merge them. */
1036 if (next->m_last_line > current->m_last_line)
1037 current->m_last_line = next->m_last_line;
1039 else
1041 /* No merger possible. */
1042 m_line_spans.safe_push (*next);
1046 /* Verify the result, in m_line_spans. */
1047 gcc_assert (m_line_spans.length () > 0);
1048 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1050 const line_span *prev = &m_line_spans[i - 1];
1051 const line_span *next = &m_line_spans[i];
1052 /* The individual spans must be sane. */
1053 gcc_assert (prev->m_first_line <= prev->m_last_line);
1054 gcc_assert (next->m_first_line <= next->m_last_line);
1055 /* The spans must be ordered. */
1056 gcc_assert (prev->m_first_line < next->m_first_line);
1057 /* There must be a gap of at least one line between separate spans. */
1058 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1062 /* Print line ROW of source code, potentially colorized at any ranges, and
1063 populate *LBOUNDS_OUT.
1064 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1065 is its width. */
1067 void
1068 layout::print_source_line (int row, const char *line, int line_width,
1069 line_bounds *lbounds_out)
1071 m_colorizer.set_normal_text ();
1073 /* We will stop printing the source line at any trailing
1074 whitespace. */
1075 line_width = get_line_width_without_trailing_whitespace (line,
1076 line_width);
1077 line += m_x_offset;
1079 pp_space (m_pp);
1080 int first_non_ws = INT_MAX;
1081 int last_non_ws = 0;
1082 int column;
1083 for (column = 1 + m_x_offset; column <= line_width; column++)
1085 /* Assuming colorization is enabled for the caret and underline
1086 characters, we may also colorize the associated characters
1087 within the source line.
1089 For frontends that generate range information, we color the
1090 associated characters in the source line the same as the
1091 carets and underlines in the annotation line, to make it easier
1092 for the reader to see the pertinent code.
1094 For frontends that only generate carets, we don't colorize the
1095 characters above them, since this would look strange (e.g.
1096 colorizing just the first character in a token). */
1097 if (m_colorize_source_p)
1099 bool in_range_p;
1100 point_state state;
1101 in_range_p = get_state_at_point (row, column,
1102 0, INT_MAX,
1103 &state);
1104 if (in_range_p)
1105 m_colorizer.set_range (state.range_idx);
1106 else
1107 m_colorizer.set_normal_text ();
1109 char c = *line == '\t' ? ' ' : *line;
1110 if (c == '\0')
1111 c = ' ';
1112 if (c != ' ')
1114 last_non_ws = column;
1115 if (first_non_ws == INT_MAX)
1116 first_non_ws = column;
1118 pp_character (m_pp, c);
1119 line++;
1121 print_newline ();
1123 lbounds_out->m_first_non_ws = first_non_ws;
1124 lbounds_out->m_last_non_ws = last_non_ws;
1127 /* Determine if we should print an annotation line for ROW.
1128 i.e. if any of m_layout_ranges contains ROW. */
1130 bool
1131 layout::should_print_annotation_line_p (int row) const
1133 layout_range *range;
1134 int i;
1135 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1136 if (range->intersects_line_p (row))
1137 return true;
1138 return false;
1141 /* Print a line consisting of the caret/underlines for the given
1142 source line. */
1144 void
1145 layout::print_annotation_line (int row, const line_bounds lbounds)
1147 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1148 lbounds.m_last_non_ws);
1150 pp_space (m_pp);
1151 for (int column = 1 + m_x_offset; column < x_bound; column++)
1153 bool in_range_p;
1154 point_state state;
1155 in_range_p = get_state_at_point (row, column,
1156 lbounds.m_first_non_ws,
1157 lbounds.m_last_non_ws,
1158 &state);
1159 if (in_range_p)
1161 /* Within a range. Draw either the caret or an underline. */
1162 m_colorizer.set_range (state.range_idx);
1163 if (state.draw_caret_p)
1165 /* Draw the caret. */
1166 char caret_char;
1167 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1168 caret_char = m_context->caret_chars[state.range_idx];
1169 else
1170 caret_char = '^';
1171 pp_character (m_pp, caret_char);
1173 else
1174 pp_character (m_pp, '~');
1176 else
1178 /* Not in a range. */
1179 m_colorizer.set_normal_text ();
1180 pp_character (m_pp, ' ');
1183 print_newline ();
1186 /* If there are any fixit hints inserting new lines before source line ROW,
1187 print them.
1189 They are printed on lines of their own, before the source line
1190 itself, with a leading '+'. */
1192 void
1193 layout::print_leading_fixits (int row)
1195 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1197 const fixit_hint *hint = m_fixit_hints[i];
1199 if (!hint->ends_with_newline_p ())
1200 /* Not a newline fixit; print it in print_trailing_fixits. */
1201 continue;
1203 gcc_assert (hint->insertion_p ());
1205 if (hint->affects_line_p (m_exploc.file, row))
1207 /* Printing the '+' with normal colorization
1208 and the inserted line with "insert" colorization
1209 helps them stand out from each other, and from
1210 the surrounding text. */
1211 m_colorizer.set_normal_text ();
1212 pp_character (m_pp, '+');
1213 m_colorizer.set_fixit_insert ();
1214 /* Print all but the trailing newline of the fix-it hint.
1215 We have to print the newline separately to avoid
1216 getting additional pp prefixes printed. */
1217 for (size_t i = 0; i < hint->get_length () - 1; i++)
1218 pp_character (m_pp, hint->get_string ()[i]);
1219 m_colorizer.set_normal_text ();
1220 pp_newline (m_pp);
1225 /* Subroutine of layout::print_trailing_fixits.
1227 Determine if the annotation line printed for LINE contained
1228 the exact range from START_COLUMN to FINISH_COLUMN. */
1230 bool
1231 layout::annotation_line_showed_range_p (int line, int start_column,
1232 int finish_column) const
1234 layout_range *range;
1235 int i;
1236 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1237 if (range->m_start.m_line == line
1238 && range->m_start.m_column == start_column
1239 && range->m_finish.m_line == line
1240 && range->m_finish.m_column == finish_column)
1241 return true;
1242 return false;
1245 /* Classes for printing trailing fix-it hints i.e. those that
1246 don't add new lines.
1248 For insertion, these can look like:
1250 new_text
1252 For replacement, these can look like:
1254 ------------- : underline showing affected range
1255 new_text
1257 For deletion, these can look like:
1259 ------------- : underline showing affected range
1261 This can become confusing if they overlap, and so we need
1262 to do some preprocessing to decide what to print.
1263 We use the list of fixit_hint instances affecting the line
1264 to build a list of "correction" instances, and print the
1265 latter.
1267 For example, consider a set of fix-its for converting
1268 a C-style cast to a C++ const_cast.
1270 Given:
1272 ..000000000111111111122222222223333333333.
1273 ..123456789012345678901234567890123456789.
1274 foo *f = (foo *)ptr->field;
1275 ^~~~~
1277 and the fix-it hints:
1278 - replace col 10 (the open paren) with "const_cast<"
1279 - replace col 16 (the close paren) with "> ("
1280 - insert ")" before col 27
1282 then we would get odd-looking output:
1284 foo *f = (foo *)ptr->field;
1285 ^~~~~
1287 const_cast<
1289 > ( )
1291 It would be better to detect when fixit hints are going to
1292 overlap (those that require new lines), and to consolidate
1293 the printing of such fixits, giving something like:
1295 foo *f = (foo *)ptr->field;
1296 ^~~~~
1297 -----------------
1298 const_cast<foo *> (ptr->field)
1300 This works by detecting when the printing would overlap, and
1301 effectively injecting no-op replace hints into the gaps between
1302 such fix-its, so that the printing joins up.
1304 In the above example, the overlap of:
1305 - replace col 10 (the open paren) with "const_cast<"
1306 and:
1307 - replace col 16 (the close paren) with "> ("
1308 is fixed by injecting a no-op:
1309 - replace cols 11-15 with themselves ("foo *")
1310 and consolidating these, making:
1311 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1312 i.e.:
1313 - replace cols 10-16 with "const_cast<foo *> ("
1315 This overlaps with the final fix-it hint:
1316 - insert ")" before col 27
1317 and so we repeat the consolidation process, by injecting
1318 a no-op:
1319 - replace cols 17-26 with themselves ("ptr->field")
1320 giving:
1321 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1322 i.e.:
1323 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1325 and is thus printed as desired. */
1327 /* A range of columns within a line. */
1329 struct column_range
1331 column_range (int start_, int finish_) : start (start_), finish (finish_) {}
1333 bool operator== (const column_range &other) const
1335 return start == other.start && finish == other.finish;
1338 int start;
1339 int finish;
1342 /* Get the range of columns that HINT would affect. */
1344 static column_range
1345 get_affected_columns (const fixit_hint *hint)
1347 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1348 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1350 return column_range (start_column, finish_column);
1353 /* Get the range of columns that would be printed for HINT. */
1355 static column_range
1356 get_printed_columns (const fixit_hint *hint)
1358 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1359 int final_hint_column = start_column + hint->get_length () - 1;
1360 if (hint->insertion_p ())
1362 return column_range (start_column, final_hint_column);
1364 else
1366 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1368 return column_range (start_column,
1369 MAX (finish_column, final_hint_column));
1373 /* A correction on a particular line.
1374 This describes a plan for how to print one or more fixit_hint
1375 instances that affected the line, potentially consolidating hints
1376 into corrections to make the result easier for the user to read. */
1378 struct correction
1380 correction (column_range affected_columns,
1381 column_range printed_columns,
1382 const char *new_text, size_t new_text_len)
1383 : m_affected_columns (affected_columns),
1384 m_printed_columns (printed_columns),
1385 m_text (xstrdup (new_text)),
1386 m_len (new_text_len),
1387 m_alloc_sz (new_text_len + 1)
1391 ~correction () { free (m_text); }
1393 bool insertion_p () const
1395 return m_affected_columns.start == m_affected_columns.finish + 1;
1398 void ensure_capacity (size_t len);
1399 void ensure_terminated ();
1401 /* If insert, then start: the column before which the text
1402 is to be inserted, and finish is offset by the length of
1403 the replacement.
1404 If replace, then the range of columns affected. */
1405 column_range m_affected_columns;
1407 /* If insert, then start: the column before which the text
1408 is to be inserted, and finish is offset by the length of
1409 the replacement.
1410 If replace, then the range of columns affected. */
1411 column_range m_printed_columns;
1413 /* The text to be inserted/used as replacement. */
1414 char *m_text;
1415 size_t m_len;
1416 size_t m_alloc_sz;
1419 /* Ensure that m_text can hold a string of length LEN
1420 (plus 1 for 0-termination). */
1422 void
1423 correction::ensure_capacity (size_t len)
1425 /* Allow 1 extra byte for 0-termination. */
1426 if (m_alloc_sz < (len + 1))
1428 size_t new_alloc_sz = (len + 1) * 2;
1429 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1430 m_alloc_sz = new_alloc_sz;
1434 /* Ensure that m_text is 0-terminated. */
1436 void
1437 correction::ensure_terminated ()
1439 /* 0-terminate the buffer. */
1440 gcc_assert (m_len < m_alloc_sz);
1441 m_text[m_len] = '\0';
1444 /* A list of corrections affecting a particular line.
1445 This is used by layout::print_trailing_fixits for planning
1446 how to print the fix-it hints affecting the line. */
1448 struct line_corrections
1450 line_corrections (const char *filename, int row)
1451 : m_filename (filename), m_row (row)
1453 ~line_corrections ();
1455 void add_hint (const fixit_hint *hint);
1457 const char *m_filename;
1458 int m_row;
1459 auto_vec <correction *> m_corrections;
1462 /* struct line_corrections. */
1464 line_corrections::~line_corrections ()
1466 unsigned i;
1467 correction *c;
1468 FOR_EACH_VEC_ELT (m_corrections, i, c)
1469 delete c;
1472 /* Add HINT to the corrections for this line.
1473 Attempt to consolidate nearby hints so that they will not
1474 overlap with printed. */
1476 void
1477 line_corrections::add_hint (const fixit_hint *hint)
1479 column_range affected_columns = get_affected_columns (hint);
1480 column_range printed_columns = get_printed_columns (hint);
1482 /* Potentially consolidate. */
1483 if (!m_corrections.is_empty ())
1485 correction *last_correction
1486 = m_corrections[m_corrections.length () - 1];
1487 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1489 /* We have two hints for which the printed forms of the hints
1490 would touch or overlap, so we need to consolidate them to avoid
1491 confusing the user.
1492 Attempt to inject a "replace" correction from immediately
1493 after the end of the last hint to immediately before the start
1494 of the next hint. */
1495 column_range between (last_correction->m_affected_columns.finish + 1,
1496 printed_columns.start - 1);
1498 /* Try to read the source. */
1499 int line_width;
1500 const char *line = location_get_source_line (m_filename, m_row,
1501 &line_width);
1502 if (line && between.finish < line_width)
1504 /* Consolidate into the last correction:
1505 add a no-op "replace" of the "between" text, and
1506 add the text from the new hint. */
1507 size_t old_len = last_correction->m_len;
1508 size_t between_len = between.finish + 1 - between.start;
1509 size_t new_len = old_len + between_len + hint->get_length ();
1510 last_correction->ensure_capacity (new_len);
1511 memcpy (last_correction->m_text + old_len,
1512 line + between.start - 1,
1513 between.finish + 1 - between.start);
1514 memcpy (last_correction->m_text + old_len + between_len,
1515 hint->get_string (), hint->get_length ());
1516 last_correction->m_len = new_len;
1517 last_correction->ensure_terminated ();
1518 last_correction->m_affected_columns.finish
1519 = affected_columns.finish;
1520 last_correction->m_printed_columns.finish
1521 += between_len + hint->get_length ();
1522 return;
1527 /* If no consolidation happened, add a new correction instance. */
1528 m_corrections.safe_push (new correction (affected_columns,
1529 printed_columns,
1530 hint->get_string (),
1531 hint->get_length ()));
1534 /* If there are any fixit hints on source line ROW, print them.
1535 They are printed in order, attempting to combine them onto lines, but
1536 starting new lines if necessary.
1537 Fix-it hints that insert new lines are handled separately,
1538 in layout::print_leading_fixits. */
1540 void
1541 layout::print_trailing_fixits (int row)
1543 /* Build a list of correction instances for the line,
1544 potentially consolidating hints (for the sake of readability). */
1545 line_corrections corrections (m_exploc.file, row);
1546 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1548 const fixit_hint *hint = m_fixit_hints[i];
1550 /* Newline fixits are handled by layout::print_leading_fixits. */
1551 if (hint->ends_with_newline_p ())
1552 continue;
1554 if (hint->affects_line_p (m_exploc.file, row))
1555 corrections.add_hint (hint);
1558 /* Now print the corrections. */
1559 unsigned i;
1560 correction *c;
1561 int column = 0;
1563 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1565 /* For now we assume each fixit hint can only touch one line. */
1566 if (c->insertion_p ())
1568 /* This assumes the insertion just affects one line. */
1569 int start_column = c->m_printed_columns.start;
1570 move_to_column (&column, start_column);
1571 m_colorizer.set_fixit_insert ();
1572 pp_string (m_pp, c->m_text);
1573 m_colorizer.set_normal_text ();
1574 column += c->m_len;
1576 else
1578 /* If the range of the replacement wasn't printed in the
1579 annotation line, then print an extra underline to
1580 indicate exactly what is being replaced.
1581 Always show it for removals. */
1582 int start_column = c->m_affected_columns.start;
1583 int finish_column = c->m_affected_columns.finish;
1584 if (!annotation_line_showed_range_p (row, start_column,
1585 finish_column)
1586 || c->m_len == 0)
1588 move_to_column (&column, start_column);
1589 m_colorizer.set_fixit_delete ();
1590 for (; column <= finish_column; column++)
1591 pp_character (m_pp, '-');
1592 m_colorizer.set_normal_text ();
1594 /* Print the replacement text. REPLACE also covers
1595 removals, so only do this extra work (potentially starting
1596 a new line) if we have actual replacement text. */
1597 if (c->m_len > 0)
1599 move_to_column (&column, start_column);
1600 m_colorizer.set_fixit_insert ();
1601 pp_string (m_pp, c->m_text);
1602 m_colorizer.set_normal_text ();
1603 column += c->m_len;
1608 /* Add a trailing newline, if necessary. */
1609 move_to_column (&column, 0);
1612 /* Disable any colorization and emit a newline. */
1614 void
1615 layout::print_newline ()
1617 m_colorizer.set_normal_text ();
1618 pp_newline (m_pp);
1621 /* Return true if (ROW/COLUMN) is within a range of the layout.
1622 If it returns true, OUT_STATE is written to, with the
1623 range index, and whether we should draw the caret at
1624 (ROW/COLUMN) (as opposed to an underline). */
1626 bool
1627 layout::get_state_at_point (/* Inputs. */
1628 int row, int column,
1629 int first_non_ws, int last_non_ws,
1630 /* Outputs. */
1631 point_state *out_state)
1633 layout_range *range;
1634 int i;
1635 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1637 if (range->contains_point (row, column))
1639 out_state->range_idx = i;
1641 /* Are we at the range's caret? is it visible? */
1642 out_state->draw_caret_p = false;
1643 if (range->m_show_caret_p
1644 && row == range->m_caret.m_line
1645 && column == range->m_caret.m_column)
1646 out_state->draw_caret_p = true;
1648 /* Within a multiline range, don't display any underline
1649 in any leading or trailing whitespace on a line.
1650 We do display carets, however. */
1651 if (!out_state->draw_caret_p)
1652 if (column < first_non_ws || column > last_non_ws)
1653 return false;
1655 /* We are within a range. */
1656 return true;
1660 return false;
1663 /* Helper function for use by layout::print_line when printing the
1664 annotation line under the source line.
1665 Get the column beyond the rightmost one that could contain a caret or
1666 range marker, given that we stop rendering at trailing whitespace.
1667 ROW is the source line within the given file.
1668 CARET_COLUMN is the column of range 0's caret.
1669 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1670 character of source (as determined when printing the source line). */
1673 layout::get_x_bound_for_row (int row, int caret_column,
1674 int last_non_ws_column)
1676 int result = caret_column + 1;
1678 layout_range *range;
1679 int i;
1680 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1682 if (row >= range->m_start.m_line)
1684 if (range->m_finish.m_line == row)
1686 /* On the final line within a range; ensure that
1687 we render up to the end of the range. */
1688 if (result <= range->m_finish.m_column)
1689 result = range->m_finish.m_column + 1;
1691 else if (row < range->m_finish.m_line)
1693 /* Within a multiline range; ensure that we render up to the
1694 last non-whitespace column. */
1695 if (result <= last_non_ws_column)
1696 result = last_non_ws_column + 1;
1701 return result;
1704 /* Given *COLUMN as an x-coordinate, print spaces to position
1705 successive output at DEST_COLUMN, printing a newline if necessary,
1706 and updating *COLUMN. */
1708 void
1709 layout::move_to_column (int *column, int dest_column)
1711 /* Start a new line if we need to. */
1712 if (*column > dest_column)
1714 print_newline ();
1715 *column = 0;
1718 while (*column < dest_column)
1720 pp_space (m_pp);
1721 (*column)++;
1725 /* For debugging layout issues, render a ruler giving column numbers
1726 (after the 1-column indent). */
1728 void
1729 layout::show_ruler (int max_column) const
1731 /* Hundreds. */
1732 if (max_column > 99)
1734 pp_space (m_pp);
1735 for (int column = 1 + m_x_offset; column <= max_column; column++)
1736 if (0 == column % 10)
1737 pp_character (m_pp, '0' + (column / 100) % 10);
1738 else
1739 pp_space (m_pp);
1740 pp_newline (m_pp);
1743 /* Tens. */
1744 pp_space (m_pp);
1745 for (int column = 1 + m_x_offset; column <= max_column; column++)
1746 if (0 == column % 10)
1747 pp_character (m_pp, '0' + (column / 10) % 10);
1748 else
1749 pp_space (m_pp);
1750 pp_newline (m_pp);
1752 /* Units. */
1753 pp_space (m_pp);
1754 for (int column = 1 + m_x_offset; column <= max_column; column++)
1755 pp_character (m_pp, '0' + (column % 10));
1756 pp_newline (m_pp);
1759 /* Print leading fix-its (for new lines inserted before the source line)
1760 then the source line, followed by an annotation line
1761 consisting of any caret/underlines, then any fixits.
1762 If the source line can't be read, print nothing. */
1763 void
1764 layout::print_line (int row)
1766 int line_width;
1767 const char *line = location_get_source_line (m_exploc.file, row,
1768 &line_width);
1769 if (!line)
1770 return;
1772 line_bounds lbounds;
1773 print_leading_fixits (row);
1774 print_source_line (row, line, line_width, &lbounds);
1775 if (should_print_annotation_line_p (row))
1776 print_annotation_line (row, lbounds);
1777 print_trailing_fixits (row);
1780 } /* End of anonymous namespace. */
1782 /* Print the physical source code corresponding to the location of
1783 this diagnostic, with additional annotations. */
1785 void
1786 diagnostic_show_locus (diagnostic_context * context,
1787 rich_location *richloc,
1788 diagnostic_t diagnostic_kind)
1790 pp_newline (context->printer);
1792 location_t loc = richloc->get_loc ();
1793 /* Do nothing if source-printing has been disabled. */
1794 if (!context->show_caret)
1795 return;
1797 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1798 if (loc <= BUILTINS_LOCATION)
1799 return;
1801 /* Don't print the same source location twice in a row, unless we have
1802 fix-it hints. */
1803 if (loc == context->last_location
1804 && richloc->get_num_fixit_hints () == 0)
1805 return;
1807 context->last_location = loc;
1809 const char *saved_prefix = pp_get_prefix (context->printer);
1810 pp_set_prefix (context->printer, NULL);
1812 layout layout (context, richloc, diagnostic_kind);
1813 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1814 line_span_idx++)
1816 const line_span *line_span = layout.get_line_span (line_span_idx);
1817 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1819 expanded_location exploc = layout.get_expanded_location (line_span);
1820 context->start_span (context, exploc);
1822 int last_line = line_span->get_last_line ();
1823 for (int row = line_span->get_first_line (); row <= last_line; row++)
1824 layout.print_line (row);
1827 pp_set_prefix (context->printer, saved_prefix);
1830 #if CHECKING_P
1832 namespace selftest {
1834 /* Selftests for diagnostic_show_locus. */
1836 /* Convenience subclass of diagnostic_context for testing
1837 diagnostic_show_locus. */
1839 class test_diagnostic_context : public diagnostic_context
1841 public:
1842 test_diagnostic_context ()
1844 diagnostic_initialize (this, 0);
1845 show_caret = true;
1846 show_column = true;
1847 start_span = start_span_cb;
1849 ~test_diagnostic_context ()
1851 diagnostic_finish (this);
1854 /* Implementation of diagnostic_start_span_fn, hiding the
1855 real filename (to avoid printing the names of tempfiles). */
1856 static void
1857 start_span_cb (diagnostic_context *context, expanded_location exploc)
1859 exploc.file = "FILENAME";
1860 default_diagnostic_start_span_fn (context, exploc);
1864 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1866 static void
1867 test_diagnostic_show_locus_unknown_location ()
1869 test_diagnostic_context dc;
1870 rich_location richloc (line_table, UNKNOWN_LOCATION);
1871 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1872 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1875 /* Verify that diagnostic_show_locus works sanely for various
1876 single-line cases.
1878 All of these work on the following 1-line source file:
1879 .0000000001111111
1880 .1234567890123456
1881 "foo = bar.field;\n"
1882 which is set up by test_diagnostic_show_locus_one_liner and calls
1883 them. */
1885 /* Just a caret. */
1887 static void
1888 test_one_liner_simple_caret ()
1890 test_diagnostic_context dc;
1891 location_t caret = linemap_position_for_column (line_table, 10);
1892 rich_location richloc (line_table, caret);
1893 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1894 ASSERT_STREQ ("\n"
1895 " foo = bar.field;\n"
1896 " ^\n",
1897 pp_formatted_text (dc.printer));
1900 /* Caret and range. */
1902 static void
1903 test_one_liner_caret_and_range ()
1905 test_diagnostic_context dc;
1906 location_t caret = linemap_position_for_column (line_table, 10);
1907 location_t start = linemap_position_for_column (line_table, 7);
1908 location_t finish = linemap_position_for_column (line_table, 15);
1909 location_t loc = make_location (caret, start, finish);
1910 rich_location richloc (line_table, loc);
1911 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1912 ASSERT_STREQ ("\n"
1913 " foo = bar.field;\n"
1914 " ~~~^~~~~~\n",
1915 pp_formatted_text (dc.printer));
1918 /* Multiple ranges and carets. */
1920 static void
1921 test_one_liner_multiple_carets_and_ranges ()
1923 test_diagnostic_context dc;
1924 location_t foo
1925 = make_location (linemap_position_for_column (line_table, 2),
1926 linemap_position_for_column (line_table, 1),
1927 linemap_position_for_column (line_table, 3));
1928 dc.caret_chars[0] = 'A';
1930 location_t bar
1931 = make_location (linemap_position_for_column (line_table, 8),
1932 linemap_position_for_column (line_table, 7),
1933 linemap_position_for_column (line_table, 9));
1934 dc.caret_chars[1] = 'B';
1936 location_t field
1937 = make_location (linemap_position_for_column (line_table, 13),
1938 linemap_position_for_column (line_table, 11),
1939 linemap_position_for_column (line_table, 15));
1940 dc.caret_chars[2] = 'C';
1942 rich_location richloc (line_table, foo);
1943 richloc.add_range (bar, true);
1944 richloc.add_range (field, true);
1945 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1946 ASSERT_STREQ ("\n"
1947 " foo = bar.field;\n"
1948 " ~A~ ~B~ ~~C~~\n",
1949 pp_formatted_text (dc.printer));
1952 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1954 static void
1955 test_one_liner_fixit_insert_before ()
1957 test_diagnostic_context dc;
1958 location_t caret = linemap_position_for_column (line_table, 7);
1959 rich_location richloc (line_table, caret);
1960 richloc.add_fixit_insert_before ("&");
1961 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1962 ASSERT_STREQ ("\n"
1963 " foo = bar.field;\n"
1964 " ^\n"
1965 " &\n",
1966 pp_formatted_text (dc.printer));
1969 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1971 static void
1972 test_one_liner_fixit_insert_after ()
1974 test_diagnostic_context dc;
1975 location_t start = linemap_position_for_column (line_table, 1);
1976 location_t finish = linemap_position_for_column (line_table, 3);
1977 location_t foo = make_location (start, start, finish);
1978 rich_location richloc (line_table, foo);
1979 richloc.add_fixit_insert_after ("[0]");
1980 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1981 ASSERT_STREQ ("\n"
1982 " foo = bar.field;\n"
1983 " ^~~\n"
1984 " [0]\n",
1985 pp_formatted_text (dc.printer));
1988 /* Removal fix-it hint: removal of the ".field". */
1990 static void
1991 test_one_liner_fixit_remove ()
1993 test_diagnostic_context dc;
1994 location_t start = linemap_position_for_column (line_table, 10);
1995 location_t finish = linemap_position_for_column (line_table, 15);
1996 location_t dot = make_location (start, start, finish);
1997 rich_location richloc (line_table, dot);
1998 richloc.add_fixit_remove ();
1999 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2000 ASSERT_STREQ ("\n"
2001 " foo = bar.field;\n"
2002 " ^~~~~~\n"
2003 " ------\n",
2004 pp_formatted_text (dc.printer));
2007 /* Replace fix-it hint: replacing "field" with "m_field". */
2009 static void
2010 test_one_liner_fixit_replace ()
2012 test_diagnostic_context dc;
2013 location_t start = linemap_position_for_column (line_table, 11);
2014 location_t finish = linemap_position_for_column (line_table, 15);
2015 location_t field = make_location (start, start, finish);
2016 rich_location richloc (line_table, field);
2017 richloc.add_fixit_replace ("m_field");
2018 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2019 ASSERT_STREQ ("\n"
2020 " foo = bar.field;\n"
2021 " ^~~~~\n"
2022 " m_field\n",
2023 pp_formatted_text (dc.printer));
2026 /* Replace fix-it hint: replacing "field" with "m_field",
2027 but where the caret was elsewhere. */
2029 static void
2030 test_one_liner_fixit_replace_non_equal_range ()
2032 test_diagnostic_context dc;
2033 location_t equals = linemap_position_for_column (line_table, 5);
2034 location_t start = linemap_position_for_column (line_table, 11);
2035 location_t finish = linemap_position_for_column (line_table, 15);
2036 rich_location richloc (line_table, equals);
2037 source_range range;
2038 range.m_start = start;
2039 range.m_finish = finish;
2040 richloc.add_fixit_replace (range, "m_field");
2041 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2042 /* The replacement range is not indicated in the annotation line, so
2043 it should be indicated via an additional underline. */
2044 ASSERT_STREQ ("\n"
2045 " foo = bar.field;\n"
2046 " ^\n"
2047 " -----\n"
2048 " m_field\n",
2049 pp_formatted_text (dc.printer));
2052 /* Replace fix-it hint: replacing "field" with "m_field",
2053 where the caret was elsewhere, but where a secondary range
2054 exactly covers "field". */
2056 static void
2057 test_one_liner_fixit_replace_equal_secondary_range ()
2059 test_diagnostic_context dc;
2060 location_t equals = linemap_position_for_column (line_table, 5);
2061 location_t start = linemap_position_for_column (line_table, 11);
2062 location_t finish = linemap_position_for_column (line_table, 15);
2063 rich_location richloc (line_table, equals);
2064 location_t field = make_location (start, start, finish);
2065 richloc.add_range (field, false);
2066 richloc.add_fixit_replace (field, "m_field");
2067 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2068 /* The replacement range is indicated in the annotation line,
2069 so it shouldn't be indicated via an additional underline. */
2070 ASSERT_STREQ ("\n"
2071 " foo = bar.field;\n"
2072 " ^ ~~~~~\n"
2073 " m_field\n",
2074 pp_formatted_text (dc.printer));
2077 /* Verify that we can use ad-hoc locations when adding fixits to a
2078 rich_location. */
2080 static void
2081 test_one_liner_fixit_validation_adhoc_locations ()
2083 /* Generate a range that's too long to be packed, so must
2084 be stored as an ad-hoc location (given the defaults
2085 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2086 const location_t c7 = linemap_position_for_column (line_table, 7);
2087 const location_t c47 = linemap_position_for_column (line_table, 47);
2088 const location_t loc = make_location (c7, c7, c47);
2090 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2091 return;
2093 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2095 /* Insert. */
2097 rich_location richloc (line_table, loc);
2098 richloc.add_fixit_insert_before (loc, "test");
2099 /* It should not have been discarded by the validator. */
2100 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2102 test_diagnostic_context dc;
2103 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2104 ASSERT_STREQ ("\n"
2105 " foo = bar.field;\n"
2106 " ^~~~~~~~~~ \n"
2107 " test\n",
2108 pp_formatted_text (dc.printer));
2111 /* Remove. */
2113 rich_location richloc (line_table, loc);
2114 source_range range = source_range::from_locations (loc, c47);
2115 richloc.add_fixit_remove (range);
2116 /* It should not have been discarded by the validator. */
2117 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2119 test_diagnostic_context dc;
2120 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2121 ASSERT_STREQ ("\n"
2122 " foo = bar.field;\n"
2123 " ^~~~~~~~~~ \n"
2124 " -----------------------------------------\n",
2125 pp_formatted_text (dc.printer));
2128 /* Replace. */
2130 rich_location richloc (line_table, loc);
2131 source_range range = source_range::from_locations (loc, c47);
2132 richloc.add_fixit_replace (range, "test");
2133 /* It should not have been discarded by the validator. */
2134 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2136 test_diagnostic_context dc;
2137 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2138 ASSERT_STREQ ("\n"
2139 " foo = bar.field;\n"
2140 " ^~~~~~~~~~ \n"
2141 " test\n",
2142 pp_formatted_text (dc.printer));
2146 /* Test of consolidating insertions at the same location. */
2148 static void
2149 test_one_liner_many_fixits_1 ()
2151 test_diagnostic_context dc;
2152 location_t equals = linemap_position_for_column (line_table, 5);
2153 rich_location richloc (line_table, equals);
2154 for (int i = 0; i < 19; i++)
2155 richloc.add_fixit_insert_before ("a");
2156 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2157 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2158 ASSERT_STREQ ("\n"
2159 " foo = bar.field;\n"
2160 " ^\n"
2161 " aaaaaaaaaaaaaaaaaaa\n",
2162 pp_formatted_text (dc.printer));
2165 /* Ensure that we can add an arbitrary number of fix-it hints to a
2166 rich_location, even if they are not consolidated. */
2168 static void
2169 test_one_liner_many_fixits_2 ()
2171 test_diagnostic_context dc;
2172 location_t equals = linemap_position_for_column (line_table, 5);
2173 rich_location richloc (line_table, equals);
2174 for (int i = 0; i < 19; i++)
2176 location_t loc = linemap_position_for_column (line_table, i * 2);
2177 richloc.add_fixit_insert_before (loc, "a");
2179 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2180 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2181 ASSERT_STREQ ("\n"
2182 " foo = bar.field;\n"
2183 " ^\n"
2184 "a a a a a a a a a a a a a a a a a a a\n",
2185 pp_formatted_text (dc.printer));
2188 /* Run the various one-liner tests. */
2190 static void
2191 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2193 /* Create a tempfile and write some text to it.
2194 ....................0000000001111111.
2195 ....................1234567890123456. */
2196 const char *content = "foo = bar.field;\n";
2197 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2198 line_table_test ltt (case_);
2200 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2202 location_t line_end = linemap_position_for_column (line_table, 16);
2204 /* Don't attempt to run the tests if column data might be unavailable. */
2205 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2206 return;
2208 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2209 ASSERT_EQ (1, LOCATION_LINE (line_end));
2210 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2212 test_one_liner_simple_caret ();
2213 test_one_liner_caret_and_range ();
2214 test_one_liner_multiple_carets_and_ranges ();
2215 test_one_liner_fixit_insert_before ();
2216 test_one_liner_fixit_insert_after ();
2217 test_one_liner_fixit_remove ();
2218 test_one_liner_fixit_replace ();
2219 test_one_liner_fixit_replace_non_equal_range ();
2220 test_one_liner_fixit_replace_equal_secondary_range ();
2221 test_one_liner_fixit_validation_adhoc_locations ();
2222 test_one_liner_many_fixits_1 ();
2223 test_one_liner_many_fixits_2 ();
2226 /* Verify that we print fixits even if they only affect lines
2227 outside those covered by the ranges in the rich_location. */
2229 static void
2230 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2232 /* Create a tempfile and write some text to it.
2233 ...000000000111111111122222222223333333333.
2234 ...123456789012345678901234567890123456789. */
2235 const char *content
2236 = ("struct point { double x; double y; };\n" /* line 1. */
2237 "struct point origin = {x: 0.0,\n" /* line 2. */
2238 " y\n" /* line 3. */
2239 "\n" /* line 4. */
2240 "\n" /* line 5. */
2241 " : 0.0};\n"); /* line 6. */
2242 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2243 line_table_test ltt (case_);
2245 const line_map_ordinary *ord_map
2246 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2247 tmp.get_filename (), 0));
2249 linemap_line_start (line_table, 1, 100);
2251 const location_t final_line_end
2252 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2254 /* Don't attempt to run the tests if column data might be unavailable. */
2255 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2256 return;
2258 /* A pair of tests for modernizing the initializers to C99-style. */
2260 /* The one-liner case (line 2). */
2262 test_diagnostic_context dc;
2263 const location_t x
2264 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2265 const location_t colon
2266 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2267 rich_location richloc (line_table, colon);
2268 richloc.add_fixit_insert_before (x, ".");
2269 richloc.add_fixit_replace (colon, "=");
2270 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2271 ASSERT_STREQ ("\n"
2272 " struct point origin = {x: 0.0,\n"
2273 " ^\n"
2274 " .=\n",
2275 pp_formatted_text (dc.printer));
2278 /* The multiline case. The caret for the rich_location is on line 6;
2279 verify that insertion fixit on line 3 is still printed (and that
2280 span starts are printed due to the gap between the span at line 3
2281 and that at line 6). */
2283 test_diagnostic_context dc;
2284 const location_t y
2285 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2286 const location_t colon
2287 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2288 rich_location richloc (line_table, colon);
2289 richloc.add_fixit_insert_before (y, ".");
2290 richloc.add_fixit_replace (colon, "=");
2291 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2292 ASSERT_STREQ ("\n"
2293 "FILENAME:3:24:\n"
2294 " y\n"
2295 " .\n"
2296 "FILENAME:6:25:\n"
2297 " : 0.0};\n"
2298 " ^\n"
2299 " =\n",
2300 pp_formatted_text (dc.printer));
2305 /* Verify that fix-it hints are appropriately consolidated.
2307 If any fix-it hints in a rich_location involve locations beyond
2308 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2309 the fix-it as a whole, so there should be none.
2311 Otherwise, verify that consecutive "replace" and "remove" fix-its
2312 are merged, and that other fix-its remain separate. */
2314 static void
2315 test_fixit_consolidation (const line_table_case &case_)
2317 line_table_test ltt (case_);
2319 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2321 const location_t c10 = linemap_position_for_column (line_table, 10);
2322 const location_t c15 = linemap_position_for_column (line_table, 15);
2323 const location_t c16 = linemap_position_for_column (line_table, 16);
2324 const location_t c17 = linemap_position_for_column (line_table, 17);
2325 const location_t c20 = linemap_position_for_column (line_table, 20);
2326 const location_t c21 = linemap_position_for_column (line_table, 21);
2327 const location_t caret = c10;
2329 /* Insert + insert. */
2331 rich_location richloc (line_table, caret);
2332 richloc.add_fixit_insert_before (c10, "foo");
2333 richloc.add_fixit_insert_before (c15, "bar");
2335 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2336 /* Bogus column info for 2nd fixit, so no fixits. */
2337 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2338 else
2339 /* They should not have been merged. */
2340 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2343 /* Insert + replace. */
2345 rich_location richloc (line_table, caret);
2346 richloc.add_fixit_insert_before (c10, "foo");
2347 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2348 "bar");
2350 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2351 /* Bogus column info for 2nd fixit, so no fixits. */
2352 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2353 else
2354 /* They should not have been merged. */
2355 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2358 /* Replace + non-consecutive insert. */
2360 rich_location richloc (line_table, caret);
2361 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2362 "bar");
2363 richloc.add_fixit_insert_before (c17, "foo");
2365 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2366 /* Bogus column info for 2nd fixit, so no fixits. */
2367 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2368 else
2369 /* They should not have been merged. */
2370 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2373 /* Replace + non-consecutive replace. */
2375 rich_location richloc (line_table, caret);
2376 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2377 "foo");
2378 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2379 "bar");
2381 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2382 /* Bogus column info for 2nd fixit, so no fixits. */
2383 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2384 else
2385 /* They should not have been merged. */
2386 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2389 /* Replace + consecutive replace. */
2391 rich_location richloc (line_table, caret);
2392 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2393 "foo");
2394 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2395 "bar");
2397 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2398 /* Bogus column info for 2nd fixit, so no fixits. */
2399 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2400 else
2402 /* They should have been merged into a single "replace". */
2403 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2404 const fixit_hint *hint = richloc.get_fixit_hint (0);
2405 ASSERT_STREQ ("foobar", hint->get_string ());
2406 ASSERT_EQ (c10, hint->get_start_loc ());
2407 ASSERT_EQ (c21, hint->get_next_loc ());
2411 /* Replace + consecutive removal. */
2413 rich_location richloc (line_table, caret);
2414 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2415 "foo");
2416 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2418 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2419 /* Bogus column info for 2nd fixit, so no fixits. */
2420 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2421 else
2423 /* They should have been merged into a single replace, with the
2424 range extended to cover that of the removal. */
2425 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2426 const fixit_hint *hint = richloc.get_fixit_hint (0);
2427 ASSERT_STREQ ("foo", hint->get_string ());
2428 ASSERT_EQ (c10, hint->get_start_loc ());
2429 ASSERT_EQ (c21, hint->get_next_loc ());
2433 /* Consecutive removals. */
2435 rich_location richloc (line_table, caret);
2436 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2437 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2439 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2440 /* Bogus column info for 2nd fixit, so no fixits. */
2441 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2442 else
2444 /* They should have been merged into a single "replace-with-empty". */
2445 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2446 const fixit_hint *hint = richloc.get_fixit_hint (0);
2447 ASSERT_STREQ ("", hint->get_string ());
2448 ASSERT_EQ (c10, hint->get_start_loc ());
2449 ASSERT_EQ (c21, hint->get_next_loc ());
2454 /* Verify that the line_corrections machinery correctly prints
2455 overlapping fixit-hints. */
2457 static void
2458 test_overlapped_fixit_printing (const line_table_case &case_)
2460 /* Create a tempfile and write some text to it.
2461 ...000000000111111111122222222223333333333.
2462 ...123456789012345678901234567890123456789. */
2463 const char *content
2464 = (" foo *f = (foo *)ptr->field;\n");
2465 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2466 line_table_test ltt (case_);
2468 const line_map_ordinary *ord_map
2469 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2470 tmp.get_filename (), 0));
2472 linemap_line_start (line_table, 1, 100);
2474 const location_t final_line_end
2475 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2477 /* Don't attempt to run the tests if column data might be unavailable. */
2478 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2479 return;
2481 /* A test for converting a C-style cast to a C++-style cast. */
2482 const location_t open_paren
2483 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2484 const location_t close_paren
2485 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2486 const location_t expr_start
2487 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2488 const location_t expr_finish
2489 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2490 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2492 /* Various examples of fix-it hints that aren't themselves consolidated,
2493 but for which the *printing* may need consolidation. */
2495 /* Example where 3 fix-it hints are printed as one. */
2497 test_diagnostic_context dc;
2498 rich_location richloc (line_table, expr);
2499 richloc.add_fixit_replace (open_paren, "const_cast<");
2500 richloc.add_fixit_replace (close_paren, "> (");
2501 richloc.add_fixit_insert_after (")");
2503 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2504 ASSERT_STREQ ("\n"
2505 " foo *f = (foo *)ptr->field;\n"
2506 " ^~~~~~~~~~\n"
2507 " -----------------\n"
2508 " const_cast<foo *> (ptr->field)\n",
2509 pp_formatted_text (dc.printer));
2511 /* Unit-test the line_corrections machinery. */
2512 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2513 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2514 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2515 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2516 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2517 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2518 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2519 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2520 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2521 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2523 /* Add each hint in turn to a line_corrections instance,
2524 and verify that they are consolidated into one correction instance
2525 as expected. */
2526 line_corrections lc (tmp.get_filename (), 1);
2528 /* The first replace hint by itself. */
2529 lc.add_hint (hint_0);
2530 ASSERT_EQ (1, lc.m_corrections.length ());
2531 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2532 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2533 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2535 /* After the second replacement hint, they are printed together
2536 as a replacement (along with the text between them). */
2537 lc.add_hint (hint_1);
2538 ASSERT_EQ (1, lc.m_corrections.length ());
2539 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2540 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2541 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2543 /* After the final insertion hint, they are all printed together
2544 as a replacement (along with the text between them). */
2545 lc.add_hint (hint_2);
2546 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2547 lc.m_corrections[0]->m_text);
2548 ASSERT_EQ (1, lc.m_corrections.length ());
2549 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2550 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2553 /* Example where two are consolidated during printing. */
2555 test_diagnostic_context dc;
2556 rich_location richloc (line_table, expr);
2557 richloc.add_fixit_replace (open_paren, "CAST (");
2558 richloc.add_fixit_replace (close_paren, ") (");
2559 richloc.add_fixit_insert_after (")");
2561 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2562 ASSERT_STREQ ("\n"
2563 " foo *f = (foo *)ptr->field;\n"
2564 " ^~~~~~~~~~\n"
2565 " -\n"
2566 " CAST (-\n"
2567 " ) ( )\n",
2568 pp_formatted_text (dc.printer));
2571 /* Example where none are consolidated during printing. */
2573 test_diagnostic_context dc;
2574 rich_location richloc (line_table, expr);
2575 richloc.add_fixit_replace (open_paren, "CST (");
2576 richloc.add_fixit_replace (close_paren, ") (");
2577 richloc.add_fixit_insert_after (")");
2579 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2580 ASSERT_STREQ ("\n"
2581 " foo *f = (foo *)ptr->field;\n"
2582 " ^~~~~~~~~~\n"
2583 " -\n"
2584 " CST ( -\n"
2585 " ) ( )\n",
2586 pp_formatted_text (dc.printer));
2589 /* Example of deletion fix-it hints. */
2591 test_diagnostic_context dc;
2592 rich_location richloc (line_table, expr);
2593 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2594 source_range victim = {open_paren, close_paren};
2595 richloc.add_fixit_remove (victim);
2597 /* This case is actually handled by fixit-consolidation,
2598 rather than by line_corrections. */
2599 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2601 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2602 ASSERT_STREQ ("\n"
2603 " foo *f = (foo *)ptr->field;\n"
2604 " ^~~~~~~~~~\n"
2605 " -------\n"
2606 " (bar *)\n",
2607 pp_formatted_text (dc.printer));
2610 /* Example of deletion fix-it hints that would overlap. */
2612 test_diagnostic_context dc;
2613 rich_location richloc (line_table, expr);
2614 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2615 source_range victim = {expr_start, expr_finish};
2616 richloc.add_fixit_remove (victim);
2618 /* These fixits are not consolidated. */
2619 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2621 /* But the corrections are. */
2622 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2623 ASSERT_STREQ ("\n"
2624 " foo *f = (foo *)ptr->field;\n"
2625 " ^~~~~~~~~~\n"
2626 " -----------------\n"
2627 " (longer *)(foo *)\n",
2628 pp_formatted_text (dc.printer));
2631 /* Example of insertion fix-it hints that would overlap. */
2633 test_diagnostic_context dc;
2634 rich_location richloc (line_table, expr);
2635 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2636 richloc.add_fixit_insert_after (close_paren, "TEST");
2638 /* The first insertion is long enough that if printed naively,
2639 it would overlap with the second.
2640 Verify that they are printed as a single replacement. */
2641 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2642 ASSERT_STREQ ("\n"
2643 " foo *f = (foo *)ptr->field;\n"
2644 " ^~~~~~~~~~\n"
2645 " -------\n"
2646 " LONGER THAN THE CAST(foo *)TEST\n",
2647 pp_formatted_text (dc.printer));
2651 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2653 static void
2654 test_fixit_insert_containing_newline (const line_table_case &case_)
2656 /* Create a tempfile and write some text to it.
2657 .........................0000000001111111.
2658 .........................1234567890123456. */
2659 const char *old_content = (" case 'a':\n" /* line 1. */
2660 " x = a;\n" /* line 2. */
2661 " case 'b':\n" /* line 3. */
2662 " x = b;\n");/* line 4. */
2664 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2665 line_table_test ltt (case_);
2666 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2668 location_t case_start = linemap_position_for_column (line_table, 5);
2669 location_t case_finish = linemap_position_for_column (line_table, 13);
2670 location_t case_loc = make_location (case_start, case_start, case_finish);
2671 location_t line_start = linemap_position_for_column (line_table, 1);
2673 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2674 return;
2676 /* Add a "break;" on a line by itself before line 3 i.e. before
2677 column 1 of line 3. */
2679 rich_location richloc (line_table, case_loc);
2680 richloc.add_fixit_insert_before (line_start, " break;\n");
2681 test_diagnostic_context dc;
2682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2683 ASSERT_STREQ ("\n"
2684 "+ break;\n"
2685 " case 'b':\n"
2686 " ^~~~~~~~~\n",
2687 pp_formatted_text (dc.printer));
2690 /* Verify that attempts to add text with a newline fail when the
2691 insertion point is *not* at the start of a line. */
2693 rich_location richloc (line_table, case_loc);
2694 richloc.add_fixit_insert_before (case_start, "break;\n");
2695 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2696 test_diagnostic_context dc;
2697 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2698 ASSERT_STREQ ("\n"
2699 " case 'b':\n"
2700 " ^~~~~~~~~\n",
2701 pp_formatted_text (dc.printer));
2705 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
2706 of the file, where the fix-it is printed in a different line-span
2707 to the primary range of the diagnostic. */
2709 static void
2710 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
2712 /* Create a tempfile and write some text to it.
2713 .........................0000000001111111.
2714 .........................1234567890123456. */
2715 const char *old_content = ("test (int ch)\n" /* line 1. */
2716 "{\n" /* line 2. */
2717 " putchar (ch);\n" /* line 3. */
2718 "}\n"); /* line 4. */
2720 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2721 line_table_test ltt (case_);
2723 const line_map_ordinary *ord_map = linemap_check_ordinary
2724 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
2725 linemap_line_start (line_table, 1, 100);
2727 /* The primary range is the "putchar" token. */
2728 location_t putchar_start
2729 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
2730 location_t putchar_finish
2731 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
2732 location_t putchar_loc
2733 = make_location (putchar_start, putchar_start, putchar_finish);
2734 rich_location richloc (line_table, putchar_loc);
2736 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
2737 location_t file_start
2738 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2739 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
2741 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2742 return;
2744 test_diagnostic_context dc;
2745 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2746 ASSERT_STREQ ("\n"
2747 "FILENAME:1:1:\n"
2748 "+#include <stdio.h>\n"
2749 " test (int ch)\n"
2750 "FILENAME:3:2:\n"
2751 " putchar (ch);\n"
2752 " ^~~~~~~\n",
2753 pp_formatted_text (dc.printer));
2756 /* Replacement fix-it hint containing a newline.
2757 This will fail, as newlines are only supported when inserting at the
2758 beginning of a line. */
2760 static void
2761 test_fixit_replace_containing_newline (const line_table_case &case_)
2763 /* Create a tempfile and write some text to it.
2764 .........................0000000001111.
2765 .........................1234567890123. */
2766 const char *old_content = "foo = bar ();\n";
2768 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2769 line_table_test ltt (case_);
2770 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2772 /* Replace the " = " with "\n = ", as if we were reformatting an
2773 overly long line. */
2774 location_t start = linemap_position_for_column (line_table, 4);
2775 location_t finish = linemap_position_for_column (line_table, 6);
2776 location_t loc = linemap_position_for_column (line_table, 13);
2777 rich_location richloc (line_table, loc);
2778 source_range range = source_range::from_locations (start, finish);
2779 richloc.add_fixit_replace (range, "\n =");
2781 /* Arbitrary newlines are not yet supported within fix-it hints, so
2782 the fix-it should not be displayed. */
2783 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2785 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2786 return;
2788 test_diagnostic_context dc;
2789 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2790 ASSERT_STREQ ("\n"
2791 " foo = bar ();\n"
2792 " ^\n",
2793 pp_formatted_text (dc.printer));
2796 /* Run all of the selftests within this file. */
2798 void
2799 diagnostic_show_locus_c_tests ()
2801 test_layout_range_for_single_point ();
2802 test_layout_range_for_single_line ();
2803 test_layout_range_for_multiple_lines ();
2805 test_get_line_width_without_trailing_whitespace ();
2807 test_diagnostic_show_locus_unknown_location ();
2809 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
2810 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
2811 for_each_line_table_case (test_fixit_consolidation);
2812 for_each_line_table_case (test_overlapped_fixit_printing);
2813 for_each_line_table_case (test_fixit_insert_containing_newline);
2814 for_each_line_table_case (test_fixit_insert_containing_newline_2);
2815 for_each_line_table_case (test_fixit_replace_containing_newline);
2818 } // namespace selftest
2820 #endif /* #if CHECKING_P */