* i386.c (has_dispatch): Disable for Ryzen.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob35121117f49a1702fb4b023abd9b0b7829afdb5e
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 "gcc-rich-location.h"
31 #include "selftest.h"
33 #ifdef HAVE_TERMIOS_H
34 # include <termios.h>
35 #endif
37 #ifdef GWINSZ_IN_SYS_IOCTL
38 # include <sys/ioctl.h>
39 #endif
41 /* Classes for rendering source code and diagnostics, within an
42 anonymous namespace.
43 The work is done by "class layout", which embeds and uses
44 "class colorizer" and "class layout_range" to get things done. */
46 namespace {
48 /* The state at a given point of the source code, assuming that we're
49 in a range: which range are we in, and whether we should draw a caret at
50 this point. */
52 struct point_state
54 int range_idx;
55 bool draw_caret_p;
58 /* A class to inject colorization codes when printing the diagnostic locus.
60 It has one kind of colorization for each of:
61 - normal text
62 - range 0 (the "primary location")
63 - range 1
64 - range 2
66 The class caches the lookup of the color codes for the above.
68 The class also has responsibility for tracking which of the above is
69 active, filtering out unnecessary changes. This allows
70 layout::print_source_line and layout::print_annotation_line
71 to simply request a colorization code for *every* character they print,
72 via this class, and have the filtering be done for them here. */
74 class colorizer
76 public:
77 colorizer (diagnostic_context *context,
78 diagnostic_t diagnostic_kind);
79 ~colorizer ();
81 void set_range (int range_idx) { set_state (range_idx); }
82 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
83 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
84 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
86 private:
87 void set_state (int state);
88 void begin_state (int state);
89 void finish_state (int state);
90 const char *get_color_by_name (const char *);
92 private:
93 static const int STATE_NORMAL_TEXT = -1;
94 static const int STATE_FIXIT_INSERT = -2;
95 static const int STATE_FIXIT_DELETE = -3;
97 diagnostic_context *m_context;
98 diagnostic_t m_diagnostic_kind;
99 int m_current_state;
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 bool maybe_add_location_range (const location_range *loc_range,
200 bool restrict_to_current_line_spans);
202 int get_num_line_spans () const { return m_line_spans.length (); }
203 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
205 bool print_heading_for_line_span_index_p (int line_span_idx) const;
207 expanded_location get_expanded_location (const line_span *) const;
209 void print_line (int row);
211 private:
212 bool will_show_line_p (int row) const;
213 void print_leading_fixits (int row);
214 void print_source_line (int row, const char *line, int line_width,
215 line_bounds *lbounds_out);
216 bool should_print_annotation_line_p (int row) const;
217 void print_annotation_line (int row, const line_bounds lbounds);
218 void print_trailing_fixits (int row);
220 bool annotation_line_showed_range_p (int line, int start_column,
221 int finish_column) const;
222 void show_ruler (int max_column) const;
224 bool validate_fixit_hint_p (const fixit_hint *hint);
226 void calculate_line_spans ();
228 void print_newline ();
230 bool
231 get_state_at_point (/* Inputs. */
232 int row, int column,
233 int first_non_ws, int last_non_ws,
234 /* Outputs. */
235 point_state *out_state);
238 get_x_bound_for_row (int row, int caret_column,
239 int last_non_ws);
241 void
242 move_to_column (int *column, int dest_column);
244 private:
245 diagnostic_context *m_context;
246 pretty_printer *m_pp;
247 diagnostic_t m_diagnostic_kind;
248 location_t m_primary_loc;
249 expanded_location m_exploc;
250 colorizer m_colorizer;
251 bool m_colorize_source_p;
252 auto_vec <layout_range> m_layout_ranges;
253 auto_vec <const fixit_hint *> m_fixit_hints;
254 auto_vec <line_span> m_line_spans;
255 int m_x_offset;
258 /* Implementation of "class colorizer". */
260 /* The constructor for "colorizer". Lookup and store color codes for the
261 different kinds of things we might need to print. */
263 colorizer::colorizer (diagnostic_context *context,
264 diagnostic_t diagnostic_kind) :
265 m_context (context),
266 m_diagnostic_kind (diagnostic_kind),
267 m_current_state (STATE_NORMAL_TEXT)
269 m_range1 = get_color_by_name ("range1");
270 m_range2 = get_color_by_name ("range2");
271 m_fixit_insert = get_color_by_name ("fixit-insert");
272 m_fixit_delete = get_color_by_name ("fixit-delete");
273 m_stop_color = colorize_stop (pp_show_color (context->printer));
276 /* The destructor for "colorize". If colorization is on, print a code to
277 turn it off. */
279 colorizer::~colorizer ()
281 finish_state (m_current_state);
284 /* Update state, printing color codes if necessary if there's a state
285 change. */
287 void
288 colorizer::set_state (int new_state)
290 if (m_current_state != new_state)
292 finish_state (m_current_state);
293 m_current_state = new_state;
294 begin_state (new_state);
298 /* Turn on any colorization for STATE. */
300 void
301 colorizer::begin_state (int state)
303 switch (state)
305 case STATE_NORMAL_TEXT:
306 break;
308 case STATE_FIXIT_INSERT:
309 pp_string (m_context->printer, m_fixit_insert);
310 break;
312 case STATE_FIXIT_DELETE:
313 pp_string (m_context->printer, m_fixit_delete);
314 break;
316 case 0:
317 /* Make range 0 be the same color as the "kind" text
318 (error vs warning vs note). */
319 pp_string
320 (m_context->printer,
321 colorize_start (pp_show_color (m_context->printer),
322 diagnostic_get_color_for_kind (m_diagnostic_kind)));
323 break;
325 case 1:
326 pp_string (m_context->printer, m_range1);
327 break;
329 case 2:
330 pp_string (m_context->printer, m_range2);
331 break;
333 default:
334 /* For ranges beyond 2, alternate between color 1 and color 2. */
336 gcc_assert (state > 2);
337 pp_string (m_context->printer,
338 state % 2 ? m_range1 : m_range2);
340 break;
344 /* Turn off any colorization for STATE. */
346 void
347 colorizer::finish_state (int state)
349 if (state != STATE_NORMAL_TEXT)
350 pp_string (m_context->printer, m_stop_color);
353 /* Get the color code for NAME (or the empty string if
354 colorization is disabled). */
356 const char *
357 colorizer::get_color_by_name (const char *name)
359 return colorize_start (pp_show_color (m_context->printer), name);
362 /* Implementation of class layout_range. */
364 /* The constructor for class layout_range.
365 Initialize various layout_point fields from expanded_location
366 equivalents; we've already filtered on file. */
368 layout_range::layout_range (const expanded_location *start_exploc,
369 const expanded_location *finish_exploc,
370 bool show_caret_p,
371 const expanded_location *caret_exploc)
372 : m_start (*start_exploc),
373 m_finish (*finish_exploc),
374 m_show_caret_p (show_caret_p),
375 m_caret (*caret_exploc)
379 /* Is (column, row) within the given range?
380 We've already filtered on the file.
382 Ranges are closed (both limits are within the range).
384 Example A: a single-line range:
385 start: (col=22, line=2)
386 finish: (col=38, line=2)
388 |00000011111111112222222222333333333344444444444
389 |34567890123456789012345678901234567890123456789
390 --+-----------------------------------------------
391 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
392 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
393 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
395 Example B: a multiline range with
396 start: (col=14, line=3)
397 finish: (col=08, line=5)
399 |00000011111111112222222222333333333344444444444
400 |34567890123456789012345678901234567890123456789
401 --+-----------------------------------------------
402 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
403 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
404 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
405 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
406 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
407 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
408 --+-----------------------------------------------
410 Legend:
411 - 'b' indicates a point *before* the range
412 - 'S' indicates the start of the range
413 - 'w' indicates a point within the range
414 - 'F' indicates the finish of the range (which is
415 within it).
416 - 'a' indicates a subsequent point *after* the range. */
418 bool
419 layout_range::contains_point (int row, int column) const
421 gcc_assert (m_start.m_line <= m_finish.m_line);
422 /* ...but the equivalent isn't true for the columns;
423 consider example B in the comment above. */
425 if (row < m_start.m_line)
426 /* Points before the first line of the range are
427 outside it (corresponding to line 01 in example A
428 and lines 01 and 02 in example B above). */
429 return false;
431 if (row == m_start.m_line)
432 /* On same line as start of range (corresponding
433 to line 02 in example A and line 03 in example B). */
435 if (column < m_start.m_column)
436 /* Points on the starting line of the range, but
437 before the column in which it begins. */
438 return false;
440 if (row < m_finish.m_line)
441 /* This is a multiline range; the point
442 is within it (corresponds to line 03 in example B
443 from column 14 onwards) */
444 return true;
445 else
447 /* This is a single-line range. */
448 gcc_assert (row == m_finish.m_line);
449 return column <= m_finish.m_column;
453 /* The point is in a line beyond that containing the
454 start of the range: lines 03 onwards in example A,
455 and lines 04 onwards in example B. */
456 gcc_assert (row > m_start.m_line);
458 if (row > m_finish.m_line)
459 /* The point is beyond the final line of the range
460 (lines 03 onwards in example A, and lines 06 onwards
461 in example B). */
462 return false;
464 if (row < m_finish.m_line)
466 /* The point is in a line that's fully within a multiline
467 range (e.g. line 04 in example B). */
468 gcc_assert (m_start.m_line < m_finish.m_line);
469 return true;
472 gcc_assert (row == m_finish.m_line);
474 return column <= m_finish.m_column;
477 /* Does this layout_range contain any part of line ROW? */
479 bool
480 layout_range::intersects_line_p (int row) const
482 gcc_assert (m_start.m_line <= m_finish.m_line);
483 if (row < m_start.m_line)
484 return false;
485 if (row > m_finish.m_line)
486 return false;
487 return true;
490 #if CHECKING_P
492 /* A helper function for testing layout_range. */
494 static layout_range
495 make_range (int start_line, int start_col, int end_line, int end_col)
497 const expanded_location start_exploc
498 = {"test.c", start_line, start_col, NULL, false};
499 const expanded_location finish_exploc
500 = {"test.c", end_line, end_col, NULL, false};
501 return layout_range (&start_exploc, &finish_exploc, false,
502 &start_exploc);
505 /* Selftests for layout_range::contains_point and
506 layout_range::intersects_line_p. */
508 /* Selftest for layout_range, where the layout_range
509 is a range with start==end i.e. a single point. */
511 static void
512 test_layout_range_for_single_point ()
514 layout_range point = make_range (7, 10, 7, 10);
516 /* Tests for layout_range::contains_point. */
518 /* Before the line. */
519 ASSERT_FALSE (point.contains_point (6, 1));
521 /* On the line, but before start. */
522 ASSERT_FALSE (point.contains_point (7, 9));
524 /* At the point. */
525 ASSERT_TRUE (point.contains_point (7, 10));
527 /* On the line, after the point. */
528 ASSERT_FALSE (point.contains_point (7, 11));
530 /* After the line. */
531 ASSERT_FALSE (point.contains_point (8, 1));
533 /* Tests for layout_range::intersects_line_p. */
534 ASSERT_FALSE (point.intersects_line_p (6));
535 ASSERT_TRUE (point.intersects_line_p (7));
536 ASSERT_FALSE (point.intersects_line_p (8));
539 /* Selftest for layout_range, where the layout_range
540 is the single-line range shown as "Example A" above. */
542 static void
543 test_layout_range_for_single_line ()
545 layout_range example_a = make_range (2, 22, 2, 38);
547 /* Tests for layout_range::contains_point. */
549 /* Before the line. */
550 ASSERT_FALSE (example_a.contains_point (1, 1));
552 /* On the line, but before start. */
553 ASSERT_FALSE (example_a.contains_point (2, 21));
555 /* On the line, at the start. */
556 ASSERT_TRUE (example_a.contains_point (2, 22));
558 /* On the line, within the range. */
559 ASSERT_TRUE (example_a.contains_point (2, 23));
561 /* On the line, at the end. */
562 ASSERT_TRUE (example_a.contains_point (2, 38));
564 /* On the line, after the end. */
565 ASSERT_FALSE (example_a.contains_point (2, 39));
567 /* After the line. */
568 ASSERT_FALSE (example_a.contains_point (2, 39));
570 /* Tests for layout_range::intersects_line_p. */
571 ASSERT_FALSE (example_a.intersects_line_p (1));
572 ASSERT_TRUE (example_a.intersects_line_p (2));
573 ASSERT_FALSE (example_a.intersects_line_p (3));
576 /* Selftest for layout_range, where the layout_range
577 is the multi-line range shown as "Example B" above. */
579 static void
580 test_layout_range_for_multiple_lines ()
582 layout_range example_b = make_range (3, 14, 5, 8);
584 /* Tests for layout_range::contains_point. */
586 /* Before first line. */
587 ASSERT_FALSE (example_b.contains_point (1, 1));
589 /* On the first line, but before start. */
590 ASSERT_FALSE (example_b.contains_point (3, 13));
592 /* At the start. */
593 ASSERT_TRUE (example_b.contains_point (3, 14));
595 /* On the first line, within the range. */
596 ASSERT_TRUE (example_b.contains_point (3, 15));
598 /* On an interior line.
599 The column number should not matter; try various boundary
600 values. */
601 ASSERT_TRUE (example_b.contains_point (4, 1));
602 ASSERT_TRUE (example_b.contains_point (4, 7));
603 ASSERT_TRUE (example_b.contains_point (4, 8));
604 ASSERT_TRUE (example_b.contains_point (4, 9));
605 ASSERT_TRUE (example_b.contains_point (4, 13));
606 ASSERT_TRUE (example_b.contains_point (4, 14));
607 ASSERT_TRUE (example_b.contains_point (4, 15));
609 /* On the final line, before the end. */
610 ASSERT_TRUE (example_b.contains_point (5, 7));
612 /* On the final line, at the end. */
613 ASSERT_TRUE (example_b.contains_point (5, 8));
615 /* On the final line, after the end. */
616 ASSERT_FALSE (example_b.contains_point (5, 9));
618 /* After the line. */
619 ASSERT_FALSE (example_b.contains_point (6, 1));
621 /* Tests for layout_range::intersects_line_p. */
622 ASSERT_FALSE (example_b.intersects_line_p (2));
623 ASSERT_TRUE (example_b.intersects_line_p (3));
624 ASSERT_TRUE (example_b.intersects_line_p (4));
625 ASSERT_TRUE (example_b.intersects_line_p (5));
626 ASSERT_FALSE (example_b.intersects_line_p (6));
629 #endif /* #if CHECKING_P */
631 /* Given a source line LINE of length LINE_WIDTH, determine the width
632 without any trailing whitespace. */
634 static int
635 get_line_width_without_trailing_whitespace (const char *line, int line_width)
637 int result = line_width;
638 while (result > 0)
640 char ch = line[result - 1];
641 if (ch == ' ' || ch == '\t')
642 result--;
643 else
644 break;
646 gcc_assert (result >= 0);
647 gcc_assert (result <= line_width);
648 gcc_assert (result == 0 ||
649 (line[result - 1] != ' '
650 && line[result -1] != '\t'));
651 return result;
654 #if CHECKING_P
656 /* A helper function for testing get_line_width_without_trailing_whitespace. */
658 static void
659 assert_eq (const char *line, int expected_width)
661 int actual_value
662 = get_line_width_without_trailing_whitespace (line, strlen (line));
663 ASSERT_EQ (actual_value, expected_width);
666 /* Verify that get_line_width_without_trailing_whitespace is sane for
667 various inputs. It is not required to handle newlines. */
669 static void
670 test_get_line_width_without_trailing_whitespace ()
672 assert_eq ("", 0);
673 assert_eq (" ", 0);
674 assert_eq ("\t", 0);
675 assert_eq ("hello world", 11);
676 assert_eq ("hello world ", 11);
677 assert_eq ("hello world \t\t ", 11);
680 #endif /* #if CHECKING_P */
682 /* Helper function for layout's ctor, for sanitizing locations relative
683 to the primary location within a diagnostic.
685 Compare LOC_A and LOC_B to see if it makes sense to print underlines
686 connecting their expanded locations. Doing so is only guaranteed to
687 make sense if the locations share the same macro expansion "history"
688 i.e. they can be traced through the same macro expansions, eventually
689 reaching an ordinary map.
691 This may be too strong a condition, but it effectively sanitizes
692 PR c++/70105, which has an example of printing an expression where the
693 final location of the expression is in a different macro, which
694 erroneously was leading to hundreds of lines of irrelevant source
695 being printed. */
697 static bool
698 compatible_locations_p (location_t loc_a, location_t loc_b)
700 if (IS_ADHOC_LOC (loc_a))
701 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
702 if (IS_ADHOC_LOC (loc_b))
703 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
705 /* If either location is one of the special locations outside of a
706 linemap, they are only compatible if they are equal. */
707 if (loc_a < RESERVED_LOCATION_COUNT
708 || loc_b < RESERVED_LOCATION_COUNT)
709 return loc_a == loc_b;
711 const line_map *map_a = linemap_lookup (line_table, loc_a);
712 linemap_assert (map_a);
714 const line_map *map_b = linemap_lookup (line_table, loc_b);
715 linemap_assert (map_b);
717 /* Are they within the same map? */
718 if (map_a == map_b)
720 /* Are both within the same macro expansion? */
721 if (linemap_macro_expansion_map_p (map_a))
723 /* Expand each location towards the spelling location, and
724 recurse. */
725 const line_map_macro *macro_map = linemap_check_macro (map_a);
726 source_location loc_a_toward_spelling
727 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
728 macro_map,
729 loc_a);
730 source_location loc_b_toward_spelling
731 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
732 macro_map,
733 loc_b);
734 return compatible_locations_p (loc_a_toward_spelling,
735 loc_b_toward_spelling);
738 /* Otherwise they are within the same ordinary map. */
739 return true;
741 else
743 /* Within different maps. */
745 /* If either is within a macro expansion, they are incompatible. */
746 if (linemap_macro_expansion_map_p (map_a)
747 || linemap_macro_expansion_map_p (map_b))
748 return false;
750 /* Within two different ordinary maps; they are compatible iff they
751 are in the same file. */
752 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
753 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
754 return ord_map_a->to_file == ord_map_b->to_file;
758 /* Comparator for sorting fix-it hints. */
760 static int
761 fixit_cmp (const void *p_a, const void *p_b)
763 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
764 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
765 return hint_a->get_start_loc () - hint_b->get_start_loc ();
768 /* Implementation of class layout. */
770 /* Constructor for class layout.
772 Filter the ranges from the rich_location to those that we can
773 sanely print, populating m_layout_ranges and m_fixit_hints.
774 Determine the range of lines that we will print, splitting them
775 up into an ordered list of disjoint spans of contiguous line numbers.
776 Determine m_x_offset, to ensure that the primary caret
777 will fit within the max_width provided by the diagnostic_context. */
779 layout::layout (diagnostic_context * context,
780 rich_location *richloc,
781 diagnostic_t diagnostic_kind)
782 : m_context (context),
783 m_pp (context->printer),
784 m_diagnostic_kind (diagnostic_kind),
785 m_primary_loc (richloc->get_range (0)->m_loc),
786 m_exploc (richloc->get_expanded_location (0)),
787 m_colorizer (context, diagnostic_kind),
788 m_colorize_source_p (context->colorize_source_p),
789 m_layout_ranges (richloc->get_num_locations ()),
790 m_fixit_hints (richloc->get_num_fixit_hints ()),
791 m_line_spans (1 + richloc->get_num_locations ()),
792 m_x_offset (0)
794 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
796 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
797 Ignore any ranges that are awkward to handle. */
798 const location_range *loc_range = richloc->get_range (idx);
799 maybe_add_location_range (loc_range, false);
802 /* Populate m_fixit_hints, filtering to only those that are in the
803 same file. */
804 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
806 const fixit_hint *hint = richloc->get_fixit_hint (i);
807 if (validate_fixit_hint_p (hint))
808 m_fixit_hints.safe_push (hint);
811 /* Sort m_fixit_hints. */
812 m_fixit_hints.qsort (fixit_cmp);
814 /* Populate m_line_spans. */
815 calculate_line_spans ();
817 /* Adjust m_x_offset.
818 Center the primary caret to fit in max_width; all columns
819 will be adjusted accordingly. */
820 int max_width = m_context->caret_max_width;
821 int line_width;
822 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
823 &line_width);
824 if (line && m_exploc.column <= line_width)
826 int right_margin = CARET_LINE_MARGIN;
827 int column = m_exploc.column;
828 right_margin = MIN (line_width - column, right_margin);
829 right_margin = max_width - right_margin;
830 if (line_width >= max_width && column > right_margin)
831 m_x_offset = column - right_margin;
832 gcc_assert (m_x_offset >= 0);
835 if (context->show_ruler_p)
836 show_ruler (m_x_offset + max_width);
839 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
840 those that we can sanely print.
842 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
843 filtered against this layout instance's current line spans: it
844 will only be added if the location is fully within the lines
845 already specified by other locations.
847 Return true iff LOC_RANGE was added. */
849 bool
850 layout::maybe_add_location_range (const location_range *loc_range,
851 bool restrict_to_current_line_spans)
853 gcc_assert (loc_range);
855 /* Split the "range" into caret and range information. */
856 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
858 /* Expand the various locations. */
859 expanded_location start
860 = linemap_client_expand_location_to_spelling_point
861 (src_range.m_start, LOCATION_ASPECT_START);
862 expanded_location finish
863 = linemap_client_expand_location_to_spelling_point
864 (src_range.m_finish, LOCATION_ASPECT_FINISH);
865 expanded_location caret
866 = linemap_client_expand_location_to_spelling_point
867 (loc_range->m_loc, LOCATION_ASPECT_CARET);
869 /* If any part of the range isn't in the same file as the primary
870 location of this diagnostic, ignore the range. */
871 if (start.file != m_exploc.file)
872 return false;
873 if (finish.file != m_exploc.file)
874 return false;
875 if (loc_range->m_show_caret_p)
876 if (caret.file != m_exploc.file)
877 return false;
879 /* Sanitize the caret location for non-primary ranges. */
880 if (m_layout_ranges.length () > 0)
881 if (loc_range->m_show_caret_p)
882 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
883 /* Discard any non-primary ranges that can't be printed
884 sanely relative to the primary location. */
885 return false;
887 /* Everything is now known to be in the correct source file,
888 but it may require further sanitization. */
889 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
891 /* If we have a range that finishes before it starts (perhaps
892 from something built via macro expansion), printing the
893 range is likely to be nonsensical. Also, attempting to do so
894 breaks assumptions within the printing code (PR c/68473).
895 Similarly, don't attempt to print ranges if one or both ends
896 of the range aren't sane to print relative to the
897 primary location (PR c++/70105). */
898 if (start.line > finish.line
899 || !compatible_locations_p (src_range.m_start, m_primary_loc)
900 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
902 /* Is this the primary location? */
903 if (m_layout_ranges.length () == 0)
905 /* We want to print the caret for the primary location, but
906 we must sanitize away m_start and m_finish. */
907 ri.m_start = ri.m_caret;
908 ri.m_finish = ri.m_caret;
910 else
911 /* This is a non-primary range; ignore it. */
912 return false;
915 /* Potentially filter to just the lines already specified by other
916 locations. This is for use by gcc_rich_location::add_location_if_nearby.
917 The layout ctor doesn't use it, and can't because m_line_spans
918 hasn't been set up at that point. */
919 if (restrict_to_current_line_spans)
921 if (!will_show_line_p (start.line))
922 return false;
923 if (!will_show_line_p (finish.line))
924 return false;
925 if (loc_range->m_show_caret_p)
926 if (!will_show_line_p (caret.line))
927 return false;
930 /* Passed all the tests; add the range to m_layout_ranges so that
931 it will be printed. */
932 m_layout_ranges.safe_push (ri);
933 return true;
936 /* Return true iff ROW is within one of the line spans for this layout. */
938 bool
939 layout::will_show_line_p (int row) const
941 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
942 line_span_idx++)
944 const line_span *line_span = get_line_span (line_span_idx);
945 if (line_span->contains_line_p (row))
946 return true;
948 return false;
951 /* Return true iff we should print a heading when starting the
952 line span with the given index. */
954 bool
955 layout::print_heading_for_line_span_index_p (int line_span_idx) const
957 /* We print a heading for every change of line span, hence for every
958 line span after the initial one. */
959 if (line_span_idx > 0)
960 return true;
962 /* We also do it for the initial span if the primary location of the
963 diagnostic is in a different span. */
964 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
965 return true;
967 return false;
970 /* Get an expanded_location for the first location of interest within
971 the given line_span.
972 Used when printing a heading to indicate a new line span. */
974 expanded_location
975 layout::get_expanded_location (const line_span *line_span) const
977 /* Whenever possible, use the caret location. */
978 if (line_span->contains_line_p (m_exploc.line))
979 return m_exploc;
981 /* Otherwise, use the start of the first range that's present
982 within the line_span. */
983 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
985 const layout_range *lr = &m_layout_ranges[i];
986 if (line_span->contains_line_p (lr->m_start.m_line))
988 expanded_location exploc = m_exploc;
989 exploc.line = lr->m_start.m_line;
990 exploc.column = lr->m_start.m_column;
991 return exploc;
995 /* Otherwise, use the location of the first fixit-hint present within
996 the line_span. */
997 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
999 const fixit_hint *hint = m_fixit_hints[i];
1000 location_t loc = hint->get_start_loc ();
1001 expanded_location exploc = expand_location (loc);
1002 if (line_span->contains_line_p (exploc.line))
1003 return exploc;
1006 /* It should not be possible to have a line span that didn't
1007 contain any of the layout_range or fixit_hint instances. */
1008 gcc_unreachable ();
1009 return m_exploc;
1012 /* Determine if HINT is meaningful to print within this layout. */
1014 bool
1015 layout::validate_fixit_hint_p (const fixit_hint *hint)
1017 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1018 return false;
1019 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1020 return false;
1022 return true;
1025 /* Determine the range of lines affected by HINT.
1026 This assumes that HINT has already been filtered by
1027 validate_fixit_hint_p, and so affects the correct source file. */
1029 static line_span
1030 get_line_span_for_fixit_hint (const fixit_hint *hint)
1032 gcc_assert (hint);
1033 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1034 LOCATION_LINE (hint->get_next_loc ()));
1037 /* We want to print the pertinent source code at a diagnostic. The
1038 rich_location can contain multiple locations. This will have been
1039 filtered into m_exploc (the caret for the primary location) and
1040 m_layout_ranges, for those ranges within the same source file.
1042 We will print a subset of the lines within the source file in question,
1043 as a collection of "spans" of lines.
1045 This function populates m_line_spans with an ordered, disjoint list of
1046 the line spans of interest.
1048 For example, if the primary caret location is on line 7, with ranges
1049 covering lines 5-6 and lines 9-12:
1052 005 |RANGE 0
1053 006 |RANGE 0
1054 007 |PRIMARY CARET
1056 009 |RANGE 1
1057 010 |RANGE 1
1058 011 |RANGE 1
1059 012 |RANGE 1
1062 then we want two spans: lines 5-7 and lines 9-12. */
1064 void
1065 layout::calculate_line_spans ()
1067 /* This should only be called once, by the ctor. */
1068 gcc_assert (m_line_spans.length () == 0);
1070 /* Populate tmp_spans with individual spans, for each of
1071 m_exploc, and for m_layout_ranges. */
1072 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1073 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1074 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1076 const layout_range *lr = &m_layout_ranges[i];
1077 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1078 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1079 lr->m_finish.m_line));
1082 /* Also add spans for any fix-it hints, in case they cover other lines. */
1083 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1085 const fixit_hint *hint = m_fixit_hints[i];
1086 gcc_assert (hint);
1087 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1090 /* Sort them. */
1091 tmp_spans.qsort(line_span::comparator);
1093 /* Now iterate through tmp_spans, copying into m_line_spans, and
1094 combining where possible. */
1095 gcc_assert (tmp_spans.length () > 0);
1096 m_line_spans.safe_push (tmp_spans[0]);
1097 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1099 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1100 const line_span *next = &tmp_spans[i];
1101 gcc_assert (next->m_first_line >= current->m_first_line);
1102 if (next->m_first_line <= current->m_last_line + 1)
1104 /* We can merge them. */
1105 if (next->m_last_line > current->m_last_line)
1106 current->m_last_line = next->m_last_line;
1108 else
1110 /* No merger possible. */
1111 m_line_spans.safe_push (*next);
1115 /* Verify the result, in m_line_spans. */
1116 gcc_assert (m_line_spans.length () > 0);
1117 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1119 const line_span *prev = &m_line_spans[i - 1];
1120 const line_span *next = &m_line_spans[i];
1121 /* The individual spans must be sane. */
1122 gcc_assert (prev->m_first_line <= prev->m_last_line);
1123 gcc_assert (next->m_first_line <= next->m_last_line);
1124 /* The spans must be ordered. */
1125 gcc_assert (prev->m_first_line < next->m_first_line);
1126 /* There must be a gap of at least one line between separate spans. */
1127 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1131 /* Print line ROW of source code, potentially colorized at any ranges, and
1132 populate *LBOUNDS_OUT.
1133 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1134 is its width. */
1136 void
1137 layout::print_source_line (int row, const char *line, int line_width,
1138 line_bounds *lbounds_out)
1140 m_colorizer.set_normal_text ();
1142 /* We will stop printing the source line at any trailing
1143 whitespace. */
1144 line_width = get_line_width_without_trailing_whitespace (line,
1145 line_width);
1146 line += m_x_offset;
1148 pp_space (m_pp);
1149 int first_non_ws = INT_MAX;
1150 int last_non_ws = 0;
1151 int column;
1152 for (column = 1 + m_x_offset; column <= line_width; column++)
1154 /* Assuming colorization is enabled for the caret and underline
1155 characters, we may also colorize the associated characters
1156 within the source line.
1158 For frontends that generate range information, we color the
1159 associated characters in the source line the same as the
1160 carets and underlines in the annotation line, to make it easier
1161 for the reader to see the pertinent code.
1163 For frontends that only generate carets, we don't colorize the
1164 characters above them, since this would look strange (e.g.
1165 colorizing just the first character in a token). */
1166 if (m_colorize_source_p)
1168 bool in_range_p;
1169 point_state state;
1170 in_range_p = get_state_at_point (row, column,
1171 0, INT_MAX,
1172 &state);
1173 if (in_range_p)
1174 m_colorizer.set_range (state.range_idx);
1175 else
1176 m_colorizer.set_normal_text ();
1178 char c = *line == '\t' ? ' ' : *line;
1179 if (c == '\0')
1180 c = ' ';
1181 if (c != ' ')
1183 last_non_ws = column;
1184 if (first_non_ws == INT_MAX)
1185 first_non_ws = column;
1187 pp_character (m_pp, c);
1188 line++;
1190 print_newline ();
1192 lbounds_out->m_first_non_ws = first_non_ws;
1193 lbounds_out->m_last_non_ws = last_non_ws;
1196 /* Determine if we should print an annotation line for ROW.
1197 i.e. if any of m_layout_ranges contains ROW. */
1199 bool
1200 layout::should_print_annotation_line_p (int row) const
1202 layout_range *range;
1203 int i;
1204 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1205 if (range->intersects_line_p (row))
1206 return true;
1207 return false;
1210 /* Print a line consisting of the caret/underlines for the given
1211 source line. */
1213 void
1214 layout::print_annotation_line (int row, const line_bounds lbounds)
1216 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1217 lbounds.m_last_non_ws);
1219 pp_space (m_pp);
1220 for (int column = 1 + m_x_offset; column < x_bound; column++)
1222 bool in_range_p;
1223 point_state state;
1224 in_range_p = get_state_at_point (row, column,
1225 lbounds.m_first_non_ws,
1226 lbounds.m_last_non_ws,
1227 &state);
1228 if (in_range_p)
1230 /* Within a range. Draw either the caret or an underline. */
1231 m_colorizer.set_range (state.range_idx);
1232 if (state.draw_caret_p)
1234 /* Draw the caret. */
1235 char caret_char;
1236 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1237 caret_char = m_context->caret_chars[state.range_idx];
1238 else
1239 caret_char = '^';
1240 pp_character (m_pp, caret_char);
1242 else
1243 pp_character (m_pp, '~');
1245 else
1247 /* Not in a range. */
1248 m_colorizer.set_normal_text ();
1249 pp_character (m_pp, ' ');
1252 print_newline ();
1255 /* If there are any fixit hints inserting new lines before source line ROW,
1256 print them.
1258 They are printed on lines of their own, before the source line
1259 itself, with a leading '+'. */
1261 void
1262 layout::print_leading_fixits (int row)
1264 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1266 const fixit_hint *hint = m_fixit_hints[i];
1268 if (!hint->ends_with_newline_p ())
1269 /* Not a newline fixit; print it in print_trailing_fixits. */
1270 continue;
1272 gcc_assert (hint->insertion_p ());
1274 if (hint->affects_line_p (m_exploc.file, row))
1276 /* Printing the '+' with normal colorization
1277 and the inserted line with "insert" colorization
1278 helps them stand out from each other, and from
1279 the surrounding text. */
1280 m_colorizer.set_normal_text ();
1281 pp_character (m_pp, '+');
1282 m_colorizer.set_fixit_insert ();
1283 /* Print all but the trailing newline of the fix-it hint.
1284 We have to print the newline separately to avoid
1285 getting additional pp prefixes printed. */
1286 for (size_t i = 0; i < hint->get_length () - 1; i++)
1287 pp_character (m_pp, hint->get_string ()[i]);
1288 m_colorizer.set_normal_text ();
1289 pp_newline (m_pp);
1294 /* Subroutine of layout::print_trailing_fixits.
1296 Determine if the annotation line printed for LINE contained
1297 the exact range from START_COLUMN to FINISH_COLUMN. */
1299 bool
1300 layout::annotation_line_showed_range_p (int line, int start_column,
1301 int finish_column) const
1303 layout_range *range;
1304 int i;
1305 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1306 if (range->m_start.m_line == line
1307 && range->m_start.m_column == start_column
1308 && range->m_finish.m_line == line
1309 && range->m_finish.m_column == finish_column)
1310 return true;
1311 return false;
1314 /* Classes for printing trailing fix-it hints i.e. those that
1315 don't add new lines.
1317 For insertion, these can look like:
1319 new_text
1321 For replacement, these can look like:
1323 ------------- : underline showing affected range
1324 new_text
1326 For deletion, these can look like:
1328 ------------- : underline showing affected range
1330 This can become confusing if they overlap, and so we need
1331 to do some preprocessing to decide what to print.
1332 We use the list of fixit_hint instances affecting the line
1333 to build a list of "correction" instances, and print the
1334 latter.
1336 For example, consider a set of fix-its for converting
1337 a C-style cast to a C++ const_cast.
1339 Given:
1341 ..000000000111111111122222222223333333333.
1342 ..123456789012345678901234567890123456789.
1343 foo *f = (foo *)ptr->field;
1344 ^~~~~
1346 and the fix-it hints:
1347 - replace col 10 (the open paren) with "const_cast<"
1348 - replace col 16 (the close paren) with "> ("
1349 - insert ")" before col 27
1351 then we would get odd-looking output:
1353 foo *f = (foo *)ptr->field;
1354 ^~~~~
1356 const_cast<
1358 > ( )
1360 It would be better to detect when fixit hints are going to
1361 overlap (those that require new lines), and to consolidate
1362 the printing of such fixits, giving something like:
1364 foo *f = (foo *)ptr->field;
1365 ^~~~~
1366 -----------------
1367 const_cast<foo *> (ptr->field)
1369 This works by detecting when the printing would overlap, and
1370 effectively injecting no-op replace hints into the gaps between
1371 such fix-its, so that the printing joins up.
1373 In the above example, the overlap of:
1374 - replace col 10 (the open paren) with "const_cast<"
1375 and:
1376 - replace col 16 (the close paren) with "> ("
1377 is fixed by injecting a no-op:
1378 - replace cols 11-15 with themselves ("foo *")
1379 and consolidating these, making:
1380 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1381 i.e.:
1382 - replace cols 10-16 with "const_cast<foo *> ("
1384 This overlaps with the final fix-it hint:
1385 - insert ")" before col 27
1386 and so we repeat the consolidation process, by injecting
1387 a no-op:
1388 - replace cols 17-26 with themselves ("ptr->field")
1389 giving:
1390 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1391 i.e.:
1392 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1394 and is thus printed as desired. */
1396 /* A range of columns within a line. */
1398 struct column_range
1400 column_range (int start_, int finish_) : start (start_), finish (finish_)
1402 /* We must have either a range, or an insertion. */
1403 gcc_assert (start <= finish || finish == start - 1);
1406 bool operator== (const column_range &other) const
1408 return start == other.start && finish == other.finish;
1411 int start;
1412 int finish;
1415 /* Get the range of columns that HINT would affect. */
1417 static column_range
1418 get_affected_columns (const fixit_hint *hint)
1420 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1421 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1423 return column_range (start_column, finish_column);
1426 /* Get the range of columns that would be printed for HINT. */
1428 static column_range
1429 get_printed_columns (const fixit_hint *hint)
1431 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1432 int final_hint_column = start_column + hint->get_length () - 1;
1433 if (hint->insertion_p ())
1435 return column_range (start_column, final_hint_column);
1437 else
1439 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1441 return column_range (start_column,
1442 MAX (finish_column, final_hint_column));
1446 /* A struct capturing the bounds of a buffer, to allow for run-time
1447 bounds-checking in a checked build. */
1449 struct char_span
1451 char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {}
1453 char_span subspan (int offset, int n_elts)
1455 gcc_assert (offset >= 0);
1456 gcc_assert (offset < (int)m_n_elts);
1457 gcc_assert (n_elts >= 0);
1458 gcc_assert (offset + n_elts <= (int)m_n_elts);
1459 return char_span (m_ptr + offset, n_elts);
1462 const char *m_ptr;
1463 size_t m_n_elts;
1466 /* A correction on a particular line.
1467 This describes a plan for how to print one or more fixit_hint
1468 instances that affected the line, potentially consolidating hints
1469 into corrections to make the result easier for the user to read. */
1471 struct correction
1473 correction (column_range affected_columns,
1474 column_range printed_columns,
1475 const char *new_text, size_t new_text_len)
1476 : m_affected_columns (affected_columns),
1477 m_printed_columns (printed_columns),
1478 m_text (xstrdup (new_text)),
1479 m_len (new_text_len),
1480 m_alloc_sz (new_text_len + 1)
1484 ~correction () { free (m_text); }
1486 bool insertion_p () const
1488 return m_affected_columns.start == m_affected_columns.finish + 1;
1491 void ensure_capacity (size_t len);
1492 void ensure_terminated ();
1494 void overwrite (int dst_offset, const char_span &src_span)
1496 gcc_assert (dst_offset >= 0);
1497 gcc_assert (dst_offset + src_span.m_n_elts < m_alloc_sz);
1498 memcpy (m_text + dst_offset, src_span.m_ptr,
1499 src_span.m_n_elts);
1502 /* If insert, then start: the column before which the text
1503 is to be inserted, and finish is offset by the length of
1504 the replacement.
1505 If replace, then the range of columns affected. */
1506 column_range m_affected_columns;
1508 /* If insert, then start: the column before which the text
1509 is to be inserted, and finish is offset by the length of
1510 the replacement.
1511 If replace, then the range of columns affected. */
1512 column_range m_printed_columns;
1514 /* The text to be inserted/used as replacement. */
1515 char *m_text;
1516 size_t m_len;
1517 size_t m_alloc_sz;
1520 /* Ensure that m_text can hold a string of length LEN
1521 (plus 1 for 0-termination). */
1523 void
1524 correction::ensure_capacity (size_t len)
1526 /* Allow 1 extra byte for 0-termination. */
1527 if (m_alloc_sz < (len + 1))
1529 size_t new_alloc_sz = (len + 1) * 2;
1530 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1531 m_alloc_sz = new_alloc_sz;
1535 /* Ensure that m_text is 0-terminated. */
1537 void
1538 correction::ensure_terminated ()
1540 /* 0-terminate the buffer. */
1541 gcc_assert (m_len < m_alloc_sz);
1542 m_text[m_len] = '\0';
1545 /* A list of corrections affecting a particular line.
1546 This is used by layout::print_trailing_fixits for planning
1547 how to print the fix-it hints affecting the line. */
1549 struct line_corrections
1551 line_corrections (const char *filename, int row)
1552 : m_filename (filename), m_row (row)
1554 ~line_corrections ();
1556 void add_hint (const fixit_hint *hint);
1558 const char *m_filename;
1559 int m_row;
1560 auto_vec <correction *> m_corrections;
1563 /* struct line_corrections. */
1565 line_corrections::~line_corrections ()
1567 unsigned i;
1568 correction *c;
1569 FOR_EACH_VEC_ELT (m_corrections, i, c)
1570 delete c;
1573 /* A struct wrapping a particular source line, allowing
1574 run-time bounds-checking of accesses in a checked build. */
1576 struct source_line
1578 source_line (const char *filename, int line);
1580 char_span as_span () { return char_span (chars, width); }
1582 const char *chars;
1583 int width;
1586 /* source_line's ctor. */
1588 source_line::source_line (const char *filename, int line)
1590 chars = location_get_source_line (filename, line, &width);
1593 /* Add HINT to the corrections for this line.
1594 Attempt to consolidate nearby hints so that they will not
1595 overlap with printed. */
1597 void
1598 line_corrections::add_hint (const fixit_hint *hint)
1600 column_range affected_columns = get_affected_columns (hint);
1601 column_range printed_columns = get_printed_columns (hint);
1603 /* Potentially consolidate. */
1604 if (!m_corrections.is_empty ())
1606 correction *last_correction
1607 = m_corrections[m_corrections.length () - 1];
1609 /* The following consolidation code assumes that the fix-it hints
1610 have been sorted by start (done within layout's ctor). */
1611 gcc_assert (affected_columns.start
1612 >= last_correction->m_affected_columns.start);
1613 gcc_assert (printed_columns.start
1614 >= last_correction->m_printed_columns.start);
1616 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1618 /* We have two hints for which the printed forms of the hints
1619 would touch or overlap, so we need to consolidate them to avoid
1620 confusing the user.
1621 Attempt to inject a "replace" correction from immediately
1622 after the end of the last hint to immediately before the start
1623 of the next hint. */
1624 column_range between (last_correction->m_affected_columns.finish + 1,
1625 printed_columns.start - 1);
1627 /* Try to read the source. */
1628 source_line line (m_filename, m_row);
1629 if (line.chars && between.finish < line.width)
1631 /* Consolidate into the last correction:
1632 add a no-op "replace" of the "between" text, and
1633 add the text from the new hint. */
1634 int old_len = last_correction->m_len;
1635 gcc_assert (old_len >= 0);
1636 int between_len = between.finish + 1 - between.start;
1637 gcc_assert (between_len >= 0);
1638 int new_len = old_len + between_len + hint->get_length ();
1639 gcc_assert (new_len >= 0);
1640 last_correction->ensure_capacity (new_len);
1641 last_correction->overwrite
1642 (old_len,
1643 line.as_span ().subspan (between.start - 1,
1644 between.finish + 1 - between.start));
1645 last_correction->overwrite (old_len + between_len,
1646 char_span (hint->get_string (),
1647 hint->get_length ()));
1648 last_correction->m_len = new_len;
1649 last_correction->ensure_terminated ();
1650 last_correction->m_affected_columns.finish
1651 = affected_columns.finish;
1652 last_correction->m_printed_columns.finish
1653 += between_len + hint->get_length ();
1654 return;
1659 /* If no consolidation happened, add a new correction instance. */
1660 m_corrections.safe_push (new correction (affected_columns,
1661 printed_columns,
1662 hint->get_string (),
1663 hint->get_length ()));
1666 /* If there are any fixit hints on source line ROW, print them.
1667 They are printed in order, attempting to combine them onto lines, but
1668 starting new lines if necessary.
1669 Fix-it hints that insert new lines are handled separately,
1670 in layout::print_leading_fixits. */
1672 void
1673 layout::print_trailing_fixits (int row)
1675 /* Build a list of correction instances for the line,
1676 potentially consolidating hints (for the sake of readability). */
1677 line_corrections corrections (m_exploc.file, row);
1678 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1680 const fixit_hint *hint = m_fixit_hints[i];
1682 /* Newline fixits are handled by layout::print_leading_fixits. */
1683 if (hint->ends_with_newline_p ())
1684 continue;
1686 if (hint->affects_line_p (m_exploc.file, row))
1687 corrections.add_hint (hint);
1690 /* Now print the corrections. */
1691 unsigned i;
1692 correction *c;
1693 int column = 0;
1695 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1697 /* For now we assume each fixit hint can only touch one line. */
1698 if (c->insertion_p ())
1700 /* This assumes the insertion just affects one line. */
1701 int start_column = c->m_printed_columns.start;
1702 move_to_column (&column, start_column);
1703 m_colorizer.set_fixit_insert ();
1704 pp_string (m_pp, c->m_text);
1705 m_colorizer.set_normal_text ();
1706 column += c->m_len;
1708 else
1710 /* If the range of the replacement wasn't printed in the
1711 annotation line, then print an extra underline to
1712 indicate exactly what is being replaced.
1713 Always show it for removals. */
1714 int start_column = c->m_affected_columns.start;
1715 int finish_column = c->m_affected_columns.finish;
1716 if (!annotation_line_showed_range_p (row, start_column,
1717 finish_column)
1718 || c->m_len == 0)
1720 move_to_column (&column, start_column);
1721 m_colorizer.set_fixit_delete ();
1722 for (; column <= finish_column; column++)
1723 pp_character (m_pp, '-');
1724 m_colorizer.set_normal_text ();
1726 /* Print the replacement text. REPLACE also covers
1727 removals, so only do this extra work (potentially starting
1728 a new line) if we have actual replacement text. */
1729 if (c->m_len > 0)
1731 move_to_column (&column, start_column);
1732 m_colorizer.set_fixit_insert ();
1733 pp_string (m_pp, c->m_text);
1734 m_colorizer.set_normal_text ();
1735 column += c->m_len;
1740 /* Add a trailing newline, if necessary. */
1741 move_to_column (&column, 0);
1744 /* Disable any colorization and emit a newline. */
1746 void
1747 layout::print_newline ()
1749 m_colorizer.set_normal_text ();
1750 pp_newline (m_pp);
1753 /* Return true if (ROW/COLUMN) is within a range of the layout.
1754 If it returns true, OUT_STATE is written to, with the
1755 range index, and whether we should draw the caret at
1756 (ROW/COLUMN) (as opposed to an underline). */
1758 bool
1759 layout::get_state_at_point (/* Inputs. */
1760 int row, int column,
1761 int first_non_ws, int last_non_ws,
1762 /* Outputs. */
1763 point_state *out_state)
1765 layout_range *range;
1766 int i;
1767 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1769 if (range->contains_point (row, column))
1771 out_state->range_idx = i;
1773 /* Are we at the range's caret? is it visible? */
1774 out_state->draw_caret_p = false;
1775 if (range->m_show_caret_p
1776 && row == range->m_caret.m_line
1777 && column == range->m_caret.m_column)
1778 out_state->draw_caret_p = true;
1780 /* Within a multiline range, don't display any underline
1781 in any leading or trailing whitespace on a line.
1782 We do display carets, however. */
1783 if (!out_state->draw_caret_p)
1784 if (column < first_non_ws || column > last_non_ws)
1785 return false;
1787 /* We are within a range. */
1788 return true;
1792 return false;
1795 /* Helper function for use by layout::print_line when printing the
1796 annotation line under the source line.
1797 Get the column beyond the rightmost one that could contain a caret or
1798 range marker, given that we stop rendering at trailing whitespace.
1799 ROW is the source line within the given file.
1800 CARET_COLUMN is the column of range 0's caret.
1801 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1802 character of source (as determined when printing the source line). */
1805 layout::get_x_bound_for_row (int row, int caret_column,
1806 int last_non_ws_column)
1808 int result = caret_column + 1;
1810 layout_range *range;
1811 int i;
1812 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1814 if (row >= range->m_start.m_line)
1816 if (range->m_finish.m_line == row)
1818 /* On the final line within a range; ensure that
1819 we render up to the end of the range. */
1820 if (result <= range->m_finish.m_column)
1821 result = range->m_finish.m_column + 1;
1823 else if (row < range->m_finish.m_line)
1825 /* Within a multiline range; ensure that we render up to the
1826 last non-whitespace column. */
1827 if (result <= last_non_ws_column)
1828 result = last_non_ws_column + 1;
1833 return result;
1836 /* Given *COLUMN as an x-coordinate, print spaces to position
1837 successive output at DEST_COLUMN, printing a newline if necessary,
1838 and updating *COLUMN. */
1840 void
1841 layout::move_to_column (int *column, int dest_column)
1843 /* Start a new line if we need to. */
1844 if (*column > dest_column)
1846 print_newline ();
1847 *column = 0;
1850 while (*column < dest_column)
1852 pp_space (m_pp);
1853 (*column)++;
1857 /* For debugging layout issues, render a ruler giving column numbers
1858 (after the 1-column indent). */
1860 void
1861 layout::show_ruler (int max_column) const
1863 /* Hundreds. */
1864 if (max_column > 99)
1866 pp_space (m_pp);
1867 for (int column = 1 + m_x_offset; column <= max_column; column++)
1868 if (0 == column % 10)
1869 pp_character (m_pp, '0' + (column / 100) % 10);
1870 else
1871 pp_space (m_pp);
1872 pp_newline (m_pp);
1875 /* Tens. */
1876 pp_space (m_pp);
1877 for (int column = 1 + m_x_offset; column <= max_column; column++)
1878 if (0 == column % 10)
1879 pp_character (m_pp, '0' + (column / 10) % 10);
1880 else
1881 pp_space (m_pp);
1882 pp_newline (m_pp);
1884 /* Units. */
1885 pp_space (m_pp);
1886 for (int column = 1 + m_x_offset; column <= max_column; column++)
1887 pp_character (m_pp, '0' + (column % 10));
1888 pp_newline (m_pp);
1891 /* Print leading fix-its (for new lines inserted before the source line)
1892 then the source line, followed by an annotation line
1893 consisting of any caret/underlines, then any fixits.
1894 If the source line can't be read, print nothing. */
1895 void
1896 layout::print_line (int row)
1898 int line_width;
1899 const char *line = location_get_source_line (m_exploc.file, row,
1900 &line_width);
1901 if (!line)
1902 return;
1904 line_bounds lbounds;
1905 print_leading_fixits (row);
1906 print_source_line (row, line, line_width, &lbounds);
1907 if (should_print_annotation_line_p (row))
1908 print_annotation_line (row, lbounds);
1909 print_trailing_fixits (row);
1912 } /* End of anonymous namespace. */
1914 /* If LOC is within the spans of lines that will already be printed for
1915 this gcc_rich_location, then add it as a secondary location and return true.
1917 Otherwise return false. */
1919 bool
1920 gcc_rich_location::add_location_if_nearby (location_t loc)
1922 /* Use the layout location-handling logic to sanitize LOC,
1923 filtering it to the current line spans within a temporary
1924 layout instance. */
1925 layout layout (global_dc, this, DK_ERROR);
1926 location_range loc_range;
1927 loc_range.m_loc = loc;
1928 loc_range.m_show_caret_p = false;
1929 if (!layout.maybe_add_location_range (&loc_range, true))
1930 return false;
1932 add_range (loc, false);
1933 return true;
1936 /* Print the physical source code corresponding to the location of
1937 this diagnostic, with additional annotations. */
1939 void
1940 diagnostic_show_locus (diagnostic_context * context,
1941 rich_location *richloc,
1942 diagnostic_t diagnostic_kind)
1944 pp_newline (context->printer);
1946 location_t loc = richloc->get_loc ();
1947 /* Do nothing if source-printing has been disabled. */
1948 if (!context->show_caret)
1949 return;
1951 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1952 if (loc <= BUILTINS_LOCATION)
1953 return;
1955 /* Don't print the same source location twice in a row, unless we have
1956 fix-it hints. */
1957 if (loc == context->last_location
1958 && richloc->get_num_fixit_hints () == 0)
1959 return;
1961 context->last_location = loc;
1963 const char *saved_prefix = pp_get_prefix (context->printer);
1964 pp_set_prefix (context->printer, NULL);
1966 layout layout (context, richloc, diagnostic_kind);
1967 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1968 line_span_idx++)
1970 const line_span *line_span = layout.get_line_span (line_span_idx);
1971 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1973 expanded_location exploc = layout.get_expanded_location (line_span);
1974 context->start_span (context, exploc);
1976 int last_line = line_span->get_last_line ();
1977 for (int row = line_span->get_first_line (); row <= last_line; row++)
1978 layout.print_line (row);
1981 pp_set_prefix (context->printer, saved_prefix);
1984 #if CHECKING_P
1986 namespace selftest {
1988 /* Selftests for diagnostic_show_locus. */
1990 /* Convenience subclass of diagnostic_context for testing
1991 diagnostic_show_locus. */
1993 class test_diagnostic_context : public diagnostic_context
1995 public:
1996 test_diagnostic_context ()
1998 diagnostic_initialize (this, 0);
1999 show_caret = true;
2000 show_column = true;
2001 start_span = start_span_cb;
2003 ~test_diagnostic_context ()
2005 diagnostic_finish (this);
2008 /* Implementation of diagnostic_start_span_fn, hiding the
2009 real filename (to avoid printing the names of tempfiles). */
2010 static void
2011 start_span_cb (diagnostic_context *context, expanded_location exploc)
2013 exploc.file = "FILENAME";
2014 default_diagnostic_start_span_fn (context, exploc);
2018 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2020 static void
2021 test_diagnostic_show_locus_unknown_location ()
2023 test_diagnostic_context dc;
2024 rich_location richloc (line_table, UNKNOWN_LOCATION);
2025 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2026 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2029 /* Verify that diagnostic_show_locus works sanely for various
2030 single-line cases.
2032 All of these work on the following 1-line source file:
2033 .0000000001111111
2034 .1234567890123456
2035 "foo = bar.field;\n"
2036 which is set up by test_diagnostic_show_locus_one_liner and calls
2037 them. */
2039 /* Just a caret. */
2041 static void
2042 test_one_liner_simple_caret ()
2044 test_diagnostic_context dc;
2045 location_t caret = linemap_position_for_column (line_table, 10);
2046 rich_location richloc (line_table, caret);
2047 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2048 ASSERT_STREQ ("\n"
2049 " foo = bar.field;\n"
2050 " ^\n",
2051 pp_formatted_text (dc.printer));
2054 /* Caret and range. */
2056 static void
2057 test_one_liner_caret_and_range ()
2059 test_diagnostic_context dc;
2060 location_t caret = linemap_position_for_column (line_table, 10);
2061 location_t start = linemap_position_for_column (line_table, 7);
2062 location_t finish = linemap_position_for_column (line_table, 15);
2063 location_t loc = make_location (caret, start, finish);
2064 rich_location richloc (line_table, loc);
2065 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2066 ASSERT_STREQ ("\n"
2067 " foo = bar.field;\n"
2068 " ~~~^~~~~~\n",
2069 pp_formatted_text (dc.printer));
2072 /* Multiple ranges and carets. */
2074 static void
2075 test_one_liner_multiple_carets_and_ranges ()
2077 test_diagnostic_context dc;
2078 location_t foo
2079 = make_location (linemap_position_for_column (line_table, 2),
2080 linemap_position_for_column (line_table, 1),
2081 linemap_position_for_column (line_table, 3));
2082 dc.caret_chars[0] = 'A';
2084 location_t bar
2085 = make_location (linemap_position_for_column (line_table, 8),
2086 linemap_position_for_column (line_table, 7),
2087 linemap_position_for_column (line_table, 9));
2088 dc.caret_chars[1] = 'B';
2090 location_t field
2091 = make_location (linemap_position_for_column (line_table, 13),
2092 linemap_position_for_column (line_table, 11),
2093 linemap_position_for_column (line_table, 15));
2094 dc.caret_chars[2] = 'C';
2096 rich_location richloc (line_table, foo);
2097 richloc.add_range (bar, true);
2098 richloc.add_range (field, true);
2099 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2100 ASSERT_STREQ ("\n"
2101 " foo = bar.field;\n"
2102 " ~A~ ~B~ ~~C~~\n",
2103 pp_formatted_text (dc.printer));
2106 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2108 static void
2109 test_one_liner_fixit_insert_before ()
2111 test_diagnostic_context dc;
2112 location_t caret = linemap_position_for_column (line_table, 7);
2113 rich_location richloc (line_table, caret);
2114 richloc.add_fixit_insert_before ("&");
2115 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2116 ASSERT_STREQ ("\n"
2117 " foo = bar.field;\n"
2118 " ^\n"
2119 " &\n",
2120 pp_formatted_text (dc.printer));
2123 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2125 static void
2126 test_one_liner_fixit_insert_after ()
2128 test_diagnostic_context dc;
2129 location_t start = linemap_position_for_column (line_table, 1);
2130 location_t finish = linemap_position_for_column (line_table, 3);
2131 location_t foo = make_location (start, start, finish);
2132 rich_location richloc (line_table, foo);
2133 richloc.add_fixit_insert_after ("[0]");
2134 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2135 ASSERT_STREQ ("\n"
2136 " foo = bar.field;\n"
2137 " ^~~\n"
2138 " [0]\n",
2139 pp_formatted_text (dc.printer));
2142 /* Removal fix-it hint: removal of the ".field". */
2144 static void
2145 test_one_liner_fixit_remove ()
2147 test_diagnostic_context dc;
2148 location_t start = linemap_position_for_column (line_table, 10);
2149 location_t finish = linemap_position_for_column (line_table, 15);
2150 location_t dot = make_location (start, start, finish);
2151 rich_location richloc (line_table, dot);
2152 richloc.add_fixit_remove ();
2153 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2154 ASSERT_STREQ ("\n"
2155 " foo = bar.field;\n"
2156 " ^~~~~~\n"
2157 " ------\n",
2158 pp_formatted_text (dc.printer));
2161 /* Replace fix-it hint: replacing "field" with "m_field". */
2163 static void
2164 test_one_liner_fixit_replace ()
2166 test_diagnostic_context dc;
2167 location_t start = linemap_position_for_column (line_table, 11);
2168 location_t finish = linemap_position_for_column (line_table, 15);
2169 location_t field = make_location (start, start, finish);
2170 rich_location richloc (line_table, field);
2171 richloc.add_fixit_replace ("m_field");
2172 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2173 ASSERT_STREQ ("\n"
2174 " foo = bar.field;\n"
2175 " ^~~~~\n"
2176 " m_field\n",
2177 pp_formatted_text (dc.printer));
2180 /* Replace fix-it hint: replacing "field" with "m_field",
2181 but where the caret was elsewhere. */
2183 static void
2184 test_one_liner_fixit_replace_non_equal_range ()
2186 test_diagnostic_context dc;
2187 location_t equals = linemap_position_for_column (line_table, 5);
2188 location_t start = linemap_position_for_column (line_table, 11);
2189 location_t finish = linemap_position_for_column (line_table, 15);
2190 rich_location richloc (line_table, equals);
2191 source_range range;
2192 range.m_start = start;
2193 range.m_finish = finish;
2194 richloc.add_fixit_replace (range, "m_field");
2195 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2196 /* The replacement range is not indicated in the annotation line, so
2197 it should be indicated via an additional underline. */
2198 ASSERT_STREQ ("\n"
2199 " foo = bar.field;\n"
2200 " ^\n"
2201 " -----\n"
2202 " m_field\n",
2203 pp_formatted_text (dc.printer));
2206 /* Replace fix-it hint: replacing "field" with "m_field",
2207 where the caret was elsewhere, but where a secondary range
2208 exactly covers "field". */
2210 static void
2211 test_one_liner_fixit_replace_equal_secondary_range ()
2213 test_diagnostic_context dc;
2214 location_t equals = linemap_position_for_column (line_table, 5);
2215 location_t start = linemap_position_for_column (line_table, 11);
2216 location_t finish = linemap_position_for_column (line_table, 15);
2217 rich_location richloc (line_table, equals);
2218 location_t field = make_location (start, start, finish);
2219 richloc.add_range (field, false);
2220 richloc.add_fixit_replace (field, "m_field");
2221 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2222 /* The replacement range is indicated in the annotation line,
2223 so it shouldn't be indicated via an additional underline. */
2224 ASSERT_STREQ ("\n"
2225 " foo = bar.field;\n"
2226 " ^ ~~~~~\n"
2227 " m_field\n",
2228 pp_formatted_text (dc.printer));
2231 /* Verify that we can use ad-hoc locations when adding fixits to a
2232 rich_location. */
2234 static void
2235 test_one_liner_fixit_validation_adhoc_locations ()
2237 /* Generate a range that's too long to be packed, so must
2238 be stored as an ad-hoc location (given the defaults
2239 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2240 const location_t c7 = linemap_position_for_column (line_table, 7);
2241 const location_t c47 = linemap_position_for_column (line_table, 47);
2242 const location_t loc = make_location (c7, c7, c47);
2244 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2245 return;
2247 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2249 /* Insert. */
2251 rich_location richloc (line_table, loc);
2252 richloc.add_fixit_insert_before (loc, "test");
2253 /* It should not have been discarded by the validator. */
2254 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2256 test_diagnostic_context dc;
2257 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2258 ASSERT_STREQ ("\n"
2259 " foo = bar.field;\n"
2260 " ^~~~~~~~~~ \n"
2261 " test\n",
2262 pp_formatted_text (dc.printer));
2265 /* Remove. */
2267 rich_location richloc (line_table, loc);
2268 source_range range = source_range::from_locations (loc, c47);
2269 richloc.add_fixit_remove (range);
2270 /* It should not have been discarded by the validator. */
2271 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2273 test_diagnostic_context dc;
2274 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2275 ASSERT_STREQ ("\n"
2276 " foo = bar.field;\n"
2277 " ^~~~~~~~~~ \n"
2278 " -----------------------------------------\n",
2279 pp_formatted_text (dc.printer));
2282 /* Replace. */
2284 rich_location richloc (line_table, loc);
2285 source_range range = source_range::from_locations (loc, c47);
2286 richloc.add_fixit_replace (range, "test");
2287 /* It should not have been discarded by the validator. */
2288 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2290 test_diagnostic_context dc;
2291 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2292 ASSERT_STREQ ("\n"
2293 " foo = bar.field;\n"
2294 " ^~~~~~~~~~ \n"
2295 " test\n",
2296 pp_formatted_text (dc.printer));
2300 /* Test of consolidating insertions at the same location. */
2302 static void
2303 test_one_liner_many_fixits_1 ()
2305 test_diagnostic_context dc;
2306 location_t equals = linemap_position_for_column (line_table, 5);
2307 rich_location richloc (line_table, equals);
2308 for (int i = 0; i < 19; i++)
2309 richloc.add_fixit_insert_before ("a");
2310 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2311 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2312 ASSERT_STREQ ("\n"
2313 " foo = bar.field;\n"
2314 " ^\n"
2315 " aaaaaaaaaaaaaaaaaaa\n",
2316 pp_formatted_text (dc.printer));
2319 /* Ensure that we can add an arbitrary number of fix-it hints to a
2320 rich_location, even if they are not consolidated. */
2322 static void
2323 test_one_liner_many_fixits_2 ()
2325 test_diagnostic_context dc;
2326 location_t equals = linemap_position_for_column (line_table, 5);
2327 rich_location richloc (line_table, equals);
2328 for (int i = 0; i < 19; i++)
2330 location_t loc = linemap_position_for_column (line_table, i * 2);
2331 richloc.add_fixit_insert_before (loc, "a");
2333 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2334 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2335 ASSERT_STREQ ("\n"
2336 " foo = bar.field;\n"
2337 " ^\n"
2338 "a a a a a a a a a a a a a a a a a a a\n",
2339 pp_formatted_text (dc.printer));
2342 /* Run the various one-liner tests. */
2344 static void
2345 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2347 /* Create a tempfile and write some text to it.
2348 ....................0000000001111111.
2349 ....................1234567890123456. */
2350 const char *content = "foo = bar.field;\n";
2351 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2352 line_table_test ltt (case_);
2354 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2356 location_t line_end = linemap_position_for_column (line_table, 16);
2358 /* Don't attempt to run the tests if column data might be unavailable. */
2359 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2360 return;
2362 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2363 ASSERT_EQ (1, LOCATION_LINE (line_end));
2364 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2366 test_one_liner_simple_caret ();
2367 test_one_liner_caret_and_range ();
2368 test_one_liner_multiple_carets_and_ranges ();
2369 test_one_liner_fixit_insert_before ();
2370 test_one_liner_fixit_insert_after ();
2371 test_one_liner_fixit_remove ();
2372 test_one_liner_fixit_replace ();
2373 test_one_liner_fixit_replace_non_equal_range ();
2374 test_one_liner_fixit_replace_equal_secondary_range ();
2375 test_one_liner_fixit_validation_adhoc_locations ();
2376 test_one_liner_many_fixits_1 ();
2377 test_one_liner_many_fixits_2 ();
2380 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2382 static void
2383 test_add_location_if_nearby (const line_table_case &case_)
2385 /* Create a tempfile and write some text to it.
2386 ...000000000111111111122222222223333333333.
2387 ...123456789012345678901234567890123456789. */
2388 const char *content
2389 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2390 "struct different_line\n" /* line 2. */
2391 "{\n" /* line 3. */
2392 " double x;\n" /* line 4. */
2393 " double y;\n" /* line 5. */
2394 ";\n"); /* line 6. */
2395 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2396 line_table_test ltt (case_);
2398 const line_map_ordinary *ord_map
2399 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2400 tmp.get_filename (), 0));
2402 linemap_line_start (line_table, 1, 100);
2404 const location_t final_line_end
2405 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2407 /* Don't attempt to run the tests if column data might be unavailable. */
2408 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2409 return;
2411 /* Test of add_location_if_nearby on the same line as the
2412 primary location. */
2414 const location_t missing_close_brace_1_39
2415 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2416 const location_t matching_open_brace_1_18
2417 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2418 gcc_rich_location richloc (missing_close_brace_1_39);
2419 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2420 ASSERT_TRUE (added);
2421 ASSERT_EQ (2, richloc.get_num_locations ());
2422 test_diagnostic_context dc;
2423 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2424 ASSERT_STREQ ("\n"
2425 " struct same_line { double x; double y; ;\n"
2426 " ~ ^\n",
2427 pp_formatted_text (dc.printer));
2430 /* Test of add_location_if_nearby on a different line to the
2431 primary location. */
2433 const location_t missing_close_brace_6_1
2434 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2435 const location_t matching_open_brace_3_1
2436 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2437 gcc_rich_location richloc (missing_close_brace_6_1);
2438 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2439 ASSERT_FALSE (added);
2440 ASSERT_EQ (1, richloc.get_num_locations ());
2444 /* Verify that we print fixits even if they only affect lines
2445 outside those covered by the ranges in the rich_location. */
2447 static void
2448 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2450 /* Create a tempfile and write some text to it.
2451 ...000000000111111111122222222223333333333.
2452 ...123456789012345678901234567890123456789. */
2453 const char *content
2454 = ("struct point { double x; double y; };\n" /* line 1. */
2455 "struct point origin = {x: 0.0,\n" /* line 2. */
2456 " y\n" /* line 3. */
2457 "\n" /* line 4. */
2458 "\n" /* line 5. */
2459 " : 0.0};\n"); /* line 6. */
2460 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2461 line_table_test ltt (case_);
2463 const line_map_ordinary *ord_map
2464 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2465 tmp.get_filename (), 0));
2467 linemap_line_start (line_table, 1, 100);
2469 const location_t final_line_end
2470 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2472 /* Don't attempt to run the tests if column data might be unavailable. */
2473 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2474 return;
2476 /* A pair of tests for modernizing the initializers to C99-style. */
2478 /* The one-liner case (line 2). */
2480 test_diagnostic_context dc;
2481 const location_t x
2482 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2483 const location_t colon
2484 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2485 rich_location richloc (line_table, colon);
2486 richloc.add_fixit_insert_before (x, ".");
2487 richloc.add_fixit_replace (colon, "=");
2488 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2489 ASSERT_STREQ ("\n"
2490 " struct point origin = {x: 0.0,\n"
2491 " ^\n"
2492 " .=\n",
2493 pp_formatted_text (dc.printer));
2496 /* The multiline case. The caret for the rich_location is on line 6;
2497 verify that insertion fixit on line 3 is still printed (and that
2498 span starts are printed due to the gap between the span at line 3
2499 and that at line 6). */
2501 test_diagnostic_context dc;
2502 const location_t y
2503 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2504 const location_t colon
2505 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2506 rich_location richloc (line_table, colon);
2507 richloc.add_fixit_insert_before (y, ".");
2508 richloc.add_fixit_replace (colon, "=");
2509 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2510 ASSERT_STREQ ("\n"
2511 "FILENAME:3:24:\n"
2512 " y\n"
2513 " .\n"
2514 "FILENAME:6:25:\n"
2515 " : 0.0};\n"
2516 " ^\n"
2517 " =\n",
2518 pp_formatted_text (dc.printer));
2523 /* Verify that fix-it hints are appropriately consolidated.
2525 If any fix-it hints in a rich_location involve locations beyond
2526 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2527 the fix-it as a whole, so there should be none.
2529 Otherwise, verify that consecutive "replace" and "remove" fix-its
2530 are merged, and that other fix-its remain separate. */
2532 static void
2533 test_fixit_consolidation (const line_table_case &case_)
2535 line_table_test ltt (case_);
2537 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2539 const location_t c10 = linemap_position_for_column (line_table, 10);
2540 const location_t c15 = linemap_position_for_column (line_table, 15);
2541 const location_t c16 = linemap_position_for_column (line_table, 16);
2542 const location_t c17 = linemap_position_for_column (line_table, 17);
2543 const location_t c20 = linemap_position_for_column (line_table, 20);
2544 const location_t c21 = linemap_position_for_column (line_table, 21);
2545 const location_t caret = c10;
2547 /* Insert + insert. */
2549 rich_location richloc (line_table, caret);
2550 richloc.add_fixit_insert_before (c10, "foo");
2551 richloc.add_fixit_insert_before (c15, "bar");
2553 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2554 /* Bogus column info for 2nd fixit, so no fixits. */
2555 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2556 else
2557 /* They should not have been merged. */
2558 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2561 /* Insert + replace. */
2563 rich_location richloc (line_table, caret);
2564 richloc.add_fixit_insert_before (c10, "foo");
2565 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2566 "bar");
2568 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2569 /* Bogus column info for 2nd fixit, so no fixits. */
2570 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2571 else
2572 /* They should not have been merged. */
2573 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2576 /* Replace + non-consecutive insert. */
2578 rich_location richloc (line_table, caret);
2579 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2580 "bar");
2581 richloc.add_fixit_insert_before (c17, "foo");
2583 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2584 /* Bogus column info for 2nd fixit, so no fixits. */
2585 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2586 else
2587 /* They should not have been merged. */
2588 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2591 /* Replace + non-consecutive replace. */
2593 rich_location richloc (line_table, caret);
2594 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2595 "foo");
2596 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2597 "bar");
2599 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2600 /* Bogus column info for 2nd fixit, so no fixits. */
2601 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2602 else
2603 /* They should not have been merged. */
2604 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2607 /* Replace + consecutive replace. */
2609 rich_location richloc (line_table, caret);
2610 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2611 "foo");
2612 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2613 "bar");
2615 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2616 /* Bogus column info for 2nd fixit, so no fixits. */
2617 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2618 else
2620 /* They should have been merged into a single "replace". */
2621 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2622 const fixit_hint *hint = richloc.get_fixit_hint (0);
2623 ASSERT_STREQ ("foobar", hint->get_string ());
2624 ASSERT_EQ (c10, hint->get_start_loc ());
2625 ASSERT_EQ (c21, hint->get_next_loc ());
2629 /* Replace + consecutive removal. */
2631 rich_location richloc (line_table, caret);
2632 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2633 "foo");
2634 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2636 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2637 /* Bogus column info for 2nd fixit, so no fixits. */
2638 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2639 else
2641 /* They should have been merged into a single replace, with the
2642 range extended to cover that of the removal. */
2643 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2644 const fixit_hint *hint = richloc.get_fixit_hint (0);
2645 ASSERT_STREQ ("foo", hint->get_string ());
2646 ASSERT_EQ (c10, hint->get_start_loc ());
2647 ASSERT_EQ (c21, hint->get_next_loc ());
2651 /* Consecutive removals. */
2653 rich_location richloc (line_table, caret);
2654 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2655 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2657 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2658 /* Bogus column info for 2nd fixit, so no fixits. */
2659 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2660 else
2662 /* They should have been merged into a single "replace-with-empty". */
2663 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2664 const fixit_hint *hint = richloc.get_fixit_hint (0);
2665 ASSERT_STREQ ("", hint->get_string ());
2666 ASSERT_EQ (c10, hint->get_start_loc ());
2667 ASSERT_EQ (c21, hint->get_next_loc ());
2672 /* Verify that the line_corrections machinery correctly prints
2673 overlapping fixit-hints. */
2675 static void
2676 test_overlapped_fixit_printing (const line_table_case &case_)
2678 /* Create a tempfile and write some text to it.
2679 ...000000000111111111122222222223333333333.
2680 ...123456789012345678901234567890123456789. */
2681 const char *content
2682 = (" foo *f = (foo *)ptr->field;\n");
2683 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2684 line_table_test ltt (case_);
2686 const line_map_ordinary *ord_map
2687 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2688 tmp.get_filename (), 0));
2690 linemap_line_start (line_table, 1, 100);
2692 const location_t final_line_end
2693 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2695 /* Don't attempt to run the tests if column data might be unavailable. */
2696 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2697 return;
2699 /* A test for converting a C-style cast to a C++-style cast. */
2700 const location_t open_paren
2701 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2702 const location_t close_paren
2703 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2704 const location_t expr_start
2705 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2706 const location_t expr_finish
2707 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2708 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2710 /* Various examples of fix-it hints that aren't themselves consolidated,
2711 but for which the *printing* may need consolidation. */
2713 /* Example where 3 fix-it hints are printed as one. */
2715 test_diagnostic_context dc;
2716 rich_location richloc (line_table, expr);
2717 richloc.add_fixit_replace (open_paren, "const_cast<");
2718 richloc.add_fixit_replace (close_paren, "> (");
2719 richloc.add_fixit_insert_after (")");
2721 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2722 ASSERT_STREQ ("\n"
2723 " foo *f = (foo *)ptr->field;\n"
2724 " ^~~~~~~~~~\n"
2725 " -----------------\n"
2726 " const_cast<foo *> (ptr->field)\n",
2727 pp_formatted_text (dc.printer));
2729 /* Unit-test the line_corrections machinery. */
2730 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2731 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2732 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2733 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2734 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2735 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2736 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2737 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2738 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2739 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2741 /* Add each hint in turn to a line_corrections instance,
2742 and verify that they are consolidated into one correction instance
2743 as expected. */
2744 line_corrections lc (tmp.get_filename (), 1);
2746 /* The first replace hint by itself. */
2747 lc.add_hint (hint_0);
2748 ASSERT_EQ (1, lc.m_corrections.length ());
2749 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2750 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2751 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2753 /* After the second replacement hint, they are printed together
2754 as a replacement (along with the text between them). */
2755 lc.add_hint (hint_1);
2756 ASSERT_EQ (1, lc.m_corrections.length ());
2757 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2758 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2759 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2761 /* After the final insertion hint, they are all printed together
2762 as a replacement (along with the text between them). */
2763 lc.add_hint (hint_2);
2764 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2765 lc.m_corrections[0]->m_text);
2766 ASSERT_EQ (1, lc.m_corrections.length ());
2767 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2768 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2771 /* Example where two are consolidated during printing. */
2773 test_diagnostic_context dc;
2774 rich_location richloc (line_table, expr);
2775 richloc.add_fixit_replace (open_paren, "CAST (");
2776 richloc.add_fixit_replace (close_paren, ") (");
2777 richloc.add_fixit_insert_after (")");
2779 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2780 ASSERT_STREQ ("\n"
2781 " foo *f = (foo *)ptr->field;\n"
2782 " ^~~~~~~~~~\n"
2783 " -\n"
2784 " CAST (-\n"
2785 " ) ( )\n",
2786 pp_formatted_text (dc.printer));
2789 /* Example where none are consolidated during printing. */
2791 test_diagnostic_context dc;
2792 rich_location richloc (line_table, expr);
2793 richloc.add_fixit_replace (open_paren, "CST (");
2794 richloc.add_fixit_replace (close_paren, ") (");
2795 richloc.add_fixit_insert_after (")");
2797 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2798 ASSERT_STREQ ("\n"
2799 " foo *f = (foo *)ptr->field;\n"
2800 " ^~~~~~~~~~\n"
2801 " -\n"
2802 " CST ( -\n"
2803 " ) ( )\n",
2804 pp_formatted_text (dc.printer));
2807 /* Example of deletion fix-it hints. */
2809 test_diagnostic_context dc;
2810 rich_location richloc (line_table, expr);
2811 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2812 source_range victim = {open_paren, close_paren};
2813 richloc.add_fixit_remove (victim);
2815 /* This case is actually handled by fixit-consolidation,
2816 rather than by line_corrections. */
2817 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2819 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2820 ASSERT_STREQ ("\n"
2821 " foo *f = (foo *)ptr->field;\n"
2822 " ^~~~~~~~~~\n"
2823 " -------\n"
2824 " (bar *)\n",
2825 pp_formatted_text (dc.printer));
2828 /* Example of deletion fix-it hints that would overlap. */
2830 test_diagnostic_context dc;
2831 rich_location richloc (line_table, expr);
2832 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2833 source_range victim = {expr_start, expr_finish};
2834 richloc.add_fixit_remove (victim);
2836 /* These fixits are not consolidated. */
2837 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2839 /* But the corrections are. */
2840 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2841 ASSERT_STREQ ("\n"
2842 " foo *f = (foo *)ptr->field;\n"
2843 " ^~~~~~~~~~\n"
2844 " -----------------\n"
2845 " (longer *)(foo *)\n",
2846 pp_formatted_text (dc.printer));
2849 /* Example of insertion fix-it hints that would overlap. */
2851 test_diagnostic_context dc;
2852 rich_location richloc (line_table, expr);
2853 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2854 richloc.add_fixit_insert_after (close_paren, "TEST");
2856 /* The first insertion is long enough that if printed naively,
2857 it would overlap with the second.
2858 Verify that they are printed as a single replacement. */
2859 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2860 ASSERT_STREQ ("\n"
2861 " foo *f = (foo *)ptr->field;\n"
2862 " ^~~~~~~~~~\n"
2863 " -------\n"
2864 " LONGER THAN THE CAST(foo *)TEST\n",
2865 pp_formatted_text (dc.printer));
2869 /* Verify that the line_corrections machinery correctly prints
2870 overlapping fixit-hints that have been added in the wrong
2871 order.
2872 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
2874 static void
2875 test_overlapped_fixit_printing_2 (const line_table_case &case_)
2877 /* Create a tempfile and write some text to it.
2878 ...000000000111111111122222222223333333333.
2879 ...123456789012345678901234567890123456789. */
2880 const char *content
2881 = ("int a5[][0][0] = { 1, 2 };\n");
2882 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2883 line_table_test ltt (case_);
2885 const line_map_ordinary *ord_map
2886 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2887 tmp.get_filename (), 0));
2889 linemap_line_start (line_table, 1, 100);
2891 const location_t final_line_end
2892 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
2894 /* Don't attempt to run the tests if column data might be unavailable. */
2895 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2896 return;
2898 const location_t col_1
2899 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2900 const location_t col_20
2901 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
2902 const location_t col_21
2903 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
2904 const location_t col_23
2905 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
2906 const location_t col_25
2907 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
2909 /* Two insertions, in the wrong order. */
2911 rich_location richloc (line_table, col_20);
2912 richloc.add_fixit_insert_before (col_23, "{");
2913 richloc.add_fixit_insert_before (col_21, "}");
2915 /* These fixits should be accepted; they can't be consolidated. */
2916 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2917 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2918 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
2919 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
2920 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2921 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
2922 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
2924 /* Verify that they're printed correctly. */
2925 test_diagnostic_context dc;
2926 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2927 ASSERT_STREQ ("\n"
2928 " int a5[][0][0] = { 1, 2 };\n"
2929 " ^\n"
2930 " } {\n",
2931 pp_formatted_text (dc.printer));
2934 /* Various overlapping insertions, some occurring "out of order"
2935 (reproducing the fix-it hints from PR c/81405). */
2937 test_diagnostic_context dc;
2938 rich_location richloc (line_table, col_20);
2940 richloc.add_fixit_insert_before (col_20, "{{");
2941 richloc.add_fixit_insert_before (col_21, "}}");
2942 richloc.add_fixit_insert_before (col_23, "{");
2943 richloc.add_fixit_insert_before (col_21, "}");
2944 richloc.add_fixit_insert_before (col_23, "{{");
2945 richloc.add_fixit_insert_before (col_25, "}");
2946 richloc.add_fixit_insert_before (col_21, "}");
2947 richloc.add_fixit_insert_before (col_1, "{");
2948 richloc.add_fixit_insert_before (col_25, "}");
2949 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2950 ASSERT_STREQ ("\n"
2951 " int a5[][0][0] = { 1, 2 };\n"
2952 " ^\n"
2953 " { -----\n"
2954 " {{1}}}}, {{{2 }}\n",
2955 pp_formatted_text (dc.printer));
2959 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2961 static void
2962 test_fixit_insert_containing_newline (const line_table_case &case_)
2964 /* Create a tempfile and write some text to it.
2965 .........................0000000001111111.
2966 .........................1234567890123456. */
2967 const char *old_content = (" case 'a':\n" /* line 1. */
2968 " x = a;\n" /* line 2. */
2969 " case 'b':\n" /* line 3. */
2970 " x = b;\n");/* line 4. */
2972 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2973 line_table_test ltt (case_);
2974 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2976 location_t case_start = linemap_position_for_column (line_table, 5);
2977 location_t case_finish = linemap_position_for_column (line_table, 13);
2978 location_t case_loc = make_location (case_start, case_start, case_finish);
2979 location_t line_start = linemap_position_for_column (line_table, 1);
2981 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2982 return;
2984 /* Add a "break;" on a line by itself before line 3 i.e. before
2985 column 1 of line 3. */
2987 rich_location richloc (line_table, case_loc);
2988 richloc.add_fixit_insert_before (line_start, " break;\n");
2989 test_diagnostic_context dc;
2990 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2991 ASSERT_STREQ ("\n"
2992 "+ break;\n"
2993 " case 'b':\n"
2994 " ^~~~~~~~~\n",
2995 pp_formatted_text (dc.printer));
2998 /* Verify that attempts to add text with a newline fail when the
2999 insertion point is *not* at the start of a line. */
3001 rich_location richloc (line_table, case_loc);
3002 richloc.add_fixit_insert_before (case_start, "break;\n");
3003 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3004 test_diagnostic_context dc;
3005 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3006 ASSERT_STREQ ("\n"
3007 " case 'b':\n"
3008 " ^~~~~~~~~\n",
3009 pp_formatted_text (dc.printer));
3013 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3014 of the file, where the fix-it is printed in a different line-span
3015 to the primary range of the diagnostic. */
3017 static void
3018 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3020 /* Create a tempfile and write some text to it.
3021 .........................0000000001111111.
3022 .........................1234567890123456. */
3023 const char *old_content = ("test (int ch)\n" /* line 1. */
3024 "{\n" /* line 2. */
3025 " putchar (ch);\n" /* line 3. */
3026 "}\n"); /* line 4. */
3028 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3029 line_table_test ltt (case_);
3031 const line_map_ordinary *ord_map = linemap_check_ordinary
3032 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3033 linemap_line_start (line_table, 1, 100);
3035 /* The primary range is the "putchar" token. */
3036 location_t putchar_start
3037 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3038 location_t putchar_finish
3039 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3040 location_t putchar_loc
3041 = make_location (putchar_start, putchar_start, putchar_finish);
3042 rich_location richloc (line_table, putchar_loc);
3044 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3045 location_t file_start
3046 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3047 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3049 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3050 return;
3052 test_diagnostic_context dc;
3053 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3054 ASSERT_STREQ ("\n"
3055 "FILENAME:1:1:\n"
3056 "+#include <stdio.h>\n"
3057 " test (int ch)\n"
3058 "FILENAME:3:2:\n"
3059 " putchar (ch);\n"
3060 " ^~~~~~~\n",
3061 pp_formatted_text (dc.printer));
3064 /* Replacement fix-it hint containing a newline.
3065 This will fail, as newlines are only supported when inserting at the
3066 beginning of a line. */
3068 static void
3069 test_fixit_replace_containing_newline (const line_table_case &case_)
3071 /* Create a tempfile and write some text to it.
3072 .........................0000000001111.
3073 .........................1234567890123. */
3074 const char *old_content = "foo = bar ();\n";
3076 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3077 line_table_test ltt (case_);
3078 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3080 /* Replace the " = " with "\n = ", as if we were reformatting an
3081 overly long line. */
3082 location_t start = linemap_position_for_column (line_table, 4);
3083 location_t finish = linemap_position_for_column (line_table, 6);
3084 location_t loc = linemap_position_for_column (line_table, 13);
3085 rich_location richloc (line_table, loc);
3086 source_range range = source_range::from_locations (start, finish);
3087 richloc.add_fixit_replace (range, "\n =");
3089 /* Arbitrary newlines are not yet supported within fix-it hints, so
3090 the fix-it should not be displayed. */
3091 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3093 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3094 return;
3096 test_diagnostic_context dc;
3097 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3098 ASSERT_STREQ ("\n"
3099 " foo = bar ();\n"
3100 " ^\n",
3101 pp_formatted_text (dc.printer));
3104 /* Fix-it hint, attempting to delete a newline.
3105 This will fail, as we currently only support fix-it hints that
3106 affect one line at a time. */
3108 static void
3109 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3111 /* Create a tempfile and write some text to it.
3112 ..........................0000000001111.
3113 ..........................1234567890123. */
3114 const char *old_content = ("foo = bar (\n"
3115 " );\n");
3117 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3118 line_table_test ltt (case_);
3119 const line_map_ordinary *ord_map = linemap_check_ordinary
3120 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3121 linemap_line_start (line_table, 1, 100);
3123 /* Attempt to delete the " (\n...)". */
3124 location_t start
3125 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3126 location_t caret
3127 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3128 location_t finish
3129 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3130 location_t loc = make_location (caret, start, finish);
3131 rich_location richloc (line_table, loc);
3132 richloc. add_fixit_remove ();
3134 /* Fix-it hints that affect more than one line are not yet supported, so
3135 the fix-it should not be displayed. */
3136 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3138 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3139 return;
3141 test_diagnostic_context dc;
3142 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3143 ASSERT_STREQ ("\n"
3144 " foo = bar (\n"
3145 " ~^\n"
3146 " );\n"
3147 " ~ \n",
3148 pp_formatted_text (dc.printer));
3151 /* Run all of the selftests within this file. */
3153 void
3154 diagnostic_show_locus_c_tests ()
3156 test_layout_range_for_single_point ();
3157 test_layout_range_for_single_line ();
3158 test_layout_range_for_multiple_lines ();
3160 test_get_line_width_without_trailing_whitespace ();
3162 test_diagnostic_show_locus_unknown_location ();
3164 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3165 for_each_line_table_case (test_add_location_if_nearby);
3166 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3167 for_each_line_table_case (test_fixit_consolidation);
3168 for_each_line_table_case (test_overlapped_fixit_printing);
3169 for_each_line_table_case (test_overlapped_fixit_printing_2);
3170 for_each_line_table_case (test_fixit_insert_containing_newline);
3171 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3172 for_each_line_table_case (test_fixit_replace_containing_newline);
3173 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3176 } // namespace selftest
3178 #endif /* #if CHECKING_P */