[Ada] Wrong accessibility level under -gnat12
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob89074beb6d10c3995e26e839130a7b4341a3c586
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2019 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"
32 #include "selftest-diagnostic.h"
34 #ifdef HAVE_TERMIOS_H
35 # include <termios.h>
36 #endif
38 #ifdef GWINSZ_IN_SYS_IOCTL
39 # include <sys/ioctl.h>
40 #endif
42 /* Disable warnings about quoting issues in the pp_xxx calls below
43 that (intentionally) don't follow GCC diagnostic conventions. */
44 #if __GNUC__ >= 10
45 # pragma GCC diagnostic push
46 # pragma GCC diagnostic ignored "-Wformat-diag"
47 #endif
49 /* Classes for rendering source code and diagnostics, within an
50 anonymous namespace.
51 The work is done by "class layout", which embeds and uses
52 "class colorizer" and "class layout_range" to get things done. */
54 namespace {
56 /* The state at a given point of the source code, assuming that we're
57 in a range: which range are we in, and whether we should draw a caret at
58 this point. */
60 struct point_state
62 int range_idx;
63 bool draw_caret_p;
66 /* A class to inject colorization codes when printing the diagnostic locus.
68 It has one kind of colorization for each of:
69 - normal text
70 - range 0 (the "primary location")
71 - range 1
72 - range 2
74 The class caches the lookup of the color codes for the above.
76 The class also has responsibility for tracking which of the above is
77 active, filtering out unnecessary changes. This allows
78 layout::print_source_line and layout::print_annotation_line
79 to simply request a colorization code for *every* character they print,
80 via this class, and have the filtering be done for them here. */
82 class colorizer
84 public:
85 colorizer (diagnostic_context *context,
86 diagnostic_t diagnostic_kind);
87 ~colorizer ();
89 void set_range (int range_idx) { set_state (range_idx); }
90 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
91 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
92 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
94 private:
95 void set_state (int state);
96 void begin_state (int state);
97 void finish_state (int state);
98 const char *get_color_by_name (const char *);
100 private:
101 static const int STATE_NORMAL_TEXT = -1;
102 static const int STATE_FIXIT_INSERT = -2;
103 static const int STATE_FIXIT_DELETE = -3;
105 diagnostic_context *m_context;
106 diagnostic_t m_diagnostic_kind;
107 int m_current_state;
108 const char *m_range1;
109 const char *m_range2;
110 const char *m_fixit_insert;
111 const char *m_fixit_delete;
112 const char *m_stop_color;
115 /* A point within a layout_range; similar to an expanded_location,
116 but after filtering on file. */
118 class layout_point
120 public:
121 layout_point (const expanded_location &exploc)
122 : m_line (exploc.line),
123 m_column (exploc.column) {}
125 linenum_type m_line;
126 int m_column;
129 /* A class for use by "class layout" below: a filtered location_range. */
131 class layout_range
133 public:
134 layout_range (const expanded_location *start_exploc,
135 const expanded_location *finish_exploc,
136 enum range_display_kind range_display_kind,
137 const expanded_location *caret_exploc,
138 unsigned original_idx,
139 const range_label *label);
141 bool contains_point (linenum_type row, int column) const;
142 bool intersects_line_p (linenum_type row) const;
144 layout_point m_start;
145 layout_point m_finish;
146 enum range_display_kind m_range_display_kind;
147 layout_point m_caret;
148 unsigned m_original_idx;
149 const range_label *m_label;
152 /* A struct for use by layout::print_source_line for telling
153 layout::print_annotation_line the extents of the source line that
154 it printed, so that underlines can be clipped appropriately. */
156 struct line_bounds
158 int m_first_non_ws;
159 int m_last_non_ws;
162 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
163 or "line 23"). During the layout ctor, layout::calculate_line_spans
164 splits the pertinent source lines into a list of disjoint line_span
165 instances (e.g. lines 5-10, lines 15-20, line 23). */
167 struct line_span
169 line_span (linenum_type first_line, linenum_type last_line)
170 : m_first_line (first_line), m_last_line (last_line)
172 gcc_assert (first_line <= last_line);
174 linenum_type get_first_line () const { return m_first_line; }
175 linenum_type get_last_line () const { return m_last_line; }
177 bool contains_line_p (linenum_type line) const
179 return line >= m_first_line && line <= m_last_line;
182 static int comparator (const void *p1, const void *p2)
184 const line_span *ls1 = (const line_span *)p1;
185 const line_span *ls2 = (const line_span *)p2;
186 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
187 if (first_line_cmp)
188 return first_line_cmp;
189 return compare (ls1->m_last_line, ls2->m_last_line);
192 linenum_type m_first_line;
193 linenum_type m_last_line;
196 #if CHECKING_P
198 /* Selftests for line_span. */
200 static void
201 test_line_span ()
203 line_span line_one (1, 1);
204 ASSERT_EQ (1, line_one.get_first_line ());
205 ASSERT_EQ (1, line_one.get_last_line ());
206 ASSERT_FALSE (line_one.contains_line_p (0));
207 ASSERT_TRUE (line_one.contains_line_p (1));
208 ASSERT_FALSE (line_one.contains_line_p (2));
210 line_span lines_1_to_3 (1, 3);
211 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
212 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
213 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
214 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
216 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
217 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
218 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
220 /* A linenum > 2^31. */
221 const linenum_type LARGEST_LINE = 0xffffffff;
222 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
223 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
224 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
226 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
227 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
230 #endif /* #if CHECKING_P */
232 /* A class to control the overall layout when printing a diagnostic.
234 The layout is determined within the constructor.
235 It is then printed by repeatedly calling the "print_source_line",
236 "print_annotation_line" and "print_any_fixits" methods.
238 We assume we have disjoint ranges. */
240 class layout
242 public:
243 layout (diagnostic_context *context,
244 rich_location *richloc,
245 diagnostic_t diagnostic_kind);
247 bool maybe_add_location_range (const location_range *loc_range,
248 unsigned original_idx,
249 bool restrict_to_current_line_spans);
251 int get_num_line_spans () const { return m_line_spans.length (); }
252 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
254 void print_gap_in_line_numbering ();
255 bool print_heading_for_line_span_index_p (int line_span_idx) const;
257 expanded_location get_expanded_location (const line_span *) const;
259 void print_line (linenum_type row);
261 private:
262 bool will_show_line_p (linenum_type row) const;
263 void print_leading_fixits (linenum_type row);
264 void print_source_line (linenum_type row, const char *line, int line_width,
265 line_bounds *lbounds_out);
266 bool should_print_annotation_line_p (linenum_type row) const;
267 void start_annotation_line (char margin_char = ' ') const;
268 void print_annotation_line (linenum_type row, const line_bounds lbounds);
269 void print_any_labels (linenum_type row);
270 void print_trailing_fixits (linenum_type row);
272 bool annotation_line_showed_range_p (linenum_type line, int start_column,
273 int finish_column) const;
274 void show_ruler (int max_column) const;
276 bool validate_fixit_hint_p (const fixit_hint *hint);
278 void calculate_line_spans ();
280 void print_newline ();
282 bool
283 get_state_at_point (/* Inputs. */
284 linenum_type row, int column,
285 int first_non_ws, int last_non_ws,
286 /* Outputs. */
287 point_state *out_state);
290 get_x_bound_for_row (linenum_type row, int caret_column,
291 int last_non_ws);
293 void
294 move_to_column (int *column, int dest_column, bool add_left_margin);
296 private:
297 diagnostic_context *m_context;
298 pretty_printer *m_pp;
299 location_t m_primary_loc;
300 expanded_location m_exploc;
301 colorizer m_colorizer;
302 bool m_colorize_source_p;
303 bool m_show_labels_p;
304 bool m_show_line_numbers_p;
305 auto_vec <layout_range> m_layout_ranges;
306 auto_vec <const fixit_hint *> m_fixit_hints;
307 auto_vec <line_span> m_line_spans;
308 int m_linenum_width;
309 int m_x_offset;
312 /* Implementation of "class colorizer". */
314 /* The constructor for "colorizer". Lookup and store color codes for the
315 different kinds of things we might need to print. */
317 colorizer::colorizer (diagnostic_context *context,
318 diagnostic_t diagnostic_kind) :
319 m_context (context),
320 m_diagnostic_kind (diagnostic_kind),
321 m_current_state (STATE_NORMAL_TEXT)
323 m_range1 = get_color_by_name ("range1");
324 m_range2 = get_color_by_name ("range2");
325 m_fixit_insert = get_color_by_name ("fixit-insert");
326 m_fixit_delete = get_color_by_name ("fixit-delete");
327 m_stop_color = colorize_stop (pp_show_color (context->printer));
330 /* The destructor for "colorize". If colorization is on, print a code to
331 turn it off. */
333 colorizer::~colorizer ()
335 finish_state (m_current_state);
338 /* Update state, printing color codes if necessary if there's a state
339 change. */
341 void
342 colorizer::set_state (int new_state)
344 if (m_current_state != new_state)
346 finish_state (m_current_state);
347 m_current_state = new_state;
348 begin_state (new_state);
352 /* Turn on any colorization for STATE. */
354 void
355 colorizer::begin_state (int state)
357 switch (state)
359 case STATE_NORMAL_TEXT:
360 break;
362 case STATE_FIXIT_INSERT:
363 pp_string (m_context->printer, m_fixit_insert);
364 break;
366 case STATE_FIXIT_DELETE:
367 pp_string (m_context->printer, m_fixit_delete);
368 break;
370 case 0:
371 /* Make range 0 be the same color as the "kind" text
372 (error vs warning vs note). */
373 pp_string
374 (m_context->printer,
375 colorize_start (pp_show_color (m_context->printer),
376 diagnostic_get_color_for_kind (m_diagnostic_kind)));
377 break;
379 case 1:
380 pp_string (m_context->printer, m_range1);
381 break;
383 case 2:
384 pp_string (m_context->printer, m_range2);
385 break;
387 default:
388 /* For ranges beyond 2, alternate between color 1 and color 2. */
390 gcc_assert (state > 2);
391 pp_string (m_context->printer,
392 state % 2 ? m_range1 : m_range2);
394 break;
398 /* Turn off any colorization for STATE. */
400 void
401 colorizer::finish_state (int state)
403 if (state != STATE_NORMAL_TEXT)
404 pp_string (m_context->printer, m_stop_color);
407 /* Get the color code for NAME (or the empty string if
408 colorization is disabled). */
410 const char *
411 colorizer::get_color_by_name (const char *name)
413 return colorize_start (pp_show_color (m_context->printer), name);
416 /* Implementation of class layout_range. */
418 /* The constructor for class layout_range.
419 Initialize various layout_point fields from expanded_location
420 equivalents; we've already filtered on file. */
422 layout_range::layout_range (const expanded_location *start_exploc,
423 const expanded_location *finish_exploc,
424 enum range_display_kind range_display_kind,
425 const expanded_location *caret_exploc,
426 unsigned original_idx,
427 const range_label *label)
428 : m_start (*start_exploc),
429 m_finish (*finish_exploc),
430 m_range_display_kind (range_display_kind),
431 m_caret (*caret_exploc),
432 m_original_idx (original_idx),
433 m_label (label)
437 /* Is (column, row) within the given range?
438 We've already filtered on the file.
440 Ranges are closed (both limits are within the range).
442 Example A: a single-line range:
443 start: (col=22, line=2)
444 finish: (col=38, line=2)
446 |00000011111111112222222222333333333344444444444
447 |34567890123456789012345678901234567890123456789
448 --+-----------------------------------------------
449 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
450 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
451 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
453 Example B: a multiline range with
454 start: (col=14, line=3)
455 finish: (col=08, line=5)
457 |00000011111111112222222222333333333344444444444
458 |34567890123456789012345678901234567890123456789
459 --+-----------------------------------------------
460 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
461 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
462 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
463 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
464 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
465 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
466 --+-----------------------------------------------
468 Legend:
469 - 'b' indicates a point *before* the range
470 - 'S' indicates the start of the range
471 - 'w' indicates a point within the range
472 - 'F' indicates the finish of the range (which is
473 within it).
474 - 'a' indicates a subsequent point *after* the range. */
476 bool
477 layout_range::contains_point (linenum_type row, int column) const
479 gcc_assert (m_start.m_line <= m_finish.m_line);
480 /* ...but the equivalent isn't true for the columns;
481 consider example B in the comment above. */
483 if (row < m_start.m_line)
484 /* Points before the first line of the range are
485 outside it (corresponding to line 01 in example A
486 and lines 01 and 02 in example B above). */
487 return false;
489 if (row == m_start.m_line)
490 /* On same line as start of range (corresponding
491 to line 02 in example A and line 03 in example B). */
493 if (column < m_start.m_column)
494 /* Points on the starting line of the range, but
495 before the column in which it begins. */
496 return false;
498 if (row < m_finish.m_line)
499 /* This is a multiline range; the point
500 is within it (corresponds to line 03 in example B
501 from column 14 onwards) */
502 return true;
503 else
505 /* This is a single-line range. */
506 gcc_assert (row == m_finish.m_line);
507 return column <= m_finish.m_column;
511 /* The point is in a line beyond that containing the
512 start of the range: lines 03 onwards in example A,
513 and lines 04 onwards in example B. */
514 gcc_assert (row > m_start.m_line);
516 if (row > m_finish.m_line)
517 /* The point is beyond the final line of the range
518 (lines 03 onwards in example A, and lines 06 onwards
519 in example B). */
520 return false;
522 if (row < m_finish.m_line)
524 /* The point is in a line that's fully within a multiline
525 range (e.g. line 04 in example B). */
526 gcc_assert (m_start.m_line < m_finish.m_line);
527 return true;
530 gcc_assert (row == m_finish.m_line);
532 return column <= m_finish.m_column;
535 /* Does this layout_range contain any part of line ROW? */
537 bool
538 layout_range::intersects_line_p (linenum_type row) const
540 gcc_assert (m_start.m_line <= m_finish.m_line);
541 if (row < m_start.m_line)
542 return false;
543 if (row > m_finish.m_line)
544 return false;
545 return true;
548 #if CHECKING_P
550 /* A helper function for testing layout_range. */
552 static layout_range
553 make_range (int start_line, int start_col, int end_line, int end_col)
555 const expanded_location start_exploc
556 = {"test.c", start_line, start_col, NULL, false};
557 const expanded_location finish_exploc
558 = {"test.c", end_line, end_col, NULL, false};
559 return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
560 &start_exploc, 0, NULL);
563 /* Selftests for layout_range::contains_point and
564 layout_range::intersects_line_p. */
566 /* Selftest for layout_range, where the layout_range
567 is a range with start==end i.e. a single point. */
569 static void
570 test_layout_range_for_single_point ()
572 layout_range point = make_range (7, 10, 7, 10);
574 /* Tests for layout_range::contains_point. */
576 /* Before the line. */
577 ASSERT_FALSE (point.contains_point (6, 1));
579 /* On the line, but before start. */
580 ASSERT_FALSE (point.contains_point (7, 9));
582 /* At the point. */
583 ASSERT_TRUE (point.contains_point (7, 10));
585 /* On the line, after the point. */
586 ASSERT_FALSE (point.contains_point (7, 11));
588 /* After the line. */
589 ASSERT_FALSE (point.contains_point (8, 1));
591 /* Tests for layout_range::intersects_line_p. */
592 ASSERT_FALSE (point.intersects_line_p (6));
593 ASSERT_TRUE (point.intersects_line_p (7));
594 ASSERT_FALSE (point.intersects_line_p (8));
597 /* Selftest for layout_range, where the layout_range
598 is the single-line range shown as "Example A" above. */
600 static void
601 test_layout_range_for_single_line ()
603 layout_range example_a = make_range (2, 22, 2, 38);
605 /* Tests for layout_range::contains_point. */
607 /* Before the line. */
608 ASSERT_FALSE (example_a.contains_point (1, 1));
610 /* On the line, but before start. */
611 ASSERT_FALSE (example_a.contains_point (2, 21));
613 /* On the line, at the start. */
614 ASSERT_TRUE (example_a.contains_point (2, 22));
616 /* On the line, within the range. */
617 ASSERT_TRUE (example_a.contains_point (2, 23));
619 /* On the line, at the end. */
620 ASSERT_TRUE (example_a.contains_point (2, 38));
622 /* On the line, after the end. */
623 ASSERT_FALSE (example_a.contains_point (2, 39));
625 /* After the line. */
626 ASSERT_FALSE (example_a.contains_point (2, 39));
628 /* Tests for layout_range::intersects_line_p. */
629 ASSERT_FALSE (example_a.intersects_line_p (1));
630 ASSERT_TRUE (example_a.intersects_line_p (2));
631 ASSERT_FALSE (example_a.intersects_line_p (3));
634 /* Selftest for layout_range, where the layout_range
635 is the multi-line range shown as "Example B" above. */
637 static void
638 test_layout_range_for_multiple_lines ()
640 layout_range example_b = make_range (3, 14, 5, 8);
642 /* Tests for layout_range::contains_point. */
644 /* Before first line. */
645 ASSERT_FALSE (example_b.contains_point (1, 1));
647 /* On the first line, but before start. */
648 ASSERT_FALSE (example_b.contains_point (3, 13));
650 /* At the start. */
651 ASSERT_TRUE (example_b.contains_point (3, 14));
653 /* On the first line, within the range. */
654 ASSERT_TRUE (example_b.contains_point (3, 15));
656 /* On an interior line.
657 The column number should not matter; try various boundary
658 values. */
659 ASSERT_TRUE (example_b.contains_point (4, 1));
660 ASSERT_TRUE (example_b.contains_point (4, 7));
661 ASSERT_TRUE (example_b.contains_point (4, 8));
662 ASSERT_TRUE (example_b.contains_point (4, 9));
663 ASSERT_TRUE (example_b.contains_point (4, 13));
664 ASSERT_TRUE (example_b.contains_point (4, 14));
665 ASSERT_TRUE (example_b.contains_point (4, 15));
667 /* On the final line, before the end. */
668 ASSERT_TRUE (example_b.contains_point (5, 7));
670 /* On the final line, at the end. */
671 ASSERT_TRUE (example_b.contains_point (5, 8));
673 /* On the final line, after the end. */
674 ASSERT_FALSE (example_b.contains_point (5, 9));
676 /* After the line. */
677 ASSERT_FALSE (example_b.contains_point (6, 1));
679 /* Tests for layout_range::intersects_line_p. */
680 ASSERT_FALSE (example_b.intersects_line_p (2));
681 ASSERT_TRUE (example_b.intersects_line_p (3));
682 ASSERT_TRUE (example_b.intersects_line_p (4));
683 ASSERT_TRUE (example_b.intersects_line_p (5));
684 ASSERT_FALSE (example_b.intersects_line_p (6));
687 #endif /* #if CHECKING_P */
689 /* Given a source line LINE of length LINE_WIDTH, determine the width
690 without any trailing whitespace. */
692 static int
693 get_line_width_without_trailing_whitespace (const char *line, int line_width)
695 int result = line_width;
696 while (result > 0)
698 char ch = line[result - 1];
699 if (ch == ' ' || ch == '\t' || ch == '\r')
700 result--;
701 else
702 break;
704 gcc_assert (result >= 0);
705 gcc_assert (result <= line_width);
706 gcc_assert (result == 0 ||
707 (line[result - 1] != ' '
708 && line[result -1] != '\t'
709 && line[result -1] != '\r'));
710 return result;
713 #if CHECKING_P
715 /* A helper function for testing get_line_width_without_trailing_whitespace. */
717 static void
718 assert_eq (const char *line, int expected_width)
720 int actual_value
721 = get_line_width_without_trailing_whitespace (line, strlen (line));
722 ASSERT_EQ (actual_value, expected_width);
725 /* Verify that get_line_width_without_trailing_whitespace is sane for
726 various inputs. It is not required to handle newlines. */
728 static void
729 test_get_line_width_without_trailing_whitespace ()
731 assert_eq ("", 0);
732 assert_eq (" ", 0);
733 assert_eq ("\t", 0);
734 assert_eq ("\r", 0);
735 assert_eq ("hello world", 11);
736 assert_eq ("hello world ", 11);
737 assert_eq ("hello world \t\t ", 11);
738 assert_eq ("hello world\r", 11);
741 #endif /* #if CHECKING_P */
743 /* Helper function for layout's ctor, for sanitizing locations relative
744 to the primary location within a diagnostic.
746 Compare LOC_A and LOC_B to see if it makes sense to print underlines
747 connecting their expanded locations. Doing so is only guaranteed to
748 make sense if the locations share the same macro expansion "history"
749 i.e. they can be traced through the same macro expansions, eventually
750 reaching an ordinary map.
752 This may be too strong a condition, but it effectively sanitizes
753 PR c++/70105, which has an example of printing an expression where the
754 final location of the expression is in a different macro, which
755 erroneously was leading to hundreds of lines of irrelevant source
756 being printed. */
758 static bool
759 compatible_locations_p (location_t loc_a, location_t loc_b)
761 if (IS_ADHOC_LOC (loc_a))
762 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
763 if (IS_ADHOC_LOC (loc_b))
764 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
766 /* If either location is one of the special locations outside of a
767 linemap, they are only compatible if they are equal. */
768 if (loc_a < RESERVED_LOCATION_COUNT
769 || loc_b < RESERVED_LOCATION_COUNT)
770 return loc_a == loc_b;
772 const line_map *map_a = linemap_lookup (line_table, loc_a);
773 linemap_assert (map_a);
775 const line_map *map_b = linemap_lookup (line_table, loc_b);
776 linemap_assert (map_b);
778 /* Are they within the same map? */
779 if (map_a == map_b)
781 /* Are both within the same macro expansion? */
782 if (linemap_macro_expansion_map_p (map_a))
784 /* Expand each location towards the spelling location, and
785 recurse. */
786 const line_map_macro *macro_map = linemap_check_macro (map_a);
787 location_t loc_a_toward_spelling
788 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
789 macro_map,
790 loc_a);
791 location_t loc_b_toward_spelling
792 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
793 macro_map,
794 loc_b);
795 return compatible_locations_p (loc_a_toward_spelling,
796 loc_b_toward_spelling);
799 /* Otherwise they are within the same ordinary map. */
800 return true;
802 else
804 /* Within different maps. */
806 /* If either is within a macro expansion, they are incompatible. */
807 if (linemap_macro_expansion_map_p (map_a)
808 || linemap_macro_expansion_map_p (map_b))
809 return false;
811 /* Within two different ordinary maps; they are compatible iff they
812 are in the same file. */
813 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
814 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
815 return ord_map_a->to_file == ord_map_b->to_file;
819 /* Comparator for sorting fix-it hints. */
821 static int
822 fixit_cmp (const void *p_a, const void *p_b)
824 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
825 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
826 return hint_a->get_start_loc () - hint_b->get_start_loc ();
829 /* Implementation of class layout. */
831 /* Constructor for class layout.
833 Filter the ranges from the rich_location to those that we can
834 sanely print, populating m_layout_ranges and m_fixit_hints.
835 Determine the range of lines that we will print, splitting them
836 up into an ordered list of disjoint spans of contiguous line numbers.
837 Determine m_x_offset, to ensure that the primary caret
838 will fit within the max_width provided by the diagnostic_context. */
840 layout::layout (diagnostic_context * context,
841 rich_location *richloc,
842 diagnostic_t diagnostic_kind)
843 : m_context (context),
844 m_pp (context->printer),
845 m_primary_loc (richloc->get_range (0)->m_loc),
846 m_exploc (richloc->get_expanded_location (0)),
847 m_colorizer (context, diagnostic_kind),
848 m_colorize_source_p (context->colorize_source_p),
849 m_show_labels_p (context->show_labels_p),
850 m_show_line_numbers_p (context->show_line_numbers_p),
851 m_layout_ranges (richloc->get_num_locations ()),
852 m_fixit_hints (richloc->get_num_fixit_hints ()),
853 m_line_spans (1 + richloc->get_num_locations ()),
854 m_linenum_width (0),
855 m_x_offset (0)
857 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
859 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
860 Ignore any ranges that are awkward to handle. */
861 const location_range *loc_range = richloc->get_range (idx);
862 maybe_add_location_range (loc_range, idx, false);
865 /* Populate m_fixit_hints, filtering to only those that are in the
866 same file. */
867 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
869 const fixit_hint *hint = richloc->get_fixit_hint (i);
870 if (validate_fixit_hint_p (hint))
871 m_fixit_hints.safe_push (hint);
874 /* Sort m_fixit_hints. */
875 m_fixit_hints.qsort (fixit_cmp);
877 /* Populate m_line_spans. */
878 calculate_line_spans ();
880 /* Determine m_linenum_width. */
881 gcc_assert (m_line_spans.length () > 0);
882 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
883 int highest_line = last_span->m_last_line;
884 if (highest_line < 0)
885 highest_line = 0;
886 m_linenum_width = num_digits (highest_line);
887 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
888 if (m_line_spans.length () > 1)
889 m_linenum_width = MAX (m_linenum_width, 3);
890 /* If there's a minimum margin width, apply it (subtracting 1 for the space
891 after the line number. */
892 m_linenum_width = MAX (m_linenum_width, context->min_margin_width - 1);
894 /* Adjust m_x_offset.
895 Center the primary caret to fit in max_width; all columns
896 will be adjusted accordingly. */
897 size_t max_width = m_context->caret_max_width;
898 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
899 if (line && (size_t)m_exploc.column <= line.length ())
901 size_t right_margin = CARET_LINE_MARGIN;
902 size_t column = m_exploc.column;
903 if (m_show_line_numbers_p)
904 column += m_linenum_width + 2;
905 right_margin = MIN (line.length () - column, right_margin);
906 right_margin = max_width - right_margin;
907 if (line.length () >= max_width && column > right_margin)
908 m_x_offset = column - right_margin;
909 gcc_assert (m_x_offset >= 0);
912 if (context->show_ruler_p)
913 show_ruler (m_x_offset + max_width);
916 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
917 those that we can sanely print.
919 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
920 (for use as extrinsic state by label ranges FIXME).
922 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
923 filtered against this layout instance's current line spans: it
924 will only be added if the location is fully within the lines
925 already specified by other locations.
927 Return true iff LOC_RANGE was added. */
929 bool
930 layout::maybe_add_location_range (const location_range *loc_range,
931 unsigned original_idx,
932 bool restrict_to_current_line_spans)
934 gcc_assert (loc_range);
936 /* Split the "range" into caret and range information. */
937 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
939 /* Expand the various locations. */
940 expanded_location start
941 = linemap_client_expand_location_to_spelling_point
942 (src_range.m_start, LOCATION_ASPECT_START);
943 expanded_location finish
944 = linemap_client_expand_location_to_spelling_point
945 (src_range.m_finish, LOCATION_ASPECT_FINISH);
946 expanded_location caret
947 = linemap_client_expand_location_to_spelling_point
948 (loc_range->m_loc, LOCATION_ASPECT_CARET);
950 /* If any part of the range isn't in the same file as the primary
951 location of this diagnostic, ignore the range. */
952 if (start.file != m_exploc.file)
953 return false;
954 if (finish.file != m_exploc.file)
955 return false;
956 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
957 if (caret.file != m_exploc.file)
958 return false;
960 /* Sanitize the caret location for non-primary ranges. */
961 if (m_layout_ranges.length () > 0)
962 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
963 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
964 /* Discard any non-primary ranges that can't be printed
965 sanely relative to the primary location. */
966 return false;
968 /* Everything is now known to be in the correct source file,
969 but it may require further sanitization. */
970 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
971 original_idx, loc_range->m_label);
973 /* If we have a range that finishes before it starts (perhaps
974 from something built via macro expansion), printing the
975 range is likely to be nonsensical. Also, attempting to do so
976 breaks assumptions within the printing code (PR c/68473).
977 Similarly, don't attempt to print ranges if one or both ends
978 of the range aren't sane to print relative to the
979 primary location (PR c++/70105). */
980 if (start.line > finish.line
981 || !compatible_locations_p (src_range.m_start, m_primary_loc)
982 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
984 /* Is this the primary location? */
985 if (m_layout_ranges.length () == 0)
987 /* We want to print the caret for the primary location, but
988 we must sanitize away m_start and m_finish. */
989 ri.m_start = ri.m_caret;
990 ri.m_finish = ri.m_caret;
992 else
993 /* This is a non-primary range; ignore it. */
994 return false;
997 /* Potentially filter to just the lines already specified by other
998 locations. This is for use by gcc_rich_location::add_location_if_nearby.
999 The layout ctor doesn't use it, and can't because m_line_spans
1000 hasn't been set up at that point. */
1001 if (restrict_to_current_line_spans)
1003 if (!will_show_line_p (start.line))
1004 return false;
1005 if (!will_show_line_p (finish.line))
1006 return false;
1007 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1008 if (!will_show_line_p (caret.line))
1009 return false;
1012 /* Passed all the tests; add the range to m_layout_ranges so that
1013 it will be printed. */
1014 m_layout_ranges.safe_push (ri);
1015 return true;
1018 /* Return true iff ROW is within one of the line spans for this layout. */
1020 bool
1021 layout::will_show_line_p (linenum_type row) const
1023 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1024 line_span_idx++)
1026 const line_span *line_span = get_line_span (line_span_idx);
1027 if (line_span->contains_line_p (row))
1028 return true;
1030 return false;
1033 /* Print a line showing a gap in the line numbers, for showing the boundary
1034 between two line spans. */
1036 void
1037 layout::print_gap_in_line_numbering ()
1039 gcc_assert (m_show_line_numbers_p);
1041 for (int i = 0; i < m_linenum_width + 1; i++)
1042 pp_character (m_pp, '.');
1044 pp_newline (m_pp);
1047 /* Return true iff we should print a heading when starting the
1048 line span with the given index. */
1050 bool
1051 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1053 /* We print a heading for every change of line span, hence for every
1054 line span after the initial one. */
1055 if (line_span_idx > 0)
1056 return true;
1058 /* We also do it for the initial span if the primary location of the
1059 diagnostic is in a different span. */
1060 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1061 return true;
1063 return false;
1066 /* Get an expanded_location for the first location of interest within
1067 the given line_span.
1068 Used when printing a heading to indicate a new line span. */
1070 expanded_location
1071 layout::get_expanded_location (const line_span *line_span) const
1073 /* Whenever possible, use the caret location. */
1074 if (line_span->contains_line_p (m_exploc.line))
1075 return m_exploc;
1077 /* Otherwise, use the start of the first range that's present
1078 within the line_span. */
1079 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1081 const layout_range *lr = &m_layout_ranges[i];
1082 if (line_span->contains_line_p (lr->m_start.m_line))
1084 expanded_location exploc = m_exploc;
1085 exploc.line = lr->m_start.m_line;
1086 exploc.column = lr->m_start.m_column;
1087 return exploc;
1091 /* Otherwise, use the location of the first fixit-hint present within
1092 the line_span. */
1093 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1095 const fixit_hint *hint = m_fixit_hints[i];
1096 location_t loc = hint->get_start_loc ();
1097 expanded_location exploc = expand_location (loc);
1098 if (line_span->contains_line_p (exploc.line))
1099 return exploc;
1102 /* It should not be possible to have a line span that didn't
1103 contain any of the layout_range or fixit_hint instances. */
1104 gcc_unreachable ();
1105 return m_exploc;
1108 /* Determine if HINT is meaningful to print within this layout. */
1110 bool
1111 layout::validate_fixit_hint_p (const fixit_hint *hint)
1113 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1114 return false;
1115 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1116 return false;
1118 return true;
1121 /* Determine the range of lines affected by HINT.
1122 This assumes that HINT has already been filtered by
1123 validate_fixit_hint_p, and so affects the correct source file. */
1125 static line_span
1126 get_line_span_for_fixit_hint (const fixit_hint *hint)
1128 gcc_assert (hint);
1130 int start_line = LOCATION_LINE (hint->get_start_loc ());
1132 /* For line-insertion fix-it hints, add the previous line to the
1133 span, to give the user more context on the proposed change. */
1134 if (hint->ends_with_newline_p ())
1135 if (start_line > 1)
1136 start_line--;
1138 return line_span (start_line,
1139 LOCATION_LINE (hint->get_next_loc ()));
1142 /* We want to print the pertinent source code at a diagnostic. The
1143 rich_location can contain multiple locations. This will have been
1144 filtered into m_exploc (the caret for the primary location) and
1145 m_layout_ranges, for those ranges within the same source file.
1147 We will print a subset of the lines within the source file in question,
1148 as a collection of "spans" of lines.
1150 This function populates m_line_spans with an ordered, disjoint list of
1151 the line spans of interest.
1153 Printing a gap between line spans takes one line, so, when printing
1154 line numbers, we allow a gap of up to one line between spans when
1155 merging, since it makes more sense to print the source line rather than a
1156 "gap-in-line-numbering" line. When not printing line numbers, it's
1157 better to be more explicit about what's going on, so keeping them as
1158 separate spans is preferred.
1160 For example, if the primary range is on lines 8-10, with secondary ranges
1161 covering lines 5-6 and lines 13-15:
1164 005 |RANGE 1
1165 006 |RANGE 1
1167 008 |PRIMARY RANGE
1168 009 |PRIMARY CARET
1169 010 |PRIMARY RANGE
1172 013 |RANGE 2
1173 014 |RANGE 2
1174 015 |RANGE 2
1177 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1179 With line numbering off (with span headers), we want three spans: lines 5-6,
1180 lines 8-10, and lines 13-15. */
1182 void
1183 layout::calculate_line_spans ()
1185 /* This should only be called once, by the ctor. */
1186 gcc_assert (m_line_spans.length () == 0);
1188 /* Populate tmp_spans with individual spans, for each of
1189 m_exploc, and for m_layout_ranges. */
1190 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1191 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1192 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1194 const layout_range *lr = &m_layout_ranges[i];
1195 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1196 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1197 lr->m_finish.m_line));
1200 /* Also add spans for any fix-it hints, in case they cover other lines. */
1201 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1203 const fixit_hint *hint = m_fixit_hints[i];
1204 gcc_assert (hint);
1205 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1208 /* Sort them. */
1209 tmp_spans.qsort(line_span::comparator);
1211 /* Now iterate through tmp_spans, copying into m_line_spans, and
1212 combining where possible. */
1213 gcc_assert (tmp_spans.length () > 0);
1214 m_line_spans.safe_push (tmp_spans[0]);
1215 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1217 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1218 const line_span *next = &tmp_spans[i];
1219 gcc_assert (next->m_first_line >= current->m_first_line);
1220 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1221 if ((linenum_arith_t)next->m_first_line
1222 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1224 /* We can merge them. */
1225 if (next->m_last_line > current->m_last_line)
1226 current->m_last_line = next->m_last_line;
1228 else
1230 /* No merger possible. */
1231 m_line_spans.safe_push (*next);
1235 /* Verify the result, in m_line_spans. */
1236 gcc_assert (m_line_spans.length () > 0);
1237 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1239 const line_span *prev = &m_line_spans[i - 1];
1240 const line_span *next = &m_line_spans[i];
1241 /* The individual spans must be sane. */
1242 gcc_assert (prev->m_first_line <= prev->m_last_line);
1243 gcc_assert (next->m_first_line <= next->m_last_line);
1244 /* The spans must be ordered. */
1245 gcc_assert (prev->m_first_line < next->m_first_line);
1246 /* There must be a gap of at least one line between separate spans. */
1247 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1251 /* Print line ROW of source code, potentially colorized at any ranges, and
1252 populate *LBOUNDS_OUT.
1253 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1254 is its width. */
1256 void
1257 layout::print_source_line (linenum_type row, const char *line, int line_width,
1258 line_bounds *lbounds_out)
1260 m_colorizer.set_normal_text ();
1262 /* We will stop printing the source line at any trailing
1263 whitespace. */
1264 line_width = get_line_width_without_trailing_whitespace (line,
1265 line_width);
1266 line += m_x_offset;
1268 if (m_show_line_numbers_p)
1270 int width = num_digits (row);
1271 for (int i = 0; i < m_linenum_width - width; i++)
1272 pp_space (m_pp);
1273 pp_printf (m_pp, "%i | ", row);
1275 else
1276 pp_space (m_pp);
1277 int first_non_ws = INT_MAX;
1278 int last_non_ws = 0;
1279 int column;
1280 for (column = 1 + m_x_offset; column <= line_width; column++)
1282 /* Assuming colorization is enabled for the caret and underline
1283 characters, we may also colorize the associated characters
1284 within the source line.
1286 For frontends that generate range information, we color the
1287 associated characters in the source line the same as the
1288 carets and underlines in the annotation line, to make it easier
1289 for the reader to see the pertinent code.
1291 For frontends that only generate carets, we don't colorize the
1292 characters above them, since this would look strange (e.g.
1293 colorizing just the first character in a token). */
1294 if (m_colorize_source_p)
1296 bool in_range_p;
1297 point_state state;
1298 in_range_p = get_state_at_point (row, column,
1299 0, INT_MAX,
1300 &state);
1301 if (in_range_p)
1302 m_colorizer.set_range (state.range_idx);
1303 else
1304 m_colorizer.set_normal_text ();
1306 char c = *line;
1307 if (c == '\0' || c == '\t' || c == '\r')
1308 c = ' ';
1309 if (c != ' ')
1311 last_non_ws = column;
1312 if (first_non_ws == INT_MAX)
1313 first_non_ws = column;
1315 pp_character (m_pp, c);
1316 line++;
1318 print_newline ();
1320 lbounds_out->m_first_non_ws = first_non_ws;
1321 lbounds_out->m_last_non_ws = last_non_ws;
1324 /* Determine if we should print an annotation line for ROW.
1325 i.e. if any of m_layout_ranges contains ROW. */
1327 bool
1328 layout::should_print_annotation_line_p (linenum_type row) const
1330 layout_range *range;
1331 int i;
1332 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1334 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1335 return false;
1336 if (range->intersects_line_p (row))
1337 return true;
1339 return false;
1342 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1343 margin, which is empty for annotation lines. Otherwise, do nothing. */
1345 void
1346 layout::start_annotation_line (char margin_char) const
1348 if (m_show_line_numbers_p)
1350 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1351 of it, right-aligned, padded with spaces. */
1352 int i;
1353 for (i = 0; i < m_linenum_width - 3; i++)
1354 pp_space (m_pp);
1355 for (; i < m_linenum_width; i++)
1356 pp_character (m_pp, margin_char);
1357 pp_string (m_pp, " |");
1361 /* Print a line consisting of the caret/underlines for the given
1362 source line. */
1364 void
1365 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1367 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1368 lbounds.m_last_non_ws);
1370 start_annotation_line ();
1371 pp_space (m_pp);
1373 for (int column = 1 + m_x_offset; column < x_bound; column++)
1375 bool in_range_p;
1376 point_state state;
1377 in_range_p = get_state_at_point (row, column,
1378 lbounds.m_first_non_ws,
1379 lbounds.m_last_non_ws,
1380 &state);
1381 if (in_range_p)
1383 /* Within a range. Draw either the caret or an underline. */
1384 m_colorizer.set_range (state.range_idx);
1385 if (state.draw_caret_p)
1387 /* Draw the caret. */
1388 char caret_char;
1389 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1390 caret_char = m_context->caret_chars[state.range_idx];
1391 else
1392 caret_char = '^';
1393 pp_character (m_pp, caret_char);
1395 else
1396 pp_character (m_pp, '~');
1398 else
1400 /* Not in a range. */
1401 m_colorizer.set_normal_text ();
1402 pp_character (m_pp, ' ');
1405 print_newline ();
1408 /* Implementation detail of layout::print_any_labels.
1410 A label within the given row of source. */
1412 struct line_label
1414 line_label (int state_idx, int column, label_text text)
1415 : m_state_idx (state_idx), m_column (column),
1416 m_text (text), m_length (strlen (text.m_buffer)),
1417 m_label_line (0)
1420 /* Sorting is primarily by column, then by state index. */
1421 static int comparator (const void *p1, const void *p2)
1423 const line_label *ll1 = (const line_label *)p1;
1424 const line_label *ll2 = (const line_label *)p2;
1425 int column_cmp = compare (ll1->m_column, ll2->m_column);
1426 if (column_cmp)
1427 return column_cmp;
1428 return compare (ll1->m_state_idx, ll2->m_state_idx);
1431 int m_state_idx;
1432 int m_column;
1433 label_text m_text;
1434 size_t m_length;
1435 int m_label_line;
1438 /* Print any labels in this row. */
1439 void
1440 layout::print_any_labels (linenum_type row)
1442 int i;
1443 auto_vec<line_label> labels;
1445 /* Gather the labels that are to be printed into "labels". */
1447 layout_range *range;
1448 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1450 /* Most ranges don't have labels, so reject this first. */
1451 if (range->m_label == NULL)
1452 continue;
1454 /* The range's caret must be on this line. */
1455 if (range->m_caret.m_line != row)
1456 continue;
1458 /* Reject labels that aren't fully visible due to clipping
1459 by m_x_offset. */
1460 if (range->m_caret.m_column <= m_x_offset)
1461 continue;
1463 label_text text;
1464 text = range->m_label->get_text (range->m_original_idx);
1466 /* Allow for labels that return NULL from their get_text
1467 implementation (so e.g. such labels can control their own
1468 visibility). */
1469 if (text.m_buffer == NULL)
1470 continue;
1472 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1476 /* Bail out if there are no labels on this row. */
1477 if (labels.length () == 0)
1478 return;
1480 /* Sort them. */
1481 labels.qsort(line_label::comparator);
1483 /* Figure out how many "label lines" we need, and which
1484 one each label is printed in.
1486 For example, if the labels aren't too densely packed,
1487 we can fit them on the same line, giving two "label lines":
1489 foo + bar
1490 ~~~ ~~~
1491 | | : label line 0
1492 l0 l1 : label line 1
1494 If they would touch each other or overlap, then we need
1495 additional "label lines":
1497 foo + bar
1498 ~~~ ~~~
1499 | | : label line 0
1500 | label 1 : label line 1
1501 label 0 : label line 2
1503 Place the final label on label line 1, and work backwards, adding
1504 label lines as needed.
1506 If multiple labels are at the same place, put them on separate
1507 label lines:
1509 foo + bar
1510 ^ : label line 0
1511 | : label line 1
1512 label 1 : label line 2
1513 label 0 : label line 3. */
1515 int max_label_line = 1;
1517 int next_column = INT_MAX;
1518 line_label *label;
1519 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1521 /* Would this label "touch" or overlap the next label? */
1522 if (label->m_column + label->m_length >= (size_t)next_column)
1523 max_label_line++;
1525 label->m_label_line = max_label_line;
1526 next_column = label->m_column;
1530 /* Print the "label lines". For each label within the line, print
1531 either a vertical bar ('|') for the labels that are lower down, or the
1532 labels themselves once we've reached their line. */
1534 /* Keep track of in which column we last printed a vertical bar.
1535 This allows us to suppress duplicate vertical bars for the case
1536 where multiple labels are on one column. */
1537 int last_vbar = 0;
1538 for (int label_line = 0; label_line <= max_label_line; label_line++)
1540 start_annotation_line ();
1541 pp_space (m_pp);
1542 int column = 1 + m_x_offset;
1543 line_label *label;
1544 FOR_EACH_VEC_ELT (labels, i, label)
1546 if (label_line > label->m_label_line)
1547 /* We've printed all the labels for this label line. */
1548 break;
1550 if (label_line == label->m_label_line)
1552 gcc_assert (column <= label->m_column);
1553 move_to_column (&column, label->m_column, true);
1554 m_colorizer.set_range (label->m_state_idx);
1555 pp_string (m_pp, label->m_text.m_buffer);
1556 m_colorizer.set_normal_text ();
1557 column += label->m_length;
1559 else if (label->m_column != last_vbar)
1561 gcc_assert (column <= label->m_column);
1562 move_to_column (&column, label->m_column, true);
1563 m_colorizer.set_range (label->m_state_idx);
1564 pp_character (m_pp, '|');
1565 m_colorizer.set_normal_text ();
1566 last_vbar = column;
1567 column++;
1570 print_newline ();
1574 /* Clean up. */
1576 line_label *label;
1577 FOR_EACH_VEC_ELT (labels, i, label)
1578 label->m_text.maybe_free ();
1582 /* If there are any fixit hints inserting new lines before source line ROW,
1583 print them.
1585 They are printed on lines of their own, before the source line
1586 itself, with a leading '+'. */
1588 void
1589 layout::print_leading_fixits (linenum_type row)
1591 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1593 const fixit_hint *hint = m_fixit_hints[i];
1595 if (!hint->ends_with_newline_p ())
1596 /* Not a newline fixit; print it in print_trailing_fixits. */
1597 continue;
1599 gcc_assert (hint->insertion_p ());
1601 if (hint->affects_line_p (m_exploc.file, row))
1603 /* Printing the '+' with normal colorization
1604 and the inserted line with "insert" colorization
1605 helps them stand out from each other, and from
1606 the surrounding text. */
1607 m_colorizer.set_normal_text ();
1608 start_annotation_line ('+');
1609 pp_character (m_pp, '+');
1610 m_colorizer.set_fixit_insert ();
1611 /* Print all but the trailing newline of the fix-it hint.
1612 We have to print the newline separately to avoid
1613 getting additional pp prefixes printed. */
1614 for (size_t i = 0; i < hint->get_length () - 1; i++)
1615 pp_character (m_pp, hint->get_string ()[i]);
1616 m_colorizer.set_normal_text ();
1617 pp_newline (m_pp);
1622 /* Subroutine of layout::print_trailing_fixits.
1624 Determine if the annotation line printed for LINE contained
1625 the exact range from START_COLUMN to FINISH_COLUMN. */
1627 bool
1628 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1629 int finish_column) const
1631 layout_range *range;
1632 int i;
1633 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1634 if (range->m_start.m_line == line
1635 && range->m_start.m_column == start_column
1636 && range->m_finish.m_line == line
1637 && range->m_finish.m_column == finish_column)
1638 return true;
1639 return false;
1642 /* Classes for printing trailing fix-it hints i.e. those that
1643 don't add new lines.
1645 For insertion, these can look like:
1647 new_text
1649 For replacement, these can look like:
1651 ------------- : underline showing affected range
1652 new_text
1654 For deletion, these can look like:
1656 ------------- : underline showing affected range
1658 This can become confusing if they overlap, and so we need
1659 to do some preprocessing to decide what to print.
1660 We use the list of fixit_hint instances affecting the line
1661 to build a list of "correction" instances, and print the
1662 latter.
1664 For example, consider a set of fix-its for converting
1665 a C-style cast to a C++ const_cast.
1667 Given:
1669 ..000000000111111111122222222223333333333.
1670 ..123456789012345678901234567890123456789.
1671 foo *f = (foo *)ptr->field;
1672 ^~~~~
1674 and the fix-it hints:
1675 - replace col 10 (the open paren) with "const_cast<"
1676 - replace col 16 (the close paren) with "> ("
1677 - insert ")" before col 27
1679 then we would get odd-looking output:
1681 foo *f = (foo *)ptr->field;
1682 ^~~~~
1684 const_cast<
1686 > ( )
1688 It would be better to detect when fixit hints are going to
1689 overlap (those that require new lines), and to consolidate
1690 the printing of such fixits, giving something like:
1692 foo *f = (foo *)ptr->field;
1693 ^~~~~
1694 -----------------
1695 const_cast<foo *> (ptr->field)
1697 This works by detecting when the printing would overlap, and
1698 effectively injecting no-op replace hints into the gaps between
1699 such fix-its, so that the printing joins up.
1701 In the above example, the overlap of:
1702 - replace col 10 (the open paren) with "const_cast<"
1703 and:
1704 - replace col 16 (the close paren) with "> ("
1705 is fixed by injecting a no-op:
1706 - replace cols 11-15 with themselves ("foo *")
1707 and consolidating these, making:
1708 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1709 i.e.:
1710 - replace cols 10-16 with "const_cast<foo *> ("
1712 This overlaps with the final fix-it hint:
1713 - insert ")" before col 27
1714 and so we repeat the consolidation process, by injecting
1715 a no-op:
1716 - replace cols 17-26 with themselves ("ptr->field")
1717 giving:
1718 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1719 i.e.:
1720 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1722 and is thus printed as desired. */
1724 /* A range of columns within a line. */
1726 struct column_range
1728 column_range (int start_, int finish_) : start (start_), finish (finish_)
1730 /* We must have either a range, or an insertion. */
1731 gcc_assert (start <= finish || finish == start - 1);
1734 bool operator== (const column_range &other) const
1736 return start == other.start && finish == other.finish;
1739 int start;
1740 int finish;
1743 /* Get the range of columns that HINT would affect. */
1745 static column_range
1746 get_affected_columns (const fixit_hint *hint)
1748 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1749 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1751 return column_range (start_column, finish_column);
1754 /* Get the range of columns that would be printed for HINT. */
1756 static column_range
1757 get_printed_columns (const fixit_hint *hint)
1759 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1760 int final_hint_column = start_column + hint->get_length () - 1;
1761 if (hint->insertion_p ())
1763 return column_range (start_column, final_hint_column);
1765 else
1767 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1769 return column_range (start_column,
1770 MAX (finish_column, final_hint_column));
1774 /* A correction on a particular line.
1775 This describes a plan for how to print one or more fixit_hint
1776 instances that affected the line, potentially consolidating hints
1777 into corrections to make the result easier for the user to read. */
1779 struct correction
1781 correction (column_range affected_columns,
1782 column_range printed_columns,
1783 const char *new_text, size_t new_text_len)
1784 : m_affected_columns (affected_columns),
1785 m_printed_columns (printed_columns),
1786 m_text (xstrdup (new_text)),
1787 m_len (new_text_len),
1788 m_alloc_sz (new_text_len + 1)
1792 ~correction () { free (m_text); }
1794 bool insertion_p () const
1796 return m_affected_columns.start == m_affected_columns.finish + 1;
1799 void ensure_capacity (size_t len);
1800 void ensure_terminated ();
1802 void overwrite (int dst_offset, const char_span &src_span)
1804 gcc_assert (dst_offset >= 0);
1805 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1806 memcpy (m_text + dst_offset, src_span.get_buffer (),
1807 src_span.length ());
1810 /* If insert, then start: the column before which the text
1811 is to be inserted, and finish is offset by the length of
1812 the replacement.
1813 If replace, then the range of columns affected. */
1814 column_range m_affected_columns;
1816 /* If insert, then start: the column before which the text
1817 is to be inserted, and finish is offset by the length of
1818 the replacement.
1819 If replace, then the range of columns affected. */
1820 column_range m_printed_columns;
1822 /* The text to be inserted/used as replacement. */
1823 char *m_text;
1824 size_t m_len;
1825 size_t m_alloc_sz;
1828 /* Ensure that m_text can hold a string of length LEN
1829 (plus 1 for 0-termination). */
1831 void
1832 correction::ensure_capacity (size_t len)
1834 /* Allow 1 extra byte for 0-termination. */
1835 if (m_alloc_sz < (len + 1))
1837 size_t new_alloc_sz = (len + 1) * 2;
1838 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1839 m_alloc_sz = new_alloc_sz;
1843 /* Ensure that m_text is 0-terminated. */
1845 void
1846 correction::ensure_terminated ()
1848 /* 0-terminate the buffer. */
1849 gcc_assert (m_len < m_alloc_sz);
1850 m_text[m_len] = '\0';
1853 /* A list of corrections affecting a particular line.
1854 This is used by layout::print_trailing_fixits for planning
1855 how to print the fix-it hints affecting the line. */
1857 struct line_corrections
1859 line_corrections (const char *filename, linenum_type row)
1860 : m_filename (filename), m_row (row)
1862 ~line_corrections ();
1864 void add_hint (const fixit_hint *hint);
1866 const char *m_filename;
1867 linenum_type m_row;
1868 auto_vec <correction *> m_corrections;
1871 /* struct line_corrections. */
1873 line_corrections::~line_corrections ()
1875 unsigned i;
1876 correction *c;
1877 FOR_EACH_VEC_ELT (m_corrections, i, c)
1878 delete c;
1881 /* A struct wrapping a particular source line, allowing
1882 run-time bounds-checking of accesses in a checked build. */
1884 struct source_line
1886 source_line (const char *filename, int line);
1888 char_span as_span () { return char_span (chars, width); }
1890 const char *chars;
1891 int width;
1894 /* source_line's ctor. */
1896 source_line::source_line (const char *filename, int line)
1898 char_span span = location_get_source_line (filename, line);
1899 chars = span.get_buffer ();
1900 width = span.length ();
1903 /* Add HINT to the corrections for this line.
1904 Attempt to consolidate nearby hints so that they will not
1905 overlap with printed. */
1907 void
1908 line_corrections::add_hint (const fixit_hint *hint)
1910 column_range affected_columns = get_affected_columns (hint);
1911 column_range printed_columns = get_printed_columns (hint);
1913 /* Potentially consolidate. */
1914 if (!m_corrections.is_empty ())
1916 correction *last_correction
1917 = m_corrections[m_corrections.length () - 1];
1919 /* The following consolidation code assumes that the fix-it hints
1920 have been sorted by start (done within layout's ctor). */
1921 gcc_assert (affected_columns.start
1922 >= last_correction->m_affected_columns.start);
1923 gcc_assert (printed_columns.start
1924 >= last_correction->m_printed_columns.start);
1926 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1928 /* We have two hints for which the printed forms of the hints
1929 would touch or overlap, so we need to consolidate them to avoid
1930 confusing the user.
1931 Attempt to inject a "replace" correction from immediately
1932 after the end of the last hint to immediately before the start
1933 of the next hint. */
1934 column_range between (last_correction->m_affected_columns.finish + 1,
1935 printed_columns.start - 1);
1937 /* Try to read the source. */
1938 source_line line (m_filename, m_row);
1939 if (line.chars && between.finish < line.width)
1941 /* Consolidate into the last correction:
1942 add a no-op "replace" of the "between" text, and
1943 add the text from the new hint. */
1944 int old_len = last_correction->m_len;
1945 gcc_assert (old_len >= 0);
1946 int between_len = between.finish + 1 - between.start;
1947 gcc_assert (between_len >= 0);
1948 int new_len = old_len + between_len + hint->get_length ();
1949 gcc_assert (new_len >= 0);
1950 last_correction->ensure_capacity (new_len);
1951 last_correction->overwrite
1952 (old_len,
1953 line.as_span ().subspan (between.start - 1,
1954 between.finish + 1 - between.start));
1955 last_correction->overwrite (old_len + between_len,
1956 char_span (hint->get_string (),
1957 hint->get_length ()));
1958 last_correction->m_len = new_len;
1959 last_correction->ensure_terminated ();
1960 last_correction->m_affected_columns.finish
1961 = affected_columns.finish;
1962 last_correction->m_printed_columns.finish
1963 += between_len + hint->get_length ();
1964 return;
1969 /* If no consolidation happened, add a new correction instance. */
1970 m_corrections.safe_push (new correction (affected_columns,
1971 printed_columns,
1972 hint->get_string (),
1973 hint->get_length ()));
1976 /* If there are any fixit hints on source line ROW, print them.
1977 They are printed in order, attempting to combine them onto lines, but
1978 starting new lines if necessary.
1979 Fix-it hints that insert new lines are handled separately,
1980 in layout::print_leading_fixits. */
1982 void
1983 layout::print_trailing_fixits (linenum_type row)
1985 /* Build a list of correction instances for the line,
1986 potentially consolidating hints (for the sake of readability). */
1987 line_corrections corrections (m_exploc.file, row);
1988 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1990 const fixit_hint *hint = m_fixit_hints[i];
1992 /* Newline fixits are handled by layout::print_leading_fixits. */
1993 if (hint->ends_with_newline_p ())
1994 continue;
1996 if (hint->affects_line_p (m_exploc.file, row))
1997 corrections.add_hint (hint);
2000 /* Now print the corrections. */
2001 unsigned i;
2002 correction *c;
2003 int column = m_x_offset;
2005 if (!corrections.m_corrections.is_empty ())
2006 start_annotation_line ();
2008 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2010 /* For now we assume each fixit hint can only touch one line. */
2011 if (c->insertion_p ())
2013 /* This assumes the insertion just affects one line. */
2014 int start_column = c->m_printed_columns.start;
2015 move_to_column (&column, start_column, true);
2016 m_colorizer.set_fixit_insert ();
2017 pp_string (m_pp, c->m_text);
2018 m_colorizer.set_normal_text ();
2019 column += c->m_len;
2021 else
2023 /* If the range of the replacement wasn't printed in the
2024 annotation line, then print an extra underline to
2025 indicate exactly what is being replaced.
2026 Always show it for removals. */
2027 int start_column = c->m_affected_columns.start;
2028 int finish_column = c->m_affected_columns.finish;
2029 if (!annotation_line_showed_range_p (row, start_column,
2030 finish_column)
2031 || c->m_len == 0)
2033 move_to_column (&column, start_column, true);
2034 m_colorizer.set_fixit_delete ();
2035 for (; column <= finish_column; column++)
2036 pp_character (m_pp, '-');
2037 m_colorizer.set_normal_text ();
2039 /* Print the replacement text. REPLACE also covers
2040 removals, so only do this extra work (potentially starting
2041 a new line) if we have actual replacement text. */
2042 if (c->m_len > 0)
2044 move_to_column (&column, start_column, true);
2045 m_colorizer.set_fixit_insert ();
2046 pp_string (m_pp, c->m_text);
2047 m_colorizer.set_normal_text ();
2048 column += c->m_len;
2053 /* Add a trailing newline, if necessary. */
2054 move_to_column (&column, 0, false);
2057 /* Disable any colorization and emit a newline. */
2059 void
2060 layout::print_newline ()
2062 m_colorizer.set_normal_text ();
2063 pp_newline (m_pp);
2066 /* Return true if (ROW/COLUMN) is within a range of the layout.
2067 If it returns true, OUT_STATE is written to, with the
2068 range index, and whether we should draw the caret at
2069 (ROW/COLUMN) (as opposed to an underline). */
2071 bool
2072 layout::get_state_at_point (/* Inputs. */
2073 linenum_type row, int column,
2074 int first_non_ws, int last_non_ws,
2075 /* Outputs. */
2076 point_state *out_state)
2078 layout_range *range;
2079 int i;
2080 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2082 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2083 /* Bail out early, so that such ranges don't affect underlining or
2084 source colorization. */
2085 continue;
2087 if (range->contains_point (row, column))
2089 out_state->range_idx = i;
2091 /* Are we at the range's caret? is it visible? */
2092 out_state->draw_caret_p = false;
2093 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2094 && row == range->m_caret.m_line
2095 && column == range->m_caret.m_column)
2096 out_state->draw_caret_p = true;
2098 /* Within a multiline range, don't display any underline
2099 in any leading or trailing whitespace on a line.
2100 We do display carets, however. */
2101 if (!out_state->draw_caret_p)
2102 if (column < first_non_ws || column > last_non_ws)
2103 return false;
2105 /* We are within a range. */
2106 return true;
2110 return false;
2113 /* Helper function for use by layout::print_line when printing the
2114 annotation line under the source line.
2115 Get the column beyond the rightmost one that could contain a caret or
2116 range marker, given that we stop rendering at trailing whitespace.
2117 ROW is the source line within the given file.
2118 CARET_COLUMN is the column of range 0's caret.
2119 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2120 character of source (as determined when printing the source line). */
2123 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2124 int last_non_ws_column)
2126 int result = caret_column + 1;
2128 layout_range *range;
2129 int i;
2130 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2132 if (row >= range->m_start.m_line)
2134 if (range->m_finish.m_line == row)
2136 /* On the final line within a range; ensure that
2137 we render up to the end of the range. */
2138 if (result <= range->m_finish.m_column)
2139 result = range->m_finish.m_column + 1;
2141 else if (row < range->m_finish.m_line)
2143 /* Within a multiline range; ensure that we render up to the
2144 last non-whitespace column. */
2145 if (result <= last_non_ws_column)
2146 result = last_non_ws_column + 1;
2151 return result;
2154 /* Given *COLUMN as an x-coordinate, print spaces to position
2155 successive output at DEST_COLUMN, printing a newline if necessary,
2156 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2157 left margin after any newline. */
2159 void
2160 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2162 /* Start a new line if we need to. */
2163 if (*column > dest_column)
2165 print_newline ();
2166 if (add_left_margin)
2167 start_annotation_line ();
2168 *column = m_x_offset;
2171 while (*column < dest_column)
2173 pp_space (m_pp);
2174 (*column)++;
2178 /* For debugging layout issues, render a ruler giving column numbers
2179 (after the 1-column indent). */
2181 void
2182 layout::show_ruler (int max_column) const
2184 /* Hundreds. */
2185 if (max_column > 99)
2187 start_annotation_line ();
2188 pp_space (m_pp);
2189 for (int column = 1 + m_x_offset; column <= max_column; column++)
2190 if (column % 10 == 0)
2191 pp_character (m_pp, '0' + (column / 100) % 10);
2192 else
2193 pp_space (m_pp);
2194 pp_newline (m_pp);
2197 /* Tens. */
2198 start_annotation_line ();
2199 pp_space (m_pp);
2200 for (int column = 1 + m_x_offset; column <= max_column; column++)
2201 if (column % 10 == 0)
2202 pp_character (m_pp, '0' + (column / 10) % 10);
2203 else
2204 pp_space (m_pp);
2205 pp_newline (m_pp);
2207 /* Units. */
2208 start_annotation_line ();
2209 pp_space (m_pp);
2210 for (int column = 1 + m_x_offset; column <= max_column; column++)
2211 pp_character (m_pp, '0' + (column % 10));
2212 pp_newline (m_pp);
2215 /* Print leading fix-its (for new lines inserted before the source line)
2216 then the source line, followed by an annotation line
2217 consisting of any caret/underlines, then any fixits.
2218 If the source line can't be read, print nothing. */
2219 void
2220 layout::print_line (linenum_type row)
2222 char_span line = location_get_source_line (m_exploc.file, row);
2223 if (!line)
2224 return;
2226 line_bounds lbounds;
2227 print_leading_fixits (row);
2228 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2229 if (should_print_annotation_line_p (row))
2230 print_annotation_line (row, lbounds);
2231 if (m_show_labels_p)
2232 print_any_labels (row);
2233 print_trailing_fixits (row);
2236 } /* End of anonymous namespace. */
2238 /* If LOC is within the spans of lines that will already be printed for
2239 this gcc_rich_location, then add it as a secondary location and return true.
2241 Otherwise return false. */
2243 bool
2244 gcc_rich_location::add_location_if_nearby (location_t loc)
2246 /* Use the layout location-handling logic to sanitize LOC,
2247 filtering it to the current line spans within a temporary
2248 layout instance. */
2249 layout layout (global_dc, this, DK_ERROR);
2250 location_range loc_range;
2251 loc_range.m_loc = loc;
2252 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2253 if (!layout.maybe_add_location_range (&loc_range, 0, true))
2254 return false;
2256 add_range (loc);
2257 return true;
2260 /* Print the physical source code corresponding to the location of
2261 this diagnostic, with additional annotations. */
2263 void
2264 diagnostic_show_locus (diagnostic_context * context,
2265 rich_location *richloc,
2266 diagnostic_t diagnostic_kind)
2268 pp_newline (context->printer);
2270 location_t loc = richloc->get_loc ();
2271 /* Do nothing if source-printing has been disabled. */
2272 if (!context->show_caret)
2273 return;
2275 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2276 if (loc <= BUILTINS_LOCATION)
2277 return;
2279 /* Don't print the same source location twice in a row, unless we have
2280 fix-it hints. */
2281 if (loc == context->last_location
2282 && richloc->get_num_fixit_hints () == 0)
2283 return;
2285 context->last_location = loc;
2287 char *saved_prefix = pp_take_prefix (context->printer);
2288 pp_set_prefix (context->printer, NULL);
2290 layout layout (context, richloc, diagnostic_kind);
2291 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2292 line_span_idx++)
2294 const line_span *line_span = layout.get_line_span (line_span_idx);
2295 if (context->show_line_numbers_p)
2297 /* With line numbers, we should show whenever the line-numbering
2298 "jumps". */
2299 if (line_span_idx > 0)
2300 layout.print_gap_in_line_numbering ();
2302 else
2304 /* Without line numbers, we print headings for some line spans. */
2305 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2307 expanded_location exploc
2308 = layout.get_expanded_location (line_span);
2309 context->start_span (context, exploc);
2312 /* Iterate over the lines within this span (using linenum_arith_t to
2313 avoid overflow with 0xffffffff causing an infinite loop). */
2314 linenum_arith_t last_line = line_span->get_last_line ();
2315 for (linenum_arith_t row = line_span->get_first_line ();
2316 row <= last_line; row++)
2317 layout.print_line (row);
2320 pp_set_prefix (context->printer, saved_prefix);
2323 #if CHECKING_P
2325 namespace selftest {
2327 /* Selftests for diagnostic_show_locus. */
2329 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2331 static void
2332 test_diagnostic_show_locus_unknown_location ()
2334 test_diagnostic_context dc;
2335 rich_location richloc (line_table, UNKNOWN_LOCATION);
2336 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2337 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2340 /* Verify that diagnostic_show_locus works sanely for various
2341 single-line cases.
2343 All of these work on the following 1-line source file:
2344 .0000000001111111
2345 .1234567890123456
2346 "foo = bar.field;\n"
2347 which is set up by test_diagnostic_show_locus_one_liner and calls
2348 them. */
2350 /* Just a caret. */
2352 static void
2353 test_one_liner_simple_caret ()
2355 test_diagnostic_context dc;
2356 location_t caret = linemap_position_for_column (line_table, 10);
2357 rich_location richloc (line_table, caret);
2358 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2359 ASSERT_STREQ ("\n"
2360 " foo = bar.field;\n"
2361 " ^\n",
2362 pp_formatted_text (dc.printer));
2365 /* Caret and range. */
2367 static void
2368 test_one_liner_caret_and_range ()
2370 test_diagnostic_context dc;
2371 location_t caret = linemap_position_for_column (line_table, 10);
2372 location_t start = linemap_position_for_column (line_table, 7);
2373 location_t finish = linemap_position_for_column (line_table, 15);
2374 location_t loc = make_location (caret, start, finish);
2375 rich_location richloc (line_table, loc);
2376 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2377 ASSERT_STREQ ("\n"
2378 " foo = bar.field;\n"
2379 " ~~~^~~~~~\n",
2380 pp_formatted_text (dc.printer));
2383 /* Multiple ranges and carets. */
2385 static void
2386 test_one_liner_multiple_carets_and_ranges ()
2388 test_diagnostic_context dc;
2389 location_t foo
2390 = make_location (linemap_position_for_column (line_table, 2),
2391 linemap_position_for_column (line_table, 1),
2392 linemap_position_for_column (line_table, 3));
2393 dc.caret_chars[0] = 'A';
2395 location_t bar
2396 = make_location (linemap_position_for_column (line_table, 8),
2397 linemap_position_for_column (line_table, 7),
2398 linemap_position_for_column (line_table, 9));
2399 dc.caret_chars[1] = 'B';
2401 location_t field
2402 = make_location (linemap_position_for_column (line_table, 13),
2403 linemap_position_for_column (line_table, 11),
2404 linemap_position_for_column (line_table, 15));
2405 dc.caret_chars[2] = 'C';
2407 rich_location richloc (line_table, foo);
2408 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2409 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2410 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2411 ASSERT_STREQ ("\n"
2412 " foo = bar.field;\n"
2413 " ~A~ ~B~ ~~C~~\n",
2414 pp_formatted_text (dc.printer));
2417 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2419 static void
2420 test_one_liner_fixit_insert_before ()
2422 test_diagnostic_context dc;
2423 location_t caret = linemap_position_for_column (line_table, 7);
2424 rich_location richloc (line_table, caret);
2425 richloc.add_fixit_insert_before ("&");
2426 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2427 ASSERT_STREQ ("\n"
2428 " foo = bar.field;\n"
2429 " ^\n"
2430 " &\n",
2431 pp_formatted_text (dc.printer));
2434 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2436 static void
2437 test_one_liner_fixit_insert_after ()
2439 test_diagnostic_context dc;
2440 location_t start = linemap_position_for_column (line_table, 1);
2441 location_t finish = linemap_position_for_column (line_table, 3);
2442 location_t foo = make_location (start, start, finish);
2443 rich_location richloc (line_table, foo);
2444 richloc.add_fixit_insert_after ("[0]");
2445 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2446 ASSERT_STREQ ("\n"
2447 " foo = bar.field;\n"
2448 " ^~~\n"
2449 " [0]\n",
2450 pp_formatted_text (dc.printer));
2453 /* Removal fix-it hint: removal of the ".field". */
2455 static void
2456 test_one_liner_fixit_remove ()
2458 test_diagnostic_context dc;
2459 location_t start = linemap_position_for_column (line_table, 10);
2460 location_t finish = linemap_position_for_column (line_table, 15);
2461 location_t dot = make_location (start, start, finish);
2462 rich_location richloc (line_table, dot);
2463 richloc.add_fixit_remove ();
2464 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2465 ASSERT_STREQ ("\n"
2466 " foo = bar.field;\n"
2467 " ^~~~~~\n"
2468 " ------\n",
2469 pp_formatted_text (dc.printer));
2472 /* Replace fix-it hint: replacing "field" with "m_field". */
2474 static void
2475 test_one_liner_fixit_replace ()
2477 test_diagnostic_context dc;
2478 location_t start = linemap_position_for_column (line_table, 11);
2479 location_t finish = linemap_position_for_column (line_table, 15);
2480 location_t field = make_location (start, start, finish);
2481 rich_location richloc (line_table, field);
2482 richloc.add_fixit_replace ("m_field");
2483 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2484 ASSERT_STREQ ("\n"
2485 " foo = bar.field;\n"
2486 " ^~~~~\n"
2487 " m_field\n",
2488 pp_formatted_text (dc.printer));
2491 /* Replace fix-it hint: replacing "field" with "m_field",
2492 but where the caret was elsewhere. */
2494 static void
2495 test_one_liner_fixit_replace_non_equal_range ()
2497 test_diagnostic_context dc;
2498 location_t equals = linemap_position_for_column (line_table, 5);
2499 location_t start = linemap_position_for_column (line_table, 11);
2500 location_t finish = linemap_position_for_column (line_table, 15);
2501 rich_location richloc (line_table, equals);
2502 source_range range;
2503 range.m_start = start;
2504 range.m_finish = finish;
2505 richloc.add_fixit_replace (range, "m_field");
2506 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2507 /* The replacement range is not indicated in the annotation line, so
2508 it should be indicated via an additional underline. */
2509 ASSERT_STREQ ("\n"
2510 " foo = bar.field;\n"
2511 " ^\n"
2512 " -----\n"
2513 " m_field\n",
2514 pp_formatted_text (dc.printer));
2517 /* Replace fix-it hint: replacing "field" with "m_field",
2518 where the caret was elsewhere, but where a secondary range
2519 exactly covers "field". */
2521 static void
2522 test_one_liner_fixit_replace_equal_secondary_range ()
2524 test_diagnostic_context dc;
2525 location_t equals = linemap_position_for_column (line_table, 5);
2526 location_t start = linemap_position_for_column (line_table, 11);
2527 location_t finish = linemap_position_for_column (line_table, 15);
2528 rich_location richloc (line_table, equals);
2529 location_t field = make_location (start, start, finish);
2530 richloc.add_range (field);
2531 richloc.add_fixit_replace (field, "m_field");
2532 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2533 /* The replacement range is indicated in the annotation line,
2534 so it shouldn't be indicated via an additional underline. */
2535 ASSERT_STREQ ("\n"
2536 " foo = bar.field;\n"
2537 " ^ ~~~~~\n"
2538 " m_field\n",
2539 pp_formatted_text (dc.printer));
2542 /* Verify that we can use ad-hoc locations when adding fixits to a
2543 rich_location. */
2545 static void
2546 test_one_liner_fixit_validation_adhoc_locations ()
2548 /* Generate a range that's too long to be packed, so must
2549 be stored as an ad-hoc location (given the defaults
2550 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2551 const location_t c7 = linemap_position_for_column (line_table, 7);
2552 const location_t c47 = linemap_position_for_column (line_table, 47);
2553 const location_t loc = make_location (c7, c7, c47);
2555 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2556 return;
2558 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2560 /* Insert. */
2562 rich_location richloc (line_table, loc);
2563 richloc.add_fixit_insert_before (loc, "test");
2564 /* It should not have been discarded by the validator. */
2565 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2567 test_diagnostic_context dc;
2568 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2569 ASSERT_STREQ ("\n"
2570 " foo = bar.field;\n"
2571 " ^~~~~~~~~~ \n"
2572 " test\n",
2573 pp_formatted_text (dc.printer));
2576 /* Remove. */
2578 rich_location richloc (line_table, loc);
2579 source_range range = source_range::from_locations (loc, c47);
2580 richloc.add_fixit_remove (range);
2581 /* It should not have been discarded by the validator. */
2582 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2584 test_diagnostic_context dc;
2585 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2586 ASSERT_STREQ ("\n"
2587 " foo = bar.field;\n"
2588 " ^~~~~~~~~~ \n"
2589 " -----------------------------------------\n",
2590 pp_formatted_text (dc.printer));
2593 /* Replace. */
2595 rich_location richloc (line_table, loc);
2596 source_range range = source_range::from_locations (loc, c47);
2597 richloc.add_fixit_replace (range, "test");
2598 /* It should not have been discarded by the validator. */
2599 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2601 test_diagnostic_context dc;
2602 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2603 ASSERT_STREQ ("\n"
2604 " foo = bar.field;\n"
2605 " ^~~~~~~~~~ \n"
2606 " test\n",
2607 pp_formatted_text (dc.printer));
2611 /* Test of consolidating insertions at the same location. */
2613 static void
2614 test_one_liner_many_fixits_1 ()
2616 test_diagnostic_context dc;
2617 location_t equals = linemap_position_for_column (line_table, 5);
2618 rich_location richloc (line_table, equals);
2619 for (int i = 0; i < 19; i++)
2620 richloc.add_fixit_insert_before ("a");
2621 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2622 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2623 ASSERT_STREQ ("\n"
2624 " foo = bar.field;\n"
2625 " ^\n"
2626 " aaaaaaaaaaaaaaaaaaa\n",
2627 pp_formatted_text (dc.printer));
2630 /* Ensure that we can add an arbitrary number of fix-it hints to a
2631 rich_location, even if they are not consolidated. */
2633 static void
2634 test_one_liner_many_fixits_2 ()
2636 test_diagnostic_context dc;
2637 location_t equals = linemap_position_for_column (line_table, 5);
2638 rich_location richloc (line_table, equals);
2639 for (int i = 0; i < 19; i++)
2641 location_t loc = linemap_position_for_column (line_table, i * 2);
2642 richloc.add_fixit_insert_before (loc, "a");
2644 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2645 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2646 ASSERT_STREQ ("\n"
2647 " foo = bar.field;\n"
2648 " ^\n"
2649 "a a a a a a a a a a a a a a a a a a a\n",
2650 pp_formatted_text (dc.printer));
2653 /* Test of labeling the ranges within a rich_location. */
2655 static void
2656 test_one_liner_labels ()
2658 location_t foo
2659 = make_location (linemap_position_for_column (line_table, 1),
2660 linemap_position_for_column (line_table, 1),
2661 linemap_position_for_column (line_table, 3));
2662 location_t bar
2663 = make_location (linemap_position_for_column (line_table, 7),
2664 linemap_position_for_column (line_table, 7),
2665 linemap_position_for_column (line_table, 9));
2666 location_t field
2667 = make_location (linemap_position_for_column (line_table, 11),
2668 linemap_position_for_column (line_table, 11),
2669 linemap_position_for_column (line_table, 15));
2671 /* Example where all the labels fit on one line. */
2673 text_range_label label0 ("0");
2674 text_range_label label1 ("1");
2675 text_range_label label2 ("2");
2676 gcc_rich_location richloc (foo, &label0);
2677 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2678 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2681 test_diagnostic_context dc;
2682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2683 ASSERT_STREQ ("\n"
2684 " foo = bar.field;\n"
2685 " ^~~ ~~~ ~~~~~\n"
2686 " | | |\n"
2687 " 0 1 2\n",
2688 pp_formatted_text (dc.printer));
2691 /* Verify that we can disable label-printing. */
2693 test_diagnostic_context dc;
2694 dc.show_labels_p = false;
2695 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2696 ASSERT_STREQ ("\n"
2697 " foo = bar.field;\n"
2698 " ^~~ ~~~ ~~~~~\n",
2699 pp_formatted_text (dc.printer));
2703 /* Example where the labels need extra lines. */
2705 text_range_label label0 ("label 0");
2706 text_range_label label1 ("label 1");
2707 text_range_label label2 ("label 2");
2708 gcc_rich_location richloc (foo, &label0);
2709 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2710 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2712 test_diagnostic_context dc;
2713 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2714 ASSERT_STREQ ("\n"
2715 " foo = bar.field;\n"
2716 " ^~~ ~~~ ~~~~~\n"
2717 " | | |\n"
2718 " | | label 2\n"
2719 " | label 1\n"
2720 " label 0\n",
2721 pp_formatted_text (dc.printer));
2724 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2725 but label 1 just touches label 2. */
2727 text_range_label label0 ("aaaaa");
2728 text_range_label label1 ("bbbb");
2729 text_range_label label2 ("c");
2730 gcc_rich_location richloc (foo, &label0);
2731 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2732 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2734 test_diagnostic_context dc;
2735 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2736 ASSERT_STREQ ("\n"
2737 " foo = bar.field;\n"
2738 " ^~~ ~~~ ~~~~~\n"
2739 " | | |\n"
2740 " | | c\n"
2741 " aaaaa bbbb\n",
2742 pp_formatted_text (dc.printer));
2745 /* Example of out-of-order ranges (thus requiring a sort). */
2747 text_range_label label0 ("0");
2748 text_range_label label1 ("1");
2749 text_range_label label2 ("2");
2750 gcc_rich_location richloc (field, &label0);
2751 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2752 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2754 test_diagnostic_context dc;
2755 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2756 ASSERT_STREQ ("\n"
2757 " foo = bar.field;\n"
2758 " ~~~ ~~~ ^~~~~\n"
2759 " | | |\n"
2760 " 2 1 0\n",
2761 pp_formatted_text (dc.printer));
2764 /* Ensure we don't ICE if multiple ranges with labels are on
2765 the same point. */
2767 text_range_label label0 ("label 0");
2768 text_range_label label1 ("label 1");
2769 text_range_label label2 ("label 2");
2770 gcc_rich_location richloc (bar, &label0);
2771 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2772 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2774 test_diagnostic_context dc;
2775 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2776 ASSERT_STREQ ("\n"
2777 " foo = bar.field;\n"
2778 " ^~~\n"
2779 " |\n"
2780 " label 2\n"
2781 " label 1\n"
2782 " label 0\n",
2783 pp_formatted_text (dc.printer));
2786 /* Verify that a NULL result from range_label::get_text is
2787 handled gracefully. */
2789 text_range_label label (NULL);
2790 gcc_rich_location richloc (bar, &label);
2792 test_diagnostic_context dc;
2793 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2794 ASSERT_STREQ ("\n"
2795 " foo = bar.field;\n"
2796 " ^~~\n",
2797 pp_formatted_text (dc.printer));
2800 /* TODO: example of formatted printing (needs to be in
2801 gcc-rich-location.c due to Makefile.in issues). */
2804 /* Run the various one-liner tests. */
2806 static void
2807 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2809 /* Create a tempfile and write some text to it.
2810 ....................0000000001111111.
2811 ....................1234567890123456. */
2812 const char *content = "foo = bar.field;\n";
2813 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2814 line_table_test ltt (case_);
2816 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2818 location_t line_end = linemap_position_for_column (line_table, 16);
2820 /* Don't attempt to run the tests if column data might be unavailable. */
2821 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2822 return;
2824 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2825 ASSERT_EQ (1, LOCATION_LINE (line_end));
2826 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2828 test_one_liner_simple_caret ();
2829 test_one_liner_caret_and_range ();
2830 test_one_liner_multiple_carets_and_ranges ();
2831 test_one_liner_fixit_insert_before ();
2832 test_one_liner_fixit_insert_after ();
2833 test_one_liner_fixit_remove ();
2834 test_one_liner_fixit_replace ();
2835 test_one_liner_fixit_replace_non_equal_range ();
2836 test_one_liner_fixit_replace_equal_secondary_range ();
2837 test_one_liner_fixit_validation_adhoc_locations ();
2838 test_one_liner_many_fixits_1 ();
2839 test_one_liner_many_fixits_2 ();
2840 test_one_liner_labels ();
2843 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2845 static void
2846 test_add_location_if_nearby (const line_table_case &case_)
2848 /* Create a tempfile and write some text to it.
2849 ...000000000111111111122222222223333333333.
2850 ...123456789012345678901234567890123456789. */
2851 const char *content
2852 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2853 "struct different_line\n" /* line 2. */
2854 "{\n" /* line 3. */
2855 " double x;\n" /* line 4. */
2856 " double y;\n" /* line 5. */
2857 ";\n"); /* line 6. */
2858 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2859 line_table_test ltt (case_);
2861 const line_map_ordinary *ord_map
2862 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2863 tmp.get_filename (), 0));
2865 linemap_line_start (line_table, 1, 100);
2867 const location_t final_line_end
2868 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2870 /* Don't attempt to run the tests if column data might be unavailable. */
2871 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2872 return;
2874 /* Test of add_location_if_nearby on the same line as the
2875 primary location. */
2877 const location_t missing_close_brace_1_39
2878 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2879 const location_t matching_open_brace_1_18
2880 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2881 gcc_rich_location richloc (missing_close_brace_1_39);
2882 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2883 ASSERT_TRUE (added);
2884 ASSERT_EQ (2, richloc.get_num_locations ());
2885 test_diagnostic_context dc;
2886 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2887 ASSERT_STREQ ("\n"
2888 " struct same_line { double x; double y; ;\n"
2889 " ~ ^\n",
2890 pp_formatted_text (dc.printer));
2893 /* Test of add_location_if_nearby on a different line to the
2894 primary location. */
2896 const location_t missing_close_brace_6_1
2897 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2898 const location_t matching_open_brace_3_1
2899 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2900 gcc_rich_location richloc (missing_close_brace_6_1);
2901 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2902 ASSERT_FALSE (added);
2903 ASSERT_EQ (1, richloc.get_num_locations ());
2907 /* Verify that we print fixits even if they only affect lines
2908 outside those covered by the ranges in the rich_location. */
2910 static void
2911 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2913 /* Create a tempfile and write some text to it.
2914 ...000000000111111111122222222223333333333.
2915 ...123456789012345678901234567890123456789. */
2916 const char *content
2917 = ("struct point { double x; double y; };\n" /* line 1. */
2918 "struct point origin = {x: 0.0,\n" /* line 2. */
2919 " y\n" /* line 3. */
2920 "\n" /* line 4. */
2921 "\n" /* line 5. */
2922 " : 0.0};\n"); /* line 6. */
2923 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2924 line_table_test ltt (case_);
2926 const line_map_ordinary *ord_map
2927 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2928 tmp.get_filename (), 0));
2930 linemap_line_start (line_table, 1, 100);
2932 const location_t final_line_end
2933 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2935 /* Don't attempt to run the tests if column data might be unavailable. */
2936 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2937 return;
2939 /* A pair of tests for modernizing the initializers to C99-style. */
2941 /* The one-liner case (line 2). */
2943 test_diagnostic_context dc;
2944 const location_t x
2945 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2946 const location_t colon
2947 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2948 rich_location richloc (line_table, colon);
2949 richloc.add_fixit_insert_before (x, ".");
2950 richloc.add_fixit_replace (colon, "=");
2951 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2952 ASSERT_STREQ ("\n"
2953 " struct point origin = {x: 0.0,\n"
2954 " ^\n"
2955 " .=\n",
2956 pp_formatted_text (dc.printer));
2959 /* The multiline case. The caret for the rich_location is on line 6;
2960 verify that insertion fixit on line 3 is still printed (and that
2961 span starts are printed due to the gap between the span at line 3
2962 and that at line 6). */
2964 test_diagnostic_context dc;
2965 const location_t y
2966 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2967 const location_t colon
2968 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2969 rich_location richloc (line_table, colon);
2970 richloc.add_fixit_insert_before (y, ".");
2971 richloc.add_fixit_replace (colon, "=");
2972 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2973 ASSERT_STREQ ("\n"
2974 "FILENAME:3:24:\n"
2975 " y\n"
2976 " .\n"
2977 "FILENAME:6:25:\n"
2978 " : 0.0};\n"
2979 " ^\n"
2980 " =\n",
2981 pp_formatted_text (dc.printer));
2984 /* As above, but verify the behavior of multiple line spans
2985 with line-numbering enabled. */
2987 const location_t y
2988 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2989 const location_t colon
2990 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2991 rich_location richloc (line_table, colon);
2992 richloc.add_fixit_insert_before (y, ".");
2993 richloc.add_fixit_replace (colon, "=");
2994 test_diagnostic_context dc;
2995 dc.show_line_numbers_p = true;
2996 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2997 ASSERT_STREQ ("\n"
2998 " 3 | y\n"
2999 " | .\n"
3000 "......\n"
3001 " 6 | : 0.0};\n"
3002 " | ^\n"
3003 " | =\n",
3004 pp_formatted_text (dc.printer));
3009 /* Verify that fix-it hints are appropriately consolidated.
3011 If any fix-it hints in a rich_location involve locations beyond
3012 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3013 the fix-it as a whole, so there should be none.
3015 Otherwise, verify that consecutive "replace" and "remove" fix-its
3016 are merged, and that other fix-its remain separate. */
3018 static void
3019 test_fixit_consolidation (const line_table_case &case_)
3021 line_table_test ltt (case_);
3023 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3025 const location_t c10 = linemap_position_for_column (line_table, 10);
3026 const location_t c15 = linemap_position_for_column (line_table, 15);
3027 const location_t c16 = linemap_position_for_column (line_table, 16);
3028 const location_t c17 = linemap_position_for_column (line_table, 17);
3029 const location_t c20 = linemap_position_for_column (line_table, 20);
3030 const location_t c21 = linemap_position_for_column (line_table, 21);
3031 const location_t caret = c10;
3033 /* Insert + insert. */
3035 rich_location richloc (line_table, caret);
3036 richloc.add_fixit_insert_before (c10, "foo");
3037 richloc.add_fixit_insert_before (c15, "bar");
3039 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3040 /* Bogus column info for 2nd fixit, so no fixits. */
3041 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3042 else
3043 /* They should not have been merged. */
3044 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3047 /* Insert + replace. */
3049 rich_location richloc (line_table, caret);
3050 richloc.add_fixit_insert_before (c10, "foo");
3051 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3052 "bar");
3054 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3055 /* Bogus column info for 2nd fixit, so no fixits. */
3056 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3057 else
3058 /* They should not have been merged. */
3059 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3062 /* Replace + non-consecutive insert. */
3064 rich_location richloc (line_table, caret);
3065 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3066 "bar");
3067 richloc.add_fixit_insert_before (c17, "foo");
3069 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3070 /* Bogus column info for 2nd fixit, so no fixits. */
3071 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3072 else
3073 /* They should not have been merged. */
3074 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3077 /* Replace + non-consecutive replace. */
3079 rich_location richloc (line_table, caret);
3080 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3081 "foo");
3082 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3083 "bar");
3085 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3086 /* Bogus column info for 2nd fixit, so no fixits. */
3087 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3088 else
3089 /* They should not have been merged. */
3090 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3093 /* Replace + consecutive replace. */
3095 rich_location richloc (line_table, caret);
3096 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3097 "foo");
3098 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3099 "bar");
3101 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3102 /* Bogus column info for 2nd fixit, so no fixits. */
3103 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3104 else
3106 /* They should have been merged into a single "replace". */
3107 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3108 const fixit_hint *hint = richloc.get_fixit_hint (0);
3109 ASSERT_STREQ ("foobar", hint->get_string ());
3110 ASSERT_EQ (c10, hint->get_start_loc ());
3111 ASSERT_EQ (c21, hint->get_next_loc ());
3115 /* Replace + consecutive removal. */
3117 rich_location richloc (line_table, caret);
3118 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3119 "foo");
3120 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3122 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3123 /* Bogus column info for 2nd fixit, so no fixits. */
3124 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3125 else
3127 /* They should have been merged into a single replace, with the
3128 range extended to cover that of the removal. */
3129 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3130 const fixit_hint *hint = richloc.get_fixit_hint (0);
3131 ASSERT_STREQ ("foo", hint->get_string ());
3132 ASSERT_EQ (c10, hint->get_start_loc ());
3133 ASSERT_EQ (c21, hint->get_next_loc ());
3137 /* Consecutive removals. */
3139 rich_location richloc (line_table, caret);
3140 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3141 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3143 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3144 /* Bogus column info for 2nd fixit, so no fixits. */
3145 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3146 else
3148 /* They should have been merged into a single "replace-with-empty". */
3149 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3150 const fixit_hint *hint = richloc.get_fixit_hint (0);
3151 ASSERT_STREQ ("", hint->get_string ());
3152 ASSERT_EQ (c10, hint->get_start_loc ());
3153 ASSERT_EQ (c21, hint->get_next_loc ());
3158 /* Verify that the line_corrections machinery correctly prints
3159 overlapping fixit-hints. */
3161 static void
3162 test_overlapped_fixit_printing (const line_table_case &case_)
3164 /* Create a tempfile and write some text to it.
3165 ...000000000111111111122222222223333333333.
3166 ...123456789012345678901234567890123456789. */
3167 const char *content
3168 = (" foo *f = (foo *)ptr->field;\n");
3169 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3170 line_table_test ltt (case_);
3172 const line_map_ordinary *ord_map
3173 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3174 tmp.get_filename (), 0));
3176 linemap_line_start (line_table, 1, 100);
3178 const location_t final_line_end
3179 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3181 /* Don't attempt to run the tests if column data might be unavailable. */
3182 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3183 return;
3185 /* A test for converting a C-style cast to a C++-style cast. */
3186 const location_t open_paren
3187 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3188 const location_t close_paren
3189 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3190 const location_t expr_start
3191 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3192 const location_t expr_finish
3193 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3194 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3196 /* Various examples of fix-it hints that aren't themselves consolidated,
3197 but for which the *printing* may need consolidation. */
3199 /* Example where 3 fix-it hints are printed as one. */
3201 test_diagnostic_context dc;
3202 rich_location richloc (line_table, expr);
3203 richloc.add_fixit_replace (open_paren, "const_cast<");
3204 richloc.add_fixit_replace (close_paren, "> (");
3205 richloc.add_fixit_insert_after (")");
3207 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3208 ASSERT_STREQ ("\n"
3209 " foo *f = (foo *)ptr->field;\n"
3210 " ^~~~~~~~~~\n"
3211 " -----------------\n"
3212 " const_cast<foo *> (ptr->field)\n",
3213 pp_formatted_text (dc.printer));
3215 /* Unit-test the line_corrections machinery. */
3216 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3217 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3218 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3219 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3220 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3221 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3222 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3223 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3224 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3225 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3227 /* Add each hint in turn to a line_corrections instance,
3228 and verify that they are consolidated into one correction instance
3229 as expected. */
3230 line_corrections lc (tmp.get_filename (), 1);
3232 /* The first replace hint by itself. */
3233 lc.add_hint (hint_0);
3234 ASSERT_EQ (1, lc.m_corrections.length ());
3235 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3236 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3237 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3239 /* After the second replacement hint, they are printed together
3240 as a replacement (along with the text between them). */
3241 lc.add_hint (hint_1);
3242 ASSERT_EQ (1, lc.m_corrections.length ());
3243 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3244 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3245 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3247 /* After the final insertion hint, they are all printed together
3248 as a replacement (along with the text between them). */
3249 lc.add_hint (hint_2);
3250 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3251 lc.m_corrections[0]->m_text);
3252 ASSERT_EQ (1, lc.m_corrections.length ());
3253 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3254 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3257 /* Example where two are consolidated during printing. */
3259 test_diagnostic_context dc;
3260 rich_location richloc (line_table, expr);
3261 richloc.add_fixit_replace (open_paren, "CAST (");
3262 richloc.add_fixit_replace (close_paren, ") (");
3263 richloc.add_fixit_insert_after (")");
3265 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3266 ASSERT_STREQ ("\n"
3267 " foo *f = (foo *)ptr->field;\n"
3268 " ^~~~~~~~~~\n"
3269 " -\n"
3270 " CAST (-\n"
3271 " ) ( )\n",
3272 pp_formatted_text (dc.printer));
3275 /* Example where none are consolidated during printing. */
3277 test_diagnostic_context dc;
3278 rich_location richloc (line_table, expr);
3279 richloc.add_fixit_replace (open_paren, "CST (");
3280 richloc.add_fixit_replace (close_paren, ") (");
3281 richloc.add_fixit_insert_after (")");
3283 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3284 ASSERT_STREQ ("\n"
3285 " foo *f = (foo *)ptr->field;\n"
3286 " ^~~~~~~~~~\n"
3287 " -\n"
3288 " CST ( -\n"
3289 " ) ( )\n",
3290 pp_formatted_text (dc.printer));
3293 /* Example of deletion fix-it hints. */
3295 test_diagnostic_context dc;
3296 rich_location richloc (line_table, expr);
3297 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3298 source_range victim = {open_paren, close_paren};
3299 richloc.add_fixit_remove (victim);
3301 /* This case is actually handled by fixit-consolidation,
3302 rather than by line_corrections. */
3303 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3305 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3306 ASSERT_STREQ ("\n"
3307 " foo *f = (foo *)ptr->field;\n"
3308 " ^~~~~~~~~~\n"
3309 " -------\n"
3310 " (bar *)\n",
3311 pp_formatted_text (dc.printer));
3314 /* Example of deletion fix-it hints that would overlap. */
3316 test_diagnostic_context dc;
3317 rich_location richloc (line_table, expr);
3318 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3319 source_range victim = {expr_start, expr_finish};
3320 richloc.add_fixit_remove (victim);
3322 /* These fixits are not consolidated. */
3323 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3325 /* But the corrections are. */
3326 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3327 ASSERT_STREQ ("\n"
3328 " foo *f = (foo *)ptr->field;\n"
3329 " ^~~~~~~~~~\n"
3330 " -----------------\n"
3331 " (longer *)(foo *)\n",
3332 pp_formatted_text (dc.printer));
3335 /* Example of insertion fix-it hints that would overlap. */
3337 test_diagnostic_context dc;
3338 rich_location richloc (line_table, expr);
3339 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3340 richloc.add_fixit_insert_after (close_paren, "TEST");
3342 /* The first insertion is long enough that if printed naively,
3343 it would overlap with the second.
3344 Verify that they are printed as a single replacement. */
3345 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3346 ASSERT_STREQ ("\n"
3347 " foo *f = (foo *)ptr->field;\n"
3348 " ^~~~~~~~~~\n"
3349 " -------\n"
3350 " LONGER THAN THE CAST(foo *)TEST\n",
3351 pp_formatted_text (dc.printer));
3355 /* Verify that the line_corrections machinery correctly prints
3356 overlapping fixit-hints that have been added in the wrong
3357 order.
3358 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3360 static void
3361 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3363 /* Create a tempfile and write some text to it.
3364 ...000000000111111111122222222223333333333.
3365 ...123456789012345678901234567890123456789. */
3366 const char *content
3367 = ("int a5[][0][0] = { 1, 2 };\n");
3368 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3369 line_table_test ltt (case_);
3371 const line_map_ordinary *ord_map
3372 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3373 tmp.get_filename (), 0));
3375 linemap_line_start (line_table, 1, 100);
3377 const location_t final_line_end
3378 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3380 /* Don't attempt to run the tests if column data might be unavailable. */
3381 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3382 return;
3384 const location_t col_1
3385 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3386 const location_t col_20
3387 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3388 const location_t col_21
3389 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3390 const location_t col_23
3391 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3392 const location_t col_25
3393 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3395 /* Two insertions, in the wrong order. */
3397 rich_location richloc (line_table, col_20);
3398 richloc.add_fixit_insert_before (col_23, "{");
3399 richloc.add_fixit_insert_before (col_21, "}");
3401 /* These fixits should be accepted; they can't be consolidated. */
3402 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3403 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3404 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3405 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3406 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3407 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3408 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3410 /* Verify that they're printed correctly. */
3411 test_diagnostic_context dc;
3412 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3413 ASSERT_STREQ ("\n"
3414 " int a5[][0][0] = { 1, 2 };\n"
3415 " ^\n"
3416 " } {\n",
3417 pp_formatted_text (dc.printer));
3420 /* Various overlapping insertions, some occurring "out of order"
3421 (reproducing the fix-it hints from PR c/81405). */
3423 test_diagnostic_context dc;
3424 rich_location richloc (line_table, col_20);
3426 richloc.add_fixit_insert_before (col_20, "{{");
3427 richloc.add_fixit_insert_before (col_21, "}}");
3428 richloc.add_fixit_insert_before (col_23, "{");
3429 richloc.add_fixit_insert_before (col_21, "}");
3430 richloc.add_fixit_insert_before (col_23, "{{");
3431 richloc.add_fixit_insert_before (col_25, "}");
3432 richloc.add_fixit_insert_before (col_21, "}");
3433 richloc.add_fixit_insert_before (col_1, "{");
3434 richloc.add_fixit_insert_before (col_25, "}");
3435 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3436 ASSERT_STREQ ("\n"
3437 " int a5[][0][0] = { 1, 2 };\n"
3438 " ^\n"
3439 " { -----\n"
3440 " {{1}}}}, {{{2 }}\n",
3441 pp_formatted_text (dc.printer));
3445 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3447 static void
3448 test_fixit_insert_containing_newline (const line_table_case &case_)
3450 /* Create a tempfile and write some text to it.
3451 .........................0000000001111111.
3452 .........................1234567890123456. */
3453 const char *old_content = (" case 'a':\n" /* line 1. */
3454 " x = a;\n" /* line 2. */
3455 " case 'b':\n" /* line 3. */
3456 " x = b;\n");/* line 4. */
3458 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3459 line_table_test ltt (case_);
3460 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3462 location_t case_start = linemap_position_for_column (line_table, 5);
3463 location_t case_finish = linemap_position_for_column (line_table, 13);
3464 location_t case_loc = make_location (case_start, case_start, case_finish);
3465 location_t line_start = linemap_position_for_column (line_table, 1);
3467 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3468 return;
3470 /* Add a "break;" on a line by itself before line 3 i.e. before
3471 column 1 of line 3. */
3473 rich_location richloc (line_table, case_loc);
3474 richloc.add_fixit_insert_before (line_start, " break;\n");
3476 /* Without line numbers. */
3478 test_diagnostic_context dc;
3479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3480 ASSERT_STREQ ("\n"
3481 " x = a;\n"
3482 "+ break;\n"
3483 " case 'b':\n"
3484 " ^~~~~~~~~\n",
3485 pp_formatted_text (dc.printer));
3488 /* With line numbers. */
3490 test_diagnostic_context dc;
3491 dc.show_line_numbers_p = true;
3492 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3493 ASSERT_STREQ ("\n"
3494 " 2 | x = a;\n"
3495 " +++ |+ break;\n"
3496 " 3 | case 'b':\n"
3497 " | ^~~~~~~~~\n",
3498 pp_formatted_text (dc.printer));
3502 /* Verify that attempts to add text with a newline fail when the
3503 insertion point is *not* at the start of a line. */
3505 rich_location richloc (line_table, case_loc);
3506 richloc.add_fixit_insert_before (case_start, "break;\n");
3507 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3508 test_diagnostic_context dc;
3509 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3510 ASSERT_STREQ ("\n"
3511 " case 'b':\n"
3512 " ^~~~~~~~~\n",
3513 pp_formatted_text (dc.printer));
3517 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3518 of the file, where the fix-it is printed in a different line-span
3519 to the primary range of the diagnostic. */
3521 static void
3522 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3524 /* Create a tempfile and write some text to it.
3525 .........................0000000001111111.
3526 .........................1234567890123456. */
3527 const char *old_content = ("test (int ch)\n" /* line 1. */
3528 "{\n" /* line 2. */
3529 " putchar (ch);\n" /* line 3. */
3530 "}\n"); /* line 4. */
3532 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3533 line_table_test ltt (case_);
3535 const line_map_ordinary *ord_map = linemap_check_ordinary
3536 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3537 linemap_line_start (line_table, 1, 100);
3539 /* The primary range is the "putchar" token. */
3540 location_t putchar_start
3541 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3542 location_t putchar_finish
3543 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3544 location_t putchar_loc
3545 = make_location (putchar_start, putchar_start, putchar_finish);
3546 rich_location richloc (line_table, putchar_loc);
3548 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3549 location_t file_start
3550 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3551 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3553 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3554 return;
3557 test_diagnostic_context dc;
3558 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3559 ASSERT_STREQ ("\n"
3560 "FILENAME:1:1:\n"
3561 "+#include <stdio.h>\n"
3562 " test (int ch)\n"
3563 "FILENAME:3:2:\n"
3564 " putchar (ch);\n"
3565 " ^~~~~~~\n",
3566 pp_formatted_text (dc.printer));
3569 /* With line-numbering, the line spans are close enough to be
3570 consolidated, since it makes little sense to skip line 2. */
3572 test_diagnostic_context dc;
3573 dc.show_line_numbers_p = true;
3574 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3575 ASSERT_STREQ ("\n"
3576 " +++ |+#include <stdio.h>\n"
3577 " 1 | test (int ch)\n"
3578 " 2 | {\n"
3579 " 3 | putchar (ch);\n"
3580 " | ^~~~~~~\n",
3581 pp_formatted_text (dc.printer));
3585 /* Replacement fix-it hint containing a newline.
3586 This will fail, as newlines are only supported when inserting at the
3587 beginning of a line. */
3589 static void
3590 test_fixit_replace_containing_newline (const line_table_case &case_)
3592 /* Create a tempfile and write some text to it.
3593 .........................0000000001111.
3594 .........................1234567890123. */
3595 const char *old_content = "foo = bar ();\n";
3597 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3598 line_table_test ltt (case_);
3599 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3601 /* Replace the " = " with "\n = ", as if we were reformatting an
3602 overly long line. */
3603 location_t start = linemap_position_for_column (line_table, 4);
3604 location_t finish = linemap_position_for_column (line_table, 6);
3605 location_t loc = linemap_position_for_column (line_table, 13);
3606 rich_location richloc (line_table, loc);
3607 source_range range = source_range::from_locations (start, finish);
3608 richloc.add_fixit_replace (range, "\n =");
3610 /* Arbitrary newlines are not yet supported within fix-it hints, so
3611 the fix-it should not be displayed. */
3612 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3614 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3615 return;
3617 test_diagnostic_context dc;
3618 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3619 ASSERT_STREQ ("\n"
3620 " foo = bar ();\n"
3621 " ^\n",
3622 pp_formatted_text (dc.printer));
3625 /* Fix-it hint, attempting to delete a newline.
3626 This will fail, as we currently only support fix-it hints that
3627 affect one line at a time. */
3629 static void
3630 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3632 /* Create a tempfile and write some text to it.
3633 ..........................0000000001111.
3634 ..........................1234567890123. */
3635 const char *old_content = ("foo = bar (\n"
3636 " );\n");
3638 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3639 line_table_test ltt (case_);
3640 const line_map_ordinary *ord_map = linemap_check_ordinary
3641 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3642 linemap_line_start (line_table, 1, 100);
3644 /* Attempt to delete the " (\n...)". */
3645 location_t start
3646 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3647 location_t caret
3648 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3649 location_t finish
3650 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3651 location_t loc = make_location (caret, start, finish);
3652 rich_location richloc (line_table, loc);
3653 richloc. add_fixit_remove ();
3655 /* Fix-it hints that affect more than one line are not yet supported, so
3656 the fix-it should not be displayed. */
3657 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3659 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3660 return;
3662 test_diagnostic_context dc;
3663 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3664 ASSERT_STREQ ("\n"
3665 " foo = bar (\n"
3666 " ~^\n"
3667 " );\n"
3668 " ~ \n",
3669 pp_formatted_text (dc.printer));
3672 /* Verify that line numbers are correctly printed for the case of
3673 a multiline range in which the width of the line numbers changes
3674 (e.g. from "9" to "10"). */
3676 static void
3677 test_line_numbers_multiline_range ()
3679 /* Create a tempfile and write some text to it. */
3680 pretty_printer pp;
3681 for (int i = 0; i < 20; i++)
3682 /* .........0000000001111111.
3683 .............1234567890123456. */
3684 pp_printf (&pp, "this is line %i\n", i + 1);
3685 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3686 line_table_test ltt;
3688 const line_map_ordinary *ord_map = linemap_check_ordinary
3689 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3690 linemap_line_start (line_table, 1, 100);
3692 /* Create a multi-line location, starting at the "line" of line 9, with
3693 a caret on the "is" of line 10, finishing on the "this" line 11. */
3695 location_t start
3696 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3697 location_t caret
3698 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3699 location_t finish
3700 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3701 location_t loc = make_location (caret, start, finish);
3703 test_diagnostic_context dc;
3704 dc.show_line_numbers_p = true;
3705 dc.min_margin_width = 0;
3706 gcc_rich_location richloc (loc);
3707 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3708 ASSERT_STREQ ("\n"
3709 " 9 | this is line 9\n"
3710 " | ~~~~~~\n"
3711 "10 | this is line 10\n"
3712 " | ~~~~~^~~~~~~~~~\n"
3713 "11 | this is line 11\n"
3714 " | ~~~~ \n",
3715 pp_formatted_text (dc.printer));
3718 /* Run all of the selftests within this file. */
3720 void
3721 diagnostic_show_locus_c_tests ()
3723 test_line_span ();
3725 test_layout_range_for_single_point ();
3726 test_layout_range_for_single_line ();
3727 test_layout_range_for_multiple_lines ();
3729 test_get_line_width_without_trailing_whitespace ();
3731 test_diagnostic_show_locus_unknown_location ();
3733 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3734 for_each_line_table_case (test_add_location_if_nearby);
3735 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3736 for_each_line_table_case (test_fixit_consolidation);
3737 for_each_line_table_case (test_overlapped_fixit_printing);
3738 for_each_line_table_case (test_overlapped_fixit_printing_2);
3739 for_each_line_table_case (test_fixit_insert_containing_newline);
3740 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3741 for_each_line_table_case (test_fixit_replace_containing_newline);
3742 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3744 test_line_numbers_multiline_range ();
3747 } // namespace selftest
3749 #endif /* #if CHECKING_P */
3751 #if __GNUC__ >= 10
3752 # pragma GCC diagnostic pop
3753 #endif