gcc:
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob6ce8a0f4a9b7e1bc949c95797dc10e3be19a69e9
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 const range_label *label);
133 bool contains_point (linenum_type row, int column) const;
134 bool intersects_line_p (linenum_type row) const;
136 layout_point m_start;
137 layout_point m_finish;
138 enum range_display_kind m_range_display_kind;
139 layout_point m_caret;
140 const range_label *m_label;
143 /* A struct for use by layout::print_source_line for telling
144 layout::print_annotation_line the extents of the source line that
145 it printed, so that underlines can be clipped appropriately. */
147 struct line_bounds
149 int m_first_non_ws;
150 int m_last_non_ws;
153 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
154 or "line 23"). During the layout ctor, layout::calculate_line_spans
155 splits the pertinent source lines into a list of disjoint line_span
156 instances (e.g. lines 5-10, lines 15-20, line 23). */
158 struct line_span
160 line_span (linenum_type first_line, linenum_type last_line)
161 : m_first_line (first_line), m_last_line (last_line)
163 gcc_assert (first_line <= last_line);
165 linenum_type get_first_line () const { return m_first_line; }
166 linenum_type get_last_line () const { return m_last_line; }
168 bool contains_line_p (linenum_type line) const
170 return line >= m_first_line && line <= m_last_line;
173 static int comparator (const void *p1, const void *p2)
175 const line_span *ls1 = (const line_span *)p1;
176 const line_span *ls2 = (const line_span *)p2;
177 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
178 if (first_line_cmp)
179 return first_line_cmp;
180 return compare (ls1->m_last_line, ls2->m_last_line);
183 linenum_type m_first_line;
184 linenum_type m_last_line;
187 #if CHECKING_P
189 /* Selftests for line_span. */
191 static void
192 test_line_span ()
194 line_span line_one (1, 1);
195 ASSERT_EQ (1, line_one.get_first_line ());
196 ASSERT_EQ (1, line_one.get_last_line ());
197 ASSERT_FALSE (line_one.contains_line_p (0));
198 ASSERT_TRUE (line_one.contains_line_p (1));
199 ASSERT_FALSE (line_one.contains_line_p (2));
201 line_span lines_1_to_3 (1, 3);
202 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
203 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
204 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
205 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
207 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
208 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
209 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
211 /* A linenum > 2^31. */
212 const linenum_type LARGEST_LINE = 0xffffffff;
213 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
214 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
215 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
217 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
218 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
221 #endif /* #if CHECKING_P */
223 /* A class to control the overall layout when printing a diagnostic.
225 The layout is determined within the constructor.
226 It is then printed by repeatedly calling the "print_source_line",
227 "print_annotation_line" and "print_any_fixits" methods.
229 We assume we have disjoint ranges. */
231 class layout
233 public:
234 layout (diagnostic_context *context,
235 rich_location *richloc,
236 diagnostic_t diagnostic_kind);
238 bool maybe_add_location_range (const location_range *loc_range,
239 bool restrict_to_current_line_spans);
241 int get_num_line_spans () const { return m_line_spans.length (); }
242 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
244 void print_gap_in_line_numbering ();
245 bool print_heading_for_line_span_index_p (int line_span_idx) const;
247 expanded_location get_expanded_location (const line_span *) const;
249 void print_line (linenum_type row);
251 private:
252 bool will_show_line_p (linenum_type row) const;
253 void print_leading_fixits (linenum_type row);
254 void print_source_line (linenum_type row, const char *line, int line_width,
255 line_bounds *lbounds_out);
256 bool should_print_annotation_line_p (linenum_type row) const;
257 void start_annotation_line (char margin_char = ' ') const;
258 void print_annotation_line (linenum_type row, const line_bounds lbounds);
259 void print_any_labels (linenum_type row);
260 void print_trailing_fixits (linenum_type row);
262 bool annotation_line_showed_range_p (linenum_type line, int start_column,
263 int finish_column) const;
264 void show_ruler (int max_column) const;
266 bool validate_fixit_hint_p (const fixit_hint *hint);
268 void calculate_line_spans ();
270 void print_newline ();
272 bool
273 get_state_at_point (/* Inputs. */
274 linenum_type row, int column,
275 int first_non_ws, int last_non_ws,
276 /* Outputs. */
277 point_state *out_state);
280 get_x_bound_for_row (linenum_type row, int caret_column,
281 int last_non_ws);
283 void
284 move_to_column (int *column, int dest_column, bool add_left_margin);
286 private:
287 diagnostic_context *m_context;
288 pretty_printer *m_pp;
289 diagnostic_t m_diagnostic_kind;
290 location_t m_primary_loc;
291 expanded_location m_exploc;
292 colorizer m_colorizer;
293 bool m_colorize_source_p;
294 bool m_show_labels_p;
295 bool m_show_line_numbers_p;
296 auto_vec <layout_range> m_layout_ranges;
297 auto_vec <const fixit_hint *> m_fixit_hints;
298 auto_vec <line_span> m_line_spans;
299 int m_linenum_width;
300 int m_x_offset;
303 /* Implementation of "class colorizer". */
305 /* The constructor for "colorizer". Lookup and store color codes for the
306 different kinds of things we might need to print. */
308 colorizer::colorizer (diagnostic_context *context,
309 diagnostic_t diagnostic_kind) :
310 m_context (context),
311 m_diagnostic_kind (diagnostic_kind),
312 m_current_state (STATE_NORMAL_TEXT)
314 m_range1 = get_color_by_name ("range1");
315 m_range2 = get_color_by_name ("range2");
316 m_fixit_insert = get_color_by_name ("fixit-insert");
317 m_fixit_delete = get_color_by_name ("fixit-delete");
318 m_stop_color = colorize_stop (pp_show_color (context->printer));
321 /* The destructor for "colorize". If colorization is on, print a code to
322 turn it off. */
324 colorizer::~colorizer ()
326 finish_state (m_current_state);
329 /* Update state, printing color codes if necessary if there's a state
330 change. */
332 void
333 colorizer::set_state (int new_state)
335 if (m_current_state != new_state)
337 finish_state (m_current_state);
338 m_current_state = new_state;
339 begin_state (new_state);
343 /* Turn on any colorization for STATE. */
345 void
346 colorizer::begin_state (int state)
348 switch (state)
350 case STATE_NORMAL_TEXT:
351 break;
353 case STATE_FIXIT_INSERT:
354 pp_string (m_context->printer, m_fixit_insert);
355 break;
357 case STATE_FIXIT_DELETE:
358 pp_string (m_context->printer, m_fixit_delete);
359 break;
361 case 0:
362 /* Make range 0 be the same color as the "kind" text
363 (error vs warning vs note). */
364 pp_string
365 (m_context->printer,
366 colorize_start (pp_show_color (m_context->printer),
367 diagnostic_get_color_for_kind (m_diagnostic_kind)));
368 break;
370 case 1:
371 pp_string (m_context->printer, m_range1);
372 break;
374 case 2:
375 pp_string (m_context->printer, m_range2);
376 break;
378 default:
379 /* For ranges beyond 2, alternate between color 1 and color 2. */
381 gcc_assert (state > 2);
382 pp_string (m_context->printer,
383 state % 2 ? m_range1 : m_range2);
385 break;
389 /* Turn off any colorization for STATE. */
391 void
392 colorizer::finish_state (int state)
394 if (state != STATE_NORMAL_TEXT)
395 pp_string (m_context->printer, m_stop_color);
398 /* Get the color code for NAME (or the empty string if
399 colorization is disabled). */
401 const char *
402 colorizer::get_color_by_name (const char *name)
404 return colorize_start (pp_show_color (m_context->printer), name);
407 /* Implementation of class layout_range. */
409 /* The constructor for class layout_range.
410 Initialize various layout_point fields from expanded_location
411 equivalents; we've already filtered on file. */
413 layout_range::layout_range (const expanded_location *start_exploc,
414 const expanded_location *finish_exploc,
415 enum range_display_kind range_display_kind,
416 const expanded_location *caret_exploc,
417 const range_label *label)
418 : m_start (*start_exploc),
419 m_finish (*finish_exploc),
420 m_range_display_kind (range_display_kind),
421 m_caret (*caret_exploc),
422 m_label (label)
426 /* Is (column, row) within the given range?
427 We've already filtered on the file.
429 Ranges are closed (both limits are within the range).
431 Example A: a single-line range:
432 start: (col=22, line=2)
433 finish: (col=38, line=2)
435 |00000011111111112222222222333333333344444444444
436 |34567890123456789012345678901234567890123456789
437 --+-----------------------------------------------
438 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
439 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
440 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
442 Example B: a multiline range with
443 start: (col=14, line=3)
444 finish: (col=08, line=5)
446 |00000011111111112222222222333333333344444444444
447 |34567890123456789012345678901234567890123456789
448 --+-----------------------------------------------
449 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
450 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
451 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
452 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
453 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
454 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
455 --+-----------------------------------------------
457 Legend:
458 - 'b' indicates a point *before* the range
459 - 'S' indicates the start of the range
460 - 'w' indicates a point within the range
461 - 'F' indicates the finish of the range (which is
462 within it).
463 - 'a' indicates a subsequent point *after* the range. */
465 bool
466 layout_range::contains_point (linenum_type row, int column) const
468 gcc_assert (m_start.m_line <= m_finish.m_line);
469 /* ...but the equivalent isn't true for the columns;
470 consider example B in the comment above. */
472 if (row < m_start.m_line)
473 /* Points before the first line of the range are
474 outside it (corresponding to line 01 in example A
475 and lines 01 and 02 in example B above). */
476 return false;
478 if (row == m_start.m_line)
479 /* On same line as start of range (corresponding
480 to line 02 in example A and line 03 in example B). */
482 if (column < m_start.m_column)
483 /* Points on the starting line of the range, but
484 before the column in which it begins. */
485 return false;
487 if (row < m_finish.m_line)
488 /* This is a multiline range; the point
489 is within it (corresponds to line 03 in example B
490 from column 14 onwards) */
491 return true;
492 else
494 /* This is a single-line range. */
495 gcc_assert (row == m_finish.m_line);
496 return column <= m_finish.m_column;
500 /* The point is in a line beyond that containing the
501 start of the range: lines 03 onwards in example A,
502 and lines 04 onwards in example B. */
503 gcc_assert (row > m_start.m_line);
505 if (row > m_finish.m_line)
506 /* The point is beyond the final line of the range
507 (lines 03 onwards in example A, and lines 06 onwards
508 in example B). */
509 return false;
511 if (row < m_finish.m_line)
513 /* The point is in a line that's fully within a multiline
514 range (e.g. line 04 in example B). */
515 gcc_assert (m_start.m_line < m_finish.m_line);
516 return true;
519 gcc_assert (row == m_finish.m_line);
521 return column <= m_finish.m_column;
524 /* Does this layout_range contain any part of line ROW? */
526 bool
527 layout_range::intersects_line_p (linenum_type row) const
529 gcc_assert (m_start.m_line <= m_finish.m_line);
530 if (row < m_start.m_line)
531 return false;
532 if (row > m_finish.m_line)
533 return false;
534 return true;
537 #if CHECKING_P
539 /* A helper function for testing layout_range. */
541 static layout_range
542 make_range (int start_line, int start_col, int end_line, int end_col)
544 const expanded_location start_exploc
545 = {"test.c", start_line, start_col, NULL, false};
546 const expanded_location finish_exploc
547 = {"test.c", end_line, end_col, NULL, false};
548 return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
549 &start_exploc, NULL);
552 /* Selftests for layout_range::contains_point and
553 layout_range::intersects_line_p. */
555 /* Selftest for layout_range, where the layout_range
556 is a range with start==end i.e. a single point. */
558 static void
559 test_layout_range_for_single_point ()
561 layout_range point = make_range (7, 10, 7, 10);
563 /* Tests for layout_range::contains_point. */
565 /* Before the line. */
566 ASSERT_FALSE (point.contains_point (6, 1));
568 /* On the line, but before start. */
569 ASSERT_FALSE (point.contains_point (7, 9));
571 /* At the point. */
572 ASSERT_TRUE (point.contains_point (7, 10));
574 /* On the line, after the point. */
575 ASSERT_FALSE (point.contains_point (7, 11));
577 /* After the line. */
578 ASSERT_FALSE (point.contains_point (8, 1));
580 /* Tests for layout_range::intersects_line_p. */
581 ASSERT_FALSE (point.intersects_line_p (6));
582 ASSERT_TRUE (point.intersects_line_p (7));
583 ASSERT_FALSE (point.intersects_line_p (8));
586 /* Selftest for layout_range, where the layout_range
587 is the single-line range shown as "Example A" above. */
589 static void
590 test_layout_range_for_single_line ()
592 layout_range example_a = make_range (2, 22, 2, 38);
594 /* Tests for layout_range::contains_point. */
596 /* Before the line. */
597 ASSERT_FALSE (example_a.contains_point (1, 1));
599 /* On the line, but before start. */
600 ASSERT_FALSE (example_a.contains_point (2, 21));
602 /* On the line, at the start. */
603 ASSERT_TRUE (example_a.contains_point (2, 22));
605 /* On the line, within the range. */
606 ASSERT_TRUE (example_a.contains_point (2, 23));
608 /* On the line, at the end. */
609 ASSERT_TRUE (example_a.contains_point (2, 38));
611 /* On the line, after the end. */
612 ASSERT_FALSE (example_a.contains_point (2, 39));
614 /* After the line. */
615 ASSERT_FALSE (example_a.contains_point (2, 39));
617 /* Tests for layout_range::intersects_line_p. */
618 ASSERT_FALSE (example_a.intersects_line_p (1));
619 ASSERT_TRUE (example_a.intersects_line_p (2));
620 ASSERT_FALSE (example_a.intersects_line_p (3));
623 /* Selftest for layout_range, where the layout_range
624 is the multi-line range shown as "Example B" above. */
626 static void
627 test_layout_range_for_multiple_lines ()
629 layout_range example_b = make_range (3, 14, 5, 8);
631 /* Tests for layout_range::contains_point. */
633 /* Before first line. */
634 ASSERT_FALSE (example_b.contains_point (1, 1));
636 /* On the first line, but before start. */
637 ASSERT_FALSE (example_b.contains_point (3, 13));
639 /* At the start. */
640 ASSERT_TRUE (example_b.contains_point (3, 14));
642 /* On the first line, within the range. */
643 ASSERT_TRUE (example_b.contains_point (3, 15));
645 /* On an interior line.
646 The column number should not matter; try various boundary
647 values. */
648 ASSERT_TRUE (example_b.contains_point (4, 1));
649 ASSERT_TRUE (example_b.contains_point (4, 7));
650 ASSERT_TRUE (example_b.contains_point (4, 8));
651 ASSERT_TRUE (example_b.contains_point (4, 9));
652 ASSERT_TRUE (example_b.contains_point (4, 13));
653 ASSERT_TRUE (example_b.contains_point (4, 14));
654 ASSERT_TRUE (example_b.contains_point (4, 15));
656 /* On the final line, before the end. */
657 ASSERT_TRUE (example_b.contains_point (5, 7));
659 /* On the final line, at the end. */
660 ASSERT_TRUE (example_b.contains_point (5, 8));
662 /* On the final line, after the end. */
663 ASSERT_FALSE (example_b.contains_point (5, 9));
665 /* After the line. */
666 ASSERT_FALSE (example_b.contains_point (6, 1));
668 /* Tests for layout_range::intersects_line_p. */
669 ASSERT_FALSE (example_b.intersects_line_p (2));
670 ASSERT_TRUE (example_b.intersects_line_p (3));
671 ASSERT_TRUE (example_b.intersects_line_p (4));
672 ASSERT_TRUE (example_b.intersects_line_p (5));
673 ASSERT_FALSE (example_b.intersects_line_p (6));
676 #endif /* #if CHECKING_P */
678 /* Given a source line LINE of length LINE_WIDTH, determine the width
679 without any trailing whitespace. */
681 static int
682 get_line_width_without_trailing_whitespace (const char *line, int line_width)
684 int result = line_width;
685 while (result > 0)
687 char ch = line[result - 1];
688 if (ch == ' ' || ch == '\t' || ch == '\r')
689 result--;
690 else
691 break;
693 gcc_assert (result >= 0);
694 gcc_assert (result <= line_width);
695 gcc_assert (result == 0 ||
696 (line[result - 1] != ' '
697 && line[result -1] != '\t'
698 && line[result -1] != '\r'));
699 return result;
702 #if CHECKING_P
704 /* A helper function for testing get_line_width_without_trailing_whitespace. */
706 static void
707 assert_eq (const char *line, int expected_width)
709 int actual_value
710 = get_line_width_without_trailing_whitespace (line, strlen (line));
711 ASSERT_EQ (actual_value, expected_width);
714 /* Verify that get_line_width_without_trailing_whitespace is sane for
715 various inputs. It is not required to handle newlines. */
717 static void
718 test_get_line_width_without_trailing_whitespace ()
720 assert_eq ("", 0);
721 assert_eq (" ", 0);
722 assert_eq ("\t", 0);
723 assert_eq ("\r", 0);
724 assert_eq ("hello world", 11);
725 assert_eq ("hello world ", 11);
726 assert_eq ("hello world \t\t ", 11);
727 assert_eq ("hello world\r", 11);
730 #endif /* #if CHECKING_P */
732 /* Helper function for layout's ctor, for sanitizing locations relative
733 to the primary location within a diagnostic.
735 Compare LOC_A and LOC_B to see if it makes sense to print underlines
736 connecting their expanded locations. Doing so is only guaranteed to
737 make sense if the locations share the same macro expansion "history"
738 i.e. they can be traced through the same macro expansions, eventually
739 reaching an ordinary map.
741 This may be too strong a condition, but it effectively sanitizes
742 PR c++/70105, which has an example of printing an expression where the
743 final location of the expression is in a different macro, which
744 erroneously was leading to hundreds of lines of irrelevant source
745 being printed. */
747 static bool
748 compatible_locations_p (location_t loc_a, location_t loc_b)
750 if (IS_ADHOC_LOC (loc_a))
751 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
752 if (IS_ADHOC_LOC (loc_b))
753 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
755 /* If either location is one of the special locations outside of a
756 linemap, they are only compatible if they are equal. */
757 if (loc_a < RESERVED_LOCATION_COUNT
758 || loc_b < RESERVED_LOCATION_COUNT)
759 return loc_a == loc_b;
761 const line_map *map_a = linemap_lookup (line_table, loc_a);
762 linemap_assert (map_a);
764 const line_map *map_b = linemap_lookup (line_table, loc_b);
765 linemap_assert (map_b);
767 /* Are they within the same map? */
768 if (map_a == map_b)
770 /* Are both within the same macro expansion? */
771 if (linemap_macro_expansion_map_p (map_a))
773 /* Expand each location towards the spelling location, and
774 recurse. */
775 const line_map_macro *macro_map = linemap_check_macro (map_a);
776 source_location loc_a_toward_spelling
777 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
778 macro_map,
779 loc_a);
780 source_location loc_b_toward_spelling
781 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
782 macro_map,
783 loc_b);
784 return compatible_locations_p (loc_a_toward_spelling,
785 loc_b_toward_spelling);
788 /* Otherwise they are within the same ordinary map. */
789 return true;
791 else
793 /* Within different maps. */
795 /* If either is within a macro expansion, they are incompatible. */
796 if (linemap_macro_expansion_map_p (map_a)
797 || linemap_macro_expansion_map_p (map_b))
798 return false;
800 /* Within two different ordinary maps; they are compatible iff they
801 are in the same file. */
802 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
803 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
804 return ord_map_a->to_file == ord_map_b->to_file;
808 /* Comparator for sorting fix-it hints. */
810 static int
811 fixit_cmp (const void *p_a, const void *p_b)
813 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
814 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
815 return hint_a->get_start_loc () - hint_b->get_start_loc ();
818 /* Get the number of digits in the decimal representation
819 of VALUE. */
821 static int
822 num_digits (int value)
824 /* Perhaps simpler to use log10 for this, but doing it this way avoids
825 using floating point. */
826 gcc_assert (value >= 0);
828 if (value == 0)
829 return 1;
831 int digits = 0;
832 while (value > 0)
834 digits++;
835 value /= 10;
837 return digits;
841 #if CHECKING_P
843 /* Selftest for num_digits. */
845 static void
846 test_num_digits ()
848 ASSERT_EQ (1, num_digits (0));
849 ASSERT_EQ (1, num_digits (9));
850 ASSERT_EQ (2, num_digits (10));
851 ASSERT_EQ (2, num_digits (99));
852 ASSERT_EQ (3, num_digits (100));
853 ASSERT_EQ (3, num_digits (999));
854 ASSERT_EQ (4, num_digits (1000));
855 ASSERT_EQ (4, num_digits (9999));
856 ASSERT_EQ (5, num_digits (10000));
857 ASSERT_EQ (5, num_digits (99999));
858 ASSERT_EQ (6, num_digits (100000));
859 ASSERT_EQ (6, num_digits (999999));
860 ASSERT_EQ (7, num_digits (1000000));
861 ASSERT_EQ (7, num_digits (9999999));
862 ASSERT_EQ (8, num_digits (10000000));
863 ASSERT_EQ (8, num_digits (99999999));
866 #endif /* #if CHECKING_P */
868 /* Implementation of class layout. */
870 /* Constructor for class layout.
872 Filter the ranges from the rich_location to those that we can
873 sanely print, populating m_layout_ranges and m_fixit_hints.
874 Determine the range of lines that we will print, splitting them
875 up into an ordered list of disjoint spans of contiguous line numbers.
876 Determine m_x_offset, to ensure that the primary caret
877 will fit within the max_width provided by the diagnostic_context. */
879 layout::layout (diagnostic_context * context,
880 rich_location *richloc,
881 diagnostic_t diagnostic_kind)
882 : m_context (context),
883 m_pp (context->printer),
884 m_diagnostic_kind (diagnostic_kind),
885 m_primary_loc (richloc->get_range (0)->m_loc),
886 m_exploc (richloc->get_expanded_location (0)),
887 m_colorizer (context, diagnostic_kind),
888 m_colorize_source_p (context->colorize_source_p),
889 m_show_labels_p (context->show_labels_p),
890 m_show_line_numbers_p (context->show_line_numbers_p),
891 m_layout_ranges (richloc->get_num_locations ()),
892 m_fixit_hints (richloc->get_num_fixit_hints ()),
893 m_line_spans (1 + richloc->get_num_locations ()),
894 m_linenum_width (0),
895 m_x_offset (0)
897 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
899 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
900 Ignore any ranges that are awkward to handle. */
901 const location_range *loc_range = richloc->get_range (idx);
902 maybe_add_location_range (loc_range, false);
905 /* Populate m_fixit_hints, filtering to only those that are in the
906 same file. */
907 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
909 const fixit_hint *hint = richloc->get_fixit_hint (i);
910 if (validate_fixit_hint_p (hint))
911 m_fixit_hints.safe_push (hint);
914 /* Sort m_fixit_hints. */
915 m_fixit_hints.qsort (fixit_cmp);
917 /* Populate m_line_spans. */
918 calculate_line_spans ();
920 /* Determine m_linenum_width. */
921 gcc_assert (m_line_spans.length () > 0);
922 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
923 int highest_line = last_span->m_last_line;
924 if (highest_line < 0)
925 highest_line = 0;
926 m_linenum_width = num_digits (highest_line);
927 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
928 if (m_line_spans.length () > 1)
929 m_linenum_width = MAX (m_linenum_width, 3);
931 /* Adjust m_x_offset.
932 Center the primary caret to fit in max_width; all columns
933 will be adjusted accordingly. */
934 size_t max_width = m_context->caret_max_width;
935 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
936 if (line && (size_t)m_exploc.column <= line.length ())
938 size_t right_margin = CARET_LINE_MARGIN;
939 size_t column = m_exploc.column;
940 if (m_show_line_numbers_p)
941 column += m_linenum_width + 2;
942 right_margin = MIN (line.length () - column, right_margin);
943 right_margin = max_width - right_margin;
944 if (line.length () >= max_width && column > right_margin)
945 m_x_offset = column - right_margin;
946 gcc_assert (m_x_offset >= 0);
949 if (context->show_ruler_p)
950 show_ruler (m_x_offset + max_width);
953 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
954 those that we can sanely print.
956 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
957 filtered against this layout instance's current line spans: it
958 will only be added if the location is fully within the lines
959 already specified by other locations.
961 Return true iff LOC_RANGE was added. */
963 bool
964 layout::maybe_add_location_range (const location_range *loc_range,
965 bool restrict_to_current_line_spans)
967 gcc_assert (loc_range);
969 /* Split the "range" into caret and range information. */
970 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
972 /* Expand the various locations. */
973 expanded_location start
974 = linemap_client_expand_location_to_spelling_point
975 (src_range.m_start, LOCATION_ASPECT_START);
976 expanded_location finish
977 = linemap_client_expand_location_to_spelling_point
978 (src_range.m_finish, LOCATION_ASPECT_FINISH);
979 expanded_location caret
980 = linemap_client_expand_location_to_spelling_point
981 (loc_range->m_loc, LOCATION_ASPECT_CARET);
983 /* If any part of the range isn't in the same file as the primary
984 location of this diagnostic, ignore the range. */
985 if (start.file != m_exploc.file)
986 return false;
987 if (finish.file != m_exploc.file)
988 return false;
989 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
990 if (caret.file != m_exploc.file)
991 return false;
993 /* Sanitize the caret location for non-primary ranges. */
994 if (m_layout_ranges.length () > 0)
995 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
996 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
997 /* Discard any non-primary ranges that can't be printed
998 sanely relative to the primary location. */
999 return false;
1001 /* Everything is now known to be in the correct source file,
1002 but it may require further sanitization. */
1003 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
1004 loc_range->m_label);
1006 /* If we have a range that finishes before it starts (perhaps
1007 from something built via macro expansion), printing the
1008 range is likely to be nonsensical. Also, attempting to do so
1009 breaks assumptions within the printing code (PR c/68473).
1010 Similarly, don't attempt to print ranges if one or both ends
1011 of the range aren't sane to print relative to the
1012 primary location (PR c++/70105). */
1013 if (start.line > finish.line
1014 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1015 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1017 /* Is this the primary location? */
1018 if (m_layout_ranges.length () == 0)
1020 /* We want to print the caret for the primary location, but
1021 we must sanitize away m_start and m_finish. */
1022 ri.m_start = ri.m_caret;
1023 ri.m_finish = ri.m_caret;
1025 else
1026 /* This is a non-primary range; ignore it. */
1027 return false;
1030 /* Potentially filter to just the lines already specified by other
1031 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1032 The layout ctor doesn't use it, and can't because m_line_spans
1033 hasn't been set up at that point. */
1034 if (restrict_to_current_line_spans)
1036 if (!will_show_line_p (start.line))
1037 return false;
1038 if (!will_show_line_p (finish.line))
1039 return false;
1040 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1041 if (!will_show_line_p (caret.line))
1042 return false;
1045 /* Passed all the tests; add the range to m_layout_ranges so that
1046 it will be printed. */
1047 m_layout_ranges.safe_push (ri);
1048 return true;
1051 /* Return true iff ROW is within one of the line spans for this layout. */
1053 bool
1054 layout::will_show_line_p (linenum_type row) const
1056 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1057 line_span_idx++)
1059 const line_span *line_span = get_line_span (line_span_idx);
1060 if (line_span->contains_line_p (row))
1061 return true;
1063 return false;
1066 /* Print a line showing a gap in the line numbers, for showing the boundary
1067 between two line spans. */
1069 void
1070 layout::print_gap_in_line_numbering ()
1072 gcc_assert (m_show_line_numbers_p);
1074 for (int i = 0; i < m_linenum_width + 1; i++)
1075 pp_character (m_pp, '.');
1077 pp_newline (m_pp);
1080 /* Return true iff we should print a heading when starting the
1081 line span with the given index. */
1083 bool
1084 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1086 /* We print a heading for every change of line span, hence for every
1087 line span after the initial one. */
1088 if (line_span_idx > 0)
1089 return true;
1091 /* We also do it for the initial span if the primary location of the
1092 diagnostic is in a different span. */
1093 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1094 return true;
1096 return false;
1099 /* Get an expanded_location for the first location of interest within
1100 the given line_span.
1101 Used when printing a heading to indicate a new line span. */
1103 expanded_location
1104 layout::get_expanded_location (const line_span *line_span) const
1106 /* Whenever possible, use the caret location. */
1107 if (line_span->contains_line_p (m_exploc.line))
1108 return m_exploc;
1110 /* Otherwise, use the start of the first range that's present
1111 within the line_span. */
1112 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1114 const layout_range *lr = &m_layout_ranges[i];
1115 if (line_span->contains_line_p (lr->m_start.m_line))
1117 expanded_location exploc = m_exploc;
1118 exploc.line = lr->m_start.m_line;
1119 exploc.column = lr->m_start.m_column;
1120 return exploc;
1124 /* Otherwise, use the location of the first fixit-hint present within
1125 the line_span. */
1126 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1128 const fixit_hint *hint = m_fixit_hints[i];
1129 location_t loc = hint->get_start_loc ();
1130 expanded_location exploc = expand_location (loc);
1131 if (line_span->contains_line_p (exploc.line))
1132 return exploc;
1135 /* It should not be possible to have a line span that didn't
1136 contain any of the layout_range or fixit_hint instances. */
1137 gcc_unreachable ();
1138 return m_exploc;
1141 /* Determine if HINT is meaningful to print within this layout. */
1143 bool
1144 layout::validate_fixit_hint_p (const fixit_hint *hint)
1146 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1147 return false;
1148 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1149 return false;
1151 return true;
1154 /* Determine the range of lines affected by HINT.
1155 This assumes that HINT has already been filtered by
1156 validate_fixit_hint_p, and so affects the correct source file. */
1158 static line_span
1159 get_line_span_for_fixit_hint (const fixit_hint *hint)
1161 gcc_assert (hint);
1163 int start_line = LOCATION_LINE (hint->get_start_loc ());
1165 /* For line-insertion fix-it hints, add the previous line to the
1166 span, to give the user more context on the proposed change. */
1167 if (hint->ends_with_newline_p ())
1168 if (start_line > 1)
1169 start_line--;
1171 return line_span (start_line,
1172 LOCATION_LINE (hint->get_next_loc ()));
1175 /* We want to print the pertinent source code at a diagnostic. The
1176 rich_location can contain multiple locations. This will have been
1177 filtered into m_exploc (the caret for the primary location) and
1178 m_layout_ranges, for those ranges within the same source file.
1180 We will print a subset of the lines within the source file in question,
1181 as a collection of "spans" of lines.
1183 This function populates m_line_spans with an ordered, disjoint list of
1184 the line spans of interest.
1186 Printing a gap between line spans takes one line, so, when printing
1187 line numbers, we allow a gap of up to one line between spans when
1188 merging, since it makes more sense to print the source line rather than a
1189 "gap-in-line-numbering" line. When not printing line numbers, it's
1190 better to be more explicit about what's going on, so keeping them as
1191 separate spans is preferred.
1193 For example, if the primary range is on lines 8-10, with secondary ranges
1194 covering lines 5-6 and lines 13-15:
1197 005 |RANGE 1
1198 006 |RANGE 1
1200 008 |PRIMARY RANGE
1201 009 |PRIMARY CARET
1202 010 |PRIMARY RANGE
1205 013 |RANGE 2
1206 014 |RANGE 2
1207 015 |RANGE 2
1210 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1212 With line numbering off (with span headers), we want three spans: lines 5-6,
1213 lines 8-10, and lines 13-15. */
1215 void
1216 layout::calculate_line_spans ()
1218 /* This should only be called once, by the ctor. */
1219 gcc_assert (m_line_spans.length () == 0);
1221 /* Populate tmp_spans with individual spans, for each of
1222 m_exploc, and for m_layout_ranges. */
1223 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1224 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1225 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1227 const layout_range *lr = &m_layout_ranges[i];
1228 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1229 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1230 lr->m_finish.m_line));
1233 /* Also add spans for any fix-it hints, in case they cover other lines. */
1234 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1236 const fixit_hint *hint = m_fixit_hints[i];
1237 gcc_assert (hint);
1238 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1241 /* Sort them. */
1242 tmp_spans.qsort(line_span::comparator);
1244 /* Now iterate through tmp_spans, copying into m_line_spans, and
1245 combining where possible. */
1246 gcc_assert (tmp_spans.length () > 0);
1247 m_line_spans.safe_push (tmp_spans[0]);
1248 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1250 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1251 const line_span *next = &tmp_spans[i];
1252 gcc_assert (next->m_first_line >= current->m_first_line);
1253 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1254 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
1256 /* We can merge them. */
1257 if (next->m_last_line > current->m_last_line)
1258 current->m_last_line = next->m_last_line;
1260 else
1262 /* No merger possible. */
1263 m_line_spans.safe_push (*next);
1267 /* Verify the result, in m_line_spans. */
1268 gcc_assert (m_line_spans.length () > 0);
1269 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1271 const line_span *prev = &m_line_spans[i - 1];
1272 const line_span *next = &m_line_spans[i];
1273 /* The individual spans must be sane. */
1274 gcc_assert (prev->m_first_line <= prev->m_last_line);
1275 gcc_assert (next->m_first_line <= next->m_last_line);
1276 /* The spans must be ordered. */
1277 gcc_assert (prev->m_first_line < next->m_first_line);
1278 /* There must be a gap of at least one line between separate spans. */
1279 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1283 /* Print line ROW of source code, potentially colorized at any ranges, and
1284 populate *LBOUNDS_OUT.
1285 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1286 is its width. */
1288 void
1289 layout::print_source_line (linenum_type row, const char *line, int line_width,
1290 line_bounds *lbounds_out)
1292 m_colorizer.set_normal_text ();
1294 /* We will stop printing the source line at any trailing
1295 whitespace. */
1296 line_width = get_line_width_without_trailing_whitespace (line,
1297 line_width);
1298 line += m_x_offset;
1300 if (m_show_line_numbers_p)
1302 int width = num_digits (row);
1303 for (int i = 0; i < m_linenum_width - width; i++)
1304 pp_space (m_pp);
1305 pp_printf (m_pp, "%i | ", row);
1307 else
1308 pp_space (m_pp);
1309 int first_non_ws = INT_MAX;
1310 int last_non_ws = 0;
1311 int column;
1312 for (column = 1 + m_x_offset; column <= line_width; column++)
1314 /* Assuming colorization is enabled for the caret and underline
1315 characters, we may also colorize the associated characters
1316 within the source line.
1318 For frontends that generate range information, we color the
1319 associated characters in the source line the same as the
1320 carets and underlines in the annotation line, to make it easier
1321 for the reader to see the pertinent code.
1323 For frontends that only generate carets, we don't colorize the
1324 characters above them, since this would look strange (e.g.
1325 colorizing just the first character in a token). */
1326 if (m_colorize_source_p)
1328 bool in_range_p;
1329 point_state state;
1330 in_range_p = get_state_at_point (row, column,
1331 0, INT_MAX,
1332 &state);
1333 if (in_range_p)
1334 m_colorizer.set_range (state.range_idx);
1335 else
1336 m_colorizer.set_normal_text ();
1338 char c = *line;
1339 if (c == '\0' || c == '\t' || c == '\r')
1340 c = ' ';
1341 if (c != ' ')
1343 last_non_ws = column;
1344 if (first_non_ws == INT_MAX)
1345 first_non_ws = column;
1347 pp_character (m_pp, c);
1348 line++;
1350 print_newline ();
1352 lbounds_out->m_first_non_ws = first_non_ws;
1353 lbounds_out->m_last_non_ws = last_non_ws;
1356 /* Determine if we should print an annotation line for ROW.
1357 i.e. if any of m_layout_ranges contains ROW. */
1359 bool
1360 layout::should_print_annotation_line_p (linenum_type row) const
1362 layout_range *range;
1363 int i;
1364 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1366 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1367 return false;
1368 if (range->intersects_line_p (row))
1369 return true;
1371 return false;
1374 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1375 margin, which is empty for annotation lines. Otherwise, do nothing. */
1377 void
1378 layout::start_annotation_line (char margin_char) const
1380 if (m_show_line_numbers_p)
1382 for (int i = 0; i < m_linenum_width; i++)
1383 pp_character (m_pp, margin_char);
1384 pp_string (m_pp, " |");
1388 /* Print a line consisting of the caret/underlines for the given
1389 source line. */
1391 void
1392 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1394 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1395 lbounds.m_last_non_ws);
1397 start_annotation_line ();
1398 pp_space (m_pp);
1400 for (int column = 1 + m_x_offset; column < x_bound; column++)
1402 bool in_range_p;
1403 point_state state;
1404 in_range_p = get_state_at_point (row, column,
1405 lbounds.m_first_non_ws,
1406 lbounds.m_last_non_ws,
1407 &state);
1408 if (in_range_p)
1410 /* Within a range. Draw either the caret or an underline. */
1411 m_colorizer.set_range (state.range_idx);
1412 if (state.draw_caret_p)
1414 /* Draw the caret. */
1415 char caret_char;
1416 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1417 caret_char = m_context->caret_chars[state.range_idx];
1418 else
1419 caret_char = '^';
1420 pp_character (m_pp, caret_char);
1422 else
1423 pp_character (m_pp, '~');
1425 else
1427 /* Not in a range. */
1428 m_colorizer.set_normal_text ();
1429 pp_character (m_pp, ' ');
1432 print_newline ();
1435 /* Implementation detail of layout::print_any_labels.
1437 A label within the given row of source. */
1439 struct line_label
1441 line_label (int state_idx, int column, label_text text)
1442 : m_state_idx (state_idx), m_column (column),
1443 m_text (text), m_length (strlen (text.m_buffer)),
1444 m_label_line (0)
1447 /* Sorting is primarily by column, then by state index. */
1448 static int comparator (const void *p1, const void *p2)
1450 const line_label *ll1 = (const line_label *)p1;
1451 const line_label *ll2 = (const line_label *)p2;
1452 int column_cmp = compare (ll1->m_column, ll2->m_column);
1453 if (column_cmp)
1454 return column_cmp;
1455 return compare (ll1->m_state_idx, ll2->m_state_idx);
1458 int m_state_idx;
1459 int m_column;
1460 label_text m_text;
1461 size_t m_length;
1462 int m_label_line;
1465 /* Print any labels in this row. */
1466 void
1467 layout::print_any_labels (linenum_type row)
1469 int i;
1470 auto_vec<line_label> labels;
1472 /* Gather the labels that are to be printed into "labels". */
1474 layout_range *range;
1475 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1477 /* Most ranges don't have labels, so reject this first. */
1478 if (range->m_label == NULL)
1479 continue;
1481 /* The range's caret must be on this line. */
1482 if (range->m_caret.m_line != row)
1483 continue;
1485 /* Reject labels that aren't fully visible due to clipping
1486 by m_x_offset. */
1487 if (range->m_caret.m_column <= m_x_offset)
1488 continue;
1490 label_text text;
1491 text = range->m_label->get_text ();
1493 /* Allow for labels that return NULL from their get_text
1494 implementation (so e.g. such labels can control their own
1495 visibility). */
1496 if (text.m_buffer == NULL)
1497 continue;
1499 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1503 /* Bail out if there are no labels on this row. */
1504 if (labels.length () == 0)
1505 return;
1507 /* Sort them. */
1508 labels.qsort(line_label::comparator);
1510 /* Figure out how many "label lines" we need, and which
1511 one each label is printed in.
1513 For example, if the labels aren't too densely packed,
1514 we can fit them on the same line, giving two "label lines":
1516 foo + bar
1517 ~~~ ~~~
1518 | | : label line 0
1519 l0 l1 : label line 1
1521 If they would touch each other or overlap, then we need
1522 additional "label lines":
1524 foo + bar
1525 ~~~ ~~~
1526 | | : label line 0
1527 | label 1 : label line 1
1528 label 0 : label line 2
1530 Place the final label on label line 1, and work backwards, adding
1531 label lines as needed.
1533 If multiple labels are at the same place, put them on separate
1534 label lines:
1536 foo + bar
1537 ^ : label line 0
1538 | : label line 1
1539 label 1 : label line 2
1540 label 0 : label line 3. */
1542 int max_label_line = 1;
1544 int next_column = INT_MAX;
1545 line_label *label;
1546 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1548 /* Would this label "touch" or overlap the next label? */
1549 if (label->m_column + label->m_length >= (size_t)next_column)
1550 max_label_line++;
1552 label->m_label_line = max_label_line;
1553 next_column = label->m_column;
1557 /* Print the "label lines". For each label within the line, print
1558 either a vertical bar ('|') for the labels that are lower down, or the
1559 labels themselves once we've reached their line. */
1561 /* Keep track of in which column we last printed a vertical bar.
1562 This allows us to suppress duplicate vertical bars for the case
1563 where multiple labels are on one column. */
1564 int last_vbar = 0;
1565 for (int label_line = 0; label_line <= max_label_line; label_line++)
1567 start_annotation_line ();
1568 pp_space (m_pp);
1569 int column = 1 + m_x_offset;
1570 line_label *label;
1571 FOR_EACH_VEC_ELT (labels, i, label)
1573 if (label_line > label->m_label_line)
1574 /* We've printed all the labels for this label line. */
1575 break;
1577 if (label_line == label->m_label_line)
1579 gcc_assert (column <= label->m_column);
1580 move_to_column (&column, label->m_column, true);
1581 m_colorizer.set_range (label->m_state_idx);
1582 pp_string (m_pp, label->m_text.m_buffer);
1583 m_colorizer.set_normal_text ();
1584 column += label->m_length;
1586 else if (label->m_column != last_vbar)
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_character (m_pp, '|');
1592 m_colorizer.set_normal_text ();
1593 last_vbar = column;
1594 column++;
1597 print_newline ();
1601 /* Clean up. */
1603 line_label *label;
1604 FOR_EACH_VEC_ELT (labels, i, label)
1605 label->m_text.maybe_free ();
1609 /* If there are any fixit hints inserting new lines before source line ROW,
1610 print them.
1612 They are printed on lines of their own, before the source line
1613 itself, with a leading '+'. */
1615 void
1616 layout::print_leading_fixits (linenum_type row)
1618 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1620 const fixit_hint *hint = m_fixit_hints[i];
1622 if (!hint->ends_with_newline_p ())
1623 /* Not a newline fixit; print it in print_trailing_fixits. */
1624 continue;
1626 gcc_assert (hint->insertion_p ());
1628 if (hint->affects_line_p (m_exploc.file, row))
1630 /* Printing the '+' with normal colorization
1631 and the inserted line with "insert" colorization
1632 helps them stand out from each other, and from
1633 the surrounding text. */
1634 m_colorizer.set_normal_text ();
1635 start_annotation_line ('+');
1636 pp_character (m_pp, '+');
1637 m_colorizer.set_fixit_insert ();
1638 /* Print all but the trailing newline of the fix-it hint.
1639 We have to print the newline separately to avoid
1640 getting additional pp prefixes printed. */
1641 for (size_t i = 0; i < hint->get_length () - 1; i++)
1642 pp_character (m_pp, hint->get_string ()[i]);
1643 m_colorizer.set_normal_text ();
1644 pp_newline (m_pp);
1649 /* Subroutine of layout::print_trailing_fixits.
1651 Determine if the annotation line printed for LINE contained
1652 the exact range from START_COLUMN to FINISH_COLUMN. */
1654 bool
1655 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1656 int finish_column) const
1658 layout_range *range;
1659 int i;
1660 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1661 if (range->m_start.m_line == line
1662 && range->m_start.m_column == start_column
1663 && range->m_finish.m_line == line
1664 && range->m_finish.m_column == finish_column)
1665 return true;
1666 return false;
1669 /* Classes for printing trailing fix-it hints i.e. those that
1670 don't add new lines.
1672 For insertion, these can look like:
1674 new_text
1676 For replacement, these can look like:
1678 ------------- : underline showing affected range
1679 new_text
1681 For deletion, these can look like:
1683 ------------- : underline showing affected range
1685 This can become confusing if they overlap, and so we need
1686 to do some preprocessing to decide what to print.
1687 We use the list of fixit_hint instances affecting the line
1688 to build a list of "correction" instances, and print the
1689 latter.
1691 For example, consider a set of fix-its for converting
1692 a C-style cast to a C++ const_cast.
1694 Given:
1696 ..000000000111111111122222222223333333333.
1697 ..123456789012345678901234567890123456789.
1698 foo *f = (foo *)ptr->field;
1699 ^~~~~
1701 and the fix-it hints:
1702 - replace col 10 (the open paren) with "const_cast<"
1703 - replace col 16 (the close paren) with "> ("
1704 - insert ")" before col 27
1706 then we would get odd-looking output:
1708 foo *f = (foo *)ptr->field;
1709 ^~~~~
1711 const_cast<
1713 > ( )
1715 It would be better to detect when fixit hints are going to
1716 overlap (those that require new lines), and to consolidate
1717 the printing of such fixits, giving something like:
1719 foo *f = (foo *)ptr->field;
1720 ^~~~~
1721 -----------------
1722 const_cast<foo *> (ptr->field)
1724 This works by detecting when the printing would overlap, and
1725 effectively injecting no-op replace hints into the gaps between
1726 such fix-its, so that the printing joins up.
1728 In the above example, the overlap of:
1729 - replace col 10 (the open paren) with "const_cast<"
1730 and:
1731 - replace col 16 (the close paren) with "> ("
1732 is fixed by injecting a no-op:
1733 - replace cols 11-15 with themselves ("foo *")
1734 and consolidating these, making:
1735 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1736 i.e.:
1737 - replace cols 10-16 with "const_cast<foo *> ("
1739 This overlaps with the final fix-it hint:
1740 - insert ")" before col 27
1741 and so we repeat the consolidation process, by injecting
1742 a no-op:
1743 - replace cols 17-26 with themselves ("ptr->field")
1744 giving:
1745 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1746 i.e.:
1747 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1749 and is thus printed as desired. */
1751 /* A range of columns within a line. */
1753 struct column_range
1755 column_range (int start_, int finish_) : start (start_), finish (finish_)
1757 /* We must have either a range, or an insertion. */
1758 gcc_assert (start <= finish || finish == start - 1);
1761 bool operator== (const column_range &other) const
1763 return start == other.start && finish == other.finish;
1766 int start;
1767 int finish;
1770 /* Get the range of columns that HINT would affect. */
1772 static column_range
1773 get_affected_columns (const fixit_hint *hint)
1775 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1776 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1778 return column_range (start_column, finish_column);
1781 /* Get the range of columns that would be printed for HINT. */
1783 static column_range
1784 get_printed_columns (const fixit_hint *hint)
1786 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1787 int final_hint_column = start_column + hint->get_length () - 1;
1788 if (hint->insertion_p ())
1790 return column_range (start_column, final_hint_column);
1792 else
1794 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1796 return column_range (start_column,
1797 MAX (finish_column, final_hint_column));
1801 /* A correction on a particular line.
1802 This describes a plan for how to print one or more fixit_hint
1803 instances that affected the line, potentially consolidating hints
1804 into corrections to make the result easier for the user to read. */
1806 struct correction
1808 correction (column_range affected_columns,
1809 column_range printed_columns,
1810 const char *new_text, size_t new_text_len)
1811 : m_affected_columns (affected_columns),
1812 m_printed_columns (printed_columns),
1813 m_text (xstrdup (new_text)),
1814 m_len (new_text_len),
1815 m_alloc_sz (new_text_len + 1)
1819 ~correction () { free (m_text); }
1821 bool insertion_p () const
1823 return m_affected_columns.start == m_affected_columns.finish + 1;
1826 void ensure_capacity (size_t len);
1827 void ensure_terminated ();
1829 void overwrite (int dst_offset, const char_span &src_span)
1831 gcc_assert (dst_offset >= 0);
1832 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1833 memcpy (m_text + dst_offset, src_span.get_buffer (),
1834 src_span.length ());
1837 /* If insert, then start: the column before which the text
1838 is to be inserted, and finish is offset by the length of
1839 the replacement.
1840 If replace, then the range of columns affected. */
1841 column_range m_affected_columns;
1843 /* If insert, then start: the column before which the text
1844 is to be inserted, and finish is offset by the length of
1845 the replacement.
1846 If replace, then the range of columns affected. */
1847 column_range m_printed_columns;
1849 /* The text to be inserted/used as replacement. */
1850 char *m_text;
1851 size_t m_len;
1852 size_t m_alloc_sz;
1855 /* Ensure that m_text can hold a string of length LEN
1856 (plus 1 for 0-termination). */
1858 void
1859 correction::ensure_capacity (size_t len)
1861 /* Allow 1 extra byte for 0-termination. */
1862 if (m_alloc_sz < (len + 1))
1864 size_t new_alloc_sz = (len + 1) * 2;
1865 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1866 m_alloc_sz = new_alloc_sz;
1870 /* Ensure that m_text is 0-terminated. */
1872 void
1873 correction::ensure_terminated ()
1875 /* 0-terminate the buffer. */
1876 gcc_assert (m_len < m_alloc_sz);
1877 m_text[m_len] = '\0';
1880 /* A list of corrections affecting a particular line.
1881 This is used by layout::print_trailing_fixits for planning
1882 how to print the fix-it hints affecting the line. */
1884 struct line_corrections
1886 line_corrections (const char *filename, linenum_type row)
1887 : m_filename (filename), m_row (row)
1889 ~line_corrections ();
1891 void add_hint (const fixit_hint *hint);
1893 const char *m_filename;
1894 linenum_type m_row;
1895 auto_vec <correction *> m_corrections;
1898 /* struct line_corrections. */
1900 line_corrections::~line_corrections ()
1902 unsigned i;
1903 correction *c;
1904 FOR_EACH_VEC_ELT (m_corrections, i, c)
1905 delete c;
1908 /* A struct wrapping a particular source line, allowing
1909 run-time bounds-checking of accesses in a checked build. */
1911 struct source_line
1913 source_line (const char *filename, int line);
1915 char_span as_span () { return char_span (chars, width); }
1917 const char *chars;
1918 int width;
1921 /* source_line's ctor. */
1923 source_line::source_line (const char *filename, int line)
1925 char_span span = location_get_source_line (filename, line);
1926 chars = span.get_buffer ();
1927 width = span.length ();
1930 /* Add HINT to the corrections for this line.
1931 Attempt to consolidate nearby hints so that they will not
1932 overlap with printed. */
1934 void
1935 line_corrections::add_hint (const fixit_hint *hint)
1937 column_range affected_columns = get_affected_columns (hint);
1938 column_range printed_columns = get_printed_columns (hint);
1940 /* Potentially consolidate. */
1941 if (!m_corrections.is_empty ())
1943 correction *last_correction
1944 = m_corrections[m_corrections.length () - 1];
1946 /* The following consolidation code assumes that the fix-it hints
1947 have been sorted by start (done within layout's ctor). */
1948 gcc_assert (affected_columns.start
1949 >= last_correction->m_affected_columns.start);
1950 gcc_assert (printed_columns.start
1951 >= last_correction->m_printed_columns.start);
1953 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1955 /* We have two hints for which the printed forms of the hints
1956 would touch or overlap, so we need to consolidate them to avoid
1957 confusing the user.
1958 Attempt to inject a "replace" correction from immediately
1959 after the end of the last hint to immediately before the start
1960 of the next hint. */
1961 column_range between (last_correction->m_affected_columns.finish + 1,
1962 printed_columns.start - 1);
1964 /* Try to read the source. */
1965 source_line line (m_filename, m_row);
1966 if (line.chars && between.finish < line.width)
1968 /* Consolidate into the last correction:
1969 add a no-op "replace" of the "between" text, and
1970 add the text from the new hint. */
1971 int old_len = last_correction->m_len;
1972 gcc_assert (old_len >= 0);
1973 int between_len = between.finish + 1 - between.start;
1974 gcc_assert (between_len >= 0);
1975 int new_len = old_len + between_len + hint->get_length ();
1976 gcc_assert (new_len >= 0);
1977 last_correction->ensure_capacity (new_len);
1978 last_correction->overwrite
1979 (old_len,
1980 line.as_span ().subspan (between.start - 1,
1981 between.finish + 1 - between.start));
1982 last_correction->overwrite (old_len + between_len,
1983 char_span (hint->get_string (),
1984 hint->get_length ()));
1985 last_correction->m_len = new_len;
1986 last_correction->ensure_terminated ();
1987 last_correction->m_affected_columns.finish
1988 = affected_columns.finish;
1989 last_correction->m_printed_columns.finish
1990 += between_len + hint->get_length ();
1991 return;
1996 /* If no consolidation happened, add a new correction instance. */
1997 m_corrections.safe_push (new correction (affected_columns,
1998 printed_columns,
1999 hint->get_string (),
2000 hint->get_length ()));
2003 /* If there are any fixit hints on source line ROW, print them.
2004 They are printed in order, attempting to combine them onto lines, but
2005 starting new lines if necessary.
2006 Fix-it hints that insert new lines are handled separately,
2007 in layout::print_leading_fixits. */
2009 void
2010 layout::print_trailing_fixits (linenum_type row)
2012 /* Build a list of correction instances for the line,
2013 potentially consolidating hints (for the sake of readability). */
2014 line_corrections corrections (m_exploc.file, row);
2015 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2017 const fixit_hint *hint = m_fixit_hints[i];
2019 /* Newline fixits are handled by layout::print_leading_fixits. */
2020 if (hint->ends_with_newline_p ())
2021 continue;
2023 if (hint->affects_line_p (m_exploc.file, row))
2024 corrections.add_hint (hint);
2027 /* Now print the corrections. */
2028 unsigned i;
2029 correction *c;
2030 int column = m_x_offset;
2032 if (!corrections.m_corrections.is_empty ())
2033 start_annotation_line ();
2035 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2037 /* For now we assume each fixit hint can only touch one line. */
2038 if (c->insertion_p ())
2040 /* This assumes the insertion just affects one line. */
2041 int start_column = c->m_printed_columns.start;
2042 move_to_column (&column, start_column, true);
2043 m_colorizer.set_fixit_insert ();
2044 pp_string (m_pp, c->m_text);
2045 m_colorizer.set_normal_text ();
2046 column += c->m_len;
2048 else
2050 /* If the range of the replacement wasn't printed in the
2051 annotation line, then print an extra underline to
2052 indicate exactly what is being replaced.
2053 Always show it for removals. */
2054 int start_column = c->m_affected_columns.start;
2055 int finish_column = c->m_affected_columns.finish;
2056 if (!annotation_line_showed_range_p (row, start_column,
2057 finish_column)
2058 || c->m_len == 0)
2060 move_to_column (&column, start_column, true);
2061 m_colorizer.set_fixit_delete ();
2062 for (; column <= finish_column; column++)
2063 pp_character (m_pp, '-');
2064 m_colorizer.set_normal_text ();
2066 /* Print the replacement text. REPLACE also covers
2067 removals, so only do this extra work (potentially starting
2068 a new line) if we have actual replacement text. */
2069 if (c->m_len > 0)
2071 move_to_column (&column, start_column, true);
2072 m_colorizer.set_fixit_insert ();
2073 pp_string (m_pp, c->m_text);
2074 m_colorizer.set_normal_text ();
2075 column += c->m_len;
2080 /* Add a trailing newline, if necessary. */
2081 move_to_column (&column, 0, false);
2084 /* Disable any colorization and emit a newline. */
2086 void
2087 layout::print_newline ()
2089 m_colorizer.set_normal_text ();
2090 pp_newline (m_pp);
2093 /* Return true if (ROW/COLUMN) is within a range of the layout.
2094 If it returns true, OUT_STATE is written to, with the
2095 range index, and whether we should draw the caret at
2096 (ROW/COLUMN) (as opposed to an underline). */
2098 bool
2099 layout::get_state_at_point (/* Inputs. */
2100 linenum_type row, int column,
2101 int first_non_ws, int last_non_ws,
2102 /* Outputs. */
2103 point_state *out_state)
2105 layout_range *range;
2106 int i;
2107 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2109 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2110 /* Bail out early, so that such ranges don't affect underlining or
2111 source colorization. */
2112 continue;
2114 if (range->contains_point (row, column))
2116 out_state->range_idx = i;
2118 /* Are we at the range's caret? is it visible? */
2119 out_state->draw_caret_p = false;
2120 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2121 && row == range->m_caret.m_line
2122 && column == range->m_caret.m_column)
2123 out_state->draw_caret_p = true;
2125 /* Within a multiline range, don't display any underline
2126 in any leading or trailing whitespace on a line.
2127 We do display carets, however. */
2128 if (!out_state->draw_caret_p)
2129 if (column < first_non_ws || column > last_non_ws)
2130 return false;
2132 /* We are within a range. */
2133 return true;
2137 return false;
2140 /* Helper function for use by layout::print_line when printing the
2141 annotation line under the source line.
2142 Get the column beyond the rightmost one that could contain a caret or
2143 range marker, given that we stop rendering at trailing whitespace.
2144 ROW is the source line within the given file.
2145 CARET_COLUMN is the column of range 0's caret.
2146 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2147 character of source (as determined when printing the source line). */
2150 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2151 int last_non_ws_column)
2153 int result = caret_column + 1;
2155 layout_range *range;
2156 int i;
2157 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2159 if (row >= range->m_start.m_line)
2161 if (range->m_finish.m_line == row)
2163 /* On the final line within a range; ensure that
2164 we render up to the end of the range. */
2165 if (result <= range->m_finish.m_column)
2166 result = range->m_finish.m_column + 1;
2168 else if (row < range->m_finish.m_line)
2170 /* Within a multiline range; ensure that we render up to the
2171 last non-whitespace column. */
2172 if (result <= last_non_ws_column)
2173 result = last_non_ws_column + 1;
2178 return result;
2181 /* Given *COLUMN as an x-coordinate, print spaces to position
2182 successive output at DEST_COLUMN, printing a newline if necessary,
2183 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2184 left margin after any newline. */
2186 void
2187 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2189 /* Start a new line if we need to. */
2190 if (*column > dest_column)
2192 print_newline ();
2193 if (add_left_margin)
2194 start_annotation_line ();
2195 *column = m_x_offset;
2198 while (*column < dest_column)
2200 pp_space (m_pp);
2201 (*column)++;
2205 /* For debugging layout issues, render a ruler giving column numbers
2206 (after the 1-column indent). */
2208 void
2209 layout::show_ruler (int max_column) const
2211 /* Hundreds. */
2212 if (max_column > 99)
2214 start_annotation_line ();
2215 pp_space (m_pp);
2216 for (int column = 1 + m_x_offset; column <= max_column; column++)
2217 if (column % 10 == 0)
2218 pp_character (m_pp, '0' + (column / 100) % 10);
2219 else
2220 pp_space (m_pp);
2221 pp_newline (m_pp);
2224 /* Tens. */
2225 start_annotation_line ();
2226 pp_space (m_pp);
2227 for (int column = 1 + m_x_offset; column <= max_column; column++)
2228 if (column % 10 == 0)
2229 pp_character (m_pp, '0' + (column / 10) % 10);
2230 else
2231 pp_space (m_pp);
2232 pp_newline (m_pp);
2234 /* Units. */
2235 start_annotation_line ();
2236 pp_space (m_pp);
2237 for (int column = 1 + m_x_offset; column <= max_column; column++)
2238 pp_character (m_pp, '0' + (column % 10));
2239 pp_newline (m_pp);
2242 /* Print leading fix-its (for new lines inserted before the source line)
2243 then the source line, followed by an annotation line
2244 consisting of any caret/underlines, then any fixits.
2245 If the source line can't be read, print nothing. */
2246 void
2247 layout::print_line (linenum_type row)
2249 char_span line = location_get_source_line (m_exploc.file, row);
2250 if (!line)
2251 return;
2253 line_bounds lbounds;
2254 print_leading_fixits (row);
2255 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2256 if (should_print_annotation_line_p (row))
2257 print_annotation_line (row, lbounds);
2258 if (m_show_labels_p)
2259 print_any_labels (row);
2260 print_trailing_fixits (row);
2263 } /* End of anonymous namespace. */
2265 /* If LOC is within the spans of lines that will already be printed for
2266 this gcc_rich_location, then add it as a secondary location and return true.
2268 Otherwise return false. */
2270 bool
2271 gcc_rich_location::add_location_if_nearby (location_t loc)
2273 /* Use the layout location-handling logic to sanitize LOC,
2274 filtering it to the current line spans within a temporary
2275 layout instance. */
2276 layout layout (global_dc, this, DK_ERROR);
2277 location_range loc_range;
2278 loc_range.m_loc = loc;
2279 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2280 if (!layout.maybe_add_location_range (&loc_range, true))
2281 return false;
2283 add_range (loc);
2284 return true;
2287 /* Print the physical source code corresponding to the location of
2288 this diagnostic, with additional annotations. */
2290 void
2291 diagnostic_show_locus (diagnostic_context * context,
2292 rich_location *richloc,
2293 diagnostic_t diagnostic_kind)
2295 pp_newline (context->printer);
2297 location_t loc = richloc->get_loc ();
2298 /* Do nothing if source-printing has been disabled. */
2299 if (!context->show_caret)
2300 return;
2302 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2303 if (loc <= BUILTINS_LOCATION)
2304 return;
2306 /* Don't print the same source location twice in a row, unless we have
2307 fix-it hints. */
2308 if (loc == context->last_location
2309 && richloc->get_num_fixit_hints () == 0)
2310 return;
2312 context->last_location = loc;
2314 char *saved_prefix = pp_take_prefix (context->printer);
2315 pp_set_prefix (context->printer, NULL);
2317 layout layout (context, richloc, diagnostic_kind);
2318 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2319 line_span_idx++)
2321 const line_span *line_span = layout.get_line_span (line_span_idx);
2322 if (context->show_line_numbers_p)
2324 /* With line numbers, we should show whenever the line-numbering
2325 "jumps". */
2326 if (line_span_idx > 0)
2327 layout.print_gap_in_line_numbering ();
2329 else
2331 /* Without line numbers, we print headings for some line spans. */
2332 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2334 expanded_location exploc
2335 = layout.get_expanded_location (line_span);
2336 context->start_span (context, exploc);
2339 linenum_type last_line = line_span->get_last_line ();
2340 for (linenum_type row = line_span->get_first_line ();
2341 row <= last_line; row++)
2342 layout.print_line (row);
2345 pp_set_prefix (context->printer, saved_prefix);
2348 #if CHECKING_P
2350 namespace selftest {
2352 /* Selftests for diagnostic_show_locus. */
2354 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2356 static void
2357 test_diagnostic_show_locus_unknown_location ()
2359 test_diagnostic_context dc;
2360 rich_location richloc (line_table, UNKNOWN_LOCATION);
2361 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2362 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2365 /* Verify that diagnostic_show_locus works sanely for various
2366 single-line cases.
2368 All of these work on the following 1-line source file:
2369 .0000000001111111
2370 .1234567890123456
2371 "foo = bar.field;\n"
2372 which is set up by test_diagnostic_show_locus_one_liner and calls
2373 them. */
2375 /* Just a caret. */
2377 static void
2378 test_one_liner_simple_caret ()
2380 test_diagnostic_context dc;
2381 location_t caret = linemap_position_for_column (line_table, 10);
2382 rich_location richloc (line_table, caret);
2383 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2384 ASSERT_STREQ ("\n"
2385 " foo = bar.field;\n"
2386 " ^\n",
2387 pp_formatted_text (dc.printer));
2390 /* Caret and range. */
2392 static void
2393 test_one_liner_caret_and_range ()
2395 test_diagnostic_context dc;
2396 location_t caret = linemap_position_for_column (line_table, 10);
2397 location_t start = linemap_position_for_column (line_table, 7);
2398 location_t finish = linemap_position_for_column (line_table, 15);
2399 location_t loc = make_location (caret, start, finish);
2400 rich_location richloc (line_table, loc);
2401 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2402 ASSERT_STREQ ("\n"
2403 " foo = bar.field;\n"
2404 " ~~~^~~~~~\n",
2405 pp_formatted_text (dc.printer));
2408 /* Multiple ranges and carets. */
2410 static void
2411 test_one_liner_multiple_carets_and_ranges ()
2413 test_diagnostic_context dc;
2414 location_t foo
2415 = make_location (linemap_position_for_column (line_table, 2),
2416 linemap_position_for_column (line_table, 1),
2417 linemap_position_for_column (line_table, 3));
2418 dc.caret_chars[0] = 'A';
2420 location_t bar
2421 = make_location (linemap_position_for_column (line_table, 8),
2422 linemap_position_for_column (line_table, 7),
2423 linemap_position_for_column (line_table, 9));
2424 dc.caret_chars[1] = 'B';
2426 location_t field
2427 = make_location (linemap_position_for_column (line_table, 13),
2428 linemap_position_for_column (line_table, 11),
2429 linemap_position_for_column (line_table, 15));
2430 dc.caret_chars[2] = 'C';
2432 rich_location richloc (line_table, foo);
2433 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2434 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2435 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2436 ASSERT_STREQ ("\n"
2437 " foo = bar.field;\n"
2438 " ~A~ ~B~ ~~C~~\n",
2439 pp_formatted_text (dc.printer));
2442 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2444 static void
2445 test_one_liner_fixit_insert_before ()
2447 test_diagnostic_context dc;
2448 location_t caret = linemap_position_for_column (line_table, 7);
2449 rich_location richloc (line_table, caret);
2450 richloc.add_fixit_insert_before ("&");
2451 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2452 ASSERT_STREQ ("\n"
2453 " foo = bar.field;\n"
2454 " ^\n"
2455 " &\n",
2456 pp_formatted_text (dc.printer));
2459 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2461 static void
2462 test_one_liner_fixit_insert_after ()
2464 test_diagnostic_context dc;
2465 location_t start = linemap_position_for_column (line_table, 1);
2466 location_t finish = linemap_position_for_column (line_table, 3);
2467 location_t foo = make_location (start, start, finish);
2468 rich_location richloc (line_table, foo);
2469 richloc.add_fixit_insert_after ("[0]");
2470 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2471 ASSERT_STREQ ("\n"
2472 " foo = bar.field;\n"
2473 " ^~~\n"
2474 " [0]\n",
2475 pp_formatted_text (dc.printer));
2478 /* Removal fix-it hint: removal of the ".field". */
2480 static void
2481 test_one_liner_fixit_remove ()
2483 test_diagnostic_context dc;
2484 location_t start = linemap_position_for_column (line_table, 10);
2485 location_t finish = linemap_position_for_column (line_table, 15);
2486 location_t dot = make_location (start, start, finish);
2487 rich_location richloc (line_table, dot);
2488 richloc.add_fixit_remove ();
2489 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2490 ASSERT_STREQ ("\n"
2491 " foo = bar.field;\n"
2492 " ^~~~~~\n"
2493 " ------\n",
2494 pp_formatted_text (dc.printer));
2497 /* Replace fix-it hint: replacing "field" with "m_field". */
2499 static void
2500 test_one_liner_fixit_replace ()
2502 test_diagnostic_context dc;
2503 location_t start = linemap_position_for_column (line_table, 11);
2504 location_t finish = linemap_position_for_column (line_table, 15);
2505 location_t field = make_location (start, start, finish);
2506 rich_location richloc (line_table, field);
2507 richloc.add_fixit_replace ("m_field");
2508 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2509 ASSERT_STREQ ("\n"
2510 " foo = bar.field;\n"
2511 " ^~~~~\n"
2512 " m_field\n",
2513 pp_formatted_text (dc.printer));
2516 /* Replace fix-it hint: replacing "field" with "m_field",
2517 but where the caret was elsewhere. */
2519 static void
2520 test_one_liner_fixit_replace_non_equal_range ()
2522 test_diagnostic_context dc;
2523 location_t equals = linemap_position_for_column (line_table, 5);
2524 location_t start = linemap_position_for_column (line_table, 11);
2525 location_t finish = linemap_position_for_column (line_table, 15);
2526 rich_location richloc (line_table, equals);
2527 source_range range;
2528 range.m_start = start;
2529 range.m_finish = finish;
2530 richloc.add_fixit_replace (range, "m_field");
2531 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2532 /* The replacement range is not indicated in the annotation line, so
2533 it should be indicated via an additional underline. */
2534 ASSERT_STREQ ("\n"
2535 " foo = bar.field;\n"
2536 " ^\n"
2537 " -----\n"
2538 " m_field\n",
2539 pp_formatted_text (dc.printer));
2542 /* Replace fix-it hint: replacing "field" with "m_field",
2543 where the caret was elsewhere, but where a secondary range
2544 exactly covers "field". */
2546 static void
2547 test_one_liner_fixit_replace_equal_secondary_range ()
2549 test_diagnostic_context dc;
2550 location_t equals = linemap_position_for_column (line_table, 5);
2551 location_t start = linemap_position_for_column (line_table, 11);
2552 location_t finish = linemap_position_for_column (line_table, 15);
2553 rich_location richloc (line_table, equals);
2554 location_t field = make_location (start, start, finish);
2555 richloc.add_range (field);
2556 richloc.add_fixit_replace (field, "m_field");
2557 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2558 /* The replacement range is indicated in the annotation line,
2559 so it shouldn't be indicated via an additional underline. */
2560 ASSERT_STREQ ("\n"
2561 " foo = bar.field;\n"
2562 " ^ ~~~~~\n"
2563 " m_field\n",
2564 pp_formatted_text (dc.printer));
2567 /* Verify that we can use ad-hoc locations when adding fixits to a
2568 rich_location. */
2570 static void
2571 test_one_liner_fixit_validation_adhoc_locations ()
2573 /* Generate a range that's too long to be packed, so must
2574 be stored as an ad-hoc location (given the defaults
2575 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2576 const location_t c7 = linemap_position_for_column (line_table, 7);
2577 const location_t c47 = linemap_position_for_column (line_table, 47);
2578 const location_t loc = make_location (c7, c7, c47);
2580 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2581 return;
2583 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2585 /* Insert. */
2587 rich_location richloc (line_table, loc);
2588 richloc.add_fixit_insert_before (loc, "test");
2589 /* It should not have been discarded by the validator. */
2590 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2592 test_diagnostic_context dc;
2593 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2594 ASSERT_STREQ ("\n"
2595 " foo = bar.field;\n"
2596 " ^~~~~~~~~~ \n"
2597 " test\n",
2598 pp_formatted_text (dc.printer));
2601 /* Remove. */
2603 rich_location richloc (line_table, loc);
2604 source_range range = source_range::from_locations (loc, c47);
2605 richloc.add_fixit_remove (range);
2606 /* It should not have been discarded by the validator. */
2607 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2609 test_diagnostic_context dc;
2610 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2611 ASSERT_STREQ ("\n"
2612 " foo = bar.field;\n"
2613 " ^~~~~~~~~~ \n"
2614 " -----------------------------------------\n",
2615 pp_formatted_text (dc.printer));
2618 /* Replace. */
2620 rich_location richloc (line_table, loc);
2621 source_range range = source_range::from_locations (loc, c47);
2622 richloc.add_fixit_replace (range, "test");
2623 /* It should not have been discarded by the validator. */
2624 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2626 test_diagnostic_context dc;
2627 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2628 ASSERT_STREQ ("\n"
2629 " foo = bar.field;\n"
2630 " ^~~~~~~~~~ \n"
2631 " test\n",
2632 pp_formatted_text (dc.printer));
2636 /* Test of consolidating insertions at the same location. */
2638 static void
2639 test_one_liner_many_fixits_1 ()
2641 test_diagnostic_context dc;
2642 location_t equals = linemap_position_for_column (line_table, 5);
2643 rich_location richloc (line_table, equals);
2644 for (int i = 0; i < 19; i++)
2645 richloc.add_fixit_insert_before ("a");
2646 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2647 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2648 ASSERT_STREQ ("\n"
2649 " foo = bar.field;\n"
2650 " ^\n"
2651 " aaaaaaaaaaaaaaaaaaa\n",
2652 pp_formatted_text (dc.printer));
2655 /* Ensure that we can add an arbitrary number of fix-it hints to a
2656 rich_location, even if they are not consolidated. */
2658 static void
2659 test_one_liner_many_fixits_2 ()
2661 test_diagnostic_context dc;
2662 location_t equals = linemap_position_for_column (line_table, 5);
2663 rich_location richloc (line_table, equals);
2664 for (int i = 0; i < 19; i++)
2666 location_t loc = linemap_position_for_column (line_table, i * 2);
2667 richloc.add_fixit_insert_before (loc, "a");
2669 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2670 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2671 ASSERT_STREQ ("\n"
2672 " foo = bar.field;\n"
2673 " ^\n"
2674 "a a a a a a a a a a a a a a a a a a a\n",
2675 pp_formatted_text (dc.printer));
2678 /* Test of labeling the ranges within a rich_location. */
2680 static void
2681 test_one_liner_labels ()
2683 location_t foo
2684 = make_location (linemap_position_for_column (line_table, 1),
2685 linemap_position_for_column (line_table, 1),
2686 linemap_position_for_column (line_table, 3));
2687 location_t bar
2688 = make_location (linemap_position_for_column (line_table, 7),
2689 linemap_position_for_column (line_table, 7),
2690 linemap_position_for_column (line_table, 9));
2691 location_t field
2692 = make_location (linemap_position_for_column (line_table, 11),
2693 linemap_position_for_column (line_table, 11),
2694 linemap_position_for_column (line_table, 15));
2696 /* Example where all the labels fit on one line. */
2698 text_range_label label0 ("0");
2699 text_range_label label1 ("1");
2700 text_range_label label2 ("2");
2701 gcc_rich_location richloc (foo, &label0);
2702 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2703 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2706 test_diagnostic_context dc;
2707 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2708 ASSERT_STREQ ("\n"
2709 " foo = bar.field;\n"
2710 " ^~~ ~~~ ~~~~~\n"
2711 " | | |\n"
2712 " 0 1 2\n",
2713 pp_formatted_text (dc.printer));
2716 /* Verify that we can disable label-printing. */
2718 test_diagnostic_context dc;
2719 dc.show_labels_p = false;
2720 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2721 ASSERT_STREQ ("\n"
2722 " foo = bar.field;\n"
2723 " ^~~ ~~~ ~~~~~\n",
2724 pp_formatted_text (dc.printer));
2728 /* Example where the labels need extra lines. */
2730 text_range_label label0 ("label 0");
2731 text_range_label label1 ("label 1");
2732 text_range_label label2 ("label 2");
2733 gcc_rich_location richloc (foo, &label0);
2734 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2735 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2737 test_diagnostic_context dc;
2738 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2739 ASSERT_STREQ ("\n"
2740 " foo = bar.field;\n"
2741 " ^~~ ~~~ ~~~~~\n"
2742 " | | |\n"
2743 " | | label 2\n"
2744 " | label 1\n"
2745 " label 0\n",
2746 pp_formatted_text (dc.printer));
2749 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2750 but label 1 just touches label 2. */
2752 text_range_label label0 ("aaaaa");
2753 text_range_label label1 ("bbbb");
2754 text_range_label label2 ("c");
2755 gcc_rich_location richloc (foo, &label0);
2756 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2757 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2759 test_diagnostic_context dc;
2760 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2761 ASSERT_STREQ ("\n"
2762 " foo = bar.field;\n"
2763 " ^~~ ~~~ ~~~~~\n"
2764 " | | |\n"
2765 " | | c\n"
2766 " aaaaa bbbb\n",
2767 pp_formatted_text (dc.printer));
2770 /* Example of out-of-order ranges (thus requiring a sort). */
2772 text_range_label label0 ("0");
2773 text_range_label label1 ("1");
2774 text_range_label label2 ("2");
2775 gcc_rich_location richloc (field, &label0);
2776 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2777 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2779 test_diagnostic_context dc;
2780 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2781 ASSERT_STREQ ("\n"
2782 " foo = bar.field;\n"
2783 " ~~~ ~~~ ^~~~~\n"
2784 " | | |\n"
2785 " 2 1 0\n",
2786 pp_formatted_text (dc.printer));
2789 /* Ensure we don't ICE if multiple ranges with labels are on
2790 the same point. */
2792 text_range_label label0 ("label 0");
2793 text_range_label label1 ("label 1");
2794 text_range_label label2 ("label 2");
2795 gcc_rich_location richloc (bar, &label0);
2796 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2797 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2799 test_diagnostic_context dc;
2800 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2801 ASSERT_STREQ ("\n"
2802 " foo = bar.field;\n"
2803 " ^~~\n"
2804 " |\n"
2805 " label 2\n"
2806 " label 1\n"
2807 " label 0\n",
2808 pp_formatted_text (dc.printer));
2811 /* Verify that a NULL result from range_label::get_text is
2812 handled gracefully. */
2814 text_range_label label (NULL);
2815 gcc_rich_location richloc (bar, &label);
2817 test_diagnostic_context dc;
2818 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2819 ASSERT_STREQ ("\n"
2820 " foo = bar.field;\n"
2821 " ^~~\n",
2822 pp_formatted_text (dc.printer));
2825 /* TODO: example of formatted printing (needs to be in
2826 gcc-rich-location.c due to Makefile.in issues). */
2829 /* Run the various one-liner tests. */
2831 static void
2832 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2834 /* Create a tempfile and write some text to it.
2835 ....................0000000001111111.
2836 ....................1234567890123456. */
2837 const char *content = "foo = bar.field;\n";
2838 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2839 line_table_test ltt (case_);
2841 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2843 location_t line_end = linemap_position_for_column (line_table, 16);
2845 /* Don't attempt to run the tests if column data might be unavailable. */
2846 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2847 return;
2849 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2850 ASSERT_EQ (1, LOCATION_LINE (line_end));
2851 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2853 test_one_liner_simple_caret ();
2854 test_one_liner_caret_and_range ();
2855 test_one_liner_multiple_carets_and_ranges ();
2856 test_one_liner_fixit_insert_before ();
2857 test_one_liner_fixit_insert_after ();
2858 test_one_liner_fixit_remove ();
2859 test_one_liner_fixit_replace ();
2860 test_one_liner_fixit_replace_non_equal_range ();
2861 test_one_liner_fixit_replace_equal_secondary_range ();
2862 test_one_liner_fixit_validation_adhoc_locations ();
2863 test_one_liner_many_fixits_1 ();
2864 test_one_liner_many_fixits_2 ();
2865 test_one_liner_labels ();
2868 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2870 static void
2871 test_add_location_if_nearby (const line_table_case &case_)
2873 /* Create a tempfile and write some text to it.
2874 ...000000000111111111122222222223333333333.
2875 ...123456789012345678901234567890123456789. */
2876 const char *content
2877 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2878 "struct different_line\n" /* line 2. */
2879 "{\n" /* line 3. */
2880 " double x;\n" /* line 4. */
2881 " double y;\n" /* line 5. */
2882 ";\n"); /* line 6. */
2883 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2884 line_table_test ltt (case_);
2886 const line_map_ordinary *ord_map
2887 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2888 tmp.get_filename (), 0));
2890 linemap_line_start (line_table, 1, 100);
2892 const location_t final_line_end
2893 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2895 /* Don't attempt to run the tests if column data might be unavailable. */
2896 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2897 return;
2899 /* Test of add_location_if_nearby on the same line as the
2900 primary location. */
2902 const location_t missing_close_brace_1_39
2903 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2904 const location_t matching_open_brace_1_18
2905 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2906 gcc_rich_location richloc (missing_close_brace_1_39);
2907 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2908 ASSERT_TRUE (added);
2909 ASSERT_EQ (2, richloc.get_num_locations ());
2910 test_diagnostic_context dc;
2911 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2912 ASSERT_STREQ ("\n"
2913 " struct same_line { double x; double y; ;\n"
2914 " ~ ^\n",
2915 pp_formatted_text (dc.printer));
2918 /* Test of add_location_if_nearby on a different line to the
2919 primary location. */
2921 const location_t missing_close_brace_6_1
2922 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2923 const location_t matching_open_brace_3_1
2924 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2925 gcc_rich_location richloc (missing_close_brace_6_1);
2926 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2927 ASSERT_FALSE (added);
2928 ASSERT_EQ (1, richloc.get_num_locations ());
2932 /* Verify that we print fixits even if they only affect lines
2933 outside those covered by the ranges in the rich_location. */
2935 static void
2936 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2938 /* Create a tempfile and write some text to it.
2939 ...000000000111111111122222222223333333333.
2940 ...123456789012345678901234567890123456789. */
2941 const char *content
2942 = ("struct point { double x; double y; };\n" /* line 1. */
2943 "struct point origin = {x: 0.0,\n" /* line 2. */
2944 " y\n" /* line 3. */
2945 "\n" /* line 4. */
2946 "\n" /* line 5. */
2947 " : 0.0};\n"); /* line 6. */
2948 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2949 line_table_test ltt (case_);
2951 const line_map_ordinary *ord_map
2952 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2953 tmp.get_filename (), 0));
2955 linemap_line_start (line_table, 1, 100);
2957 const location_t final_line_end
2958 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2960 /* Don't attempt to run the tests if column data might be unavailable. */
2961 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2962 return;
2964 /* A pair of tests for modernizing the initializers to C99-style. */
2966 /* The one-liner case (line 2). */
2968 test_diagnostic_context dc;
2969 const location_t x
2970 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2971 const location_t colon
2972 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2973 rich_location richloc (line_table, colon);
2974 richloc.add_fixit_insert_before (x, ".");
2975 richloc.add_fixit_replace (colon, "=");
2976 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2977 ASSERT_STREQ ("\n"
2978 " struct point origin = {x: 0.0,\n"
2979 " ^\n"
2980 " .=\n",
2981 pp_formatted_text (dc.printer));
2984 /* The multiline case. The caret for the rich_location is on line 6;
2985 verify that insertion fixit on line 3 is still printed (and that
2986 span starts are printed due to the gap between the span at line 3
2987 and that at line 6). */
2989 test_diagnostic_context dc;
2990 const location_t y
2991 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2992 const location_t colon
2993 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2994 rich_location richloc (line_table, colon);
2995 richloc.add_fixit_insert_before (y, ".");
2996 richloc.add_fixit_replace (colon, "=");
2997 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2998 ASSERT_STREQ ("\n"
2999 "FILENAME:3:24:\n"
3000 " y\n"
3001 " .\n"
3002 "FILENAME:6:25:\n"
3003 " : 0.0};\n"
3004 " ^\n"
3005 " =\n",
3006 pp_formatted_text (dc.printer));
3009 /* As above, but verify the behavior of multiple line spans
3010 with line-numbering enabled. */
3012 const location_t y
3013 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3014 const location_t colon
3015 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3016 rich_location richloc (line_table, colon);
3017 richloc.add_fixit_insert_before (y, ".");
3018 richloc.add_fixit_replace (colon, "=");
3019 test_diagnostic_context dc;
3020 dc.show_line_numbers_p = true;
3021 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3022 ASSERT_STREQ ("\n"
3023 " 3 | y\n"
3024 " | .\n"
3025 "....\n"
3026 " 6 | : 0.0};\n"
3027 " | ^\n"
3028 " | =\n",
3029 pp_formatted_text (dc.printer));
3034 /* Verify that fix-it hints are appropriately consolidated.
3036 If any fix-it hints in a rich_location involve locations beyond
3037 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3038 the fix-it as a whole, so there should be none.
3040 Otherwise, verify that consecutive "replace" and "remove" fix-its
3041 are merged, and that other fix-its remain separate. */
3043 static void
3044 test_fixit_consolidation (const line_table_case &case_)
3046 line_table_test ltt (case_);
3048 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3050 const location_t c10 = linemap_position_for_column (line_table, 10);
3051 const location_t c15 = linemap_position_for_column (line_table, 15);
3052 const location_t c16 = linemap_position_for_column (line_table, 16);
3053 const location_t c17 = linemap_position_for_column (line_table, 17);
3054 const location_t c20 = linemap_position_for_column (line_table, 20);
3055 const location_t c21 = linemap_position_for_column (line_table, 21);
3056 const location_t caret = c10;
3058 /* Insert + insert. */
3060 rich_location richloc (line_table, caret);
3061 richloc.add_fixit_insert_before (c10, "foo");
3062 richloc.add_fixit_insert_before (c15, "bar");
3064 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3065 /* Bogus column info for 2nd fixit, so no fixits. */
3066 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3067 else
3068 /* They should not have been merged. */
3069 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3072 /* Insert + replace. */
3074 rich_location richloc (line_table, caret);
3075 richloc.add_fixit_insert_before (c10, "foo");
3076 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3077 "bar");
3079 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3080 /* Bogus column info for 2nd fixit, so no fixits. */
3081 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3082 else
3083 /* They should not have been merged. */
3084 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3087 /* Replace + non-consecutive insert. */
3089 rich_location richloc (line_table, caret);
3090 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3091 "bar");
3092 richloc.add_fixit_insert_before (c17, "foo");
3094 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3095 /* Bogus column info for 2nd fixit, so no fixits. */
3096 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3097 else
3098 /* They should not have been merged. */
3099 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3102 /* Replace + non-consecutive replace. */
3104 rich_location richloc (line_table, caret);
3105 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3106 "foo");
3107 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3108 "bar");
3110 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3111 /* Bogus column info for 2nd fixit, so no fixits. */
3112 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3113 else
3114 /* They should not have been merged. */
3115 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3118 /* Replace + consecutive replace. */
3120 rich_location richloc (line_table, caret);
3121 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3122 "foo");
3123 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3124 "bar");
3126 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3127 /* Bogus column info for 2nd fixit, so no fixits. */
3128 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3129 else
3131 /* They should have been merged into a single "replace". */
3132 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3133 const fixit_hint *hint = richloc.get_fixit_hint (0);
3134 ASSERT_STREQ ("foobar", hint->get_string ());
3135 ASSERT_EQ (c10, hint->get_start_loc ());
3136 ASSERT_EQ (c21, hint->get_next_loc ());
3140 /* Replace + consecutive removal. */
3142 rich_location richloc (line_table, caret);
3143 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3144 "foo");
3145 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3147 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3148 /* Bogus column info for 2nd fixit, so no fixits. */
3149 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3150 else
3152 /* They should have been merged into a single replace, with the
3153 range extended to cover that of the removal. */
3154 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3155 const fixit_hint *hint = richloc.get_fixit_hint (0);
3156 ASSERT_STREQ ("foo", hint->get_string ());
3157 ASSERT_EQ (c10, hint->get_start_loc ());
3158 ASSERT_EQ (c21, hint->get_next_loc ());
3162 /* Consecutive removals. */
3164 rich_location richloc (line_table, caret);
3165 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3166 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3168 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3169 /* Bogus column info for 2nd fixit, so no fixits. */
3170 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3171 else
3173 /* They should have been merged into a single "replace-with-empty". */
3174 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3175 const fixit_hint *hint = richloc.get_fixit_hint (0);
3176 ASSERT_STREQ ("", hint->get_string ());
3177 ASSERT_EQ (c10, hint->get_start_loc ());
3178 ASSERT_EQ (c21, hint->get_next_loc ());
3183 /* Verify that the line_corrections machinery correctly prints
3184 overlapping fixit-hints. */
3186 static void
3187 test_overlapped_fixit_printing (const line_table_case &case_)
3189 /* Create a tempfile and write some text to it.
3190 ...000000000111111111122222222223333333333.
3191 ...123456789012345678901234567890123456789. */
3192 const char *content
3193 = (" foo *f = (foo *)ptr->field;\n");
3194 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3195 line_table_test ltt (case_);
3197 const line_map_ordinary *ord_map
3198 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3199 tmp.get_filename (), 0));
3201 linemap_line_start (line_table, 1, 100);
3203 const location_t final_line_end
3204 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3206 /* Don't attempt to run the tests if column data might be unavailable. */
3207 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3208 return;
3210 /* A test for converting a C-style cast to a C++-style cast. */
3211 const location_t open_paren
3212 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3213 const location_t close_paren
3214 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3215 const location_t expr_start
3216 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3217 const location_t expr_finish
3218 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3219 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3221 /* Various examples of fix-it hints that aren't themselves consolidated,
3222 but for which the *printing* may need consolidation. */
3224 /* Example where 3 fix-it hints are printed as one. */
3226 test_diagnostic_context dc;
3227 rich_location richloc (line_table, expr);
3228 richloc.add_fixit_replace (open_paren, "const_cast<");
3229 richloc.add_fixit_replace (close_paren, "> (");
3230 richloc.add_fixit_insert_after (")");
3232 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3233 ASSERT_STREQ ("\n"
3234 " foo *f = (foo *)ptr->field;\n"
3235 " ^~~~~~~~~~\n"
3236 " -----------------\n"
3237 " const_cast<foo *> (ptr->field)\n",
3238 pp_formatted_text (dc.printer));
3240 /* Unit-test the line_corrections machinery. */
3241 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3242 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3243 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3244 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3245 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3246 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3247 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3248 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3249 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3250 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3252 /* Add each hint in turn to a line_corrections instance,
3253 and verify that they are consolidated into one correction instance
3254 as expected. */
3255 line_corrections lc (tmp.get_filename (), 1);
3257 /* The first replace hint by itself. */
3258 lc.add_hint (hint_0);
3259 ASSERT_EQ (1, lc.m_corrections.length ());
3260 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3261 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3262 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3264 /* After the second replacement hint, they are printed together
3265 as a replacement (along with the text between them). */
3266 lc.add_hint (hint_1);
3267 ASSERT_EQ (1, lc.m_corrections.length ());
3268 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3269 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3270 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3272 /* After the final insertion hint, they are all printed together
3273 as a replacement (along with the text between them). */
3274 lc.add_hint (hint_2);
3275 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3276 lc.m_corrections[0]->m_text);
3277 ASSERT_EQ (1, lc.m_corrections.length ());
3278 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3279 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3282 /* Example where two are consolidated during printing. */
3284 test_diagnostic_context dc;
3285 rich_location richloc (line_table, expr);
3286 richloc.add_fixit_replace (open_paren, "CAST (");
3287 richloc.add_fixit_replace (close_paren, ") (");
3288 richloc.add_fixit_insert_after (")");
3290 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3291 ASSERT_STREQ ("\n"
3292 " foo *f = (foo *)ptr->field;\n"
3293 " ^~~~~~~~~~\n"
3294 " -\n"
3295 " CAST (-\n"
3296 " ) ( )\n",
3297 pp_formatted_text (dc.printer));
3300 /* Example where none are consolidated during printing. */
3302 test_diagnostic_context dc;
3303 rich_location richloc (line_table, expr);
3304 richloc.add_fixit_replace (open_paren, "CST (");
3305 richloc.add_fixit_replace (close_paren, ") (");
3306 richloc.add_fixit_insert_after (")");
3308 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3309 ASSERT_STREQ ("\n"
3310 " foo *f = (foo *)ptr->field;\n"
3311 " ^~~~~~~~~~\n"
3312 " -\n"
3313 " CST ( -\n"
3314 " ) ( )\n",
3315 pp_formatted_text (dc.printer));
3318 /* Example of deletion fix-it hints. */
3320 test_diagnostic_context dc;
3321 rich_location richloc (line_table, expr);
3322 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3323 source_range victim = {open_paren, close_paren};
3324 richloc.add_fixit_remove (victim);
3326 /* This case is actually handled by fixit-consolidation,
3327 rather than by line_corrections. */
3328 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3330 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3331 ASSERT_STREQ ("\n"
3332 " foo *f = (foo *)ptr->field;\n"
3333 " ^~~~~~~~~~\n"
3334 " -------\n"
3335 " (bar *)\n",
3336 pp_formatted_text (dc.printer));
3339 /* Example of deletion fix-it hints that would overlap. */
3341 test_diagnostic_context dc;
3342 rich_location richloc (line_table, expr);
3343 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3344 source_range victim = {expr_start, expr_finish};
3345 richloc.add_fixit_remove (victim);
3347 /* These fixits are not consolidated. */
3348 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3350 /* But the corrections are. */
3351 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3352 ASSERT_STREQ ("\n"
3353 " foo *f = (foo *)ptr->field;\n"
3354 " ^~~~~~~~~~\n"
3355 " -----------------\n"
3356 " (longer *)(foo *)\n",
3357 pp_formatted_text (dc.printer));
3360 /* Example of insertion fix-it hints that would overlap. */
3362 test_diagnostic_context dc;
3363 rich_location richloc (line_table, expr);
3364 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3365 richloc.add_fixit_insert_after (close_paren, "TEST");
3367 /* The first insertion is long enough that if printed naively,
3368 it would overlap with the second.
3369 Verify that they are printed as a single replacement. */
3370 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3371 ASSERT_STREQ ("\n"
3372 " foo *f = (foo *)ptr->field;\n"
3373 " ^~~~~~~~~~\n"
3374 " -------\n"
3375 " LONGER THAN THE CAST(foo *)TEST\n",
3376 pp_formatted_text (dc.printer));
3380 /* Verify that the line_corrections machinery correctly prints
3381 overlapping fixit-hints that have been added in the wrong
3382 order.
3383 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3385 static void
3386 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3388 /* Create a tempfile and write some text to it.
3389 ...000000000111111111122222222223333333333.
3390 ...123456789012345678901234567890123456789. */
3391 const char *content
3392 = ("int a5[][0][0] = { 1, 2 };\n");
3393 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3394 line_table_test ltt (case_);
3396 const line_map_ordinary *ord_map
3397 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3398 tmp.get_filename (), 0));
3400 linemap_line_start (line_table, 1, 100);
3402 const location_t final_line_end
3403 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3405 /* Don't attempt to run the tests if column data might be unavailable. */
3406 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3407 return;
3409 const location_t col_1
3410 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3411 const location_t col_20
3412 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3413 const location_t col_21
3414 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3415 const location_t col_23
3416 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3417 const location_t col_25
3418 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3420 /* Two insertions, in the wrong order. */
3422 rich_location richloc (line_table, col_20);
3423 richloc.add_fixit_insert_before (col_23, "{");
3424 richloc.add_fixit_insert_before (col_21, "}");
3426 /* These fixits should be accepted; they can't be consolidated. */
3427 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3428 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3429 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3430 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3431 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3432 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3433 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3435 /* Verify that they're printed correctly. */
3436 test_diagnostic_context dc;
3437 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3438 ASSERT_STREQ ("\n"
3439 " int a5[][0][0] = { 1, 2 };\n"
3440 " ^\n"
3441 " } {\n",
3442 pp_formatted_text (dc.printer));
3445 /* Various overlapping insertions, some occurring "out of order"
3446 (reproducing the fix-it hints from PR c/81405). */
3448 test_diagnostic_context dc;
3449 rich_location richloc (line_table, col_20);
3451 richloc.add_fixit_insert_before (col_20, "{{");
3452 richloc.add_fixit_insert_before (col_21, "}}");
3453 richloc.add_fixit_insert_before (col_23, "{");
3454 richloc.add_fixit_insert_before (col_21, "}");
3455 richloc.add_fixit_insert_before (col_23, "{{");
3456 richloc.add_fixit_insert_before (col_25, "}");
3457 richloc.add_fixit_insert_before (col_21, "}");
3458 richloc.add_fixit_insert_before (col_1, "{");
3459 richloc.add_fixit_insert_before (col_25, "}");
3460 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3461 ASSERT_STREQ ("\n"
3462 " int a5[][0][0] = { 1, 2 };\n"
3463 " ^\n"
3464 " { -----\n"
3465 " {{1}}}}, {{{2 }}\n",
3466 pp_formatted_text (dc.printer));
3470 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3472 static void
3473 test_fixit_insert_containing_newline (const line_table_case &case_)
3475 /* Create a tempfile and write some text to it.
3476 .........................0000000001111111.
3477 .........................1234567890123456. */
3478 const char *old_content = (" case 'a':\n" /* line 1. */
3479 " x = a;\n" /* line 2. */
3480 " case 'b':\n" /* line 3. */
3481 " x = b;\n");/* line 4. */
3483 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3484 line_table_test ltt (case_);
3485 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3487 location_t case_start = linemap_position_for_column (line_table, 5);
3488 location_t case_finish = linemap_position_for_column (line_table, 13);
3489 location_t case_loc = make_location (case_start, case_start, case_finish);
3490 location_t line_start = linemap_position_for_column (line_table, 1);
3492 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3493 return;
3495 /* Add a "break;" on a line by itself before line 3 i.e. before
3496 column 1 of line 3. */
3498 rich_location richloc (line_table, case_loc);
3499 richloc.add_fixit_insert_before (line_start, " break;\n");
3501 /* Without line numbers. */
3503 test_diagnostic_context dc;
3504 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3505 ASSERT_STREQ ("\n"
3506 " x = a;\n"
3507 "+ break;\n"
3508 " case 'b':\n"
3509 " ^~~~~~~~~\n",
3510 pp_formatted_text (dc.printer));
3513 /* With line numbers. */
3515 test_diagnostic_context dc;
3516 dc.show_line_numbers_p = true;
3517 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3518 ASSERT_STREQ ("\n"
3519 "2 | x = a;\n"
3520 "+ |+ break;\n"
3521 "3 | case 'b':\n"
3522 " | ^~~~~~~~~\n",
3523 pp_formatted_text (dc.printer));
3527 /* Verify that attempts to add text with a newline fail when the
3528 insertion point is *not* at the start of a line. */
3530 rich_location richloc (line_table, case_loc);
3531 richloc.add_fixit_insert_before (case_start, "break;\n");
3532 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3533 test_diagnostic_context dc;
3534 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3535 ASSERT_STREQ ("\n"
3536 " case 'b':\n"
3537 " ^~~~~~~~~\n",
3538 pp_formatted_text (dc.printer));
3542 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3543 of the file, where the fix-it is printed in a different line-span
3544 to the primary range of the diagnostic. */
3546 static void
3547 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3549 /* Create a tempfile and write some text to it.
3550 .........................0000000001111111.
3551 .........................1234567890123456. */
3552 const char *old_content = ("test (int ch)\n" /* line 1. */
3553 "{\n" /* line 2. */
3554 " putchar (ch);\n" /* line 3. */
3555 "}\n"); /* line 4. */
3557 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3558 line_table_test ltt (case_);
3560 const line_map_ordinary *ord_map = linemap_check_ordinary
3561 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3562 linemap_line_start (line_table, 1, 100);
3564 /* The primary range is the "putchar" token. */
3565 location_t putchar_start
3566 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3567 location_t putchar_finish
3568 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3569 location_t putchar_loc
3570 = make_location (putchar_start, putchar_start, putchar_finish);
3571 rich_location richloc (line_table, putchar_loc);
3573 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3574 location_t file_start
3575 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3576 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3578 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3579 return;
3582 test_diagnostic_context dc;
3583 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3584 ASSERT_STREQ ("\n"
3585 "FILENAME:1:1:\n"
3586 "+#include <stdio.h>\n"
3587 " test (int ch)\n"
3588 "FILENAME:3:2:\n"
3589 " putchar (ch);\n"
3590 " ^~~~~~~\n",
3591 pp_formatted_text (dc.printer));
3594 /* With line-numbering, the line spans are close enough to be
3595 consolidated, since it makes little sense to skip line 2. */
3597 test_diagnostic_context dc;
3598 dc.show_line_numbers_p = true;
3599 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3600 ASSERT_STREQ ("\n"
3601 "+ |+#include <stdio.h>\n"
3602 "1 | test (int ch)\n"
3603 "2 | {\n"
3604 "3 | putchar (ch);\n"
3605 " | ^~~~~~~\n",
3606 pp_formatted_text (dc.printer));
3610 /* Replacement fix-it hint containing a newline.
3611 This will fail, as newlines are only supported when inserting at the
3612 beginning of a line. */
3614 static void
3615 test_fixit_replace_containing_newline (const line_table_case &case_)
3617 /* Create a tempfile and write some text to it.
3618 .........................0000000001111.
3619 .........................1234567890123. */
3620 const char *old_content = "foo = bar ();\n";
3622 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3623 line_table_test ltt (case_);
3624 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3626 /* Replace the " = " with "\n = ", as if we were reformatting an
3627 overly long line. */
3628 location_t start = linemap_position_for_column (line_table, 4);
3629 location_t finish = linemap_position_for_column (line_table, 6);
3630 location_t loc = linemap_position_for_column (line_table, 13);
3631 rich_location richloc (line_table, loc);
3632 source_range range = source_range::from_locations (start, finish);
3633 richloc.add_fixit_replace (range, "\n =");
3635 /* Arbitrary newlines are not yet supported within fix-it hints, so
3636 the fix-it should not be displayed. */
3637 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3639 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3640 return;
3642 test_diagnostic_context dc;
3643 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3644 ASSERT_STREQ ("\n"
3645 " foo = bar ();\n"
3646 " ^\n",
3647 pp_formatted_text (dc.printer));
3650 /* Fix-it hint, attempting to delete a newline.
3651 This will fail, as we currently only support fix-it hints that
3652 affect one line at a time. */
3654 static void
3655 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3657 /* Create a tempfile and write some text to it.
3658 ..........................0000000001111.
3659 ..........................1234567890123. */
3660 const char *old_content = ("foo = bar (\n"
3661 " );\n");
3663 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3664 line_table_test ltt (case_);
3665 const line_map_ordinary *ord_map = linemap_check_ordinary
3666 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3667 linemap_line_start (line_table, 1, 100);
3669 /* Attempt to delete the " (\n...)". */
3670 location_t start
3671 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3672 location_t caret
3673 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3674 location_t finish
3675 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3676 location_t loc = make_location (caret, start, finish);
3677 rich_location richloc (line_table, loc);
3678 richloc. add_fixit_remove ();
3680 /* Fix-it hints that affect more than one line are not yet supported, so
3681 the fix-it should not be displayed. */
3682 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3684 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3685 return;
3687 test_diagnostic_context dc;
3688 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3689 ASSERT_STREQ ("\n"
3690 " foo = bar (\n"
3691 " ~^\n"
3692 " );\n"
3693 " ~ \n",
3694 pp_formatted_text (dc.printer));
3697 /* Verify that line numbers are correctly printed for the case of
3698 a multiline range in which the width of the line numbers changes
3699 (e.g. from "9" to "10"). */
3701 static void
3702 test_line_numbers_multiline_range ()
3704 /* Create a tempfile and write some text to it. */
3705 pretty_printer pp;
3706 for (int i = 0; i < 20; i++)
3707 /* .........0000000001111111.
3708 .............1234567890123456. */
3709 pp_printf (&pp, "this is line %i\n", i + 1);
3710 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3711 line_table_test ltt;
3713 const line_map_ordinary *ord_map = linemap_check_ordinary
3714 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3715 linemap_line_start (line_table, 1, 100);
3717 /* Create a multi-line location, starting at the "line" of line 9, with
3718 a caret on the "is" of line 10, finishing on the "this" line 11. */
3720 location_t start
3721 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3722 location_t caret
3723 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3724 location_t finish
3725 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3726 location_t loc = make_location (caret, start, finish);
3728 test_diagnostic_context dc;
3729 dc.show_line_numbers_p = true;
3730 gcc_rich_location richloc (loc);
3731 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3732 ASSERT_STREQ ("\n"
3733 " 9 | this is line 9\n"
3734 " | ~~~~~~\n"
3735 "10 | this is line 10\n"
3736 " | ~~~~~^~~~~~~~~~\n"
3737 "11 | this is line 11\n"
3738 " | ~~~~ \n",
3739 pp_formatted_text (dc.printer));
3742 /* Run all of the selftests within this file. */
3744 void
3745 diagnostic_show_locus_c_tests ()
3747 test_line_span ();
3748 test_num_digits ();
3750 test_layout_range_for_single_point ();
3751 test_layout_range_for_single_line ();
3752 test_layout_range_for_multiple_lines ();
3754 test_get_line_width_without_trailing_whitespace ();
3756 test_diagnostic_show_locus_unknown_location ();
3758 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3759 for_each_line_table_case (test_add_location_if_nearby);
3760 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3761 for_each_line_table_case (test_fixit_consolidation);
3762 for_each_line_table_case (test_overlapped_fixit_printing);
3763 for_each_line_table_case (test_overlapped_fixit_printing_2);
3764 for_each_line_table_case (test_fixit_insert_containing_newline);
3765 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3766 for_each_line_table_case (test_fixit_replace_containing_newline);
3767 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3769 test_line_numbers_multiline_range ();
3772 } // namespace selftest
3774 #endif /* #if CHECKING_P */