Fix build on sparc64-linux-gnu.
[official-gcc.git] / gcc / diagnostic-show-locus.c
bloba42ff819512ff1adbc170fbef5b91f47de2a4e93
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2018 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
34 #ifdef HAVE_TERMIOS_H
35 # include <termios.h>
36 #endif
38 #ifdef GWINSZ_IN_SYS_IOCTL
39 # include <sys/ioctl.h>
40 #endif
42 /* Classes for rendering source code and diagnostics, within an
43 anonymous namespace.
44 The work is done by "class layout", which embeds and uses
45 "class colorizer" and "class layout_range" to get things done. */
47 namespace {
49 /* The state at a given point of the source code, assuming that we're
50 in a range: which range are we in, and whether we should draw a caret at
51 this point. */
53 struct point_state
55 int range_idx;
56 bool draw_caret_p;
59 /* A class to inject colorization codes when printing the diagnostic locus.
61 It has one kind of colorization for each of:
62 - normal text
63 - range 0 (the "primary location")
64 - range 1
65 - range 2
67 The class caches the lookup of the color codes for the above.
69 The class also has responsibility for tracking which of the above is
70 active, filtering out unnecessary changes. This allows
71 layout::print_source_line and layout::print_annotation_line
72 to simply request a colorization code for *every* character they print,
73 via this class, and have the filtering be done for them here. */
75 class colorizer
77 public:
78 colorizer (diagnostic_context *context,
79 diagnostic_t diagnostic_kind);
80 ~colorizer ();
82 void set_range (int range_idx) { set_state (range_idx); }
83 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
84 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
85 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
87 private:
88 void set_state (int state);
89 void begin_state (int state);
90 void finish_state (int state);
91 const char *get_color_by_name (const char *);
93 private:
94 static const int STATE_NORMAL_TEXT = -1;
95 static const int STATE_FIXIT_INSERT = -2;
96 static const int STATE_FIXIT_DELETE = -3;
98 diagnostic_context *m_context;
99 diagnostic_t m_diagnostic_kind;
100 int m_current_state;
101 const char *m_range1;
102 const char *m_range2;
103 const char *m_fixit_insert;
104 const char *m_fixit_delete;
105 const char *m_stop_color;
108 /* A point within a layout_range; similar to an expanded_location,
109 but after filtering on file. */
111 class layout_point
113 public:
114 layout_point (const expanded_location &exploc)
115 : m_line (exploc.line),
116 m_column (exploc.column) {}
118 linenum_type m_line;
119 int m_column;
122 /* A class for use by "class layout" below: a filtered location_range. */
124 class layout_range
126 public:
127 layout_range (const expanded_location *start_exploc,
128 const expanded_location *finish_exploc,
129 enum range_display_kind range_display_kind,
130 const expanded_location *caret_exploc,
131 unsigned original_idx,
132 const range_label *label);
134 bool contains_point (linenum_type row, int column) const;
135 bool intersects_line_p (linenum_type row) const;
137 layout_point m_start;
138 layout_point m_finish;
139 enum range_display_kind m_range_display_kind;
140 layout_point m_caret;
141 unsigned m_original_idx;
142 const range_label *m_label;
145 /* A struct for use by layout::print_source_line for telling
146 layout::print_annotation_line the extents of the source line that
147 it printed, so that underlines can be clipped appropriately. */
149 struct line_bounds
151 int m_first_non_ws;
152 int m_last_non_ws;
155 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
156 or "line 23"). During the layout ctor, layout::calculate_line_spans
157 splits the pertinent source lines into a list of disjoint line_span
158 instances (e.g. lines 5-10, lines 15-20, line 23). */
160 struct line_span
162 line_span (linenum_type first_line, linenum_type last_line)
163 : m_first_line (first_line), m_last_line (last_line)
165 gcc_assert (first_line <= last_line);
167 linenum_type get_first_line () const { return m_first_line; }
168 linenum_type get_last_line () const { return m_last_line; }
170 bool contains_line_p (linenum_type line) const
172 return line >= m_first_line && line <= m_last_line;
175 static int comparator (const void *p1, const void *p2)
177 const line_span *ls1 = (const line_span *)p1;
178 const line_span *ls2 = (const line_span *)p2;
179 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
180 if (first_line_cmp)
181 return first_line_cmp;
182 return compare (ls1->m_last_line, ls2->m_last_line);
185 linenum_type m_first_line;
186 linenum_type m_last_line;
189 #if CHECKING_P
191 /* Selftests for line_span. */
193 static void
194 test_line_span ()
196 line_span line_one (1, 1);
197 ASSERT_EQ (1, line_one.get_first_line ());
198 ASSERT_EQ (1, line_one.get_last_line ());
199 ASSERT_FALSE (line_one.contains_line_p (0));
200 ASSERT_TRUE (line_one.contains_line_p (1));
201 ASSERT_FALSE (line_one.contains_line_p (2));
203 line_span lines_1_to_3 (1, 3);
204 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
205 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
206 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
207 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
209 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
210 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
211 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
213 /* A linenum > 2^31. */
214 const linenum_type LARGEST_LINE = 0xffffffff;
215 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
216 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
217 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
219 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
220 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
223 #endif /* #if CHECKING_P */
225 /* A class to control the overall layout when printing a diagnostic.
227 The layout is determined within the constructor.
228 It is then printed by repeatedly calling the "print_source_line",
229 "print_annotation_line" and "print_any_fixits" methods.
231 We assume we have disjoint ranges. */
233 class layout
235 public:
236 layout (diagnostic_context *context,
237 rich_location *richloc,
238 diagnostic_t diagnostic_kind);
240 bool maybe_add_location_range (const location_range *loc_range,
241 unsigned original_idx,
242 bool restrict_to_current_line_spans);
244 int get_num_line_spans () const { return m_line_spans.length (); }
245 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
247 void print_gap_in_line_numbering ();
248 bool print_heading_for_line_span_index_p (int line_span_idx) const;
250 expanded_location get_expanded_location (const line_span *) const;
252 void print_line (linenum_type row);
254 private:
255 bool will_show_line_p (linenum_type row) const;
256 void print_leading_fixits (linenum_type row);
257 void print_source_line (linenum_type row, const char *line, int line_width,
258 line_bounds *lbounds_out);
259 bool should_print_annotation_line_p (linenum_type row) const;
260 void start_annotation_line (char margin_char = ' ') const;
261 void print_annotation_line (linenum_type row, const line_bounds lbounds);
262 void print_any_labels (linenum_type row);
263 void print_trailing_fixits (linenum_type row);
265 bool annotation_line_showed_range_p (linenum_type line, int start_column,
266 int finish_column) const;
267 void show_ruler (int max_column) const;
269 bool validate_fixit_hint_p (const fixit_hint *hint);
271 void calculate_line_spans ();
273 void print_newline ();
275 bool
276 get_state_at_point (/* Inputs. */
277 linenum_type row, int column,
278 int first_non_ws, int last_non_ws,
279 /* Outputs. */
280 point_state *out_state);
283 get_x_bound_for_row (linenum_type row, int caret_column,
284 int last_non_ws);
286 void
287 move_to_column (int *column, int dest_column, bool add_left_margin);
289 private:
290 diagnostic_context *m_context;
291 pretty_printer *m_pp;
292 location_t m_primary_loc;
293 expanded_location m_exploc;
294 colorizer m_colorizer;
295 bool m_colorize_source_p;
296 bool m_show_labels_p;
297 bool m_show_line_numbers_p;
298 auto_vec <layout_range> m_layout_ranges;
299 auto_vec <const fixit_hint *> m_fixit_hints;
300 auto_vec <line_span> m_line_spans;
301 int m_linenum_width;
302 int m_x_offset;
305 /* Implementation of "class colorizer". */
307 /* The constructor for "colorizer". Lookup and store color codes for the
308 different kinds of things we might need to print. */
310 colorizer::colorizer (diagnostic_context *context,
311 diagnostic_t diagnostic_kind) :
312 m_context (context),
313 m_diagnostic_kind (diagnostic_kind),
314 m_current_state (STATE_NORMAL_TEXT)
316 m_range1 = get_color_by_name ("range1");
317 m_range2 = get_color_by_name ("range2");
318 m_fixit_insert = get_color_by_name ("fixit-insert");
319 m_fixit_delete = get_color_by_name ("fixit-delete");
320 m_stop_color = colorize_stop (pp_show_color (context->printer));
323 /* The destructor for "colorize". If colorization is on, print a code to
324 turn it off. */
326 colorizer::~colorizer ()
328 finish_state (m_current_state);
331 /* Update state, printing color codes if necessary if there's a state
332 change. */
334 void
335 colorizer::set_state (int new_state)
337 if (m_current_state != new_state)
339 finish_state (m_current_state);
340 m_current_state = new_state;
341 begin_state (new_state);
345 /* Turn on any colorization for STATE. */
347 void
348 colorizer::begin_state (int state)
350 switch (state)
352 case STATE_NORMAL_TEXT:
353 break;
355 case STATE_FIXIT_INSERT:
356 pp_string (m_context->printer, m_fixit_insert);
357 break;
359 case STATE_FIXIT_DELETE:
360 pp_string (m_context->printer, m_fixit_delete);
361 break;
363 case 0:
364 /* Make range 0 be the same color as the "kind" text
365 (error vs warning vs note). */
366 pp_string
367 (m_context->printer,
368 colorize_start (pp_show_color (m_context->printer),
369 diagnostic_get_color_for_kind (m_diagnostic_kind)));
370 break;
372 case 1:
373 pp_string (m_context->printer, m_range1);
374 break;
376 case 2:
377 pp_string (m_context->printer, m_range2);
378 break;
380 default:
381 /* For ranges beyond 2, alternate between color 1 and color 2. */
383 gcc_assert (state > 2);
384 pp_string (m_context->printer,
385 state % 2 ? m_range1 : m_range2);
387 break;
391 /* Turn off any colorization for STATE. */
393 void
394 colorizer::finish_state (int state)
396 if (state != STATE_NORMAL_TEXT)
397 pp_string (m_context->printer, m_stop_color);
400 /* Get the color code for NAME (or the empty string if
401 colorization is disabled). */
403 const char *
404 colorizer::get_color_by_name (const char *name)
406 return colorize_start (pp_show_color (m_context->printer), name);
409 /* Implementation of class layout_range. */
411 /* The constructor for class layout_range.
412 Initialize various layout_point fields from expanded_location
413 equivalents; we've already filtered on file. */
415 layout_range::layout_range (const expanded_location *start_exploc,
416 const expanded_location *finish_exploc,
417 enum range_display_kind range_display_kind,
418 const expanded_location *caret_exploc,
419 unsigned original_idx,
420 const range_label *label)
421 : m_start (*start_exploc),
422 m_finish (*finish_exploc),
423 m_range_display_kind (range_display_kind),
424 m_caret (*caret_exploc),
425 m_original_idx (original_idx),
426 m_label (label)
430 /* Is (column, row) within the given range?
431 We've already filtered on the file.
433 Ranges are closed (both limits are within the range).
435 Example A: a single-line range:
436 start: (col=22, line=2)
437 finish: (col=38, line=2)
439 |00000011111111112222222222333333333344444444444
440 |34567890123456789012345678901234567890123456789
441 --+-----------------------------------------------
442 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
443 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
444 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
446 Example B: a multiline range with
447 start: (col=14, line=3)
448 finish: (col=08, line=5)
450 |00000011111111112222222222333333333344444444444
451 |34567890123456789012345678901234567890123456789
452 --+-----------------------------------------------
453 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
454 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
455 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
456 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
457 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
458 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
459 --+-----------------------------------------------
461 Legend:
462 - 'b' indicates a point *before* the range
463 - 'S' indicates the start of the range
464 - 'w' indicates a point within the range
465 - 'F' indicates the finish of the range (which is
466 within it).
467 - 'a' indicates a subsequent point *after* the range. */
469 bool
470 layout_range::contains_point (linenum_type row, int column) const
472 gcc_assert (m_start.m_line <= m_finish.m_line);
473 /* ...but the equivalent isn't true for the columns;
474 consider example B in the comment above. */
476 if (row < m_start.m_line)
477 /* Points before the first line of the range are
478 outside it (corresponding to line 01 in example A
479 and lines 01 and 02 in example B above). */
480 return false;
482 if (row == m_start.m_line)
483 /* On same line as start of range (corresponding
484 to line 02 in example A and line 03 in example B). */
486 if (column < m_start.m_column)
487 /* Points on the starting line of the range, but
488 before the column in which it begins. */
489 return false;
491 if (row < m_finish.m_line)
492 /* This is a multiline range; the point
493 is within it (corresponds to line 03 in example B
494 from column 14 onwards) */
495 return true;
496 else
498 /* This is a single-line range. */
499 gcc_assert (row == m_finish.m_line);
500 return column <= m_finish.m_column;
504 /* The point is in a line beyond that containing the
505 start of the range: lines 03 onwards in example A,
506 and lines 04 onwards in example B. */
507 gcc_assert (row > m_start.m_line);
509 if (row > m_finish.m_line)
510 /* The point is beyond the final line of the range
511 (lines 03 onwards in example A, and lines 06 onwards
512 in example B). */
513 return false;
515 if (row < m_finish.m_line)
517 /* The point is in a line that's fully within a multiline
518 range (e.g. line 04 in example B). */
519 gcc_assert (m_start.m_line < m_finish.m_line);
520 return true;
523 gcc_assert (row == m_finish.m_line);
525 return column <= m_finish.m_column;
528 /* Does this layout_range contain any part of line ROW? */
530 bool
531 layout_range::intersects_line_p (linenum_type row) const
533 gcc_assert (m_start.m_line <= m_finish.m_line);
534 if (row < m_start.m_line)
535 return false;
536 if (row > m_finish.m_line)
537 return false;
538 return true;
541 #if CHECKING_P
543 /* A helper function for testing layout_range. */
545 static layout_range
546 make_range (int start_line, int start_col, int end_line, int end_col)
548 const expanded_location start_exploc
549 = {"test.c", start_line, start_col, NULL, false};
550 const expanded_location finish_exploc
551 = {"test.c", end_line, end_col, NULL, false};
552 return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
553 &start_exploc, 0, NULL);
556 /* Selftests for layout_range::contains_point and
557 layout_range::intersects_line_p. */
559 /* Selftest for layout_range, where the layout_range
560 is a range with start==end i.e. a single point. */
562 static void
563 test_layout_range_for_single_point ()
565 layout_range point = make_range (7, 10, 7, 10);
567 /* Tests for layout_range::contains_point. */
569 /* Before the line. */
570 ASSERT_FALSE (point.contains_point (6, 1));
572 /* On the line, but before start. */
573 ASSERT_FALSE (point.contains_point (7, 9));
575 /* At the point. */
576 ASSERT_TRUE (point.contains_point (7, 10));
578 /* On the line, after the point. */
579 ASSERT_FALSE (point.contains_point (7, 11));
581 /* After the line. */
582 ASSERT_FALSE (point.contains_point (8, 1));
584 /* Tests for layout_range::intersects_line_p. */
585 ASSERT_FALSE (point.intersects_line_p (6));
586 ASSERT_TRUE (point.intersects_line_p (7));
587 ASSERT_FALSE (point.intersects_line_p (8));
590 /* Selftest for layout_range, where the layout_range
591 is the single-line range shown as "Example A" above. */
593 static void
594 test_layout_range_for_single_line ()
596 layout_range example_a = make_range (2, 22, 2, 38);
598 /* Tests for layout_range::contains_point. */
600 /* Before the line. */
601 ASSERT_FALSE (example_a.contains_point (1, 1));
603 /* On the line, but before start. */
604 ASSERT_FALSE (example_a.contains_point (2, 21));
606 /* On the line, at the start. */
607 ASSERT_TRUE (example_a.contains_point (2, 22));
609 /* On the line, within the range. */
610 ASSERT_TRUE (example_a.contains_point (2, 23));
612 /* On the line, at the end. */
613 ASSERT_TRUE (example_a.contains_point (2, 38));
615 /* On the line, after the end. */
616 ASSERT_FALSE (example_a.contains_point (2, 39));
618 /* After the line. */
619 ASSERT_FALSE (example_a.contains_point (2, 39));
621 /* Tests for layout_range::intersects_line_p. */
622 ASSERT_FALSE (example_a.intersects_line_p (1));
623 ASSERT_TRUE (example_a.intersects_line_p (2));
624 ASSERT_FALSE (example_a.intersects_line_p (3));
627 /* Selftest for layout_range, where the layout_range
628 is the multi-line range shown as "Example B" above. */
630 static void
631 test_layout_range_for_multiple_lines ()
633 layout_range example_b = make_range (3, 14, 5, 8);
635 /* Tests for layout_range::contains_point. */
637 /* Before first line. */
638 ASSERT_FALSE (example_b.contains_point (1, 1));
640 /* On the first line, but before start. */
641 ASSERT_FALSE (example_b.contains_point (3, 13));
643 /* At the start. */
644 ASSERT_TRUE (example_b.contains_point (3, 14));
646 /* On the first line, within the range. */
647 ASSERT_TRUE (example_b.contains_point (3, 15));
649 /* On an interior line.
650 The column number should not matter; try various boundary
651 values. */
652 ASSERT_TRUE (example_b.contains_point (4, 1));
653 ASSERT_TRUE (example_b.contains_point (4, 7));
654 ASSERT_TRUE (example_b.contains_point (4, 8));
655 ASSERT_TRUE (example_b.contains_point (4, 9));
656 ASSERT_TRUE (example_b.contains_point (4, 13));
657 ASSERT_TRUE (example_b.contains_point (4, 14));
658 ASSERT_TRUE (example_b.contains_point (4, 15));
660 /* On the final line, before the end. */
661 ASSERT_TRUE (example_b.contains_point (5, 7));
663 /* On the final line, at the end. */
664 ASSERT_TRUE (example_b.contains_point (5, 8));
666 /* On the final line, after the end. */
667 ASSERT_FALSE (example_b.contains_point (5, 9));
669 /* After the line. */
670 ASSERT_FALSE (example_b.contains_point (6, 1));
672 /* Tests for layout_range::intersects_line_p. */
673 ASSERT_FALSE (example_b.intersects_line_p (2));
674 ASSERT_TRUE (example_b.intersects_line_p (3));
675 ASSERT_TRUE (example_b.intersects_line_p (4));
676 ASSERT_TRUE (example_b.intersects_line_p (5));
677 ASSERT_FALSE (example_b.intersects_line_p (6));
680 #endif /* #if CHECKING_P */
682 /* Given a source line LINE of length LINE_WIDTH, determine the width
683 without any trailing whitespace. */
685 static int
686 get_line_width_without_trailing_whitespace (const char *line, int line_width)
688 int result = line_width;
689 while (result > 0)
691 char ch = line[result - 1];
692 if (ch == ' ' || ch == '\t' || ch == '\r')
693 result--;
694 else
695 break;
697 gcc_assert (result >= 0);
698 gcc_assert (result <= line_width);
699 gcc_assert (result == 0 ||
700 (line[result - 1] != ' '
701 && line[result -1] != '\t'
702 && line[result -1] != '\r'));
703 return result;
706 #if CHECKING_P
708 /* A helper function for testing get_line_width_without_trailing_whitespace. */
710 static void
711 assert_eq (const char *line, int expected_width)
713 int actual_value
714 = get_line_width_without_trailing_whitespace (line, strlen (line));
715 ASSERT_EQ (actual_value, expected_width);
718 /* Verify that get_line_width_without_trailing_whitespace is sane for
719 various inputs. It is not required to handle newlines. */
721 static void
722 test_get_line_width_without_trailing_whitespace ()
724 assert_eq ("", 0);
725 assert_eq (" ", 0);
726 assert_eq ("\t", 0);
727 assert_eq ("\r", 0);
728 assert_eq ("hello world", 11);
729 assert_eq ("hello world ", 11);
730 assert_eq ("hello world \t\t ", 11);
731 assert_eq ("hello world\r", 11);
734 #endif /* #if CHECKING_P */
736 /* Helper function for layout's ctor, for sanitizing locations relative
737 to the primary location within a diagnostic.
739 Compare LOC_A and LOC_B to see if it makes sense to print underlines
740 connecting their expanded locations. Doing so is only guaranteed to
741 make sense if the locations share the same macro expansion "history"
742 i.e. they can be traced through the same macro expansions, eventually
743 reaching an ordinary map.
745 This may be too strong a condition, but it effectively sanitizes
746 PR c++/70105, which has an example of printing an expression where the
747 final location of the expression is in a different macro, which
748 erroneously was leading to hundreds of lines of irrelevant source
749 being printed. */
751 static bool
752 compatible_locations_p (location_t loc_a, location_t loc_b)
754 if (IS_ADHOC_LOC (loc_a))
755 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
756 if (IS_ADHOC_LOC (loc_b))
757 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
759 /* If either location is one of the special locations outside of a
760 linemap, they are only compatible if they are equal. */
761 if (loc_a < RESERVED_LOCATION_COUNT
762 || loc_b < RESERVED_LOCATION_COUNT)
763 return loc_a == loc_b;
765 const line_map *map_a = linemap_lookup (line_table, loc_a);
766 linemap_assert (map_a);
768 const line_map *map_b = linemap_lookup (line_table, loc_b);
769 linemap_assert (map_b);
771 /* Are they within the same map? */
772 if (map_a == map_b)
774 /* Are both within the same macro expansion? */
775 if (linemap_macro_expansion_map_p (map_a))
777 /* Expand each location towards the spelling location, and
778 recurse. */
779 const line_map_macro *macro_map = linemap_check_macro (map_a);
780 source_location loc_a_toward_spelling
781 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
782 macro_map,
783 loc_a);
784 source_location loc_b_toward_spelling
785 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
786 macro_map,
787 loc_b);
788 return compatible_locations_p (loc_a_toward_spelling,
789 loc_b_toward_spelling);
792 /* Otherwise they are within the same ordinary map. */
793 return true;
795 else
797 /* Within different maps. */
799 /* If either is within a macro expansion, they are incompatible. */
800 if (linemap_macro_expansion_map_p (map_a)
801 || linemap_macro_expansion_map_p (map_b))
802 return false;
804 /* Within two different ordinary maps; they are compatible iff they
805 are in the same file. */
806 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
807 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
808 return ord_map_a->to_file == ord_map_b->to_file;
812 /* Comparator for sorting fix-it hints. */
814 static int
815 fixit_cmp (const void *p_a, const void *p_b)
817 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
818 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
819 return hint_a->get_start_loc () - hint_b->get_start_loc ();
822 /* Get the number of digits in the decimal representation
823 of VALUE. */
825 static int
826 num_digits (int value)
828 /* Perhaps simpler to use log10 for this, but doing it this way avoids
829 using floating point. */
830 gcc_assert (value >= 0);
832 if (value == 0)
833 return 1;
835 int digits = 0;
836 while (value > 0)
838 digits++;
839 value /= 10;
841 return digits;
845 #if CHECKING_P
847 /* Selftest for num_digits. */
849 static void
850 test_num_digits ()
852 ASSERT_EQ (1, num_digits (0));
853 ASSERT_EQ (1, num_digits (9));
854 ASSERT_EQ (2, num_digits (10));
855 ASSERT_EQ (2, num_digits (99));
856 ASSERT_EQ (3, num_digits (100));
857 ASSERT_EQ (3, num_digits (999));
858 ASSERT_EQ (4, num_digits (1000));
859 ASSERT_EQ (4, num_digits (9999));
860 ASSERT_EQ (5, num_digits (10000));
861 ASSERT_EQ (5, num_digits (99999));
862 ASSERT_EQ (6, num_digits (100000));
863 ASSERT_EQ (6, num_digits (999999));
864 ASSERT_EQ (7, num_digits (1000000));
865 ASSERT_EQ (7, num_digits (9999999));
866 ASSERT_EQ (8, num_digits (10000000));
867 ASSERT_EQ (8, num_digits (99999999));
870 #endif /* #if CHECKING_P */
872 /* Implementation of class layout. */
874 /* Constructor for class layout.
876 Filter the ranges from the rich_location to those that we can
877 sanely print, populating m_layout_ranges and m_fixit_hints.
878 Determine the range of lines that we will print, splitting them
879 up into an ordered list of disjoint spans of contiguous line numbers.
880 Determine m_x_offset, to ensure that the primary caret
881 will fit within the max_width provided by the diagnostic_context. */
883 layout::layout (diagnostic_context * context,
884 rich_location *richloc,
885 diagnostic_t diagnostic_kind)
886 : m_context (context),
887 m_pp (context->printer),
888 m_primary_loc (richloc->get_range (0)->m_loc),
889 m_exploc (richloc->get_expanded_location (0)),
890 m_colorizer (context, diagnostic_kind),
891 m_colorize_source_p (context->colorize_source_p),
892 m_show_labels_p (context->show_labels_p),
893 m_show_line_numbers_p (context->show_line_numbers_p),
894 m_layout_ranges (richloc->get_num_locations ()),
895 m_fixit_hints (richloc->get_num_fixit_hints ()),
896 m_line_spans (1 + richloc->get_num_locations ()),
897 m_linenum_width (0),
898 m_x_offset (0)
900 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
902 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
903 Ignore any ranges that are awkward to handle. */
904 const location_range *loc_range = richloc->get_range (idx);
905 maybe_add_location_range (loc_range, idx, false);
908 /* Populate m_fixit_hints, filtering to only those that are in the
909 same file. */
910 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
912 const fixit_hint *hint = richloc->get_fixit_hint (i);
913 if (validate_fixit_hint_p (hint))
914 m_fixit_hints.safe_push (hint);
917 /* Sort m_fixit_hints. */
918 m_fixit_hints.qsort (fixit_cmp);
920 /* Populate m_line_spans. */
921 calculate_line_spans ();
923 /* Determine m_linenum_width. */
924 gcc_assert (m_line_spans.length () > 0);
925 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
926 int highest_line = last_span->m_last_line;
927 if (highest_line < 0)
928 highest_line = 0;
929 m_linenum_width = num_digits (highest_line);
930 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
931 if (m_line_spans.length () > 1)
932 m_linenum_width = MAX (m_linenum_width, 3);
933 /* If there's a minimum margin width, apply it (subtracting 1 for the space
934 after the line number. */
935 m_linenum_width = MAX (m_linenum_width, context->min_margin_width - 1);
937 /* Adjust m_x_offset.
938 Center the primary caret to fit in max_width; all columns
939 will be adjusted accordingly. */
940 size_t max_width = m_context->caret_max_width;
941 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
942 if (line && (size_t)m_exploc.column <= line.length ())
944 size_t right_margin = CARET_LINE_MARGIN;
945 size_t column = m_exploc.column;
946 if (m_show_line_numbers_p)
947 column += m_linenum_width + 2;
948 right_margin = MIN (line.length () - column, right_margin);
949 right_margin = max_width - right_margin;
950 if (line.length () >= max_width && column > right_margin)
951 m_x_offset = column - right_margin;
952 gcc_assert (m_x_offset >= 0);
955 if (context->show_ruler_p)
956 show_ruler (m_x_offset + max_width);
959 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
960 those that we can sanely print.
962 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
963 (for use as extrinsic state by label ranges FIXME).
965 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
966 filtered against this layout instance's current line spans: it
967 will only be added if the location is fully within the lines
968 already specified by other locations.
970 Return true iff LOC_RANGE was added. */
972 bool
973 layout::maybe_add_location_range (const location_range *loc_range,
974 unsigned original_idx,
975 bool restrict_to_current_line_spans)
977 gcc_assert (loc_range);
979 /* Split the "range" into caret and range information. */
980 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
982 /* Expand the various locations. */
983 expanded_location start
984 = linemap_client_expand_location_to_spelling_point
985 (src_range.m_start, LOCATION_ASPECT_START);
986 expanded_location finish
987 = linemap_client_expand_location_to_spelling_point
988 (src_range.m_finish, LOCATION_ASPECT_FINISH);
989 expanded_location caret
990 = linemap_client_expand_location_to_spelling_point
991 (loc_range->m_loc, LOCATION_ASPECT_CARET);
993 /* If any part of the range isn't in the same file as the primary
994 location of this diagnostic, ignore the range. */
995 if (start.file != m_exploc.file)
996 return false;
997 if (finish.file != m_exploc.file)
998 return false;
999 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1000 if (caret.file != m_exploc.file)
1001 return false;
1003 /* Sanitize the caret location for non-primary ranges. */
1004 if (m_layout_ranges.length () > 0)
1005 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1006 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1007 /* Discard any non-primary ranges that can't be printed
1008 sanely relative to the primary location. */
1009 return false;
1011 /* Everything is now known to be in the correct source file,
1012 but it may require further sanitization. */
1013 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
1014 original_idx, loc_range->m_label);
1016 /* If we have a range that finishes before it starts (perhaps
1017 from something built via macro expansion), printing the
1018 range is likely to be nonsensical. Also, attempting to do so
1019 breaks assumptions within the printing code (PR c/68473).
1020 Similarly, don't attempt to print ranges if one or both ends
1021 of the range aren't sane to print relative to the
1022 primary location (PR c++/70105). */
1023 if (start.line > finish.line
1024 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1025 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1027 /* Is this the primary location? */
1028 if (m_layout_ranges.length () == 0)
1030 /* We want to print the caret for the primary location, but
1031 we must sanitize away m_start and m_finish. */
1032 ri.m_start = ri.m_caret;
1033 ri.m_finish = ri.m_caret;
1035 else
1036 /* This is a non-primary range; ignore it. */
1037 return false;
1040 /* Potentially filter to just the lines already specified by other
1041 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1042 The layout ctor doesn't use it, and can't because m_line_spans
1043 hasn't been set up at that point. */
1044 if (restrict_to_current_line_spans)
1046 if (!will_show_line_p (start.line))
1047 return false;
1048 if (!will_show_line_p (finish.line))
1049 return false;
1050 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1051 if (!will_show_line_p (caret.line))
1052 return false;
1055 /* Passed all the tests; add the range to m_layout_ranges so that
1056 it will be printed. */
1057 m_layout_ranges.safe_push (ri);
1058 return true;
1061 /* Return true iff ROW is within one of the line spans for this layout. */
1063 bool
1064 layout::will_show_line_p (linenum_type row) const
1066 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1067 line_span_idx++)
1069 const line_span *line_span = get_line_span (line_span_idx);
1070 if (line_span->contains_line_p (row))
1071 return true;
1073 return false;
1076 /* Print a line showing a gap in the line numbers, for showing the boundary
1077 between two line spans. */
1079 void
1080 layout::print_gap_in_line_numbering ()
1082 gcc_assert (m_show_line_numbers_p);
1084 for (int i = 0; i < m_linenum_width + 1; i++)
1085 pp_character (m_pp, '.');
1087 pp_newline (m_pp);
1090 /* Return true iff we should print a heading when starting the
1091 line span with the given index. */
1093 bool
1094 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1096 /* We print a heading for every change of line span, hence for every
1097 line span after the initial one. */
1098 if (line_span_idx > 0)
1099 return true;
1101 /* We also do it for the initial span if the primary location of the
1102 diagnostic is in a different span. */
1103 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1104 return true;
1106 return false;
1109 /* Get an expanded_location for the first location of interest within
1110 the given line_span.
1111 Used when printing a heading to indicate a new line span. */
1113 expanded_location
1114 layout::get_expanded_location (const line_span *line_span) const
1116 /* Whenever possible, use the caret location. */
1117 if (line_span->contains_line_p (m_exploc.line))
1118 return m_exploc;
1120 /* Otherwise, use the start of the first range that's present
1121 within the line_span. */
1122 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1124 const layout_range *lr = &m_layout_ranges[i];
1125 if (line_span->contains_line_p (lr->m_start.m_line))
1127 expanded_location exploc = m_exploc;
1128 exploc.line = lr->m_start.m_line;
1129 exploc.column = lr->m_start.m_column;
1130 return exploc;
1134 /* Otherwise, use the location of the first fixit-hint present within
1135 the line_span. */
1136 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1138 const fixit_hint *hint = m_fixit_hints[i];
1139 location_t loc = hint->get_start_loc ();
1140 expanded_location exploc = expand_location (loc);
1141 if (line_span->contains_line_p (exploc.line))
1142 return exploc;
1145 /* It should not be possible to have a line span that didn't
1146 contain any of the layout_range or fixit_hint instances. */
1147 gcc_unreachable ();
1148 return m_exploc;
1151 /* Determine if HINT is meaningful to print within this layout. */
1153 bool
1154 layout::validate_fixit_hint_p (const fixit_hint *hint)
1156 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1157 return false;
1158 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1159 return false;
1161 return true;
1164 /* Determine the range of lines affected by HINT.
1165 This assumes that HINT has already been filtered by
1166 validate_fixit_hint_p, and so affects the correct source file. */
1168 static line_span
1169 get_line_span_for_fixit_hint (const fixit_hint *hint)
1171 gcc_assert (hint);
1173 int start_line = LOCATION_LINE (hint->get_start_loc ());
1175 /* For line-insertion fix-it hints, add the previous line to the
1176 span, to give the user more context on the proposed change. */
1177 if (hint->ends_with_newline_p ())
1178 if (start_line > 1)
1179 start_line--;
1181 return line_span (start_line,
1182 LOCATION_LINE (hint->get_next_loc ()));
1185 /* We want to print the pertinent source code at a diagnostic. The
1186 rich_location can contain multiple locations. This will have been
1187 filtered into m_exploc (the caret for the primary location) and
1188 m_layout_ranges, for those ranges within the same source file.
1190 We will print a subset of the lines within the source file in question,
1191 as a collection of "spans" of lines.
1193 This function populates m_line_spans with an ordered, disjoint list of
1194 the line spans of interest.
1196 Printing a gap between line spans takes one line, so, when printing
1197 line numbers, we allow a gap of up to one line between spans when
1198 merging, since it makes more sense to print the source line rather than a
1199 "gap-in-line-numbering" line. When not printing line numbers, it's
1200 better to be more explicit about what's going on, so keeping them as
1201 separate spans is preferred.
1203 For example, if the primary range is on lines 8-10, with secondary ranges
1204 covering lines 5-6 and lines 13-15:
1207 005 |RANGE 1
1208 006 |RANGE 1
1210 008 |PRIMARY RANGE
1211 009 |PRIMARY CARET
1212 010 |PRIMARY RANGE
1215 013 |RANGE 2
1216 014 |RANGE 2
1217 015 |RANGE 2
1220 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1222 With line numbering off (with span headers), we want three spans: lines 5-6,
1223 lines 8-10, and lines 13-15. */
1225 void
1226 layout::calculate_line_spans ()
1228 /* This should only be called once, by the ctor. */
1229 gcc_assert (m_line_spans.length () == 0);
1231 /* Populate tmp_spans with individual spans, for each of
1232 m_exploc, and for m_layout_ranges. */
1233 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1234 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1235 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1237 const layout_range *lr = &m_layout_ranges[i];
1238 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1239 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1240 lr->m_finish.m_line));
1243 /* Also add spans for any fix-it hints, in case they cover other lines. */
1244 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1246 const fixit_hint *hint = m_fixit_hints[i];
1247 gcc_assert (hint);
1248 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1251 /* Sort them. */
1252 tmp_spans.qsort(line_span::comparator);
1254 /* Now iterate through tmp_spans, copying into m_line_spans, and
1255 combining where possible. */
1256 gcc_assert (tmp_spans.length () > 0);
1257 m_line_spans.safe_push (tmp_spans[0]);
1258 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1260 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1261 const line_span *next = &tmp_spans[i];
1262 gcc_assert (next->m_first_line >= current->m_first_line);
1263 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1264 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
1266 /* We can merge them. */
1267 if (next->m_last_line > current->m_last_line)
1268 current->m_last_line = next->m_last_line;
1270 else
1272 /* No merger possible. */
1273 m_line_spans.safe_push (*next);
1277 /* Verify the result, in m_line_spans. */
1278 gcc_assert (m_line_spans.length () > 0);
1279 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1281 const line_span *prev = &m_line_spans[i - 1];
1282 const line_span *next = &m_line_spans[i];
1283 /* The individual spans must be sane. */
1284 gcc_assert (prev->m_first_line <= prev->m_last_line);
1285 gcc_assert (next->m_first_line <= next->m_last_line);
1286 /* The spans must be ordered. */
1287 gcc_assert (prev->m_first_line < next->m_first_line);
1288 /* There must be a gap of at least one line between separate spans. */
1289 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1293 /* Print line ROW of source code, potentially colorized at any ranges, and
1294 populate *LBOUNDS_OUT.
1295 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1296 is its width. */
1298 void
1299 layout::print_source_line (linenum_type row, const char *line, int line_width,
1300 line_bounds *lbounds_out)
1302 m_colorizer.set_normal_text ();
1304 /* We will stop printing the source line at any trailing
1305 whitespace. */
1306 line_width = get_line_width_without_trailing_whitespace (line,
1307 line_width);
1308 line += m_x_offset;
1310 if (m_show_line_numbers_p)
1312 int width = num_digits (row);
1313 for (int i = 0; i < m_linenum_width - width; i++)
1314 pp_space (m_pp);
1315 pp_printf (m_pp, "%i | ", row);
1317 else
1318 pp_space (m_pp);
1319 int first_non_ws = INT_MAX;
1320 int last_non_ws = 0;
1321 int column;
1322 for (column = 1 + m_x_offset; column <= line_width; column++)
1324 /* Assuming colorization is enabled for the caret and underline
1325 characters, we may also colorize the associated characters
1326 within the source line.
1328 For frontends that generate range information, we color the
1329 associated characters in the source line the same as the
1330 carets and underlines in the annotation line, to make it easier
1331 for the reader to see the pertinent code.
1333 For frontends that only generate carets, we don't colorize the
1334 characters above them, since this would look strange (e.g.
1335 colorizing just the first character in a token). */
1336 if (m_colorize_source_p)
1338 bool in_range_p;
1339 point_state state;
1340 in_range_p = get_state_at_point (row, column,
1341 0, INT_MAX,
1342 &state);
1343 if (in_range_p)
1344 m_colorizer.set_range (state.range_idx);
1345 else
1346 m_colorizer.set_normal_text ();
1348 char c = *line;
1349 if (c == '\0' || c == '\t' || c == '\r')
1350 c = ' ';
1351 if (c != ' ')
1353 last_non_ws = column;
1354 if (first_non_ws == INT_MAX)
1355 first_non_ws = column;
1357 pp_character (m_pp, c);
1358 line++;
1360 print_newline ();
1362 lbounds_out->m_first_non_ws = first_non_ws;
1363 lbounds_out->m_last_non_ws = last_non_ws;
1366 /* Determine if we should print an annotation line for ROW.
1367 i.e. if any of m_layout_ranges contains ROW. */
1369 bool
1370 layout::should_print_annotation_line_p (linenum_type row) const
1372 layout_range *range;
1373 int i;
1374 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1376 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1377 return false;
1378 if (range->intersects_line_p (row))
1379 return true;
1381 return false;
1384 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1385 margin, which is empty for annotation lines. Otherwise, do nothing. */
1387 void
1388 layout::start_annotation_line (char margin_char) const
1390 if (m_show_line_numbers_p)
1392 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1393 of it, right-aligned, padded with spaces. */
1394 int i;
1395 for (i = 0; i < m_linenum_width - 3; i++)
1396 pp_space (m_pp);
1397 for (; i < m_linenum_width; i++)
1398 pp_character (m_pp, margin_char);
1399 pp_string (m_pp, " |");
1403 /* Print a line consisting of the caret/underlines for the given
1404 source line. */
1406 void
1407 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1409 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1410 lbounds.m_last_non_ws);
1412 start_annotation_line ();
1413 pp_space (m_pp);
1415 for (int column = 1 + m_x_offset; column < x_bound; column++)
1417 bool in_range_p;
1418 point_state state;
1419 in_range_p = get_state_at_point (row, column,
1420 lbounds.m_first_non_ws,
1421 lbounds.m_last_non_ws,
1422 &state);
1423 if (in_range_p)
1425 /* Within a range. Draw either the caret or an underline. */
1426 m_colorizer.set_range (state.range_idx);
1427 if (state.draw_caret_p)
1429 /* Draw the caret. */
1430 char caret_char;
1431 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1432 caret_char = m_context->caret_chars[state.range_idx];
1433 else
1434 caret_char = '^';
1435 pp_character (m_pp, caret_char);
1437 else
1438 pp_character (m_pp, '~');
1440 else
1442 /* Not in a range. */
1443 m_colorizer.set_normal_text ();
1444 pp_character (m_pp, ' ');
1447 print_newline ();
1450 /* Implementation detail of layout::print_any_labels.
1452 A label within the given row of source. */
1454 struct line_label
1456 line_label (int state_idx, int column, label_text text)
1457 : m_state_idx (state_idx), m_column (column),
1458 m_text (text), m_length (strlen (text.m_buffer)),
1459 m_label_line (0)
1462 /* Sorting is primarily by column, then by state index. */
1463 static int comparator (const void *p1, const void *p2)
1465 const line_label *ll1 = (const line_label *)p1;
1466 const line_label *ll2 = (const line_label *)p2;
1467 int column_cmp = compare (ll1->m_column, ll2->m_column);
1468 if (column_cmp)
1469 return column_cmp;
1470 return compare (ll1->m_state_idx, ll2->m_state_idx);
1473 int m_state_idx;
1474 int m_column;
1475 label_text m_text;
1476 size_t m_length;
1477 int m_label_line;
1480 /* Print any labels in this row. */
1481 void
1482 layout::print_any_labels (linenum_type row)
1484 int i;
1485 auto_vec<line_label> labels;
1487 /* Gather the labels that are to be printed into "labels". */
1489 layout_range *range;
1490 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1492 /* Most ranges don't have labels, so reject this first. */
1493 if (range->m_label == NULL)
1494 continue;
1496 /* The range's caret must be on this line. */
1497 if (range->m_caret.m_line != row)
1498 continue;
1500 /* Reject labels that aren't fully visible due to clipping
1501 by m_x_offset. */
1502 if (range->m_caret.m_column <= m_x_offset)
1503 continue;
1505 label_text text;
1506 text = range->m_label->get_text (range->m_original_idx);
1508 /* Allow for labels that return NULL from their get_text
1509 implementation (so e.g. such labels can control their own
1510 visibility). */
1511 if (text.m_buffer == NULL)
1512 continue;
1514 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1518 /* Bail out if there are no labels on this row. */
1519 if (labels.length () == 0)
1520 return;
1522 /* Sort them. */
1523 labels.qsort(line_label::comparator);
1525 /* Figure out how many "label lines" we need, and which
1526 one each label is printed in.
1528 For example, if the labels aren't too densely packed,
1529 we can fit them on the same line, giving two "label lines":
1531 foo + bar
1532 ~~~ ~~~
1533 | | : label line 0
1534 l0 l1 : label line 1
1536 If they would touch each other or overlap, then we need
1537 additional "label lines":
1539 foo + bar
1540 ~~~ ~~~
1541 | | : label line 0
1542 | label 1 : label line 1
1543 label 0 : label line 2
1545 Place the final label on label line 1, and work backwards, adding
1546 label lines as needed.
1548 If multiple labels are at the same place, put them on separate
1549 label lines:
1551 foo + bar
1552 ^ : label line 0
1553 | : label line 1
1554 label 1 : label line 2
1555 label 0 : label line 3. */
1557 int max_label_line = 1;
1559 int next_column = INT_MAX;
1560 line_label *label;
1561 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1563 /* Would this label "touch" or overlap the next label? */
1564 if (label->m_column + label->m_length >= (size_t)next_column)
1565 max_label_line++;
1567 label->m_label_line = max_label_line;
1568 next_column = label->m_column;
1572 /* Print the "label lines". For each label within the line, print
1573 either a vertical bar ('|') for the labels that are lower down, or the
1574 labels themselves once we've reached their line. */
1576 /* Keep track of in which column we last printed a vertical bar.
1577 This allows us to suppress duplicate vertical bars for the case
1578 where multiple labels are on one column. */
1579 int last_vbar = 0;
1580 for (int label_line = 0; label_line <= max_label_line; label_line++)
1582 start_annotation_line ();
1583 pp_space (m_pp);
1584 int column = 1 + m_x_offset;
1585 line_label *label;
1586 FOR_EACH_VEC_ELT (labels, i, label)
1588 if (label_line > label->m_label_line)
1589 /* We've printed all the labels for this label line. */
1590 break;
1592 if (label_line == label->m_label_line)
1594 gcc_assert (column <= label->m_column);
1595 move_to_column (&column, label->m_column, true);
1596 m_colorizer.set_range (label->m_state_idx);
1597 pp_string (m_pp, label->m_text.m_buffer);
1598 m_colorizer.set_normal_text ();
1599 column += label->m_length;
1601 else if (label->m_column != last_vbar)
1603 gcc_assert (column <= label->m_column);
1604 move_to_column (&column, label->m_column, true);
1605 m_colorizer.set_range (label->m_state_idx);
1606 pp_character (m_pp, '|');
1607 m_colorizer.set_normal_text ();
1608 last_vbar = column;
1609 column++;
1612 print_newline ();
1616 /* Clean up. */
1618 line_label *label;
1619 FOR_EACH_VEC_ELT (labels, i, label)
1620 label->m_text.maybe_free ();
1624 /* If there are any fixit hints inserting new lines before source line ROW,
1625 print them.
1627 They are printed on lines of their own, before the source line
1628 itself, with a leading '+'. */
1630 void
1631 layout::print_leading_fixits (linenum_type row)
1633 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1635 const fixit_hint *hint = m_fixit_hints[i];
1637 if (!hint->ends_with_newline_p ())
1638 /* Not a newline fixit; print it in print_trailing_fixits. */
1639 continue;
1641 gcc_assert (hint->insertion_p ());
1643 if (hint->affects_line_p (m_exploc.file, row))
1645 /* Printing the '+' with normal colorization
1646 and the inserted line with "insert" colorization
1647 helps them stand out from each other, and from
1648 the surrounding text. */
1649 m_colorizer.set_normal_text ();
1650 start_annotation_line ('+');
1651 pp_character (m_pp, '+');
1652 m_colorizer.set_fixit_insert ();
1653 /* Print all but the trailing newline of the fix-it hint.
1654 We have to print the newline separately to avoid
1655 getting additional pp prefixes printed. */
1656 for (size_t i = 0; i < hint->get_length () - 1; i++)
1657 pp_character (m_pp, hint->get_string ()[i]);
1658 m_colorizer.set_normal_text ();
1659 pp_newline (m_pp);
1664 /* Subroutine of layout::print_trailing_fixits.
1666 Determine if the annotation line printed for LINE contained
1667 the exact range from START_COLUMN to FINISH_COLUMN. */
1669 bool
1670 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1671 int finish_column) const
1673 layout_range *range;
1674 int i;
1675 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1676 if (range->m_start.m_line == line
1677 && range->m_start.m_column == start_column
1678 && range->m_finish.m_line == line
1679 && range->m_finish.m_column == finish_column)
1680 return true;
1681 return false;
1684 /* Classes for printing trailing fix-it hints i.e. those that
1685 don't add new lines.
1687 For insertion, these can look like:
1689 new_text
1691 For replacement, these can look like:
1693 ------------- : underline showing affected range
1694 new_text
1696 For deletion, these can look like:
1698 ------------- : underline showing affected range
1700 This can become confusing if they overlap, and so we need
1701 to do some preprocessing to decide what to print.
1702 We use the list of fixit_hint instances affecting the line
1703 to build a list of "correction" instances, and print the
1704 latter.
1706 For example, consider a set of fix-its for converting
1707 a C-style cast to a C++ const_cast.
1709 Given:
1711 ..000000000111111111122222222223333333333.
1712 ..123456789012345678901234567890123456789.
1713 foo *f = (foo *)ptr->field;
1714 ^~~~~
1716 and the fix-it hints:
1717 - replace col 10 (the open paren) with "const_cast<"
1718 - replace col 16 (the close paren) with "> ("
1719 - insert ")" before col 27
1721 then we would get odd-looking output:
1723 foo *f = (foo *)ptr->field;
1724 ^~~~~
1726 const_cast<
1728 > ( )
1730 It would be better to detect when fixit hints are going to
1731 overlap (those that require new lines), and to consolidate
1732 the printing of such fixits, giving something like:
1734 foo *f = (foo *)ptr->field;
1735 ^~~~~
1736 -----------------
1737 const_cast<foo *> (ptr->field)
1739 This works by detecting when the printing would overlap, and
1740 effectively injecting no-op replace hints into the gaps between
1741 such fix-its, so that the printing joins up.
1743 In the above example, the overlap of:
1744 - replace col 10 (the open paren) with "const_cast<"
1745 and:
1746 - replace col 16 (the close paren) with "> ("
1747 is fixed by injecting a no-op:
1748 - replace cols 11-15 with themselves ("foo *")
1749 and consolidating these, making:
1750 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1751 i.e.:
1752 - replace cols 10-16 with "const_cast<foo *> ("
1754 This overlaps with the final fix-it hint:
1755 - insert ")" before col 27
1756 and so we repeat the consolidation process, by injecting
1757 a no-op:
1758 - replace cols 17-26 with themselves ("ptr->field")
1759 giving:
1760 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1761 i.e.:
1762 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1764 and is thus printed as desired. */
1766 /* A range of columns within a line. */
1768 struct column_range
1770 column_range (int start_, int finish_) : start (start_), finish (finish_)
1772 /* We must have either a range, or an insertion. */
1773 gcc_assert (start <= finish || finish == start - 1);
1776 bool operator== (const column_range &other) const
1778 return start == other.start && finish == other.finish;
1781 int start;
1782 int finish;
1785 /* Get the range of columns that HINT would affect. */
1787 static column_range
1788 get_affected_columns (const fixit_hint *hint)
1790 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1791 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1793 return column_range (start_column, finish_column);
1796 /* Get the range of columns that would be printed for HINT. */
1798 static column_range
1799 get_printed_columns (const fixit_hint *hint)
1801 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1802 int final_hint_column = start_column + hint->get_length () - 1;
1803 if (hint->insertion_p ())
1805 return column_range (start_column, final_hint_column);
1807 else
1809 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1811 return column_range (start_column,
1812 MAX (finish_column, final_hint_column));
1816 /* A correction on a particular line.
1817 This describes a plan for how to print one or more fixit_hint
1818 instances that affected the line, potentially consolidating hints
1819 into corrections to make the result easier for the user to read. */
1821 struct correction
1823 correction (column_range affected_columns,
1824 column_range printed_columns,
1825 const char *new_text, size_t new_text_len)
1826 : m_affected_columns (affected_columns),
1827 m_printed_columns (printed_columns),
1828 m_text (xstrdup (new_text)),
1829 m_len (new_text_len),
1830 m_alloc_sz (new_text_len + 1)
1834 ~correction () { free (m_text); }
1836 bool insertion_p () const
1838 return m_affected_columns.start == m_affected_columns.finish + 1;
1841 void ensure_capacity (size_t len);
1842 void ensure_terminated ();
1844 void overwrite (int dst_offset, const char_span &src_span)
1846 gcc_assert (dst_offset >= 0);
1847 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1848 memcpy (m_text + dst_offset, src_span.get_buffer (),
1849 src_span.length ());
1852 /* If insert, then start: the column before which the text
1853 is to be inserted, and finish is offset by the length of
1854 the replacement.
1855 If replace, then the range of columns affected. */
1856 column_range m_affected_columns;
1858 /* If insert, then start: the column before which the text
1859 is to be inserted, and finish is offset by the length of
1860 the replacement.
1861 If replace, then the range of columns affected. */
1862 column_range m_printed_columns;
1864 /* The text to be inserted/used as replacement. */
1865 char *m_text;
1866 size_t m_len;
1867 size_t m_alloc_sz;
1870 /* Ensure that m_text can hold a string of length LEN
1871 (plus 1 for 0-termination). */
1873 void
1874 correction::ensure_capacity (size_t len)
1876 /* Allow 1 extra byte for 0-termination. */
1877 if (m_alloc_sz < (len + 1))
1879 size_t new_alloc_sz = (len + 1) * 2;
1880 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1881 m_alloc_sz = new_alloc_sz;
1885 /* Ensure that m_text is 0-terminated. */
1887 void
1888 correction::ensure_terminated ()
1890 /* 0-terminate the buffer. */
1891 gcc_assert (m_len < m_alloc_sz);
1892 m_text[m_len] = '\0';
1895 /* A list of corrections affecting a particular line.
1896 This is used by layout::print_trailing_fixits for planning
1897 how to print the fix-it hints affecting the line. */
1899 struct line_corrections
1901 line_corrections (const char *filename, linenum_type row)
1902 : m_filename (filename), m_row (row)
1904 ~line_corrections ();
1906 void add_hint (const fixit_hint *hint);
1908 const char *m_filename;
1909 linenum_type m_row;
1910 auto_vec <correction *> m_corrections;
1913 /* struct line_corrections. */
1915 line_corrections::~line_corrections ()
1917 unsigned i;
1918 correction *c;
1919 FOR_EACH_VEC_ELT (m_corrections, i, c)
1920 delete c;
1923 /* A struct wrapping a particular source line, allowing
1924 run-time bounds-checking of accesses in a checked build. */
1926 struct source_line
1928 source_line (const char *filename, int line);
1930 char_span as_span () { return char_span (chars, width); }
1932 const char *chars;
1933 int width;
1936 /* source_line's ctor. */
1938 source_line::source_line (const char *filename, int line)
1940 char_span span = location_get_source_line (filename, line);
1941 chars = span.get_buffer ();
1942 width = span.length ();
1945 /* Add HINT to the corrections for this line.
1946 Attempt to consolidate nearby hints so that they will not
1947 overlap with printed. */
1949 void
1950 line_corrections::add_hint (const fixit_hint *hint)
1952 column_range affected_columns = get_affected_columns (hint);
1953 column_range printed_columns = get_printed_columns (hint);
1955 /* Potentially consolidate. */
1956 if (!m_corrections.is_empty ())
1958 correction *last_correction
1959 = m_corrections[m_corrections.length () - 1];
1961 /* The following consolidation code assumes that the fix-it hints
1962 have been sorted by start (done within layout's ctor). */
1963 gcc_assert (affected_columns.start
1964 >= last_correction->m_affected_columns.start);
1965 gcc_assert (printed_columns.start
1966 >= last_correction->m_printed_columns.start);
1968 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1970 /* We have two hints for which the printed forms of the hints
1971 would touch or overlap, so we need to consolidate them to avoid
1972 confusing the user.
1973 Attempt to inject a "replace" correction from immediately
1974 after the end of the last hint to immediately before the start
1975 of the next hint. */
1976 column_range between (last_correction->m_affected_columns.finish + 1,
1977 printed_columns.start - 1);
1979 /* Try to read the source. */
1980 source_line line (m_filename, m_row);
1981 if (line.chars && between.finish < line.width)
1983 /* Consolidate into the last correction:
1984 add a no-op "replace" of the "between" text, and
1985 add the text from the new hint. */
1986 int old_len = last_correction->m_len;
1987 gcc_assert (old_len >= 0);
1988 int between_len = between.finish + 1 - between.start;
1989 gcc_assert (between_len >= 0);
1990 int new_len = old_len + between_len + hint->get_length ();
1991 gcc_assert (new_len >= 0);
1992 last_correction->ensure_capacity (new_len);
1993 last_correction->overwrite
1994 (old_len,
1995 line.as_span ().subspan (between.start - 1,
1996 between.finish + 1 - between.start));
1997 last_correction->overwrite (old_len + between_len,
1998 char_span (hint->get_string (),
1999 hint->get_length ()));
2000 last_correction->m_len = new_len;
2001 last_correction->ensure_terminated ();
2002 last_correction->m_affected_columns.finish
2003 = affected_columns.finish;
2004 last_correction->m_printed_columns.finish
2005 += between_len + hint->get_length ();
2006 return;
2011 /* If no consolidation happened, add a new correction instance. */
2012 m_corrections.safe_push (new correction (affected_columns,
2013 printed_columns,
2014 hint->get_string (),
2015 hint->get_length ()));
2018 /* If there are any fixit hints on source line ROW, print them.
2019 They are printed in order, attempting to combine them onto lines, but
2020 starting new lines if necessary.
2021 Fix-it hints that insert new lines are handled separately,
2022 in layout::print_leading_fixits. */
2024 void
2025 layout::print_trailing_fixits (linenum_type row)
2027 /* Build a list of correction instances for the line,
2028 potentially consolidating hints (for the sake of readability). */
2029 line_corrections corrections (m_exploc.file, row);
2030 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2032 const fixit_hint *hint = m_fixit_hints[i];
2034 /* Newline fixits are handled by layout::print_leading_fixits. */
2035 if (hint->ends_with_newline_p ())
2036 continue;
2038 if (hint->affects_line_p (m_exploc.file, row))
2039 corrections.add_hint (hint);
2042 /* Now print the corrections. */
2043 unsigned i;
2044 correction *c;
2045 int column = m_x_offset;
2047 if (!corrections.m_corrections.is_empty ())
2048 start_annotation_line ();
2050 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2052 /* For now we assume each fixit hint can only touch one line. */
2053 if (c->insertion_p ())
2055 /* This assumes the insertion just affects one line. */
2056 int start_column = c->m_printed_columns.start;
2057 move_to_column (&column, start_column, true);
2058 m_colorizer.set_fixit_insert ();
2059 pp_string (m_pp, c->m_text);
2060 m_colorizer.set_normal_text ();
2061 column += c->m_len;
2063 else
2065 /* If the range of the replacement wasn't printed in the
2066 annotation line, then print an extra underline to
2067 indicate exactly what is being replaced.
2068 Always show it for removals. */
2069 int start_column = c->m_affected_columns.start;
2070 int finish_column = c->m_affected_columns.finish;
2071 if (!annotation_line_showed_range_p (row, start_column,
2072 finish_column)
2073 || c->m_len == 0)
2075 move_to_column (&column, start_column, true);
2076 m_colorizer.set_fixit_delete ();
2077 for (; column <= finish_column; column++)
2078 pp_character (m_pp, '-');
2079 m_colorizer.set_normal_text ();
2081 /* Print the replacement text. REPLACE also covers
2082 removals, so only do this extra work (potentially starting
2083 a new line) if we have actual replacement text. */
2084 if (c->m_len > 0)
2086 move_to_column (&column, start_column, true);
2087 m_colorizer.set_fixit_insert ();
2088 pp_string (m_pp, c->m_text);
2089 m_colorizer.set_normal_text ();
2090 column += c->m_len;
2095 /* Add a trailing newline, if necessary. */
2096 move_to_column (&column, 0, false);
2099 /* Disable any colorization and emit a newline. */
2101 void
2102 layout::print_newline ()
2104 m_colorizer.set_normal_text ();
2105 pp_newline (m_pp);
2108 /* Return true if (ROW/COLUMN) is within a range of the layout.
2109 If it returns true, OUT_STATE is written to, with the
2110 range index, and whether we should draw the caret at
2111 (ROW/COLUMN) (as opposed to an underline). */
2113 bool
2114 layout::get_state_at_point (/* Inputs. */
2115 linenum_type row, int column,
2116 int first_non_ws, int last_non_ws,
2117 /* Outputs. */
2118 point_state *out_state)
2120 layout_range *range;
2121 int i;
2122 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2124 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2125 /* Bail out early, so that such ranges don't affect underlining or
2126 source colorization. */
2127 continue;
2129 if (range->contains_point (row, column))
2131 out_state->range_idx = i;
2133 /* Are we at the range's caret? is it visible? */
2134 out_state->draw_caret_p = false;
2135 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2136 && row == range->m_caret.m_line
2137 && column == range->m_caret.m_column)
2138 out_state->draw_caret_p = true;
2140 /* Within a multiline range, don't display any underline
2141 in any leading or trailing whitespace on a line.
2142 We do display carets, however. */
2143 if (!out_state->draw_caret_p)
2144 if (column < first_non_ws || column > last_non_ws)
2145 return false;
2147 /* We are within a range. */
2148 return true;
2152 return false;
2155 /* Helper function for use by layout::print_line when printing the
2156 annotation line under the source line.
2157 Get the column beyond the rightmost one that could contain a caret or
2158 range marker, given that we stop rendering at trailing whitespace.
2159 ROW is the source line within the given file.
2160 CARET_COLUMN is the column of range 0's caret.
2161 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2162 character of source (as determined when printing the source line). */
2165 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2166 int last_non_ws_column)
2168 int result = caret_column + 1;
2170 layout_range *range;
2171 int i;
2172 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2174 if (row >= range->m_start.m_line)
2176 if (range->m_finish.m_line == row)
2178 /* On the final line within a range; ensure that
2179 we render up to the end of the range. */
2180 if (result <= range->m_finish.m_column)
2181 result = range->m_finish.m_column + 1;
2183 else if (row < range->m_finish.m_line)
2185 /* Within a multiline range; ensure that we render up to the
2186 last non-whitespace column. */
2187 if (result <= last_non_ws_column)
2188 result = last_non_ws_column + 1;
2193 return result;
2196 /* Given *COLUMN as an x-coordinate, print spaces to position
2197 successive output at DEST_COLUMN, printing a newline if necessary,
2198 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2199 left margin after any newline. */
2201 void
2202 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2204 /* Start a new line if we need to. */
2205 if (*column > dest_column)
2207 print_newline ();
2208 if (add_left_margin)
2209 start_annotation_line ();
2210 *column = m_x_offset;
2213 while (*column < dest_column)
2215 pp_space (m_pp);
2216 (*column)++;
2220 /* For debugging layout issues, render a ruler giving column numbers
2221 (after the 1-column indent). */
2223 void
2224 layout::show_ruler (int max_column) const
2226 /* Hundreds. */
2227 if (max_column > 99)
2229 start_annotation_line ();
2230 pp_space (m_pp);
2231 for (int column = 1 + m_x_offset; column <= max_column; column++)
2232 if (column % 10 == 0)
2233 pp_character (m_pp, '0' + (column / 100) % 10);
2234 else
2235 pp_space (m_pp);
2236 pp_newline (m_pp);
2239 /* Tens. */
2240 start_annotation_line ();
2241 pp_space (m_pp);
2242 for (int column = 1 + m_x_offset; column <= max_column; column++)
2243 if (column % 10 == 0)
2244 pp_character (m_pp, '0' + (column / 10) % 10);
2245 else
2246 pp_space (m_pp);
2247 pp_newline (m_pp);
2249 /* Units. */
2250 start_annotation_line ();
2251 pp_space (m_pp);
2252 for (int column = 1 + m_x_offset; column <= max_column; column++)
2253 pp_character (m_pp, '0' + (column % 10));
2254 pp_newline (m_pp);
2257 /* Print leading fix-its (for new lines inserted before the source line)
2258 then the source line, followed by an annotation line
2259 consisting of any caret/underlines, then any fixits.
2260 If the source line can't be read, print nothing. */
2261 void
2262 layout::print_line (linenum_type row)
2264 char_span line = location_get_source_line (m_exploc.file, row);
2265 if (!line)
2266 return;
2268 line_bounds lbounds;
2269 print_leading_fixits (row);
2270 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2271 if (should_print_annotation_line_p (row))
2272 print_annotation_line (row, lbounds);
2273 if (m_show_labels_p)
2274 print_any_labels (row);
2275 print_trailing_fixits (row);
2278 } /* End of anonymous namespace. */
2280 /* If LOC is within the spans of lines that will already be printed for
2281 this gcc_rich_location, then add it as a secondary location and return true.
2283 Otherwise return false. */
2285 bool
2286 gcc_rich_location::add_location_if_nearby (location_t loc)
2288 /* Use the layout location-handling logic to sanitize LOC,
2289 filtering it to the current line spans within a temporary
2290 layout instance. */
2291 layout layout (global_dc, this, DK_ERROR);
2292 location_range loc_range;
2293 loc_range.m_loc = loc;
2294 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2295 if (!layout.maybe_add_location_range (&loc_range, 0, true))
2296 return false;
2298 add_range (loc);
2299 return true;
2302 /* Print the physical source code corresponding to the location of
2303 this diagnostic, with additional annotations. */
2305 void
2306 diagnostic_show_locus (diagnostic_context * context,
2307 rich_location *richloc,
2308 diagnostic_t diagnostic_kind)
2310 pp_newline (context->printer);
2312 location_t loc = richloc->get_loc ();
2313 /* Do nothing if source-printing has been disabled. */
2314 if (!context->show_caret)
2315 return;
2317 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2318 if (loc <= BUILTINS_LOCATION)
2319 return;
2321 /* Don't print the same source location twice in a row, unless we have
2322 fix-it hints. */
2323 if (loc == context->last_location
2324 && richloc->get_num_fixit_hints () == 0)
2325 return;
2327 context->last_location = loc;
2329 char *saved_prefix = pp_take_prefix (context->printer);
2330 pp_set_prefix (context->printer, NULL);
2332 layout layout (context, richloc, diagnostic_kind);
2333 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2334 line_span_idx++)
2336 const line_span *line_span = layout.get_line_span (line_span_idx);
2337 if (context->show_line_numbers_p)
2339 /* With line numbers, we should show whenever the line-numbering
2340 "jumps". */
2341 if (line_span_idx > 0)
2342 layout.print_gap_in_line_numbering ();
2344 else
2346 /* Without line numbers, we print headings for some line spans. */
2347 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2349 expanded_location exploc
2350 = layout.get_expanded_location (line_span);
2351 context->start_span (context, exploc);
2354 linenum_type last_line = line_span->get_last_line ();
2355 for (linenum_type row = line_span->get_first_line ();
2356 row <= last_line; row++)
2357 layout.print_line (row);
2360 pp_set_prefix (context->printer, saved_prefix);
2363 #if CHECKING_P
2365 namespace selftest {
2367 /* Selftests for diagnostic_show_locus. */
2369 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2371 static void
2372 test_diagnostic_show_locus_unknown_location ()
2374 test_diagnostic_context dc;
2375 rich_location richloc (line_table, UNKNOWN_LOCATION);
2376 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2377 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2380 /* Verify that diagnostic_show_locus works sanely for various
2381 single-line cases.
2383 All of these work on the following 1-line source file:
2384 .0000000001111111
2385 .1234567890123456
2386 "foo = bar.field;\n"
2387 which is set up by test_diagnostic_show_locus_one_liner and calls
2388 them. */
2390 /* Just a caret. */
2392 static void
2393 test_one_liner_simple_caret ()
2395 test_diagnostic_context dc;
2396 location_t caret = linemap_position_for_column (line_table, 10);
2397 rich_location richloc (line_table, caret);
2398 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2399 ASSERT_STREQ ("\n"
2400 " foo = bar.field;\n"
2401 " ^\n",
2402 pp_formatted_text (dc.printer));
2405 /* Caret and range. */
2407 static void
2408 test_one_liner_caret_and_range ()
2410 test_diagnostic_context dc;
2411 location_t caret = linemap_position_for_column (line_table, 10);
2412 location_t start = linemap_position_for_column (line_table, 7);
2413 location_t finish = linemap_position_for_column (line_table, 15);
2414 location_t loc = make_location (caret, start, finish);
2415 rich_location richloc (line_table, loc);
2416 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2417 ASSERT_STREQ ("\n"
2418 " foo = bar.field;\n"
2419 " ~~~^~~~~~\n",
2420 pp_formatted_text (dc.printer));
2423 /* Multiple ranges and carets. */
2425 static void
2426 test_one_liner_multiple_carets_and_ranges ()
2428 test_diagnostic_context dc;
2429 location_t foo
2430 = make_location (linemap_position_for_column (line_table, 2),
2431 linemap_position_for_column (line_table, 1),
2432 linemap_position_for_column (line_table, 3));
2433 dc.caret_chars[0] = 'A';
2435 location_t bar
2436 = make_location (linemap_position_for_column (line_table, 8),
2437 linemap_position_for_column (line_table, 7),
2438 linemap_position_for_column (line_table, 9));
2439 dc.caret_chars[1] = 'B';
2441 location_t field
2442 = make_location (linemap_position_for_column (line_table, 13),
2443 linemap_position_for_column (line_table, 11),
2444 linemap_position_for_column (line_table, 15));
2445 dc.caret_chars[2] = 'C';
2447 rich_location richloc (line_table, foo);
2448 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2449 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2450 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2451 ASSERT_STREQ ("\n"
2452 " foo = bar.field;\n"
2453 " ~A~ ~B~ ~~C~~\n",
2454 pp_formatted_text (dc.printer));
2457 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2459 static void
2460 test_one_liner_fixit_insert_before ()
2462 test_diagnostic_context dc;
2463 location_t caret = linemap_position_for_column (line_table, 7);
2464 rich_location richloc (line_table, caret);
2465 richloc.add_fixit_insert_before ("&");
2466 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2467 ASSERT_STREQ ("\n"
2468 " foo = bar.field;\n"
2469 " ^\n"
2470 " &\n",
2471 pp_formatted_text (dc.printer));
2474 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2476 static void
2477 test_one_liner_fixit_insert_after ()
2479 test_diagnostic_context dc;
2480 location_t start = linemap_position_for_column (line_table, 1);
2481 location_t finish = linemap_position_for_column (line_table, 3);
2482 location_t foo = make_location (start, start, finish);
2483 rich_location richloc (line_table, foo);
2484 richloc.add_fixit_insert_after ("[0]");
2485 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2486 ASSERT_STREQ ("\n"
2487 " foo = bar.field;\n"
2488 " ^~~\n"
2489 " [0]\n",
2490 pp_formatted_text (dc.printer));
2493 /* Removal fix-it hint: removal of the ".field". */
2495 static void
2496 test_one_liner_fixit_remove ()
2498 test_diagnostic_context dc;
2499 location_t start = linemap_position_for_column (line_table, 10);
2500 location_t finish = linemap_position_for_column (line_table, 15);
2501 location_t dot = make_location (start, start, finish);
2502 rich_location richloc (line_table, dot);
2503 richloc.add_fixit_remove ();
2504 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2505 ASSERT_STREQ ("\n"
2506 " foo = bar.field;\n"
2507 " ^~~~~~\n"
2508 " ------\n",
2509 pp_formatted_text (dc.printer));
2512 /* Replace fix-it hint: replacing "field" with "m_field". */
2514 static void
2515 test_one_liner_fixit_replace ()
2517 test_diagnostic_context dc;
2518 location_t start = linemap_position_for_column (line_table, 11);
2519 location_t finish = linemap_position_for_column (line_table, 15);
2520 location_t field = make_location (start, start, finish);
2521 rich_location richloc (line_table, field);
2522 richloc.add_fixit_replace ("m_field");
2523 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2524 ASSERT_STREQ ("\n"
2525 " foo = bar.field;\n"
2526 " ^~~~~\n"
2527 " m_field\n",
2528 pp_formatted_text (dc.printer));
2531 /* Replace fix-it hint: replacing "field" with "m_field",
2532 but where the caret was elsewhere. */
2534 static void
2535 test_one_liner_fixit_replace_non_equal_range ()
2537 test_diagnostic_context dc;
2538 location_t equals = linemap_position_for_column (line_table, 5);
2539 location_t start = linemap_position_for_column (line_table, 11);
2540 location_t finish = linemap_position_for_column (line_table, 15);
2541 rich_location richloc (line_table, equals);
2542 source_range range;
2543 range.m_start = start;
2544 range.m_finish = finish;
2545 richloc.add_fixit_replace (range, "m_field");
2546 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2547 /* The replacement range is not indicated in the annotation line, so
2548 it should be indicated via an additional underline. */
2549 ASSERT_STREQ ("\n"
2550 " foo = bar.field;\n"
2551 " ^\n"
2552 " -----\n"
2553 " m_field\n",
2554 pp_formatted_text (dc.printer));
2557 /* Replace fix-it hint: replacing "field" with "m_field",
2558 where the caret was elsewhere, but where a secondary range
2559 exactly covers "field". */
2561 static void
2562 test_one_liner_fixit_replace_equal_secondary_range ()
2564 test_diagnostic_context dc;
2565 location_t equals = linemap_position_for_column (line_table, 5);
2566 location_t start = linemap_position_for_column (line_table, 11);
2567 location_t finish = linemap_position_for_column (line_table, 15);
2568 rich_location richloc (line_table, equals);
2569 location_t field = make_location (start, start, finish);
2570 richloc.add_range (field);
2571 richloc.add_fixit_replace (field, "m_field");
2572 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2573 /* The replacement range is indicated in the annotation line,
2574 so it shouldn't be indicated via an additional underline. */
2575 ASSERT_STREQ ("\n"
2576 " foo = bar.field;\n"
2577 " ^ ~~~~~\n"
2578 " m_field\n",
2579 pp_formatted_text (dc.printer));
2582 /* Verify that we can use ad-hoc locations when adding fixits to a
2583 rich_location. */
2585 static void
2586 test_one_liner_fixit_validation_adhoc_locations ()
2588 /* Generate a range that's too long to be packed, so must
2589 be stored as an ad-hoc location (given the defaults
2590 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2591 const location_t c7 = linemap_position_for_column (line_table, 7);
2592 const location_t c47 = linemap_position_for_column (line_table, 47);
2593 const location_t loc = make_location (c7, c7, c47);
2595 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2596 return;
2598 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2600 /* Insert. */
2602 rich_location richloc (line_table, loc);
2603 richloc.add_fixit_insert_before (loc, "test");
2604 /* It should not have been discarded by the validator. */
2605 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2607 test_diagnostic_context dc;
2608 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2609 ASSERT_STREQ ("\n"
2610 " foo = bar.field;\n"
2611 " ^~~~~~~~~~ \n"
2612 " test\n",
2613 pp_formatted_text (dc.printer));
2616 /* Remove. */
2618 rich_location richloc (line_table, loc);
2619 source_range range = source_range::from_locations (loc, c47);
2620 richloc.add_fixit_remove (range);
2621 /* It should not have been discarded by the validator. */
2622 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2624 test_diagnostic_context dc;
2625 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2626 ASSERT_STREQ ("\n"
2627 " foo = bar.field;\n"
2628 " ^~~~~~~~~~ \n"
2629 " -----------------------------------------\n",
2630 pp_formatted_text (dc.printer));
2633 /* Replace. */
2635 rich_location richloc (line_table, loc);
2636 source_range range = source_range::from_locations (loc, c47);
2637 richloc.add_fixit_replace (range, "test");
2638 /* It should not have been discarded by the validator. */
2639 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2641 test_diagnostic_context dc;
2642 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2643 ASSERT_STREQ ("\n"
2644 " foo = bar.field;\n"
2645 " ^~~~~~~~~~ \n"
2646 " test\n",
2647 pp_formatted_text (dc.printer));
2651 /* Test of consolidating insertions at the same location. */
2653 static void
2654 test_one_liner_many_fixits_1 ()
2656 test_diagnostic_context dc;
2657 location_t equals = linemap_position_for_column (line_table, 5);
2658 rich_location richloc (line_table, equals);
2659 for (int i = 0; i < 19; i++)
2660 richloc.add_fixit_insert_before ("a");
2661 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2662 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2663 ASSERT_STREQ ("\n"
2664 " foo = bar.field;\n"
2665 " ^\n"
2666 " aaaaaaaaaaaaaaaaaaa\n",
2667 pp_formatted_text (dc.printer));
2670 /* Ensure that we can add an arbitrary number of fix-it hints to a
2671 rich_location, even if they are not consolidated. */
2673 static void
2674 test_one_liner_many_fixits_2 ()
2676 test_diagnostic_context dc;
2677 location_t equals = linemap_position_for_column (line_table, 5);
2678 rich_location richloc (line_table, equals);
2679 for (int i = 0; i < 19; i++)
2681 location_t loc = linemap_position_for_column (line_table, i * 2);
2682 richloc.add_fixit_insert_before (loc, "a");
2684 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2686 ASSERT_STREQ ("\n"
2687 " foo = bar.field;\n"
2688 " ^\n"
2689 "a a a a a a a a a a a a a a a a a a a\n",
2690 pp_formatted_text (dc.printer));
2693 /* Test of labeling the ranges within a rich_location. */
2695 static void
2696 test_one_liner_labels ()
2698 location_t foo
2699 = make_location (linemap_position_for_column (line_table, 1),
2700 linemap_position_for_column (line_table, 1),
2701 linemap_position_for_column (line_table, 3));
2702 location_t bar
2703 = make_location (linemap_position_for_column (line_table, 7),
2704 linemap_position_for_column (line_table, 7),
2705 linemap_position_for_column (line_table, 9));
2706 location_t field
2707 = make_location (linemap_position_for_column (line_table, 11),
2708 linemap_position_for_column (line_table, 11),
2709 linemap_position_for_column (line_table, 15));
2711 /* Example where all the labels fit on one line. */
2713 text_range_label label0 ("0");
2714 text_range_label label1 ("1");
2715 text_range_label label2 ("2");
2716 gcc_rich_location richloc (foo, &label0);
2717 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2718 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2721 test_diagnostic_context dc;
2722 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2723 ASSERT_STREQ ("\n"
2724 " foo = bar.field;\n"
2725 " ^~~ ~~~ ~~~~~\n"
2726 " | | |\n"
2727 " 0 1 2\n",
2728 pp_formatted_text (dc.printer));
2731 /* Verify that we can disable label-printing. */
2733 test_diagnostic_context dc;
2734 dc.show_labels_p = false;
2735 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2736 ASSERT_STREQ ("\n"
2737 " foo = bar.field;\n"
2738 " ^~~ ~~~ ~~~~~\n",
2739 pp_formatted_text (dc.printer));
2743 /* Example where the labels need extra lines. */
2745 text_range_label label0 ("label 0");
2746 text_range_label label1 ("label 1");
2747 text_range_label label2 ("label 2");
2748 gcc_rich_location richloc (foo, &label0);
2749 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2750 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2752 test_diagnostic_context dc;
2753 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2754 ASSERT_STREQ ("\n"
2755 " foo = bar.field;\n"
2756 " ^~~ ~~~ ~~~~~\n"
2757 " | | |\n"
2758 " | | label 2\n"
2759 " | label 1\n"
2760 " label 0\n",
2761 pp_formatted_text (dc.printer));
2764 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2765 but label 1 just touches label 2. */
2767 text_range_label label0 ("aaaaa");
2768 text_range_label label1 ("bbbb");
2769 text_range_label label2 ("c");
2770 gcc_rich_location richloc (foo, &label0);
2771 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2772 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2774 test_diagnostic_context dc;
2775 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2776 ASSERT_STREQ ("\n"
2777 " foo = bar.field;\n"
2778 " ^~~ ~~~ ~~~~~\n"
2779 " | | |\n"
2780 " | | c\n"
2781 " aaaaa bbbb\n",
2782 pp_formatted_text (dc.printer));
2785 /* Example of out-of-order ranges (thus requiring a sort). */
2787 text_range_label label0 ("0");
2788 text_range_label label1 ("1");
2789 text_range_label label2 ("2");
2790 gcc_rich_location richloc (field, &label0);
2791 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2792 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2794 test_diagnostic_context dc;
2795 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2796 ASSERT_STREQ ("\n"
2797 " foo = bar.field;\n"
2798 " ~~~ ~~~ ^~~~~\n"
2799 " | | |\n"
2800 " 2 1 0\n",
2801 pp_formatted_text (dc.printer));
2804 /* Ensure we don't ICE if multiple ranges with labels are on
2805 the same point. */
2807 text_range_label label0 ("label 0");
2808 text_range_label label1 ("label 1");
2809 text_range_label label2 ("label 2");
2810 gcc_rich_location richloc (bar, &label0);
2811 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2812 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2814 test_diagnostic_context dc;
2815 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2816 ASSERT_STREQ ("\n"
2817 " foo = bar.field;\n"
2818 " ^~~\n"
2819 " |\n"
2820 " label 2\n"
2821 " label 1\n"
2822 " label 0\n",
2823 pp_formatted_text (dc.printer));
2826 /* Verify that a NULL result from range_label::get_text is
2827 handled gracefully. */
2829 text_range_label label (NULL);
2830 gcc_rich_location richloc (bar, &label);
2832 test_diagnostic_context dc;
2833 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2834 ASSERT_STREQ ("\n"
2835 " foo = bar.field;\n"
2836 " ^~~\n",
2837 pp_formatted_text (dc.printer));
2840 /* TODO: example of formatted printing (needs to be in
2841 gcc-rich-location.c due to Makefile.in issues). */
2844 /* Run the various one-liner tests. */
2846 static void
2847 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2849 /* Create a tempfile and write some text to it.
2850 ....................0000000001111111.
2851 ....................1234567890123456. */
2852 const char *content = "foo = bar.field;\n";
2853 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2854 line_table_test ltt (case_);
2856 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2858 location_t line_end = linemap_position_for_column (line_table, 16);
2860 /* Don't attempt to run the tests if column data might be unavailable. */
2861 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2862 return;
2864 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2865 ASSERT_EQ (1, LOCATION_LINE (line_end));
2866 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2868 test_one_liner_simple_caret ();
2869 test_one_liner_caret_and_range ();
2870 test_one_liner_multiple_carets_and_ranges ();
2871 test_one_liner_fixit_insert_before ();
2872 test_one_liner_fixit_insert_after ();
2873 test_one_liner_fixit_remove ();
2874 test_one_liner_fixit_replace ();
2875 test_one_liner_fixit_replace_non_equal_range ();
2876 test_one_liner_fixit_replace_equal_secondary_range ();
2877 test_one_liner_fixit_validation_adhoc_locations ();
2878 test_one_liner_many_fixits_1 ();
2879 test_one_liner_many_fixits_2 ();
2880 test_one_liner_labels ();
2883 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2885 static void
2886 test_add_location_if_nearby (const line_table_case &case_)
2888 /* Create a tempfile and write some text to it.
2889 ...000000000111111111122222222223333333333.
2890 ...123456789012345678901234567890123456789. */
2891 const char *content
2892 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2893 "struct different_line\n" /* line 2. */
2894 "{\n" /* line 3. */
2895 " double x;\n" /* line 4. */
2896 " double y;\n" /* line 5. */
2897 ";\n"); /* line 6. */
2898 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2899 line_table_test ltt (case_);
2901 const line_map_ordinary *ord_map
2902 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2903 tmp.get_filename (), 0));
2905 linemap_line_start (line_table, 1, 100);
2907 const location_t final_line_end
2908 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2910 /* Don't attempt to run the tests if column data might be unavailable. */
2911 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2912 return;
2914 /* Test of add_location_if_nearby on the same line as the
2915 primary location. */
2917 const location_t missing_close_brace_1_39
2918 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2919 const location_t matching_open_brace_1_18
2920 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2921 gcc_rich_location richloc (missing_close_brace_1_39);
2922 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2923 ASSERT_TRUE (added);
2924 ASSERT_EQ (2, richloc.get_num_locations ());
2925 test_diagnostic_context dc;
2926 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2927 ASSERT_STREQ ("\n"
2928 " struct same_line { double x; double y; ;\n"
2929 " ~ ^\n",
2930 pp_formatted_text (dc.printer));
2933 /* Test of add_location_if_nearby on a different line to the
2934 primary location. */
2936 const location_t missing_close_brace_6_1
2937 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2938 const location_t matching_open_brace_3_1
2939 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2940 gcc_rich_location richloc (missing_close_brace_6_1);
2941 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2942 ASSERT_FALSE (added);
2943 ASSERT_EQ (1, richloc.get_num_locations ());
2947 /* Verify that we print fixits even if they only affect lines
2948 outside those covered by the ranges in the rich_location. */
2950 static void
2951 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2953 /* Create a tempfile and write some text to it.
2954 ...000000000111111111122222222223333333333.
2955 ...123456789012345678901234567890123456789. */
2956 const char *content
2957 = ("struct point { double x; double y; };\n" /* line 1. */
2958 "struct point origin = {x: 0.0,\n" /* line 2. */
2959 " y\n" /* line 3. */
2960 "\n" /* line 4. */
2961 "\n" /* line 5. */
2962 " : 0.0};\n"); /* line 6. */
2963 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2964 line_table_test ltt (case_);
2966 const line_map_ordinary *ord_map
2967 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2968 tmp.get_filename (), 0));
2970 linemap_line_start (line_table, 1, 100);
2972 const location_t final_line_end
2973 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2975 /* Don't attempt to run the tests if column data might be unavailable. */
2976 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2977 return;
2979 /* A pair of tests for modernizing the initializers to C99-style. */
2981 /* The one-liner case (line 2). */
2983 test_diagnostic_context dc;
2984 const location_t x
2985 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2986 const location_t colon
2987 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2988 rich_location richloc (line_table, colon);
2989 richloc.add_fixit_insert_before (x, ".");
2990 richloc.add_fixit_replace (colon, "=");
2991 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2992 ASSERT_STREQ ("\n"
2993 " struct point origin = {x: 0.0,\n"
2994 " ^\n"
2995 " .=\n",
2996 pp_formatted_text (dc.printer));
2999 /* The multiline case. The caret for the rich_location is on line 6;
3000 verify that insertion fixit on line 3 is still printed (and that
3001 span starts are printed due to the gap between the span at line 3
3002 and that at line 6). */
3004 test_diagnostic_context dc;
3005 const location_t y
3006 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3007 const location_t colon
3008 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3009 rich_location richloc (line_table, colon);
3010 richloc.add_fixit_insert_before (y, ".");
3011 richloc.add_fixit_replace (colon, "=");
3012 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3013 ASSERT_STREQ ("\n"
3014 "FILENAME:3:24:\n"
3015 " y\n"
3016 " .\n"
3017 "FILENAME:6:25:\n"
3018 " : 0.0};\n"
3019 " ^\n"
3020 " =\n",
3021 pp_formatted_text (dc.printer));
3024 /* As above, but verify the behavior of multiple line spans
3025 with line-numbering enabled. */
3027 const location_t y
3028 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3029 const location_t colon
3030 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3031 rich_location richloc (line_table, colon);
3032 richloc.add_fixit_insert_before (y, ".");
3033 richloc.add_fixit_replace (colon, "=");
3034 test_diagnostic_context dc;
3035 dc.show_line_numbers_p = true;
3036 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3037 ASSERT_STREQ ("\n"
3038 " 3 | y\n"
3039 " | .\n"
3040 "......\n"
3041 " 6 | : 0.0};\n"
3042 " | ^\n"
3043 " | =\n",
3044 pp_formatted_text (dc.printer));
3049 /* Verify that fix-it hints are appropriately consolidated.
3051 If any fix-it hints in a rich_location involve locations beyond
3052 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3053 the fix-it as a whole, so there should be none.
3055 Otherwise, verify that consecutive "replace" and "remove" fix-its
3056 are merged, and that other fix-its remain separate. */
3058 static void
3059 test_fixit_consolidation (const line_table_case &case_)
3061 line_table_test ltt (case_);
3063 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3065 const location_t c10 = linemap_position_for_column (line_table, 10);
3066 const location_t c15 = linemap_position_for_column (line_table, 15);
3067 const location_t c16 = linemap_position_for_column (line_table, 16);
3068 const location_t c17 = linemap_position_for_column (line_table, 17);
3069 const location_t c20 = linemap_position_for_column (line_table, 20);
3070 const location_t c21 = linemap_position_for_column (line_table, 21);
3071 const location_t caret = c10;
3073 /* Insert + insert. */
3075 rich_location richloc (line_table, caret);
3076 richloc.add_fixit_insert_before (c10, "foo");
3077 richloc.add_fixit_insert_before (c15, "bar");
3079 if (c15 > 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 /* Insert + replace. */
3089 rich_location richloc (line_table, caret);
3090 richloc.add_fixit_insert_before (c10, "foo");
3091 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3092 "bar");
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 insert. */
3104 rich_location richloc (line_table, caret);
3105 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3106 "bar");
3107 richloc.add_fixit_insert_before (c17, "foo");
3109 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3110 /* Bogus column info for 2nd fixit, so no fixits. */
3111 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3112 else
3113 /* They should not have been merged. */
3114 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3117 /* Replace + non-consecutive replace. */
3119 rich_location richloc (line_table, caret);
3120 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3121 "foo");
3122 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3123 "bar");
3125 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3126 /* Bogus column info for 2nd fixit, so no fixits. */
3127 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3128 else
3129 /* They should not have been merged. */
3130 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3133 /* Replace + consecutive replace. */
3135 rich_location richloc (line_table, caret);
3136 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3137 "foo");
3138 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3139 "bar");
3141 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3142 /* Bogus column info for 2nd fixit, so no fixits. */
3143 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3144 else
3146 /* They should have been merged into a single "replace". */
3147 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3148 const fixit_hint *hint = richloc.get_fixit_hint (0);
3149 ASSERT_STREQ ("foobar", hint->get_string ());
3150 ASSERT_EQ (c10, hint->get_start_loc ());
3151 ASSERT_EQ (c21, hint->get_next_loc ());
3155 /* Replace + consecutive removal. */
3157 rich_location richloc (line_table, caret);
3158 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3159 "foo");
3160 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3162 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3163 /* Bogus column info for 2nd fixit, so no fixits. */
3164 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3165 else
3167 /* They should have been merged into a single replace, with the
3168 range extended to cover that of the removal. */
3169 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3170 const fixit_hint *hint = richloc.get_fixit_hint (0);
3171 ASSERT_STREQ ("foo", hint->get_string ());
3172 ASSERT_EQ (c10, hint->get_start_loc ());
3173 ASSERT_EQ (c21, hint->get_next_loc ());
3177 /* Consecutive removals. */
3179 rich_location richloc (line_table, caret);
3180 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3181 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3183 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3184 /* Bogus column info for 2nd fixit, so no fixits. */
3185 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3186 else
3188 /* They should have been merged into a single "replace-with-empty". */
3189 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3190 const fixit_hint *hint = richloc.get_fixit_hint (0);
3191 ASSERT_STREQ ("", hint->get_string ());
3192 ASSERT_EQ (c10, hint->get_start_loc ());
3193 ASSERT_EQ (c21, hint->get_next_loc ());
3198 /* Verify that the line_corrections machinery correctly prints
3199 overlapping fixit-hints. */
3201 static void
3202 test_overlapped_fixit_printing (const line_table_case &case_)
3204 /* Create a tempfile and write some text to it.
3205 ...000000000111111111122222222223333333333.
3206 ...123456789012345678901234567890123456789. */
3207 const char *content
3208 = (" foo *f = (foo *)ptr->field;\n");
3209 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3210 line_table_test ltt (case_);
3212 const line_map_ordinary *ord_map
3213 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3214 tmp.get_filename (), 0));
3216 linemap_line_start (line_table, 1, 100);
3218 const location_t final_line_end
3219 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3221 /* Don't attempt to run the tests if column data might be unavailable. */
3222 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3223 return;
3225 /* A test for converting a C-style cast to a C++-style cast. */
3226 const location_t open_paren
3227 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3228 const location_t close_paren
3229 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3230 const location_t expr_start
3231 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3232 const location_t expr_finish
3233 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3234 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3236 /* Various examples of fix-it hints that aren't themselves consolidated,
3237 but for which the *printing* may need consolidation. */
3239 /* Example where 3 fix-it hints are printed as one. */
3241 test_diagnostic_context dc;
3242 rich_location richloc (line_table, expr);
3243 richloc.add_fixit_replace (open_paren, "const_cast<");
3244 richloc.add_fixit_replace (close_paren, "> (");
3245 richloc.add_fixit_insert_after (")");
3247 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3248 ASSERT_STREQ ("\n"
3249 " foo *f = (foo *)ptr->field;\n"
3250 " ^~~~~~~~~~\n"
3251 " -----------------\n"
3252 " const_cast<foo *> (ptr->field)\n",
3253 pp_formatted_text (dc.printer));
3255 /* Unit-test the line_corrections machinery. */
3256 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3257 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3258 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3259 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3260 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3261 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3262 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3263 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3264 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3265 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3267 /* Add each hint in turn to a line_corrections instance,
3268 and verify that they are consolidated into one correction instance
3269 as expected. */
3270 line_corrections lc (tmp.get_filename (), 1);
3272 /* The first replace hint by itself. */
3273 lc.add_hint (hint_0);
3274 ASSERT_EQ (1, lc.m_corrections.length ());
3275 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3276 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3277 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3279 /* After the second replacement hint, they are printed together
3280 as a replacement (along with the text between them). */
3281 lc.add_hint (hint_1);
3282 ASSERT_EQ (1, lc.m_corrections.length ());
3283 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3284 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3285 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3287 /* After the final insertion hint, they are all printed together
3288 as a replacement (along with the text between them). */
3289 lc.add_hint (hint_2);
3290 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3291 lc.m_corrections[0]->m_text);
3292 ASSERT_EQ (1, lc.m_corrections.length ());
3293 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3294 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3297 /* Example where two are consolidated during printing. */
3299 test_diagnostic_context dc;
3300 rich_location richloc (line_table, expr);
3301 richloc.add_fixit_replace (open_paren, "CAST (");
3302 richloc.add_fixit_replace (close_paren, ") (");
3303 richloc.add_fixit_insert_after (")");
3305 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3306 ASSERT_STREQ ("\n"
3307 " foo *f = (foo *)ptr->field;\n"
3308 " ^~~~~~~~~~\n"
3309 " -\n"
3310 " CAST (-\n"
3311 " ) ( )\n",
3312 pp_formatted_text (dc.printer));
3315 /* Example where none are consolidated during printing. */
3317 test_diagnostic_context dc;
3318 rich_location richloc (line_table, expr);
3319 richloc.add_fixit_replace (open_paren, "CST (");
3320 richloc.add_fixit_replace (close_paren, ") (");
3321 richloc.add_fixit_insert_after (")");
3323 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3324 ASSERT_STREQ ("\n"
3325 " foo *f = (foo *)ptr->field;\n"
3326 " ^~~~~~~~~~\n"
3327 " -\n"
3328 " CST ( -\n"
3329 " ) ( )\n",
3330 pp_formatted_text (dc.printer));
3333 /* Example of deletion fix-it hints. */
3335 test_diagnostic_context dc;
3336 rich_location richloc (line_table, expr);
3337 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3338 source_range victim = {open_paren, close_paren};
3339 richloc.add_fixit_remove (victim);
3341 /* This case is actually handled by fixit-consolidation,
3342 rather than by line_corrections. */
3343 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3345 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3346 ASSERT_STREQ ("\n"
3347 " foo *f = (foo *)ptr->field;\n"
3348 " ^~~~~~~~~~\n"
3349 " -------\n"
3350 " (bar *)\n",
3351 pp_formatted_text (dc.printer));
3354 /* Example of deletion fix-it hints that would overlap. */
3356 test_diagnostic_context dc;
3357 rich_location richloc (line_table, expr);
3358 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3359 source_range victim = {expr_start, expr_finish};
3360 richloc.add_fixit_remove (victim);
3362 /* These fixits are not consolidated. */
3363 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3365 /* But the corrections are. */
3366 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3367 ASSERT_STREQ ("\n"
3368 " foo *f = (foo *)ptr->field;\n"
3369 " ^~~~~~~~~~\n"
3370 " -----------------\n"
3371 " (longer *)(foo *)\n",
3372 pp_formatted_text (dc.printer));
3375 /* Example of insertion fix-it hints that would overlap. */
3377 test_diagnostic_context dc;
3378 rich_location richloc (line_table, expr);
3379 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3380 richloc.add_fixit_insert_after (close_paren, "TEST");
3382 /* The first insertion is long enough that if printed naively,
3383 it would overlap with the second.
3384 Verify that they are printed as a single replacement. */
3385 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3386 ASSERT_STREQ ("\n"
3387 " foo *f = (foo *)ptr->field;\n"
3388 " ^~~~~~~~~~\n"
3389 " -------\n"
3390 " LONGER THAN THE CAST(foo *)TEST\n",
3391 pp_formatted_text (dc.printer));
3395 /* Verify that the line_corrections machinery correctly prints
3396 overlapping fixit-hints that have been added in the wrong
3397 order.
3398 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3400 static void
3401 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3403 /* Create a tempfile and write some text to it.
3404 ...000000000111111111122222222223333333333.
3405 ...123456789012345678901234567890123456789. */
3406 const char *content
3407 = ("int a5[][0][0] = { 1, 2 };\n");
3408 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3409 line_table_test ltt (case_);
3411 const line_map_ordinary *ord_map
3412 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3413 tmp.get_filename (), 0));
3415 linemap_line_start (line_table, 1, 100);
3417 const location_t final_line_end
3418 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3420 /* Don't attempt to run the tests if column data might be unavailable. */
3421 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3422 return;
3424 const location_t col_1
3425 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3426 const location_t col_20
3427 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3428 const location_t col_21
3429 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3430 const location_t col_23
3431 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3432 const location_t col_25
3433 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3435 /* Two insertions, in the wrong order. */
3437 rich_location richloc (line_table, col_20);
3438 richloc.add_fixit_insert_before (col_23, "{");
3439 richloc.add_fixit_insert_before (col_21, "}");
3441 /* These fixits should be accepted; they can't be consolidated. */
3442 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3443 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3444 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3445 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3446 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3447 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3448 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3450 /* Verify that they're printed correctly. */
3451 test_diagnostic_context dc;
3452 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3453 ASSERT_STREQ ("\n"
3454 " int a5[][0][0] = { 1, 2 };\n"
3455 " ^\n"
3456 " } {\n",
3457 pp_formatted_text (dc.printer));
3460 /* Various overlapping insertions, some occurring "out of order"
3461 (reproducing the fix-it hints from PR c/81405). */
3463 test_diagnostic_context dc;
3464 rich_location richloc (line_table, col_20);
3466 richloc.add_fixit_insert_before (col_20, "{{");
3467 richloc.add_fixit_insert_before (col_21, "}}");
3468 richloc.add_fixit_insert_before (col_23, "{");
3469 richloc.add_fixit_insert_before (col_21, "}");
3470 richloc.add_fixit_insert_before (col_23, "{{");
3471 richloc.add_fixit_insert_before (col_25, "}");
3472 richloc.add_fixit_insert_before (col_21, "}");
3473 richloc.add_fixit_insert_before (col_1, "{");
3474 richloc.add_fixit_insert_before (col_25, "}");
3475 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3476 ASSERT_STREQ ("\n"
3477 " int a5[][0][0] = { 1, 2 };\n"
3478 " ^\n"
3479 " { -----\n"
3480 " {{1}}}}, {{{2 }}\n",
3481 pp_formatted_text (dc.printer));
3485 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3487 static void
3488 test_fixit_insert_containing_newline (const line_table_case &case_)
3490 /* Create a tempfile and write some text to it.
3491 .........................0000000001111111.
3492 .........................1234567890123456. */
3493 const char *old_content = (" case 'a':\n" /* line 1. */
3494 " x = a;\n" /* line 2. */
3495 " case 'b':\n" /* line 3. */
3496 " x = b;\n");/* line 4. */
3498 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3499 line_table_test ltt (case_);
3500 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3502 location_t case_start = linemap_position_for_column (line_table, 5);
3503 location_t case_finish = linemap_position_for_column (line_table, 13);
3504 location_t case_loc = make_location (case_start, case_start, case_finish);
3505 location_t line_start = linemap_position_for_column (line_table, 1);
3507 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3508 return;
3510 /* Add a "break;" on a line by itself before line 3 i.e. before
3511 column 1 of line 3. */
3513 rich_location richloc (line_table, case_loc);
3514 richloc.add_fixit_insert_before (line_start, " break;\n");
3516 /* Without line numbers. */
3518 test_diagnostic_context dc;
3519 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3520 ASSERT_STREQ ("\n"
3521 " x = a;\n"
3522 "+ break;\n"
3523 " case 'b':\n"
3524 " ^~~~~~~~~\n",
3525 pp_formatted_text (dc.printer));
3528 /* With line numbers. */
3530 test_diagnostic_context dc;
3531 dc.show_line_numbers_p = true;
3532 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3533 ASSERT_STREQ ("\n"
3534 " 2 | x = a;\n"
3535 " +++ |+ break;\n"
3536 " 3 | case 'b':\n"
3537 " | ^~~~~~~~~\n",
3538 pp_formatted_text (dc.printer));
3542 /* Verify that attempts to add text with a newline fail when the
3543 insertion point is *not* at the start of a line. */
3545 rich_location richloc (line_table, case_loc);
3546 richloc.add_fixit_insert_before (case_start, "break;\n");
3547 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3548 test_diagnostic_context dc;
3549 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3550 ASSERT_STREQ ("\n"
3551 " case 'b':\n"
3552 " ^~~~~~~~~\n",
3553 pp_formatted_text (dc.printer));
3557 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3558 of the file, where the fix-it is printed in a different line-span
3559 to the primary range of the diagnostic. */
3561 static void
3562 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3564 /* Create a tempfile and write some text to it.
3565 .........................0000000001111111.
3566 .........................1234567890123456. */
3567 const char *old_content = ("test (int ch)\n" /* line 1. */
3568 "{\n" /* line 2. */
3569 " putchar (ch);\n" /* line 3. */
3570 "}\n"); /* line 4. */
3572 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3573 line_table_test ltt (case_);
3575 const line_map_ordinary *ord_map = linemap_check_ordinary
3576 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3577 linemap_line_start (line_table, 1, 100);
3579 /* The primary range is the "putchar" token. */
3580 location_t putchar_start
3581 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3582 location_t putchar_finish
3583 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3584 location_t putchar_loc
3585 = make_location (putchar_start, putchar_start, putchar_finish);
3586 rich_location richloc (line_table, putchar_loc);
3588 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3589 location_t file_start
3590 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3591 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3593 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3594 return;
3597 test_diagnostic_context dc;
3598 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3599 ASSERT_STREQ ("\n"
3600 "FILENAME:1:1:\n"
3601 "+#include <stdio.h>\n"
3602 " test (int ch)\n"
3603 "FILENAME:3:2:\n"
3604 " putchar (ch);\n"
3605 " ^~~~~~~\n",
3606 pp_formatted_text (dc.printer));
3609 /* With line-numbering, the line spans are close enough to be
3610 consolidated, since it makes little sense to skip line 2. */
3612 test_diagnostic_context dc;
3613 dc.show_line_numbers_p = true;
3614 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3615 ASSERT_STREQ ("\n"
3616 " +++ |+#include <stdio.h>\n"
3617 " 1 | test (int ch)\n"
3618 " 2 | {\n"
3619 " 3 | putchar (ch);\n"
3620 " | ^~~~~~~\n",
3621 pp_formatted_text (dc.printer));
3625 /* Replacement fix-it hint containing a newline.
3626 This will fail, as newlines are only supported when inserting at the
3627 beginning of a line. */
3629 static void
3630 test_fixit_replace_containing_newline (const line_table_case &case_)
3632 /* Create a tempfile and write some text to it.
3633 .........................0000000001111.
3634 .........................1234567890123. */
3635 const char *old_content = "foo = bar ();\n";
3637 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3638 line_table_test ltt (case_);
3639 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3641 /* Replace the " = " with "\n = ", as if we were reformatting an
3642 overly long line. */
3643 location_t start = linemap_position_for_column (line_table, 4);
3644 location_t finish = linemap_position_for_column (line_table, 6);
3645 location_t loc = linemap_position_for_column (line_table, 13);
3646 rich_location richloc (line_table, loc);
3647 source_range range = source_range::from_locations (start, finish);
3648 richloc.add_fixit_replace (range, "\n =");
3650 /* Arbitrary newlines are not yet supported within fix-it hints, so
3651 the fix-it should not be displayed. */
3652 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3654 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3655 return;
3657 test_diagnostic_context dc;
3658 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3659 ASSERT_STREQ ("\n"
3660 " foo = bar ();\n"
3661 " ^\n",
3662 pp_formatted_text (dc.printer));
3665 /* Fix-it hint, attempting to delete a newline.
3666 This will fail, as we currently only support fix-it hints that
3667 affect one line at a time. */
3669 static void
3670 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3672 /* Create a tempfile and write some text to it.
3673 ..........................0000000001111.
3674 ..........................1234567890123. */
3675 const char *old_content = ("foo = bar (\n"
3676 " );\n");
3678 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3679 line_table_test ltt (case_);
3680 const line_map_ordinary *ord_map = linemap_check_ordinary
3681 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3682 linemap_line_start (line_table, 1, 100);
3684 /* Attempt to delete the " (\n...)". */
3685 location_t start
3686 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3687 location_t caret
3688 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3689 location_t finish
3690 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3691 location_t loc = make_location (caret, start, finish);
3692 rich_location richloc (line_table, loc);
3693 richloc. add_fixit_remove ();
3695 /* Fix-it hints that affect more than one line are not yet supported, so
3696 the fix-it should not be displayed. */
3697 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3699 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3700 return;
3702 test_diagnostic_context dc;
3703 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3704 ASSERT_STREQ ("\n"
3705 " foo = bar (\n"
3706 " ~^\n"
3707 " );\n"
3708 " ~ \n",
3709 pp_formatted_text (dc.printer));
3712 /* Verify that line numbers are correctly printed for the case of
3713 a multiline range in which the width of the line numbers changes
3714 (e.g. from "9" to "10"). */
3716 static void
3717 test_line_numbers_multiline_range ()
3719 /* Create a tempfile and write some text to it. */
3720 pretty_printer pp;
3721 for (int i = 0; i < 20; i++)
3722 /* .........0000000001111111.
3723 .............1234567890123456. */
3724 pp_printf (&pp, "this is line %i\n", i + 1);
3725 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3726 line_table_test ltt;
3728 const line_map_ordinary *ord_map = linemap_check_ordinary
3729 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3730 linemap_line_start (line_table, 1, 100);
3732 /* Create a multi-line location, starting at the "line" of line 9, with
3733 a caret on the "is" of line 10, finishing on the "this" line 11. */
3735 location_t start
3736 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3737 location_t caret
3738 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3739 location_t finish
3740 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3741 location_t loc = make_location (caret, start, finish);
3743 test_diagnostic_context dc;
3744 dc.show_line_numbers_p = true;
3745 dc.min_margin_width = 0;
3746 gcc_rich_location richloc (loc);
3747 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3748 ASSERT_STREQ ("\n"
3749 " 9 | this is line 9\n"
3750 " | ~~~~~~\n"
3751 "10 | this is line 10\n"
3752 " | ~~~~~^~~~~~~~~~\n"
3753 "11 | this is line 11\n"
3754 " | ~~~~ \n",
3755 pp_formatted_text (dc.printer));
3758 /* Run all of the selftests within this file. */
3760 void
3761 diagnostic_show_locus_c_tests ()
3763 test_line_span ();
3764 test_num_digits ();
3766 test_layout_range_for_single_point ();
3767 test_layout_range_for_single_line ();
3768 test_layout_range_for_multiple_lines ();
3770 test_get_line_width_without_trailing_whitespace ();
3772 test_diagnostic_show_locus_unknown_location ();
3774 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3775 for_each_line_table_case (test_add_location_if_nearby);
3776 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3777 for_each_line_table_case (test_fixit_consolidation);
3778 for_each_line_table_case (test_overlapped_fixit_printing);
3779 for_each_line_table_case (test_overlapped_fixit_printing_2);
3780 for_each_line_table_case (test_fixit_insert_containing_newline);
3781 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3782 for_each_line_table_case (test_fixit_replace_containing_newline);
3783 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3785 test_line_numbers_multiline_range ();
3788 } // namespace selftest
3790 #endif /* #if CHECKING_P */