2018-10-09 Richard Biener <rguenther@suse.de>
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob43a49ea89137504412443f8d38c547db0867d350
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);
934 /* Adjust m_x_offset.
935 Center the primary caret to fit in max_width; all columns
936 will be adjusted accordingly. */
937 size_t max_width = m_context->caret_max_width;
938 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
939 if (line && (size_t)m_exploc.column <= line.length ())
941 size_t right_margin = CARET_LINE_MARGIN;
942 size_t column = m_exploc.column;
943 if (m_show_line_numbers_p)
944 column += m_linenum_width + 2;
945 right_margin = MIN (line.length () - column, right_margin);
946 right_margin = max_width - right_margin;
947 if (line.length () >= max_width && column > right_margin)
948 m_x_offset = column - right_margin;
949 gcc_assert (m_x_offset >= 0);
952 if (context->show_ruler_p)
953 show_ruler (m_x_offset + max_width);
956 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
957 those that we can sanely print.
959 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
960 (for use as extrinsic state by label ranges FIXME).
962 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
963 filtered against this layout instance's current line spans: it
964 will only be added if the location is fully within the lines
965 already specified by other locations.
967 Return true iff LOC_RANGE was added. */
969 bool
970 layout::maybe_add_location_range (const location_range *loc_range,
971 unsigned original_idx,
972 bool restrict_to_current_line_spans)
974 gcc_assert (loc_range);
976 /* Split the "range" into caret and range information. */
977 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
979 /* Expand the various locations. */
980 expanded_location start
981 = linemap_client_expand_location_to_spelling_point
982 (src_range.m_start, LOCATION_ASPECT_START);
983 expanded_location finish
984 = linemap_client_expand_location_to_spelling_point
985 (src_range.m_finish, LOCATION_ASPECT_FINISH);
986 expanded_location caret
987 = linemap_client_expand_location_to_spelling_point
988 (loc_range->m_loc, LOCATION_ASPECT_CARET);
990 /* If any part of the range isn't in the same file as the primary
991 location of this diagnostic, ignore the range. */
992 if (start.file != m_exploc.file)
993 return false;
994 if (finish.file != m_exploc.file)
995 return false;
996 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
997 if (caret.file != m_exploc.file)
998 return false;
1000 /* Sanitize the caret location for non-primary ranges. */
1001 if (m_layout_ranges.length () > 0)
1002 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1003 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1004 /* Discard any non-primary ranges that can't be printed
1005 sanely relative to the primary location. */
1006 return false;
1008 /* Everything is now known to be in the correct source file,
1009 but it may require further sanitization. */
1010 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
1011 original_idx, loc_range->m_label);
1013 /* If we have a range that finishes before it starts (perhaps
1014 from something built via macro expansion), printing the
1015 range is likely to be nonsensical. Also, attempting to do so
1016 breaks assumptions within the printing code (PR c/68473).
1017 Similarly, don't attempt to print ranges if one or both ends
1018 of the range aren't sane to print relative to the
1019 primary location (PR c++/70105). */
1020 if (start.line > finish.line
1021 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1022 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1024 /* Is this the primary location? */
1025 if (m_layout_ranges.length () == 0)
1027 /* We want to print the caret for the primary location, but
1028 we must sanitize away m_start and m_finish. */
1029 ri.m_start = ri.m_caret;
1030 ri.m_finish = ri.m_caret;
1032 else
1033 /* This is a non-primary range; ignore it. */
1034 return false;
1037 /* Potentially filter to just the lines already specified by other
1038 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1039 The layout ctor doesn't use it, and can't because m_line_spans
1040 hasn't been set up at that point. */
1041 if (restrict_to_current_line_spans)
1043 if (!will_show_line_p (start.line))
1044 return false;
1045 if (!will_show_line_p (finish.line))
1046 return false;
1047 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1048 if (!will_show_line_p (caret.line))
1049 return false;
1052 /* Passed all the tests; add the range to m_layout_ranges so that
1053 it will be printed. */
1054 m_layout_ranges.safe_push (ri);
1055 return true;
1058 /* Return true iff ROW is within one of the line spans for this layout. */
1060 bool
1061 layout::will_show_line_p (linenum_type row) const
1063 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1064 line_span_idx++)
1066 const line_span *line_span = get_line_span (line_span_idx);
1067 if (line_span->contains_line_p (row))
1068 return true;
1070 return false;
1073 /* Print a line showing a gap in the line numbers, for showing the boundary
1074 between two line spans. */
1076 void
1077 layout::print_gap_in_line_numbering ()
1079 gcc_assert (m_show_line_numbers_p);
1081 for (int i = 0; i < m_linenum_width + 1; i++)
1082 pp_character (m_pp, '.');
1084 pp_newline (m_pp);
1087 /* Return true iff we should print a heading when starting the
1088 line span with the given index. */
1090 bool
1091 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1093 /* We print a heading for every change of line span, hence for every
1094 line span after the initial one. */
1095 if (line_span_idx > 0)
1096 return true;
1098 /* We also do it for the initial span if the primary location of the
1099 diagnostic is in a different span. */
1100 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1101 return true;
1103 return false;
1106 /* Get an expanded_location for the first location of interest within
1107 the given line_span.
1108 Used when printing a heading to indicate a new line span. */
1110 expanded_location
1111 layout::get_expanded_location (const line_span *line_span) const
1113 /* Whenever possible, use the caret location. */
1114 if (line_span->contains_line_p (m_exploc.line))
1115 return m_exploc;
1117 /* Otherwise, use the start of the first range that's present
1118 within the line_span. */
1119 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1121 const layout_range *lr = &m_layout_ranges[i];
1122 if (line_span->contains_line_p (lr->m_start.m_line))
1124 expanded_location exploc = m_exploc;
1125 exploc.line = lr->m_start.m_line;
1126 exploc.column = lr->m_start.m_column;
1127 return exploc;
1131 /* Otherwise, use the location of the first fixit-hint present within
1132 the line_span. */
1133 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1135 const fixit_hint *hint = m_fixit_hints[i];
1136 location_t loc = hint->get_start_loc ();
1137 expanded_location exploc = expand_location (loc);
1138 if (line_span->contains_line_p (exploc.line))
1139 return exploc;
1142 /* It should not be possible to have a line span that didn't
1143 contain any of the layout_range or fixit_hint instances. */
1144 gcc_unreachable ();
1145 return m_exploc;
1148 /* Determine if HINT is meaningful to print within this layout. */
1150 bool
1151 layout::validate_fixit_hint_p (const fixit_hint *hint)
1153 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1154 return false;
1155 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1156 return false;
1158 return true;
1161 /* Determine the range of lines affected by HINT.
1162 This assumes that HINT has already been filtered by
1163 validate_fixit_hint_p, and so affects the correct source file. */
1165 static line_span
1166 get_line_span_for_fixit_hint (const fixit_hint *hint)
1168 gcc_assert (hint);
1170 int start_line = LOCATION_LINE (hint->get_start_loc ());
1172 /* For line-insertion fix-it hints, add the previous line to the
1173 span, to give the user more context on the proposed change. */
1174 if (hint->ends_with_newline_p ())
1175 if (start_line > 1)
1176 start_line--;
1178 return line_span (start_line,
1179 LOCATION_LINE (hint->get_next_loc ()));
1182 /* We want to print the pertinent source code at a diagnostic. The
1183 rich_location can contain multiple locations. This will have been
1184 filtered into m_exploc (the caret for the primary location) and
1185 m_layout_ranges, for those ranges within the same source file.
1187 We will print a subset of the lines within the source file in question,
1188 as a collection of "spans" of lines.
1190 This function populates m_line_spans with an ordered, disjoint list of
1191 the line spans of interest.
1193 Printing a gap between line spans takes one line, so, when printing
1194 line numbers, we allow a gap of up to one line between spans when
1195 merging, since it makes more sense to print the source line rather than a
1196 "gap-in-line-numbering" line. When not printing line numbers, it's
1197 better to be more explicit about what's going on, so keeping them as
1198 separate spans is preferred.
1200 For example, if the primary range is on lines 8-10, with secondary ranges
1201 covering lines 5-6 and lines 13-15:
1204 005 |RANGE 1
1205 006 |RANGE 1
1207 008 |PRIMARY RANGE
1208 009 |PRIMARY CARET
1209 010 |PRIMARY RANGE
1212 013 |RANGE 2
1213 014 |RANGE 2
1214 015 |RANGE 2
1217 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1219 With line numbering off (with span headers), we want three spans: lines 5-6,
1220 lines 8-10, and lines 13-15. */
1222 void
1223 layout::calculate_line_spans ()
1225 /* This should only be called once, by the ctor. */
1226 gcc_assert (m_line_spans.length () == 0);
1228 /* Populate tmp_spans with individual spans, for each of
1229 m_exploc, and for m_layout_ranges. */
1230 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1231 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1232 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1234 const layout_range *lr = &m_layout_ranges[i];
1235 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1236 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1237 lr->m_finish.m_line));
1240 /* Also add spans for any fix-it hints, in case they cover other lines. */
1241 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1243 const fixit_hint *hint = m_fixit_hints[i];
1244 gcc_assert (hint);
1245 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1248 /* Sort them. */
1249 tmp_spans.qsort(line_span::comparator);
1251 /* Now iterate through tmp_spans, copying into m_line_spans, and
1252 combining where possible. */
1253 gcc_assert (tmp_spans.length () > 0);
1254 m_line_spans.safe_push (tmp_spans[0]);
1255 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1257 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1258 const line_span *next = &tmp_spans[i];
1259 gcc_assert (next->m_first_line >= current->m_first_line);
1260 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1261 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
1263 /* We can merge them. */
1264 if (next->m_last_line > current->m_last_line)
1265 current->m_last_line = next->m_last_line;
1267 else
1269 /* No merger possible. */
1270 m_line_spans.safe_push (*next);
1274 /* Verify the result, in m_line_spans. */
1275 gcc_assert (m_line_spans.length () > 0);
1276 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1278 const line_span *prev = &m_line_spans[i - 1];
1279 const line_span *next = &m_line_spans[i];
1280 /* The individual spans must be sane. */
1281 gcc_assert (prev->m_first_line <= prev->m_last_line);
1282 gcc_assert (next->m_first_line <= next->m_last_line);
1283 /* The spans must be ordered. */
1284 gcc_assert (prev->m_first_line < next->m_first_line);
1285 /* There must be a gap of at least one line between separate spans. */
1286 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1290 /* Print line ROW of source code, potentially colorized at any ranges, and
1291 populate *LBOUNDS_OUT.
1292 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1293 is its width. */
1295 void
1296 layout::print_source_line (linenum_type row, const char *line, int line_width,
1297 line_bounds *lbounds_out)
1299 m_colorizer.set_normal_text ();
1301 /* We will stop printing the source line at any trailing
1302 whitespace. */
1303 line_width = get_line_width_without_trailing_whitespace (line,
1304 line_width);
1305 line += m_x_offset;
1307 if (m_show_line_numbers_p)
1309 int width = num_digits (row);
1310 for (int i = 0; i < m_linenum_width - width; i++)
1311 pp_space (m_pp);
1312 pp_printf (m_pp, "%i | ", row);
1314 else
1315 pp_space (m_pp);
1316 int first_non_ws = INT_MAX;
1317 int last_non_ws = 0;
1318 int column;
1319 for (column = 1 + m_x_offset; column <= line_width; column++)
1321 /* Assuming colorization is enabled for the caret and underline
1322 characters, we may also colorize the associated characters
1323 within the source line.
1325 For frontends that generate range information, we color the
1326 associated characters in the source line the same as the
1327 carets and underlines in the annotation line, to make it easier
1328 for the reader to see the pertinent code.
1330 For frontends that only generate carets, we don't colorize the
1331 characters above them, since this would look strange (e.g.
1332 colorizing just the first character in a token). */
1333 if (m_colorize_source_p)
1335 bool in_range_p;
1336 point_state state;
1337 in_range_p = get_state_at_point (row, column,
1338 0, INT_MAX,
1339 &state);
1340 if (in_range_p)
1341 m_colorizer.set_range (state.range_idx);
1342 else
1343 m_colorizer.set_normal_text ();
1345 char c = *line;
1346 if (c == '\0' || c == '\t' || c == '\r')
1347 c = ' ';
1348 if (c != ' ')
1350 last_non_ws = column;
1351 if (first_non_ws == INT_MAX)
1352 first_non_ws = column;
1354 pp_character (m_pp, c);
1355 line++;
1357 print_newline ();
1359 lbounds_out->m_first_non_ws = first_non_ws;
1360 lbounds_out->m_last_non_ws = last_non_ws;
1363 /* Determine if we should print an annotation line for ROW.
1364 i.e. if any of m_layout_ranges contains ROW. */
1366 bool
1367 layout::should_print_annotation_line_p (linenum_type row) const
1369 layout_range *range;
1370 int i;
1371 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1373 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1374 return false;
1375 if (range->intersects_line_p (row))
1376 return true;
1378 return false;
1381 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1382 margin, which is empty for annotation lines. Otherwise, do nothing. */
1384 void
1385 layout::start_annotation_line (char margin_char) const
1387 if (m_show_line_numbers_p)
1389 for (int i = 0; i < m_linenum_width; i++)
1390 pp_character (m_pp, margin_char);
1391 pp_string (m_pp, " |");
1395 /* Print a line consisting of the caret/underlines for the given
1396 source line. */
1398 void
1399 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1401 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1402 lbounds.m_last_non_ws);
1404 start_annotation_line ();
1405 pp_space (m_pp);
1407 for (int column = 1 + m_x_offset; column < x_bound; column++)
1409 bool in_range_p;
1410 point_state state;
1411 in_range_p = get_state_at_point (row, column,
1412 lbounds.m_first_non_ws,
1413 lbounds.m_last_non_ws,
1414 &state);
1415 if (in_range_p)
1417 /* Within a range. Draw either the caret or an underline. */
1418 m_colorizer.set_range (state.range_idx);
1419 if (state.draw_caret_p)
1421 /* Draw the caret. */
1422 char caret_char;
1423 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1424 caret_char = m_context->caret_chars[state.range_idx];
1425 else
1426 caret_char = '^';
1427 pp_character (m_pp, caret_char);
1429 else
1430 pp_character (m_pp, '~');
1432 else
1434 /* Not in a range. */
1435 m_colorizer.set_normal_text ();
1436 pp_character (m_pp, ' ');
1439 print_newline ();
1442 /* Implementation detail of layout::print_any_labels.
1444 A label within the given row of source. */
1446 struct line_label
1448 line_label (int state_idx, int column, label_text text)
1449 : m_state_idx (state_idx), m_column (column),
1450 m_text (text), m_length (strlen (text.m_buffer)),
1451 m_label_line (0)
1454 /* Sorting is primarily by column, then by state index. */
1455 static int comparator (const void *p1, const void *p2)
1457 const line_label *ll1 = (const line_label *)p1;
1458 const line_label *ll2 = (const line_label *)p2;
1459 int column_cmp = compare (ll1->m_column, ll2->m_column);
1460 if (column_cmp)
1461 return column_cmp;
1462 return compare (ll1->m_state_idx, ll2->m_state_idx);
1465 int m_state_idx;
1466 int m_column;
1467 label_text m_text;
1468 size_t m_length;
1469 int m_label_line;
1472 /* Print any labels in this row. */
1473 void
1474 layout::print_any_labels (linenum_type row)
1476 int i;
1477 auto_vec<line_label> labels;
1479 /* Gather the labels that are to be printed into "labels". */
1481 layout_range *range;
1482 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1484 /* Most ranges don't have labels, so reject this first. */
1485 if (range->m_label == NULL)
1486 continue;
1488 /* The range's caret must be on this line. */
1489 if (range->m_caret.m_line != row)
1490 continue;
1492 /* Reject labels that aren't fully visible due to clipping
1493 by m_x_offset. */
1494 if (range->m_caret.m_column <= m_x_offset)
1495 continue;
1497 label_text text;
1498 text = range->m_label->get_text (range->m_original_idx);
1500 /* Allow for labels that return NULL from their get_text
1501 implementation (so e.g. such labels can control their own
1502 visibility). */
1503 if (text.m_buffer == NULL)
1504 continue;
1506 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1510 /* Bail out if there are no labels on this row. */
1511 if (labels.length () == 0)
1512 return;
1514 /* Sort them. */
1515 labels.qsort(line_label::comparator);
1517 /* Figure out how many "label lines" we need, and which
1518 one each label is printed in.
1520 For example, if the labels aren't too densely packed,
1521 we can fit them on the same line, giving two "label lines":
1523 foo + bar
1524 ~~~ ~~~
1525 | | : label line 0
1526 l0 l1 : label line 1
1528 If they would touch each other or overlap, then we need
1529 additional "label lines":
1531 foo + bar
1532 ~~~ ~~~
1533 | | : label line 0
1534 | label 1 : label line 1
1535 label 0 : label line 2
1537 Place the final label on label line 1, and work backwards, adding
1538 label lines as needed.
1540 If multiple labels are at the same place, put them on separate
1541 label lines:
1543 foo + bar
1544 ^ : label line 0
1545 | : label line 1
1546 label 1 : label line 2
1547 label 0 : label line 3. */
1549 int max_label_line = 1;
1551 int next_column = INT_MAX;
1552 line_label *label;
1553 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1555 /* Would this label "touch" or overlap the next label? */
1556 if (label->m_column + label->m_length >= (size_t)next_column)
1557 max_label_line++;
1559 label->m_label_line = max_label_line;
1560 next_column = label->m_column;
1564 /* Print the "label lines". For each label within the line, print
1565 either a vertical bar ('|') for the labels that are lower down, or the
1566 labels themselves once we've reached their line. */
1568 /* Keep track of in which column we last printed a vertical bar.
1569 This allows us to suppress duplicate vertical bars for the case
1570 where multiple labels are on one column. */
1571 int last_vbar = 0;
1572 for (int label_line = 0; label_line <= max_label_line; label_line++)
1574 start_annotation_line ();
1575 pp_space (m_pp);
1576 int column = 1 + m_x_offset;
1577 line_label *label;
1578 FOR_EACH_VEC_ELT (labels, i, label)
1580 if (label_line > label->m_label_line)
1581 /* We've printed all the labels for this label line. */
1582 break;
1584 if (label_line == label->m_label_line)
1586 gcc_assert (column <= label->m_column);
1587 move_to_column (&column, label->m_column, true);
1588 m_colorizer.set_range (label->m_state_idx);
1589 pp_string (m_pp, label->m_text.m_buffer);
1590 m_colorizer.set_normal_text ();
1591 column += label->m_length;
1593 else if (label->m_column != last_vbar)
1595 gcc_assert (column <= label->m_column);
1596 move_to_column (&column, label->m_column, true);
1597 m_colorizer.set_range (label->m_state_idx);
1598 pp_character (m_pp, '|');
1599 m_colorizer.set_normal_text ();
1600 last_vbar = column;
1601 column++;
1604 print_newline ();
1608 /* Clean up. */
1610 line_label *label;
1611 FOR_EACH_VEC_ELT (labels, i, label)
1612 label->m_text.maybe_free ();
1616 /* If there are any fixit hints inserting new lines before source line ROW,
1617 print them.
1619 They are printed on lines of their own, before the source line
1620 itself, with a leading '+'. */
1622 void
1623 layout::print_leading_fixits (linenum_type row)
1625 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1627 const fixit_hint *hint = m_fixit_hints[i];
1629 if (!hint->ends_with_newline_p ())
1630 /* Not a newline fixit; print it in print_trailing_fixits. */
1631 continue;
1633 gcc_assert (hint->insertion_p ());
1635 if (hint->affects_line_p (m_exploc.file, row))
1637 /* Printing the '+' with normal colorization
1638 and the inserted line with "insert" colorization
1639 helps them stand out from each other, and from
1640 the surrounding text. */
1641 m_colorizer.set_normal_text ();
1642 start_annotation_line ('+');
1643 pp_character (m_pp, '+');
1644 m_colorizer.set_fixit_insert ();
1645 /* Print all but the trailing newline of the fix-it hint.
1646 We have to print the newline separately to avoid
1647 getting additional pp prefixes printed. */
1648 for (size_t i = 0; i < hint->get_length () - 1; i++)
1649 pp_character (m_pp, hint->get_string ()[i]);
1650 m_colorizer.set_normal_text ();
1651 pp_newline (m_pp);
1656 /* Subroutine of layout::print_trailing_fixits.
1658 Determine if the annotation line printed for LINE contained
1659 the exact range from START_COLUMN to FINISH_COLUMN. */
1661 bool
1662 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1663 int finish_column) const
1665 layout_range *range;
1666 int i;
1667 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1668 if (range->m_start.m_line == line
1669 && range->m_start.m_column == start_column
1670 && range->m_finish.m_line == line
1671 && range->m_finish.m_column == finish_column)
1672 return true;
1673 return false;
1676 /* Classes for printing trailing fix-it hints i.e. those that
1677 don't add new lines.
1679 For insertion, these can look like:
1681 new_text
1683 For replacement, these can look like:
1685 ------------- : underline showing affected range
1686 new_text
1688 For deletion, these can look like:
1690 ------------- : underline showing affected range
1692 This can become confusing if they overlap, and so we need
1693 to do some preprocessing to decide what to print.
1694 We use the list of fixit_hint instances affecting the line
1695 to build a list of "correction" instances, and print the
1696 latter.
1698 For example, consider a set of fix-its for converting
1699 a C-style cast to a C++ const_cast.
1701 Given:
1703 ..000000000111111111122222222223333333333.
1704 ..123456789012345678901234567890123456789.
1705 foo *f = (foo *)ptr->field;
1706 ^~~~~
1708 and the fix-it hints:
1709 - replace col 10 (the open paren) with "const_cast<"
1710 - replace col 16 (the close paren) with "> ("
1711 - insert ")" before col 27
1713 then we would get odd-looking output:
1715 foo *f = (foo *)ptr->field;
1716 ^~~~~
1718 const_cast<
1720 > ( )
1722 It would be better to detect when fixit hints are going to
1723 overlap (those that require new lines), and to consolidate
1724 the printing of such fixits, giving something like:
1726 foo *f = (foo *)ptr->field;
1727 ^~~~~
1728 -----------------
1729 const_cast<foo *> (ptr->field)
1731 This works by detecting when the printing would overlap, and
1732 effectively injecting no-op replace hints into the gaps between
1733 such fix-its, so that the printing joins up.
1735 In the above example, the overlap of:
1736 - replace col 10 (the open paren) with "const_cast<"
1737 and:
1738 - replace col 16 (the close paren) with "> ("
1739 is fixed by injecting a no-op:
1740 - replace cols 11-15 with themselves ("foo *")
1741 and consolidating these, making:
1742 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1743 i.e.:
1744 - replace cols 10-16 with "const_cast<foo *> ("
1746 This overlaps with the final fix-it hint:
1747 - insert ")" before col 27
1748 and so we repeat the consolidation process, by injecting
1749 a no-op:
1750 - replace cols 17-26 with themselves ("ptr->field")
1751 giving:
1752 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1753 i.e.:
1754 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1756 and is thus printed as desired. */
1758 /* A range of columns within a line. */
1760 struct column_range
1762 column_range (int start_, int finish_) : start (start_), finish (finish_)
1764 /* We must have either a range, or an insertion. */
1765 gcc_assert (start <= finish || finish == start - 1);
1768 bool operator== (const column_range &other) const
1770 return start == other.start && finish == other.finish;
1773 int start;
1774 int finish;
1777 /* Get the range of columns that HINT would affect. */
1779 static column_range
1780 get_affected_columns (const fixit_hint *hint)
1782 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1783 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1785 return column_range (start_column, finish_column);
1788 /* Get the range of columns that would be printed for HINT. */
1790 static column_range
1791 get_printed_columns (const fixit_hint *hint)
1793 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1794 int final_hint_column = start_column + hint->get_length () - 1;
1795 if (hint->insertion_p ())
1797 return column_range (start_column, final_hint_column);
1799 else
1801 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1803 return column_range (start_column,
1804 MAX (finish_column, final_hint_column));
1808 /* A correction on a particular line.
1809 This describes a plan for how to print one or more fixit_hint
1810 instances that affected the line, potentially consolidating hints
1811 into corrections to make the result easier for the user to read. */
1813 struct correction
1815 correction (column_range affected_columns,
1816 column_range printed_columns,
1817 const char *new_text, size_t new_text_len)
1818 : m_affected_columns (affected_columns),
1819 m_printed_columns (printed_columns),
1820 m_text (xstrdup (new_text)),
1821 m_len (new_text_len),
1822 m_alloc_sz (new_text_len + 1)
1826 ~correction () { free (m_text); }
1828 bool insertion_p () const
1830 return m_affected_columns.start == m_affected_columns.finish + 1;
1833 void ensure_capacity (size_t len);
1834 void ensure_terminated ();
1836 void overwrite (int dst_offset, const char_span &src_span)
1838 gcc_assert (dst_offset >= 0);
1839 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1840 memcpy (m_text + dst_offset, src_span.get_buffer (),
1841 src_span.length ());
1844 /* If insert, then start: the column before which the text
1845 is to be inserted, and finish is offset by the length of
1846 the replacement.
1847 If replace, then the range of columns affected. */
1848 column_range m_affected_columns;
1850 /* If insert, then start: the column before which the text
1851 is to be inserted, and finish is offset by the length of
1852 the replacement.
1853 If replace, then the range of columns affected. */
1854 column_range m_printed_columns;
1856 /* The text to be inserted/used as replacement. */
1857 char *m_text;
1858 size_t m_len;
1859 size_t m_alloc_sz;
1862 /* Ensure that m_text can hold a string of length LEN
1863 (plus 1 for 0-termination). */
1865 void
1866 correction::ensure_capacity (size_t len)
1868 /* Allow 1 extra byte for 0-termination. */
1869 if (m_alloc_sz < (len + 1))
1871 size_t new_alloc_sz = (len + 1) * 2;
1872 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1873 m_alloc_sz = new_alloc_sz;
1877 /* Ensure that m_text is 0-terminated. */
1879 void
1880 correction::ensure_terminated ()
1882 /* 0-terminate the buffer. */
1883 gcc_assert (m_len < m_alloc_sz);
1884 m_text[m_len] = '\0';
1887 /* A list of corrections affecting a particular line.
1888 This is used by layout::print_trailing_fixits for planning
1889 how to print the fix-it hints affecting the line. */
1891 struct line_corrections
1893 line_corrections (const char *filename, linenum_type row)
1894 : m_filename (filename), m_row (row)
1896 ~line_corrections ();
1898 void add_hint (const fixit_hint *hint);
1900 const char *m_filename;
1901 linenum_type m_row;
1902 auto_vec <correction *> m_corrections;
1905 /* struct line_corrections. */
1907 line_corrections::~line_corrections ()
1909 unsigned i;
1910 correction *c;
1911 FOR_EACH_VEC_ELT (m_corrections, i, c)
1912 delete c;
1915 /* A struct wrapping a particular source line, allowing
1916 run-time bounds-checking of accesses in a checked build. */
1918 struct source_line
1920 source_line (const char *filename, int line);
1922 char_span as_span () { return char_span (chars, width); }
1924 const char *chars;
1925 int width;
1928 /* source_line's ctor. */
1930 source_line::source_line (const char *filename, int line)
1932 char_span span = location_get_source_line (filename, line);
1933 chars = span.get_buffer ();
1934 width = span.length ();
1937 /* Add HINT to the corrections for this line.
1938 Attempt to consolidate nearby hints so that they will not
1939 overlap with printed. */
1941 void
1942 line_corrections::add_hint (const fixit_hint *hint)
1944 column_range affected_columns = get_affected_columns (hint);
1945 column_range printed_columns = get_printed_columns (hint);
1947 /* Potentially consolidate. */
1948 if (!m_corrections.is_empty ())
1950 correction *last_correction
1951 = m_corrections[m_corrections.length () - 1];
1953 /* The following consolidation code assumes that the fix-it hints
1954 have been sorted by start (done within layout's ctor). */
1955 gcc_assert (affected_columns.start
1956 >= last_correction->m_affected_columns.start);
1957 gcc_assert (printed_columns.start
1958 >= last_correction->m_printed_columns.start);
1960 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1962 /* We have two hints for which the printed forms of the hints
1963 would touch or overlap, so we need to consolidate them to avoid
1964 confusing the user.
1965 Attempt to inject a "replace" correction from immediately
1966 after the end of the last hint to immediately before the start
1967 of the next hint. */
1968 column_range between (last_correction->m_affected_columns.finish + 1,
1969 printed_columns.start - 1);
1971 /* Try to read the source. */
1972 source_line line (m_filename, m_row);
1973 if (line.chars && between.finish < line.width)
1975 /* Consolidate into the last correction:
1976 add a no-op "replace" of the "between" text, and
1977 add the text from the new hint. */
1978 int old_len = last_correction->m_len;
1979 gcc_assert (old_len >= 0);
1980 int between_len = between.finish + 1 - between.start;
1981 gcc_assert (between_len >= 0);
1982 int new_len = old_len + between_len + hint->get_length ();
1983 gcc_assert (new_len >= 0);
1984 last_correction->ensure_capacity (new_len);
1985 last_correction->overwrite
1986 (old_len,
1987 line.as_span ().subspan (between.start - 1,
1988 between.finish + 1 - between.start));
1989 last_correction->overwrite (old_len + between_len,
1990 char_span (hint->get_string (),
1991 hint->get_length ()));
1992 last_correction->m_len = new_len;
1993 last_correction->ensure_terminated ();
1994 last_correction->m_affected_columns.finish
1995 = affected_columns.finish;
1996 last_correction->m_printed_columns.finish
1997 += between_len + hint->get_length ();
1998 return;
2003 /* If no consolidation happened, add a new correction instance. */
2004 m_corrections.safe_push (new correction (affected_columns,
2005 printed_columns,
2006 hint->get_string (),
2007 hint->get_length ()));
2010 /* If there are any fixit hints on source line ROW, print them.
2011 They are printed in order, attempting to combine them onto lines, but
2012 starting new lines if necessary.
2013 Fix-it hints that insert new lines are handled separately,
2014 in layout::print_leading_fixits. */
2016 void
2017 layout::print_trailing_fixits (linenum_type row)
2019 /* Build a list of correction instances for the line,
2020 potentially consolidating hints (for the sake of readability). */
2021 line_corrections corrections (m_exploc.file, row);
2022 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2024 const fixit_hint *hint = m_fixit_hints[i];
2026 /* Newline fixits are handled by layout::print_leading_fixits. */
2027 if (hint->ends_with_newline_p ())
2028 continue;
2030 if (hint->affects_line_p (m_exploc.file, row))
2031 corrections.add_hint (hint);
2034 /* Now print the corrections. */
2035 unsigned i;
2036 correction *c;
2037 int column = m_x_offset;
2039 if (!corrections.m_corrections.is_empty ())
2040 start_annotation_line ();
2042 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2044 /* For now we assume each fixit hint can only touch one line. */
2045 if (c->insertion_p ())
2047 /* This assumes the insertion just affects one line. */
2048 int start_column = c->m_printed_columns.start;
2049 move_to_column (&column, start_column, true);
2050 m_colorizer.set_fixit_insert ();
2051 pp_string (m_pp, c->m_text);
2052 m_colorizer.set_normal_text ();
2053 column += c->m_len;
2055 else
2057 /* If the range of the replacement wasn't printed in the
2058 annotation line, then print an extra underline to
2059 indicate exactly what is being replaced.
2060 Always show it for removals. */
2061 int start_column = c->m_affected_columns.start;
2062 int finish_column = c->m_affected_columns.finish;
2063 if (!annotation_line_showed_range_p (row, start_column,
2064 finish_column)
2065 || c->m_len == 0)
2067 move_to_column (&column, start_column, true);
2068 m_colorizer.set_fixit_delete ();
2069 for (; column <= finish_column; column++)
2070 pp_character (m_pp, '-');
2071 m_colorizer.set_normal_text ();
2073 /* Print the replacement text. REPLACE also covers
2074 removals, so only do this extra work (potentially starting
2075 a new line) if we have actual replacement text. */
2076 if (c->m_len > 0)
2078 move_to_column (&column, start_column, true);
2079 m_colorizer.set_fixit_insert ();
2080 pp_string (m_pp, c->m_text);
2081 m_colorizer.set_normal_text ();
2082 column += c->m_len;
2087 /* Add a trailing newline, if necessary. */
2088 move_to_column (&column, 0, false);
2091 /* Disable any colorization and emit a newline. */
2093 void
2094 layout::print_newline ()
2096 m_colorizer.set_normal_text ();
2097 pp_newline (m_pp);
2100 /* Return true if (ROW/COLUMN) is within a range of the layout.
2101 If it returns true, OUT_STATE is written to, with the
2102 range index, and whether we should draw the caret at
2103 (ROW/COLUMN) (as opposed to an underline). */
2105 bool
2106 layout::get_state_at_point (/* Inputs. */
2107 linenum_type row, int column,
2108 int first_non_ws, int last_non_ws,
2109 /* Outputs. */
2110 point_state *out_state)
2112 layout_range *range;
2113 int i;
2114 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2116 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2117 /* Bail out early, so that such ranges don't affect underlining or
2118 source colorization. */
2119 continue;
2121 if (range->contains_point (row, column))
2123 out_state->range_idx = i;
2125 /* Are we at the range's caret? is it visible? */
2126 out_state->draw_caret_p = false;
2127 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2128 && row == range->m_caret.m_line
2129 && column == range->m_caret.m_column)
2130 out_state->draw_caret_p = true;
2132 /* Within a multiline range, don't display any underline
2133 in any leading or trailing whitespace on a line.
2134 We do display carets, however. */
2135 if (!out_state->draw_caret_p)
2136 if (column < first_non_ws || column > last_non_ws)
2137 return false;
2139 /* We are within a range. */
2140 return true;
2144 return false;
2147 /* Helper function for use by layout::print_line when printing the
2148 annotation line under the source line.
2149 Get the column beyond the rightmost one that could contain a caret or
2150 range marker, given that we stop rendering at trailing whitespace.
2151 ROW is the source line within the given file.
2152 CARET_COLUMN is the column of range 0's caret.
2153 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2154 character of source (as determined when printing the source line). */
2157 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2158 int last_non_ws_column)
2160 int result = caret_column + 1;
2162 layout_range *range;
2163 int i;
2164 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2166 if (row >= range->m_start.m_line)
2168 if (range->m_finish.m_line == row)
2170 /* On the final line within a range; ensure that
2171 we render up to the end of the range. */
2172 if (result <= range->m_finish.m_column)
2173 result = range->m_finish.m_column + 1;
2175 else if (row < range->m_finish.m_line)
2177 /* Within a multiline range; ensure that we render up to the
2178 last non-whitespace column. */
2179 if (result <= last_non_ws_column)
2180 result = last_non_ws_column + 1;
2185 return result;
2188 /* Given *COLUMN as an x-coordinate, print spaces to position
2189 successive output at DEST_COLUMN, printing a newline if necessary,
2190 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2191 left margin after any newline. */
2193 void
2194 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2196 /* Start a new line if we need to. */
2197 if (*column > dest_column)
2199 print_newline ();
2200 if (add_left_margin)
2201 start_annotation_line ();
2202 *column = m_x_offset;
2205 while (*column < dest_column)
2207 pp_space (m_pp);
2208 (*column)++;
2212 /* For debugging layout issues, render a ruler giving column numbers
2213 (after the 1-column indent). */
2215 void
2216 layout::show_ruler (int max_column) const
2218 /* Hundreds. */
2219 if (max_column > 99)
2221 start_annotation_line ();
2222 pp_space (m_pp);
2223 for (int column = 1 + m_x_offset; column <= max_column; column++)
2224 if (column % 10 == 0)
2225 pp_character (m_pp, '0' + (column / 100) % 10);
2226 else
2227 pp_space (m_pp);
2228 pp_newline (m_pp);
2231 /* Tens. */
2232 start_annotation_line ();
2233 pp_space (m_pp);
2234 for (int column = 1 + m_x_offset; column <= max_column; column++)
2235 if (column % 10 == 0)
2236 pp_character (m_pp, '0' + (column / 10) % 10);
2237 else
2238 pp_space (m_pp);
2239 pp_newline (m_pp);
2241 /* Units. */
2242 start_annotation_line ();
2243 pp_space (m_pp);
2244 for (int column = 1 + m_x_offset; column <= max_column; column++)
2245 pp_character (m_pp, '0' + (column % 10));
2246 pp_newline (m_pp);
2249 /* Print leading fix-its (for new lines inserted before the source line)
2250 then the source line, followed by an annotation line
2251 consisting of any caret/underlines, then any fixits.
2252 If the source line can't be read, print nothing. */
2253 void
2254 layout::print_line (linenum_type row)
2256 char_span line = location_get_source_line (m_exploc.file, row);
2257 if (!line)
2258 return;
2260 line_bounds lbounds;
2261 print_leading_fixits (row);
2262 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2263 if (should_print_annotation_line_p (row))
2264 print_annotation_line (row, lbounds);
2265 if (m_show_labels_p)
2266 print_any_labels (row);
2267 print_trailing_fixits (row);
2270 } /* End of anonymous namespace. */
2272 /* If LOC is within the spans of lines that will already be printed for
2273 this gcc_rich_location, then add it as a secondary location and return true.
2275 Otherwise return false. */
2277 bool
2278 gcc_rich_location::add_location_if_nearby (location_t loc)
2280 /* Use the layout location-handling logic to sanitize LOC,
2281 filtering it to the current line spans within a temporary
2282 layout instance. */
2283 layout layout (global_dc, this, DK_ERROR);
2284 location_range loc_range;
2285 loc_range.m_loc = loc;
2286 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2287 if (!layout.maybe_add_location_range (&loc_range, 0, true))
2288 return false;
2290 add_range (loc);
2291 return true;
2294 /* Print the physical source code corresponding to the location of
2295 this diagnostic, with additional annotations. */
2297 void
2298 diagnostic_show_locus (diagnostic_context * context,
2299 rich_location *richloc,
2300 diagnostic_t diagnostic_kind)
2302 pp_newline (context->printer);
2304 location_t loc = richloc->get_loc ();
2305 /* Do nothing if source-printing has been disabled. */
2306 if (!context->show_caret)
2307 return;
2309 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2310 if (loc <= BUILTINS_LOCATION)
2311 return;
2313 /* Don't print the same source location twice in a row, unless we have
2314 fix-it hints. */
2315 if (loc == context->last_location
2316 && richloc->get_num_fixit_hints () == 0)
2317 return;
2319 context->last_location = loc;
2321 char *saved_prefix = pp_take_prefix (context->printer);
2322 pp_set_prefix (context->printer, NULL);
2324 layout layout (context, richloc, diagnostic_kind);
2325 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2326 line_span_idx++)
2328 const line_span *line_span = layout.get_line_span (line_span_idx);
2329 if (context->show_line_numbers_p)
2331 /* With line numbers, we should show whenever the line-numbering
2332 "jumps". */
2333 if (line_span_idx > 0)
2334 layout.print_gap_in_line_numbering ();
2336 else
2338 /* Without line numbers, we print headings for some line spans. */
2339 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2341 expanded_location exploc
2342 = layout.get_expanded_location (line_span);
2343 context->start_span (context, exploc);
2346 linenum_type last_line = line_span->get_last_line ();
2347 for (linenum_type row = line_span->get_first_line ();
2348 row <= last_line; row++)
2349 layout.print_line (row);
2352 pp_set_prefix (context->printer, saved_prefix);
2355 #if CHECKING_P
2357 namespace selftest {
2359 /* Selftests for diagnostic_show_locus. */
2361 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2363 static void
2364 test_diagnostic_show_locus_unknown_location ()
2366 test_diagnostic_context dc;
2367 rich_location richloc (line_table, UNKNOWN_LOCATION);
2368 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2369 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2372 /* Verify that diagnostic_show_locus works sanely for various
2373 single-line cases.
2375 All of these work on the following 1-line source file:
2376 .0000000001111111
2377 .1234567890123456
2378 "foo = bar.field;\n"
2379 which is set up by test_diagnostic_show_locus_one_liner and calls
2380 them. */
2382 /* Just a caret. */
2384 static void
2385 test_one_liner_simple_caret ()
2387 test_diagnostic_context dc;
2388 location_t caret = linemap_position_for_column (line_table, 10);
2389 rich_location richloc (line_table, caret);
2390 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2391 ASSERT_STREQ ("\n"
2392 " foo = bar.field;\n"
2393 " ^\n",
2394 pp_formatted_text (dc.printer));
2397 /* Caret and range. */
2399 static void
2400 test_one_liner_caret_and_range ()
2402 test_diagnostic_context dc;
2403 location_t caret = linemap_position_for_column (line_table, 10);
2404 location_t start = linemap_position_for_column (line_table, 7);
2405 location_t finish = linemap_position_for_column (line_table, 15);
2406 location_t loc = make_location (caret, start, finish);
2407 rich_location richloc (line_table, loc);
2408 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2409 ASSERT_STREQ ("\n"
2410 " foo = bar.field;\n"
2411 " ~~~^~~~~~\n",
2412 pp_formatted_text (dc.printer));
2415 /* Multiple ranges and carets. */
2417 static void
2418 test_one_liner_multiple_carets_and_ranges ()
2420 test_diagnostic_context dc;
2421 location_t foo
2422 = make_location (linemap_position_for_column (line_table, 2),
2423 linemap_position_for_column (line_table, 1),
2424 linemap_position_for_column (line_table, 3));
2425 dc.caret_chars[0] = 'A';
2427 location_t bar
2428 = make_location (linemap_position_for_column (line_table, 8),
2429 linemap_position_for_column (line_table, 7),
2430 linemap_position_for_column (line_table, 9));
2431 dc.caret_chars[1] = 'B';
2433 location_t field
2434 = make_location (linemap_position_for_column (line_table, 13),
2435 linemap_position_for_column (line_table, 11),
2436 linemap_position_for_column (line_table, 15));
2437 dc.caret_chars[2] = 'C';
2439 rich_location richloc (line_table, foo);
2440 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2441 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2442 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2443 ASSERT_STREQ ("\n"
2444 " foo = bar.field;\n"
2445 " ~A~ ~B~ ~~C~~\n",
2446 pp_formatted_text (dc.printer));
2449 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2451 static void
2452 test_one_liner_fixit_insert_before ()
2454 test_diagnostic_context dc;
2455 location_t caret = linemap_position_for_column (line_table, 7);
2456 rich_location richloc (line_table, caret);
2457 richloc.add_fixit_insert_before ("&");
2458 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2459 ASSERT_STREQ ("\n"
2460 " foo = bar.field;\n"
2461 " ^\n"
2462 " &\n",
2463 pp_formatted_text (dc.printer));
2466 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2468 static void
2469 test_one_liner_fixit_insert_after ()
2471 test_diagnostic_context dc;
2472 location_t start = linemap_position_for_column (line_table, 1);
2473 location_t finish = linemap_position_for_column (line_table, 3);
2474 location_t foo = make_location (start, start, finish);
2475 rich_location richloc (line_table, foo);
2476 richloc.add_fixit_insert_after ("[0]");
2477 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2478 ASSERT_STREQ ("\n"
2479 " foo = bar.field;\n"
2480 " ^~~\n"
2481 " [0]\n",
2482 pp_formatted_text (dc.printer));
2485 /* Removal fix-it hint: removal of the ".field". */
2487 static void
2488 test_one_liner_fixit_remove ()
2490 test_diagnostic_context dc;
2491 location_t start = linemap_position_for_column (line_table, 10);
2492 location_t finish = linemap_position_for_column (line_table, 15);
2493 location_t dot = make_location (start, start, finish);
2494 rich_location richloc (line_table, dot);
2495 richloc.add_fixit_remove ();
2496 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2497 ASSERT_STREQ ("\n"
2498 " foo = bar.field;\n"
2499 " ^~~~~~\n"
2500 " ------\n",
2501 pp_formatted_text (dc.printer));
2504 /* Replace fix-it hint: replacing "field" with "m_field". */
2506 static void
2507 test_one_liner_fixit_replace ()
2509 test_diagnostic_context dc;
2510 location_t start = linemap_position_for_column (line_table, 11);
2511 location_t finish = linemap_position_for_column (line_table, 15);
2512 location_t field = make_location (start, start, finish);
2513 rich_location richloc (line_table, field);
2514 richloc.add_fixit_replace ("m_field");
2515 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2516 ASSERT_STREQ ("\n"
2517 " foo = bar.field;\n"
2518 " ^~~~~\n"
2519 " m_field\n",
2520 pp_formatted_text (dc.printer));
2523 /* Replace fix-it hint: replacing "field" with "m_field",
2524 but where the caret was elsewhere. */
2526 static void
2527 test_one_liner_fixit_replace_non_equal_range ()
2529 test_diagnostic_context dc;
2530 location_t equals = linemap_position_for_column (line_table, 5);
2531 location_t start = linemap_position_for_column (line_table, 11);
2532 location_t finish = linemap_position_for_column (line_table, 15);
2533 rich_location richloc (line_table, equals);
2534 source_range range;
2535 range.m_start = start;
2536 range.m_finish = finish;
2537 richloc.add_fixit_replace (range, "m_field");
2538 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2539 /* The replacement range is not indicated in the annotation line, so
2540 it should be indicated via an additional underline. */
2541 ASSERT_STREQ ("\n"
2542 " foo = bar.field;\n"
2543 " ^\n"
2544 " -----\n"
2545 " m_field\n",
2546 pp_formatted_text (dc.printer));
2549 /* Replace fix-it hint: replacing "field" with "m_field",
2550 where the caret was elsewhere, but where a secondary range
2551 exactly covers "field". */
2553 static void
2554 test_one_liner_fixit_replace_equal_secondary_range ()
2556 test_diagnostic_context dc;
2557 location_t equals = linemap_position_for_column (line_table, 5);
2558 location_t start = linemap_position_for_column (line_table, 11);
2559 location_t finish = linemap_position_for_column (line_table, 15);
2560 rich_location richloc (line_table, equals);
2561 location_t field = make_location (start, start, finish);
2562 richloc.add_range (field);
2563 richloc.add_fixit_replace (field, "m_field");
2564 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2565 /* The replacement range is indicated in the annotation line,
2566 so it shouldn't be indicated via an additional underline. */
2567 ASSERT_STREQ ("\n"
2568 " foo = bar.field;\n"
2569 " ^ ~~~~~\n"
2570 " m_field\n",
2571 pp_formatted_text (dc.printer));
2574 /* Verify that we can use ad-hoc locations when adding fixits to a
2575 rich_location. */
2577 static void
2578 test_one_liner_fixit_validation_adhoc_locations ()
2580 /* Generate a range that's too long to be packed, so must
2581 be stored as an ad-hoc location (given the defaults
2582 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2583 const location_t c7 = linemap_position_for_column (line_table, 7);
2584 const location_t c47 = linemap_position_for_column (line_table, 47);
2585 const location_t loc = make_location (c7, c7, c47);
2587 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2588 return;
2590 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2592 /* Insert. */
2594 rich_location richloc (line_table, loc);
2595 richloc.add_fixit_insert_before (loc, "test");
2596 /* It should not have been discarded by the validator. */
2597 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2599 test_diagnostic_context dc;
2600 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2601 ASSERT_STREQ ("\n"
2602 " foo = bar.field;\n"
2603 " ^~~~~~~~~~ \n"
2604 " test\n",
2605 pp_formatted_text (dc.printer));
2608 /* Remove. */
2610 rich_location richloc (line_table, loc);
2611 source_range range = source_range::from_locations (loc, c47);
2612 richloc.add_fixit_remove (range);
2613 /* It should not have been discarded by the validator. */
2614 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2616 test_diagnostic_context dc;
2617 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2618 ASSERT_STREQ ("\n"
2619 " foo = bar.field;\n"
2620 " ^~~~~~~~~~ \n"
2621 " -----------------------------------------\n",
2622 pp_formatted_text (dc.printer));
2625 /* Replace. */
2627 rich_location richloc (line_table, loc);
2628 source_range range = source_range::from_locations (loc, c47);
2629 richloc.add_fixit_replace (range, "test");
2630 /* It should not have been discarded by the validator. */
2631 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2633 test_diagnostic_context dc;
2634 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2635 ASSERT_STREQ ("\n"
2636 " foo = bar.field;\n"
2637 " ^~~~~~~~~~ \n"
2638 " test\n",
2639 pp_formatted_text (dc.printer));
2643 /* Test of consolidating insertions at the same location. */
2645 static void
2646 test_one_liner_many_fixits_1 ()
2648 test_diagnostic_context dc;
2649 location_t equals = linemap_position_for_column (line_table, 5);
2650 rich_location richloc (line_table, equals);
2651 for (int i = 0; i < 19; i++)
2652 richloc.add_fixit_insert_before ("a");
2653 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2654 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2655 ASSERT_STREQ ("\n"
2656 " foo = bar.field;\n"
2657 " ^\n"
2658 " aaaaaaaaaaaaaaaaaaa\n",
2659 pp_formatted_text (dc.printer));
2662 /* Ensure that we can add an arbitrary number of fix-it hints to a
2663 rich_location, even if they are not consolidated. */
2665 static void
2666 test_one_liner_many_fixits_2 ()
2668 test_diagnostic_context dc;
2669 location_t equals = linemap_position_for_column (line_table, 5);
2670 rich_location richloc (line_table, equals);
2671 for (int i = 0; i < 19; i++)
2673 location_t loc = linemap_position_for_column (line_table, i * 2);
2674 richloc.add_fixit_insert_before (loc, "a");
2676 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2677 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2678 ASSERT_STREQ ("\n"
2679 " foo = bar.field;\n"
2680 " ^\n"
2681 "a a a a a a a a a a a a a a a a a a a\n",
2682 pp_formatted_text (dc.printer));
2685 /* Test of labeling the ranges within a rich_location. */
2687 static void
2688 test_one_liner_labels ()
2690 location_t foo
2691 = make_location (linemap_position_for_column (line_table, 1),
2692 linemap_position_for_column (line_table, 1),
2693 linemap_position_for_column (line_table, 3));
2694 location_t bar
2695 = make_location (linemap_position_for_column (line_table, 7),
2696 linemap_position_for_column (line_table, 7),
2697 linemap_position_for_column (line_table, 9));
2698 location_t field
2699 = make_location (linemap_position_for_column (line_table, 11),
2700 linemap_position_for_column (line_table, 11),
2701 linemap_position_for_column (line_table, 15));
2703 /* Example where all the labels fit on one line. */
2705 text_range_label label0 ("0");
2706 text_range_label label1 ("1");
2707 text_range_label label2 ("2");
2708 gcc_rich_location richloc (foo, &label0);
2709 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2710 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2713 test_diagnostic_context dc;
2714 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2715 ASSERT_STREQ ("\n"
2716 " foo = bar.field;\n"
2717 " ^~~ ~~~ ~~~~~\n"
2718 " | | |\n"
2719 " 0 1 2\n",
2720 pp_formatted_text (dc.printer));
2723 /* Verify that we can disable label-printing. */
2725 test_diagnostic_context dc;
2726 dc.show_labels_p = false;
2727 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2728 ASSERT_STREQ ("\n"
2729 " foo = bar.field;\n"
2730 " ^~~ ~~~ ~~~~~\n",
2731 pp_formatted_text (dc.printer));
2735 /* Example where the labels need extra lines. */
2737 text_range_label label0 ("label 0");
2738 text_range_label label1 ("label 1");
2739 text_range_label label2 ("label 2");
2740 gcc_rich_location richloc (foo, &label0);
2741 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2742 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2744 test_diagnostic_context dc;
2745 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2746 ASSERT_STREQ ("\n"
2747 " foo = bar.field;\n"
2748 " ^~~ ~~~ ~~~~~\n"
2749 " | | |\n"
2750 " | | label 2\n"
2751 " | label 1\n"
2752 " label 0\n",
2753 pp_formatted_text (dc.printer));
2756 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2757 but label 1 just touches label 2. */
2759 text_range_label label0 ("aaaaa");
2760 text_range_label label1 ("bbbb");
2761 text_range_label label2 ("c");
2762 gcc_rich_location richloc (foo, &label0);
2763 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2764 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2766 test_diagnostic_context dc;
2767 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2768 ASSERT_STREQ ("\n"
2769 " foo = bar.field;\n"
2770 " ^~~ ~~~ ~~~~~\n"
2771 " | | |\n"
2772 " | | c\n"
2773 " aaaaa bbbb\n",
2774 pp_formatted_text (dc.printer));
2777 /* Example of out-of-order ranges (thus requiring a sort). */
2779 text_range_label label0 ("0");
2780 text_range_label label1 ("1");
2781 text_range_label label2 ("2");
2782 gcc_rich_location richloc (field, &label0);
2783 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2784 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2786 test_diagnostic_context dc;
2787 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2788 ASSERT_STREQ ("\n"
2789 " foo = bar.field;\n"
2790 " ~~~ ~~~ ^~~~~\n"
2791 " | | |\n"
2792 " 2 1 0\n",
2793 pp_formatted_text (dc.printer));
2796 /* Ensure we don't ICE if multiple ranges with labels are on
2797 the same point. */
2799 text_range_label label0 ("label 0");
2800 text_range_label label1 ("label 1");
2801 text_range_label label2 ("label 2");
2802 gcc_rich_location richloc (bar, &label0);
2803 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2804 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2806 test_diagnostic_context dc;
2807 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2808 ASSERT_STREQ ("\n"
2809 " foo = bar.field;\n"
2810 " ^~~\n"
2811 " |\n"
2812 " label 2\n"
2813 " label 1\n"
2814 " label 0\n",
2815 pp_formatted_text (dc.printer));
2818 /* Verify that a NULL result from range_label::get_text is
2819 handled gracefully. */
2821 text_range_label label (NULL);
2822 gcc_rich_location richloc (bar, &label);
2824 test_diagnostic_context dc;
2825 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2826 ASSERT_STREQ ("\n"
2827 " foo = bar.field;\n"
2828 " ^~~\n",
2829 pp_formatted_text (dc.printer));
2832 /* TODO: example of formatted printing (needs to be in
2833 gcc-rich-location.c due to Makefile.in issues). */
2836 /* Run the various one-liner tests. */
2838 static void
2839 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2841 /* Create a tempfile and write some text to it.
2842 ....................0000000001111111.
2843 ....................1234567890123456. */
2844 const char *content = "foo = bar.field;\n";
2845 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2846 line_table_test ltt (case_);
2848 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2850 location_t line_end = linemap_position_for_column (line_table, 16);
2852 /* Don't attempt to run the tests if column data might be unavailable. */
2853 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2854 return;
2856 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2857 ASSERT_EQ (1, LOCATION_LINE (line_end));
2858 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2860 test_one_liner_simple_caret ();
2861 test_one_liner_caret_and_range ();
2862 test_one_liner_multiple_carets_and_ranges ();
2863 test_one_liner_fixit_insert_before ();
2864 test_one_liner_fixit_insert_after ();
2865 test_one_liner_fixit_remove ();
2866 test_one_liner_fixit_replace ();
2867 test_one_liner_fixit_replace_non_equal_range ();
2868 test_one_liner_fixit_replace_equal_secondary_range ();
2869 test_one_liner_fixit_validation_adhoc_locations ();
2870 test_one_liner_many_fixits_1 ();
2871 test_one_liner_many_fixits_2 ();
2872 test_one_liner_labels ();
2875 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2877 static void
2878 test_add_location_if_nearby (const line_table_case &case_)
2880 /* Create a tempfile and write some text to it.
2881 ...000000000111111111122222222223333333333.
2882 ...123456789012345678901234567890123456789. */
2883 const char *content
2884 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2885 "struct different_line\n" /* line 2. */
2886 "{\n" /* line 3. */
2887 " double x;\n" /* line 4. */
2888 " double y;\n" /* line 5. */
2889 ";\n"); /* line 6. */
2890 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2891 line_table_test ltt (case_);
2893 const line_map_ordinary *ord_map
2894 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2895 tmp.get_filename (), 0));
2897 linemap_line_start (line_table, 1, 100);
2899 const location_t final_line_end
2900 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2902 /* Don't attempt to run the tests if column data might be unavailable. */
2903 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2904 return;
2906 /* Test of add_location_if_nearby on the same line as the
2907 primary location. */
2909 const location_t missing_close_brace_1_39
2910 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2911 const location_t matching_open_brace_1_18
2912 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2913 gcc_rich_location richloc (missing_close_brace_1_39);
2914 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2915 ASSERT_TRUE (added);
2916 ASSERT_EQ (2, richloc.get_num_locations ());
2917 test_diagnostic_context dc;
2918 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2919 ASSERT_STREQ ("\n"
2920 " struct same_line { double x; double y; ;\n"
2921 " ~ ^\n",
2922 pp_formatted_text (dc.printer));
2925 /* Test of add_location_if_nearby on a different line to the
2926 primary location. */
2928 const location_t missing_close_brace_6_1
2929 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2930 const location_t matching_open_brace_3_1
2931 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2932 gcc_rich_location richloc (missing_close_brace_6_1);
2933 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2934 ASSERT_FALSE (added);
2935 ASSERT_EQ (1, richloc.get_num_locations ());
2939 /* Verify that we print fixits even if they only affect lines
2940 outside those covered by the ranges in the rich_location. */
2942 static void
2943 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2945 /* Create a tempfile and write some text to it.
2946 ...000000000111111111122222222223333333333.
2947 ...123456789012345678901234567890123456789. */
2948 const char *content
2949 = ("struct point { double x; double y; };\n" /* line 1. */
2950 "struct point origin = {x: 0.0,\n" /* line 2. */
2951 " y\n" /* line 3. */
2952 "\n" /* line 4. */
2953 "\n" /* line 5. */
2954 " : 0.0};\n"); /* line 6. */
2955 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2956 line_table_test ltt (case_);
2958 const line_map_ordinary *ord_map
2959 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2960 tmp.get_filename (), 0));
2962 linemap_line_start (line_table, 1, 100);
2964 const location_t final_line_end
2965 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2967 /* Don't attempt to run the tests if column data might be unavailable. */
2968 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2969 return;
2971 /* A pair of tests for modernizing the initializers to C99-style. */
2973 /* The one-liner case (line 2). */
2975 test_diagnostic_context dc;
2976 const location_t x
2977 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2978 const location_t colon
2979 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2980 rich_location richloc (line_table, colon);
2981 richloc.add_fixit_insert_before (x, ".");
2982 richloc.add_fixit_replace (colon, "=");
2983 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2984 ASSERT_STREQ ("\n"
2985 " struct point origin = {x: 0.0,\n"
2986 " ^\n"
2987 " .=\n",
2988 pp_formatted_text (dc.printer));
2991 /* The multiline case. The caret for the rich_location is on line 6;
2992 verify that insertion fixit on line 3 is still printed (and that
2993 span starts are printed due to the gap between the span at line 3
2994 and that at line 6). */
2996 test_diagnostic_context dc;
2997 const location_t y
2998 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2999 const location_t colon
3000 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3001 rich_location richloc (line_table, colon);
3002 richloc.add_fixit_insert_before (y, ".");
3003 richloc.add_fixit_replace (colon, "=");
3004 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3005 ASSERT_STREQ ("\n"
3006 "FILENAME:3:24:\n"
3007 " y\n"
3008 " .\n"
3009 "FILENAME:6:25:\n"
3010 " : 0.0};\n"
3011 " ^\n"
3012 " =\n",
3013 pp_formatted_text (dc.printer));
3016 /* As above, but verify the behavior of multiple line spans
3017 with line-numbering enabled. */
3019 const location_t y
3020 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3021 const location_t colon
3022 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3023 rich_location richloc (line_table, colon);
3024 richloc.add_fixit_insert_before (y, ".");
3025 richloc.add_fixit_replace (colon, "=");
3026 test_diagnostic_context dc;
3027 dc.show_line_numbers_p = true;
3028 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3029 ASSERT_STREQ ("\n"
3030 " 3 | y\n"
3031 " | .\n"
3032 "....\n"
3033 " 6 | : 0.0};\n"
3034 " | ^\n"
3035 " | =\n",
3036 pp_formatted_text (dc.printer));
3041 /* Verify that fix-it hints are appropriately consolidated.
3043 If any fix-it hints in a rich_location involve locations beyond
3044 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3045 the fix-it as a whole, so there should be none.
3047 Otherwise, verify that consecutive "replace" and "remove" fix-its
3048 are merged, and that other fix-its remain separate. */
3050 static void
3051 test_fixit_consolidation (const line_table_case &case_)
3053 line_table_test ltt (case_);
3055 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3057 const location_t c10 = linemap_position_for_column (line_table, 10);
3058 const location_t c15 = linemap_position_for_column (line_table, 15);
3059 const location_t c16 = linemap_position_for_column (line_table, 16);
3060 const location_t c17 = linemap_position_for_column (line_table, 17);
3061 const location_t c20 = linemap_position_for_column (line_table, 20);
3062 const location_t c21 = linemap_position_for_column (line_table, 21);
3063 const location_t caret = c10;
3065 /* Insert + insert. */
3067 rich_location richloc (line_table, caret);
3068 richloc.add_fixit_insert_before (c10, "foo");
3069 richloc.add_fixit_insert_before (c15, "bar");
3071 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3072 /* Bogus column info for 2nd fixit, so no fixits. */
3073 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3074 else
3075 /* They should not have been merged. */
3076 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3079 /* Insert + replace. */
3081 rich_location richloc (line_table, caret);
3082 richloc.add_fixit_insert_before (c10, "foo");
3083 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3084 "bar");
3086 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3087 /* Bogus column info for 2nd fixit, so no fixits. */
3088 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3089 else
3090 /* They should not have been merged. */
3091 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3094 /* Replace + non-consecutive insert. */
3096 rich_location richloc (line_table, caret);
3097 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3098 "bar");
3099 richloc.add_fixit_insert_before (c17, "foo");
3101 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3102 /* Bogus column info for 2nd fixit, so no fixits. */
3103 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3104 else
3105 /* They should not have been merged. */
3106 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3109 /* Replace + non-consecutive replace. */
3111 rich_location richloc (line_table, caret);
3112 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3113 "foo");
3114 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3115 "bar");
3117 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3118 /* Bogus column info for 2nd fixit, so no fixits. */
3119 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3120 else
3121 /* They should not have been merged. */
3122 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3125 /* Replace + consecutive replace. */
3127 rich_location richloc (line_table, caret);
3128 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3129 "foo");
3130 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3131 "bar");
3133 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3134 /* Bogus column info for 2nd fixit, so no fixits. */
3135 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3136 else
3138 /* They should have been merged into a single "replace". */
3139 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3140 const fixit_hint *hint = richloc.get_fixit_hint (0);
3141 ASSERT_STREQ ("foobar", hint->get_string ());
3142 ASSERT_EQ (c10, hint->get_start_loc ());
3143 ASSERT_EQ (c21, hint->get_next_loc ());
3147 /* Replace + consecutive removal. */
3149 rich_location richloc (line_table, caret);
3150 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3151 "foo");
3152 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3154 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3155 /* Bogus column info for 2nd fixit, so no fixits. */
3156 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3157 else
3159 /* They should have been merged into a single replace, with the
3160 range extended to cover that of the removal. */
3161 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3162 const fixit_hint *hint = richloc.get_fixit_hint (0);
3163 ASSERT_STREQ ("foo", hint->get_string ());
3164 ASSERT_EQ (c10, hint->get_start_loc ());
3165 ASSERT_EQ (c21, hint->get_next_loc ());
3169 /* Consecutive removals. */
3171 rich_location richloc (line_table, caret);
3172 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3173 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3175 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3176 /* Bogus column info for 2nd fixit, so no fixits. */
3177 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3178 else
3180 /* They should have been merged into a single "replace-with-empty". */
3181 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3182 const fixit_hint *hint = richloc.get_fixit_hint (0);
3183 ASSERT_STREQ ("", hint->get_string ());
3184 ASSERT_EQ (c10, hint->get_start_loc ());
3185 ASSERT_EQ (c21, hint->get_next_loc ());
3190 /* Verify that the line_corrections machinery correctly prints
3191 overlapping fixit-hints. */
3193 static void
3194 test_overlapped_fixit_printing (const line_table_case &case_)
3196 /* Create a tempfile and write some text to it.
3197 ...000000000111111111122222222223333333333.
3198 ...123456789012345678901234567890123456789. */
3199 const char *content
3200 = (" foo *f = (foo *)ptr->field;\n");
3201 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3202 line_table_test ltt (case_);
3204 const line_map_ordinary *ord_map
3205 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3206 tmp.get_filename (), 0));
3208 linemap_line_start (line_table, 1, 100);
3210 const location_t final_line_end
3211 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3213 /* Don't attempt to run the tests if column data might be unavailable. */
3214 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3215 return;
3217 /* A test for converting a C-style cast to a C++-style cast. */
3218 const location_t open_paren
3219 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3220 const location_t close_paren
3221 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3222 const location_t expr_start
3223 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3224 const location_t expr_finish
3225 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3226 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3228 /* Various examples of fix-it hints that aren't themselves consolidated,
3229 but for which the *printing* may need consolidation. */
3231 /* Example where 3 fix-it hints are printed as one. */
3233 test_diagnostic_context dc;
3234 rich_location richloc (line_table, expr);
3235 richloc.add_fixit_replace (open_paren, "const_cast<");
3236 richloc.add_fixit_replace (close_paren, "> (");
3237 richloc.add_fixit_insert_after (")");
3239 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3240 ASSERT_STREQ ("\n"
3241 " foo *f = (foo *)ptr->field;\n"
3242 " ^~~~~~~~~~\n"
3243 " -----------------\n"
3244 " const_cast<foo *> (ptr->field)\n",
3245 pp_formatted_text (dc.printer));
3247 /* Unit-test the line_corrections machinery. */
3248 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3249 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3250 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3251 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3252 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3253 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3254 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3255 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3256 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3257 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3259 /* Add each hint in turn to a line_corrections instance,
3260 and verify that they are consolidated into one correction instance
3261 as expected. */
3262 line_corrections lc (tmp.get_filename (), 1);
3264 /* The first replace hint by itself. */
3265 lc.add_hint (hint_0);
3266 ASSERT_EQ (1, lc.m_corrections.length ());
3267 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3268 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3269 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3271 /* After the second replacement hint, they are printed together
3272 as a replacement (along with the text between them). */
3273 lc.add_hint (hint_1);
3274 ASSERT_EQ (1, lc.m_corrections.length ());
3275 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3276 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3277 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3279 /* After the final insertion hint, they are all printed together
3280 as a replacement (along with the text between them). */
3281 lc.add_hint (hint_2);
3282 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3283 lc.m_corrections[0]->m_text);
3284 ASSERT_EQ (1, lc.m_corrections.length ());
3285 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3286 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3289 /* Example where two are consolidated during printing. */
3291 test_diagnostic_context dc;
3292 rich_location richloc (line_table, expr);
3293 richloc.add_fixit_replace (open_paren, "CAST (");
3294 richloc.add_fixit_replace (close_paren, ") (");
3295 richloc.add_fixit_insert_after (")");
3297 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3298 ASSERT_STREQ ("\n"
3299 " foo *f = (foo *)ptr->field;\n"
3300 " ^~~~~~~~~~\n"
3301 " -\n"
3302 " CAST (-\n"
3303 " ) ( )\n",
3304 pp_formatted_text (dc.printer));
3307 /* Example where none are consolidated during printing. */
3309 test_diagnostic_context dc;
3310 rich_location richloc (line_table, expr);
3311 richloc.add_fixit_replace (open_paren, "CST (");
3312 richloc.add_fixit_replace (close_paren, ") (");
3313 richloc.add_fixit_insert_after (")");
3315 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3316 ASSERT_STREQ ("\n"
3317 " foo *f = (foo *)ptr->field;\n"
3318 " ^~~~~~~~~~\n"
3319 " -\n"
3320 " CST ( -\n"
3321 " ) ( )\n",
3322 pp_formatted_text (dc.printer));
3325 /* Example of deletion fix-it hints. */
3327 test_diagnostic_context dc;
3328 rich_location richloc (line_table, expr);
3329 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3330 source_range victim = {open_paren, close_paren};
3331 richloc.add_fixit_remove (victim);
3333 /* This case is actually handled by fixit-consolidation,
3334 rather than by line_corrections. */
3335 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3337 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3338 ASSERT_STREQ ("\n"
3339 " foo *f = (foo *)ptr->field;\n"
3340 " ^~~~~~~~~~\n"
3341 " -------\n"
3342 " (bar *)\n",
3343 pp_formatted_text (dc.printer));
3346 /* Example of deletion fix-it hints that would overlap. */
3348 test_diagnostic_context dc;
3349 rich_location richloc (line_table, expr);
3350 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3351 source_range victim = {expr_start, expr_finish};
3352 richloc.add_fixit_remove (victim);
3354 /* These fixits are not consolidated. */
3355 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3357 /* But the corrections are. */
3358 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3359 ASSERT_STREQ ("\n"
3360 " foo *f = (foo *)ptr->field;\n"
3361 " ^~~~~~~~~~\n"
3362 " -----------------\n"
3363 " (longer *)(foo *)\n",
3364 pp_formatted_text (dc.printer));
3367 /* Example of insertion fix-it hints that would overlap. */
3369 test_diagnostic_context dc;
3370 rich_location richloc (line_table, expr);
3371 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3372 richloc.add_fixit_insert_after (close_paren, "TEST");
3374 /* The first insertion is long enough that if printed naively,
3375 it would overlap with the second.
3376 Verify that they are printed as a single replacement. */
3377 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3378 ASSERT_STREQ ("\n"
3379 " foo *f = (foo *)ptr->field;\n"
3380 " ^~~~~~~~~~\n"
3381 " -------\n"
3382 " LONGER THAN THE CAST(foo *)TEST\n",
3383 pp_formatted_text (dc.printer));
3387 /* Verify that the line_corrections machinery correctly prints
3388 overlapping fixit-hints that have been added in the wrong
3389 order.
3390 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3392 static void
3393 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3395 /* Create a tempfile and write some text to it.
3396 ...000000000111111111122222222223333333333.
3397 ...123456789012345678901234567890123456789. */
3398 const char *content
3399 = ("int a5[][0][0] = { 1, 2 };\n");
3400 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3401 line_table_test ltt (case_);
3403 const line_map_ordinary *ord_map
3404 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3405 tmp.get_filename (), 0));
3407 linemap_line_start (line_table, 1, 100);
3409 const location_t final_line_end
3410 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3412 /* Don't attempt to run the tests if column data might be unavailable. */
3413 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3414 return;
3416 const location_t col_1
3417 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3418 const location_t col_20
3419 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3420 const location_t col_21
3421 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3422 const location_t col_23
3423 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3424 const location_t col_25
3425 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3427 /* Two insertions, in the wrong order. */
3429 rich_location richloc (line_table, col_20);
3430 richloc.add_fixit_insert_before (col_23, "{");
3431 richloc.add_fixit_insert_before (col_21, "}");
3433 /* These fixits should be accepted; they can't be consolidated. */
3434 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3435 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3436 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3437 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3438 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3439 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3440 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3442 /* Verify that they're printed correctly. */
3443 test_diagnostic_context dc;
3444 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3445 ASSERT_STREQ ("\n"
3446 " int a5[][0][0] = { 1, 2 };\n"
3447 " ^\n"
3448 " } {\n",
3449 pp_formatted_text (dc.printer));
3452 /* Various overlapping insertions, some occurring "out of order"
3453 (reproducing the fix-it hints from PR c/81405). */
3455 test_diagnostic_context dc;
3456 rich_location richloc (line_table, col_20);
3458 richloc.add_fixit_insert_before (col_20, "{{");
3459 richloc.add_fixit_insert_before (col_21, "}}");
3460 richloc.add_fixit_insert_before (col_23, "{");
3461 richloc.add_fixit_insert_before (col_21, "}");
3462 richloc.add_fixit_insert_before (col_23, "{{");
3463 richloc.add_fixit_insert_before (col_25, "}");
3464 richloc.add_fixit_insert_before (col_21, "}");
3465 richloc.add_fixit_insert_before (col_1, "{");
3466 richloc.add_fixit_insert_before (col_25, "}");
3467 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3468 ASSERT_STREQ ("\n"
3469 " int a5[][0][0] = { 1, 2 };\n"
3470 " ^\n"
3471 " { -----\n"
3472 " {{1}}}}, {{{2 }}\n",
3473 pp_formatted_text (dc.printer));
3477 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3479 static void
3480 test_fixit_insert_containing_newline (const line_table_case &case_)
3482 /* Create a tempfile and write some text to it.
3483 .........................0000000001111111.
3484 .........................1234567890123456. */
3485 const char *old_content = (" case 'a':\n" /* line 1. */
3486 " x = a;\n" /* line 2. */
3487 " case 'b':\n" /* line 3. */
3488 " x = b;\n");/* line 4. */
3490 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3491 line_table_test ltt (case_);
3492 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3494 location_t case_start = linemap_position_for_column (line_table, 5);
3495 location_t case_finish = linemap_position_for_column (line_table, 13);
3496 location_t case_loc = make_location (case_start, case_start, case_finish);
3497 location_t line_start = linemap_position_for_column (line_table, 1);
3499 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3500 return;
3502 /* Add a "break;" on a line by itself before line 3 i.e. before
3503 column 1 of line 3. */
3505 rich_location richloc (line_table, case_loc);
3506 richloc.add_fixit_insert_before (line_start, " break;\n");
3508 /* Without line numbers. */
3510 test_diagnostic_context dc;
3511 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3512 ASSERT_STREQ ("\n"
3513 " x = a;\n"
3514 "+ break;\n"
3515 " case 'b':\n"
3516 " ^~~~~~~~~\n",
3517 pp_formatted_text (dc.printer));
3520 /* With line numbers. */
3522 test_diagnostic_context dc;
3523 dc.show_line_numbers_p = true;
3524 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3525 ASSERT_STREQ ("\n"
3526 "2 | x = a;\n"
3527 "+ |+ break;\n"
3528 "3 | case 'b':\n"
3529 " | ^~~~~~~~~\n",
3530 pp_formatted_text (dc.printer));
3534 /* Verify that attempts to add text with a newline fail when the
3535 insertion point is *not* at the start of a line. */
3537 rich_location richloc (line_table, case_loc);
3538 richloc.add_fixit_insert_before (case_start, "break;\n");
3539 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3540 test_diagnostic_context dc;
3541 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3542 ASSERT_STREQ ("\n"
3543 " case 'b':\n"
3544 " ^~~~~~~~~\n",
3545 pp_formatted_text (dc.printer));
3549 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3550 of the file, where the fix-it is printed in a different line-span
3551 to the primary range of the diagnostic. */
3553 static void
3554 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3556 /* Create a tempfile and write some text to it.
3557 .........................0000000001111111.
3558 .........................1234567890123456. */
3559 const char *old_content = ("test (int ch)\n" /* line 1. */
3560 "{\n" /* line 2. */
3561 " putchar (ch);\n" /* line 3. */
3562 "}\n"); /* line 4. */
3564 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3565 line_table_test ltt (case_);
3567 const line_map_ordinary *ord_map = linemap_check_ordinary
3568 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3569 linemap_line_start (line_table, 1, 100);
3571 /* The primary range is the "putchar" token. */
3572 location_t putchar_start
3573 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3574 location_t putchar_finish
3575 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3576 location_t putchar_loc
3577 = make_location (putchar_start, putchar_start, putchar_finish);
3578 rich_location richloc (line_table, putchar_loc);
3580 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3581 location_t file_start
3582 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3583 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3585 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3586 return;
3589 test_diagnostic_context dc;
3590 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3591 ASSERT_STREQ ("\n"
3592 "FILENAME:1:1:\n"
3593 "+#include <stdio.h>\n"
3594 " test (int ch)\n"
3595 "FILENAME:3:2:\n"
3596 " putchar (ch);\n"
3597 " ^~~~~~~\n",
3598 pp_formatted_text (dc.printer));
3601 /* With line-numbering, the line spans are close enough to be
3602 consolidated, since it makes little sense to skip line 2. */
3604 test_diagnostic_context dc;
3605 dc.show_line_numbers_p = true;
3606 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3607 ASSERT_STREQ ("\n"
3608 "+ |+#include <stdio.h>\n"
3609 "1 | test (int ch)\n"
3610 "2 | {\n"
3611 "3 | putchar (ch);\n"
3612 " | ^~~~~~~\n",
3613 pp_formatted_text (dc.printer));
3617 /* Replacement fix-it hint containing a newline.
3618 This will fail, as newlines are only supported when inserting at the
3619 beginning of a line. */
3621 static void
3622 test_fixit_replace_containing_newline (const line_table_case &case_)
3624 /* Create a tempfile and write some text to it.
3625 .........................0000000001111.
3626 .........................1234567890123. */
3627 const char *old_content = "foo = bar ();\n";
3629 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3630 line_table_test ltt (case_);
3631 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3633 /* Replace the " = " with "\n = ", as if we were reformatting an
3634 overly long line. */
3635 location_t start = linemap_position_for_column (line_table, 4);
3636 location_t finish = linemap_position_for_column (line_table, 6);
3637 location_t loc = linemap_position_for_column (line_table, 13);
3638 rich_location richloc (line_table, loc);
3639 source_range range = source_range::from_locations (start, finish);
3640 richloc.add_fixit_replace (range, "\n =");
3642 /* Arbitrary newlines are not yet supported within fix-it hints, so
3643 the fix-it should not be displayed. */
3644 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3646 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3647 return;
3649 test_diagnostic_context dc;
3650 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3651 ASSERT_STREQ ("\n"
3652 " foo = bar ();\n"
3653 " ^\n",
3654 pp_formatted_text (dc.printer));
3657 /* Fix-it hint, attempting to delete a newline.
3658 This will fail, as we currently only support fix-it hints that
3659 affect one line at a time. */
3661 static void
3662 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3664 /* Create a tempfile and write some text to it.
3665 ..........................0000000001111.
3666 ..........................1234567890123. */
3667 const char *old_content = ("foo = bar (\n"
3668 " );\n");
3670 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3671 line_table_test ltt (case_);
3672 const line_map_ordinary *ord_map = linemap_check_ordinary
3673 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3674 linemap_line_start (line_table, 1, 100);
3676 /* Attempt to delete the " (\n...)". */
3677 location_t start
3678 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3679 location_t caret
3680 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3681 location_t finish
3682 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3683 location_t loc = make_location (caret, start, finish);
3684 rich_location richloc (line_table, loc);
3685 richloc. add_fixit_remove ();
3687 /* Fix-it hints that affect more than one line are not yet supported, so
3688 the fix-it should not be displayed. */
3689 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3691 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3692 return;
3694 test_diagnostic_context dc;
3695 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3696 ASSERT_STREQ ("\n"
3697 " foo = bar (\n"
3698 " ~^\n"
3699 " );\n"
3700 " ~ \n",
3701 pp_formatted_text (dc.printer));
3704 /* Verify that line numbers are correctly printed for the case of
3705 a multiline range in which the width of the line numbers changes
3706 (e.g. from "9" to "10"). */
3708 static void
3709 test_line_numbers_multiline_range ()
3711 /* Create a tempfile and write some text to it. */
3712 pretty_printer pp;
3713 for (int i = 0; i < 20; i++)
3714 /* .........0000000001111111.
3715 .............1234567890123456. */
3716 pp_printf (&pp, "this is line %i\n", i + 1);
3717 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3718 line_table_test ltt;
3720 const line_map_ordinary *ord_map = linemap_check_ordinary
3721 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3722 linemap_line_start (line_table, 1, 100);
3724 /* Create a multi-line location, starting at the "line" of line 9, with
3725 a caret on the "is" of line 10, finishing on the "this" line 11. */
3727 location_t start
3728 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3729 location_t caret
3730 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3731 location_t finish
3732 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3733 location_t loc = make_location (caret, start, finish);
3735 test_diagnostic_context dc;
3736 dc.show_line_numbers_p = true;
3737 gcc_rich_location richloc (loc);
3738 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3739 ASSERT_STREQ ("\n"
3740 " 9 | this is line 9\n"
3741 " | ~~~~~~\n"
3742 "10 | this is line 10\n"
3743 " | ~~~~~^~~~~~~~~~\n"
3744 "11 | this is line 11\n"
3745 " | ~~~~ \n",
3746 pp_formatted_text (dc.printer));
3749 /* Run all of the selftests within this file. */
3751 void
3752 diagnostic_show_locus_c_tests ()
3754 test_line_span ();
3755 test_num_digits ();
3757 test_layout_range_for_single_point ();
3758 test_layout_range_for_single_line ();
3759 test_layout_range_for_multiple_lines ();
3761 test_get_line_width_without_trailing_whitespace ();
3763 test_diagnostic_show_locus_unknown_location ();
3765 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3766 for_each_line_table_case (test_add_location_if_nearby);
3767 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3768 for_each_line_table_case (test_fixit_consolidation);
3769 for_each_line_table_case (test_overlapped_fixit_printing);
3770 for_each_line_table_case (test_overlapped_fixit_printing_2);
3771 for_each_line_table_case (test_fixit_insert_containing_newline);
3772 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3773 for_each_line_table_case (test_fixit_replace_containing_newline);
3774 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3776 test_line_numbers_multiline_range ();
3779 } // namespace selftest
3781 #endif /* #if CHECKING_P */