Come up with fndecl_built_in_p.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob1e7f96978e90169bef631b158d525f2539048e95
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 bool show_caret_p,
130 const expanded_location *caret_exploc,
131 const range_label *label);
133 bool contains_point (linenum_type row, int column) const;
134 bool intersects_line_p (linenum_type row) const;
136 layout_point m_start;
137 layout_point m_finish;
138 bool m_show_caret_p;
139 layout_point m_caret;
140 const range_label *m_label;
143 /* A struct for use by layout::print_source_line for telling
144 layout::print_annotation_line the extents of the source line that
145 it printed, so that underlines can be clipped appropriately. */
147 struct line_bounds
149 int m_first_non_ws;
150 int m_last_non_ws;
153 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
154 or "line 23"). During the layout ctor, layout::calculate_line_spans
155 splits the pertinent source lines into a list of disjoint line_span
156 instances (e.g. lines 5-10, lines 15-20, line 23). */
158 struct line_span
160 line_span (linenum_type first_line, linenum_type last_line)
161 : m_first_line (first_line), m_last_line (last_line)
163 gcc_assert (first_line <= last_line);
165 linenum_type get_first_line () const { return m_first_line; }
166 linenum_type get_last_line () const { return m_last_line; }
168 bool contains_line_p (linenum_type line) const
170 return line >= m_first_line && line <= m_last_line;
173 static int comparator (const void *p1, const void *p2)
175 const line_span *ls1 = (const line_span *)p1;
176 const line_span *ls2 = (const line_span *)p2;
177 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
178 if (first_line_cmp)
179 return first_line_cmp;
180 return compare (ls1->m_last_line, ls2->m_last_line);
183 linenum_type m_first_line;
184 linenum_type m_last_line;
187 #if CHECKING_P
189 /* Selftests for line_span. */
191 static void
192 test_line_span ()
194 line_span line_one (1, 1);
195 ASSERT_EQ (1, line_one.get_first_line ());
196 ASSERT_EQ (1, line_one.get_last_line ());
197 ASSERT_FALSE (line_one.contains_line_p (0));
198 ASSERT_TRUE (line_one.contains_line_p (1));
199 ASSERT_FALSE (line_one.contains_line_p (2));
201 line_span lines_1_to_3 (1, 3);
202 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
203 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
204 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
205 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
207 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
208 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
209 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
211 /* A linenum > 2^31. */
212 const linenum_type LARGEST_LINE = 0xffffffff;
213 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
214 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
215 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
217 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
218 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
221 #endif /* #if CHECKING_P */
223 /* A class to control the overall layout when printing a diagnostic.
225 The layout is determined within the constructor.
226 It is then printed by repeatedly calling the "print_source_line",
227 "print_annotation_line" and "print_any_fixits" methods.
229 We assume we have disjoint ranges. */
231 class layout
233 public:
234 layout (diagnostic_context *context,
235 rich_location *richloc,
236 diagnostic_t diagnostic_kind);
238 bool maybe_add_location_range (const location_range *loc_range,
239 bool restrict_to_current_line_spans);
241 int get_num_line_spans () const { return m_line_spans.length (); }
242 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
244 void print_gap_in_line_numbering ();
245 bool print_heading_for_line_span_index_p (int line_span_idx) const;
247 expanded_location get_expanded_location (const line_span *) const;
249 void print_line (linenum_type row);
251 private:
252 bool will_show_line_p (linenum_type row) const;
253 void print_leading_fixits (linenum_type row);
254 void print_source_line (linenum_type row, const char *line, int line_width,
255 line_bounds *lbounds_out);
256 bool should_print_annotation_line_p (linenum_type row) const;
257 void start_annotation_line (char margin_char = ' ') const;
258 void print_annotation_line (linenum_type row, const line_bounds lbounds);
259 void print_any_labels (linenum_type row);
260 void print_trailing_fixits (linenum_type row);
262 bool annotation_line_showed_range_p (linenum_type line, int start_column,
263 int finish_column) const;
264 void show_ruler (int max_column) const;
266 bool validate_fixit_hint_p (const fixit_hint *hint);
268 void calculate_line_spans ();
270 void print_newline ();
272 bool
273 get_state_at_point (/* Inputs. */
274 linenum_type row, int column,
275 int first_non_ws, int last_non_ws,
276 /* Outputs. */
277 point_state *out_state);
280 get_x_bound_for_row (linenum_type row, int caret_column,
281 int last_non_ws);
283 void
284 move_to_column (int *column, int dest_column, bool add_left_margin);
286 private:
287 diagnostic_context *m_context;
288 pretty_printer *m_pp;
289 diagnostic_t m_diagnostic_kind;
290 location_t m_primary_loc;
291 expanded_location m_exploc;
292 colorizer m_colorizer;
293 bool m_colorize_source_p;
294 bool m_show_labels_p;
295 bool m_show_line_numbers_p;
296 auto_vec <layout_range> m_layout_ranges;
297 auto_vec <const fixit_hint *> m_fixit_hints;
298 auto_vec <line_span> m_line_spans;
299 int m_linenum_width;
300 int m_x_offset;
303 /* Implementation of "class colorizer". */
305 /* The constructor for "colorizer". Lookup and store color codes for the
306 different kinds of things we might need to print. */
308 colorizer::colorizer (diagnostic_context *context,
309 diagnostic_t diagnostic_kind) :
310 m_context (context),
311 m_diagnostic_kind (diagnostic_kind),
312 m_current_state (STATE_NORMAL_TEXT)
314 m_range1 = get_color_by_name ("range1");
315 m_range2 = get_color_by_name ("range2");
316 m_fixit_insert = get_color_by_name ("fixit-insert");
317 m_fixit_delete = get_color_by_name ("fixit-delete");
318 m_stop_color = colorize_stop (pp_show_color (context->printer));
321 /* The destructor for "colorize". If colorization is on, print a code to
322 turn it off. */
324 colorizer::~colorizer ()
326 finish_state (m_current_state);
329 /* Update state, printing color codes if necessary if there's a state
330 change. */
332 void
333 colorizer::set_state (int new_state)
335 if (m_current_state != new_state)
337 finish_state (m_current_state);
338 m_current_state = new_state;
339 begin_state (new_state);
343 /* Turn on any colorization for STATE. */
345 void
346 colorizer::begin_state (int state)
348 switch (state)
350 case STATE_NORMAL_TEXT:
351 break;
353 case STATE_FIXIT_INSERT:
354 pp_string (m_context->printer, m_fixit_insert);
355 break;
357 case STATE_FIXIT_DELETE:
358 pp_string (m_context->printer, m_fixit_delete);
359 break;
361 case 0:
362 /* Make range 0 be the same color as the "kind" text
363 (error vs warning vs note). */
364 pp_string
365 (m_context->printer,
366 colorize_start (pp_show_color (m_context->printer),
367 diagnostic_get_color_for_kind (m_diagnostic_kind)));
368 break;
370 case 1:
371 pp_string (m_context->printer, m_range1);
372 break;
374 case 2:
375 pp_string (m_context->printer, m_range2);
376 break;
378 default:
379 /* For ranges beyond 2, alternate between color 1 and color 2. */
381 gcc_assert (state > 2);
382 pp_string (m_context->printer,
383 state % 2 ? m_range1 : m_range2);
385 break;
389 /* Turn off any colorization for STATE. */
391 void
392 colorizer::finish_state (int state)
394 if (state != STATE_NORMAL_TEXT)
395 pp_string (m_context->printer, m_stop_color);
398 /* Get the color code for NAME (or the empty string if
399 colorization is disabled). */
401 const char *
402 colorizer::get_color_by_name (const char *name)
404 return colorize_start (pp_show_color (m_context->printer), name);
407 /* Implementation of class layout_range. */
409 /* The constructor for class layout_range.
410 Initialize various layout_point fields from expanded_location
411 equivalents; we've already filtered on file. */
413 layout_range::layout_range (const expanded_location *start_exploc,
414 const expanded_location *finish_exploc,
415 bool show_caret_p,
416 const expanded_location *caret_exploc,
417 const range_label *label)
418 : m_start (*start_exploc),
419 m_finish (*finish_exploc),
420 m_show_caret_p (show_caret_p),
421 m_caret (*caret_exploc),
422 m_label (label)
426 /* Is (column, row) within the given range?
427 We've already filtered on the file.
429 Ranges are closed (both limits are within the range).
431 Example A: a single-line range:
432 start: (col=22, line=2)
433 finish: (col=38, line=2)
435 |00000011111111112222222222333333333344444444444
436 |34567890123456789012345678901234567890123456789
437 --+-----------------------------------------------
438 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
439 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
440 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
442 Example B: a multiline range with
443 start: (col=14, line=3)
444 finish: (col=08, line=5)
446 |00000011111111112222222222333333333344444444444
447 |34567890123456789012345678901234567890123456789
448 --+-----------------------------------------------
449 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
450 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
451 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
452 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
453 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
454 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
455 --+-----------------------------------------------
457 Legend:
458 - 'b' indicates a point *before* the range
459 - 'S' indicates the start of the range
460 - 'w' indicates a point within the range
461 - 'F' indicates the finish of the range (which is
462 within it).
463 - 'a' indicates a subsequent point *after* the range. */
465 bool
466 layout_range::contains_point (linenum_type row, int column) const
468 gcc_assert (m_start.m_line <= m_finish.m_line);
469 /* ...but the equivalent isn't true for the columns;
470 consider example B in the comment above. */
472 if (row < m_start.m_line)
473 /* Points before the first line of the range are
474 outside it (corresponding to line 01 in example A
475 and lines 01 and 02 in example B above). */
476 return false;
478 if (row == m_start.m_line)
479 /* On same line as start of range (corresponding
480 to line 02 in example A and line 03 in example B). */
482 if (column < m_start.m_column)
483 /* Points on the starting line of the range, but
484 before the column in which it begins. */
485 return false;
487 if (row < m_finish.m_line)
488 /* This is a multiline range; the point
489 is within it (corresponds to line 03 in example B
490 from column 14 onwards) */
491 return true;
492 else
494 /* This is a single-line range. */
495 gcc_assert (row == m_finish.m_line);
496 return column <= m_finish.m_column;
500 /* The point is in a line beyond that containing the
501 start of the range: lines 03 onwards in example A,
502 and lines 04 onwards in example B. */
503 gcc_assert (row > m_start.m_line);
505 if (row > m_finish.m_line)
506 /* The point is beyond the final line of the range
507 (lines 03 onwards in example A, and lines 06 onwards
508 in example B). */
509 return false;
511 if (row < m_finish.m_line)
513 /* The point is in a line that's fully within a multiline
514 range (e.g. line 04 in example B). */
515 gcc_assert (m_start.m_line < m_finish.m_line);
516 return true;
519 gcc_assert (row == m_finish.m_line);
521 return column <= m_finish.m_column;
524 /* Does this layout_range contain any part of line ROW? */
526 bool
527 layout_range::intersects_line_p (linenum_type row) const
529 gcc_assert (m_start.m_line <= m_finish.m_line);
530 if (row < m_start.m_line)
531 return false;
532 if (row > m_finish.m_line)
533 return false;
534 return true;
537 #if CHECKING_P
539 /* A helper function for testing layout_range. */
541 static layout_range
542 make_range (int start_line, int start_col, int end_line, int end_col)
544 const expanded_location start_exploc
545 = {"test.c", start_line, start_col, NULL, false};
546 const expanded_location finish_exploc
547 = {"test.c", end_line, end_col, NULL, false};
548 return layout_range (&start_exploc, &finish_exploc, false,
549 &start_exploc, NULL);
552 /* Selftests for layout_range::contains_point and
553 layout_range::intersects_line_p. */
555 /* Selftest for layout_range, where the layout_range
556 is a range with start==end i.e. a single point. */
558 static void
559 test_layout_range_for_single_point ()
561 layout_range point = make_range (7, 10, 7, 10);
563 /* Tests for layout_range::contains_point. */
565 /* Before the line. */
566 ASSERT_FALSE (point.contains_point (6, 1));
568 /* On the line, but before start. */
569 ASSERT_FALSE (point.contains_point (7, 9));
571 /* At the point. */
572 ASSERT_TRUE (point.contains_point (7, 10));
574 /* On the line, after the point. */
575 ASSERT_FALSE (point.contains_point (7, 11));
577 /* After the line. */
578 ASSERT_FALSE (point.contains_point (8, 1));
580 /* Tests for layout_range::intersects_line_p. */
581 ASSERT_FALSE (point.intersects_line_p (6));
582 ASSERT_TRUE (point.intersects_line_p (7));
583 ASSERT_FALSE (point.intersects_line_p (8));
586 /* Selftest for layout_range, where the layout_range
587 is the single-line range shown as "Example A" above. */
589 static void
590 test_layout_range_for_single_line ()
592 layout_range example_a = make_range (2, 22, 2, 38);
594 /* Tests for layout_range::contains_point. */
596 /* Before the line. */
597 ASSERT_FALSE (example_a.contains_point (1, 1));
599 /* On the line, but before start. */
600 ASSERT_FALSE (example_a.contains_point (2, 21));
602 /* On the line, at the start. */
603 ASSERT_TRUE (example_a.contains_point (2, 22));
605 /* On the line, within the range. */
606 ASSERT_TRUE (example_a.contains_point (2, 23));
608 /* On the line, at the end. */
609 ASSERT_TRUE (example_a.contains_point (2, 38));
611 /* On the line, after the end. */
612 ASSERT_FALSE (example_a.contains_point (2, 39));
614 /* After the line. */
615 ASSERT_FALSE (example_a.contains_point (2, 39));
617 /* Tests for layout_range::intersects_line_p. */
618 ASSERT_FALSE (example_a.intersects_line_p (1));
619 ASSERT_TRUE (example_a.intersects_line_p (2));
620 ASSERT_FALSE (example_a.intersects_line_p (3));
623 /* Selftest for layout_range, where the layout_range
624 is the multi-line range shown as "Example B" above. */
626 static void
627 test_layout_range_for_multiple_lines ()
629 layout_range example_b = make_range (3, 14, 5, 8);
631 /* Tests for layout_range::contains_point. */
633 /* Before first line. */
634 ASSERT_FALSE (example_b.contains_point (1, 1));
636 /* On the first line, but before start. */
637 ASSERT_FALSE (example_b.contains_point (3, 13));
639 /* At the start. */
640 ASSERT_TRUE (example_b.contains_point (3, 14));
642 /* On the first line, within the range. */
643 ASSERT_TRUE (example_b.contains_point (3, 15));
645 /* On an interior line.
646 The column number should not matter; try various boundary
647 values. */
648 ASSERT_TRUE (example_b.contains_point (4, 1));
649 ASSERT_TRUE (example_b.contains_point (4, 7));
650 ASSERT_TRUE (example_b.contains_point (4, 8));
651 ASSERT_TRUE (example_b.contains_point (4, 9));
652 ASSERT_TRUE (example_b.contains_point (4, 13));
653 ASSERT_TRUE (example_b.contains_point (4, 14));
654 ASSERT_TRUE (example_b.contains_point (4, 15));
656 /* On the final line, before the end. */
657 ASSERT_TRUE (example_b.contains_point (5, 7));
659 /* On the final line, at the end. */
660 ASSERT_TRUE (example_b.contains_point (5, 8));
662 /* On the final line, after the end. */
663 ASSERT_FALSE (example_b.contains_point (5, 9));
665 /* After the line. */
666 ASSERT_FALSE (example_b.contains_point (6, 1));
668 /* Tests for layout_range::intersects_line_p. */
669 ASSERT_FALSE (example_b.intersects_line_p (2));
670 ASSERT_TRUE (example_b.intersects_line_p (3));
671 ASSERT_TRUE (example_b.intersects_line_p (4));
672 ASSERT_TRUE (example_b.intersects_line_p (5));
673 ASSERT_FALSE (example_b.intersects_line_p (6));
676 #endif /* #if CHECKING_P */
678 /* Given a source line LINE of length LINE_WIDTH, determine the width
679 without any trailing whitespace. */
681 static int
682 get_line_width_without_trailing_whitespace (const char *line, int line_width)
684 int result = line_width;
685 while (result > 0)
687 char ch = line[result - 1];
688 if (ch == ' ' || ch == '\t' || ch == '\r')
689 result--;
690 else
691 break;
693 gcc_assert (result >= 0);
694 gcc_assert (result <= line_width);
695 gcc_assert (result == 0 ||
696 (line[result - 1] != ' '
697 && line[result -1] != '\t'
698 && line[result -1] != '\r'));
699 return result;
702 #if CHECKING_P
704 /* A helper function for testing get_line_width_without_trailing_whitespace. */
706 static void
707 assert_eq (const char *line, int expected_width)
709 int actual_value
710 = get_line_width_without_trailing_whitespace (line, strlen (line));
711 ASSERT_EQ (actual_value, expected_width);
714 /* Verify that get_line_width_without_trailing_whitespace is sane for
715 various inputs. It is not required to handle newlines. */
717 static void
718 test_get_line_width_without_trailing_whitespace ()
720 assert_eq ("", 0);
721 assert_eq (" ", 0);
722 assert_eq ("\t", 0);
723 assert_eq ("\r", 0);
724 assert_eq ("hello world", 11);
725 assert_eq ("hello world ", 11);
726 assert_eq ("hello world \t\t ", 11);
727 assert_eq ("hello world\r", 11);
730 #endif /* #if CHECKING_P */
732 /* Helper function for layout's ctor, for sanitizing locations relative
733 to the primary location within a diagnostic.
735 Compare LOC_A and LOC_B to see if it makes sense to print underlines
736 connecting their expanded locations. Doing so is only guaranteed to
737 make sense if the locations share the same macro expansion "history"
738 i.e. they can be traced through the same macro expansions, eventually
739 reaching an ordinary map.
741 This may be too strong a condition, but it effectively sanitizes
742 PR c++/70105, which has an example of printing an expression where the
743 final location of the expression is in a different macro, which
744 erroneously was leading to hundreds of lines of irrelevant source
745 being printed. */
747 static bool
748 compatible_locations_p (location_t loc_a, location_t loc_b)
750 if (IS_ADHOC_LOC (loc_a))
751 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
752 if (IS_ADHOC_LOC (loc_b))
753 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
755 /* If either location is one of the special locations outside of a
756 linemap, they are only compatible if they are equal. */
757 if (loc_a < RESERVED_LOCATION_COUNT
758 || loc_b < RESERVED_LOCATION_COUNT)
759 return loc_a == loc_b;
761 const line_map *map_a = linemap_lookup (line_table, loc_a);
762 linemap_assert (map_a);
764 const line_map *map_b = linemap_lookup (line_table, loc_b);
765 linemap_assert (map_b);
767 /* Are they within the same map? */
768 if (map_a == map_b)
770 /* Are both within the same macro expansion? */
771 if (linemap_macro_expansion_map_p (map_a))
773 /* Expand each location towards the spelling location, and
774 recurse. */
775 const line_map_macro *macro_map = linemap_check_macro (map_a);
776 source_location loc_a_toward_spelling
777 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
778 macro_map,
779 loc_a);
780 source_location loc_b_toward_spelling
781 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
782 macro_map,
783 loc_b);
784 return compatible_locations_p (loc_a_toward_spelling,
785 loc_b_toward_spelling);
788 /* Otherwise they are within the same ordinary map. */
789 return true;
791 else
793 /* Within different maps. */
795 /* If either is within a macro expansion, they are incompatible. */
796 if (linemap_macro_expansion_map_p (map_a)
797 || linemap_macro_expansion_map_p (map_b))
798 return false;
800 /* Within two different ordinary maps; they are compatible iff they
801 are in the same file. */
802 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
803 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
804 return ord_map_a->to_file == ord_map_b->to_file;
808 /* Comparator for sorting fix-it hints. */
810 static int
811 fixit_cmp (const void *p_a, const void *p_b)
813 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
814 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
815 return hint_a->get_start_loc () - hint_b->get_start_loc ();
818 /* Get the number of digits in the decimal representation
819 of VALUE. */
821 static int
822 num_digits (int value)
824 /* Perhaps simpler to use log10 for this, but doing it this way avoids
825 using floating point. */
826 gcc_assert (value >= 0);
828 if (value == 0)
829 return 1;
831 int digits = 0;
832 while (value > 0)
834 digits++;
835 value /= 10;
837 return digits;
841 #if CHECKING_P
843 /* Selftest for num_digits. */
845 static void
846 test_num_digits ()
848 ASSERT_EQ (1, num_digits (0));
849 ASSERT_EQ (1, num_digits (9));
850 ASSERT_EQ (2, num_digits (10));
851 ASSERT_EQ (2, num_digits (99));
852 ASSERT_EQ (3, num_digits (100));
853 ASSERT_EQ (3, num_digits (999));
854 ASSERT_EQ (4, num_digits (1000));
855 ASSERT_EQ (4, num_digits (9999));
856 ASSERT_EQ (5, num_digits (10000));
857 ASSERT_EQ (5, num_digits (99999));
858 ASSERT_EQ (6, num_digits (100000));
859 ASSERT_EQ (6, num_digits (999999));
860 ASSERT_EQ (7, num_digits (1000000));
861 ASSERT_EQ (7, num_digits (9999999));
862 ASSERT_EQ (8, num_digits (10000000));
863 ASSERT_EQ (8, num_digits (99999999));
866 #endif /* #if CHECKING_P */
868 /* Implementation of class layout. */
870 /* Constructor for class layout.
872 Filter the ranges from the rich_location to those that we can
873 sanely print, populating m_layout_ranges and m_fixit_hints.
874 Determine the range of lines that we will print, splitting them
875 up into an ordered list of disjoint spans of contiguous line numbers.
876 Determine m_x_offset, to ensure that the primary caret
877 will fit within the max_width provided by the diagnostic_context. */
879 layout::layout (diagnostic_context * context,
880 rich_location *richloc,
881 diagnostic_t diagnostic_kind)
882 : m_context (context),
883 m_pp (context->printer),
884 m_diagnostic_kind (diagnostic_kind),
885 m_primary_loc (richloc->get_range (0)->m_loc),
886 m_exploc (richloc->get_expanded_location (0)),
887 m_colorizer (context, diagnostic_kind),
888 m_colorize_source_p (context->colorize_source_p),
889 m_show_labels_p (context->show_labels_p),
890 m_show_line_numbers_p (context->show_line_numbers_p),
891 m_layout_ranges (richloc->get_num_locations ()),
892 m_fixit_hints (richloc->get_num_fixit_hints ()),
893 m_line_spans (1 + richloc->get_num_locations ()),
894 m_linenum_width (0),
895 m_x_offset (0)
897 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
899 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
900 Ignore any ranges that are awkward to handle. */
901 const location_range *loc_range = richloc->get_range (idx);
902 maybe_add_location_range (loc_range, false);
905 /* Populate m_fixit_hints, filtering to only those that are in the
906 same file. */
907 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
909 const fixit_hint *hint = richloc->get_fixit_hint (i);
910 if (validate_fixit_hint_p (hint))
911 m_fixit_hints.safe_push (hint);
914 /* Sort m_fixit_hints. */
915 m_fixit_hints.qsort (fixit_cmp);
917 /* Populate m_line_spans. */
918 calculate_line_spans ();
920 /* Determine m_linenum_width. */
921 gcc_assert (m_line_spans.length () > 0);
922 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
923 int highest_line = last_span->m_last_line;
924 if (highest_line < 0)
925 highest_line = 0;
926 m_linenum_width = num_digits (highest_line);
927 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
928 if (m_line_spans.length () > 1)
929 m_linenum_width = MAX (m_linenum_width, 3);
931 /* Adjust m_x_offset.
932 Center the primary caret to fit in max_width; all columns
933 will be adjusted accordingly. */
934 size_t max_width = m_context->caret_max_width;
935 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
936 if (line && (size_t)m_exploc.column <= line.length ())
938 size_t right_margin = CARET_LINE_MARGIN;
939 size_t column = m_exploc.column;
940 if (m_show_line_numbers_p)
941 column += m_linenum_width + 2;
942 right_margin = MIN (line.length () - column, right_margin);
943 right_margin = max_width - right_margin;
944 if (line.length () >= max_width && column > right_margin)
945 m_x_offset = column - right_margin;
946 gcc_assert (m_x_offset >= 0);
949 if (context->show_ruler_p)
950 show_ruler (m_x_offset + max_width);
953 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
954 those that we can sanely print.
956 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
957 filtered against this layout instance's current line spans: it
958 will only be added if the location is fully within the lines
959 already specified by other locations.
961 Return true iff LOC_RANGE was added. */
963 bool
964 layout::maybe_add_location_range (const location_range *loc_range,
965 bool restrict_to_current_line_spans)
967 gcc_assert (loc_range);
969 /* Split the "range" into caret and range information. */
970 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
972 /* Expand the various locations. */
973 expanded_location start
974 = linemap_client_expand_location_to_spelling_point
975 (src_range.m_start, LOCATION_ASPECT_START);
976 expanded_location finish
977 = linemap_client_expand_location_to_spelling_point
978 (src_range.m_finish, LOCATION_ASPECT_FINISH);
979 expanded_location caret
980 = linemap_client_expand_location_to_spelling_point
981 (loc_range->m_loc, LOCATION_ASPECT_CARET);
983 /* If any part of the range isn't in the same file as the primary
984 location of this diagnostic, ignore the range. */
985 if (start.file != m_exploc.file)
986 return false;
987 if (finish.file != m_exploc.file)
988 return false;
989 if (loc_range->m_show_caret_p)
990 if (caret.file != m_exploc.file)
991 return false;
993 /* Sanitize the caret location for non-primary ranges. */
994 if (m_layout_ranges.length () > 0)
995 if (loc_range->m_show_caret_p)
996 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
997 /* Discard any non-primary ranges that can't be printed
998 sanely relative to the primary location. */
999 return false;
1001 /* Everything is now known to be in the correct source file,
1002 but it may require further sanitization. */
1003 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret,
1004 loc_range->m_label);
1006 /* If we have a range that finishes before it starts (perhaps
1007 from something built via macro expansion), printing the
1008 range is likely to be nonsensical. Also, attempting to do so
1009 breaks assumptions within the printing code (PR c/68473).
1010 Similarly, don't attempt to print ranges if one or both ends
1011 of the range aren't sane to print relative to the
1012 primary location (PR c++/70105). */
1013 if (start.line > finish.line
1014 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1015 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1017 /* Is this the primary location? */
1018 if (m_layout_ranges.length () == 0)
1020 /* We want to print the caret for the primary location, but
1021 we must sanitize away m_start and m_finish. */
1022 ri.m_start = ri.m_caret;
1023 ri.m_finish = ri.m_caret;
1025 else
1026 /* This is a non-primary range; ignore it. */
1027 return false;
1030 /* Potentially filter to just the lines already specified by other
1031 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1032 The layout ctor doesn't use it, and can't because m_line_spans
1033 hasn't been set up at that point. */
1034 if (restrict_to_current_line_spans)
1036 if (!will_show_line_p (start.line))
1037 return false;
1038 if (!will_show_line_p (finish.line))
1039 return false;
1040 if (loc_range->m_show_caret_p)
1041 if (!will_show_line_p (caret.line))
1042 return false;
1045 /* Passed all the tests; add the range to m_layout_ranges so that
1046 it will be printed. */
1047 m_layout_ranges.safe_push (ri);
1048 return true;
1051 /* Return true iff ROW is within one of the line spans for this layout. */
1053 bool
1054 layout::will_show_line_p (linenum_type row) const
1056 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1057 line_span_idx++)
1059 const line_span *line_span = get_line_span (line_span_idx);
1060 if (line_span->contains_line_p (row))
1061 return true;
1063 return false;
1066 /* Print a line showing a gap in the line numbers, for showing the boundary
1067 between two line spans. */
1069 void
1070 layout::print_gap_in_line_numbering ()
1072 gcc_assert (m_show_line_numbers_p);
1074 for (int i = 0; i < m_linenum_width + 1; i++)
1075 pp_character (m_pp, '.');
1077 pp_newline (m_pp);
1080 /* Return true iff we should print a heading when starting the
1081 line span with the given index. */
1083 bool
1084 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1086 /* We print a heading for every change of line span, hence for every
1087 line span after the initial one. */
1088 if (line_span_idx > 0)
1089 return true;
1091 /* We also do it for the initial span if the primary location of the
1092 diagnostic is in a different span. */
1093 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1094 return true;
1096 return false;
1099 /* Get an expanded_location for the first location of interest within
1100 the given line_span.
1101 Used when printing a heading to indicate a new line span. */
1103 expanded_location
1104 layout::get_expanded_location (const line_span *line_span) const
1106 /* Whenever possible, use the caret location. */
1107 if (line_span->contains_line_p (m_exploc.line))
1108 return m_exploc;
1110 /* Otherwise, use the start of the first range that's present
1111 within the line_span. */
1112 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1114 const layout_range *lr = &m_layout_ranges[i];
1115 if (line_span->contains_line_p (lr->m_start.m_line))
1117 expanded_location exploc = m_exploc;
1118 exploc.line = lr->m_start.m_line;
1119 exploc.column = lr->m_start.m_column;
1120 return exploc;
1124 /* Otherwise, use the location of the first fixit-hint present within
1125 the line_span. */
1126 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1128 const fixit_hint *hint = m_fixit_hints[i];
1129 location_t loc = hint->get_start_loc ();
1130 expanded_location exploc = expand_location (loc);
1131 if (line_span->contains_line_p (exploc.line))
1132 return exploc;
1135 /* It should not be possible to have a line span that didn't
1136 contain any of the layout_range or fixit_hint instances. */
1137 gcc_unreachable ();
1138 return m_exploc;
1141 /* Determine if HINT is meaningful to print within this layout. */
1143 bool
1144 layout::validate_fixit_hint_p (const fixit_hint *hint)
1146 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1147 return false;
1148 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1149 return false;
1151 return true;
1154 /* Determine the range of lines affected by HINT.
1155 This assumes that HINT has already been filtered by
1156 validate_fixit_hint_p, and so affects the correct source file. */
1158 static line_span
1159 get_line_span_for_fixit_hint (const fixit_hint *hint)
1161 gcc_assert (hint);
1162 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1163 LOCATION_LINE (hint->get_next_loc ()));
1166 /* We want to print the pertinent source code at a diagnostic. The
1167 rich_location can contain multiple locations. This will have been
1168 filtered into m_exploc (the caret for the primary location) and
1169 m_layout_ranges, for those ranges within the same source file.
1171 We will print a subset of the lines within the source file in question,
1172 as a collection of "spans" of lines.
1174 This function populates m_line_spans with an ordered, disjoint list of
1175 the line spans of interest.
1177 Printing a gap between line spans takes one line, so, when printing
1178 line numbers, we allow a gap of up to one line between spans when
1179 merging, since it makes more sense to print the source line rather than a
1180 "gap-in-line-numbering" line. When not printing line numbers, it's
1181 better to be more explicit about what's going on, so keeping them as
1182 separate spans is preferred.
1184 For example, if the primary range is on lines 8-10, with secondary ranges
1185 covering lines 5-6 and lines 13-15:
1188 005 |RANGE 1
1189 006 |RANGE 1
1191 008 |PRIMARY RANGE
1192 009 |PRIMARY CARET
1193 010 |PRIMARY RANGE
1196 013 |RANGE 2
1197 014 |RANGE 2
1198 015 |RANGE 2
1201 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1203 With line numbering off (with span headers), we want three spans: lines 5-6,
1204 lines 8-10, and lines 13-15. */
1206 void
1207 layout::calculate_line_spans ()
1209 /* This should only be called once, by the ctor. */
1210 gcc_assert (m_line_spans.length () == 0);
1212 /* Populate tmp_spans with individual spans, for each of
1213 m_exploc, and for m_layout_ranges. */
1214 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1215 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1216 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1218 const layout_range *lr = &m_layout_ranges[i];
1219 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1220 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1221 lr->m_finish.m_line));
1224 /* Also add spans for any fix-it hints, in case they cover other lines. */
1225 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1227 const fixit_hint *hint = m_fixit_hints[i];
1228 gcc_assert (hint);
1229 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1232 /* Sort them. */
1233 tmp_spans.qsort(line_span::comparator);
1235 /* Now iterate through tmp_spans, copying into m_line_spans, and
1236 combining where possible. */
1237 gcc_assert (tmp_spans.length () > 0);
1238 m_line_spans.safe_push (tmp_spans[0]);
1239 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1241 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1242 const line_span *next = &tmp_spans[i];
1243 gcc_assert (next->m_first_line >= current->m_first_line);
1244 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1245 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
1247 /* We can merge them. */
1248 if (next->m_last_line > current->m_last_line)
1249 current->m_last_line = next->m_last_line;
1251 else
1253 /* No merger possible. */
1254 m_line_spans.safe_push (*next);
1258 /* Verify the result, in m_line_spans. */
1259 gcc_assert (m_line_spans.length () > 0);
1260 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1262 const line_span *prev = &m_line_spans[i - 1];
1263 const line_span *next = &m_line_spans[i];
1264 /* The individual spans must be sane. */
1265 gcc_assert (prev->m_first_line <= prev->m_last_line);
1266 gcc_assert (next->m_first_line <= next->m_last_line);
1267 /* The spans must be ordered. */
1268 gcc_assert (prev->m_first_line < next->m_first_line);
1269 /* There must be a gap of at least one line between separate spans. */
1270 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1274 /* Print line ROW of source code, potentially colorized at any ranges, and
1275 populate *LBOUNDS_OUT.
1276 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1277 is its width. */
1279 void
1280 layout::print_source_line (linenum_type row, const char *line, int line_width,
1281 line_bounds *lbounds_out)
1283 m_colorizer.set_normal_text ();
1285 /* We will stop printing the source line at any trailing
1286 whitespace. */
1287 line_width = get_line_width_without_trailing_whitespace (line,
1288 line_width);
1289 line += m_x_offset;
1291 if (m_show_line_numbers_p)
1293 int width = num_digits (row);
1294 for (int i = 0; i < m_linenum_width - width; i++)
1295 pp_space (m_pp);
1296 pp_printf (m_pp, "%i | ", row);
1298 else
1299 pp_space (m_pp);
1300 int first_non_ws = INT_MAX;
1301 int last_non_ws = 0;
1302 int column;
1303 for (column = 1 + m_x_offset; column <= line_width; column++)
1305 /* Assuming colorization is enabled for the caret and underline
1306 characters, we may also colorize the associated characters
1307 within the source line.
1309 For frontends that generate range information, we color the
1310 associated characters in the source line the same as the
1311 carets and underlines in the annotation line, to make it easier
1312 for the reader to see the pertinent code.
1314 For frontends that only generate carets, we don't colorize the
1315 characters above them, since this would look strange (e.g.
1316 colorizing just the first character in a token). */
1317 if (m_colorize_source_p)
1319 bool in_range_p;
1320 point_state state;
1321 in_range_p = get_state_at_point (row, column,
1322 0, INT_MAX,
1323 &state);
1324 if (in_range_p)
1325 m_colorizer.set_range (state.range_idx);
1326 else
1327 m_colorizer.set_normal_text ();
1329 char c = *line;
1330 if (c == '\0' || c == '\t' || c == '\r')
1331 c = ' ';
1332 if (c != ' ')
1334 last_non_ws = column;
1335 if (first_non_ws == INT_MAX)
1336 first_non_ws = column;
1338 pp_character (m_pp, c);
1339 line++;
1341 print_newline ();
1343 lbounds_out->m_first_non_ws = first_non_ws;
1344 lbounds_out->m_last_non_ws = last_non_ws;
1347 /* Determine if we should print an annotation line for ROW.
1348 i.e. if any of m_layout_ranges contains ROW. */
1350 bool
1351 layout::should_print_annotation_line_p (linenum_type row) const
1353 layout_range *range;
1354 int i;
1355 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1356 if (range->intersects_line_p (row))
1357 return true;
1358 return false;
1361 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1362 margin, which is empty for annotation lines. Otherwise, do nothing. */
1364 void
1365 layout::start_annotation_line (char margin_char) const
1367 if (m_show_line_numbers_p)
1369 for (int i = 0; i < m_linenum_width; i++)
1370 pp_character (m_pp, margin_char);
1371 pp_string (m_pp, " |");
1375 /* Print a line consisting of the caret/underlines for the given
1376 source line. */
1378 void
1379 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1381 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1382 lbounds.m_last_non_ws);
1384 start_annotation_line ();
1385 pp_space (m_pp);
1387 for (int column = 1 + m_x_offset; column < x_bound; column++)
1389 bool in_range_p;
1390 point_state state;
1391 in_range_p = get_state_at_point (row, column,
1392 lbounds.m_first_non_ws,
1393 lbounds.m_last_non_ws,
1394 &state);
1395 if (in_range_p)
1397 /* Within a range. Draw either the caret or an underline. */
1398 m_colorizer.set_range (state.range_idx);
1399 if (state.draw_caret_p)
1401 /* Draw the caret. */
1402 char caret_char;
1403 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1404 caret_char = m_context->caret_chars[state.range_idx];
1405 else
1406 caret_char = '^';
1407 pp_character (m_pp, caret_char);
1409 else
1410 pp_character (m_pp, '~');
1412 else
1414 /* Not in a range. */
1415 m_colorizer.set_normal_text ();
1416 pp_character (m_pp, ' ');
1419 print_newline ();
1422 /* Implementation detail of layout::print_any_labels.
1424 A label within the given row of source. */
1426 struct line_label
1428 line_label (int state_idx, int column, label_text text)
1429 : m_state_idx (state_idx), m_column (column),
1430 m_text (text), m_length (strlen (text.m_buffer)),
1431 m_label_line (0)
1434 /* Sorting is primarily by column, then by state index. */
1435 static int comparator (const void *p1, const void *p2)
1437 const line_label *ll1 = (const line_label *)p1;
1438 const line_label *ll2 = (const line_label *)p2;
1439 int column_cmp = compare (ll1->m_column, ll2->m_column);
1440 if (column_cmp)
1441 return column_cmp;
1442 return compare (ll1->m_state_idx, ll2->m_state_idx);
1445 int m_state_idx;
1446 int m_column;
1447 label_text m_text;
1448 size_t m_length;
1449 int m_label_line;
1452 /* Print any labels in this row. */
1453 void
1454 layout::print_any_labels (linenum_type row)
1456 int i;
1457 auto_vec<line_label> labels;
1459 /* Gather the labels that are to be printed into "labels". */
1461 layout_range *range;
1462 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1464 /* Most ranges don't have labels, so reject this first. */
1465 if (range->m_label == NULL)
1466 continue;
1468 /* The range's caret must be on this line. */
1469 if (range->m_caret.m_line != row)
1470 continue;
1472 /* Reject labels that aren't fully visible due to clipping
1473 by m_x_offset. */
1474 if (range->m_caret.m_column <= m_x_offset)
1475 continue;
1477 label_text text;
1478 text = range->m_label->get_text ();
1480 /* Allow for labels that return NULL from their get_text
1481 implementation (so e.g. such labels can control their own
1482 visibility). */
1483 if (text.m_buffer == NULL)
1484 continue;
1486 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1490 /* Bail out if there are no labels on this row. */
1491 if (labels.length () == 0)
1492 return;
1494 /* Sort them. */
1495 labels.qsort(line_label::comparator);
1497 /* Figure out how many "label lines" we need, and which
1498 one each label is printed in.
1500 For example, if the labels aren't too densely packed,
1501 we can fit them on the same line, giving two "label lines":
1503 foo + bar
1504 ~~~ ~~~
1505 | | : label line 0
1506 l0 l1 : label line 1
1508 If they would touch each other or overlap, then we need
1509 additional "label lines":
1511 foo + bar
1512 ~~~ ~~~
1513 | | : label line 0
1514 | label 1 : label line 1
1515 label 0 : label line 2
1517 Place the final label on label line 1, and work backwards, adding
1518 label lines as needed.
1520 If multiple labels are at the same place, put them on separate
1521 label lines:
1523 foo + bar
1524 ^ : label line 0
1525 | : label line 1
1526 label 1 : label line 2
1527 label 0 : label line 3. */
1529 int max_label_line = 1;
1531 int next_column = INT_MAX;
1532 line_label *label;
1533 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1535 /* Would this label "touch" or overlap the next label? */
1536 if (label->m_column + label->m_length >= (size_t)next_column)
1537 max_label_line++;
1539 label->m_label_line = max_label_line;
1540 next_column = label->m_column;
1544 /* Print the "label lines". For each label within the line, print
1545 either a vertical bar ('|') for the labels that are lower down, or the
1546 labels themselves once we've reached their line. */
1548 /* Keep track of in which column we last printed a vertical bar.
1549 This allows us to suppress duplicate vertical bars for the case
1550 where multiple labels are on one column. */
1551 int last_vbar = 0;
1552 for (int label_line = 0; label_line <= max_label_line; label_line++)
1554 start_annotation_line ();
1555 pp_space (m_pp);
1556 int column = 1 + m_x_offset;
1557 line_label *label;
1558 FOR_EACH_VEC_ELT (labels, i, label)
1560 if (label_line > label->m_label_line)
1561 /* We've printed all the labels for this label line. */
1562 break;
1564 if (label_line == label->m_label_line)
1566 gcc_assert (column <= label->m_column);
1567 move_to_column (&column, label->m_column, true);
1568 m_colorizer.set_range (label->m_state_idx);
1569 pp_string (m_pp, label->m_text.m_buffer);
1570 m_colorizer.set_normal_text ();
1571 column += label->m_length;
1573 else if (label->m_column != last_vbar)
1575 gcc_assert (column <= label->m_column);
1576 move_to_column (&column, label->m_column, true);
1577 m_colorizer.set_range (label->m_state_idx);
1578 pp_character (m_pp, '|');
1579 m_colorizer.set_normal_text ();
1580 last_vbar = column;
1581 column++;
1584 print_newline ();
1588 /* Clean up. */
1590 line_label *label;
1591 FOR_EACH_VEC_ELT (labels, i, label)
1592 label->m_text.maybe_free ();
1596 /* If there are any fixit hints inserting new lines before source line ROW,
1597 print them.
1599 They are printed on lines of their own, before the source line
1600 itself, with a leading '+'. */
1602 void
1603 layout::print_leading_fixits (linenum_type row)
1605 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1607 const fixit_hint *hint = m_fixit_hints[i];
1609 if (!hint->ends_with_newline_p ())
1610 /* Not a newline fixit; print it in print_trailing_fixits. */
1611 continue;
1613 gcc_assert (hint->insertion_p ());
1615 if (hint->affects_line_p (m_exploc.file, row))
1617 /* Printing the '+' with normal colorization
1618 and the inserted line with "insert" colorization
1619 helps them stand out from each other, and from
1620 the surrounding text. */
1621 m_colorizer.set_normal_text ();
1622 start_annotation_line ('+');
1623 pp_character (m_pp, '+');
1624 m_colorizer.set_fixit_insert ();
1625 /* Print all but the trailing newline of the fix-it hint.
1626 We have to print the newline separately to avoid
1627 getting additional pp prefixes printed. */
1628 for (size_t i = 0; i < hint->get_length () - 1; i++)
1629 pp_character (m_pp, hint->get_string ()[i]);
1630 m_colorizer.set_normal_text ();
1631 pp_newline (m_pp);
1636 /* Subroutine of layout::print_trailing_fixits.
1638 Determine if the annotation line printed for LINE contained
1639 the exact range from START_COLUMN to FINISH_COLUMN. */
1641 bool
1642 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1643 int finish_column) const
1645 layout_range *range;
1646 int i;
1647 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1648 if (range->m_start.m_line == line
1649 && range->m_start.m_column == start_column
1650 && range->m_finish.m_line == line
1651 && range->m_finish.m_column == finish_column)
1652 return true;
1653 return false;
1656 /* Classes for printing trailing fix-it hints i.e. those that
1657 don't add new lines.
1659 For insertion, these can look like:
1661 new_text
1663 For replacement, these can look like:
1665 ------------- : underline showing affected range
1666 new_text
1668 For deletion, these can look like:
1670 ------------- : underline showing affected range
1672 This can become confusing if they overlap, and so we need
1673 to do some preprocessing to decide what to print.
1674 We use the list of fixit_hint instances affecting the line
1675 to build a list of "correction" instances, and print the
1676 latter.
1678 For example, consider a set of fix-its for converting
1679 a C-style cast to a C++ const_cast.
1681 Given:
1683 ..000000000111111111122222222223333333333.
1684 ..123456789012345678901234567890123456789.
1685 foo *f = (foo *)ptr->field;
1686 ^~~~~
1688 and the fix-it hints:
1689 - replace col 10 (the open paren) with "const_cast<"
1690 - replace col 16 (the close paren) with "> ("
1691 - insert ")" before col 27
1693 then we would get odd-looking output:
1695 foo *f = (foo *)ptr->field;
1696 ^~~~~
1698 const_cast<
1700 > ( )
1702 It would be better to detect when fixit hints are going to
1703 overlap (those that require new lines), and to consolidate
1704 the printing of such fixits, giving something like:
1706 foo *f = (foo *)ptr->field;
1707 ^~~~~
1708 -----------------
1709 const_cast<foo *> (ptr->field)
1711 This works by detecting when the printing would overlap, and
1712 effectively injecting no-op replace hints into the gaps between
1713 such fix-its, so that the printing joins up.
1715 In the above example, the overlap of:
1716 - replace col 10 (the open paren) with "const_cast<"
1717 and:
1718 - replace col 16 (the close paren) with "> ("
1719 is fixed by injecting a no-op:
1720 - replace cols 11-15 with themselves ("foo *")
1721 and consolidating these, making:
1722 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1723 i.e.:
1724 - replace cols 10-16 with "const_cast<foo *> ("
1726 This overlaps with the final fix-it hint:
1727 - insert ")" before col 27
1728 and so we repeat the consolidation process, by injecting
1729 a no-op:
1730 - replace cols 17-26 with themselves ("ptr->field")
1731 giving:
1732 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1733 i.e.:
1734 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1736 and is thus printed as desired. */
1738 /* A range of columns within a line. */
1740 struct column_range
1742 column_range (int start_, int finish_) : start (start_), finish (finish_)
1744 /* We must have either a range, or an insertion. */
1745 gcc_assert (start <= finish || finish == start - 1);
1748 bool operator== (const column_range &other) const
1750 return start == other.start && finish == other.finish;
1753 int start;
1754 int finish;
1757 /* Get the range of columns that HINT would affect. */
1759 static column_range
1760 get_affected_columns (const fixit_hint *hint)
1762 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1763 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1765 return column_range (start_column, finish_column);
1768 /* Get the range of columns that would be printed for HINT. */
1770 static column_range
1771 get_printed_columns (const fixit_hint *hint)
1773 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1774 int final_hint_column = start_column + hint->get_length () - 1;
1775 if (hint->insertion_p ())
1777 return column_range (start_column, final_hint_column);
1779 else
1781 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1783 return column_range (start_column,
1784 MAX (finish_column, final_hint_column));
1788 /* A correction on a particular line.
1789 This describes a plan for how to print one or more fixit_hint
1790 instances that affected the line, potentially consolidating hints
1791 into corrections to make the result easier for the user to read. */
1793 struct correction
1795 correction (column_range affected_columns,
1796 column_range printed_columns,
1797 const char *new_text, size_t new_text_len)
1798 : m_affected_columns (affected_columns),
1799 m_printed_columns (printed_columns),
1800 m_text (xstrdup (new_text)),
1801 m_len (new_text_len),
1802 m_alloc_sz (new_text_len + 1)
1806 ~correction () { free (m_text); }
1808 bool insertion_p () const
1810 return m_affected_columns.start == m_affected_columns.finish + 1;
1813 void ensure_capacity (size_t len);
1814 void ensure_terminated ();
1816 void overwrite (int dst_offset, const char_span &src_span)
1818 gcc_assert (dst_offset >= 0);
1819 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1820 memcpy (m_text + dst_offset, src_span.get_buffer (),
1821 src_span.length ());
1824 /* If insert, then start: the column before which the text
1825 is to be inserted, and finish is offset by the length of
1826 the replacement.
1827 If replace, then the range of columns affected. */
1828 column_range m_affected_columns;
1830 /* If insert, then start: the column before which the text
1831 is to be inserted, and finish is offset by the length of
1832 the replacement.
1833 If replace, then the range of columns affected. */
1834 column_range m_printed_columns;
1836 /* The text to be inserted/used as replacement. */
1837 char *m_text;
1838 size_t m_len;
1839 size_t m_alloc_sz;
1842 /* Ensure that m_text can hold a string of length LEN
1843 (plus 1 for 0-termination). */
1845 void
1846 correction::ensure_capacity (size_t len)
1848 /* Allow 1 extra byte for 0-termination. */
1849 if (m_alloc_sz < (len + 1))
1851 size_t new_alloc_sz = (len + 1) * 2;
1852 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1853 m_alloc_sz = new_alloc_sz;
1857 /* Ensure that m_text is 0-terminated. */
1859 void
1860 correction::ensure_terminated ()
1862 /* 0-terminate the buffer. */
1863 gcc_assert (m_len < m_alloc_sz);
1864 m_text[m_len] = '\0';
1867 /* A list of corrections affecting a particular line.
1868 This is used by layout::print_trailing_fixits for planning
1869 how to print the fix-it hints affecting the line. */
1871 struct line_corrections
1873 line_corrections (const char *filename, linenum_type row)
1874 : m_filename (filename), m_row (row)
1876 ~line_corrections ();
1878 void add_hint (const fixit_hint *hint);
1880 const char *m_filename;
1881 linenum_type m_row;
1882 auto_vec <correction *> m_corrections;
1885 /* struct line_corrections. */
1887 line_corrections::~line_corrections ()
1889 unsigned i;
1890 correction *c;
1891 FOR_EACH_VEC_ELT (m_corrections, i, c)
1892 delete c;
1895 /* A struct wrapping a particular source line, allowing
1896 run-time bounds-checking of accesses in a checked build. */
1898 struct source_line
1900 source_line (const char *filename, int line);
1902 char_span as_span () { return char_span (chars, width); }
1904 const char *chars;
1905 int width;
1908 /* source_line's ctor. */
1910 source_line::source_line (const char *filename, int line)
1912 char_span span = location_get_source_line (filename, line);
1913 chars = span.get_buffer ();
1914 width = span.length ();
1917 /* Add HINT to the corrections for this line.
1918 Attempt to consolidate nearby hints so that they will not
1919 overlap with printed. */
1921 void
1922 line_corrections::add_hint (const fixit_hint *hint)
1924 column_range affected_columns = get_affected_columns (hint);
1925 column_range printed_columns = get_printed_columns (hint);
1927 /* Potentially consolidate. */
1928 if (!m_corrections.is_empty ())
1930 correction *last_correction
1931 = m_corrections[m_corrections.length () - 1];
1933 /* The following consolidation code assumes that the fix-it hints
1934 have been sorted by start (done within layout's ctor). */
1935 gcc_assert (affected_columns.start
1936 >= last_correction->m_affected_columns.start);
1937 gcc_assert (printed_columns.start
1938 >= last_correction->m_printed_columns.start);
1940 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1942 /* We have two hints for which the printed forms of the hints
1943 would touch or overlap, so we need to consolidate them to avoid
1944 confusing the user.
1945 Attempt to inject a "replace" correction from immediately
1946 after the end of the last hint to immediately before the start
1947 of the next hint. */
1948 column_range between (last_correction->m_affected_columns.finish + 1,
1949 printed_columns.start - 1);
1951 /* Try to read the source. */
1952 source_line line (m_filename, m_row);
1953 if (line.chars && between.finish < line.width)
1955 /* Consolidate into the last correction:
1956 add a no-op "replace" of the "between" text, and
1957 add the text from the new hint. */
1958 int old_len = last_correction->m_len;
1959 gcc_assert (old_len >= 0);
1960 int between_len = between.finish + 1 - between.start;
1961 gcc_assert (between_len >= 0);
1962 int new_len = old_len + between_len + hint->get_length ();
1963 gcc_assert (new_len >= 0);
1964 last_correction->ensure_capacity (new_len);
1965 last_correction->overwrite
1966 (old_len,
1967 line.as_span ().subspan (between.start - 1,
1968 between.finish + 1 - between.start));
1969 last_correction->overwrite (old_len + between_len,
1970 char_span (hint->get_string (),
1971 hint->get_length ()));
1972 last_correction->m_len = new_len;
1973 last_correction->ensure_terminated ();
1974 last_correction->m_affected_columns.finish
1975 = affected_columns.finish;
1976 last_correction->m_printed_columns.finish
1977 += between_len + hint->get_length ();
1978 return;
1983 /* If no consolidation happened, add a new correction instance. */
1984 m_corrections.safe_push (new correction (affected_columns,
1985 printed_columns,
1986 hint->get_string (),
1987 hint->get_length ()));
1990 /* If there are any fixit hints on source line ROW, print them.
1991 They are printed in order, attempting to combine them onto lines, but
1992 starting new lines if necessary.
1993 Fix-it hints that insert new lines are handled separately,
1994 in layout::print_leading_fixits. */
1996 void
1997 layout::print_trailing_fixits (linenum_type row)
1999 /* Build a list of correction instances for the line,
2000 potentially consolidating hints (for the sake of readability). */
2001 line_corrections corrections (m_exploc.file, row);
2002 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2004 const fixit_hint *hint = m_fixit_hints[i];
2006 /* Newline fixits are handled by layout::print_leading_fixits. */
2007 if (hint->ends_with_newline_p ())
2008 continue;
2010 if (hint->affects_line_p (m_exploc.file, row))
2011 corrections.add_hint (hint);
2014 /* Now print the corrections. */
2015 unsigned i;
2016 correction *c;
2017 int column = m_x_offset;
2019 if (!corrections.m_corrections.is_empty ())
2020 start_annotation_line ();
2022 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2024 /* For now we assume each fixit hint can only touch one line. */
2025 if (c->insertion_p ())
2027 /* This assumes the insertion just affects one line. */
2028 int start_column = c->m_printed_columns.start;
2029 move_to_column (&column, start_column, true);
2030 m_colorizer.set_fixit_insert ();
2031 pp_string (m_pp, c->m_text);
2032 m_colorizer.set_normal_text ();
2033 column += c->m_len;
2035 else
2037 /* If the range of the replacement wasn't printed in the
2038 annotation line, then print an extra underline to
2039 indicate exactly what is being replaced.
2040 Always show it for removals. */
2041 int start_column = c->m_affected_columns.start;
2042 int finish_column = c->m_affected_columns.finish;
2043 if (!annotation_line_showed_range_p (row, start_column,
2044 finish_column)
2045 || c->m_len == 0)
2047 move_to_column (&column, start_column, true);
2048 m_colorizer.set_fixit_delete ();
2049 for (; column <= finish_column; column++)
2050 pp_character (m_pp, '-');
2051 m_colorizer.set_normal_text ();
2053 /* Print the replacement text. REPLACE also covers
2054 removals, so only do this extra work (potentially starting
2055 a new line) if we have actual replacement text. */
2056 if (c->m_len > 0)
2058 move_to_column (&column, start_column, true);
2059 m_colorizer.set_fixit_insert ();
2060 pp_string (m_pp, c->m_text);
2061 m_colorizer.set_normal_text ();
2062 column += c->m_len;
2067 /* Add a trailing newline, if necessary. */
2068 move_to_column (&column, 0, false);
2071 /* Disable any colorization and emit a newline. */
2073 void
2074 layout::print_newline ()
2076 m_colorizer.set_normal_text ();
2077 pp_newline (m_pp);
2080 /* Return true if (ROW/COLUMN) is within a range of the layout.
2081 If it returns true, OUT_STATE is written to, with the
2082 range index, and whether we should draw the caret at
2083 (ROW/COLUMN) (as opposed to an underline). */
2085 bool
2086 layout::get_state_at_point (/* Inputs. */
2087 linenum_type row, int column,
2088 int first_non_ws, int last_non_ws,
2089 /* Outputs. */
2090 point_state *out_state)
2092 layout_range *range;
2093 int i;
2094 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2096 if (range->contains_point (row, column))
2098 out_state->range_idx = i;
2100 /* Are we at the range's caret? is it visible? */
2101 out_state->draw_caret_p = false;
2102 if (range->m_show_caret_p
2103 && row == range->m_caret.m_line
2104 && column == range->m_caret.m_column)
2105 out_state->draw_caret_p = true;
2107 /* Within a multiline range, don't display any underline
2108 in any leading or trailing whitespace on a line.
2109 We do display carets, however. */
2110 if (!out_state->draw_caret_p)
2111 if (column < first_non_ws || column > last_non_ws)
2112 return false;
2114 /* We are within a range. */
2115 return true;
2119 return false;
2122 /* Helper function for use by layout::print_line when printing the
2123 annotation line under the source line.
2124 Get the column beyond the rightmost one that could contain a caret or
2125 range marker, given that we stop rendering at trailing whitespace.
2126 ROW is the source line within the given file.
2127 CARET_COLUMN is the column of range 0's caret.
2128 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2129 character of source (as determined when printing the source line). */
2132 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2133 int last_non_ws_column)
2135 int result = caret_column + 1;
2137 layout_range *range;
2138 int i;
2139 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2141 if (row >= range->m_start.m_line)
2143 if (range->m_finish.m_line == row)
2145 /* On the final line within a range; ensure that
2146 we render up to the end of the range. */
2147 if (result <= range->m_finish.m_column)
2148 result = range->m_finish.m_column + 1;
2150 else if (row < range->m_finish.m_line)
2152 /* Within a multiline range; ensure that we render up to the
2153 last non-whitespace column. */
2154 if (result <= last_non_ws_column)
2155 result = last_non_ws_column + 1;
2160 return result;
2163 /* Given *COLUMN as an x-coordinate, print spaces to position
2164 successive output at DEST_COLUMN, printing a newline if necessary,
2165 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2166 left margin after any newline. */
2168 void
2169 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2171 /* Start a new line if we need to. */
2172 if (*column > dest_column)
2174 print_newline ();
2175 if (add_left_margin)
2176 start_annotation_line ();
2177 *column = m_x_offset;
2180 while (*column < dest_column)
2182 pp_space (m_pp);
2183 (*column)++;
2187 /* For debugging layout issues, render a ruler giving column numbers
2188 (after the 1-column indent). */
2190 void
2191 layout::show_ruler (int max_column) const
2193 /* Hundreds. */
2194 if (max_column > 99)
2196 start_annotation_line ();
2197 pp_space (m_pp);
2198 for (int column = 1 + m_x_offset; column <= max_column; column++)
2199 if (column % 10 == 0)
2200 pp_character (m_pp, '0' + (column / 100) % 10);
2201 else
2202 pp_space (m_pp);
2203 pp_newline (m_pp);
2206 /* Tens. */
2207 start_annotation_line ();
2208 pp_space (m_pp);
2209 for (int column = 1 + m_x_offset; column <= max_column; column++)
2210 if (column % 10 == 0)
2211 pp_character (m_pp, '0' + (column / 10) % 10);
2212 else
2213 pp_space (m_pp);
2214 pp_newline (m_pp);
2216 /* Units. */
2217 start_annotation_line ();
2218 pp_space (m_pp);
2219 for (int column = 1 + m_x_offset; column <= max_column; column++)
2220 pp_character (m_pp, '0' + (column % 10));
2221 pp_newline (m_pp);
2224 /* Print leading fix-its (for new lines inserted before the source line)
2225 then the source line, followed by an annotation line
2226 consisting of any caret/underlines, then any fixits.
2227 If the source line can't be read, print nothing. */
2228 void
2229 layout::print_line (linenum_type row)
2231 char_span line = location_get_source_line (m_exploc.file, row);
2232 if (!line)
2233 return;
2235 line_bounds lbounds;
2236 print_leading_fixits (row);
2237 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2238 if (should_print_annotation_line_p (row))
2239 print_annotation_line (row, lbounds);
2240 if (m_show_labels_p)
2241 print_any_labels (row);
2242 print_trailing_fixits (row);
2245 } /* End of anonymous namespace. */
2247 /* If LOC is within the spans of lines that will already be printed for
2248 this gcc_rich_location, then add it as a secondary location and return true.
2250 Otherwise return false. */
2252 bool
2253 gcc_rich_location::add_location_if_nearby (location_t loc)
2255 /* Use the layout location-handling logic to sanitize LOC,
2256 filtering it to the current line spans within a temporary
2257 layout instance. */
2258 layout layout (global_dc, this, DK_ERROR);
2259 location_range loc_range;
2260 loc_range.m_loc = loc;
2261 loc_range.m_show_caret_p = false;
2262 if (!layout.maybe_add_location_range (&loc_range, true))
2263 return false;
2265 add_range (loc, false);
2266 return true;
2269 /* Print the physical source code corresponding to the location of
2270 this diagnostic, with additional annotations. */
2272 void
2273 diagnostic_show_locus (diagnostic_context * context,
2274 rich_location *richloc,
2275 diagnostic_t diagnostic_kind)
2277 pp_newline (context->printer);
2279 location_t loc = richloc->get_loc ();
2280 /* Do nothing if source-printing has been disabled. */
2281 if (!context->show_caret)
2282 return;
2284 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2285 if (loc <= BUILTINS_LOCATION)
2286 return;
2288 /* Don't print the same source location twice in a row, unless we have
2289 fix-it hints. */
2290 if (loc == context->last_location
2291 && richloc->get_num_fixit_hints () == 0)
2292 return;
2294 context->last_location = loc;
2296 char *saved_prefix = pp_take_prefix (context->printer);
2297 pp_set_prefix (context->printer, NULL);
2299 layout layout (context, richloc, diagnostic_kind);
2300 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2301 line_span_idx++)
2303 const line_span *line_span = layout.get_line_span (line_span_idx);
2304 if (context->show_line_numbers_p)
2306 /* With line numbers, we should show whenever the line-numbering
2307 "jumps". */
2308 if (line_span_idx > 0)
2309 layout.print_gap_in_line_numbering ();
2311 else
2313 /* Without line numbers, we print headings for some line spans. */
2314 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2316 expanded_location exploc
2317 = layout.get_expanded_location (line_span);
2318 context->start_span (context, exploc);
2321 linenum_type last_line = line_span->get_last_line ();
2322 for (linenum_type row = line_span->get_first_line ();
2323 row <= last_line; row++)
2324 layout.print_line (row);
2327 pp_set_prefix (context->printer, saved_prefix);
2330 #if CHECKING_P
2332 namespace selftest {
2334 /* Selftests for diagnostic_show_locus. */
2336 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2338 static void
2339 test_diagnostic_show_locus_unknown_location ()
2341 test_diagnostic_context dc;
2342 rich_location richloc (line_table, UNKNOWN_LOCATION);
2343 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2344 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2347 /* Verify that diagnostic_show_locus works sanely for various
2348 single-line cases.
2350 All of these work on the following 1-line source file:
2351 .0000000001111111
2352 .1234567890123456
2353 "foo = bar.field;\n"
2354 which is set up by test_diagnostic_show_locus_one_liner and calls
2355 them. */
2357 /* Just a caret. */
2359 static void
2360 test_one_liner_simple_caret ()
2362 test_diagnostic_context dc;
2363 location_t caret = linemap_position_for_column (line_table, 10);
2364 rich_location richloc (line_table, caret);
2365 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2366 ASSERT_STREQ ("\n"
2367 " foo = bar.field;\n"
2368 " ^\n",
2369 pp_formatted_text (dc.printer));
2372 /* Caret and range. */
2374 static void
2375 test_one_liner_caret_and_range ()
2377 test_diagnostic_context dc;
2378 location_t caret = linemap_position_for_column (line_table, 10);
2379 location_t start = linemap_position_for_column (line_table, 7);
2380 location_t finish = linemap_position_for_column (line_table, 15);
2381 location_t loc = make_location (caret, start, finish);
2382 rich_location richloc (line_table, loc);
2383 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2384 ASSERT_STREQ ("\n"
2385 " foo = bar.field;\n"
2386 " ~~~^~~~~~\n",
2387 pp_formatted_text (dc.printer));
2390 /* Multiple ranges and carets. */
2392 static void
2393 test_one_liner_multiple_carets_and_ranges ()
2395 test_diagnostic_context dc;
2396 location_t foo
2397 = make_location (linemap_position_for_column (line_table, 2),
2398 linemap_position_for_column (line_table, 1),
2399 linemap_position_for_column (line_table, 3));
2400 dc.caret_chars[0] = 'A';
2402 location_t bar
2403 = make_location (linemap_position_for_column (line_table, 8),
2404 linemap_position_for_column (line_table, 7),
2405 linemap_position_for_column (line_table, 9));
2406 dc.caret_chars[1] = 'B';
2408 location_t field
2409 = make_location (linemap_position_for_column (line_table, 13),
2410 linemap_position_for_column (line_table, 11),
2411 linemap_position_for_column (line_table, 15));
2412 dc.caret_chars[2] = 'C';
2414 rich_location richloc (line_table, foo);
2415 richloc.add_range (bar, true);
2416 richloc.add_range (field, true);
2417 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2418 ASSERT_STREQ ("\n"
2419 " foo = bar.field;\n"
2420 " ~A~ ~B~ ~~C~~\n",
2421 pp_formatted_text (dc.printer));
2424 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2426 static void
2427 test_one_liner_fixit_insert_before ()
2429 test_diagnostic_context dc;
2430 location_t caret = linemap_position_for_column (line_table, 7);
2431 rich_location richloc (line_table, caret);
2432 richloc.add_fixit_insert_before ("&");
2433 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2434 ASSERT_STREQ ("\n"
2435 " foo = bar.field;\n"
2436 " ^\n"
2437 " &\n",
2438 pp_formatted_text (dc.printer));
2441 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2443 static void
2444 test_one_liner_fixit_insert_after ()
2446 test_diagnostic_context dc;
2447 location_t start = linemap_position_for_column (line_table, 1);
2448 location_t finish = linemap_position_for_column (line_table, 3);
2449 location_t foo = make_location (start, start, finish);
2450 rich_location richloc (line_table, foo);
2451 richloc.add_fixit_insert_after ("[0]");
2452 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2453 ASSERT_STREQ ("\n"
2454 " foo = bar.field;\n"
2455 " ^~~\n"
2456 " [0]\n",
2457 pp_formatted_text (dc.printer));
2460 /* Removal fix-it hint: removal of the ".field". */
2462 static void
2463 test_one_liner_fixit_remove ()
2465 test_diagnostic_context dc;
2466 location_t start = linemap_position_for_column (line_table, 10);
2467 location_t finish = linemap_position_for_column (line_table, 15);
2468 location_t dot = make_location (start, start, finish);
2469 rich_location richloc (line_table, dot);
2470 richloc.add_fixit_remove ();
2471 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2472 ASSERT_STREQ ("\n"
2473 " foo = bar.field;\n"
2474 " ^~~~~~\n"
2475 " ------\n",
2476 pp_formatted_text (dc.printer));
2479 /* Replace fix-it hint: replacing "field" with "m_field". */
2481 static void
2482 test_one_liner_fixit_replace ()
2484 test_diagnostic_context dc;
2485 location_t start = linemap_position_for_column (line_table, 11);
2486 location_t finish = linemap_position_for_column (line_table, 15);
2487 location_t field = make_location (start, start, finish);
2488 rich_location richloc (line_table, field);
2489 richloc.add_fixit_replace ("m_field");
2490 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2491 ASSERT_STREQ ("\n"
2492 " foo = bar.field;\n"
2493 " ^~~~~\n"
2494 " m_field\n",
2495 pp_formatted_text (dc.printer));
2498 /* Replace fix-it hint: replacing "field" with "m_field",
2499 but where the caret was elsewhere. */
2501 static void
2502 test_one_liner_fixit_replace_non_equal_range ()
2504 test_diagnostic_context dc;
2505 location_t equals = linemap_position_for_column (line_table, 5);
2506 location_t start = linemap_position_for_column (line_table, 11);
2507 location_t finish = linemap_position_for_column (line_table, 15);
2508 rich_location richloc (line_table, equals);
2509 source_range range;
2510 range.m_start = start;
2511 range.m_finish = finish;
2512 richloc.add_fixit_replace (range, "m_field");
2513 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2514 /* The replacement range is not indicated in the annotation line, so
2515 it should be indicated via an additional underline. */
2516 ASSERT_STREQ ("\n"
2517 " foo = bar.field;\n"
2518 " ^\n"
2519 " -----\n"
2520 " m_field\n",
2521 pp_formatted_text (dc.printer));
2524 /* Replace fix-it hint: replacing "field" with "m_field",
2525 where the caret was elsewhere, but where a secondary range
2526 exactly covers "field". */
2528 static void
2529 test_one_liner_fixit_replace_equal_secondary_range ()
2531 test_diagnostic_context dc;
2532 location_t equals = linemap_position_for_column (line_table, 5);
2533 location_t start = linemap_position_for_column (line_table, 11);
2534 location_t finish = linemap_position_for_column (line_table, 15);
2535 rich_location richloc (line_table, equals);
2536 location_t field = make_location (start, start, finish);
2537 richloc.add_range (field, false);
2538 richloc.add_fixit_replace (field, "m_field");
2539 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2540 /* The replacement range is indicated in the annotation line,
2541 so it shouldn't be indicated via an additional underline. */
2542 ASSERT_STREQ ("\n"
2543 " foo = bar.field;\n"
2544 " ^ ~~~~~\n"
2545 " m_field\n",
2546 pp_formatted_text (dc.printer));
2549 /* Verify that we can use ad-hoc locations when adding fixits to a
2550 rich_location. */
2552 static void
2553 test_one_liner_fixit_validation_adhoc_locations ()
2555 /* Generate a range that's too long to be packed, so must
2556 be stored as an ad-hoc location (given the defaults
2557 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2558 const location_t c7 = linemap_position_for_column (line_table, 7);
2559 const location_t c47 = linemap_position_for_column (line_table, 47);
2560 const location_t loc = make_location (c7, c7, c47);
2562 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2563 return;
2565 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2567 /* Insert. */
2569 rich_location richloc (line_table, loc);
2570 richloc.add_fixit_insert_before (loc, "test");
2571 /* It should not have been discarded by the validator. */
2572 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2574 test_diagnostic_context dc;
2575 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2576 ASSERT_STREQ ("\n"
2577 " foo = bar.field;\n"
2578 " ^~~~~~~~~~ \n"
2579 " test\n",
2580 pp_formatted_text (dc.printer));
2583 /* Remove. */
2585 rich_location richloc (line_table, loc);
2586 source_range range = source_range::from_locations (loc, c47);
2587 richloc.add_fixit_remove (range);
2588 /* It should not have been discarded by the validator. */
2589 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2591 test_diagnostic_context dc;
2592 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2593 ASSERT_STREQ ("\n"
2594 " foo = bar.field;\n"
2595 " ^~~~~~~~~~ \n"
2596 " -----------------------------------------\n",
2597 pp_formatted_text (dc.printer));
2600 /* Replace. */
2602 rich_location richloc (line_table, loc);
2603 source_range range = source_range::from_locations (loc, c47);
2604 richloc.add_fixit_replace (range, "test");
2605 /* It should not have been discarded by the validator. */
2606 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2608 test_diagnostic_context dc;
2609 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2610 ASSERT_STREQ ("\n"
2611 " foo = bar.field;\n"
2612 " ^~~~~~~~~~ \n"
2613 " test\n",
2614 pp_formatted_text (dc.printer));
2618 /* Test of consolidating insertions at the same location. */
2620 static void
2621 test_one_liner_many_fixits_1 ()
2623 test_diagnostic_context dc;
2624 location_t equals = linemap_position_for_column (line_table, 5);
2625 rich_location richloc (line_table, equals);
2626 for (int i = 0; i < 19; i++)
2627 richloc.add_fixit_insert_before ("a");
2628 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2629 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2630 ASSERT_STREQ ("\n"
2631 " foo = bar.field;\n"
2632 " ^\n"
2633 " aaaaaaaaaaaaaaaaaaa\n",
2634 pp_formatted_text (dc.printer));
2637 /* Ensure that we can add an arbitrary number of fix-it hints to a
2638 rich_location, even if they are not consolidated. */
2640 static void
2641 test_one_liner_many_fixits_2 ()
2643 test_diagnostic_context dc;
2644 location_t equals = linemap_position_for_column (line_table, 5);
2645 rich_location richloc (line_table, equals);
2646 for (int i = 0; i < 19; i++)
2648 location_t loc = linemap_position_for_column (line_table, i * 2);
2649 richloc.add_fixit_insert_before (loc, "a");
2651 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2652 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2653 ASSERT_STREQ ("\n"
2654 " foo = bar.field;\n"
2655 " ^\n"
2656 "a a a a a a a a a a a a a a a a a a a\n",
2657 pp_formatted_text (dc.printer));
2660 /* Test of labeling the ranges within a rich_location. */
2662 static void
2663 test_one_liner_labels ()
2665 location_t foo
2666 = make_location (linemap_position_for_column (line_table, 1),
2667 linemap_position_for_column (line_table, 1),
2668 linemap_position_for_column (line_table, 3));
2669 location_t bar
2670 = make_location (linemap_position_for_column (line_table, 7),
2671 linemap_position_for_column (line_table, 7),
2672 linemap_position_for_column (line_table, 9));
2673 location_t field
2674 = make_location (linemap_position_for_column (line_table, 11),
2675 linemap_position_for_column (line_table, 11),
2676 linemap_position_for_column (line_table, 15));
2678 /* Example where all the labels fit on one line. */
2680 text_range_label label0 ("0");
2681 text_range_label label1 ("1");
2682 text_range_label label2 ("2");
2683 gcc_rich_location richloc (foo, &label0);
2684 richloc.add_range (bar, false, &label1);
2685 richloc.add_range (field, false, &label2);
2688 test_diagnostic_context dc;
2689 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2690 ASSERT_STREQ ("\n"
2691 " foo = bar.field;\n"
2692 " ^~~ ~~~ ~~~~~\n"
2693 " | | |\n"
2694 " 0 1 2\n",
2695 pp_formatted_text (dc.printer));
2698 /* Verify that we can disable label-printing. */
2700 test_diagnostic_context dc;
2701 dc.show_labels_p = false;
2702 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2703 ASSERT_STREQ ("\n"
2704 " foo = bar.field;\n"
2705 " ^~~ ~~~ ~~~~~\n",
2706 pp_formatted_text (dc.printer));
2710 /* Example where the labels need extra lines. */
2712 text_range_label label0 ("label 0");
2713 text_range_label label1 ("label 1");
2714 text_range_label label2 ("label 2");
2715 gcc_rich_location richloc (foo, &label0);
2716 richloc.add_range (bar, false, &label1);
2717 richloc.add_range (field, false, &label2);
2719 test_diagnostic_context dc;
2720 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2721 ASSERT_STREQ ("\n"
2722 " foo = bar.field;\n"
2723 " ^~~ ~~~ ~~~~~\n"
2724 " | | |\n"
2725 " | | label 2\n"
2726 " | label 1\n"
2727 " label 0\n",
2728 pp_formatted_text (dc.printer));
2731 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2732 but label 1 just touches label 2. */
2734 text_range_label label0 ("aaaaa");
2735 text_range_label label1 ("bbbb");
2736 text_range_label label2 ("c");
2737 gcc_rich_location richloc (foo, &label0);
2738 richloc.add_range (bar, false, &label1);
2739 richloc.add_range (field, false, &label2);
2741 test_diagnostic_context dc;
2742 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2743 ASSERT_STREQ ("\n"
2744 " foo = bar.field;\n"
2745 " ^~~ ~~~ ~~~~~\n"
2746 " | | |\n"
2747 " | | c\n"
2748 " aaaaa bbbb\n",
2749 pp_formatted_text (dc.printer));
2752 /* Example of out-of-order ranges (thus requiring a sort). */
2754 text_range_label label0 ("0");
2755 text_range_label label1 ("1");
2756 text_range_label label2 ("2");
2757 gcc_rich_location richloc (field, &label0);
2758 richloc.add_range (bar, false, &label1);
2759 richloc.add_range (foo, false, &label2);
2761 test_diagnostic_context dc;
2762 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2763 ASSERT_STREQ ("\n"
2764 " foo = bar.field;\n"
2765 " ~~~ ~~~ ^~~~~\n"
2766 " | | |\n"
2767 " 2 1 0\n",
2768 pp_formatted_text (dc.printer));
2771 /* Ensure we don't ICE if multiple ranges with labels are on
2772 the same point. */
2774 text_range_label label0 ("label 0");
2775 text_range_label label1 ("label 1");
2776 text_range_label label2 ("label 2");
2777 gcc_rich_location richloc (bar, &label0);
2778 richloc.add_range (bar, false, &label1);
2779 richloc.add_range (bar, false, &label2);
2781 test_diagnostic_context dc;
2782 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2783 ASSERT_STREQ ("\n"
2784 " foo = bar.field;\n"
2785 " ^~~\n"
2786 " |\n"
2787 " label 2\n"
2788 " label 1\n"
2789 " label 0\n",
2790 pp_formatted_text (dc.printer));
2793 /* Verify that a NULL result from range_label::get_text is
2794 handled gracefully. */
2796 text_range_label label (NULL);
2797 gcc_rich_location richloc (bar, &label);
2799 test_diagnostic_context dc;
2800 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2801 ASSERT_STREQ ("\n"
2802 " foo = bar.field;\n"
2803 " ^~~\n",
2804 pp_formatted_text (dc.printer));
2807 /* TODO: example of formatted printing (needs to be in
2808 gcc-rich-location.c due to Makefile.in issues). */
2811 /* Run the various one-liner tests. */
2813 static void
2814 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2816 /* Create a tempfile and write some text to it.
2817 ....................0000000001111111.
2818 ....................1234567890123456. */
2819 const char *content = "foo = bar.field;\n";
2820 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2821 line_table_test ltt (case_);
2823 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2825 location_t line_end = linemap_position_for_column (line_table, 16);
2827 /* Don't attempt to run the tests if column data might be unavailable. */
2828 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2829 return;
2831 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2832 ASSERT_EQ (1, LOCATION_LINE (line_end));
2833 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2835 test_one_liner_simple_caret ();
2836 test_one_liner_caret_and_range ();
2837 test_one_liner_multiple_carets_and_ranges ();
2838 test_one_liner_fixit_insert_before ();
2839 test_one_liner_fixit_insert_after ();
2840 test_one_liner_fixit_remove ();
2841 test_one_liner_fixit_replace ();
2842 test_one_liner_fixit_replace_non_equal_range ();
2843 test_one_liner_fixit_replace_equal_secondary_range ();
2844 test_one_liner_fixit_validation_adhoc_locations ();
2845 test_one_liner_many_fixits_1 ();
2846 test_one_liner_many_fixits_2 ();
2847 test_one_liner_labels ();
2850 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2852 static void
2853 test_add_location_if_nearby (const line_table_case &case_)
2855 /* Create a tempfile and write some text to it.
2856 ...000000000111111111122222222223333333333.
2857 ...123456789012345678901234567890123456789. */
2858 const char *content
2859 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2860 "struct different_line\n" /* line 2. */
2861 "{\n" /* line 3. */
2862 " double x;\n" /* line 4. */
2863 " double y;\n" /* line 5. */
2864 ";\n"); /* line 6. */
2865 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2866 line_table_test ltt (case_);
2868 const line_map_ordinary *ord_map
2869 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2870 tmp.get_filename (), 0));
2872 linemap_line_start (line_table, 1, 100);
2874 const location_t final_line_end
2875 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2877 /* Don't attempt to run the tests if column data might be unavailable. */
2878 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2879 return;
2881 /* Test of add_location_if_nearby on the same line as the
2882 primary location. */
2884 const location_t missing_close_brace_1_39
2885 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2886 const location_t matching_open_brace_1_18
2887 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2888 gcc_rich_location richloc (missing_close_brace_1_39);
2889 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2890 ASSERT_TRUE (added);
2891 ASSERT_EQ (2, richloc.get_num_locations ());
2892 test_diagnostic_context dc;
2893 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2894 ASSERT_STREQ ("\n"
2895 " struct same_line { double x; double y; ;\n"
2896 " ~ ^\n",
2897 pp_formatted_text (dc.printer));
2900 /* Test of add_location_if_nearby on a different line to the
2901 primary location. */
2903 const location_t missing_close_brace_6_1
2904 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2905 const location_t matching_open_brace_3_1
2906 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2907 gcc_rich_location richloc (missing_close_brace_6_1);
2908 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2909 ASSERT_FALSE (added);
2910 ASSERT_EQ (1, richloc.get_num_locations ());
2914 /* Verify that we print fixits even if they only affect lines
2915 outside those covered by the ranges in the rich_location. */
2917 static void
2918 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2920 /* Create a tempfile and write some text to it.
2921 ...000000000111111111122222222223333333333.
2922 ...123456789012345678901234567890123456789. */
2923 const char *content
2924 = ("struct point { double x; double y; };\n" /* line 1. */
2925 "struct point origin = {x: 0.0,\n" /* line 2. */
2926 " y\n" /* line 3. */
2927 "\n" /* line 4. */
2928 "\n" /* line 5. */
2929 " : 0.0};\n"); /* line 6. */
2930 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2931 line_table_test ltt (case_);
2933 const line_map_ordinary *ord_map
2934 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2935 tmp.get_filename (), 0));
2937 linemap_line_start (line_table, 1, 100);
2939 const location_t final_line_end
2940 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2942 /* Don't attempt to run the tests if column data might be unavailable. */
2943 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2944 return;
2946 /* A pair of tests for modernizing the initializers to C99-style. */
2948 /* The one-liner case (line 2). */
2950 test_diagnostic_context dc;
2951 const location_t x
2952 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2953 const location_t colon
2954 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2955 rich_location richloc (line_table, colon);
2956 richloc.add_fixit_insert_before (x, ".");
2957 richloc.add_fixit_replace (colon, "=");
2958 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2959 ASSERT_STREQ ("\n"
2960 " struct point origin = {x: 0.0,\n"
2961 " ^\n"
2962 " .=\n",
2963 pp_formatted_text (dc.printer));
2966 /* The multiline case. The caret for the rich_location is on line 6;
2967 verify that insertion fixit on line 3 is still printed (and that
2968 span starts are printed due to the gap between the span at line 3
2969 and that at line 6). */
2971 test_diagnostic_context dc;
2972 const location_t y
2973 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2974 const location_t colon
2975 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2976 rich_location richloc (line_table, colon);
2977 richloc.add_fixit_insert_before (y, ".");
2978 richloc.add_fixit_replace (colon, "=");
2979 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2980 ASSERT_STREQ ("\n"
2981 "FILENAME:3:24:\n"
2982 " y\n"
2983 " .\n"
2984 "FILENAME:6:25:\n"
2985 " : 0.0};\n"
2986 " ^\n"
2987 " =\n",
2988 pp_formatted_text (dc.printer));
2991 /* As above, but verify the behavior of multiple line spans
2992 with line-numbering enabled. */
2994 const location_t y
2995 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2996 const location_t colon
2997 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2998 rich_location richloc (line_table, colon);
2999 richloc.add_fixit_insert_before (y, ".");
3000 richloc.add_fixit_replace (colon, "=");
3001 test_diagnostic_context dc;
3002 dc.show_line_numbers_p = true;
3003 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3004 ASSERT_STREQ ("\n"
3005 " 3 | y\n"
3006 " | .\n"
3007 "....\n"
3008 " 6 | : 0.0};\n"
3009 " | ^\n"
3010 " | =\n",
3011 pp_formatted_text (dc.printer));
3016 /* Verify that fix-it hints are appropriately consolidated.
3018 If any fix-it hints in a rich_location involve locations beyond
3019 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3020 the fix-it as a whole, so there should be none.
3022 Otherwise, verify that consecutive "replace" and "remove" fix-its
3023 are merged, and that other fix-its remain separate. */
3025 static void
3026 test_fixit_consolidation (const line_table_case &case_)
3028 line_table_test ltt (case_);
3030 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3032 const location_t c10 = linemap_position_for_column (line_table, 10);
3033 const location_t c15 = linemap_position_for_column (line_table, 15);
3034 const location_t c16 = linemap_position_for_column (line_table, 16);
3035 const location_t c17 = linemap_position_for_column (line_table, 17);
3036 const location_t c20 = linemap_position_for_column (line_table, 20);
3037 const location_t c21 = linemap_position_for_column (line_table, 21);
3038 const location_t caret = c10;
3040 /* Insert + insert. */
3042 rich_location richloc (line_table, caret);
3043 richloc.add_fixit_insert_before (c10, "foo");
3044 richloc.add_fixit_insert_before (c15, "bar");
3046 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3047 /* Bogus column info for 2nd fixit, so no fixits. */
3048 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3049 else
3050 /* They should not have been merged. */
3051 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3054 /* Insert + replace. */
3056 rich_location richloc (line_table, caret);
3057 richloc.add_fixit_insert_before (c10, "foo");
3058 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3059 "bar");
3061 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3062 /* Bogus column info for 2nd fixit, so no fixits. */
3063 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3064 else
3065 /* They should not have been merged. */
3066 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3069 /* Replace + non-consecutive insert. */
3071 rich_location richloc (line_table, caret);
3072 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3073 "bar");
3074 richloc.add_fixit_insert_before (c17, "foo");
3076 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3077 /* Bogus column info for 2nd fixit, so no fixits. */
3078 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3079 else
3080 /* They should not have been merged. */
3081 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3084 /* Replace + non-consecutive replace. */
3086 rich_location richloc (line_table, caret);
3087 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3088 "foo");
3089 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3090 "bar");
3092 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3093 /* Bogus column info for 2nd fixit, so no fixits. */
3094 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3095 else
3096 /* They should not have been merged. */
3097 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3100 /* Replace + consecutive replace. */
3102 rich_location richloc (line_table, caret);
3103 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3104 "foo");
3105 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3106 "bar");
3108 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3109 /* Bogus column info for 2nd fixit, so no fixits. */
3110 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3111 else
3113 /* They should have been merged into a single "replace". */
3114 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3115 const fixit_hint *hint = richloc.get_fixit_hint (0);
3116 ASSERT_STREQ ("foobar", hint->get_string ());
3117 ASSERT_EQ (c10, hint->get_start_loc ());
3118 ASSERT_EQ (c21, hint->get_next_loc ());
3122 /* Replace + consecutive removal. */
3124 rich_location richloc (line_table, caret);
3125 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3126 "foo");
3127 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3129 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3130 /* Bogus column info for 2nd fixit, so no fixits. */
3131 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3132 else
3134 /* They should have been merged into a single replace, with the
3135 range extended to cover that of the removal. */
3136 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3137 const fixit_hint *hint = richloc.get_fixit_hint (0);
3138 ASSERT_STREQ ("foo", hint->get_string ());
3139 ASSERT_EQ (c10, hint->get_start_loc ());
3140 ASSERT_EQ (c21, hint->get_next_loc ());
3144 /* Consecutive removals. */
3146 rich_location richloc (line_table, caret);
3147 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3148 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3150 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3151 /* Bogus column info for 2nd fixit, so no fixits. */
3152 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3153 else
3155 /* They should have been merged into a single "replace-with-empty". */
3156 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3157 const fixit_hint *hint = richloc.get_fixit_hint (0);
3158 ASSERT_STREQ ("", hint->get_string ());
3159 ASSERT_EQ (c10, hint->get_start_loc ());
3160 ASSERT_EQ (c21, hint->get_next_loc ());
3165 /* Verify that the line_corrections machinery correctly prints
3166 overlapping fixit-hints. */
3168 static void
3169 test_overlapped_fixit_printing (const line_table_case &case_)
3171 /* Create a tempfile and write some text to it.
3172 ...000000000111111111122222222223333333333.
3173 ...123456789012345678901234567890123456789. */
3174 const char *content
3175 = (" foo *f = (foo *)ptr->field;\n");
3176 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3177 line_table_test ltt (case_);
3179 const line_map_ordinary *ord_map
3180 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3181 tmp.get_filename (), 0));
3183 linemap_line_start (line_table, 1, 100);
3185 const location_t final_line_end
3186 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3188 /* Don't attempt to run the tests if column data might be unavailable. */
3189 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3190 return;
3192 /* A test for converting a C-style cast to a C++-style cast. */
3193 const location_t open_paren
3194 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3195 const location_t close_paren
3196 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3197 const location_t expr_start
3198 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3199 const location_t expr_finish
3200 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3201 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3203 /* Various examples of fix-it hints that aren't themselves consolidated,
3204 but for which the *printing* may need consolidation. */
3206 /* Example where 3 fix-it hints are printed as one. */
3208 test_diagnostic_context dc;
3209 rich_location richloc (line_table, expr);
3210 richloc.add_fixit_replace (open_paren, "const_cast<");
3211 richloc.add_fixit_replace (close_paren, "> (");
3212 richloc.add_fixit_insert_after (")");
3214 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3215 ASSERT_STREQ ("\n"
3216 " foo *f = (foo *)ptr->field;\n"
3217 " ^~~~~~~~~~\n"
3218 " -----------------\n"
3219 " const_cast<foo *> (ptr->field)\n",
3220 pp_formatted_text (dc.printer));
3222 /* Unit-test the line_corrections machinery. */
3223 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3224 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3225 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3226 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3227 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3228 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3229 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3230 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3231 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3232 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3234 /* Add each hint in turn to a line_corrections instance,
3235 and verify that they are consolidated into one correction instance
3236 as expected. */
3237 line_corrections lc (tmp.get_filename (), 1);
3239 /* The first replace hint by itself. */
3240 lc.add_hint (hint_0);
3241 ASSERT_EQ (1, lc.m_corrections.length ());
3242 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3243 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3244 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3246 /* After the second replacement hint, they are printed together
3247 as a replacement (along with the text between them). */
3248 lc.add_hint (hint_1);
3249 ASSERT_EQ (1, lc.m_corrections.length ());
3250 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3251 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3252 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3254 /* After the final insertion hint, they are all printed together
3255 as a replacement (along with the text between them). */
3256 lc.add_hint (hint_2);
3257 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3258 lc.m_corrections[0]->m_text);
3259 ASSERT_EQ (1, lc.m_corrections.length ());
3260 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3261 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3264 /* Example where two are consolidated during printing. */
3266 test_diagnostic_context dc;
3267 rich_location richloc (line_table, expr);
3268 richloc.add_fixit_replace (open_paren, "CAST (");
3269 richloc.add_fixit_replace (close_paren, ") (");
3270 richloc.add_fixit_insert_after (")");
3272 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3273 ASSERT_STREQ ("\n"
3274 " foo *f = (foo *)ptr->field;\n"
3275 " ^~~~~~~~~~\n"
3276 " -\n"
3277 " CAST (-\n"
3278 " ) ( )\n",
3279 pp_formatted_text (dc.printer));
3282 /* Example where none are consolidated during printing. */
3284 test_diagnostic_context dc;
3285 rich_location richloc (line_table, expr);
3286 richloc.add_fixit_replace (open_paren, "CST (");
3287 richloc.add_fixit_replace (close_paren, ") (");
3288 richloc.add_fixit_insert_after (")");
3290 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3291 ASSERT_STREQ ("\n"
3292 " foo *f = (foo *)ptr->field;\n"
3293 " ^~~~~~~~~~\n"
3294 " -\n"
3295 " CST ( -\n"
3296 " ) ( )\n",
3297 pp_formatted_text (dc.printer));
3300 /* Example of deletion fix-it hints. */
3302 test_diagnostic_context dc;
3303 rich_location richloc (line_table, expr);
3304 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3305 source_range victim = {open_paren, close_paren};
3306 richloc.add_fixit_remove (victim);
3308 /* This case is actually handled by fixit-consolidation,
3309 rather than by line_corrections. */
3310 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3312 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3313 ASSERT_STREQ ("\n"
3314 " foo *f = (foo *)ptr->field;\n"
3315 " ^~~~~~~~~~\n"
3316 " -------\n"
3317 " (bar *)\n",
3318 pp_formatted_text (dc.printer));
3321 /* Example of deletion fix-it hints that would overlap. */
3323 test_diagnostic_context dc;
3324 rich_location richloc (line_table, expr);
3325 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3326 source_range victim = {expr_start, expr_finish};
3327 richloc.add_fixit_remove (victim);
3329 /* These fixits are not consolidated. */
3330 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3332 /* But the corrections are. */
3333 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3334 ASSERT_STREQ ("\n"
3335 " foo *f = (foo *)ptr->field;\n"
3336 " ^~~~~~~~~~\n"
3337 " -----------------\n"
3338 " (longer *)(foo *)\n",
3339 pp_formatted_text (dc.printer));
3342 /* Example of insertion fix-it hints that would overlap. */
3344 test_diagnostic_context dc;
3345 rich_location richloc (line_table, expr);
3346 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3347 richloc.add_fixit_insert_after (close_paren, "TEST");
3349 /* The first insertion is long enough that if printed naively,
3350 it would overlap with the second.
3351 Verify that they are printed as a single replacement. */
3352 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3353 ASSERT_STREQ ("\n"
3354 " foo *f = (foo *)ptr->field;\n"
3355 " ^~~~~~~~~~\n"
3356 " -------\n"
3357 " LONGER THAN THE CAST(foo *)TEST\n",
3358 pp_formatted_text (dc.printer));
3362 /* Verify that the line_corrections machinery correctly prints
3363 overlapping fixit-hints that have been added in the wrong
3364 order.
3365 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3367 static void
3368 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3370 /* Create a tempfile and write some text to it.
3371 ...000000000111111111122222222223333333333.
3372 ...123456789012345678901234567890123456789. */
3373 const char *content
3374 = ("int a5[][0][0] = { 1, 2 };\n");
3375 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3376 line_table_test ltt (case_);
3378 const line_map_ordinary *ord_map
3379 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3380 tmp.get_filename (), 0));
3382 linemap_line_start (line_table, 1, 100);
3384 const location_t final_line_end
3385 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3387 /* Don't attempt to run the tests if column data might be unavailable. */
3388 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3389 return;
3391 const location_t col_1
3392 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3393 const location_t col_20
3394 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3395 const location_t col_21
3396 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3397 const location_t col_23
3398 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3399 const location_t col_25
3400 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3402 /* Two insertions, in the wrong order. */
3404 rich_location richloc (line_table, col_20);
3405 richloc.add_fixit_insert_before (col_23, "{");
3406 richloc.add_fixit_insert_before (col_21, "}");
3408 /* These fixits should be accepted; they can't be consolidated. */
3409 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3410 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3411 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3412 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3413 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3414 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3415 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3417 /* Verify that they're printed correctly. */
3418 test_diagnostic_context dc;
3419 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3420 ASSERT_STREQ ("\n"
3421 " int a5[][0][0] = { 1, 2 };\n"
3422 " ^\n"
3423 " } {\n",
3424 pp_formatted_text (dc.printer));
3427 /* Various overlapping insertions, some occurring "out of order"
3428 (reproducing the fix-it hints from PR c/81405). */
3430 test_diagnostic_context dc;
3431 rich_location richloc (line_table, col_20);
3433 richloc.add_fixit_insert_before (col_20, "{{");
3434 richloc.add_fixit_insert_before (col_21, "}}");
3435 richloc.add_fixit_insert_before (col_23, "{");
3436 richloc.add_fixit_insert_before (col_21, "}");
3437 richloc.add_fixit_insert_before (col_23, "{{");
3438 richloc.add_fixit_insert_before (col_25, "}");
3439 richloc.add_fixit_insert_before (col_21, "}");
3440 richloc.add_fixit_insert_before (col_1, "{");
3441 richloc.add_fixit_insert_before (col_25, "}");
3442 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3443 ASSERT_STREQ ("\n"
3444 " int a5[][0][0] = { 1, 2 };\n"
3445 " ^\n"
3446 " { -----\n"
3447 " {{1}}}}, {{{2 }}\n",
3448 pp_formatted_text (dc.printer));
3452 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3454 static void
3455 test_fixit_insert_containing_newline (const line_table_case &case_)
3457 /* Create a tempfile and write some text to it.
3458 .........................0000000001111111.
3459 .........................1234567890123456. */
3460 const char *old_content = (" case 'a':\n" /* line 1. */
3461 " x = a;\n" /* line 2. */
3462 " case 'b':\n" /* line 3. */
3463 " x = b;\n");/* line 4. */
3465 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3466 line_table_test ltt (case_);
3467 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3469 location_t case_start = linemap_position_for_column (line_table, 5);
3470 location_t case_finish = linemap_position_for_column (line_table, 13);
3471 location_t case_loc = make_location (case_start, case_start, case_finish);
3472 location_t line_start = linemap_position_for_column (line_table, 1);
3474 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3475 return;
3477 /* Add a "break;" on a line by itself before line 3 i.e. before
3478 column 1 of line 3. */
3480 rich_location richloc (line_table, case_loc);
3481 richloc.add_fixit_insert_before (line_start, " break;\n");
3482 test_diagnostic_context dc;
3483 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3484 ASSERT_STREQ ("\n"
3485 "+ break;\n"
3486 " case 'b':\n"
3487 " ^~~~~~~~~\n",
3488 pp_formatted_text (dc.printer));
3491 /* Verify that attempts to add text with a newline fail when the
3492 insertion point is *not* at the start of a line. */
3494 rich_location richloc (line_table, case_loc);
3495 richloc.add_fixit_insert_before (case_start, "break;\n");
3496 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3497 test_diagnostic_context dc;
3498 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3499 ASSERT_STREQ ("\n"
3500 " case 'b':\n"
3501 " ^~~~~~~~~\n",
3502 pp_formatted_text (dc.printer));
3506 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3507 of the file, where the fix-it is printed in a different line-span
3508 to the primary range of the diagnostic. */
3510 static void
3511 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3513 /* Create a tempfile and write some text to it.
3514 .........................0000000001111111.
3515 .........................1234567890123456. */
3516 const char *old_content = ("test (int ch)\n" /* line 1. */
3517 "{\n" /* line 2. */
3518 " putchar (ch);\n" /* line 3. */
3519 "}\n"); /* line 4. */
3521 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3522 line_table_test ltt (case_);
3524 const line_map_ordinary *ord_map = linemap_check_ordinary
3525 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3526 linemap_line_start (line_table, 1, 100);
3528 /* The primary range is the "putchar" token. */
3529 location_t putchar_start
3530 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3531 location_t putchar_finish
3532 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3533 location_t putchar_loc
3534 = make_location (putchar_start, putchar_start, putchar_finish);
3535 rich_location richloc (line_table, putchar_loc);
3537 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3538 location_t file_start
3539 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3540 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3542 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3543 return;
3546 test_diagnostic_context dc;
3547 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3548 ASSERT_STREQ ("\n"
3549 "FILENAME:1:1:\n"
3550 "+#include <stdio.h>\n"
3551 " test (int ch)\n"
3552 "FILENAME:3:2:\n"
3553 " putchar (ch);\n"
3554 " ^~~~~~~\n",
3555 pp_formatted_text (dc.printer));
3558 /* With line-numbering, the line spans are close enough to be
3559 consolidated, since it makes little sense to skip line 2. */
3561 test_diagnostic_context dc;
3562 dc.show_line_numbers_p = true;
3563 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3564 ASSERT_STREQ ("\n"
3565 "+ |+#include <stdio.h>\n"
3566 "1 | test (int ch)\n"
3567 "2 | {\n"
3568 "3 | putchar (ch);\n"
3569 " | ^~~~~~~\n",
3570 pp_formatted_text (dc.printer));
3574 /* Replacement fix-it hint containing a newline.
3575 This will fail, as newlines are only supported when inserting at the
3576 beginning of a line. */
3578 static void
3579 test_fixit_replace_containing_newline (const line_table_case &case_)
3581 /* Create a tempfile and write some text to it.
3582 .........................0000000001111.
3583 .........................1234567890123. */
3584 const char *old_content = "foo = bar ();\n";
3586 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3587 line_table_test ltt (case_);
3588 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3590 /* Replace the " = " with "\n = ", as if we were reformatting an
3591 overly long line. */
3592 location_t start = linemap_position_for_column (line_table, 4);
3593 location_t finish = linemap_position_for_column (line_table, 6);
3594 location_t loc = linemap_position_for_column (line_table, 13);
3595 rich_location richloc (line_table, loc);
3596 source_range range = source_range::from_locations (start, finish);
3597 richloc.add_fixit_replace (range, "\n =");
3599 /* Arbitrary newlines are not yet supported within fix-it hints, so
3600 the fix-it should not be displayed. */
3601 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3603 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3604 return;
3606 test_diagnostic_context dc;
3607 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3608 ASSERT_STREQ ("\n"
3609 " foo = bar ();\n"
3610 " ^\n",
3611 pp_formatted_text (dc.printer));
3614 /* Fix-it hint, attempting to delete a newline.
3615 This will fail, as we currently only support fix-it hints that
3616 affect one line at a time. */
3618 static void
3619 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3621 /* Create a tempfile and write some text to it.
3622 ..........................0000000001111.
3623 ..........................1234567890123. */
3624 const char *old_content = ("foo = bar (\n"
3625 " );\n");
3627 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3628 line_table_test ltt (case_);
3629 const line_map_ordinary *ord_map = linemap_check_ordinary
3630 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3631 linemap_line_start (line_table, 1, 100);
3633 /* Attempt to delete the " (\n...)". */
3634 location_t start
3635 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3636 location_t caret
3637 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3638 location_t finish
3639 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3640 location_t loc = make_location (caret, start, finish);
3641 rich_location richloc (line_table, loc);
3642 richloc. add_fixit_remove ();
3644 /* Fix-it hints that affect more than one line are not yet supported, so
3645 the fix-it should not be displayed. */
3646 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3648 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3649 return;
3651 test_diagnostic_context dc;
3652 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3653 ASSERT_STREQ ("\n"
3654 " foo = bar (\n"
3655 " ~^\n"
3656 " );\n"
3657 " ~ \n",
3658 pp_formatted_text (dc.printer));
3661 /* Verify that line numbers are correctly printed for the case of
3662 a multiline range in which the width of the line numbers changes
3663 (e.g. from "9" to "10"). */
3665 static void
3666 test_line_numbers_multiline_range ()
3668 /* Create a tempfile and write some text to it. */
3669 pretty_printer pp;
3670 for (int i = 0; i < 20; i++)
3671 /* .........0000000001111111.
3672 .............1234567890123456. */
3673 pp_printf (&pp, "this is line %i\n", i + 1);
3674 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3675 line_table_test ltt;
3677 const line_map_ordinary *ord_map = linemap_check_ordinary
3678 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3679 linemap_line_start (line_table, 1, 100);
3681 /* Create a multi-line location, starting at the "line" of line 9, with
3682 a caret on the "is" of line 10, finishing on the "this" line 11. */
3684 location_t start
3685 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3686 location_t caret
3687 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3688 location_t finish
3689 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3690 location_t loc = make_location (caret, start, finish);
3692 test_diagnostic_context dc;
3693 dc.show_line_numbers_p = true;
3694 gcc_rich_location richloc (loc);
3695 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3696 ASSERT_STREQ ("\n"
3697 " 9 | this is line 9\n"
3698 " | ~~~~~~\n"
3699 "10 | this is line 10\n"
3700 " | ~~~~~^~~~~~~~~~\n"
3701 "11 | this is line 11\n"
3702 " | ~~~~ \n",
3703 pp_formatted_text (dc.printer));
3706 /* Run all of the selftests within this file. */
3708 void
3709 diagnostic_show_locus_c_tests ()
3711 test_line_span ();
3712 test_num_digits ();
3714 test_layout_range_for_single_point ();
3715 test_layout_range_for_single_line ();
3716 test_layout_range_for_multiple_lines ();
3718 test_get_line_width_without_trailing_whitespace ();
3720 test_diagnostic_show_locus_unknown_location ();
3722 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3723 for_each_line_table_case (test_add_location_if_nearby);
3724 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3725 for_each_line_table_case (test_fixit_consolidation);
3726 for_each_line_table_case (test_overlapped_fixit_printing);
3727 for_each_line_table_case (test_overlapped_fixit_printing_2);
3728 for_each_line_table_case (test_fixit_insert_containing_newline);
3729 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3730 for_each_line_table_case (test_fixit_replace_containing_newline);
3731 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3733 test_line_numbers_multiline_range ();
3736 } // namespace selftest
3738 #endif /* #if CHECKING_P */