Add <bit> and <version> to freestanding headers
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob238c689be1ac10efb8994cf636aff02a6a923f03
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);
132 bool contains_point (linenum_type row, int column) const;
133 bool intersects_line_p (linenum_type row) const;
135 layout_point m_start;
136 layout_point m_finish;
137 bool m_show_caret_p;
138 layout_point m_caret;
141 /* A struct for use by layout::print_source_line for telling
142 layout::print_annotation_line the extents of the source line that
143 it printed, so that underlines can be clipped appropriately. */
145 struct line_bounds
147 int m_first_non_ws;
148 int m_last_non_ws;
151 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
152 or "line 23"). During the layout ctor, layout::calculate_line_spans
153 splits the pertinent source lines into a list of disjoint line_span
154 instances (e.g. lines 5-10, lines 15-20, line 23). */
156 struct line_span
158 line_span (linenum_type first_line, linenum_type last_line)
159 : m_first_line (first_line), m_last_line (last_line)
161 gcc_assert (first_line <= last_line);
163 linenum_type get_first_line () const { return m_first_line; }
164 linenum_type get_last_line () const { return m_last_line; }
166 bool contains_line_p (linenum_type line) const
168 return line >= m_first_line && line <= m_last_line;
171 static int comparator (const void *p1, const void *p2)
173 const line_span *ls1 = (const line_span *)p1;
174 const line_span *ls2 = (const line_span *)p2;
175 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
176 if (first_line_cmp)
177 return first_line_cmp;
178 return compare (ls1->m_last_line, ls2->m_last_line);
181 linenum_type m_first_line;
182 linenum_type m_last_line;
185 #if CHECKING_P
187 /* Selftests for line_span. */
189 static void
190 test_line_span ()
192 line_span line_one (1, 1);
193 ASSERT_EQ (1, line_one.get_first_line ());
194 ASSERT_EQ (1, line_one.get_last_line ());
195 ASSERT_FALSE (line_one.contains_line_p (0));
196 ASSERT_TRUE (line_one.contains_line_p (1));
197 ASSERT_FALSE (line_one.contains_line_p (2));
199 line_span lines_1_to_3 (1, 3);
200 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
201 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
202 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
203 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
205 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
206 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
207 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
209 /* A linenum > 2^31. */
210 const linenum_type LARGEST_LINE = 0xffffffff;
211 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
212 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
213 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
215 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
216 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
219 #endif /* #if CHECKING_P */
221 /* A class to control the overall layout when printing a diagnostic.
223 The layout is determined within the constructor.
224 It is then printed by repeatedly calling the "print_source_line",
225 "print_annotation_line" and "print_any_fixits" methods.
227 We assume we have disjoint ranges. */
229 class layout
231 public:
232 layout (diagnostic_context *context,
233 rich_location *richloc,
234 diagnostic_t diagnostic_kind);
236 bool maybe_add_location_range (const location_range *loc_range,
237 bool restrict_to_current_line_spans);
239 int get_num_line_spans () const { return m_line_spans.length (); }
240 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
242 bool print_heading_for_line_span_index_p (int line_span_idx) const;
244 expanded_location get_expanded_location (const line_span *) const;
246 void print_line (linenum_type row);
248 private:
249 bool will_show_line_p (linenum_type row) const;
250 void print_leading_fixits (linenum_type row);
251 void print_source_line (linenum_type row, const char *line, int line_width,
252 line_bounds *lbounds_out);
253 bool should_print_annotation_line_p (linenum_type row) const;
254 void start_annotation_line () const;
255 void print_annotation_line (linenum_type row, const line_bounds lbounds);
256 void print_trailing_fixits (linenum_type row);
258 bool annotation_line_showed_range_p (linenum_type line, int start_column,
259 int finish_column) const;
260 void show_ruler (int max_column) const;
262 bool validate_fixit_hint_p (const fixit_hint *hint);
264 void calculate_line_spans ();
266 void print_newline ();
268 bool
269 get_state_at_point (/* Inputs. */
270 linenum_type row, int column,
271 int first_non_ws, int last_non_ws,
272 /* Outputs. */
273 point_state *out_state);
276 get_x_bound_for_row (linenum_type row, int caret_column,
277 int last_non_ws);
279 void
280 move_to_column (int *column, int dest_column, bool add_left_margin);
282 private:
283 diagnostic_context *m_context;
284 pretty_printer *m_pp;
285 diagnostic_t m_diagnostic_kind;
286 location_t m_primary_loc;
287 expanded_location m_exploc;
288 colorizer m_colorizer;
289 bool m_colorize_source_p;
290 bool m_show_line_numbers_p;
291 auto_vec <layout_range> m_layout_ranges;
292 auto_vec <const fixit_hint *> m_fixit_hints;
293 auto_vec <line_span> m_line_spans;
294 int m_linenum_width;
295 int m_x_offset;
298 /* Implementation of "class colorizer". */
300 /* The constructor for "colorizer". Lookup and store color codes for the
301 different kinds of things we might need to print. */
303 colorizer::colorizer (diagnostic_context *context,
304 diagnostic_t diagnostic_kind) :
305 m_context (context),
306 m_diagnostic_kind (diagnostic_kind),
307 m_current_state (STATE_NORMAL_TEXT)
309 m_range1 = get_color_by_name ("range1");
310 m_range2 = get_color_by_name ("range2");
311 m_fixit_insert = get_color_by_name ("fixit-insert");
312 m_fixit_delete = get_color_by_name ("fixit-delete");
313 m_stop_color = colorize_stop (pp_show_color (context->printer));
316 /* The destructor for "colorize". If colorization is on, print a code to
317 turn it off. */
319 colorizer::~colorizer ()
321 finish_state (m_current_state);
324 /* Update state, printing color codes if necessary if there's a state
325 change. */
327 void
328 colorizer::set_state (int new_state)
330 if (m_current_state != new_state)
332 finish_state (m_current_state);
333 m_current_state = new_state;
334 begin_state (new_state);
338 /* Turn on any colorization for STATE. */
340 void
341 colorizer::begin_state (int state)
343 switch (state)
345 case STATE_NORMAL_TEXT:
346 break;
348 case STATE_FIXIT_INSERT:
349 pp_string (m_context->printer, m_fixit_insert);
350 break;
352 case STATE_FIXIT_DELETE:
353 pp_string (m_context->printer, m_fixit_delete);
354 break;
356 case 0:
357 /* Make range 0 be the same color as the "kind" text
358 (error vs warning vs note). */
359 pp_string
360 (m_context->printer,
361 colorize_start (pp_show_color (m_context->printer),
362 diagnostic_get_color_for_kind (m_diagnostic_kind)));
363 break;
365 case 1:
366 pp_string (m_context->printer, m_range1);
367 break;
369 case 2:
370 pp_string (m_context->printer, m_range2);
371 break;
373 default:
374 /* For ranges beyond 2, alternate between color 1 and color 2. */
376 gcc_assert (state > 2);
377 pp_string (m_context->printer,
378 state % 2 ? m_range1 : m_range2);
380 break;
384 /* Turn off any colorization for STATE. */
386 void
387 colorizer::finish_state (int state)
389 if (state != STATE_NORMAL_TEXT)
390 pp_string (m_context->printer, m_stop_color);
393 /* Get the color code for NAME (or the empty string if
394 colorization is disabled). */
396 const char *
397 colorizer::get_color_by_name (const char *name)
399 return colorize_start (pp_show_color (m_context->printer), name);
402 /* Implementation of class layout_range. */
404 /* The constructor for class layout_range.
405 Initialize various layout_point fields from expanded_location
406 equivalents; we've already filtered on file. */
408 layout_range::layout_range (const expanded_location *start_exploc,
409 const expanded_location *finish_exploc,
410 bool show_caret_p,
411 const expanded_location *caret_exploc)
412 : m_start (*start_exploc),
413 m_finish (*finish_exploc),
414 m_show_caret_p (show_caret_p),
415 m_caret (*caret_exploc)
419 /* Is (column, row) within the given range?
420 We've already filtered on the file.
422 Ranges are closed (both limits are within the range).
424 Example A: a single-line range:
425 start: (col=22, line=2)
426 finish: (col=38, line=2)
428 |00000011111111112222222222333333333344444444444
429 |34567890123456789012345678901234567890123456789
430 --+-----------------------------------------------
431 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
432 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
433 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
435 Example B: a multiline range with
436 start: (col=14, line=3)
437 finish: (col=08, line=5)
439 |00000011111111112222222222333333333344444444444
440 |34567890123456789012345678901234567890123456789
441 --+-----------------------------------------------
442 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
443 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
444 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
445 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
446 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
447 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
448 --+-----------------------------------------------
450 Legend:
451 - 'b' indicates a point *before* the range
452 - 'S' indicates the start of the range
453 - 'w' indicates a point within the range
454 - 'F' indicates the finish of the range (which is
455 within it).
456 - 'a' indicates a subsequent point *after* the range. */
458 bool
459 layout_range::contains_point (linenum_type row, int column) const
461 gcc_assert (m_start.m_line <= m_finish.m_line);
462 /* ...but the equivalent isn't true for the columns;
463 consider example B in the comment above. */
465 if (row < m_start.m_line)
466 /* Points before the first line of the range are
467 outside it (corresponding to line 01 in example A
468 and lines 01 and 02 in example B above). */
469 return false;
471 if (row == m_start.m_line)
472 /* On same line as start of range (corresponding
473 to line 02 in example A and line 03 in example B). */
475 if (column < m_start.m_column)
476 /* Points on the starting line of the range, but
477 before the column in which it begins. */
478 return false;
480 if (row < m_finish.m_line)
481 /* This is a multiline range; the point
482 is within it (corresponds to line 03 in example B
483 from column 14 onwards) */
484 return true;
485 else
487 /* This is a single-line range. */
488 gcc_assert (row == m_finish.m_line);
489 return column <= m_finish.m_column;
493 /* The point is in a line beyond that containing the
494 start of the range: lines 03 onwards in example A,
495 and lines 04 onwards in example B. */
496 gcc_assert (row > m_start.m_line);
498 if (row > m_finish.m_line)
499 /* The point is beyond the final line of the range
500 (lines 03 onwards in example A, and lines 06 onwards
501 in example B). */
502 return false;
504 if (row < m_finish.m_line)
506 /* The point is in a line that's fully within a multiline
507 range (e.g. line 04 in example B). */
508 gcc_assert (m_start.m_line < m_finish.m_line);
509 return true;
512 gcc_assert (row == m_finish.m_line);
514 return column <= m_finish.m_column;
517 /* Does this layout_range contain any part of line ROW? */
519 bool
520 layout_range::intersects_line_p (linenum_type row) const
522 gcc_assert (m_start.m_line <= m_finish.m_line);
523 if (row < m_start.m_line)
524 return false;
525 if (row > m_finish.m_line)
526 return false;
527 return true;
530 #if CHECKING_P
532 /* A helper function for testing layout_range. */
534 static layout_range
535 make_range (int start_line, int start_col, int end_line, int end_col)
537 const expanded_location start_exploc
538 = {"test.c", start_line, start_col, NULL, false};
539 const expanded_location finish_exploc
540 = {"test.c", end_line, end_col, NULL, false};
541 return layout_range (&start_exploc, &finish_exploc, false,
542 &start_exploc);
545 /* Selftests for layout_range::contains_point and
546 layout_range::intersects_line_p. */
548 /* Selftest for layout_range, where the layout_range
549 is a range with start==end i.e. a single point. */
551 static void
552 test_layout_range_for_single_point ()
554 layout_range point = make_range (7, 10, 7, 10);
556 /* Tests for layout_range::contains_point. */
558 /* Before the line. */
559 ASSERT_FALSE (point.contains_point (6, 1));
561 /* On the line, but before start. */
562 ASSERT_FALSE (point.contains_point (7, 9));
564 /* At the point. */
565 ASSERT_TRUE (point.contains_point (7, 10));
567 /* On the line, after the point. */
568 ASSERT_FALSE (point.contains_point (7, 11));
570 /* After the line. */
571 ASSERT_FALSE (point.contains_point (8, 1));
573 /* Tests for layout_range::intersects_line_p. */
574 ASSERT_FALSE (point.intersects_line_p (6));
575 ASSERT_TRUE (point.intersects_line_p (7));
576 ASSERT_FALSE (point.intersects_line_p (8));
579 /* Selftest for layout_range, where the layout_range
580 is the single-line range shown as "Example A" above. */
582 static void
583 test_layout_range_for_single_line ()
585 layout_range example_a = make_range (2, 22, 2, 38);
587 /* Tests for layout_range::contains_point. */
589 /* Before the line. */
590 ASSERT_FALSE (example_a.contains_point (1, 1));
592 /* On the line, but before start. */
593 ASSERT_FALSE (example_a.contains_point (2, 21));
595 /* On the line, at the start. */
596 ASSERT_TRUE (example_a.contains_point (2, 22));
598 /* On the line, within the range. */
599 ASSERT_TRUE (example_a.contains_point (2, 23));
601 /* On the line, at the end. */
602 ASSERT_TRUE (example_a.contains_point (2, 38));
604 /* On the line, after the end. */
605 ASSERT_FALSE (example_a.contains_point (2, 39));
607 /* After the line. */
608 ASSERT_FALSE (example_a.contains_point (2, 39));
610 /* Tests for layout_range::intersects_line_p. */
611 ASSERT_FALSE (example_a.intersects_line_p (1));
612 ASSERT_TRUE (example_a.intersects_line_p (2));
613 ASSERT_FALSE (example_a.intersects_line_p (3));
616 /* Selftest for layout_range, where the layout_range
617 is the multi-line range shown as "Example B" above. */
619 static void
620 test_layout_range_for_multiple_lines ()
622 layout_range example_b = make_range (3, 14, 5, 8);
624 /* Tests for layout_range::contains_point. */
626 /* Before first line. */
627 ASSERT_FALSE (example_b.contains_point (1, 1));
629 /* On the first line, but before start. */
630 ASSERT_FALSE (example_b.contains_point (3, 13));
632 /* At the start. */
633 ASSERT_TRUE (example_b.contains_point (3, 14));
635 /* On the first line, within the range. */
636 ASSERT_TRUE (example_b.contains_point (3, 15));
638 /* On an interior line.
639 The column number should not matter; try various boundary
640 values. */
641 ASSERT_TRUE (example_b.contains_point (4, 1));
642 ASSERT_TRUE (example_b.contains_point (4, 7));
643 ASSERT_TRUE (example_b.contains_point (4, 8));
644 ASSERT_TRUE (example_b.contains_point (4, 9));
645 ASSERT_TRUE (example_b.contains_point (4, 13));
646 ASSERT_TRUE (example_b.contains_point (4, 14));
647 ASSERT_TRUE (example_b.contains_point (4, 15));
649 /* On the final line, before the end. */
650 ASSERT_TRUE (example_b.contains_point (5, 7));
652 /* On the final line, at the end. */
653 ASSERT_TRUE (example_b.contains_point (5, 8));
655 /* On the final line, after the end. */
656 ASSERT_FALSE (example_b.contains_point (5, 9));
658 /* After the line. */
659 ASSERT_FALSE (example_b.contains_point (6, 1));
661 /* Tests for layout_range::intersects_line_p. */
662 ASSERT_FALSE (example_b.intersects_line_p (2));
663 ASSERT_TRUE (example_b.intersects_line_p (3));
664 ASSERT_TRUE (example_b.intersects_line_p (4));
665 ASSERT_TRUE (example_b.intersects_line_p (5));
666 ASSERT_FALSE (example_b.intersects_line_p (6));
669 #endif /* #if CHECKING_P */
671 /* Given a source line LINE of length LINE_WIDTH, determine the width
672 without any trailing whitespace. */
674 static int
675 get_line_width_without_trailing_whitespace (const char *line, int line_width)
677 int result = line_width;
678 while (result > 0)
680 char ch = line[result - 1];
681 if (ch == ' ' || ch == '\t' || ch == '\r')
682 result--;
683 else
684 break;
686 gcc_assert (result >= 0);
687 gcc_assert (result <= line_width);
688 gcc_assert (result == 0 ||
689 (line[result - 1] != ' '
690 && line[result -1] != '\t'
691 && line[result -1] != '\r'));
692 return result;
695 #if CHECKING_P
697 /* A helper function for testing get_line_width_without_trailing_whitespace. */
699 static void
700 assert_eq (const char *line, int expected_width)
702 int actual_value
703 = get_line_width_without_trailing_whitespace (line, strlen (line));
704 ASSERT_EQ (actual_value, expected_width);
707 /* Verify that get_line_width_without_trailing_whitespace is sane for
708 various inputs. It is not required to handle newlines. */
710 static void
711 test_get_line_width_without_trailing_whitespace ()
713 assert_eq ("", 0);
714 assert_eq (" ", 0);
715 assert_eq ("\t", 0);
716 assert_eq ("\r", 0);
717 assert_eq ("hello world", 11);
718 assert_eq ("hello world ", 11);
719 assert_eq ("hello world \t\t ", 11);
720 assert_eq ("hello world\r", 11);
723 #endif /* #if CHECKING_P */
725 /* Helper function for layout's ctor, for sanitizing locations relative
726 to the primary location within a diagnostic.
728 Compare LOC_A and LOC_B to see if it makes sense to print underlines
729 connecting their expanded locations. Doing so is only guaranteed to
730 make sense if the locations share the same macro expansion "history"
731 i.e. they can be traced through the same macro expansions, eventually
732 reaching an ordinary map.
734 This may be too strong a condition, but it effectively sanitizes
735 PR c++/70105, which has an example of printing an expression where the
736 final location of the expression is in a different macro, which
737 erroneously was leading to hundreds of lines of irrelevant source
738 being printed. */
740 static bool
741 compatible_locations_p (location_t loc_a, location_t loc_b)
743 if (IS_ADHOC_LOC (loc_a))
744 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
745 if (IS_ADHOC_LOC (loc_b))
746 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
748 /* If either location is one of the special locations outside of a
749 linemap, they are only compatible if they are equal. */
750 if (loc_a < RESERVED_LOCATION_COUNT
751 || loc_b < RESERVED_LOCATION_COUNT)
752 return loc_a == loc_b;
754 const line_map *map_a = linemap_lookup (line_table, loc_a);
755 linemap_assert (map_a);
757 const line_map *map_b = linemap_lookup (line_table, loc_b);
758 linemap_assert (map_b);
760 /* Are they within the same map? */
761 if (map_a == map_b)
763 /* Are both within the same macro expansion? */
764 if (linemap_macro_expansion_map_p (map_a))
766 /* Expand each location towards the spelling location, and
767 recurse. */
768 const line_map_macro *macro_map = linemap_check_macro (map_a);
769 source_location loc_a_toward_spelling
770 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
771 macro_map,
772 loc_a);
773 source_location loc_b_toward_spelling
774 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
775 macro_map,
776 loc_b);
777 return compatible_locations_p (loc_a_toward_spelling,
778 loc_b_toward_spelling);
781 /* Otherwise they are within the same ordinary map. */
782 return true;
784 else
786 /* Within different maps. */
788 /* If either is within a macro expansion, they are incompatible. */
789 if (linemap_macro_expansion_map_p (map_a)
790 || linemap_macro_expansion_map_p (map_b))
791 return false;
793 /* Within two different ordinary maps; they are compatible iff they
794 are in the same file. */
795 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
796 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
797 return ord_map_a->to_file == ord_map_b->to_file;
801 /* Comparator for sorting fix-it hints. */
803 static int
804 fixit_cmp (const void *p_a, const void *p_b)
806 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
807 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
808 return hint_a->get_start_loc () - hint_b->get_start_loc ();
811 /* Get the number of digits in the decimal representation
812 of VALUE. */
814 static int
815 num_digits (int value)
817 /* Perhaps simpler to use log10 for this, but doing it this way avoids
818 using floating point. */
819 gcc_assert (value >= 0);
821 if (value == 0)
822 return 1;
824 int digits = 0;
825 while (value > 0)
827 digits++;
828 value /= 10;
830 return digits;
834 #if CHECKING_P
836 /* Selftest for num_digits. */
838 static void
839 test_num_digits ()
841 ASSERT_EQ (1, num_digits (0));
842 ASSERT_EQ (1, num_digits (9));
843 ASSERT_EQ (2, num_digits (10));
844 ASSERT_EQ (2, num_digits (99));
845 ASSERT_EQ (3, num_digits (100));
846 ASSERT_EQ (3, num_digits (999));
847 ASSERT_EQ (4, num_digits (1000));
848 ASSERT_EQ (4, num_digits (9999));
849 ASSERT_EQ (5, num_digits (10000));
850 ASSERT_EQ (5, num_digits (99999));
851 ASSERT_EQ (6, num_digits (100000));
852 ASSERT_EQ (6, num_digits (999999));
853 ASSERT_EQ (7, num_digits (1000000));
854 ASSERT_EQ (7, num_digits (9999999));
855 ASSERT_EQ (8, num_digits (10000000));
856 ASSERT_EQ (8, num_digits (99999999));
859 #endif /* #if CHECKING_P */
861 /* Implementation of class layout. */
863 /* Constructor for class layout.
865 Filter the ranges from the rich_location to those that we can
866 sanely print, populating m_layout_ranges and m_fixit_hints.
867 Determine the range of lines that we will print, splitting them
868 up into an ordered list of disjoint spans of contiguous line numbers.
869 Determine m_x_offset, to ensure that the primary caret
870 will fit within the max_width provided by the diagnostic_context. */
872 layout::layout (diagnostic_context * context,
873 rich_location *richloc,
874 diagnostic_t diagnostic_kind)
875 : m_context (context),
876 m_pp (context->printer),
877 m_diagnostic_kind (diagnostic_kind),
878 m_primary_loc (richloc->get_range (0)->m_loc),
879 m_exploc (richloc->get_expanded_location (0)),
880 m_colorizer (context, diagnostic_kind),
881 m_colorize_source_p (context->colorize_source_p),
882 m_show_line_numbers_p (context->show_line_numbers_p),
883 m_layout_ranges (richloc->get_num_locations ()),
884 m_fixit_hints (richloc->get_num_fixit_hints ()),
885 m_line_spans (1 + richloc->get_num_locations ()),
886 m_linenum_width (0),
887 m_x_offset (0)
889 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
891 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
892 Ignore any ranges that are awkward to handle. */
893 const location_range *loc_range = richloc->get_range (idx);
894 maybe_add_location_range (loc_range, false);
897 /* Populate m_fixit_hints, filtering to only those that are in the
898 same file. */
899 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
901 const fixit_hint *hint = richloc->get_fixit_hint (i);
902 if (validate_fixit_hint_p (hint))
903 m_fixit_hints.safe_push (hint);
906 /* Sort m_fixit_hints. */
907 m_fixit_hints.qsort (fixit_cmp);
909 /* Populate m_line_spans. */
910 calculate_line_spans ();
912 /* Determine m_linenum_width. */
913 gcc_assert (m_line_spans.length () > 0);
914 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
915 int highest_line = last_span->m_last_line;
916 if (highest_line < 0)
917 highest_line = 0;
918 m_linenum_width = num_digits (highest_line);
920 /* Adjust m_x_offset.
921 Center the primary caret to fit in max_width; all columns
922 will be adjusted accordingly. */
923 size_t max_width = m_context->caret_max_width;
924 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
925 if (line && (size_t)m_exploc.column <= line.length ())
927 size_t right_margin = CARET_LINE_MARGIN;
928 size_t column = m_exploc.column;
929 if (m_show_line_numbers_p)
930 column += m_linenum_width + 2;
931 right_margin = MIN (line.length () - column, right_margin);
932 right_margin = max_width - right_margin;
933 if (line.length () >= max_width && column > right_margin)
934 m_x_offset = column - right_margin;
935 gcc_assert (m_x_offset >= 0);
938 if (context->show_ruler_p)
939 show_ruler (m_x_offset + max_width);
942 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
943 those that we can sanely print.
945 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
946 filtered against this layout instance's current line spans: it
947 will only be added if the location is fully within the lines
948 already specified by other locations.
950 Return true iff LOC_RANGE was added. */
952 bool
953 layout::maybe_add_location_range (const location_range *loc_range,
954 bool restrict_to_current_line_spans)
956 gcc_assert (loc_range);
958 /* Split the "range" into caret and range information. */
959 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
961 /* Expand the various locations. */
962 expanded_location start
963 = linemap_client_expand_location_to_spelling_point
964 (src_range.m_start, LOCATION_ASPECT_START);
965 expanded_location finish
966 = linemap_client_expand_location_to_spelling_point
967 (src_range.m_finish, LOCATION_ASPECT_FINISH);
968 expanded_location caret
969 = linemap_client_expand_location_to_spelling_point
970 (loc_range->m_loc, LOCATION_ASPECT_CARET);
972 /* If any part of the range isn't in the same file as the primary
973 location of this diagnostic, ignore the range. */
974 if (start.file != m_exploc.file)
975 return false;
976 if (finish.file != m_exploc.file)
977 return false;
978 if (loc_range->m_show_caret_p)
979 if (caret.file != m_exploc.file)
980 return false;
982 /* Sanitize the caret location for non-primary ranges. */
983 if (m_layout_ranges.length () > 0)
984 if (loc_range->m_show_caret_p)
985 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
986 /* Discard any non-primary ranges that can't be printed
987 sanely relative to the primary location. */
988 return false;
990 /* Everything is now known to be in the correct source file,
991 but it may require further sanitization. */
992 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
994 /* If we have a range that finishes before it starts (perhaps
995 from something built via macro expansion), printing the
996 range is likely to be nonsensical. Also, attempting to do so
997 breaks assumptions within the printing code (PR c/68473).
998 Similarly, don't attempt to print ranges if one or both ends
999 of the range aren't sane to print relative to the
1000 primary location (PR c++/70105). */
1001 if (start.line > finish.line
1002 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1003 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1005 /* Is this the primary location? */
1006 if (m_layout_ranges.length () == 0)
1008 /* We want to print the caret for the primary location, but
1009 we must sanitize away m_start and m_finish. */
1010 ri.m_start = ri.m_caret;
1011 ri.m_finish = ri.m_caret;
1013 else
1014 /* This is a non-primary range; ignore it. */
1015 return false;
1018 /* Potentially filter to just the lines already specified by other
1019 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1020 The layout ctor doesn't use it, and can't because m_line_spans
1021 hasn't been set up at that point. */
1022 if (restrict_to_current_line_spans)
1024 if (!will_show_line_p (start.line))
1025 return false;
1026 if (!will_show_line_p (finish.line))
1027 return false;
1028 if (loc_range->m_show_caret_p)
1029 if (!will_show_line_p (caret.line))
1030 return false;
1033 /* Passed all the tests; add the range to m_layout_ranges so that
1034 it will be printed. */
1035 m_layout_ranges.safe_push (ri);
1036 return true;
1039 /* Return true iff ROW is within one of the line spans for this layout. */
1041 bool
1042 layout::will_show_line_p (linenum_type row) const
1044 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1045 line_span_idx++)
1047 const line_span *line_span = get_line_span (line_span_idx);
1048 if (line_span->contains_line_p (row))
1049 return true;
1051 return false;
1054 /* Return true iff we should print a heading when starting the
1055 line span with the given index. */
1057 bool
1058 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1060 /* We print a heading for every change of line span, hence for every
1061 line span after the initial one. */
1062 if (line_span_idx > 0)
1063 return true;
1065 /* We also do it for the initial span if the primary location of the
1066 diagnostic is in a different span. */
1067 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1068 return true;
1070 return false;
1073 /* Get an expanded_location for the first location of interest within
1074 the given line_span.
1075 Used when printing a heading to indicate a new line span. */
1077 expanded_location
1078 layout::get_expanded_location (const line_span *line_span) const
1080 /* Whenever possible, use the caret location. */
1081 if (line_span->contains_line_p (m_exploc.line))
1082 return m_exploc;
1084 /* Otherwise, use the start of the first range that's present
1085 within the line_span. */
1086 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1088 const layout_range *lr = &m_layout_ranges[i];
1089 if (line_span->contains_line_p (lr->m_start.m_line))
1091 expanded_location exploc = m_exploc;
1092 exploc.line = lr->m_start.m_line;
1093 exploc.column = lr->m_start.m_column;
1094 return exploc;
1098 /* Otherwise, use the location of the first fixit-hint present within
1099 the line_span. */
1100 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1102 const fixit_hint *hint = m_fixit_hints[i];
1103 location_t loc = hint->get_start_loc ();
1104 expanded_location exploc = expand_location (loc);
1105 if (line_span->contains_line_p (exploc.line))
1106 return exploc;
1109 /* It should not be possible to have a line span that didn't
1110 contain any of the layout_range or fixit_hint instances. */
1111 gcc_unreachable ();
1112 return m_exploc;
1115 /* Determine if HINT is meaningful to print within this layout. */
1117 bool
1118 layout::validate_fixit_hint_p (const fixit_hint *hint)
1120 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1121 return false;
1122 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1123 return false;
1125 return true;
1128 /* Determine the range of lines affected by HINT.
1129 This assumes that HINT has already been filtered by
1130 validate_fixit_hint_p, and so affects the correct source file. */
1132 static line_span
1133 get_line_span_for_fixit_hint (const fixit_hint *hint)
1135 gcc_assert (hint);
1136 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1137 LOCATION_LINE (hint->get_next_loc ()));
1140 /* We want to print the pertinent source code at a diagnostic. The
1141 rich_location can contain multiple locations. This will have been
1142 filtered into m_exploc (the caret for the primary location) and
1143 m_layout_ranges, for those ranges within the same source file.
1145 We will print a subset of the lines within the source file in question,
1146 as a collection of "spans" of lines.
1148 This function populates m_line_spans with an ordered, disjoint list of
1149 the line spans of interest.
1151 For example, if the primary caret location is on line 7, with ranges
1152 covering lines 5-6 and lines 9-12:
1155 005 |RANGE 0
1156 006 |RANGE 0
1157 007 |PRIMARY CARET
1159 009 |RANGE 1
1160 010 |RANGE 1
1161 011 |RANGE 1
1162 012 |RANGE 1
1165 then we want two spans: lines 5-7 and lines 9-12. */
1167 void
1168 layout::calculate_line_spans ()
1170 /* This should only be called once, by the ctor. */
1171 gcc_assert (m_line_spans.length () == 0);
1173 /* Populate tmp_spans with individual spans, for each of
1174 m_exploc, and for m_layout_ranges. */
1175 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1176 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1177 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1179 const layout_range *lr = &m_layout_ranges[i];
1180 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1181 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1182 lr->m_finish.m_line));
1185 /* Also add spans for any fix-it hints, in case they cover other lines. */
1186 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1188 const fixit_hint *hint = m_fixit_hints[i];
1189 gcc_assert (hint);
1190 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1193 /* Sort them. */
1194 tmp_spans.qsort(line_span::comparator);
1196 /* Now iterate through tmp_spans, copying into m_line_spans, and
1197 combining where possible. */
1198 gcc_assert (tmp_spans.length () > 0);
1199 m_line_spans.safe_push (tmp_spans[0]);
1200 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1202 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1203 const line_span *next = &tmp_spans[i];
1204 gcc_assert (next->m_first_line >= current->m_first_line);
1205 if (next->m_first_line <= current->m_last_line + 1)
1207 /* We can merge them. */
1208 if (next->m_last_line > current->m_last_line)
1209 current->m_last_line = next->m_last_line;
1211 else
1213 /* No merger possible. */
1214 m_line_spans.safe_push (*next);
1218 /* Verify the result, in m_line_spans. */
1219 gcc_assert (m_line_spans.length () > 0);
1220 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1222 const line_span *prev = &m_line_spans[i - 1];
1223 const line_span *next = &m_line_spans[i];
1224 /* The individual spans must be sane. */
1225 gcc_assert (prev->m_first_line <= prev->m_last_line);
1226 gcc_assert (next->m_first_line <= next->m_last_line);
1227 /* The spans must be ordered. */
1228 gcc_assert (prev->m_first_line < next->m_first_line);
1229 /* There must be a gap of at least one line between separate spans. */
1230 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1234 /* Print line ROW of source code, potentially colorized at any ranges, and
1235 populate *LBOUNDS_OUT.
1236 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1237 is its width. */
1239 void
1240 layout::print_source_line (linenum_type row, const char *line, int line_width,
1241 line_bounds *lbounds_out)
1243 m_colorizer.set_normal_text ();
1245 /* We will stop printing the source line at any trailing
1246 whitespace. */
1247 line_width = get_line_width_without_trailing_whitespace (line,
1248 line_width);
1249 line += m_x_offset;
1251 if (m_show_line_numbers_p)
1253 int width = num_digits (row);
1254 for (int i = 0; i < m_linenum_width - width; i++)
1255 pp_space (m_pp);
1256 pp_printf (m_pp, "%i | ", row);
1258 else
1259 pp_space (m_pp);
1260 int first_non_ws = INT_MAX;
1261 int last_non_ws = 0;
1262 int column;
1263 for (column = 1 + m_x_offset; column <= line_width; column++)
1265 /* Assuming colorization is enabled for the caret and underline
1266 characters, we may also colorize the associated characters
1267 within the source line.
1269 For frontends that generate range information, we color the
1270 associated characters in the source line the same as the
1271 carets and underlines in the annotation line, to make it easier
1272 for the reader to see the pertinent code.
1274 For frontends that only generate carets, we don't colorize the
1275 characters above them, since this would look strange (e.g.
1276 colorizing just the first character in a token). */
1277 if (m_colorize_source_p)
1279 bool in_range_p;
1280 point_state state;
1281 in_range_p = get_state_at_point (row, column,
1282 0, INT_MAX,
1283 &state);
1284 if (in_range_p)
1285 m_colorizer.set_range (state.range_idx);
1286 else
1287 m_colorizer.set_normal_text ();
1289 char c = *line;
1290 if (c == '\0' || c == '\t' || c == '\r')
1291 c = ' ';
1292 if (c != ' ')
1294 last_non_ws = column;
1295 if (first_non_ws == INT_MAX)
1296 first_non_ws = column;
1298 pp_character (m_pp, c);
1299 line++;
1301 print_newline ();
1303 lbounds_out->m_first_non_ws = first_non_ws;
1304 lbounds_out->m_last_non_ws = last_non_ws;
1307 /* Determine if we should print an annotation line for ROW.
1308 i.e. if any of m_layout_ranges contains ROW. */
1310 bool
1311 layout::should_print_annotation_line_p (linenum_type row) const
1313 layout_range *range;
1314 int i;
1315 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1316 if (range->intersects_line_p (row))
1317 return true;
1318 return false;
1321 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1322 margin, which is empty for annotation lines. Otherwise, do nothing. */
1324 void
1325 layout::start_annotation_line () const
1327 if (m_show_line_numbers_p)
1329 for (int i = 0; i < m_linenum_width; i++)
1330 pp_space (m_pp);
1331 pp_string (m_pp, " |");
1335 /* Print a line consisting of the caret/underlines for the given
1336 source line. */
1338 void
1339 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1341 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1342 lbounds.m_last_non_ws);
1344 start_annotation_line ();
1345 pp_space (m_pp);
1347 for (int column = 1 + m_x_offset; column < x_bound; column++)
1349 bool in_range_p;
1350 point_state state;
1351 in_range_p = get_state_at_point (row, column,
1352 lbounds.m_first_non_ws,
1353 lbounds.m_last_non_ws,
1354 &state);
1355 if (in_range_p)
1357 /* Within a range. Draw either the caret or an underline. */
1358 m_colorizer.set_range (state.range_idx);
1359 if (state.draw_caret_p)
1361 /* Draw the caret. */
1362 char caret_char;
1363 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1364 caret_char = m_context->caret_chars[state.range_idx];
1365 else
1366 caret_char = '^';
1367 pp_character (m_pp, caret_char);
1369 else
1370 pp_character (m_pp, '~');
1372 else
1374 /* Not in a range. */
1375 m_colorizer.set_normal_text ();
1376 pp_character (m_pp, ' ');
1379 print_newline ();
1382 /* If there are any fixit hints inserting new lines before source line ROW,
1383 print them.
1385 They are printed on lines of their own, before the source line
1386 itself, with a leading '+'. */
1388 void
1389 layout::print_leading_fixits (linenum_type row)
1391 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1393 const fixit_hint *hint = m_fixit_hints[i];
1395 if (!hint->ends_with_newline_p ())
1396 /* Not a newline fixit; print it in print_trailing_fixits. */
1397 continue;
1399 gcc_assert (hint->insertion_p ());
1401 if (hint->affects_line_p (m_exploc.file, row))
1403 /* Printing the '+' with normal colorization
1404 and the inserted line with "insert" colorization
1405 helps them stand out from each other, and from
1406 the surrounding text. */
1407 m_colorizer.set_normal_text ();
1408 start_annotation_line ();
1409 pp_character (m_pp, '+');
1410 m_colorizer.set_fixit_insert ();
1411 /* Print all but the trailing newline of the fix-it hint.
1412 We have to print the newline separately to avoid
1413 getting additional pp prefixes printed. */
1414 for (size_t i = 0; i < hint->get_length () - 1; i++)
1415 pp_character (m_pp, hint->get_string ()[i]);
1416 m_colorizer.set_normal_text ();
1417 pp_newline (m_pp);
1422 /* Subroutine of layout::print_trailing_fixits.
1424 Determine if the annotation line printed for LINE contained
1425 the exact range from START_COLUMN to FINISH_COLUMN. */
1427 bool
1428 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1429 int finish_column) const
1431 layout_range *range;
1432 int i;
1433 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1434 if (range->m_start.m_line == line
1435 && range->m_start.m_column == start_column
1436 && range->m_finish.m_line == line
1437 && range->m_finish.m_column == finish_column)
1438 return true;
1439 return false;
1442 /* Classes for printing trailing fix-it hints i.e. those that
1443 don't add new lines.
1445 For insertion, these can look like:
1447 new_text
1449 For replacement, these can look like:
1451 ------------- : underline showing affected range
1452 new_text
1454 For deletion, these can look like:
1456 ------------- : underline showing affected range
1458 This can become confusing if they overlap, and so we need
1459 to do some preprocessing to decide what to print.
1460 We use the list of fixit_hint instances affecting the line
1461 to build a list of "correction" instances, and print the
1462 latter.
1464 For example, consider a set of fix-its for converting
1465 a C-style cast to a C++ const_cast.
1467 Given:
1469 ..000000000111111111122222222223333333333.
1470 ..123456789012345678901234567890123456789.
1471 foo *f = (foo *)ptr->field;
1472 ^~~~~
1474 and the fix-it hints:
1475 - replace col 10 (the open paren) with "const_cast<"
1476 - replace col 16 (the close paren) with "> ("
1477 - insert ")" before col 27
1479 then we would get odd-looking output:
1481 foo *f = (foo *)ptr->field;
1482 ^~~~~
1484 const_cast<
1486 > ( )
1488 It would be better to detect when fixit hints are going to
1489 overlap (those that require new lines), and to consolidate
1490 the printing of such fixits, giving something like:
1492 foo *f = (foo *)ptr->field;
1493 ^~~~~
1494 -----------------
1495 const_cast<foo *> (ptr->field)
1497 This works by detecting when the printing would overlap, and
1498 effectively injecting no-op replace hints into the gaps between
1499 such fix-its, so that the printing joins up.
1501 In the above example, the overlap of:
1502 - replace col 10 (the open paren) with "const_cast<"
1503 and:
1504 - replace col 16 (the close paren) with "> ("
1505 is fixed by injecting a no-op:
1506 - replace cols 11-15 with themselves ("foo *")
1507 and consolidating these, making:
1508 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1509 i.e.:
1510 - replace cols 10-16 with "const_cast<foo *> ("
1512 This overlaps with the final fix-it hint:
1513 - insert ")" before col 27
1514 and so we repeat the consolidation process, by injecting
1515 a no-op:
1516 - replace cols 17-26 with themselves ("ptr->field")
1517 giving:
1518 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1519 i.e.:
1520 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1522 and is thus printed as desired. */
1524 /* A range of columns within a line. */
1526 struct column_range
1528 column_range (int start_, int finish_) : start (start_), finish (finish_)
1530 /* We must have either a range, or an insertion. */
1531 gcc_assert (start <= finish || finish == start - 1);
1534 bool operator== (const column_range &other) const
1536 return start == other.start && finish == other.finish;
1539 int start;
1540 int finish;
1543 /* Get the range of columns that HINT would affect. */
1545 static column_range
1546 get_affected_columns (const fixit_hint *hint)
1548 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1549 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1551 return column_range (start_column, finish_column);
1554 /* Get the range of columns that would be printed for HINT. */
1556 static column_range
1557 get_printed_columns (const fixit_hint *hint)
1559 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1560 int final_hint_column = start_column + hint->get_length () - 1;
1561 if (hint->insertion_p ())
1563 return column_range (start_column, final_hint_column);
1565 else
1567 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1569 return column_range (start_column,
1570 MAX (finish_column, final_hint_column));
1574 /* A correction on a particular line.
1575 This describes a plan for how to print one or more fixit_hint
1576 instances that affected the line, potentially consolidating hints
1577 into corrections to make the result easier for the user to read. */
1579 struct correction
1581 correction (column_range affected_columns,
1582 column_range printed_columns,
1583 const char *new_text, size_t new_text_len)
1584 : m_affected_columns (affected_columns),
1585 m_printed_columns (printed_columns),
1586 m_text (xstrdup (new_text)),
1587 m_len (new_text_len),
1588 m_alloc_sz (new_text_len + 1)
1592 ~correction () { free (m_text); }
1594 bool insertion_p () const
1596 return m_affected_columns.start == m_affected_columns.finish + 1;
1599 void ensure_capacity (size_t len);
1600 void ensure_terminated ();
1602 void overwrite (int dst_offset, const char_span &src_span)
1604 gcc_assert (dst_offset >= 0);
1605 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1606 memcpy (m_text + dst_offset, src_span.get_buffer (),
1607 src_span.length ());
1610 /* If insert, then start: the column before which the text
1611 is to be inserted, and finish is offset by the length of
1612 the replacement.
1613 If replace, then the range of columns affected. */
1614 column_range m_affected_columns;
1616 /* If insert, then start: the column before which the text
1617 is to be inserted, and finish is offset by the length of
1618 the replacement.
1619 If replace, then the range of columns affected. */
1620 column_range m_printed_columns;
1622 /* The text to be inserted/used as replacement. */
1623 char *m_text;
1624 size_t m_len;
1625 size_t m_alloc_sz;
1628 /* Ensure that m_text can hold a string of length LEN
1629 (plus 1 for 0-termination). */
1631 void
1632 correction::ensure_capacity (size_t len)
1634 /* Allow 1 extra byte for 0-termination. */
1635 if (m_alloc_sz < (len + 1))
1637 size_t new_alloc_sz = (len + 1) * 2;
1638 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1639 m_alloc_sz = new_alloc_sz;
1643 /* Ensure that m_text is 0-terminated. */
1645 void
1646 correction::ensure_terminated ()
1648 /* 0-terminate the buffer. */
1649 gcc_assert (m_len < m_alloc_sz);
1650 m_text[m_len] = '\0';
1653 /* A list of corrections affecting a particular line.
1654 This is used by layout::print_trailing_fixits for planning
1655 how to print the fix-it hints affecting the line. */
1657 struct line_corrections
1659 line_corrections (const char *filename, linenum_type row)
1660 : m_filename (filename), m_row (row)
1662 ~line_corrections ();
1664 void add_hint (const fixit_hint *hint);
1666 const char *m_filename;
1667 linenum_type m_row;
1668 auto_vec <correction *> m_corrections;
1671 /* struct line_corrections. */
1673 line_corrections::~line_corrections ()
1675 unsigned i;
1676 correction *c;
1677 FOR_EACH_VEC_ELT (m_corrections, i, c)
1678 delete c;
1681 /* A struct wrapping a particular source line, allowing
1682 run-time bounds-checking of accesses in a checked build. */
1684 struct source_line
1686 source_line (const char *filename, int line);
1688 char_span as_span () { return char_span (chars, width); }
1690 const char *chars;
1691 int width;
1694 /* source_line's ctor. */
1696 source_line::source_line (const char *filename, int line)
1698 char_span span = location_get_source_line (filename, line);
1699 chars = span.get_buffer ();
1700 width = span.length ();
1703 /* Add HINT to the corrections for this line.
1704 Attempt to consolidate nearby hints so that they will not
1705 overlap with printed. */
1707 void
1708 line_corrections::add_hint (const fixit_hint *hint)
1710 column_range affected_columns = get_affected_columns (hint);
1711 column_range printed_columns = get_printed_columns (hint);
1713 /* Potentially consolidate. */
1714 if (!m_corrections.is_empty ())
1716 correction *last_correction
1717 = m_corrections[m_corrections.length () - 1];
1719 /* The following consolidation code assumes that the fix-it hints
1720 have been sorted by start (done within layout's ctor). */
1721 gcc_assert (affected_columns.start
1722 >= last_correction->m_affected_columns.start);
1723 gcc_assert (printed_columns.start
1724 >= last_correction->m_printed_columns.start);
1726 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1728 /* We have two hints for which the printed forms of the hints
1729 would touch or overlap, so we need to consolidate them to avoid
1730 confusing the user.
1731 Attempt to inject a "replace" correction from immediately
1732 after the end of the last hint to immediately before the start
1733 of the next hint. */
1734 column_range between (last_correction->m_affected_columns.finish + 1,
1735 printed_columns.start - 1);
1737 /* Try to read the source. */
1738 source_line line (m_filename, m_row);
1739 if (line.chars && between.finish < line.width)
1741 /* Consolidate into the last correction:
1742 add a no-op "replace" of the "between" text, and
1743 add the text from the new hint. */
1744 int old_len = last_correction->m_len;
1745 gcc_assert (old_len >= 0);
1746 int between_len = between.finish + 1 - between.start;
1747 gcc_assert (between_len >= 0);
1748 int new_len = old_len + between_len + hint->get_length ();
1749 gcc_assert (new_len >= 0);
1750 last_correction->ensure_capacity (new_len);
1751 last_correction->overwrite
1752 (old_len,
1753 line.as_span ().subspan (between.start - 1,
1754 between.finish + 1 - between.start));
1755 last_correction->overwrite (old_len + between_len,
1756 char_span (hint->get_string (),
1757 hint->get_length ()));
1758 last_correction->m_len = new_len;
1759 last_correction->ensure_terminated ();
1760 last_correction->m_affected_columns.finish
1761 = affected_columns.finish;
1762 last_correction->m_printed_columns.finish
1763 += between_len + hint->get_length ();
1764 return;
1769 /* If no consolidation happened, add a new correction instance. */
1770 m_corrections.safe_push (new correction (affected_columns,
1771 printed_columns,
1772 hint->get_string (),
1773 hint->get_length ()));
1776 /* If there are any fixit hints on source line ROW, print them.
1777 They are printed in order, attempting to combine them onto lines, but
1778 starting new lines if necessary.
1779 Fix-it hints that insert new lines are handled separately,
1780 in layout::print_leading_fixits. */
1782 void
1783 layout::print_trailing_fixits (linenum_type row)
1785 /* Build a list of correction instances for the line,
1786 potentially consolidating hints (for the sake of readability). */
1787 line_corrections corrections (m_exploc.file, row);
1788 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1790 const fixit_hint *hint = m_fixit_hints[i];
1792 /* Newline fixits are handled by layout::print_leading_fixits. */
1793 if (hint->ends_with_newline_p ())
1794 continue;
1796 if (hint->affects_line_p (m_exploc.file, row))
1797 corrections.add_hint (hint);
1800 /* Now print the corrections. */
1801 unsigned i;
1802 correction *c;
1803 int column = m_x_offset;
1805 if (!corrections.m_corrections.is_empty ())
1806 start_annotation_line ();
1808 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1810 /* For now we assume each fixit hint can only touch one line. */
1811 if (c->insertion_p ())
1813 /* This assumes the insertion just affects one line. */
1814 int start_column = c->m_printed_columns.start;
1815 move_to_column (&column, start_column, true);
1816 m_colorizer.set_fixit_insert ();
1817 pp_string (m_pp, c->m_text);
1818 m_colorizer.set_normal_text ();
1819 column += c->m_len;
1821 else
1823 /* If the range of the replacement wasn't printed in the
1824 annotation line, then print an extra underline to
1825 indicate exactly what is being replaced.
1826 Always show it for removals. */
1827 int start_column = c->m_affected_columns.start;
1828 int finish_column = c->m_affected_columns.finish;
1829 if (!annotation_line_showed_range_p (row, start_column,
1830 finish_column)
1831 || c->m_len == 0)
1833 move_to_column (&column, start_column, true);
1834 m_colorizer.set_fixit_delete ();
1835 for (; column <= finish_column; column++)
1836 pp_character (m_pp, '-');
1837 m_colorizer.set_normal_text ();
1839 /* Print the replacement text. REPLACE also covers
1840 removals, so only do this extra work (potentially starting
1841 a new line) if we have actual replacement text. */
1842 if (c->m_len > 0)
1844 move_to_column (&column, start_column, true);
1845 m_colorizer.set_fixit_insert ();
1846 pp_string (m_pp, c->m_text);
1847 m_colorizer.set_normal_text ();
1848 column += c->m_len;
1853 /* Add a trailing newline, if necessary. */
1854 move_to_column (&column, 0, false);
1857 /* Disable any colorization and emit a newline. */
1859 void
1860 layout::print_newline ()
1862 m_colorizer.set_normal_text ();
1863 pp_newline (m_pp);
1866 /* Return true if (ROW/COLUMN) is within a range of the layout.
1867 If it returns true, OUT_STATE is written to, with the
1868 range index, and whether we should draw the caret at
1869 (ROW/COLUMN) (as opposed to an underline). */
1871 bool
1872 layout::get_state_at_point (/* Inputs. */
1873 linenum_type row, int column,
1874 int first_non_ws, int last_non_ws,
1875 /* Outputs. */
1876 point_state *out_state)
1878 layout_range *range;
1879 int i;
1880 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1882 if (range->contains_point (row, column))
1884 out_state->range_idx = i;
1886 /* Are we at the range's caret? is it visible? */
1887 out_state->draw_caret_p = false;
1888 if (range->m_show_caret_p
1889 && row == range->m_caret.m_line
1890 && column == range->m_caret.m_column)
1891 out_state->draw_caret_p = true;
1893 /* Within a multiline range, don't display any underline
1894 in any leading or trailing whitespace on a line.
1895 We do display carets, however. */
1896 if (!out_state->draw_caret_p)
1897 if (column < first_non_ws || column > last_non_ws)
1898 return false;
1900 /* We are within a range. */
1901 return true;
1905 return false;
1908 /* Helper function for use by layout::print_line when printing the
1909 annotation line under the source line.
1910 Get the column beyond the rightmost one that could contain a caret or
1911 range marker, given that we stop rendering at trailing whitespace.
1912 ROW is the source line within the given file.
1913 CARET_COLUMN is the column of range 0's caret.
1914 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1915 character of source (as determined when printing the source line). */
1918 layout::get_x_bound_for_row (linenum_type row, int caret_column,
1919 int last_non_ws_column)
1921 int result = caret_column + 1;
1923 layout_range *range;
1924 int i;
1925 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1927 if (row >= range->m_start.m_line)
1929 if (range->m_finish.m_line == row)
1931 /* On the final line within a range; ensure that
1932 we render up to the end of the range. */
1933 if (result <= range->m_finish.m_column)
1934 result = range->m_finish.m_column + 1;
1936 else if (row < range->m_finish.m_line)
1938 /* Within a multiline range; ensure that we render up to the
1939 last non-whitespace column. */
1940 if (result <= last_non_ws_column)
1941 result = last_non_ws_column + 1;
1946 return result;
1949 /* Given *COLUMN as an x-coordinate, print spaces to position
1950 successive output at DEST_COLUMN, printing a newline if necessary,
1951 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
1952 left margin after any newline. */
1954 void
1955 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
1957 /* Start a new line if we need to. */
1958 if (*column > dest_column)
1960 print_newline ();
1961 if (add_left_margin)
1962 start_annotation_line ();
1963 *column = m_x_offset;
1966 while (*column < dest_column)
1968 pp_space (m_pp);
1969 (*column)++;
1973 /* For debugging layout issues, render a ruler giving column numbers
1974 (after the 1-column indent). */
1976 void
1977 layout::show_ruler (int max_column) const
1979 /* Hundreds. */
1980 if (max_column > 99)
1982 start_annotation_line ();
1983 pp_space (m_pp);
1984 for (int column = 1 + m_x_offset; column <= max_column; column++)
1985 if (column % 10 == 0)
1986 pp_character (m_pp, '0' + (column / 100) % 10);
1987 else
1988 pp_space (m_pp);
1989 pp_newline (m_pp);
1992 /* Tens. */
1993 start_annotation_line ();
1994 pp_space (m_pp);
1995 for (int column = 1 + m_x_offset; column <= max_column; column++)
1996 if (column % 10 == 0)
1997 pp_character (m_pp, '0' + (column / 10) % 10);
1998 else
1999 pp_space (m_pp);
2000 pp_newline (m_pp);
2002 /* Units. */
2003 start_annotation_line ();
2004 pp_space (m_pp);
2005 for (int column = 1 + m_x_offset; column <= max_column; column++)
2006 pp_character (m_pp, '0' + (column % 10));
2007 pp_newline (m_pp);
2010 /* Print leading fix-its (for new lines inserted before the source line)
2011 then the source line, followed by an annotation line
2012 consisting of any caret/underlines, then any fixits.
2013 If the source line can't be read, print nothing. */
2014 void
2015 layout::print_line (linenum_type row)
2017 char_span line = location_get_source_line (m_exploc.file, row);
2018 if (!line)
2019 return;
2021 line_bounds lbounds;
2022 print_leading_fixits (row);
2023 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2024 if (should_print_annotation_line_p (row))
2025 print_annotation_line (row, lbounds);
2026 print_trailing_fixits (row);
2029 } /* End of anonymous namespace. */
2031 /* If LOC is within the spans of lines that will already be printed for
2032 this gcc_rich_location, then add it as a secondary location and return true.
2034 Otherwise return false. */
2036 bool
2037 gcc_rich_location::add_location_if_nearby (location_t loc)
2039 /* Use the layout location-handling logic to sanitize LOC,
2040 filtering it to the current line spans within a temporary
2041 layout instance. */
2042 layout layout (global_dc, this, DK_ERROR);
2043 location_range loc_range;
2044 loc_range.m_loc = loc;
2045 loc_range.m_show_caret_p = false;
2046 if (!layout.maybe_add_location_range (&loc_range, true))
2047 return false;
2049 add_range (loc, false);
2050 return true;
2053 /* Print the physical source code corresponding to the location of
2054 this diagnostic, with additional annotations. */
2056 void
2057 diagnostic_show_locus (diagnostic_context * context,
2058 rich_location *richloc,
2059 diagnostic_t diagnostic_kind)
2061 pp_newline (context->printer);
2063 location_t loc = richloc->get_loc ();
2064 /* Do nothing if source-printing has been disabled. */
2065 if (!context->show_caret)
2066 return;
2068 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2069 if (loc <= BUILTINS_LOCATION)
2070 return;
2072 /* Don't print the same source location twice in a row, unless we have
2073 fix-it hints. */
2074 if (loc == context->last_location
2075 && richloc->get_num_fixit_hints () == 0)
2076 return;
2078 context->last_location = loc;
2080 char *saved_prefix = pp_take_prefix (context->printer);
2081 pp_set_prefix (context->printer, NULL);
2083 layout layout (context, richloc, diagnostic_kind);
2084 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2085 line_span_idx++)
2087 const line_span *line_span = layout.get_line_span (line_span_idx);
2088 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2090 expanded_location exploc = layout.get_expanded_location (line_span);
2091 context->start_span (context, exploc);
2093 linenum_type last_line = line_span->get_last_line ();
2094 for (linenum_type row = line_span->get_first_line ();
2095 row <= last_line; row++)
2096 layout.print_line (row);
2099 pp_set_prefix (context->printer, saved_prefix);
2102 #if CHECKING_P
2104 namespace selftest {
2106 /* Selftests for diagnostic_show_locus. */
2108 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2110 static void
2111 test_diagnostic_show_locus_unknown_location ()
2113 test_diagnostic_context dc;
2114 rich_location richloc (line_table, UNKNOWN_LOCATION);
2115 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2116 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2119 /* Verify that diagnostic_show_locus works sanely for various
2120 single-line cases.
2122 All of these work on the following 1-line source file:
2123 .0000000001111111
2124 .1234567890123456
2125 "foo = bar.field;\n"
2126 which is set up by test_diagnostic_show_locus_one_liner and calls
2127 them. */
2129 /* Just a caret. */
2131 static void
2132 test_one_liner_simple_caret ()
2134 test_diagnostic_context dc;
2135 location_t caret = linemap_position_for_column (line_table, 10);
2136 rich_location richloc (line_table, caret);
2137 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2138 ASSERT_STREQ ("\n"
2139 " foo = bar.field;\n"
2140 " ^\n",
2141 pp_formatted_text (dc.printer));
2144 /* Caret and range. */
2146 static void
2147 test_one_liner_caret_and_range ()
2149 test_diagnostic_context dc;
2150 location_t caret = linemap_position_for_column (line_table, 10);
2151 location_t start = linemap_position_for_column (line_table, 7);
2152 location_t finish = linemap_position_for_column (line_table, 15);
2153 location_t loc = make_location (caret, start, finish);
2154 rich_location richloc (line_table, loc);
2155 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2156 ASSERT_STREQ ("\n"
2157 " foo = bar.field;\n"
2158 " ~~~^~~~~~\n",
2159 pp_formatted_text (dc.printer));
2162 /* Multiple ranges and carets. */
2164 static void
2165 test_one_liner_multiple_carets_and_ranges ()
2167 test_diagnostic_context dc;
2168 location_t foo
2169 = make_location (linemap_position_for_column (line_table, 2),
2170 linemap_position_for_column (line_table, 1),
2171 linemap_position_for_column (line_table, 3));
2172 dc.caret_chars[0] = 'A';
2174 location_t bar
2175 = make_location (linemap_position_for_column (line_table, 8),
2176 linemap_position_for_column (line_table, 7),
2177 linemap_position_for_column (line_table, 9));
2178 dc.caret_chars[1] = 'B';
2180 location_t field
2181 = make_location (linemap_position_for_column (line_table, 13),
2182 linemap_position_for_column (line_table, 11),
2183 linemap_position_for_column (line_table, 15));
2184 dc.caret_chars[2] = 'C';
2186 rich_location richloc (line_table, foo);
2187 richloc.add_range (bar, true);
2188 richloc.add_range (field, true);
2189 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2190 ASSERT_STREQ ("\n"
2191 " foo = bar.field;\n"
2192 " ~A~ ~B~ ~~C~~\n",
2193 pp_formatted_text (dc.printer));
2196 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2198 static void
2199 test_one_liner_fixit_insert_before ()
2201 test_diagnostic_context dc;
2202 location_t caret = linemap_position_for_column (line_table, 7);
2203 rich_location richloc (line_table, caret);
2204 richloc.add_fixit_insert_before ("&");
2205 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2206 ASSERT_STREQ ("\n"
2207 " foo = bar.field;\n"
2208 " ^\n"
2209 " &\n",
2210 pp_formatted_text (dc.printer));
2213 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2215 static void
2216 test_one_liner_fixit_insert_after ()
2218 test_diagnostic_context dc;
2219 location_t start = linemap_position_for_column (line_table, 1);
2220 location_t finish = linemap_position_for_column (line_table, 3);
2221 location_t foo = make_location (start, start, finish);
2222 rich_location richloc (line_table, foo);
2223 richloc.add_fixit_insert_after ("[0]");
2224 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2225 ASSERT_STREQ ("\n"
2226 " foo = bar.field;\n"
2227 " ^~~\n"
2228 " [0]\n",
2229 pp_formatted_text (dc.printer));
2232 /* Removal fix-it hint: removal of the ".field". */
2234 static void
2235 test_one_liner_fixit_remove ()
2237 test_diagnostic_context dc;
2238 location_t start = linemap_position_for_column (line_table, 10);
2239 location_t finish = linemap_position_for_column (line_table, 15);
2240 location_t dot = make_location (start, start, finish);
2241 rich_location richloc (line_table, dot);
2242 richloc.add_fixit_remove ();
2243 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2244 ASSERT_STREQ ("\n"
2245 " foo = bar.field;\n"
2246 " ^~~~~~\n"
2247 " ------\n",
2248 pp_formatted_text (dc.printer));
2251 /* Replace fix-it hint: replacing "field" with "m_field". */
2253 static void
2254 test_one_liner_fixit_replace ()
2256 test_diagnostic_context dc;
2257 location_t start = linemap_position_for_column (line_table, 11);
2258 location_t finish = linemap_position_for_column (line_table, 15);
2259 location_t field = make_location (start, start, finish);
2260 rich_location richloc (line_table, field);
2261 richloc.add_fixit_replace ("m_field");
2262 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2263 ASSERT_STREQ ("\n"
2264 " foo = bar.field;\n"
2265 " ^~~~~\n"
2266 " m_field\n",
2267 pp_formatted_text (dc.printer));
2270 /* Replace fix-it hint: replacing "field" with "m_field",
2271 but where the caret was elsewhere. */
2273 static void
2274 test_one_liner_fixit_replace_non_equal_range ()
2276 test_diagnostic_context dc;
2277 location_t equals = linemap_position_for_column (line_table, 5);
2278 location_t start = linemap_position_for_column (line_table, 11);
2279 location_t finish = linemap_position_for_column (line_table, 15);
2280 rich_location richloc (line_table, equals);
2281 source_range range;
2282 range.m_start = start;
2283 range.m_finish = finish;
2284 richloc.add_fixit_replace (range, "m_field");
2285 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2286 /* The replacement range is not indicated in the annotation line, so
2287 it should be indicated via an additional underline. */
2288 ASSERT_STREQ ("\n"
2289 " foo = bar.field;\n"
2290 " ^\n"
2291 " -----\n"
2292 " m_field\n",
2293 pp_formatted_text (dc.printer));
2296 /* Replace fix-it hint: replacing "field" with "m_field",
2297 where the caret was elsewhere, but where a secondary range
2298 exactly covers "field". */
2300 static void
2301 test_one_liner_fixit_replace_equal_secondary_range ()
2303 test_diagnostic_context dc;
2304 location_t equals = linemap_position_for_column (line_table, 5);
2305 location_t start = linemap_position_for_column (line_table, 11);
2306 location_t finish = linemap_position_for_column (line_table, 15);
2307 rich_location richloc (line_table, equals);
2308 location_t field = make_location (start, start, finish);
2309 richloc.add_range (field, false);
2310 richloc.add_fixit_replace (field, "m_field");
2311 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2312 /* The replacement range is indicated in the annotation line,
2313 so it shouldn't be indicated via an additional underline. */
2314 ASSERT_STREQ ("\n"
2315 " foo = bar.field;\n"
2316 " ^ ~~~~~\n"
2317 " m_field\n",
2318 pp_formatted_text (dc.printer));
2321 /* Verify that we can use ad-hoc locations when adding fixits to a
2322 rich_location. */
2324 static void
2325 test_one_liner_fixit_validation_adhoc_locations ()
2327 /* Generate a range that's too long to be packed, so must
2328 be stored as an ad-hoc location (given the defaults
2329 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2330 const location_t c7 = linemap_position_for_column (line_table, 7);
2331 const location_t c47 = linemap_position_for_column (line_table, 47);
2332 const location_t loc = make_location (c7, c7, c47);
2334 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2335 return;
2337 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2339 /* Insert. */
2341 rich_location richloc (line_table, loc);
2342 richloc.add_fixit_insert_before (loc, "test");
2343 /* It should not have been discarded by the validator. */
2344 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2346 test_diagnostic_context dc;
2347 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2348 ASSERT_STREQ ("\n"
2349 " foo = bar.field;\n"
2350 " ^~~~~~~~~~ \n"
2351 " test\n",
2352 pp_formatted_text (dc.printer));
2355 /* Remove. */
2357 rich_location richloc (line_table, loc);
2358 source_range range = source_range::from_locations (loc, c47);
2359 richloc.add_fixit_remove (range);
2360 /* It should not have been discarded by the validator. */
2361 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2363 test_diagnostic_context dc;
2364 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2365 ASSERT_STREQ ("\n"
2366 " foo = bar.field;\n"
2367 " ^~~~~~~~~~ \n"
2368 " -----------------------------------------\n",
2369 pp_formatted_text (dc.printer));
2372 /* Replace. */
2374 rich_location richloc (line_table, loc);
2375 source_range range = source_range::from_locations (loc, c47);
2376 richloc.add_fixit_replace (range, "test");
2377 /* It should not have been discarded by the validator. */
2378 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2380 test_diagnostic_context dc;
2381 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2382 ASSERT_STREQ ("\n"
2383 " foo = bar.field;\n"
2384 " ^~~~~~~~~~ \n"
2385 " test\n",
2386 pp_formatted_text (dc.printer));
2390 /* Test of consolidating insertions at the same location. */
2392 static void
2393 test_one_liner_many_fixits_1 ()
2395 test_diagnostic_context dc;
2396 location_t equals = linemap_position_for_column (line_table, 5);
2397 rich_location richloc (line_table, equals);
2398 for (int i = 0; i < 19; i++)
2399 richloc.add_fixit_insert_before ("a");
2400 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2401 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2402 ASSERT_STREQ ("\n"
2403 " foo = bar.field;\n"
2404 " ^\n"
2405 " aaaaaaaaaaaaaaaaaaa\n",
2406 pp_formatted_text (dc.printer));
2409 /* Ensure that we can add an arbitrary number of fix-it hints to a
2410 rich_location, even if they are not consolidated. */
2412 static void
2413 test_one_liner_many_fixits_2 ()
2415 test_diagnostic_context dc;
2416 location_t equals = linemap_position_for_column (line_table, 5);
2417 rich_location richloc (line_table, equals);
2418 for (int i = 0; i < 19; i++)
2420 location_t loc = linemap_position_for_column (line_table, i * 2);
2421 richloc.add_fixit_insert_before (loc, "a");
2423 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2424 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2425 ASSERT_STREQ ("\n"
2426 " foo = bar.field;\n"
2427 " ^\n"
2428 "a a a a a a a a a a a a a a a a a a a\n",
2429 pp_formatted_text (dc.printer));
2432 /* Run the various one-liner tests. */
2434 static void
2435 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2437 /* Create a tempfile and write some text to it.
2438 ....................0000000001111111.
2439 ....................1234567890123456. */
2440 const char *content = "foo = bar.field;\n";
2441 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2442 line_table_test ltt (case_);
2444 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2446 location_t line_end = linemap_position_for_column (line_table, 16);
2448 /* Don't attempt to run the tests if column data might be unavailable. */
2449 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2450 return;
2452 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2453 ASSERT_EQ (1, LOCATION_LINE (line_end));
2454 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2456 test_one_liner_simple_caret ();
2457 test_one_liner_caret_and_range ();
2458 test_one_liner_multiple_carets_and_ranges ();
2459 test_one_liner_fixit_insert_before ();
2460 test_one_liner_fixit_insert_after ();
2461 test_one_liner_fixit_remove ();
2462 test_one_liner_fixit_replace ();
2463 test_one_liner_fixit_replace_non_equal_range ();
2464 test_one_liner_fixit_replace_equal_secondary_range ();
2465 test_one_liner_fixit_validation_adhoc_locations ();
2466 test_one_liner_many_fixits_1 ();
2467 test_one_liner_many_fixits_2 ();
2470 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2472 static void
2473 test_add_location_if_nearby (const line_table_case &case_)
2475 /* Create a tempfile and write some text to it.
2476 ...000000000111111111122222222223333333333.
2477 ...123456789012345678901234567890123456789. */
2478 const char *content
2479 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2480 "struct different_line\n" /* line 2. */
2481 "{\n" /* line 3. */
2482 " double x;\n" /* line 4. */
2483 " double y;\n" /* line 5. */
2484 ";\n"); /* line 6. */
2485 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2486 line_table_test ltt (case_);
2488 const line_map_ordinary *ord_map
2489 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2490 tmp.get_filename (), 0));
2492 linemap_line_start (line_table, 1, 100);
2494 const location_t final_line_end
2495 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2497 /* Don't attempt to run the tests if column data might be unavailable. */
2498 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2499 return;
2501 /* Test of add_location_if_nearby on the same line as the
2502 primary location. */
2504 const location_t missing_close_brace_1_39
2505 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2506 const location_t matching_open_brace_1_18
2507 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2508 gcc_rich_location richloc (missing_close_brace_1_39);
2509 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2510 ASSERT_TRUE (added);
2511 ASSERT_EQ (2, richloc.get_num_locations ());
2512 test_diagnostic_context dc;
2513 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2514 ASSERT_STREQ ("\n"
2515 " struct same_line { double x; double y; ;\n"
2516 " ~ ^\n",
2517 pp_formatted_text (dc.printer));
2520 /* Test of add_location_if_nearby on a different line to the
2521 primary location. */
2523 const location_t missing_close_brace_6_1
2524 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2525 const location_t matching_open_brace_3_1
2526 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2527 gcc_rich_location richloc (missing_close_brace_6_1);
2528 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2529 ASSERT_FALSE (added);
2530 ASSERT_EQ (1, richloc.get_num_locations ());
2534 /* Verify that we print fixits even if they only affect lines
2535 outside those covered by the ranges in the rich_location. */
2537 static void
2538 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2540 /* Create a tempfile and write some text to it.
2541 ...000000000111111111122222222223333333333.
2542 ...123456789012345678901234567890123456789. */
2543 const char *content
2544 = ("struct point { double x; double y; };\n" /* line 1. */
2545 "struct point origin = {x: 0.0,\n" /* line 2. */
2546 " y\n" /* line 3. */
2547 "\n" /* line 4. */
2548 "\n" /* line 5. */
2549 " : 0.0};\n"); /* line 6. */
2550 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2551 line_table_test ltt (case_);
2553 const line_map_ordinary *ord_map
2554 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2555 tmp.get_filename (), 0));
2557 linemap_line_start (line_table, 1, 100);
2559 const location_t final_line_end
2560 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2562 /* Don't attempt to run the tests if column data might be unavailable. */
2563 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2564 return;
2566 /* A pair of tests for modernizing the initializers to C99-style. */
2568 /* The one-liner case (line 2). */
2570 test_diagnostic_context dc;
2571 const location_t x
2572 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2573 const location_t colon
2574 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2575 rich_location richloc (line_table, colon);
2576 richloc.add_fixit_insert_before (x, ".");
2577 richloc.add_fixit_replace (colon, "=");
2578 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2579 ASSERT_STREQ ("\n"
2580 " struct point origin = {x: 0.0,\n"
2581 " ^\n"
2582 " .=\n",
2583 pp_formatted_text (dc.printer));
2586 /* The multiline case. The caret for the rich_location is on line 6;
2587 verify that insertion fixit on line 3 is still printed (and that
2588 span starts are printed due to the gap between the span at line 3
2589 and that at line 6). */
2591 test_diagnostic_context dc;
2592 const location_t y
2593 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2594 const location_t colon
2595 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2596 rich_location richloc (line_table, colon);
2597 richloc.add_fixit_insert_before (y, ".");
2598 richloc.add_fixit_replace (colon, "=");
2599 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2600 ASSERT_STREQ ("\n"
2601 "FILENAME:3:24:\n"
2602 " y\n"
2603 " .\n"
2604 "FILENAME:6:25:\n"
2605 " : 0.0};\n"
2606 " ^\n"
2607 " =\n",
2608 pp_formatted_text (dc.printer));
2613 /* Verify that fix-it hints are appropriately consolidated.
2615 If any fix-it hints in a rich_location involve locations beyond
2616 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2617 the fix-it as a whole, so there should be none.
2619 Otherwise, verify that consecutive "replace" and "remove" fix-its
2620 are merged, and that other fix-its remain separate. */
2622 static void
2623 test_fixit_consolidation (const line_table_case &case_)
2625 line_table_test ltt (case_);
2627 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2629 const location_t c10 = linemap_position_for_column (line_table, 10);
2630 const location_t c15 = linemap_position_for_column (line_table, 15);
2631 const location_t c16 = linemap_position_for_column (line_table, 16);
2632 const location_t c17 = linemap_position_for_column (line_table, 17);
2633 const location_t c20 = linemap_position_for_column (line_table, 20);
2634 const location_t c21 = linemap_position_for_column (line_table, 21);
2635 const location_t caret = c10;
2637 /* Insert + insert. */
2639 rich_location richloc (line_table, caret);
2640 richloc.add_fixit_insert_before (c10, "foo");
2641 richloc.add_fixit_insert_before (c15, "bar");
2643 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2644 /* Bogus column info for 2nd fixit, so no fixits. */
2645 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2646 else
2647 /* They should not have been merged. */
2648 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2651 /* Insert + replace. */
2653 rich_location richloc (line_table, caret);
2654 richloc.add_fixit_insert_before (c10, "foo");
2655 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2656 "bar");
2658 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2659 /* Bogus column info for 2nd fixit, so no fixits. */
2660 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2661 else
2662 /* They should not have been merged. */
2663 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2666 /* Replace + non-consecutive insert. */
2668 rich_location richloc (line_table, caret);
2669 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2670 "bar");
2671 richloc.add_fixit_insert_before (c17, "foo");
2673 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2674 /* Bogus column info for 2nd fixit, so no fixits. */
2675 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2676 else
2677 /* They should not have been merged. */
2678 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2681 /* Replace + non-consecutive replace. */
2683 rich_location richloc (line_table, caret);
2684 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2685 "foo");
2686 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2687 "bar");
2689 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2690 /* Bogus column info for 2nd fixit, so no fixits. */
2691 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2692 else
2693 /* They should not have been merged. */
2694 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2697 /* Replace + consecutive replace. */
2699 rich_location richloc (line_table, caret);
2700 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2701 "foo");
2702 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2703 "bar");
2705 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2706 /* Bogus column info for 2nd fixit, so no fixits. */
2707 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2708 else
2710 /* They should have been merged into a single "replace". */
2711 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2712 const fixit_hint *hint = richloc.get_fixit_hint (0);
2713 ASSERT_STREQ ("foobar", hint->get_string ());
2714 ASSERT_EQ (c10, hint->get_start_loc ());
2715 ASSERT_EQ (c21, hint->get_next_loc ());
2719 /* Replace + consecutive removal. */
2721 rich_location richloc (line_table, caret);
2722 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2723 "foo");
2724 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2726 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2727 /* Bogus column info for 2nd fixit, so no fixits. */
2728 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2729 else
2731 /* They should have been merged into a single replace, with the
2732 range extended to cover that of the removal. */
2733 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2734 const fixit_hint *hint = richloc.get_fixit_hint (0);
2735 ASSERT_STREQ ("foo", hint->get_string ());
2736 ASSERT_EQ (c10, hint->get_start_loc ());
2737 ASSERT_EQ (c21, hint->get_next_loc ());
2741 /* Consecutive removals. */
2743 rich_location richloc (line_table, caret);
2744 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2745 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2747 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2748 /* Bogus column info for 2nd fixit, so no fixits. */
2749 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2750 else
2752 /* They should have been merged into a single "replace-with-empty". */
2753 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2754 const fixit_hint *hint = richloc.get_fixit_hint (0);
2755 ASSERT_STREQ ("", hint->get_string ());
2756 ASSERT_EQ (c10, hint->get_start_loc ());
2757 ASSERT_EQ (c21, hint->get_next_loc ());
2762 /* Verify that the line_corrections machinery correctly prints
2763 overlapping fixit-hints. */
2765 static void
2766 test_overlapped_fixit_printing (const line_table_case &case_)
2768 /* Create a tempfile and write some text to it.
2769 ...000000000111111111122222222223333333333.
2770 ...123456789012345678901234567890123456789. */
2771 const char *content
2772 = (" foo *f = (foo *)ptr->field;\n");
2773 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2774 line_table_test ltt (case_);
2776 const line_map_ordinary *ord_map
2777 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2778 tmp.get_filename (), 0));
2780 linemap_line_start (line_table, 1, 100);
2782 const location_t final_line_end
2783 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2785 /* Don't attempt to run the tests if column data might be unavailable. */
2786 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2787 return;
2789 /* A test for converting a C-style cast to a C++-style cast. */
2790 const location_t open_paren
2791 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2792 const location_t close_paren
2793 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2794 const location_t expr_start
2795 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2796 const location_t expr_finish
2797 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2798 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2800 /* Various examples of fix-it hints that aren't themselves consolidated,
2801 but for which the *printing* may need consolidation. */
2803 /* Example where 3 fix-it hints are printed as one. */
2805 test_diagnostic_context dc;
2806 rich_location richloc (line_table, expr);
2807 richloc.add_fixit_replace (open_paren, "const_cast<");
2808 richloc.add_fixit_replace (close_paren, "> (");
2809 richloc.add_fixit_insert_after (")");
2811 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2812 ASSERT_STREQ ("\n"
2813 " foo *f = (foo *)ptr->field;\n"
2814 " ^~~~~~~~~~\n"
2815 " -----------------\n"
2816 " const_cast<foo *> (ptr->field)\n",
2817 pp_formatted_text (dc.printer));
2819 /* Unit-test the line_corrections machinery. */
2820 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2821 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2822 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2823 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2824 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2825 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2826 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2827 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2828 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2829 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2831 /* Add each hint in turn to a line_corrections instance,
2832 and verify that they are consolidated into one correction instance
2833 as expected. */
2834 line_corrections lc (tmp.get_filename (), 1);
2836 /* The first replace hint by itself. */
2837 lc.add_hint (hint_0);
2838 ASSERT_EQ (1, lc.m_corrections.length ());
2839 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2840 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2841 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2843 /* After the second replacement hint, they are printed together
2844 as a replacement (along with the text between them). */
2845 lc.add_hint (hint_1);
2846 ASSERT_EQ (1, lc.m_corrections.length ());
2847 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2848 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2849 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2851 /* After the final insertion hint, they are all printed together
2852 as a replacement (along with the text between them). */
2853 lc.add_hint (hint_2);
2854 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2855 lc.m_corrections[0]->m_text);
2856 ASSERT_EQ (1, lc.m_corrections.length ());
2857 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2858 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2861 /* Example where two are consolidated during printing. */
2863 test_diagnostic_context dc;
2864 rich_location richloc (line_table, expr);
2865 richloc.add_fixit_replace (open_paren, "CAST (");
2866 richloc.add_fixit_replace (close_paren, ") (");
2867 richloc.add_fixit_insert_after (")");
2869 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2870 ASSERT_STREQ ("\n"
2871 " foo *f = (foo *)ptr->field;\n"
2872 " ^~~~~~~~~~\n"
2873 " -\n"
2874 " CAST (-\n"
2875 " ) ( )\n",
2876 pp_formatted_text (dc.printer));
2879 /* Example where none are consolidated during printing. */
2881 test_diagnostic_context dc;
2882 rich_location richloc (line_table, expr);
2883 richloc.add_fixit_replace (open_paren, "CST (");
2884 richloc.add_fixit_replace (close_paren, ") (");
2885 richloc.add_fixit_insert_after (")");
2887 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2888 ASSERT_STREQ ("\n"
2889 " foo *f = (foo *)ptr->field;\n"
2890 " ^~~~~~~~~~\n"
2891 " -\n"
2892 " CST ( -\n"
2893 " ) ( )\n",
2894 pp_formatted_text (dc.printer));
2897 /* Example of deletion fix-it hints. */
2899 test_diagnostic_context dc;
2900 rich_location richloc (line_table, expr);
2901 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2902 source_range victim = {open_paren, close_paren};
2903 richloc.add_fixit_remove (victim);
2905 /* This case is actually handled by fixit-consolidation,
2906 rather than by line_corrections. */
2907 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2909 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2910 ASSERT_STREQ ("\n"
2911 " foo *f = (foo *)ptr->field;\n"
2912 " ^~~~~~~~~~\n"
2913 " -------\n"
2914 " (bar *)\n",
2915 pp_formatted_text (dc.printer));
2918 /* Example of deletion fix-it hints that would overlap. */
2920 test_diagnostic_context dc;
2921 rich_location richloc (line_table, expr);
2922 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2923 source_range victim = {expr_start, expr_finish};
2924 richloc.add_fixit_remove (victim);
2926 /* These fixits are not consolidated. */
2927 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2929 /* But the corrections are. */
2930 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2931 ASSERT_STREQ ("\n"
2932 " foo *f = (foo *)ptr->field;\n"
2933 " ^~~~~~~~~~\n"
2934 " -----------------\n"
2935 " (longer *)(foo *)\n",
2936 pp_formatted_text (dc.printer));
2939 /* Example of insertion fix-it hints that would overlap. */
2941 test_diagnostic_context dc;
2942 rich_location richloc (line_table, expr);
2943 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2944 richloc.add_fixit_insert_after (close_paren, "TEST");
2946 /* The first insertion is long enough that if printed naively,
2947 it would overlap with the second.
2948 Verify that they are printed as a single replacement. */
2949 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2950 ASSERT_STREQ ("\n"
2951 " foo *f = (foo *)ptr->field;\n"
2952 " ^~~~~~~~~~\n"
2953 " -------\n"
2954 " LONGER THAN THE CAST(foo *)TEST\n",
2955 pp_formatted_text (dc.printer));
2959 /* Verify that the line_corrections machinery correctly prints
2960 overlapping fixit-hints that have been added in the wrong
2961 order.
2962 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
2964 static void
2965 test_overlapped_fixit_printing_2 (const line_table_case &case_)
2967 /* Create a tempfile and write some text to it.
2968 ...000000000111111111122222222223333333333.
2969 ...123456789012345678901234567890123456789. */
2970 const char *content
2971 = ("int a5[][0][0] = { 1, 2 };\n");
2972 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2973 line_table_test ltt (case_);
2975 const line_map_ordinary *ord_map
2976 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2977 tmp.get_filename (), 0));
2979 linemap_line_start (line_table, 1, 100);
2981 const location_t final_line_end
2982 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
2984 /* Don't attempt to run the tests if column data might be unavailable. */
2985 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2986 return;
2988 const location_t col_1
2989 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2990 const location_t col_20
2991 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
2992 const location_t col_21
2993 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
2994 const location_t col_23
2995 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
2996 const location_t col_25
2997 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
2999 /* Two insertions, in the wrong order. */
3001 rich_location richloc (line_table, col_20);
3002 richloc.add_fixit_insert_before (col_23, "{");
3003 richloc.add_fixit_insert_before (col_21, "}");
3005 /* These fixits should be accepted; they can't be consolidated. */
3006 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3007 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3008 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3009 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3010 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3011 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3012 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3014 /* Verify that they're printed correctly. */
3015 test_diagnostic_context dc;
3016 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3017 ASSERT_STREQ ("\n"
3018 " int a5[][0][0] = { 1, 2 };\n"
3019 " ^\n"
3020 " } {\n",
3021 pp_formatted_text (dc.printer));
3024 /* Various overlapping insertions, some occurring "out of order"
3025 (reproducing the fix-it hints from PR c/81405). */
3027 test_diagnostic_context dc;
3028 rich_location richloc (line_table, col_20);
3030 richloc.add_fixit_insert_before (col_20, "{{");
3031 richloc.add_fixit_insert_before (col_21, "}}");
3032 richloc.add_fixit_insert_before (col_23, "{");
3033 richloc.add_fixit_insert_before (col_21, "}");
3034 richloc.add_fixit_insert_before (col_23, "{{");
3035 richloc.add_fixit_insert_before (col_25, "}");
3036 richloc.add_fixit_insert_before (col_21, "}");
3037 richloc.add_fixit_insert_before (col_1, "{");
3038 richloc.add_fixit_insert_before (col_25, "}");
3039 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3040 ASSERT_STREQ ("\n"
3041 " int a5[][0][0] = { 1, 2 };\n"
3042 " ^\n"
3043 " { -----\n"
3044 " {{1}}}}, {{{2 }}\n",
3045 pp_formatted_text (dc.printer));
3049 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
3051 static void
3052 test_fixit_insert_containing_newline (const line_table_case &case_)
3054 /* Create a tempfile and write some text to it.
3055 .........................0000000001111111.
3056 .........................1234567890123456. */
3057 const char *old_content = (" case 'a':\n" /* line 1. */
3058 " x = a;\n" /* line 2. */
3059 " case 'b':\n" /* line 3. */
3060 " x = b;\n");/* line 4. */
3062 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3063 line_table_test ltt (case_);
3064 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3066 location_t case_start = linemap_position_for_column (line_table, 5);
3067 location_t case_finish = linemap_position_for_column (line_table, 13);
3068 location_t case_loc = make_location (case_start, case_start, case_finish);
3069 location_t line_start = linemap_position_for_column (line_table, 1);
3071 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3072 return;
3074 /* Add a "break;" on a line by itself before line 3 i.e. before
3075 column 1 of line 3. */
3077 rich_location richloc (line_table, case_loc);
3078 richloc.add_fixit_insert_before (line_start, " break;\n");
3079 test_diagnostic_context dc;
3080 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3081 ASSERT_STREQ ("\n"
3082 "+ break;\n"
3083 " case 'b':\n"
3084 " ^~~~~~~~~\n",
3085 pp_formatted_text (dc.printer));
3088 /* Verify that attempts to add text with a newline fail when the
3089 insertion point is *not* at the start of a line. */
3091 rich_location richloc (line_table, case_loc);
3092 richloc.add_fixit_insert_before (case_start, "break;\n");
3093 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3094 test_diagnostic_context dc;
3095 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3096 ASSERT_STREQ ("\n"
3097 " case 'b':\n"
3098 " ^~~~~~~~~\n",
3099 pp_formatted_text (dc.printer));
3103 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3104 of the file, where the fix-it is printed in a different line-span
3105 to the primary range of the diagnostic. */
3107 static void
3108 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3110 /* Create a tempfile and write some text to it.
3111 .........................0000000001111111.
3112 .........................1234567890123456. */
3113 const char *old_content = ("test (int ch)\n" /* line 1. */
3114 "{\n" /* line 2. */
3115 " putchar (ch);\n" /* line 3. */
3116 "}\n"); /* line 4. */
3118 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3119 line_table_test ltt (case_);
3121 const line_map_ordinary *ord_map = linemap_check_ordinary
3122 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3123 linemap_line_start (line_table, 1, 100);
3125 /* The primary range is the "putchar" token. */
3126 location_t putchar_start
3127 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3128 location_t putchar_finish
3129 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3130 location_t putchar_loc
3131 = make_location (putchar_start, putchar_start, putchar_finish);
3132 rich_location richloc (line_table, putchar_loc);
3134 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3135 location_t file_start
3136 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3137 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3139 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3140 return;
3142 test_diagnostic_context dc;
3143 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3144 ASSERT_STREQ ("\n"
3145 "FILENAME:1:1:\n"
3146 "+#include <stdio.h>\n"
3147 " test (int ch)\n"
3148 "FILENAME:3:2:\n"
3149 " putchar (ch);\n"
3150 " ^~~~~~~\n",
3151 pp_formatted_text (dc.printer));
3154 /* Replacement fix-it hint containing a newline.
3155 This will fail, as newlines are only supported when inserting at the
3156 beginning of a line. */
3158 static void
3159 test_fixit_replace_containing_newline (const line_table_case &case_)
3161 /* Create a tempfile and write some text to it.
3162 .........................0000000001111.
3163 .........................1234567890123. */
3164 const char *old_content = "foo = bar ();\n";
3166 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3167 line_table_test ltt (case_);
3168 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3170 /* Replace the " = " with "\n = ", as if we were reformatting an
3171 overly long line. */
3172 location_t start = linemap_position_for_column (line_table, 4);
3173 location_t finish = linemap_position_for_column (line_table, 6);
3174 location_t loc = linemap_position_for_column (line_table, 13);
3175 rich_location richloc (line_table, loc);
3176 source_range range = source_range::from_locations (start, finish);
3177 richloc.add_fixit_replace (range, "\n =");
3179 /* Arbitrary newlines are not yet supported within fix-it hints, so
3180 the fix-it should not be displayed. */
3181 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3183 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3184 return;
3186 test_diagnostic_context dc;
3187 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3188 ASSERT_STREQ ("\n"
3189 " foo = bar ();\n"
3190 " ^\n",
3191 pp_formatted_text (dc.printer));
3194 /* Fix-it hint, attempting to delete a newline.
3195 This will fail, as we currently only support fix-it hints that
3196 affect one line at a time. */
3198 static void
3199 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3201 /* Create a tempfile and write some text to it.
3202 ..........................0000000001111.
3203 ..........................1234567890123. */
3204 const char *old_content = ("foo = bar (\n"
3205 " );\n");
3207 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3208 line_table_test ltt (case_);
3209 const line_map_ordinary *ord_map = linemap_check_ordinary
3210 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3211 linemap_line_start (line_table, 1, 100);
3213 /* Attempt to delete the " (\n...)". */
3214 location_t start
3215 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3216 location_t caret
3217 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3218 location_t finish
3219 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3220 location_t loc = make_location (caret, start, finish);
3221 rich_location richloc (line_table, loc);
3222 richloc. add_fixit_remove ();
3224 /* Fix-it hints that affect more than one line are not yet supported, so
3225 the fix-it should not be displayed. */
3226 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3228 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3229 return;
3231 test_diagnostic_context dc;
3232 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3233 ASSERT_STREQ ("\n"
3234 " foo = bar (\n"
3235 " ~^\n"
3236 " );\n"
3237 " ~ \n",
3238 pp_formatted_text (dc.printer));
3241 /* Verify that line numbers are correctly printed for the case of
3242 a multiline range in which the width of the line numbers changes
3243 (e.g. from "9" to "10"). */
3245 static void
3246 test_line_numbers_multiline_range ()
3248 /* Create a tempfile and write some text to it. */
3249 pretty_printer pp;
3250 for (int i = 0; i < 20; i++)
3251 /* .........0000000001111111.
3252 .............1234567890123456. */
3253 pp_printf (&pp, "this is line %i\n", i + 1);
3254 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3255 line_table_test ltt;
3257 const line_map_ordinary *ord_map = linemap_check_ordinary
3258 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3259 linemap_line_start (line_table, 1, 100);
3261 /* Create a multi-line location, starting at the "line" of line 9, with
3262 a caret on the "is" of line 10, finishing on the "this" line 11. */
3264 location_t start
3265 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3266 location_t caret
3267 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3268 location_t finish
3269 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3270 location_t loc = make_location (caret, start, finish);
3272 test_diagnostic_context dc;
3273 dc.show_line_numbers_p = true;
3274 gcc_rich_location richloc (loc);
3275 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3276 ASSERT_STREQ ("\n"
3277 " 9 | this is line 9\n"
3278 " | ~~~~~~\n"
3279 "10 | this is line 10\n"
3280 " | ~~~~~^~~~~~~~~~\n"
3281 "11 | this is line 11\n"
3282 " | ~~~~ \n",
3283 pp_formatted_text (dc.printer));
3286 /* Run all of the selftests within this file. */
3288 void
3289 diagnostic_show_locus_c_tests ()
3291 test_line_span ();
3292 test_num_digits ();
3294 test_layout_range_for_single_point ();
3295 test_layout_range_for_single_line ();
3296 test_layout_range_for_multiple_lines ();
3298 test_get_line_width_without_trailing_whitespace ();
3300 test_diagnostic_show_locus_unknown_location ();
3302 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3303 for_each_line_table_case (test_add_location_if_nearby);
3304 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3305 for_each_line_table_case (test_fixit_consolidation);
3306 for_each_line_table_case (test_overlapped_fixit_printing);
3307 for_each_line_table_case (test_overlapped_fixit_printing_2);
3308 for_each_line_table_case (test_fixit_insert_containing_newline);
3309 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3310 for_each_line_table_case (test_fixit_replace_containing_newline);
3311 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3313 test_line_numbers_multiline_range ();
3316 } // namespace selftest
3318 #endif /* #if CHECKING_P */