dump_printf: use %T and %G throughout
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob7dfb0a036cd6f541dcd161202a382f77f40795ab
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2018 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 /* Classes for rendering source code and diagnostics, within an
43 anonymous namespace.
44 The work is done by "class layout", which embeds and uses
45 "class colorizer" and "class layout_range" to get things done. */
47 namespace {
49 /* The state at a given point of the source code, assuming that we're
50 in a range: which range are we in, and whether we should draw a caret at
51 this point. */
53 struct point_state
55 int range_idx;
56 bool draw_caret_p;
59 /* A class to inject colorization codes when printing the diagnostic locus.
61 It has one kind of colorization for each of:
62 - normal text
63 - range 0 (the "primary location")
64 - range 1
65 - range 2
67 The class caches the lookup of the color codes for the above.
69 The class also has responsibility for tracking which of the above is
70 active, filtering out unnecessary changes. This allows
71 layout::print_source_line and layout::print_annotation_line
72 to simply request a colorization code for *every* character they print,
73 via this class, and have the filtering be done for them here. */
75 class colorizer
77 public:
78 colorizer (diagnostic_context *context,
79 diagnostic_t diagnostic_kind);
80 ~colorizer ();
82 void set_range (int range_idx) { set_state (range_idx); }
83 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
84 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
85 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
87 private:
88 void set_state (int state);
89 void begin_state (int state);
90 void finish_state (int state);
91 const char *get_color_by_name (const char *);
93 private:
94 static const int STATE_NORMAL_TEXT = -1;
95 static const int STATE_FIXIT_INSERT = -2;
96 static const int STATE_FIXIT_DELETE = -3;
98 diagnostic_context *m_context;
99 diagnostic_t m_diagnostic_kind;
100 int m_current_state;
101 const char *m_range1;
102 const char *m_range2;
103 const char *m_fixit_insert;
104 const char *m_fixit_delete;
105 const char *m_stop_color;
108 /* A point within a layout_range; similar to an expanded_location,
109 but after filtering on file. */
111 class layout_point
113 public:
114 layout_point (const expanded_location &exploc)
115 : m_line (exploc.line),
116 m_column (exploc.column) {}
118 linenum_type m_line;
119 int m_column;
122 /* A class for use by "class layout" below: a filtered location_range. */
124 class layout_range
126 public:
127 layout_range (const expanded_location *start_exploc,
128 const expanded_location *finish_exploc,
129 enum range_display_kind range_display_kind,
130 const expanded_location *caret_exploc,
131 unsigned original_idx,
132 const range_label *label);
134 bool contains_point (linenum_type row, int column) const;
135 bool intersects_line_p (linenum_type row) const;
137 layout_point m_start;
138 layout_point m_finish;
139 enum range_display_kind m_range_display_kind;
140 layout_point m_caret;
141 unsigned m_original_idx;
142 const range_label *m_label;
145 /* A struct for use by layout::print_source_line for telling
146 layout::print_annotation_line the extents of the source line that
147 it printed, so that underlines can be clipped appropriately. */
149 struct line_bounds
151 int m_first_non_ws;
152 int m_last_non_ws;
155 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
156 or "line 23"). During the layout ctor, layout::calculate_line_spans
157 splits the pertinent source lines into a list of disjoint line_span
158 instances (e.g. lines 5-10, lines 15-20, line 23). */
160 struct line_span
162 line_span (linenum_type first_line, linenum_type last_line)
163 : m_first_line (first_line), m_last_line (last_line)
165 gcc_assert (first_line <= last_line);
167 linenum_type get_first_line () const { return m_first_line; }
168 linenum_type get_last_line () const { return m_last_line; }
170 bool contains_line_p (linenum_type line) const
172 return line >= m_first_line && line <= m_last_line;
175 static int comparator (const void *p1, const void *p2)
177 const line_span *ls1 = (const line_span *)p1;
178 const line_span *ls2 = (const line_span *)p2;
179 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
180 if (first_line_cmp)
181 return first_line_cmp;
182 return compare (ls1->m_last_line, ls2->m_last_line);
185 linenum_type m_first_line;
186 linenum_type m_last_line;
189 #if CHECKING_P
191 /* Selftests for line_span. */
193 static void
194 test_line_span ()
196 line_span line_one (1, 1);
197 ASSERT_EQ (1, line_one.get_first_line ());
198 ASSERT_EQ (1, line_one.get_last_line ());
199 ASSERT_FALSE (line_one.contains_line_p (0));
200 ASSERT_TRUE (line_one.contains_line_p (1));
201 ASSERT_FALSE (line_one.contains_line_p (2));
203 line_span lines_1_to_3 (1, 3);
204 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
205 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
206 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
207 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
209 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
210 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
211 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
213 /* A linenum > 2^31. */
214 const linenum_type LARGEST_LINE = 0xffffffff;
215 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
216 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
217 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
219 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
220 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
223 #endif /* #if CHECKING_P */
225 /* A class to control the overall layout when printing a diagnostic.
227 The layout is determined within the constructor.
228 It is then printed by repeatedly calling the "print_source_line",
229 "print_annotation_line" and "print_any_fixits" methods.
231 We assume we have disjoint ranges. */
233 class layout
235 public:
236 layout (diagnostic_context *context,
237 rich_location *richloc,
238 diagnostic_t diagnostic_kind);
240 bool maybe_add_location_range (const location_range *loc_range,
241 unsigned original_idx,
242 bool restrict_to_current_line_spans);
244 int get_num_line_spans () const { return m_line_spans.length (); }
245 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
247 void print_gap_in_line_numbering ();
248 bool print_heading_for_line_span_index_p (int line_span_idx) const;
250 expanded_location get_expanded_location (const line_span *) const;
252 void print_line (linenum_type row);
254 private:
255 bool will_show_line_p (linenum_type row) const;
256 void print_leading_fixits (linenum_type row);
257 void print_source_line (linenum_type row, const char *line, int line_width,
258 line_bounds *lbounds_out);
259 bool should_print_annotation_line_p (linenum_type row) const;
260 void start_annotation_line (char margin_char = ' ') const;
261 void print_annotation_line (linenum_type row, const line_bounds lbounds);
262 void print_any_labels (linenum_type row);
263 void print_trailing_fixits (linenum_type row);
265 bool annotation_line_showed_range_p (linenum_type line, int start_column,
266 int finish_column) const;
267 void show_ruler (int max_column) const;
269 bool validate_fixit_hint_p (const fixit_hint *hint);
271 void calculate_line_spans ();
273 void print_newline ();
275 bool
276 get_state_at_point (/* Inputs. */
277 linenum_type row, int column,
278 int first_non_ws, int last_non_ws,
279 /* Outputs. */
280 point_state *out_state);
283 get_x_bound_for_row (linenum_type row, int caret_column,
284 int last_non_ws);
286 void
287 move_to_column (int *column, int dest_column, bool add_left_margin);
289 private:
290 diagnostic_context *m_context;
291 pretty_printer *m_pp;
292 diagnostic_t m_diagnostic_kind;
293 location_t m_primary_loc;
294 expanded_location m_exploc;
295 colorizer m_colorizer;
296 bool m_colorize_source_p;
297 bool m_show_labels_p;
298 bool m_show_line_numbers_p;
299 auto_vec <layout_range> m_layout_ranges;
300 auto_vec <const fixit_hint *> m_fixit_hints;
301 auto_vec <line_span> m_line_spans;
302 int m_linenum_width;
303 int m_x_offset;
306 /* Implementation of "class colorizer". */
308 /* The constructor for "colorizer". Lookup and store color codes for the
309 different kinds of things we might need to print. */
311 colorizer::colorizer (diagnostic_context *context,
312 diagnostic_t diagnostic_kind) :
313 m_context (context),
314 m_diagnostic_kind (diagnostic_kind),
315 m_current_state (STATE_NORMAL_TEXT)
317 m_range1 = get_color_by_name ("range1");
318 m_range2 = get_color_by_name ("range2");
319 m_fixit_insert = get_color_by_name ("fixit-insert");
320 m_fixit_delete = get_color_by_name ("fixit-delete");
321 m_stop_color = colorize_stop (pp_show_color (context->printer));
324 /* The destructor for "colorize". If colorization is on, print a code to
325 turn it off. */
327 colorizer::~colorizer ()
329 finish_state (m_current_state);
332 /* Update state, printing color codes if necessary if there's a state
333 change. */
335 void
336 colorizer::set_state (int new_state)
338 if (m_current_state != new_state)
340 finish_state (m_current_state);
341 m_current_state = new_state;
342 begin_state (new_state);
346 /* Turn on any colorization for STATE. */
348 void
349 colorizer::begin_state (int state)
351 switch (state)
353 case STATE_NORMAL_TEXT:
354 break;
356 case STATE_FIXIT_INSERT:
357 pp_string (m_context->printer, m_fixit_insert);
358 break;
360 case STATE_FIXIT_DELETE:
361 pp_string (m_context->printer, m_fixit_delete);
362 break;
364 case 0:
365 /* Make range 0 be the same color as the "kind" text
366 (error vs warning vs note). */
367 pp_string
368 (m_context->printer,
369 colorize_start (pp_show_color (m_context->printer),
370 diagnostic_get_color_for_kind (m_diagnostic_kind)));
371 break;
373 case 1:
374 pp_string (m_context->printer, m_range1);
375 break;
377 case 2:
378 pp_string (m_context->printer, m_range2);
379 break;
381 default:
382 /* For ranges beyond 2, alternate between color 1 and color 2. */
384 gcc_assert (state > 2);
385 pp_string (m_context->printer,
386 state % 2 ? m_range1 : m_range2);
388 break;
392 /* Turn off any colorization for STATE. */
394 void
395 colorizer::finish_state (int state)
397 if (state != STATE_NORMAL_TEXT)
398 pp_string (m_context->printer, m_stop_color);
401 /* Get the color code for NAME (or the empty string if
402 colorization is disabled). */
404 const char *
405 colorizer::get_color_by_name (const char *name)
407 return colorize_start (pp_show_color (m_context->printer), name);
410 /* Implementation of class layout_range. */
412 /* The constructor for class layout_range.
413 Initialize various layout_point fields from expanded_location
414 equivalents; we've already filtered on file. */
416 layout_range::layout_range (const expanded_location *start_exploc,
417 const expanded_location *finish_exploc,
418 enum range_display_kind range_display_kind,
419 const expanded_location *caret_exploc,
420 unsigned original_idx,
421 const range_label *label)
422 : m_start (*start_exploc),
423 m_finish (*finish_exploc),
424 m_range_display_kind (range_display_kind),
425 m_caret (*caret_exploc),
426 m_original_idx (original_idx),
427 m_label (label)
431 /* Is (column, row) within the given range?
432 We've already filtered on the file.
434 Ranges are closed (both limits are within the range).
436 Example A: a single-line range:
437 start: (col=22, line=2)
438 finish: (col=38, line=2)
440 |00000011111111112222222222333333333344444444444
441 |34567890123456789012345678901234567890123456789
442 --+-----------------------------------------------
443 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
444 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
445 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
447 Example B: a multiline range with
448 start: (col=14, line=3)
449 finish: (col=08, line=5)
451 |00000011111111112222222222333333333344444444444
452 |34567890123456789012345678901234567890123456789
453 --+-----------------------------------------------
454 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
455 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
456 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
457 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
458 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
459 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
460 --+-----------------------------------------------
462 Legend:
463 - 'b' indicates a point *before* the range
464 - 'S' indicates the start of the range
465 - 'w' indicates a point within the range
466 - 'F' indicates the finish of the range (which is
467 within it).
468 - 'a' indicates a subsequent point *after* the range. */
470 bool
471 layout_range::contains_point (linenum_type row, int column) const
473 gcc_assert (m_start.m_line <= m_finish.m_line);
474 /* ...but the equivalent isn't true for the columns;
475 consider example B in the comment above. */
477 if (row < m_start.m_line)
478 /* Points before the first line of the range are
479 outside it (corresponding to line 01 in example A
480 and lines 01 and 02 in example B above). */
481 return false;
483 if (row == m_start.m_line)
484 /* On same line as start of range (corresponding
485 to line 02 in example A and line 03 in example B). */
487 if (column < m_start.m_column)
488 /* Points on the starting line of the range, but
489 before the column in which it begins. */
490 return false;
492 if (row < m_finish.m_line)
493 /* This is a multiline range; the point
494 is within it (corresponds to line 03 in example B
495 from column 14 onwards) */
496 return true;
497 else
499 /* This is a single-line range. */
500 gcc_assert (row == m_finish.m_line);
501 return column <= m_finish.m_column;
505 /* The point is in a line beyond that containing the
506 start of the range: lines 03 onwards in example A,
507 and lines 04 onwards in example B. */
508 gcc_assert (row > m_start.m_line);
510 if (row > m_finish.m_line)
511 /* The point is beyond the final line of the range
512 (lines 03 onwards in example A, and lines 06 onwards
513 in example B). */
514 return false;
516 if (row < m_finish.m_line)
518 /* The point is in a line that's fully within a multiline
519 range (e.g. line 04 in example B). */
520 gcc_assert (m_start.m_line < m_finish.m_line);
521 return true;
524 gcc_assert (row == m_finish.m_line);
526 return column <= m_finish.m_column;
529 /* Does this layout_range contain any part of line ROW? */
531 bool
532 layout_range::intersects_line_p (linenum_type row) const
534 gcc_assert (m_start.m_line <= m_finish.m_line);
535 if (row < m_start.m_line)
536 return false;
537 if (row > m_finish.m_line)
538 return false;
539 return true;
542 #if CHECKING_P
544 /* A helper function for testing layout_range. */
546 static layout_range
547 make_range (int start_line, int start_col, int end_line, int end_col)
549 const expanded_location start_exploc
550 = {"test.c", start_line, start_col, NULL, false};
551 const expanded_location finish_exploc
552 = {"test.c", end_line, end_col, NULL, false};
553 return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
554 &start_exploc, 0, NULL);
557 /* Selftests for layout_range::contains_point and
558 layout_range::intersects_line_p. */
560 /* Selftest for layout_range, where the layout_range
561 is a range with start==end i.e. a single point. */
563 static void
564 test_layout_range_for_single_point ()
566 layout_range point = make_range (7, 10, 7, 10);
568 /* Tests for layout_range::contains_point. */
570 /* Before the line. */
571 ASSERT_FALSE (point.contains_point (6, 1));
573 /* On the line, but before start. */
574 ASSERT_FALSE (point.contains_point (7, 9));
576 /* At the point. */
577 ASSERT_TRUE (point.contains_point (7, 10));
579 /* On the line, after the point. */
580 ASSERT_FALSE (point.contains_point (7, 11));
582 /* After the line. */
583 ASSERT_FALSE (point.contains_point (8, 1));
585 /* Tests for layout_range::intersects_line_p. */
586 ASSERT_FALSE (point.intersects_line_p (6));
587 ASSERT_TRUE (point.intersects_line_p (7));
588 ASSERT_FALSE (point.intersects_line_p (8));
591 /* Selftest for layout_range, where the layout_range
592 is the single-line range shown as "Example A" above. */
594 static void
595 test_layout_range_for_single_line ()
597 layout_range example_a = make_range (2, 22, 2, 38);
599 /* Tests for layout_range::contains_point. */
601 /* Before the line. */
602 ASSERT_FALSE (example_a.contains_point (1, 1));
604 /* On the line, but before start. */
605 ASSERT_FALSE (example_a.contains_point (2, 21));
607 /* On the line, at the start. */
608 ASSERT_TRUE (example_a.contains_point (2, 22));
610 /* On the line, within the range. */
611 ASSERT_TRUE (example_a.contains_point (2, 23));
613 /* On the line, at the end. */
614 ASSERT_TRUE (example_a.contains_point (2, 38));
616 /* On the line, after the end. */
617 ASSERT_FALSE (example_a.contains_point (2, 39));
619 /* After the line. */
620 ASSERT_FALSE (example_a.contains_point (2, 39));
622 /* Tests for layout_range::intersects_line_p. */
623 ASSERT_FALSE (example_a.intersects_line_p (1));
624 ASSERT_TRUE (example_a.intersects_line_p (2));
625 ASSERT_FALSE (example_a.intersects_line_p (3));
628 /* Selftest for layout_range, where the layout_range
629 is the multi-line range shown as "Example B" above. */
631 static void
632 test_layout_range_for_multiple_lines ()
634 layout_range example_b = make_range (3, 14, 5, 8);
636 /* Tests for layout_range::contains_point. */
638 /* Before first line. */
639 ASSERT_FALSE (example_b.contains_point (1, 1));
641 /* On the first line, but before start. */
642 ASSERT_FALSE (example_b.contains_point (3, 13));
644 /* At the start. */
645 ASSERT_TRUE (example_b.contains_point (3, 14));
647 /* On the first line, within the range. */
648 ASSERT_TRUE (example_b.contains_point (3, 15));
650 /* On an interior line.
651 The column number should not matter; try various boundary
652 values. */
653 ASSERT_TRUE (example_b.contains_point (4, 1));
654 ASSERT_TRUE (example_b.contains_point (4, 7));
655 ASSERT_TRUE (example_b.contains_point (4, 8));
656 ASSERT_TRUE (example_b.contains_point (4, 9));
657 ASSERT_TRUE (example_b.contains_point (4, 13));
658 ASSERT_TRUE (example_b.contains_point (4, 14));
659 ASSERT_TRUE (example_b.contains_point (4, 15));
661 /* On the final line, before the end. */
662 ASSERT_TRUE (example_b.contains_point (5, 7));
664 /* On the final line, at the end. */
665 ASSERT_TRUE (example_b.contains_point (5, 8));
667 /* On the final line, after the end. */
668 ASSERT_FALSE (example_b.contains_point (5, 9));
670 /* After the line. */
671 ASSERT_FALSE (example_b.contains_point (6, 1));
673 /* Tests for layout_range::intersects_line_p. */
674 ASSERT_FALSE (example_b.intersects_line_p (2));
675 ASSERT_TRUE (example_b.intersects_line_p (3));
676 ASSERT_TRUE (example_b.intersects_line_p (4));
677 ASSERT_TRUE (example_b.intersects_line_p (5));
678 ASSERT_FALSE (example_b.intersects_line_p (6));
681 #endif /* #if CHECKING_P */
683 /* Given a source line LINE of length LINE_WIDTH, determine the width
684 without any trailing whitespace. */
686 static int
687 get_line_width_without_trailing_whitespace (const char *line, int line_width)
689 int result = line_width;
690 while (result > 0)
692 char ch = line[result - 1];
693 if (ch == ' ' || ch == '\t' || ch == '\r')
694 result--;
695 else
696 break;
698 gcc_assert (result >= 0);
699 gcc_assert (result <= line_width);
700 gcc_assert (result == 0 ||
701 (line[result - 1] != ' '
702 && line[result -1] != '\t'
703 && line[result -1] != '\r'));
704 return result;
707 #if CHECKING_P
709 /* A helper function for testing get_line_width_without_trailing_whitespace. */
711 static void
712 assert_eq (const char *line, int expected_width)
714 int actual_value
715 = get_line_width_without_trailing_whitespace (line, strlen (line));
716 ASSERT_EQ (actual_value, expected_width);
719 /* Verify that get_line_width_without_trailing_whitespace is sane for
720 various inputs. It is not required to handle newlines. */
722 static void
723 test_get_line_width_without_trailing_whitespace ()
725 assert_eq ("", 0);
726 assert_eq (" ", 0);
727 assert_eq ("\t", 0);
728 assert_eq ("\r", 0);
729 assert_eq ("hello world", 11);
730 assert_eq ("hello world ", 11);
731 assert_eq ("hello world \t\t ", 11);
732 assert_eq ("hello world\r", 11);
735 #endif /* #if CHECKING_P */
737 /* Helper function for layout's ctor, for sanitizing locations relative
738 to the primary location within a diagnostic.
740 Compare LOC_A and LOC_B to see if it makes sense to print underlines
741 connecting their expanded locations. Doing so is only guaranteed to
742 make sense if the locations share the same macro expansion "history"
743 i.e. they can be traced through the same macro expansions, eventually
744 reaching an ordinary map.
746 This may be too strong a condition, but it effectively sanitizes
747 PR c++/70105, which has an example of printing an expression where the
748 final location of the expression is in a different macro, which
749 erroneously was leading to hundreds of lines of irrelevant source
750 being printed. */
752 static bool
753 compatible_locations_p (location_t loc_a, location_t loc_b)
755 if (IS_ADHOC_LOC (loc_a))
756 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
757 if (IS_ADHOC_LOC (loc_b))
758 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
760 /* If either location is one of the special locations outside of a
761 linemap, they are only compatible if they are equal. */
762 if (loc_a < RESERVED_LOCATION_COUNT
763 || loc_b < RESERVED_LOCATION_COUNT)
764 return loc_a == loc_b;
766 const line_map *map_a = linemap_lookup (line_table, loc_a);
767 linemap_assert (map_a);
769 const line_map *map_b = linemap_lookup (line_table, loc_b);
770 linemap_assert (map_b);
772 /* Are they within the same map? */
773 if (map_a == map_b)
775 /* Are both within the same macro expansion? */
776 if (linemap_macro_expansion_map_p (map_a))
778 /* Expand each location towards the spelling location, and
779 recurse. */
780 const line_map_macro *macro_map = linemap_check_macro (map_a);
781 source_location loc_a_toward_spelling
782 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
783 macro_map,
784 loc_a);
785 source_location loc_b_toward_spelling
786 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
787 macro_map,
788 loc_b);
789 return compatible_locations_p (loc_a_toward_spelling,
790 loc_b_toward_spelling);
793 /* Otherwise they are within the same ordinary map. */
794 return true;
796 else
798 /* Within different maps. */
800 /* If either is within a macro expansion, they are incompatible. */
801 if (linemap_macro_expansion_map_p (map_a)
802 || linemap_macro_expansion_map_p (map_b))
803 return false;
805 /* Within two different ordinary maps; they are compatible iff they
806 are in the same file. */
807 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
808 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
809 return ord_map_a->to_file == ord_map_b->to_file;
813 /* Comparator for sorting fix-it hints. */
815 static int
816 fixit_cmp (const void *p_a, const void *p_b)
818 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
819 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
820 return hint_a->get_start_loc () - hint_b->get_start_loc ();
823 /* Get the number of digits in the decimal representation
824 of VALUE. */
826 static int
827 num_digits (int value)
829 /* Perhaps simpler to use log10 for this, but doing it this way avoids
830 using floating point. */
831 gcc_assert (value >= 0);
833 if (value == 0)
834 return 1;
836 int digits = 0;
837 while (value > 0)
839 digits++;
840 value /= 10;
842 return digits;
846 #if CHECKING_P
848 /* Selftest for num_digits. */
850 static void
851 test_num_digits ()
853 ASSERT_EQ (1, num_digits (0));
854 ASSERT_EQ (1, num_digits (9));
855 ASSERT_EQ (2, num_digits (10));
856 ASSERT_EQ (2, num_digits (99));
857 ASSERT_EQ (3, num_digits (100));
858 ASSERT_EQ (3, num_digits (999));
859 ASSERT_EQ (4, num_digits (1000));
860 ASSERT_EQ (4, num_digits (9999));
861 ASSERT_EQ (5, num_digits (10000));
862 ASSERT_EQ (5, num_digits (99999));
863 ASSERT_EQ (6, num_digits (100000));
864 ASSERT_EQ (6, num_digits (999999));
865 ASSERT_EQ (7, num_digits (1000000));
866 ASSERT_EQ (7, num_digits (9999999));
867 ASSERT_EQ (8, num_digits (10000000));
868 ASSERT_EQ (8, num_digits (99999999));
871 #endif /* #if CHECKING_P */
873 /* Implementation of class layout. */
875 /* Constructor for class layout.
877 Filter the ranges from the rich_location to those that we can
878 sanely print, populating m_layout_ranges and m_fixit_hints.
879 Determine the range of lines that we will print, splitting them
880 up into an ordered list of disjoint spans of contiguous line numbers.
881 Determine m_x_offset, to ensure that the primary caret
882 will fit within the max_width provided by the diagnostic_context. */
884 layout::layout (diagnostic_context * context,
885 rich_location *richloc,
886 diagnostic_t diagnostic_kind)
887 : m_context (context),
888 m_pp (context->printer),
889 m_diagnostic_kind (diagnostic_kind),
890 m_primary_loc (richloc->get_range (0)->m_loc),
891 m_exploc (richloc->get_expanded_location (0)),
892 m_colorizer (context, diagnostic_kind),
893 m_colorize_source_p (context->colorize_source_p),
894 m_show_labels_p (context->show_labels_p),
895 m_show_line_numbers_p (context->show_line_numbers_p),
896 m_layout_ranges (richloc->get_num_locations ()),
897 m_fixit_hints (richloc->get_num_fixit_hints ()),
898 m_line_spans (1 + richloc->get_num_locations ()),
899 m_linenum_width (0),
900 m_x_offset (0)
902 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
904 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
905 Ignore any ranges that are awkward to handle. */
906 const location_range *loc_range = richloc->get_range (idx);
907 maybe_add_location_range (loc_range, idx, false);
910 /* Populate m_fixit_hints, filtering to only those that are in the
911 same file. */
912 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
914 const fixit_hint *hint = richloc->get_fixit_hint (i);
915 if (validate_fixit_hint_p (hint))
916 m_fixit_hints.safe_push (hint);
919 /* Sort m_fixit_hints. */
920 m_fixit_hints.qsort (fixit_cmp);
922 /* Populate m_line_spans. */
923 calculate_line_spans ();
925 /* Determine m_linenum_width. */
926 gcc_assert (m_line_spans.length () > 0);
927 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
928 int highest_line = last_span->m_last_line;
929 if (highest_line < 0)
930 highest_line = 0;
931 m_linenum_width = num_digits (highest_line);
932 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
933 if (m_line_spans.length () > 1)
934 m_linenum_width = MAX (m_linenum_width, 3);
936 /* Adjust m_x_offset.
937 Center the primary caret to fit in max_width; all columns
938 will be adjusted accordingly. */
939 size_t max_width = m_context->caret_max_width;
940 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
941 if (line && (size_t)m_exploc.column <= line.length ())
943 size_t right_margin = CARET_LINE_MARGIN;
944 size_t column = m_exploc.column;
945 if (m_show_line_numbers_p)
946 column += m_linenum_width + 2;
947 right_margin = MIN (line.length () - column, right_margin);
948 right_margin = max_width - right_margin;
949 if (line.length () >= max_width && column > right_margin)
950 m_x_offset = column - right_margin;
951 gcc_assert (m_x_offset >= 0);
954 if (context->show_ruler_p)
955 show_ruler (m_x_offset + max_width);
958 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
959 those that we can sanely print.
961 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
962 (for use as extrinsic state by label ranges FIXME).
964 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
965 filtered against this layout instance's current line spans: it
966 will only be added if the location is fully within the lines
967 already specified by other locations.
969 Return true iff LOC_RANGE was added. */
971 bool
972 layout::maybe_add_location_range (const location_range *loc_range,
973 unsigned original_idx,
974 bool restrict_to_current_line_spans)
976 gcc_assert (loc_range);
978 /* Split the "range" into caret and range information. */
979 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
981 /* Expand the various locations. */
982 expanded_location start
983 = linemap_client_expand_location_to_spelling_point
984 (src_range.m_start, LOCATION_ASPECT_START);
985 expanded_location finish
986 = linemap_client_expand_location_to_spelling_point
987 (src_range.m_finish, LOCATION_ASPECT_FINISH);
988 expanded_location caret
989 = linemap_client_expand_location_to_spelling_point
990 (loc_range->m_loc, LOCATION_ASPECT_CARET);
992 /* If any part of the range isn't in the same file as the primary
993 location of this diagnostic, ignore the range. */
994 if (start.file != m_exploc.file)
995 return false;
996 if (finish.file != m_exploc.file)
997 return false;
998 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
999 if (caret.file != m_exploc.file)
1000 return false;
1002 /* Sanitize the caret location for non-primary ranges. */
1003 if (m_layout_ranges.length () > 0)
1004 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1005 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1006 /* Discard any non-primary ranges that can't be printed
1007 sanely relative to the primary location. */
1008 return false;
1010 /* Everything is now known to be in the correct source file,
1011 but it may require further sanitization. */
1012 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
1013 original_idx, loc_range->m_label);
1015 /* If we have a range that finishes before it starts (perhaps
1016 from something built via macro expansion), printing the
1017 range is likely to be nonsensical. Also, attempting to do so
1018 breaks assumptions within the printing code (PR c/68473).
1019 Similarly, don't attempt to print ranges if one or both ends
1020 of the range aren't sane to print relative to the
1021 primary location (PR c++/70105). */
1022 if (start.line > finish.line
1023 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1024 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1026 /* Is this the primary location? */
1027 if (m_layout_ranges.length () == 0)
1029 /* We want to print the caret for the primary location, but
1030 we must sanitize away m_start and m_finish. */
1031 ri.m_start = ri.m_caret;
1032 ri.m_finish = ri.m_caret;
1034 else
1035 /* This is a non-primary range; ignore it. */
1036 return false;
1039 /* Potentially filter to just the lines already specified by other
1040 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1041 The layout ctor doesn't use it, and can't because m_line_spans
1042 hasn't been set up at that point. */
1043 if (restrict_to_current_line_spans)
1045 if (!will_show_line_p (start.line))
1046 return false;
1047 if (!will_show_line_p (finish.line))
1048 return false;
1049 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1050 if (!will_show_line_p (caret.line))
1051 return false;
1054 /* Passed all the tests; add the range to m_layout_ranges so that
1055 it will be printed. */
1056 m_layout_ranges.safe_push (ri);
1057 return true;
1060 /* Return true iff ROW is within one of the line spans for this layout. */
1062 bool
1063 layout::will_show_line_p (linenum_type row) const
1065 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1066 line_span_idx++)
1068 const line_span *line_span = get_line_span (line_span_idx);
1069 if (line_span->contains_line_p (row))
1070 return true;
1072 return false;
1075 /* Print a line showing a gap in the line numbers, for showing the boundary
1076 between two line spans. */
1078 void
1079 layout::print_gap_in_line_numbering ()
1081 gcc_assert (m_show_line_numbers_p);
1083 for (int i = 0; i < m_linenum_width + 1; i++)
1084 pp_character (m_pp, '.');
1086 pp_newline (m_pp);
1089 /* Return true iff we should print a heading when starting the
1090 line span with the given index. */
1092 bool
1093 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1095 /* We print a heading for every change of line span, hence for every
1096 line span after the initial one. */
1097 if (line_span_idx > 0)
1098 return true;
1100 /* We also do it for the initial span if the primary location of the
1101 diagnostic is in a different span. */
1102 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1103 return true;
1105 return false;
1108 /* Get an expanded_location for the first location of interest within
1109 the given line_span.
1110 Used when printing a heading to indicate a new line span. */
1112 expanded_location
1113 layout::get_expanded_location (const line_span *line_span) const
1115 /* Whenever possible, use the caret location. */
1116 if (line_span->contains_line_p (m_exploc.line))
1117 return m_exploc;
1119 /* Otherwise, use the start of the first range that's present
1120 within the line_span. */
1121 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1123 const layout_range *lr = &m_layout_ranges[i];
1124 if (line_span->contains_line_p (lr->m_start.m_line))
1126 expanded_location exploc = m_exploc;
1127 exploc.line = lr->m_start.m_line;
1128 exploc.column = lr->m_start.m_column;
1129 return exploc;
1133 /* Otherwise, use the location of the first fixit-hint present within
1134 the line_span. */
1135 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1137 const fixit_hint *hint = m_fixit_hints[i];
1138 location_t loc = hint->get_start_loc ();
1139 expanded_location exploc = expand_location (loc);
1140 if (line_span->contains_line_p (exploc.line))
1141 return exploc;
1144 /* It should not be possible to have a line span that didn't
1145 contain any of the layout_range or fixit_hint instances. */
1146 gcc_unreachable ();
1147 return m_exploc;
1150 /* Determine if HINT is meaningful to print within this layout. */
1152 bool
1153 layout::validate_fixit_hint_p (const fixit_hint *hint)
1155 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1156 return false;
1157 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1158 return false;
1160 return true;
1163 /* Determine the range of lines affected by HINT.
1164 This assumes that HINT has already been filtered by
1165 validate_fixit_hint_p, and so affects the correct source file. */
1167 static line_span
1168 get_line_span_for_fixit_hint (const fixit_hint *hint)
1170 gcc_assert (hint);
1172 int start_line = LOCATION_LINE (hint->get_start_loc ());
1174 /* For line-insertion fix-it hints, add the previous line to the
1175 span, to give the user more context on the proposed change. */
1176 if (hint->ends_with_newline_p ())
1177 if (start_line > 1)
1178 start_line--;
1180 return line_span (start_line,
1181 LOCATION_LINE (hint->get_next_loc ()));
1184 /* We want to print the pertinent source code at a diagnostic. The
1185 rich_location can contain multiple locations. This will have been
1186 filtered into m_exploc (the caret for the primary location) and
1187 m_layout_ranges, for those ranges within the same source file.
1189 We will print a subset of the lines within the source file in question,
1190 as a collection of "spans" of lines.
1192 This function populates m_line_spans with an ordered, disjoint list of
1193 the line spans of interest.
1195 Printing a gap between line spans takes one line, so, when printing
1196 line numbers, we allow a gap of up to one line between spans when
1197 merging, since it makes more sense to print the source line rather than a
1198 "gap-in-line-numbering" line. When not printing line numbers, it's
1199 better to be more explicit about what's going on, so keeping them as
1200 separate spans is preferred.
1202 For example, if the primary range is on lines 8-10, with secondary ranges
1203 covering lines 5-6 and lines 13-15:
1206 005 |RANGE 1
1207 006 |RANGE 1
1209 008 |PRIMARY RANGE
1210 009 |PRIMARY CARET
1211 010 |PRIMARY RANGE
1214 013 |RANGE 2
1215 014 |RANGE 2
1216 015 |RANGE 2
1219 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1221 With line numbering off (with span headers), we want three spans: lines 5-6,
1222 lines 8-10, and lines 13-15. */
1224 void
1225 layout::calculate_line_spans ()
1227 /* This should only be called once, by the ctor. */
1228 gcc_assert (m_line_spans.length () == 0);
1230 /* Populate tmp_spans with individual spans, for each of
1231 m_exploc, and for m_layout_ranges. */
1232 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1233 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1234 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1236 const layout_range *lr = &m_layout_ranges[i];
1237 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1238 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1239 lr->m_finish.m_line));
1242 /* Also add spans for any fix-it hints, in case they cover other lines. */
1243 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1245 const fixit_hint *hint = m_fixit_hints[i];
1246 gcc_assert (hint);
1247 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1250 /* Sort them. */
1251 tmp_spans.qsort(line_span::comparator);
1253 /* Now iterate through tmp_spans, copying into m_line_spans, and
1254 combining where possible. */
1255 gcc_assert (tmp_spans.length () > 0);
1256 m_line_spans.safe_push (tmp_spans[0]);
1257 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1259 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1260 const line_span *next = &tmp_spans[i];
1261 gcc_assert (next->m_first_line >= current->m_first_line);
1262 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1263 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
1265 /* We can merge them. */
1266 if (next->m_last_line > current->m_last_line)
1267 current->m_last_line = next->m_last_line;
1269 else
1271 /* No merger possible. */
1272 m_line_spans.safe_push (*next);
1276 /* Verify the result, in m_line_spans. */
1277 gcc_assert (m_line_spans.length () > 0);
1278 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1280 const line_span *prev = &m_line_spans[i - 1];
1281 const line_span *next = &m_line_spans[i];
1282 /* The individual spans must be sane. */
1283 gcc_assert (prev->m_first_line <= prev->m_last_line);
1284 gcc_assert (next->m_first_line <= next->m_last_line);
1285 /* The spans must be ordered. */
1286 gcc_assert (prev->m_first_line < next->m_first_line);
1287 /* There must be a gap of at least one line between separate spans. */
1288 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1292 /* Print line ROW of source code, potentially colorized at any ranges, and
1293 populate *LBOUNDS_OUT.
1294 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1295 is its width. */
1297 void
1298 layout::print_source_line (linenum_type row, const char *line, int line_width,
1299 line_bounds *lbounds_out)
1301 m_colorizer.set_normal_text ();
1303 /* We will stop printing the source line at any trailing
1304 whitespace. */
1305 line_width = get_line_width_without_trailing_whitespace (line,
1306 line_width);
1307 line += m_x_offset;
1309 if (m_show_line_numbers_p)
1311 int width = num_digits (row);
1312 for (int i = 0; i < m_linenum_width - width; i++)
1313 pp_space (m_pp);
1314 pp_printf (m_pp, "%i | ", row);
1316 else
1317 pp_space (m_pp);
1318 int first_non_ws = INT_MAX;
1319 int last_non_ws = 0;
1320 int column;
1321 for (column = 1 + m_x_offset; column <= line_width; column++)
1323 /* Assuming colorization is enabled for the caret and underline
1324 characters, we may also colorize the associated characters
1325 within the source line.
1327 For frontends that generate range information, we color the
1328 associated characters in the source line the same as the
1329 carets and underlines in the annotation line, to make it easier
1330 for the reader to see the pertinent code.
1332 For frontends that only generate carets, we don't colorize the
1333 characters above them, since this would look strange (e.g.
1334 colorizing just the first character in a token). */
1335 if (m_colorize_source_p)
1337 bool in_range_p;
1338 point_state state;
1339 in_range_p = get_state_at_point (row, column,
1340 0, INT_MAX,
1341 &state);
1342 if (in_range_p)
1343 m_colorizer.set_range (state.range_idx);
1344 else
1345 m_colorizer.set_normal_text ();
1347 char c = *line;
1348 if (c == '\0' || c == '\t' || c == '\r')
1349 c = ' ';
1350 if (c != ' ')
1352 last_non_ws = column;
1353 if (first_non_ws == INT_MAX)
1354 first_non_ws = column;
1356 pp_character (m_pp, c);
1357 line++;
1359 print_newline ();
1361 lbounds_out->m_first_non_ws = first_non_ws;
1362 lbounds_out->m_last_non_ws = last_non_ws;
1365 /* Determine if we should print an annotation line for ROW.
1366 i.e. if any of m_layout_ranges contains ROW. */
1368 bool
1369 layout::should_print_annotation_line_p (linenum_type row) const
1371 layout_range *range;
1372 int i;
1373 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1375 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1376 return false;
1377 if (range->intersects_line_p (row))
1378 return true;
1380 return false;
1383 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1384 margin, which is empty for annotation lines. Otherwise, do nothing. */
1386 void
1387 layout::start_annotation_line (char margin_char) const
1389 if (m_show_line_numbers_p)
1391 for (int i = 0; i < m_linenum_width; i++)
1392 pp_character (m_pp, margin_char);
1393 pp_string (m_pp, " |");
1397 /* Print a line consisting of the caret/underlines for the given
1398 source line. */
1400 void
1401 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1403 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1404 lbounds.m_last_non_ws);
1406 start_annotation_line ();
1407 pp_space (m_pp);
1409 for (int column = 1 + m_x_offset; column < x_bound; column++)
1411 bool in_range_p;
1412 point_state state;
1413 in_range_p = get_state_at_point (row, column,
1414 lbounds.m_first_non_ws,
1415 lbounds.m_last_non_ws,
1416 &state);
1417 if (in_range_p)
1419 /* Within a range. Draw either the caret or an underline. */
1420 m_colorizer.set_range (state.range_idx);
1421 if (state.draw_caret_p)
1423 /* Draw the caret. */
1424 char caret_char;
1425 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1426 caret_char = m_context->caret_chars[state.range_idx];
1427 else
1428 caret_char = '^';
1429 pp_character (m_pp, caret_char);
1431 else
1432 pp_character (m_pp, '~');
1434 else
1436 /* Not in a range. */
1437 m_colorizer.set_normal_text ();
1438 pp_character (m_pp, ' ');
1441 print_newline ();
1444 /* Implementation detail of layout::print_any_labels.
1446 A label within the given row of source. */
1448 struct line_label
1450 line_label (int state_idx, int column, label_text text)
1451 : m_state_idx (state_idx), m_column (column),
1452 m_text (text), m_length (strlen (text.m_buffer)),
1453 m_label_line (0)
1456 /* Sorting is primarily by column, then by state index. */
1457 static int comparator (const void *p1, const void *p2)
1459 const line_label *ll1 = (const line_label *)p1;
1460 const line_label *ll2 = (const line_label *)p2;
1461 int column_cmp = compare (ll1->m_column, ll2->m_column);
1462 if (column_cmp)
1463 return column_cmp;
1464 return compare (ll1->m_state_idx, ll2->m_state_idx);
1467 int m_state_idx;
1468 int m_column;
1469 label_text m_text;
1470 size_t m_length;
1471 int m_label_line;
1474 /* Print any labels in this row. */
1475 void
1476 layout::print_any_labels (linenum_type row)
1478 int i;
1479 auto_vec<line_label> labels;
1481 /* Gather the labels that are to be printed into "labels". */
1483 layout_range *range;
1484 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1486 /* Most ranges don't have labels, so reject this first. */
1487 if (range->m_label == NULL)
1488 continue;
1490 /* The range's caret must be on this line. */
1491 if (range->m_caret.m_line != row)
1492 continue;
1494 /* Reject labels that aren't fully visible due to clipping
1495 by m_x_offset. */
1496 if (range->m_caret.m_column <= m_x_offset)
1497 continue;
1499 label_text text;
1500 text = range->m_label->get_text (range->m_original_idx);
1502 /* Allow for labels that return NULL from their get_text
1503 implementation (so e.g. such labels can control their own
1504 visibility). */
1505 if (text.m_buffer == NULL)
1506 continue;
1508 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1512 /* Bail out if there are no labels on this row. */
1513 if (labels.length () == 0)
1514 return;
1516 /* Sort them. */
1517 labels.qsort(line_label::comparator);
1519 /* Figure out how many "label lines" we need, and which
1520 one each label is printed in.
1522 For example, if the labels aren't too densely packed,
1523 we can fit them on the same line, giving two "label lines":
1525 foo + bar
1526 ~~~ ~~~
1527 | | : label line 0
1528 l0 l1 : label line 1
1530 If they would touch each other or overlap, then we need
1531 additional "label lines":
1533 foo + bar
1534 ~~~ ~~~
1535 | | : label line 0
1536 | label 1 : label line 1
1537 label 0 : label line 2
1539 Place the final label on label line 1, and work backwards, adding
1540 label lines as needed.
1542 If multiple labels are at the same place, put them on separate
1543 label lines:
1545 foo + bar
1546 ^ : label line 0
1547 | : label line 1
1548 label 1 : label line 2
1549 label 0 : label line 3. */
1551 int max_label_line = 1;
1553 int next_column = INT_MAX;
1554 line_label *label;
1555 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1557 /* Would this label "touch" or overlap the next label? */
1558 if (label->m_column + label->m_length >= (size_t)next_column)
1559 max_label_line++;
1561 label->m_label_line = max_label_line;
1562 next_column = label->m_column;
1566 /* Print the "label lines". For each label within the line, print
1567 either a vertical bar ('|') for the labels that are lower down, or the
1568 labels themselves once we've reached their line. */
1570 /* Keep track of in which column we last printed a vertical bar.
1571 This allows us to suppress duplicate vertical bars for the case
1572 where multiple labels are on one column. */
1573 int last_vbar = 0;
1574 for (int label_line = 0; label_line <= max_label_line; label_line++)
1576 start_annotation_line ();
1577 pp_space (m_pp);
1578 int column = 1 + m_x_offset;
1579 line_label *label;
1580 FOR_EACH_VEC_ELT (labels, i, label)
1582 if (label_line > label->m_label_line)
1583 /* We've printed all the labels for this label line. */
1584 break;
1586 if (label_line == label->m_label_line)
1588 gcc_assert (column <= label->m_column);
1589 move_to_column (&column, label->m_column, true);
1590 m_colorizer.set_range (label->m_state_idx);
1591 pp_string (m_pp, label->m_text.m_buffer);
1592 m_colorizer.set_normal_text ();
1593 column += label->m_length;
1595 else if (label->m_column != last_vbar)
1597 gcc_assert (column <= label->m_column);
1598 move_to_column (&column, label->m_column, true);
1599 m_colorizer.set_range (label->m_state_idx);
1600 pp_character (m_pp, '|');
1601 m_colorizer.set_normal_text ();
1602 last_vbar = column;
1603 column++;
1606 print_newline ();
1610 /* Clean up. */
1612 line_label *label;
1613 FOR_EACH_VEC_ELT (labels, i, label)
1614 label->m_text.maybe_free ();
1618 /* If there are any fixit hints inserting new lines before source line ROW,
1619 print them.
1621 They are printed on lines of their own, before the source line
1622 itself, with a leading '+'. */
1624 void
1625 layout::print_leading_fixits (linenum_type row)
1627 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1629 const fixit_hint *hint = m_fixit_hints[i];
1631 if (!hint->ends_with_newline_p ())
1632 /* Not a newline fixit; print it in print_trailing_fixits. */
1633 continue;
1635 gcc_assert (hint->insertion_p ());
1637 if (hint->affects_line_p (m_exploc.file, row))
1639 /* Printing the '+' with normal colorization
1640 and the inserted line with "insert" colorization
1641 helps them stand out from each other, and from
1642 the surrounding text. */
1643 m_colorizer.set_normal_text ();
1644 start_annotation_line ('+');
1645 pp_character (m_pp, '+');
1646 m_colorizer.set_fixit_insert ();
1647 /* Print all but the trailing newline of the fix-it hint.
1648 We have to print the newline separately to avoid
1649 getting additional pp prefixes printed. */
1650 for (size_t i = 0; i < hint->get_length () - 1; i++)
1651 pp_character (m_pp, hint->get_string ()[i]);
1652 m_colorizer.set_normal_text ();
1653 pp_newline (m_pp);
1658 /* Subroutine of layout::print_trailing_fixits.
1660 Determine if the annotation line printed for LINE contained
1661 the exact range from START_COLUMN to FINISH_COLUMN. */
1663 bool
1664 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1665 int finish_column) const
1667 layout_range *range;
1668 int i;
1669 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1670 if (range->m_start.m_line == line
1671 && range->m_start.m_column == start_column
1672 && range->m_finish.m_line == line
1673 && range->m_finish.m_column == finish_column)
1674 return true;
1675 return false;
1678 /* Classes for printing trailing fix-it hints i.e. those that
1679 don't add new lines.
1681 For insertion, these can look like:
1683 new_text
1685 For replacement, these can look like:
1687 ------------- : underline showing affected range
1688 new_text
1690 For deletion, these can look like:
1692 ------------- : underline showing affected range
1694 This can become confusing if they overlap, and so we need
1695 to do some preprocessing to decide what to print.
1696 We use the list of fixit_hint instances affecting the line
1697 to build a list of "correction" instances, and print the
1698 latter.
1700 For example, consider a set of fix-its for converting
1701 a C-style cast to a C++ const_cast.
1703 Given:
1705 ..000000000111111111122222222223333333333.
1706 ..123456789012345678901234567890123456789.
1707 foo *f = (foo *)ptr->field;
1708 ^~~~~
1710 and the fix-it hints:
1711 - replace col 10 (the open paren) with "const_cast<"
1712 - replace col 16 (the close paren) with "> ("
1713 - insert ")" before col 27
1715 then we would get odd-looking output:
1717 foo *f = (foo *)ptr->field;
1718 ^~~~~
1720 const_cast<
1722 > ( )
1724 It would be better to detect when fixit hints are going to
1725 overlap (those that require new lines), and to consolidate
1726 the printing of such fixits, giving something like:
1728 foo *f = (foo *)ptr->field;
1729 ^~~~~
1730 -----------------
1731 const_cast<foo *> (ptr->field)
1733 This works by detecting when the printing would overlap, and
1734 effectively injecting no-op replace hints into the gaps between
1735 such fix-its, so that the printing joins up.
1737 In the above example, the overlap of:
1738 - replace col 10 (the open paren) with "const_cast<"
1739 and:
1740 - replace col 16 (the close paren) with "> ("
1741 is fixed by injecting a no-op:
1742 - replace cols 11-15 with themselves ("foo *")
1743 and consolidating these, making:
1744 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1745 i.e.:
1746 - replace cols 10-16 with "const_cast<foo *> ("
1748 This overlaps with the final fix-it hint:
1749 - insert ")" before col 27
1750 and so we repeat the consolidation process, by injecting
1751 a no-op:
1752 - replace cols 17-26 with themselves ("ptr->field")
1753 giving:
1754 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1755 i.e.:
1756 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1758 and is thus printed as desired. */
1760 /* A range of columns within a line. */
1762 struct column_range
1764 column_range (int start_, int finish_) : start (start_), finish (finish_)
1766 /* We must have either a range, or an insertion. */
1767 gcc_assert (start <= finish || finish == start - 1);
1770 bool operator== (const column_range &other) const
1772 return start == other.start && finish == other.finish;
1775 int start;
1776 int finish;
1779 /* Get the range of columns that HINT would affect. */
1781 static column_range
1782 get_affected_columns (const fixit_hint *hint)
1784 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1785 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1787 return column_range (start_column, finish_column);
1790 /* Get the range of columns that would be printed for HINT. */
1792 static column_range
1793 get_printed_columns (const fixit_hint *hint)
1795 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1796 int final_hint_column = start_column + hint->get_length () - 1;
1797 if (hint->insertion_p ())
1799 return column_range (start_column, final_hint_column);
1801 else
1803 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1805 return column_range (start_column,
1806 MAX (finish_column, final_hint_column));
1810 /* A correction on a particular line.
1811 This describes a plan for how to print one or more fixit_hint
1812 instances that affected the line, potentially consolidating hints
1813 into corrections to make the result easier for the user to read. */
1815 struct correction
1817 correction (column_range affected_columns,
1818 column_range printed_columns,
1819 const char *new_text, size_t new_text_len)
1820 : m_affected_columns (affected_columns),
1821 m_printed_columns (printed_columns),
1822 m_text (xstrdup (new_text)),
1823 m_len (new_text_len),
1824 m_alloc_sz (new_text_len + 1)
1828 ~correction () { free (m_text); }
1830 bool insertion_p () const
1832 return m_affected_columns.start == m_affected_columns.finish + 1;
1835 void ensure_capacity (size_t len);
1836 void ensure_terminated ();
1838 void overwrite (int dst_offset, const char_span &src_span)
1840 gcc_assert (dst_offset >= 0);
1841 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1842 memcpy (m_text + dst_offset, src_span.get_buffer (),
1843 src_span.length ());
1846 /* If insert, then start: the column before which the text
1847 is to be inserted, and finish is offset by the length of
1848 the replacement.
1849 If replace, then the range of columns affected. */
1850 column_range m_affected_columns;
1852 /* If insert, then start: the column before which the text
1853 is to be inserted, and finish is offset by the length of
1854 the replacement.
1855 If replace, then the range of columns affected. */
1856 column_range m_printed_columns;
1858 /* The text to be inserted/used as replacement. */
1859 char *m_text;
1860 size_t m_len;
1861 size_t m_alloc_sz;
1864 /* Ensure that m_text can hold a string of length LEN
1865 (plus 1 for 0-termination). */
1867 void
1868 correction::ensure_capacity (size_t len)
1870 /* Allow 1 extra byte for 0-termination. */
1871 if (m_alloc_sz < (len + 1))
1873 size_t new_alloc_sz = (len + 1) * 2;
1874 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1875 m_alloc_sz = new_alloc_sz;
1879 /* Ensure that m_text is 0-terminated. */
1881 void
1882 correction::ensure_terminated ()
1884 /* 0-terminate the buffer. */
1885 gcc_assert (m_len < m_alloc_sz);
1886 m_text[m_len] = '\0';
1889 /* A list of corrections affecting a particular line.
1890 This is used by layout::print_trailing_fixits for planning
1891 how to print the fix-it hints affecting the line. */
1893 struct line_corrections
1895 line_corrections (const char *filename, linenum_type row)
1896 : m_filename (filename), m_row (row)
1898 ~line_corrections ();
1900 void add_hint (const fixit_hint *hint);
1902 const char *m_filename;
1903 linenum_type m_row;
1904 auto_vec <correction *> m_corrections;
1907 /* struct line_corrections. */
1909 line_corrections::~line_corrections ()
1911 unsigned i;
1912 correction *c;
1913 FOR_EACH_VEC_ELT (m_corrections, i, c)
1914 delete c;
1917 /* A struct wrapping a particular source line, allowing
1918 run-time bounds-checking of accesses in a checked build. */
1920 struct source_line
1922 source_line (const char *filename, int line);
1924 char_span as_span () { return char_span (chars, width); }
1926 const char *chars;
1927 int width;
1930 /* source_line's ctor. */
1932 source_line::source_line (const char *filename, int line)
1934 char_span span = location_get_source_line (filename, line);
1935 chars = span.get_buffer ();
1936 width = span.length ();
1939 /* Add HINT to the corrections for this line.
1940 Attempt to consolidate nearby hints so that they will not
1941 overlap with printed. */
1943 void
1944 line_corrections::add_hint (const fixit_hint *hint)
1946 column_range affected_columns = get_affected_columns (hint);
1947 column_range printed_columns = get_printed_columns (hint);
1949 /* Potentially consolidate. */
1950 if (!m_corrections.is_empty ())
1952 correction *last_correction
1953 = m_corrections[m_corrections.length () - 1];
1955 /* The following consolidation code assumes that the fix-it hints
1956 have been sorted by start (done within layout's ctor). */
1957 gcc_assert (affected_columns.start
1958 >= last_correction->m_affected_columns.start);
1959 gcc_assert (printed_columns.start
1960 >= last_correction->m_printed_columns.start);
1962 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1964 /* We have two hints for which the printed forms of the hints
1965 would touch or overlap, so we need to consolidate them to avoid
1966 confusing the user.
1967 Attempt to inject a "replace" correction from immediately
1968 after the end of the last hint to immediately before the start
1969 of the next hint. */
1970 column_range between (last_correction->m_affected_columns.finish + 1,
1971 printed_columns.start - 1);
1973 /* Try to read the source. */
1974 source_line line (m_filename, m_row);
1975 if (line.chars && between.finish < line.width)
1977 /* Consolidate into the last correction:
1978 add a no-op "replace" of the "between" text, and
1979 add the text from the new hint. */
1980 int old_len = last_correction->m_len;
1981 gcc_assert (old_len >= 0);
1982 int between_len = between.finish + 1 - between.start;
1983 gcc_assert (between_len >= 0);
1984 int new_len = old_len + between_len + hint->get_length ();
1985 gcc_assert (new_len >= 0);
1986 last_correction->ensure_capacity (new_len);
1987 last_correction->overwrite
1988 (old_len,
1989 line.as_span ().subspan (between.start - 1,
1990 between.finish + 1 - between.start));
1991 last_correction->overwrite (old_len + between_len,
1992 char_span (hint->get_string (),
1993 hint->get_length ()));
1994 last_correction->m_len = new_len;
1995 last_correction->ensure_terminated ();
1996 last_correction->m_affected_columns.finish
1997 = affected_columns.finish;
1998 last_correction->m_printed_columns.finish
1999 += between_len + hint->get_length ();
2000 return;
2005 /* If no consolidation happened, add a new correction instance. */
2006 m_corrections.safe_push (new correction (affected_columns,
2007 printed_columns,
2008 hint->get_string (),
2009 hint->get_length ()));
2012 /* If there are any fixit hints on source line ROW, print them.
2013 They are printed in order, attempting to combine them onto lines, but
2014 starting new lines if necessary.
2015 Fix-it hints that insert new lines are handled separately,
2016 in layout::print_leading_fixits. */
2018 void
2019 layout::print_trailing_fixits (linenum_type row)
2021 /* Build a list of correction instances for the line,
2022 potentially consolidating hints (for the sake of readability). */
2023 line_corrections corrections (m_exploc.file, row);
2024 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2026 const fixit_hint *hint = m_fixit_hints[i];
2028 /* Newline fixits are handled by layout::print_leading_fixits. */
2029 if (hint->ends_with_newline_p ())
2030 continue;
2032 if (hint->affects_line_p (m_exploc.file, row))
2033 corrections.add_hint (hint);
2036 /* Now print the corrections. */
2037 unsigned i;
2038 correction *c;
2039 int column = m_x_offset;
2041 if (!corrections.m_corrections.is_empty ())
2042 start_annotation_line ();
2044 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2046 /* For now we assume each fixit hint can only touch one line. */
2047 if (c->insertion_p ())
2049 /* This assumes the insertion just affects one line. */
2050 int start_column = c->m_printed_columns.start;
2051 move_to_column (&column, start_column, true);
2052 m_colorizer.set_fixit_insert ();
2053 pp_string (m_pp, c->m_text);
2054 m_colorizer.set_normal_text ();
2055 column += c->m_len;
2057 else
2059 /* If the range of the replacement wasn't printed in the
2060 annotation line, then print an extra underline to
2061 indicate exactly what is being replaced.
2062 Always show it for removals. */
2063 int start_column = c->m_affected_columns.start;
2064 int finish_column = c->m_affected_columns.finish;
2065 if (!annotation_line_showed_range_p (row, start_column,
2066 finish_column)
2067 || c->m_len == 0)
2069 move_to_column (&column, start_column, true);
2070 m_colorizer.set_fixit_delete ();
2071 for (; column <= finish_column; column++)
2072 pp_character (m_pp, '-');
2073 m_colorizer.set_normal_text ();
2075 /* Print the replacement text. REPLACE also covers
2076 removals, so only do this extra work (potentially starting
2077 a new line) if we have actual replacement text. */
2078 if (c->m_len > 0)
2080 move_to_column (&column, start_column, true);
2081 m_colorizer.set_fixit_insert ();
2082 pp_string (m_pp, c->m_text);
2083 m_colorizer.set_normal_text ();
2084 column += c->m_len;
2089 /* Add a trailing newline, if necessary. */
2090 move_to_column (&column, 0, false);
2093 /* Disable any colorization and emit a newline. */
2095 void
2096 layout::print_newline ()
2098 m_colorizer.set_normal_text ();
2099 pp_newline (m_pp);
2102 /* Return true if (ROW/COLUMN) is within a range of the layout.
2103 If it returns true, OUT_STATE is written to, with the
2104 range index, and whether we should draw the caret at
2105 (ROW/COLUMN) (as opposed to an underline). */
2107 bool
2108 layout::get_state_at_point (/* Inputs. */
2109 linenum_type row, int column,
2110 int first_non_ws, int last_non_ws,
2111 /* Outputs. */
2112 point_state *out_state)
2114 layout_range *range;
2115 int i;
2116 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2118 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2119 /* Bail out early, so that such ranges don't affect underlining or
2120 source colorization. */
2121 continue;
2123 if (range->contains_point (row, column))
2125 out_state->range_idx = i;
2127 /* Are we at the range's caret? is it visible? */
2128 out_state->draw_caret_p = false;
2129 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2130 && row == range->m_caret.m_line
2131 && column == range->m_caret.m_column)
2132 out_state->draw_caret_p = true;
2134 /* Within a multiline range, don't display any underline
2135 in any leading or trailing whitespace on a line.
2136 We do display carets, however. */
2137 if (!out_state->draw_caret_p)
2138 if (column < first_non_ws || column > last_non_ws)
2139 return false;
2141 /* We are within a range. */
2142 return true;
2146 return false;
2149 /* Helper function for use by layout::print_line when printing the
2150 annotation line under the source line.
2151 Get the column beyond the rightmost one that could contain a caret or
2152 range marker, given that we stop rendering at trailing whitespace.
2153 ROW is the source line within the given file.
2154 CARET_COLUMN is the column of range 0's caret.
2155 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2156 character of source (as determined when printing the source line). */
2159 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2160 int last_non_ws_column)
2162 int result = caret_column + 1;
2164 layout_range *range;
2165 int i;
2166 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2168 if (row >= range->m_start.m_line)
2170 if (range->m_finish.m_line == row)
2172 /* On the final line within a range; ensure that
2173 we render up to the end of the range. */
2174 if (result <= range->m_finish.m_column)
2175 result = range->m_finish.m_column + 1;
2177 else if (row < range->m_finish.m_line)
2179 /* Within a multiline range; ensure that we render up to the
2180 last non-whitespace column. */
2181 if (result <= last_non_ws_column)
2182 result = last_non_ws_column + 1;
2187 return result;
2190 /* Given *COLUMN as an x-coordinate, print spaces to position
2191 successive output at DEST_COLUMN, printing a newline if necessary,
2192 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2193 left margin after any newline. */
2195 void
2196 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2198 /* Start a new line if we need to. */
2199 if (*column > dest_column)
2201 print_newline ();
2202 if (add_left_margin)
2203 start_annotation_line ();
2204 *column = m_x_offset;
2207 while (*column < dest_column)
2209 pp_space (m_pp);
2210 (*column)++;
2214 /* For debugging layout issues, render a ruler giving column numbers
2215 (after the 1-column indent). */
2217 void
2218 layout::show_ruler (int max_column) const
2220 /* Hundreds. */
2221 if (max_column > 99)
2223 start_annotation_line ();
2224 pp_space (m_pp);
2225 for (int column = 1 + m_x_offset; column <= max_column; column++)
2226 if (column % 10 == 0)
2227 pp_character (m_pp, '0' + (column / 100) % 10);
2228 else
2229 pp_space (m_pp);
2230 pp_newline (m_pp);
2233 /* Tens. */
2234 start_annotation_line ();
2235 pp_space (m_pp);
2236 for (int column = 1 + m_x_offset; column <= max_column; column++)
2237 if (column % 10 == 0)
2238 pp_character (m_pp, '0' + (column / 10) % 10);
2239 else
2240 pp_space (m_pp);
2241 pp_newline (m_pp);
2243 /* Units. */
2244 start_annotation_line ();
2245 pp_space (m_pp);
2246 for (int column = 1 + m_x_offset; column <= max_column; column++)
2247 pp_character (m_pp, '0' + (column % 10));
2248 pp_newline (m_pp);
2251 /* Print leading fix-its (for new lines inserted before the source line)
2252 then the source line, followed by an annotation line
2253 consisting of any caret/underlines, then any fixits.
2254 If the source line can't be read, print nothing. */
2255 void
2256 layout::print_line (linenum_type row)
2258 char_span line = location_get_source_line (m_exploc.file, row);
2259 if (!line)
2260 return;
2262 line_bounds lbounds;
2263 print_leading_fixits (row);
2264 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2265 if (should_print_annotation_line_p (row))
2266 print_annotation_line (row, lbounds);
2267 if (m_show_labels_p)
2268 print_any_labels (row);
2269 print_trailing_fixits (row);
2272 } /* End of anonymous namespace. */
2274 /* If LOC is within the spans of lines that will already be printed for
2275 this gcc_rich_location, then add it as a secondary location and return true.
2277 Otherwise return false. */
2279 bool
2280 gcc_rich_location::add_location_if_nearby (location_t loc)
2282 /* Use the layout location-handling logic to sanitize LOC,
2283 filtering it to the current line spans within a temporary
2284 layout instance. */
2285 layout layout (global_dc, this, DK_ERROR);
2286 location_range loc_range;
2287 loc_range.m_loc = loc;
2288 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2289 if (!layout.maybe_add_location_range (&loc_range, 0, true))
2290 return false;
2292 add_range (loc);
2293 return true;
2296 /* Print the physical source code corresponding to the location of
2297 this diagnostic, with additional annotations. */
2299 void
2300 diagnostic_show_locus (diagnostic_context * context,
2301 rich_location *richloc,
2302 diagnostic_t diagnostic_kind)
2304 pp_newline (context->printer);
2306 location_t loc = richloc->get_loc ();
2307 /* Do nothing if source-printing has been disabled. */
2308 if (!context->show_caret)
2309 return;
2311 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2312 if (loc <= BUILTINS_LOCATION)
2313 return;
2315 /* Don't print the same source location twice in a row, unless we have
2316 fix-it hints. */
2317 if (loc == context->last_location
2318 && richloc->get_num_fixit_hints () == 0)
2319 return;
2321 context->last_location = loc;
2323 char *saved_prefix = pp_take_prefix (context->printer);
2324 pp_set_prefix (context->printer, NULL);
2326 layout layout (context, richloc, diagnostic_kind);
2327 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2328 line_span_idx++)
2330 const line_span *line_span = layout.get_line_span (line_span_idx);
2331 if (context->show_line_numbers_p)
2333 /* With line numbers, we should show whenever the line-numbering
2334 "jumps". */
2335 if (line_span_idx > 0)
2336 layout.print_gap_in_line_numbering ();
2338 else
2340 /* Without line numbers, we print headings for some line spans. */
2341 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2343 expanded_location exploc
2344 = layout.get_expanded_location (line_span);
2345 context->start_span (context, exploc);
2348 linenum_type last_line = line_span->get_last_line ();
2349 for (linenum_type row = line_span->get_first_line ();
2350 row <= last_line; row++)
2351 layout.print_line (row);
2354 pp_set_prefix (context->printer, saved_prefix);
2357 #if CHECKING_P
2359 namespace selftest {
2361 /* Selftests for diagnostic_show_locus. */
2363 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2365 static void
2366 test_diagnostic_show_locus_unknown_location ()
2368 test_diagnostic_context dc;
2369 rich_location richloc (line_table, UNKNOWN_LOCATION);
2370 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2371 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2374 /* Verify that diagnostic_show_locus works sanely for various
2375 single-line cases.
2377 All of these work on the following 1-line source file:
2378 .0000000001111111
2379 .1234567890123456
2380 "foo = bar.field;\n"
2381 which is set up by test_diagnostic_show_locus_one_liner and calls
2382 them. */
2384 /* Just a caret. */
2386 static void
2387 test_one_liner_simple_caret ()
2389 test_diagnostic_context dc;
2390 location_t caret = linemap_position_for_column (line_table, 10);
2391 rich_location richloc (line_table, caret);
2392 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2393 ASSERT_STREQ ("\n"
2394 " foo = bar.field;\n"
2395 " ^\n",
2396 pp_formatted_text (dc.printer));
2399 /* Caret and range. */
2401 static void
2402 test_one_liner_caret_and_range ()
2404 test_diagnostic_context dc;
2405 location_t caret = linemap_position_for_column (line_table, 10);
2406 location_t start = linemap_position_for_column (line_table, 7);
2407 location_t finish = linemap_position_for_column (line_table, 15);
2408 location_t loc = make_location (caret, start, finish);
2409 rich_location richloc (line_table, loc);
2410 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2411 ASSERT_STREQ ("\n"
2412 " foo = bar.field;\n"
2413 " ~~~^~~~~~\n",
2414 pp_formatted_text (dc.printer));
2417 /* Multiple ranges and carets. */
2419 static void
2420 test_one_liner_multiple_carets_and_ranges ()
2422 test_diagnostic_context dc;
2423 location_t foo
2424 = make_location (linemap_position_for_column (line_table, 2),
2425 linemap_position_for_column (line_table, 1),
2426 linemap_position_for_column (line_table, 3));
2427 dc.caret_chars[0] = 'A';
2429 location_t bar
2430 = make_location (linemap_position_for_column (line_table, 8),
2431 linemap_position_for_column (line_table, 7),
2432 linemap_position_for_column (line_table, 9));
2433 dc.caret_chars[1] = 'B';
2435 location_t field
2436 = make_location (linemap_position_for_column (line_table, 13),
2437 linemap_position_for_column (line_table, 11),
2438 linemap_position_for_column (line_table, 15));
2439 dc.caret_chars[2] = 'C';
2441 rich_location richloc (line_table, foo);
2442 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2443 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2444 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2445 ASSERT_STREQ ("\n"
2446 " foo = bar.field;\n"
2447 " ~A~ ~B~ ~~C~~\n",
2448 pp_formatted_text (dc.printer));
2451 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2453 static void
2454 test_one_liner_fixit_insert_before ()
2456 test_diagnostic_context dc;
2457 location_t caret = linemap_position_for_column (line_table, 7);
2458 rich_location richloc (line_table, caret);
2459 richloc.add_fixit_insert_before ("&");
2460 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2461 ASSERT_STREQ ("\n"
2462 " foo = bar.field;\n"
2463 " ^\n"
2464 " &\n",
2465 pp_formatted_text (dc.printer));
2468 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2470 static void
2471 test_one_liner_fixit_insert_after ()
2473 test_diagnostic_context dc;
2474 location_t start = linemap_position_for_column (line_table, 1);
2475 location_t finish = linemap_position_for_column (line_table, 3);
2476 location_t foo = make_location (start, start, finish);
2477 rich_location richloc (line_table, foo);
2478 richloc.add_fixit_insert_after ("[0]");
2479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2480 ASSERT_STREQ ("\n"
2481 " foo = bar.field;\n"
2482 " ^~~\n"
2483 " [0]\n",
2484 pp_formatted_text (dc.printer));
2487 /* Removal fix-it hint: removal of the ".field". */
2489 static void
2490 test_one_liner_fixit_remove ()
2492 test_diagnostic_context dc;
2493 location_t start = linemap_position_for_column (line_table, 10);
2494 location_t finish = linemap_position_for_column (line_table, 15);
2495 location_t dot = make_location (start, start, finish);
2496 rich_location richloc (line_table, dot);
2497 richloc.add_fixit_remove ();
2498 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2499 ASSERT_STREQ ("\n"
2500 " foo = bar.field;\n"
2501 " ^~~~~~\n"
2502 " ------\n",
2503 pp_formatted_text (dc.printer));
2506 /* Replace fix-it hint: replacing "field" with "m_field". */
2508 static void
2509 test_one_liner_fixit_replace ()
2511 test_diagnostic_context dc;
2512 location_t start = linemap_position_for_column (line_table, 11);
2513 location_t finish = linemap_position_for_column (line_table, 15);
2514 location_t field = make_location (start, start, finish);
2515 rich_location richloc (line_table, field);
2516 richloc.add_fixit_replace ("m_field");
2517 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2518 ASSERT_STREQ ("\n"
2519 " foo = bar.field;\n"
2520 " ^~~~~\n"
2521 " m_field\n",
2522 pp_formatted_text (dc.printer));
2525 /* Replace fix-it hint: replacing "field" with "m_field",
2526 but where the caret was elsewhere. */
2528 static void
2529 test_one_liner_fixit_replace_non_equal_range ()
2531 test_diagnostic_context dc;
2532 location_t equals = linemap_position_for_column (line_table, 5);
2533 location_t start = linemap_position_for_column (line_table, 11);
2534 location_t finish = linemap_position_for_column (line_table, 15);
2535 rich_location richloc (line_table, equals);
2536 source_range range;
2537 range.m_start = start;
2538 range.m_finish = finish;
2539 richloc.add_fixit_replace (range, "m_field");
2540 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2541 /* The replacement range is not indicated in the annotation line, so
2542 it should be indicated via an additional underline. */
2543 ASSERT_STREQ ("\n"
2544 " foo = bar.field;\n"
2545 " ^\n"
2546 " -----\n"
2547 " m_field\n",
2548 pp_formatted_text (dc.printer));
2551 /* Replace fix-it hint: replacing "field" with "m_field",
2552 where the caret was elsewhere, but where a secondary range
2553 exactly covers "field". */
2555 static void
2556 test_one_liner_fixit_replace_equal_secondary_range ()
2558 test_diagnostic_context dc;
2559 location_t equals = linemap_position_for_column (line_table, 5);
2560 location_t start = linemap_position_for_column (line_table, 11);
2561 location_t finish = linemap_position_for_column (line_table, 15);
2562 rich_location richloc (line_table, equals);
2563 location_t field = make_location (start, start, finish);
2564 richloc.add_range (field);
2565 richloc.add_fixit_replace (field, "m_field");
2566 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2567 /* The replacement range is indicated in the annotation line,
2568 so it shouldn't be indicated via an additional underline. */
2569 ASSERT_STREQ ("\n"
2570 " foo = bar.field;\n"
2571 " ^ ~~~~~\n"
2572 " m_field\n",
2573 pp_formatted_text (dc.printer));
2576 /* Verify that we can use ad-hoc locations when adding fixits to a
2577 rich_location. */
2579 static void
2580 test_one_liner_fixit_validation_adhoc_locations ()
2582 /* Generate a range that's too long to be packed, so must
2583 be stored as an ad-hoc location (given the defaults
2584 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2585 const location_t c7 = linemap_position_for_column (line_table, 7);
2586 const location_t c47 = linemap_position_for_column (line_table, 47);
2587 const location_t loc = make_location (c7, c7, c47);
2589 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2590 return;
2592 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2594 /* Insert. */
2596 rich_location richloc (line_table, loc);
2597 richloc.add_fixit_insert_before (loc, "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));
2610 /* Remove. */
2612 rich_location richloc (line_table, loc);
2613 source_range range = source_range::from_locations (loc, c47);
2614 richloc.add_fixit_remove (range);
2615 /* It should not have been discarded by the validator. */
2616 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2618 test_diagnostic_context dc;
2619 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2620 ASSERT_STREQ ("\n"
2621 " foo = bar.field;\n"
2622 " ^~~~~~~~~~ \n"
2623 " -----------------------------------------\n",
2624 pp_formatted_text (dc.printer));
2627 /* Replace. */
2629 rich_location richloc (line_table, loc);
2630 source_range range = source_range::from_locations (loc, c47);
2631 richloc.add_fixit_replace (range, "test");
2632 /* It should not have been discarded by the validator. */
2633 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2635 test_diagnostic_context dc;
2636 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2637 ASSERT_STREQ ("\n"
2638 " foo = bar.field;\n"
2639 " ^~~~~~~~~~ \n"
2640 " test\n",
2641 pp_formatted_text (dc.printer));
2645 /* Test of consolidating insertions at the same location. */
2647 static void
2648 test_one_liner_many_fixits_1 ()
2650 test_diagnostic_context dc;
2651 location_t equals = linemap_position_for_column (line_table, 5);
2652 rich_location richloc (line_table, equals);
2653 for (int i = 0; i < 19; i++)
2654 richloc.add_fixit_insert_before ("a");
2655 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2656 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2657 ASSERT_STREQ ("\n"
2658 " foo = bar.field;\n"
2659 " ^\n"
2660 " aaaaaaaaaaaaaaaaaaa\n",
2661 pp_formatted_text (dc.printer));
2664 /* Ensure that we can add an arbitrary number of fix-it hints to a
2665 rich_location, even if they are not consolidated. */
2667 static void
2668 test_one_liner_many_fixits_2 ()
2670 test_diagnostic_context dc;
2671 location_t equals = linemap_position_for_column (line_table, 5);
2672 rich_location richloc (line_table, equals);
2673 for (int i = 0; i < 19; i++)
2675 location_t loc = linemap_position_for_column (line_table, i * 2);
2676 richloc.add_fixit_insert_before (loc, "a");
2678 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2679 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2680 ASSERT_STREQ ("\n"
2681 " foo = bar.field;\n"
2682 " ^\n"
2683 "a a a a a a a a a a a a a a a a a a a\n",
2684 pp_formatted_text (dc.printer));
2687 /* Test of labeling the ranges within a rich_location. */
2689 static void
2690 test_one_liner_labels ()
2692 location_t foo
2693 = make_location (linemap_position_for_column (line_table, 1),
2694 linemap_position_for_column (line_table, 1),
2695 linemap_position_for_column (line_table, 3));
2696 location_t bar
2697 = make_location (linemap_position_for_column (line_table, 7),
2698 linemap_position_for_column (line_table, 7),
2699 linemap_position_for_column (line_table, 9));
2700 location_t field
2701 = make_location (linemap_position_for_column (line_table, 11),
2702 linemap_position_for_column (line_table, 11),
2703 linemap_position_for_column (line_table, 15));
2705 /* Example where all the labels fit on one line. */
2707 text_range_label label0 ("0");
2708 text_range_label label1 ("1");
2709 text_range_label label2 ("2");
2710 gcc_rich_location richloc (foo, &label0);
2711 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2712 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2715 test_diagnostic_context dc;
2716 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2717 ASSERT_STREQ ("\n"
2718 " foo = bar.field;\n"
2719 " ^~~ ~~~ ~~~~~\n"
2720 " | | |\n"
2721 " 0 1 2\n",
2722 pp_formatted_text (dc.printer));
2725 /* Verify that we can disable label-printing. */
2727 test_diagnostic_context dc;
2728 dc.show_labels_p = false;
2729 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2730 ASSERT_STREQ ("\n"
2731 " foo = bar.field;\n"
2732 " ^~~ ~~~ ~~~~~\n",
2733 pp_formatted_text (dc.printer));
2737 /* Example where the labels need extra lines. */
2739 text_range_label label0 ("label 0");
2740 text_range_label label1 ("label 1");
2741 text_range_label label2 ("label 2");
2742 gcc_rich_location richloc (foo, &label0);
2743 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2744 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2746 test_diagnostic_context dc;
2747 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2748 ASSERT_STREQ ("\n"
2749 " foo = bar.field;\n"
2750 " ^~~ ~~~ ~~~~~\n"
2751 " | | |\n"
2752 " | | label 2\n"
2753 " | label 1\n"
2754 " label 0\n",
2755 pp_formatted_text (dc.printer));
2758 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2759 but label 1 just touches label 2. */
2761 text_range_label label0 ("aaaaa");
2762 text_range_label label1 ("bbbb");
2763 text_range_label label2 ("c");
2764 gcc_rich_location richloc (foo, &label0);
2765 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2766 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2768 test_diagnostic_context dc;
2769 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2770 ASSERT_STREQ ("\n"
2771 " foo = bar.field;\n"
2772 " ^~~ ~~~ ~~~~~\n"
2773 " | | |\n"
2774 " | | c\n"
2775 " aaaaa bbbb\n",
2776 pp_formatted_text (dc.printer));
2779 /* Example of out-of-order ranges (thus requiring a sort). */
2781 text_range_label label0 ("0");
2782 text_range_label label1 ("1");
2783 text_range_label label2 ("2");
2784 gcc_rich_location richloc (field, &label0);
2785 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2786 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2788 test_diagnostic_context dc;
2789 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2790 ASSERT_STREQ ("\n"
2791 " foo = bar.field;\n"
2792 " ~~~ ~~~ ^~~~~\n"
2793 " | | |\n"
2794 " 2 1 0\n",
2795 pp_formatted_text (dc.printer));
2798 /* Ensure we don't ICE if multiple ranges with labels are on
2799 the same point. */
2801 text_range_label label0 ("label 0");
2802 text_range_label label1 ("label 1");
2803 text_range_label label2 ("label 2");
2804 gcc_rich_location richloc (bar, &label0);
2805 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2806 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2808 test_diagnostic_context dc;
2809 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2810 ASSERT_STREQ ("\n"
2811 " foo = bar.field;\n"
2812 " ^~~\n"
2813 " |\n"
2814 " label 2\n"
2815 " label 1\n"
2816 " label 0\n",
2817 pp_formatted_text (dc.printer));
2820 /* Verify that a NULL result from range_label::get_text is
2821 handled gracefully. */
2823 text_range_label label (NULL);
2824 gcc_rich_location richloc (bar, &label);
2826 test_diagnostic_context dc;
2827 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2828 ASSERT_STREQ ("\n"
2829 " foo = bar.field;\n"
2830 " ^~~\n",
2831 pp_formatted_text (dc.printer));
2834 /* TODO: example of formatted printing (needs to be in
2835 gcc-rich-location.c due to Makefile.in issues). */
2838 /* Run the various one-liner tests. */
2840 static void
2841 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2843 /* Create a tempfile and write some text to it.
2844 ....................0000000001111111.
2845 ....................1234567890123456. */
2846 const char *content = "foo = bar.field;\n";
2847 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2848 line_table_test ltt (case_);
2850 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2852 location_t line_end = linemap_position_for_column (line_table, 16);
2854 /* Don't attempt to run the tests if column data might be unavailable. */
2855 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2856 return;
2858 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2859 ASSERT_EQ (1, LOCATION_LINE (line_end));
2860 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2862 test_one_liner_simple_caret ();
2863 test_one_liner_caret_and_range ();
2864 test_one_liner_multiple_carets_and_ranges ();
2865 test_one_liner_fixit_insert_before ();
2866 test_one_liner_fixit_insert_after ();
2867 test_one_liner_fixit_remove ();
2868 test_one_liner_fixit_replace ();
2869 test_one_liner_fixit_replace_non_equal_range ();
2870 test_one_liner_fixit_replace_equal_secondary_range ();
2871 test_one_liner_fixit_validation_adhoc_locations ();
2872 test_one_liner_many_fixits_1 ();
2873 test_one_liner_many_fixits_2 ();
2874 test_one_liner_labels ();
2877 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2879 static void
2880 test_add_location_if_nearby (const line_table_case &case_)
2882 /* Create a tempfile and write some text to it.
2883 ...000000000111111111122222222223333333333.
2884 ...123456789012345678901234567890123456789. */
2885 const char *content
2886 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2887 "struct different_line\n" /* line 2. */
2888 "{\n" /* line 3. */
2889 " double x;\n" /* line 4. */
2890 " double y;\n" /* line 5. */
2891 ";\n"); /* line 6. */
2892 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2893 line_table_test ltt (case_);
2895 const line_map_ordinary *ord_map
2896 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2897 tmp.get_filename (), 0));
2899 linemap_line_start (line_table, 1, 100);
2901 const location_t final_line_end
2902 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2904 /* Don't attempt to run the tests if column data might be unavailable. */
2905 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2906 return;
2908 /* Test of add_location_if_nearby on the same line as the
2909 primary location. */
2911 const location_t missing_close_brace_1_39
2912 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2913 const location_t matching_open_brace_1_18
2914 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2915 gcc_rich_location richloc (missing_close_brace_1_39);
2916 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2917 ASSERT_TRUE (added);
2918 ASSERT_EQ (2, richloc.get_num_locations ());
2919 test_diagnostic_context dc;
2920 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2921 ASSERT_STREQ ("\n"
2922 " struct same_line { double x; double y; ;\n"
2923 " ~ ^\n",
2924 pp_formatted_text (dc.printer));
2927 /* Test of add_location_if_nearby on a different line to the
2928 primary location. */
2930 const location_t missing_close_brace_6_1
2931 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2932 const location_t matching_open_brace_3_1
2933 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2934 gcc_rich_location richloc (missing_close_brace_6_1);
2935 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2936 ASSERT_FALSE (added);
2937 ASSERT_EQ (1, richloc.get_num_locations ());
2941 /* Verify that we print fixits even if they only affect lines
2942 outside those covered by the ranges in the rich_location. */
2944 static void
2945 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2947 /* Create a tempfile and write some text to it.
2948 ...000000000111111111122222222223333333333.
2949 ...123456789012345678901234567890123456789. */
2950 const char *content
2951 = ("struct point { double x; double y; };\n" /* line 1. */
2952 "struct point origin = {x: 0.0,\n" /* line 2. */
2953 " y\n" /* line 3. */
2954 "\n" /* line 4. */
2955 "\n" /* line 5. */
2956 " : 0.0};\n"); /* line 6. */
2957 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2958 line_table_test ltt (case_);
2960 const line_map_ordinary *ord_map
2961 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2962 tmp.get_filename (), 0));
2964 linemap_line_start (line_table, 1, 100);
2966 const location_t final_line_end
2967 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2969 /* Don't attempt to run the tests if column data might be unavailable. */
2970 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2971 return;
2973 /* A pair of tests for modernizing the initializers to C99-style. */
2975 /* The one-liner case (line 2). */
2977 test_diagnostic_context dc;
2978 const location_t x
2979 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2980 const location_t colon
2981 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2982 rich_location richloc (line_table, colon);
2983 richloc.add_fixit_insert_before (x, ".");
2984 richloc.add_fixit_replace (colon, "=");
2985 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2986 ASSERT_STREQ ("\n"
2987 " struct point origin = {x: 0.0,\n"
2988 " ^\n"
2989 " .=\n",
2990 pp_formatted_text (dc.printer));
2993 /* The multiline case. The caret for the rich_location is on line 6;
2994 verify that insertion fixit on line 3 is still printed (and that
2995 span starts are printed due to the gap between the span at line 3
2996 and that at line 6). */
2998 test_diagnostic_context dc;
2999 const location_t y
3000 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3001 const location_t colon
3002 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3003 rich_location richloc (line_table, colon);
3004 richloc.add_fixit_insert_before (y, ".");
3005 richloc.add_fixit_replace (colon, "=");
3006 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3007 ASSERT_STREQ ("\n"
3008 "FILENAME:3:24:\n"
3009 " y\n"
3010 " .\n"
3011 "FILENAME:6:25:\n"
3012 " : 0.0};\n"
3013 " ^\n"
3014 " =\n",
3015 pp_formatted_text (dc.printer));
3018 /* As above, but verify the behavior of multiple line spans
3019 with line-numbering enabled. */
3021 const location_t y
3022 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3023 const location_t colon
3024 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3025 rich_location richloc (line_table, colon);
3026 richloc.add_fixit_insert_before (y, ".");
3027 richloc.add_fixit_replace (colon, "=");
3028 test_diagnostic_context dc;
3029 dc.show_line_numbers_p = true;
3030 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3031 ASSERT_STREQ ("\n"
3032 " 3 | y\n"
3033 " | .\n"
3034 "....\n"
3035 " 6 | : 0.0};\n"
3036 " | ^\n"
3037 " | =\n",
3038 pp_formatted_text (dc.printer));
3043 /* Verify that fix-it hints are appropriately consolidated.
3045 If any fix-it hints in a rich_location involve locations beyond
3046 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3047 the fix-it as a whole, so there should be none.
3049 Otherwise, verify that consecutive "replace" and "remove" fix-its
3050 are merged, and that other fix-its remain separate. */
3052 static void
3053 test_fixit_consolidation (const line_table_case &case_)
3055 line_table_test ltt (case_);
3057 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3059 const location_t c10 = linemap_position_for_column (line_table, 10);
3060 const location_t c15 = linemap_position_for_column (line_table, 15);
3061 const location_t c16 = linemap_position_for_column (line_table, 16);
3062 const location_t c17 = linemap_position_for_column (line_table, 17);
3063 const location_t c20 = linemap_position_for_column (line_table, 20);
3064 const location_t c21 = linemap_position_for_column (line_table, 21);
3065 const location_t caret = c10;
3067 /* Insert + insert. */
3069 rich_location richloc (line_table, caret);
3070 richloc.add_fixit_insert_before (c10, "foo");
3071 richloc.add_fixit_insert_before (c15, "bar");
3073 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3074 /* Bogus column info for 2nd fixit, so no fixits. */
3075 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3076 else
3077 /* They should not have been merged. */
3078 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3081 /* Insert + replace. */
3083 rich_location richloc (line_table, caret);
3084 richloc.add_fixit_insert_before (c10, "foo");
3085 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3086 "bar");
3088 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3089 /* Bogus column info for 2nd fixit, so no fixits. */
3090 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3091 else
3092 /* They should not have been merged. */
3093 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3096 /* Replace + non-consecutive insert. */
3098 rich_location richloc (line_table, caret);
3099 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3100 "bar");
3101 richloc.add_fixit_insert_before (c17, "foo");
3103 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3104 /* Bogus column info for 2nd fixit, so no fixits. */
3105 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3106 else
3107 /* They should not have been merged. */
3108 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3111 /* Replace + non-consecutive replace. */
3113 rich_location richloc (line_table, caret);
3114 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3115 "foo");
3116 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3117 "bar");
3119 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3120 /* Bogus column info for 2nd fixit, so no fixits. */
3121 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3122 else
3123 /* They should not have been merged. */
3124 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3127 /* Replace + consecutive replace. */
3129 rich_location richloc (line_table, caret);
3130 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3131 "foo");
3132 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3133 "bar");
3135 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3136 /* Bogus column info for 2nd fixit, so no fixits. */
3137 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3138 else
3140 /* They should have been merged into a single "replace". */
3141 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3142 const fixit_hint *hint = richloc.get_fixit_hint (0);
3143 ASSERT_STREQ ("foobar", hint->get_string ());
3144 ASSERT_EQ (c10, hint->get_start_loc ());
3145 ASSERT_EQ (c21, hint->get_next_loc ());
3149 /* Replace + consecutive removal. */
3151 rich_location richloc (line_table, caret);
3152 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3153 "foo");
3154 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3156 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3157 /* Bogus column info for 2nd fixit, so no fixits. */
3158 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3159 else
3161 /* They should have been merged into a single replace, with the
3162 range extended to cover that of the removal. */
3163 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3164 const fixit_hint *hint = richloc.get_fixit_hint (0);
3165 ASSERT_STREQ ("foo", hint->get_string ());
3166 ASSERT_EQ (c10, hint->get_start_loc ());
3167 ASSERT_EQ (c21, hint->get_next_loc ());
3171 /* Consecutive removals. */
3173 rich_location richloc (line_table, caret);
3174 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3175 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3177 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3178 /* Bogus column info for 2nd fixit, so no fixits. */
3179 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3180 else
3182 /* They should have been merged into a single "replace-with-empty". */
3183 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3184 const fixit_hint *hint = richloc.get_fixit_hint (0);
3185 ASSERT_STREQ ("", hint->get_string ());
3186 ASSERT_EQ (c10, hint->get_start_loc ());
3187 ASSERT_EQ (c21, hint->get_next_loc ());
3192 /* Verify that the line_corrections machinery correctly prints
3193 overlapping fixit-hints. */
3195 static void
3196 test_overlapped_fixit_printing (const line_table_case &case_)
3198 /* Create a tempfile and write some text to it.
3199 ...000000000111111111122222222223333333333.
3200 ...123456789012345678901234567890123456789. */
3201 const char *content
3202 = (" foo *f = (foo *)ptr->field;\n");
3203 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3204 line_table_test ltt (case_);
3206 const line_map_ordinary *ord_map
3207 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3208 tmp.get_filename (), 0));
3210 linemap_line_start (line_table, 1, 100);
3212 const location_t final_line_end
3213 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3215 /* Don't attempt to run the tests if column data might be unavailable. */
3216 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3217 return;
3219 /* A test for converting a C-style cast to a C++-style cast. */
3220 const location_t open_paren
3221 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3222 const location_t close_paren
3223 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3224 const location_t expr_start
3225 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3226 const location_t expr_finish
3227 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3228 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3230 /* Various examples of fix-it hints that aren't themselves consolidated,
3231 but for which the *printing* may need consolidation. */
3233 /* Example where 3 fix-it hints are printed as one. */
3235 test_diagnostic_context dc;
3236 rich_location richloc (line_table, expr);
3237 richloc.add_fixit_replace (open_paren, "const_cast<");
3238 richloc.add_fixit_replace (close_paren, "> (");
3239 richloc.add_fixit_insert_after (")");
3241 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3242 ASSERT_STREQ ("\n"
3243 " foo *f = (foo *)ptr->field;\n"
3244 " ^~~~~~~~~~\n"
3245 " -----------------\n"
3246 " const_cast<foo *> (ptr->field)\n",
3247 pp_formatted_text (dc.printer));
3249 /* Unit-test the line_corrections machinery. */
3250 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3251 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3252 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3253 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3254 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3255 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3256 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3257 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3258 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3259 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3261 /* Add each hint in turn to a line_corrections instance,
3262 and verify that they are consolidated into one correction instance
3263 as expected. */
3264 line_corrections lc (tmp.get_filename (), 1);
3266 /* The first replace hint by itself. */
3267 lc.add_hint (hint_0);
3268 ASSERT_EQ (1, lc.m_corrections.length ());
3269 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3270 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3271 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3273 /* After the second replacement hint, they are printed together
3274 as a replacement (along with the text between them). */
3275 lc.add_hint (hint_1);
3276 ASSERT_EQ (1, lc.m_corrections.length ());
3277 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3278 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3279 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3281 /* After the final insertion hint, they are all printed together
3282 as a replacement (along with the text between them). */
3283 lc.add_hint (hint_2);
3284 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3285 lc.m_corrections[0]->m_text);
3286 ASSERT_EQ (1, lc.m_corrections.length ());
3287 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3288 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3291 /* Example where two are consolidated during printing. */
3293 test_diagnostic_context dc;
3294 rich_location richloc (line_table, expr);
3295 richloc.add_fixit_replace (open_paren, "CAST (");
3296 richloc.add_fixit_replace (close_paren, ") (");
3297 richloc.add_fixit_insert_after (")");
3299 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3300 ASSERT_STREQ ("\n"
3301 " foo *f = (foo *)ptr->field;\n"
3302 " ^~~~~~~~~~\n"
3303 " -\n"
3304 " CAST (-\n"
3305 " ) ( )\n",
3306 pp_formatted_text (dc.printer));
3309 /* Example where none are consolidated during printing. */
3311 test_diagnostic_context dc;
3312 rich_location richloc (line_table, expr);
3313 richloc.add_fixit_replace (open_paren, "CST (");
3314 richloc.add_fixit_replace (close_paren, ") (");
3315 richloc.add_fixit_insert_after (")");
3317 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3318 ASSERT_STREQ ("\n"
3319 " foo *f = (foo *)ptr->field;\n"
3320 " ^~~~~~~~~~\n"
3321 " -\n"
3322 " CST ( -\n"
3323 " ) ( )\n",
3324 pp_formatted_text (dc.printer));
3327 /* Example of deletion fix-it hints. */
3329 test_diagnostic_context dc;
3330 rich_location richloc (line_table, expr);
3331 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3332 source_range victim = {open_paren, close_paren};
3333 richloc.add_fixit_remove (victim);
3335 /* This case is actually handled by fixit-consolidation,
3336 rather than by line_corrections. */
3337 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3339 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3340 ASSERT_STREQ ("\n"
3341 " foo *f = (foo *)ptr->field;\n"
3342 " ^~~~~~~~~~\n"
3343 " -------\n"
3344 " (bar *)\n",
3345 pp_formatted_text (dc.printer));
3348 /* Example of deletion fix-it hints that would overlap. */
3350 test_diagnostic_context dc;
3351 rich_location richloc (line_table, expr);
3352 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3353 source_range victim = {expr_start, expr_finish};
3354 richloc.add_fixit_remove (victim);
3356 /* These fixits are not consolidated. */
3357 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3359 /* But the corrections are. */
3360 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3361 ASSERT_STREQ ("\n"
3362 " foo *f = (foo *)ptr->field;\n"
3363 " ^~~~~~~~~~\n"
3364 " -----------------\n"
3365 " (longer *)(foo *)\n",
3366 pp_formatted_text (dc.printer));
3369 /* Example of insertion fix-it hints that would overlap. */
3371 test_diagnostic_context dc;
3372 rich_location richloc (line_table, expr);
3373 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3374 richloc.add_fixit_insert_after (close_paren, "TEST");
3376 /* The first insertion is long enough that if printed naively,
3377 it would overlap with the second.
3378 Verify that they are printed as a single replacement. */
3379 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3380 ASSERT_STREQ ("\n"
3381 " foo *f = (foo *)ptr->field;\n"
3382 " ^~~~~~~~~~\n"
3383 " -------\n"
3384 " LONGER THAN THE CAST(foo *)TEST\n",
3385 pp_formatted_text (dc.printer));
3389 /* Verify that the line_corrections machinery correctly prints
3390 overlapping fixit-hints that have been added in the wrong
3391 order.
3392 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3394 static void
3395 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3397 /* Create a tempfile and write some text to it.
3398 ...000000000111111111122222222223333333333.
3399 ...123456789012345678901234567890123456789. */
3400 const char *content
3401 = ("int a5[][0][0] = { 1, 2 };\n");
3402 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3403 line_table_test ltt (case_);
3405 const line_map_ordinary *ord_map
3406 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3407 tmp.get_filename (), 0));
3409 linemap_line_start (line_table, 1, 100);
3411 const location_t final_line_end
3412 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3414 /* Don't attempt to run the tests if column data might be unavailable. */
3415 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3416 return;
3418 const location_t col_1
3419 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3420 const location_t col_20
3421 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3422 const location_t col_21
3423 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3424 const location_t col_23
3425 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3426 const location_t col_25
3427 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3429 /* Two insertions, in the wrong order. */
3431 rich_location richloc (line_table, col_20);
3432 richloc.add_fixit_insert_before (col_23, "{");
3433 richloc.add_fixit_insert_before (col_21, "}");
3435 /* These fixits should be accepted; they can't be consolidated. */
3436 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3437 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3438 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3439 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3440 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3441 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3442 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3444 /* Verify that they're printed correctly. */
3445 test_diagnostic_context dc;
3446 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3447 ASSERT_STREQ ("\n"
3448 " int a5[][0][0] = { 1, 2 };\n"
3449 " ^\n"
3450 " } {\n",
3451 pp_formatted_text (dc.printer));
3454 /* Various overlapping insertions, some occurring "out of order"
3455 (reproducing the fix-it hints from PR c/81405). */
3457 test_diagnostic_context dc;
3458 rich_location richloc (line_table, col_20);
3460 richloc.add_fixit_insert_before (col_20, "{{");
3461 richloc.add_fixit_insert_before (col_21, "}}");
3462 richloc.add_fixit_insert_before (col_23, "{");
3463 richloc.add_fixit_insert_before (col_21, "}");
3464 richloc.add_fixit_insert_before (col_23, "{{");
3465 richloc.add_fixit_insert_before (col_25, "}");
3466 richloc.add_fixit_insert_before (col_21, "}");
3467 richloc.add_fixit_insert_before (col_1, "{");
3468 richloc.add_fixit_insert_before (col_25, "}");
3469 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3470 ASSERT_STREQ ("\n"
3471 " int a5[][0][0] = { 1, 2 };\n"
3472 " ^\n"
3473 " { -----\n"
3474 " {{1}}}}, {{{2 }}\n",
3475 pp_formatted_text (dc.printer));
3479 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3481 static void
3482 test_fixit_insert_containing_newline (const line_table_case &case_)
3484 /* Create a tempfile and write some text to it.
3485 .........................0000000001111111.
3486 .........................1234567890123456. */
3487 const char *old_content = (" case 'a':\n" /* line 1. */
3488 " x = a;\n" /* line 2. */
3489 " case 'b':\n" /* line 3. */
3490 " x = b;\n");/* line 4. */
3492 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3493 line_table_test ltt (case_);
3494 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3496 location_t case_start = linemap_position_for_column (line_table, 5);
3497 location_t case_finish = linemap_position_for_column (line_table, 13);
3498 location_t case_loc = make_location (case_start, case_start, case_finish);
3499 location_t line_start = linemap_position_for_column (line_table, 1);
3501 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3502 return;
3504 /* Add a "break;" on a line by itself before line 3 i.e. before
3505 column 1 of line 3. */
3507 rich_location richloc (line_table, case_loc);
3508 richloc.add_fixit_insert_before (line_start, " break;\n");
3510 /* Without line numbers. */
3512 test_diagnostic_context dc;
3513 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3514 ASSERT_STREQ ("\n"
3515 " x = a;\n"
3516 "+ break;\n"
3517 " case 'b':\n"
3518 " ^~~~~~~~~\n",
3519 pp_formatted_text (dc.printer));
3522 /* With line numbers. */
3524 test_diagnostic_context dc;
3525 dc.show_line_numbers_p = true;
3526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3527 ASSERT_STREQ ("\n"
3528 "2 | x = a;\n"
3529 "+ |+ break;\n"
3530 "3 | case 'b':\n"
3531 " | ^~~~~~~~~\n",
3532 pp_formatted_text (dc.printer));
3536 /* Verify that attempts to add text with a newline fail when the
3537 insertion point is *not* at the start of a line. */
3539 rich_location richloc (line_table, case_loc);
3540 richloc.add_fixit_insert_before (case_start, "break;\n");
3541 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3542 test_diagnostic_context dc;
3543 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3544 ASSERT_STREQ ("\n"
3545 " case 'b':\n"
3546 " ^~~~~~~~~\n",
3547 pp_formatted_text (dc.printer));
3551 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3552 of the file, where the fix-it is printed in a different line-span
3553 to the primary range of the diagnostic. */
3555 static void
3556 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3558 /* Create a tempfile and write some text to it.
3559 .........................0000000001111111.
3560 .........................1234567890123456. */
3561 const char *old_content = ("test (int ch)\n" /* line 1. */
3562 "{\n" /* line 2. */
3563 " putchar (ch);\n" /* line 3. */
3564 "}\n"); /* line 4. */
3566 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3567 line_table_test ltt (case_);
3569 const line_map_ordinary *ord_map = linemap_check_ordinary
3570 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3571 linemap_line_start (line_table, 1, 100);
3573 /* The primary range is the "putchar" token. */
3574 location_t putchar_start
3575 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3576 location_t putchar_finish
3577 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3578 location_t putchar_loc
3579 = make_location (putchar_start, putchar_start, putchar_finish);
3580 rich_location richloc (line_table, putchar_loc);
3582 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3583 location_t file_start
3584 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3585 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3587 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3588 return;
3591 test_diagnostic_context dc;
3592 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3593 ASSERT_STREQ ("\n"
3594 "FILENAME:1:1:\n"
3595 "+#include <stdio.h>\n"
3596 " test (int ch)\n"
3597 "FILENAME:3:2:\n"
3598 " putchar (ch);\n"
3599 " ^~~~~~~\n",
3600 pp_formatted_text (dc.printer));
3603 /* With line-numbering, the line spans are close enough to be
3604 consolidated, since it makes little sense to skip line 2. */
3606 test_diagnostic_context dc;
3607 dc.show_line_numbers_p = true;
3608 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3609 ASSERT_STREQ ("\n"
3610 "+ |+#include <stdio.h>\n"
3611 "1 | test (int ch)\n"
3612 "2 | {\n"
3613 "3 | putchar (ch);\n"
3614 " | ^~~~~~~\n",
3615 pp_formatted_text (dc.printer));
3619 /* Replacement fix-it hint containing a newline.
3620 This will fail, as newlines are only supported when inserting at the
3621 beginning of a line. */
3623 static void
3624 test_fixit_replace_containing_newline (const line_table_case &case_)
3626 /* Create a tempfile and write some text to it.
3627 .........................0000000001111.
3628 .........................1234567890123. */
3629 const char *old_content = "foo = bar ();\n";
3631 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3632 line_table_test ltt (case_);
3633 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3635 /* Replace the " = " with "\n = ", as if we were reformatting an
3636 overly long line. */
3637 location_t start = linemap_position_for_column (line_table, 4);
3638 location_t finish = linemap_position_for_column (line_table, 6);
3639 location_t loc = linemap_position_for_column (line_table, 13);
3640 rich_location richloc (line_table, loc);
3641 source_range range = source_range::from_locations (start, finish);
3642 richloc.add_fixit_replace (range, "\n =");
3644 /* Arbitrary newlines are not yet supported within fix-it hints, so
3645 the fix-it should not be displayed. */
3646 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3648 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3649 return;
3651 test_diagnostic_context dc;
3652 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3653 ASSERT_STREQ ("\n"
3654 " foo = bar ();\n"
3655 " ^\n",
3656 pp_formatted_text (dc.printer));
3659 /* Fix-it hint, attempting to delete a newline.
3660 This will fail, as we currently only support fix-it hints that
3661 affect one line at a time. */
3663 static void
3664 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3666 /* Create a tempfile and write some text to it.
3667 ..........................0000000001111.
3668 ..........................1234567890123. */
3669 const char *old_content = ("foo = bar (\n"
3670 " );\n");
3672 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3673 line_table_test ltt (case_);
3674 const line_map_ordinary *ord_map = linemap_check_ordinary
3675 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3676 linemap_line_start (line_table, 1, 100);
3678 /* Attempt to delete the " (\n...)". */
3679 location_t start
3680 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3681 location_t caret
3682 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3683 location_t finish
3684 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3685 location_t loc = make_location (caret, start, finish);
3686 rich_location richloc (line_table, loc);
3687 richloc. add_fixit_remove ();
3689 /* Fix-it hints that affect more than one line are not yet supported, so
3690 the fix-it should not be displayed. */
3691 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3693 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3694 return;
3696 test_diagnostic_context dc;
3697 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3698 ASSERT_STREQ ("\n"
3699 " foo = bar (\n"
3700 " ~^\n"
3701 " );\n"
3702 " ~ \n",
3703 pp_formatted_text (dc.printer));
3706 /* Verify that line numbers are correctly printed for the case of
3707 a multiline range in which the width of the line numbers changes
3708 (e.g. from "9" to "10"). */
3710 static void
3711 test_line_numbers_multiline_range ()
3713 /* Create a tempfile and write some text to it. */
3714 pretty_printer pp;
3715 for (int i = 0; i < 20; i++)
3716 /* .........0000000001111111.
3717 .............1234567890123456. */
3718 pp_printf (&pp, "this is line %i\n", i + 1);
3719 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3720 line_table_test ltt;
3722 const line_map_ordinary *ord_map = linemap_check_ordinary
3723 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3724 linemap_line_start (line_table, 1, 100);
3726 /* Create a multi-line location, starting at the "line" of line 9, with
3727 a caret on the "is" of line 10, finishing on the "this" line 11. */
3729 location_t start
3730 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3731 location_t caret
3732 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3733 location_t finish
3734 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3735 location_t loc = make_location (caret, start, finish);
3737 test_diagnostic_context dc;
3738 dc.show_line_numbers_p = true;
3739 gcc_rich_location richloc (loc);
3740 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3741 ASSERT_STREQ ("\n"
3742 " 9 | this is line 9\n"
3743 " | ~~~~~~\n"
3744 "10 | this is line 10\n"
3745 " | ~~~~~^~~~~~~~~~\n"
3746 "11 | this is line 11\n"
3747 " | ~~~~ \n",
3748 pp_formatted_text (dc.printer));
3751 /* Run all of the selftests within this file. */
3753 void
3754 diagnostic_show_locus_c_tests ()
3756 test_line_span ();
3757 test_num_digits ();
3759 test_layout_range_for_single_point ();
3760 test_layout_range_for_single_line ();
3761 test_layout_range_for_multiple_lines ();
3763 test_get_line_width_without_trailing_whitespace ();
3765 test_diagnostic_show_locus_unknown_location ();
3767 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3768 for_each_line_table_case (test_add_location_if_nearby);
3769 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3770 for_each_line_table_case (test_fixit_consolidation);
3771 for_each_line_table_case (test_overlapped_fixit_printing);
3772 for_each_line_table_case (test_overlapped_fixit_printing_2);
3773 for_each_line_table_case (test_fixit_insert_containing_newline);
3774 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3775 for_each_line_table_case (test_fixit_replace_containing_newline);
3776 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3778 test_line_numbers_multiline_range ();
3781 } // namespace selftest
3783 #endif /* #if CHECKING_P */