Import GCC-8 to a new vendor branch
[dragonfly.git] / contrib / gcc-8.0 / gcc / diagnostic-show-locus.c
blobbdf608a08bcad6e308549dea23dabdc46a467ef0
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 print_annotation_line (linenum_type row, const line_bounds lbounds);
255 void print_trailing_fixits (linenum_type row);
257 bool annotation_line_showed_range_p (linenum_type line, int start_column,
258 int finish_column) const;
259 void show_ruler (int max_column) const;
261 bool validate_fixit_hint_p (const fixit_hint *hint);
263 void calculate_line_spans ();
265 void print_newline ();
267 bool
268 get_state_at_point (/* Inputs. */
269 linenum_type row, int column,
270 int first_non_ws, int last_non_ws,
271 /* Outputs. */
272 point_state *out_state);
275 get_x_bound_for_row (linenum_type row, int caret_column,
276 int last_non_ws);
278 void
279 move_to_column (int *column, int dest_column);
281 private:
282 diagnostic_context *m_context;
283 pretty_printer *m_pp;
284 diagnostic_t m_diagnostic_kind;
285 location_t m_primary_loc;
286 expanded_location m_exploc;
287 colorizer m_colorizer;
288 bool m_colorize_source_p;
289 auto_vec <layout_range> m_layout_ranges;
290 auto_vec <const fixit_hint *> m_fixit_hints;
291 auto_vec <line_span> m_line_spans;
292 int m_x_offset;
295 /* Implementation of "class colorizer". */
297 /* The constructor for "colorizer". Lookup and store color codes for the
298 different kinds of things we might need to print. */
300 colorizer::colorizer (diagnostic_context *context,
301 diagnostic_t diagnostic_kind) :
302 m_context (context),
303 m_diagnostic_kind (diagnostic_kind),
304 m_current_state (STATE_NORMAL_TEXT)
306 m_range1 = get_color_by_name ("range1");
307 m_range2 = get_color_by_name ("range2");
308 m_fixit_insert = get_color_by_name ("fixit-insert");
309 m_fixit_delete = get_color_by_name ("fixit-delete");
310 m_stop_color = colorize_stop (pp_show_color (context->printer));
313 /* The destructor for "colorize". If colorization is on, print a code to
314 turn it off. */
316 colorizer::~colorizer ()
318 finish_state (m_current_state);
321 /* Update state, printing color codes if necessary if there's a state
322 change. */
324 void
325 colorizer::set_state (int new_state)
327 if (m_current_state != new_state)
329 finish_state (m_current_state);
330 m_current_state = new_state;
331 begin_state (new_state);
335 /* Turn on any colorization for STATE. */
337 void
338 colorizer::begin_state (int state)
340 switch (state)
342 case STATE_NORMAL_TEXT:
343 break;
345 case STATE_FIXIT_INSERT:
346 pp_string (m_context->printer, m_fixit_insert);
347 break;
349 case STATE_FIXIT_DELETE:
350 pp_string (m_context->printer, m_fixit_delete);
351 break;
353 case 0:
354 /* Make range 0 be the same color as the "kind" text
355 (error vs warning vs note). */
356 pp_string
357 (m_context->printer,
358 colorize_start (pp_show_color (m_context->printer),
359 diagnostic_get_color_for_kind (m_diagnostic_kind)));
360 break;
362 case 1:
363 pp_string (m_context->printer, m_range1);
364 break;
366 case 2:
367 pp_string (m_context->printer, m_range2);
368 break;
370 default:
371 /* For ranges beyond 2, alternate between color 1 and color 2. */
373 gcc_assert (state > 2);
374 pp_string (m_context->printer,
375 state % 2 ? m_range1 : m_range2);
377 break;
381 /* Turn off any colorization for STATE. */
383 void
384 colorizer::finish_state (int state)
386 if (state != STATE_NORMAL_TEXT)
387 pp_string (m_context->printer, m_stop_color);
390 /* Get the color code for NAME (or the empty string if
391 colorization is disabled). */
393 const char *
394 colorizer::get_color_by_name (const char *name)
396 return colorize_start (pp_show_color (m_context->printer), name);
399 /* Implementation of class layout_range. */
401 /* The constructor for class layout_range.
402 Initialize various layout_point fields from expanded_location
403 equivalents; we've already filtered on file. */
405 layout_range::layout_range (const expanded_location *start_exploc,
406 const expanded_location *finish_exploc,
407 bool show_caret_p,
408 const expanded_location *caret_exploc)
409 : m_start (*start_exploc),
410 m_finish (*finish_exploc),
411 m_show_caret_p (show_caret_p),
412 m_caret (*caret_exploc)
416 /* Is (column, row) within the given range?
417 We've already filtered on the file.
419 Ranges are closed (both limits are within the range).
421 Example A: a single-line range:
422 start: (col=22, line=2)
423 finish: (col=38, line=2)
425 |00000011111111112222222222333333333344444444444
426 |34567890123456789012345678901234567890123456789
427 --+-----------------------------------------------
428 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
429 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
430 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
432 Example B: a multiline range with
433 start: (col=14, line=3)
434 finish: (col=08, line=5)
436 |00000011111111112222222222333333333344444444444
437 |34567890123456789012345678901234567890123456789
438 --+-----------------------------------------------
439 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
440 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
441 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
442 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
443 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
444 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
445 --+-----------------------------------------------
447 Legend:
448 - 'b' indicates a point *before* the range
449 - 'S' indicates the start of the range
450 - 'w' indicates a point within the range
451 - 'F' indicates the finish of the range (which is
452 within it).
453 - 'a' indicates a subsequent point *after* the range. */
455 bool
456 layout_range::contains_point (linenum_type row, int column) const
458 gcc_assert (m_start.m_line <= m_finish.m_line);
459 /* ...but the equivalent isn't true for the columns;
460 consider example B in the comment above. */
462 if (row < m_start.m_line)
463 /* Points before the first line of the range are
464 outside it (corresponding to line 01 in example A
465 and lines 01 and 02 in example B above). */
466 return false;
468 if (row == m_start.m_line)
469 /* On same line as start of range (corresponding
470 to line 02 in example A and line 03 in example B). */
472 if (column < m_start.m_column)
473 /* Points on the starting line of the range, but
474 before the column in which it begins. */
475 return false;
477 if (row < m_finish.m_line)
478 /* This is a multiline range; the point
479 is within it (corresponds to line 03 in example B
480 from column 14 onwards) */
481 return true;
482 else
484 /* This is a single-line range. */
485 gcc_assert (row == m_finish.m_line);
486 return column <= m_finish.m_column;
490 /* The point is in a line beyond that containing the
491 start of the range: lines 03 onwards in example A,
492 and lines 04 onwards in example B. */
493 gcc_assert (row > m_start.m_line);
495 if (row > m_finish.m_line)
496 /* The point is beyond the final line of the range
497 (lines 03 onwards in example A, and lines 06 onwards
498 in example B). */
499 return false;
501 if (row < m_finish.m_line)
503 /* The point is in a line that's fully within a multiline
504 range (e.g. line 04 in example B). */
505 gcc_assert (m_start.m_line < m_finish.m_line);
506 return true;
509 gcc_assert (row == m_finish.m_line);
511 return column <= m_finish.m_column;
514 /* Does this layout_range contain any part of line ROW? */
516 bool
517 layout_range::intersects_line_p (linenum_type row) const
519 gcc_assert (m_start.m_line <= m_finish.m_line);
520 if (row < m_start.m_line)
521 return false;
522 if (row > m_finish.m_line)
523 return false;
524 return true;
527 #if CHECKING_P
529 /* A helper function for testing layout_range. */
531 static layout_range
532 make_range (int start_line, int start_col, int end_line, int end_col)
534 const expanded_location start_exploc
535 = {"test.c", start_line, start_col, NULL, false};
536 const expanded_location finish_exploc
537 = {"test.c", end_line, end_col, NULL, false};
538 return layout_range (&start_exploc, &finish_exploc, false,
539 &start_exploc);
542 /* Selftests for layout_range::contains_point and
543 layout_range::intersects_line_p. */
545 /* Selftest for layout_range, where the layout_range
546 is a range with start==end i.e. a single point. */
548 static void
549 test_layout_range_for_single_point ()
551 layout_range point = make_range (7, 10, 7, 10);
553 /* Tests for layout_range::contains_point. */
555 /* Before the line. */
556 ASSERT_FALSE (point.contains_point (6, 1));
558 /* On the line, but before start. */
559 ASSERT_FALSE (point.contains_point (7, 9));
561 /* At the point. */
562 ASSERT_TRUE (point.contains_point (7, 10));
564 /* On the line, after the point. */
565 ASSERT_FALSE (point.contains_point (7, 11));
567 /* After the line. */
568 ASSERT_FALSE (point.contains_point (8, 1));
570 /* Tests for layout_range::intersects_line_p. */
571 ASSERT_FALSE (point.intersects_line_p (6));
572 ASSERT_TRUE (point.intersects_line_p (7));
573 ASSERT_FALSE (point.intersects_line_p (8));
576 /* Selftest for layout_range, where the layout_range
577 is the single-line range shown as "Example A" above. */
579 static void
580 test_layout_range_for_single_line ()
582 layout_range example_a = make_range (2, 22, 2, 38);
584 /* Tests for layout_range::contains_point. */
586 /* Before the line. */
587 ASSERT_FALSE (example_a.contains_point (1, 1));
589 /* On the line, but before start. */
590 ASSERT_FALSE (example_a.contains_point (2, 21));
592 /* On the line, at the start. */
593 ASSERT_TRUE (example_a.contains_point (2, 22));
595 /* On the line, within the range. */
596 ASSERT_TRUE (example_a.contains_point (2, 23));
598 /* On the line, at the end. */
599 ASSERT_TRUE (example_a.contains_point (2, 38));
601 /* On the line, after the end. */
602 ASSERT_FALSE (example_a.contains_point (2, 39));
604 /* After the line. */
605 ASSERT_FALSE (example_a.contains_point (2, 39));
607 /* Tests for layout_range::intersects_line_p. */
608 ASSERT_FALSE (example_a.intersects_line_p (1));
609 ASSERT_TRUE (example_a.intersects_line_p (2));
610 ASSERT_FALSE (example_a.intersects_line_p (3));
613 /* Selftest for layout_range, where the layout_range
614 is the multi-line range shown as "Example B" above. */
616 static void
617 test_layout_range_for_multiple_lines ()
619 layout_range example_b = make_range (3, 14, 5, 8);
621 /* Tests for layout_range::contains_point. */
623 /* Before first line. */
624 ASSERT_FALSE (example_b.contains_point (1, 1));
626 /* On the first line, but before start. */
627 ASSERT_FALSE (example_b.contains_point (3, 13));
629 /* At the start. */
630 ASSERT_TRUE (example_b.contains_point (3, 14));
632 /* On the first line, within the range. */
633 ASSERT_TRUE (example_b.contains_point (3, 15));
635 /* On an interior line.
636 The column number should not matter; try various boundary
637 values. */
638 ASSERT_TRUE (example_b.contains_point (4, 1));
639 ASSERT_TRUE (example_b.contains_point (4, 7));
640 ASSERT_TRUE (example_b.contains_point (4, 8));
641 ASSERT_TRUE (example_b.contains_point (4, 9));
642 ASSERT_TRUE (example_b.contains_point (4, 13));
643 ASSERT_TRUE (example_b.contains_point (4, 14));
644 ASSERT_TRUE (example_b.contains_point (4, 15));
646 /* On the final line, before the end. */
647 ASSERT_TRUE (example_b.contains_point (5, 7));
649 /* On the final line, at the end. */
650 ASSERT_TRUE (example_b.contains_point (5, 8));
652 /* On the final line, after the end. */
653 ASSERT_FALSE (example_b.contains_point (5, 9));
655 /* After the line. */
656 ASSERT_FALSE (example_b.contains_point (6, 1));
658 /* Tests for layout_range::intersects_line_p. */
659 ASSERT_FALSE (example_b.intersects_line_p (2));
660 ASSERT_TRUE (example_b.intersects_line_p (3));
661 ASSERT_TRUE (example_b.intersects_line_p (4));
662 ASSERT_TRUE (example_b.intersects_line_p (5));
663 ASSERT_FALSE (example_b.intersects_line_p (6));
666 #endif /* #if CHECKING_P */
668 /* Given a source line LINE of length LINE_WIDTH, determine the width
669 without any trailing whitespace. */
671 static int
672 get_line_width_without_trailing_whitespace (const char *line, int line_width)
674 int result = line_width;
675 while (result > 0)
677 char ch = line[result - 1];
678 if (ch == ' ' || ch == '\t' || ch == '\r')
679 result--;
680 else
681 break;
683 gcc_assert (result >= 0);
684 gcc_assert (result <= line_width);
685 gcc_assert (result == 0 ||
686 (line[result - 1] != ' '
687 && line[result -1] != '\t'
688 && line[result -1] != '\r'));
689 return result;
692 #if CHECKING_P
694 /* A helper function for testing get_line_width_without_trailing_whitespace. */
696 static void
697 assert_eq (const char *line, int expected_width)
699 int actual_value
700 = get_line_width_without_trailing_whitespace (line, strlen (line));
701 ASSERT_EQ (actual_value, expected_width);
704 /* Verify that get_line_width_without_trailing_whitespace is sane for
705 various inputs. It is not required to handle newlines. */
707 static void
708 test_get_line_width_without_trailing_whitespace ()
710 assert_eq ("", 0);
711 assert_eq (" ", 0);
712 assert_eq ("\t", 0);
713 assert_eq ("\r", 0);
714 assert_eq ("hello world", 11);
715 assert_eq ("hello world ", 11);
716 assert_eq ("hello world \t\t ", 11);
717 assert_eq ("hello world\r", 11);
720 #endif /* #if CHECKING_P */
722 /* Helper function for layout's ctor, for sanitizing locations relative
723 to the primary location within a diagnostic.
725 Compare LOC_A and LOC_B to see if it makes sense to print underlines
726 connecting their expanded locations. Doing so is only guaranteed to
727 make sense if the locations share the same macro expansion "history"
728 i.e. they can be traced through the same macro expansions, eventually
729 reaching an ordinary map.
731 This may be too strong a condition, but it effectively sanitizes
732 PR c++/70105, which has an example of printing an expression where the
733 final location of the expression is in a different macro, which
734 erroneously was leading to hundreds of lines of irrelevant source
735 being printed. */
737 static bool
738 compatible_locations_p (location_t loc_a, location_t loc_b)
740 if (IS_ADHOC_LOC (loc_a))
741 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
742 if (IS_ADHOC_LOC (loc_b))
743 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
745 /* If either location is one of the special locations outside of a
746 linemap, they are only compatible if they are equal. */
747 if (loc_a < RESERVED_LOCATION_COUNT
748 || loc_b < RESERVED_LOCATION_COUNT)
749 return loc_a == loc_b;
751 const line_map *map_a = linemap_lookup (line_table, loc_a);
752 linemap_assert (map_a);
754 const line_map *map_b = linemap_lookup (line_table, loc_b);
755 linemap_assert (map_b);
757 /* Are they within the same map? */
758 if (map_a == map_b)
760 /* Are both within the same macro expansion? */
761 if (linemap_macro_expansion_map_p (map_a))
763 /* Expand each location towards the spelling location, and
764 recurse. */
765 const line_map_macro *macro_map = linemap_check_macro (map_a);
766 source_location loc_a_toward_spelling
767 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
768 macro_map,
769 loc_a);
770 source_location loc_b_toward_spelling
771 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
772 macro_map,
773 loc_b);
774 return compatible_locations_p (loc_a_toward_spelling,
775 loc_b_toward_spelling);
778 /* Otherwise they are within the same ordinary map. */
779 return true;
781 else
783 /* Within different maps. */
785 /* If either is within a macro expansion, they are incompatible. */
786 if (linemap_macro_expansion_map_p (map_a)
787 || linemap_macro_expansion_map_p (map_b))
788 return false;
790 /* Within two different ordinary maps; they are compatible iff they
791 are in the same file. */
792 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
793 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
794 return ord_map_a->to_file == ord_map_b->to_file;
798 /* Comparator for sorting fix-it hints. */
800 static int
801 fixit_cmp (const void *p_a, const void *p_b)
803 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
804 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
805 return hint_a->get_start_loc () - hint_b->get_start_loc ();
808 /* Implementation of class layout. */
810 /* Constructor for class layout.
812 Filter the ranges from the rich_location to those that we can
813 sanely print, populating m_layout_ranges and m_fixit_hints.
814 Determine the range of lines that we will print, splitting them
815 up into an ordered list of disjoint spans of contiguous line numbers.
816 Determine m_x_offset, to ensure that the primary caret
817 will fit within the max_width provided by the diagnostic_context. */
819 layout::layout (diagnostic_context * context,
820 rich_location *richloc,
821 diagnostic_t diagnostic_kind)
822 : m_context (context),
823 m_pp (context->printer),
824 m_diagnostic_kind (diagnostic_kind),
825 m_primary_loc (richloc->get_range (0)->m_loc),
826 m_exploc (richloc->get_expanded_location (0)),
827 m_colorizer (context, diagnostic_kind),
828 m_colorize_source_p (context->colorize_source_p),
829 m_layout_ranges (richloc->get_num_locations ()),
830 m_fixit_hints (richloc->get_num_fixit_hints ()),
831 m_line_spans (1 + richloc->get_num_locations ()),
832 m_x_offset (0)
834 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
836 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
837 Ignore any ranges that are awkward to handle. */
838 const location_range *loc_range = richloc->get_range (idx);
839 maybe_add_location_range (loc_range, false);
842 /* Populate m_fixit_hints, filtering to only those that are in the
843 same file. */
844 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
846 const fixit_hint *hint = richloc->get_fixit_hint (i);
847 if (validate_fixit_hint_p (hint))
848 m_fixit_hints.safe_push (hint);
851 /* Sort m_fixit_hints. */
852 m_fixit_hints.qsort (fixit_cmp);
854 /* Populate m_line_spans. */
855 calculate_line_spans ();
857 /* Adjust m_x_offset.
858 Center the primary caret to fit in max_width; all columns
859 will be adjusted accordingly. */
860 int max_width = m_context->caret_max_width;
861 int line_width;
862 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
863 &line_width);
864 if (line && m_exploc.column <= line_width)
866 int right_margin = CARET_LINE_MARGIN;
867 int column = m_exploc.column;
868 right_margin = MIN (line_width - column, right_margin);
869 right_margin = max_width - right_margin;
870 if (line_width >= max_width && column > right_margin)
871 m_x_offset = column - right_margin;
872 gcc_assert (m_x_offset >= 0);
875 if (context->show_ruler_p)
876 show_ruler (m_x_offset + max_width);
879 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
880 those that we can sanely print.
882 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
883 filtered against this layout instance's current line spans: it
884 will only be added if the location is fully within the lines
885 already specified by other locations.
887 Return true iff LOC_RANGE was added. */
889 bool
890 layout::maybe_add_location_range (const location_range *loc_range,
891 bool restrict_to_current_line_spans)
893 gcc_assert (loc_range);
895 /* Split the "range" into caret and range information. */
896 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
898 /* Expand the various locations. */
899 expanded_location start
900 = linemap_client_expand_location_to_spelling_point
901 (src_range.m_start, LOCATION_ASPECT_START);
902 expanded_location finish
903 = linemap_client_expand_location_to_spelling_point
904 (src_range.m_finish, LOCATION_ASPECT_FINISH);
905 expanded_location caret
906 = linemap_client_expand_location_to_spelling_point
907 (loc_range->m_loc, LOCATION_ASPECT_CARET);
909 /* If any part of the range isn't in the same file as the primary
910 location of this diagnostic, ignore the range. */
911 if (start.file != m_exploc.file)
912 return false;
913 if (finish.file != m_exploc.file)
914 return false;
915 if (loc_range->m_show_caret_p)
916 if (caret.file != m_exploc.file)
917 return false;
919 /* Sanitize the caret location for non-primary ranges. */
920 if (m_layout_ranges.length () > 0)
921 if (loc_range->m_show_caret_p)
922 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
923 /* Discard any non-primary ranges that can't be printed
924 sanely relative to the primary location. */
925 return false;
927 /* Everything is now known to be in the correct source file,
928 but it may require further sanitization. */
929 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
931 /* If we have a range that finishes before it starts (perhaps
932 from something built via macro expansion), printing the
933 range is likely to be nonsensical. Also, attempting to do so
934 breaks assumptions within the printing code (PR c/68473).
935 Similarly, don't attempt to print ranges if one or both ends
936 of the range aren't sane to print relative to the
937 primary location (PR c++/70105). */
938 if (start.line > finish.line
939 || !compatible_locations_p (src_range.m_start, m_primary_loc)
940 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
942 /* Is this the primary location? */
943 if (m_layout_ranges.length () == 0)
945 /* We want to print the caret for the primary location, but
946 we must sanitize away m_start and m_finish. */
947 ri.m_start = ri.m_caret;
948 ri.m_finish = ri.m_caret;
950 else
951 /* This is a non-primary range; ignore it. */
952 return false;
955 /* Potentially filter to just the lines already specified by other
956 locations. This is for use by gcc_rich_location::add_location_if_nearby.
957 The layout ctor doesn't use it, and can't because m_line_spans
958 hasn't been set up at that point. */
959 if (restrict_to_current_line_spans)
961 if (!will_show_line_p (start.line))
962 return false;
963 if (!will_show_line_p (finish.line))
964 return false;
965 if (loc_range->m_show_caret_p)
966 if (!will_show_line_p (caret.line))
967 return false;
970 /* Passed all the tests; add the range to m_layout_ranges so that
971 it will be printed. */
972 m_layout_ranges.safe_push (ri);
973 return true;
976 /* Return true iff ROW is within one of the line spans for this layout. */
978 bool
979 layout::will_show_line_p (linenum_type row) const
981 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
982 line_span_idx++)
984 const line_span *line_span = get_line_span (line_span_idx);
985 if (line_span->contains_line_p (row))
986 return true;
988 return false;
991 /* Return true iff we should print a heading when starting the
992 line span with the given index. */
994 bool
995 layout::print_heading_for_line_span_index_p (int line_span_idx) const
997 /* We print a heading for every change of line span, hence for every
998 line span after the initial one. */
999 if (line_span_idx > 0)
1000 return true;
1002 /* We also do it for the initial span if the primary location of the
1003 diagnostic is in a different span. */
1004 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1005 return true;
1007 return false;
1010 /* Get an expanded_location for the first location of interest within
1011 the given line_span.
1012 Used when printing a heading to indicate a new line span. */
1014 expanded_location
1015 layout::get_expanded_location (const line_span *line_span) const
1017 /* Whenever possible, use the caret location. */
1018 if (line_span->contains_line_p (m_exploc.line))
1019 return m_exploc;
1021 /* Otherwise, use the start of the first range that's present
1022 within the line_span. */
1023 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1025 const layout_range *lr = &m_layout_ranges[i];
1026 if (line_span->contains_line_p (lr->m_start.m_line))
1028 expanded_location exploc = m_exploc;
1029 exploc.line = lr->m_start.m_line;
1030 exploc.column = lr->m_start.m_column;
1031 return exploc;
1035 /* Otherwise, use the location of the first fixit-hint present within
1036 the line_span. */
1037 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1039 const fixit_hint *hint = m_fixit_hints[i];
1040 location_t loc = hint->get_start_loc ();
1041 expanded_location exploc = expand_location (loc);
1042 if (line_span->contains_line_p (exploc.line))
1043 return exploc;
1046 /* It should not be possible to have a line span that didn't
1047 contain any of the layout_range or fixit_hint instances. */
1048 gcc_unreachable ();
1049 return m_exploc;
1052 /* Determine if HINT is meaningful to print within this layout. */
1054 bool
1055 layout::validate_fixit_hint_p (const fixit_hint *hint)
1057 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1058 return false;
1059 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1060 return false;
1062 return true;
1065 /* Determine the range of lines affected by HINT.
1066 This assumes that HINT has already been filtered by
1067 validate_fixit_hint_p, and so affects the correct source file. */
1069 static line_span
1070 get_line_span_for_fixit_hint (const fixit_hint *hint)
1072 gcc_assert (hint);
1073 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1074 LOCATION_LINE (hint->get_next_loc ()));
1077 /* We want to print the pertinent source code at a diagnostic. The
1078 rich_location can contain multiple locations. This will have been
1079 filtered into m_exploc (the caret for the primary location) and
1080 m_layout_ranges, for those ranges within the same source file.
1082 We will print a subset of the lines within the source file in question,
1083 as a collection of "spans" of lines.
1085 This function populates m_line_spans with an ordered, disjoint list of
1086 the line spans of interest.
1088 For example, if the primary caret location is on line 7, with ranges
1089 covering lines 5-6 and lines 9-12:
1092 005 |RANGE 0
1093 006 |RANGE 0
1094 007 |PRIMARY CARET
1096 009 |RANGE 1
1097 010 |RANGE 1
1098 011 |RANGE 1
1099 012 |RANGE 1
1102 then we want two spans: lines 5-7 and lines 9-12. */
1104 void
1105 layout::calculate_line_spans ()
1107 /* This should only be called once, by the ctor. */
1108 gcc_assert (m_line_spans.length () == 0);
1110 /* Populate tmp_spans with individual spans, for each of
1111 m_exploc, and for m_layout_ranges. */
1112 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1113 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1114 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1116 const layout_range *lr = &m_layout_ranges[i];
1117 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1118 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1119 lr->m_finish.m_line));
1122 /* Also add spans for any fix-it hints, in case they cover other lines. */
1123 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1125 const fixit_hint *hint = m_fixit_hints[i];
1126 gcc_assert (hint);
1127 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1130 /* Sort them. */
1131 tmp_spans.qsort(line_span::comparator);
1133 /* Now iterate through tmp_spans, copying into m_line_spans, and
1134 combining where possible. */
1135 gcc_assert (tmp_spans.length () > 0);
1136 m_line_spans.safe_push (tmp_spans[0]);
1137 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1139 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1140 const line_span *next = &tmp_spans[i];
1141 gcc_assert (next->m_first_line >= current->m_first_line);
1142 if (next->m_first_line <= current->m_last_line + 1)
1144 /* We can merge them. */
1145 if (next->m_last_line > current->m_last_line)
1146 current->m_last_line = next->m_last_line;
1148 else
1150 /* No merger possible. */
1151 m_line_spans.safe_push (*next);
1155 /* Verify the result, in m_line_spans. */
1156 gcc_assert (m_line_spans.length () > 0);
1157 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1159 const line_span *prev = &m_line_spans[i - 1];
1160 const line_span *next = &m_line_spans[i];
1161 /* The individual spans must be sane. */
1162 gcc_assert (prev->m_first_line <= prev->m_last_line);
1163 gcc_assert (next->m_first_line <= next->m_last_line);
1164 /* The spans must be ordered. */
1165 gcc_assert (prev->m_first_line < next->m_first_line);
1166 /* There must be a gap of at least one line between separate spans. */
1167 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1171 /* Print line ROW of source code, potentially colorized at any ranges, and
1172 populate *LBOUNDS_OUT.
1173 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1174 is its width. */
1176 void
1177 layout::print_source_line (linenum_type row, const char *line, int line_width,
1178 line_bounds *lbounds_out)
1180 m_colorizer.set_normal_text ();
1182 /* We will stop printing the source line at any trailing
1183 whitespace. */
1184 line_width = get_line_width_without_trailing_whitespace (line,
1185 line_width);
1186 line += m_x_offset;
1188 pp_space (m_pp);
1189 int first_non_ws = INT_MAX;
1190 int last_non_ws = 0;
1191 int column;
1192 for (column = 1 + m_x_offset; column <= line_width; column++)
1194 /* Assuming colorization is enabled for the caret and underline
1195 characters, we may also colorize the associated characters
1196 within the source line.
1198 For frontends that generate range information, we color the
1199 associated characters in the source line the same as the
1200 carets and underlines in the annotation line, to make it easier
1201 for the reader to see the pertinent code.
1203 For frontends that only generate carets, we don't colorize the
1204 characters above them, since this would look strange (e.g.
1205 colorizing just the first character in a token). */
1206 if (m_colorize_source_p)
1208 bool in_range_p;
1209 point_state state;
1210 in_range_p = get_state_at_point (row, column,
1211 0, INT_MAX,
1212 &state);
1213 if (in_range_p)
1214 m_colorizer.set_range (state.range_idx);
1215 else
1216 m_colorizer.set_normal_text ();
1218 char c = *line;
1219 if (c == '\0' || c == '\t' || c == '\r')
1220 c = ' ';
1221 if (c != ' ')
1223 last_non_ws = column;
1224 if (first_non_ws == INT_MAX)
1225 first_non_ws = column;
1227 pp_character (m_pp, c);
1228 line++;
1230 print_newline ();
1232 lbounds_out->m_first_non_ws = first_non_ws;
1233 lbounds_out->m_last_non_ws = last_non_ws;
1236 /* Determine if we should print an annotation line for ROW.
1237 i.e. if any of m_layout_ranges contains ROW. */
1239 bool
1240 layout::should_print_annotation_line_p (linenum_type row) const
1242 layout_range *range;
1243 int i;
1244 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1245 if (range->intersects_line_p (row))
1246 return true;
1247 return false;
1250 /* Print a line consisting of the caret/underlines for the given
1251 source line. */
1253 void
1254 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1256 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1257 lbounds.m_last_non_ws);
1259 pp_space (m_pp);
1260 for (int column = 1 + m_x_offset; column < x_bound; column++)
1262 bool in_range_p;
1263 point_state state;
1264 in_range_p = get_state_at_point (row, column,
1265 lbounds.m_first_non_ws,
1266 lbounds.m_last_non_ws,
1267 &state);
1268 if (in_range_p)
1270 /* Within a range. Draw either the caret or an underline. */
1271 m_colorizer.set_range (state.range_idx);
1272 if (state.draw_caret_p)
1274 /* Draw the caret. */
1275 char caret_char;
1276 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1277 caret_char = m_context->caret_chars[state.range_idx];
1278 else
1279 caret_char = '^';
1280 pp_character (m_pp, caret_char);
1282 else
1283 pp_character (m_pp, '~');
1285 else
1287 /* Not in a range. */
1288 m_colorizer.set_normal_text ();
1289 pp_character (m_pp, ' ');
1292 print_newline ();
1295 /* If there are any fixit hints inserting new lines before source line ROW,
1296 print them.
1298 They are printed on lines of their own, before the source line
1299 itself, with a leading '+'. */
1301 void
1302 layout::print_leading_fixits (linenum_type row)
1304 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1306 const fixit_hint *hint = m_fixit_hints[i];
1308 if (!hint->ends_with_newline_p ())
1309 /* Not a newline fixit; print it in print_trailing_fixits. */
1310 continue;
1312 gcc_assert (hint->insertion_p ());
1314 if (hint->affects_line_p (m_exploc.file, row))
1316 /* Printing the '+' with normal colorization
1317 and the inserted line with "insert" colorization
1318 helps them stand out from each other, and from
1319 the surrounding text. */
1320 m_colorizer.set_normal_text ();
1321 pp_character (m_pp, '+');
1322 m_colorizer.set_fixit_insert ();
1323 /* Print all but the trailing newline of the fix-it hint.
1324 We have to print the newline separately to avoid
1325 getting additional pp prefixes printed. */
1326 for (size_t i = 0; i < hint->get_length () - 1; i++)
1327 pp_character (m_pp, hint->get_string ()[i]);
1328 m_colorizer.set_normal_text ();
1329 pp_newline (m_pp);
1334 /* Subroutine of layout::print_trailing_fixits.
1336 Determine if the annotation line printed for LINE contained
1337 the exact range from START_COLUMN to FINISH_COLUMN. */
1339 bool
1340 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1341 int finish_column) const
1343 layout_range *range;
1344 int i;
1345 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1346 if (range->m_start.m_line == line
1347 && range->m_start.m_column == start_column
1348 && range->m_finish.m_line == line
1349 && range->m_finish.m_column == finish_column)
1350 return true;
1351 return false;
1354 /* Classes for printing trailing fix-it hints i.e. those that
1355 don't add new lines.
1357 For insertion, these can look like:
1359 new_text
1361 For replacement, these can look like:
1363 ------------- : underline showing affected range
1364 new_text
1366 For deletion, these can look like:
1368 ------------- : underline showing affected range
1370 This can become confusing if they overlap, and so we need
1371 to do some preprocessing to decide what to print.
1372 We use the list of fixit_hint instances affecting the line
1373 to build a list of "correction" instances, and print the
1374 latter.
1376 For example, consider a set of fix-its for converting
1377 a C-style cast to a C++ const_cast.
1379 Given:
1381 ..000000000111111111122222222223333333333.
1382 ..123456789012345678901234567890123456789.
1383 foo *f = (foo *)ptr->field;
1384 ^~~~~
1386 and the fix-it hints:
1387 - replace col 10 (the open paren) with "const_cast<"
1388 - replace col 16 (the close paren) with "> ("
1389 - insert ")" before col 27
1391 then we would get odd-looking output:
1393 foo *f = (foo *)ptr->field;
1394 ^~~~~
1396 const_cast<
1398 > ( )
1400 It would be better to detect when fixit hints are going to
1401 overlap (those that require new lines), and to consolidate
1402 the printing of such fixits, giving something like:
1404 foo *f = (foo *)ptr->field;
1405 ^~~~~
1406 -----------------
1407 const_cast<foo *> (ptr->field)
1409 This works by detecting when the printing would overlap, and
1410 effectively injecting no-op replace hints into the gaps between
1411 such fix-its, so that the printing joins up.
1413 In the above example, the overlap of:
1414 - replace col 10 (the open paren) with "const_cast<"
1415 and:
1416 - replace col 16 (the close paren) with "> ("
1417 is fixed by injecting a no-op:
1418 - replace cols 11-15 with themselves ("foo *")
1419 and consolidating these, making:
1420 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1421 i.e.:
1422 - replace cols 10-16 with "const_cast<foo *> ("
1424 This overlaps with the final fix-it hint:
1425 - insert ")" before col 27
1426 and so we repeat the consolidation process, by injecting
1427 a no-op:
1428 - replace cols 17-26 with themselves ("ptr->field")
1429 giving:
1430 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1431 i.e.:
1432 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1434 and is thus printed as desired. */
1436 /* A range of columns within a line. */
1438 struct column_range
1440 column_range (int start_, int finish_) : start (start_), finish (finish_)
1442 /* We must have either a range, or an insertion. */
1443 gcc_assert (start <= finish || finish == start - 1);
1446 bool operator== (const column_range &other) const
1448 return start == other.start && finish == other.finish;
1451 int start;
1452 int finish;
1455 /* Get the range of columns that HINT would affect. */
1457 static column_range
1458 get_affected_columns (const fixit_hint *hint)
1460 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1461 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1463 return column_range (start_column, finish_column);
1466 /* Get the range of columns that would be printed for HINT. */
1468 static column_range
1469 get_printed_columns (const fixit_hint *hint)
1471 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1472 int final_hint_column = start_column + hint->get_length () - 1;
1473 if (hint->insertion_p ())
1475 return column_range (start_column, final_hint_column);
1477 else
1479 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1481 return column_range (start_column,
1482 MAX (finish_column, final_hint_column));
1486 /* A struct capturing the bounds of a buffer, to allow for run-time
1487 bounds-checking in a checked build. */
1489 struct char_span
1491 char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {}
1493 char_span subspan (int offset, int n_elts)
1495 gcc_assert (offset >= 0);
1496 gcc_assert (offset < (int)m_n_elts);
1497 gcc_assert (n_elts >= 0);
1498 gcc_assert (offset + n_elts <= (int)m_n_elts);
1499 return char_span (m_ptr + offset, n_elts);
1502 const char *m_ptr;
1503 size_t m_n_elts;
1506 /* A correction on a particular line.
1507 This describes a plan for how to print one or more fixit_hint
1508 instances that affected the line, potentially consolidating hints
1509 into corrections to make the result easier for the user to read. */
1511 struct correction
1513 correction (column_range affected_columns,
1514 column_range printed_columns,
1515 const char *new_text, size_t new_text_len)
1516 : m_affected_columns (affected_columns),
1517 m_printed_columns (printed_columns),
1518 m_text (xstrdup (new_text)),
1519 m_len (new_text_len),
1520 m_alloc_sz (new_text_len + 1)
1524 ~correction () { free (m_text); }
1526 bool insertion_p () const
1528 return m_affected_columns.start == m_affected_columns.finish + 1;
1531 void ensure_capacity (size_t len);
1532 void ensure_terminated ();
1534 void overwrite (int dst_offset, const char_span &src_span)
1536 gcc_assert (dst_offset >= 0);
1537 gcc_assert (dst_offset + src_span.m_n_elts < m_alloc_sz);
1538 memcpy (m_text + dst_offset, src_span.m_ptr,
1539 src_span.m_n_elts);
1542 /* If insert, then start: the column before which the text
1543 is to be inserted, and finish is offset by the length of
1544 the replacement.
1545 If replace, then the range of columns affected. */
1546 column_range m_affected_columns;
1548 /* If insert, then start: the column before which the text
1549 is to be inserted, and finish is offset by the length of
1550 the replacement.
1551 If replace, then the range of columns affected. */
1552 column_range m_printed_columns;
1554 /* The text to be inserted/used as replacement. */
1555 char *m_text;
1556 size_t m_len;
1557 size_t m_alloc_sz;
1560 /* Ensure that m_text can hold a string of length LEN
1561 (plus 1 for 0-termination). */
1563 void
1564 correction::ensure_capacity (size_t len)
1566 /* Allow 1 extra byte for 0-termination. */
1567 if (m_alloc_sz < (len + 1))
1569 size_t new_alloc_sz = (len + 1) * 2;
1570 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1571 m_alloc_sz = new_alloc_sz;
1575 /* Ensure that m_text is 0-terminated. */
1577 void
1578 correction::ensure_terminated ()
1580 /* 0-terminate the buffer. */
1581 gcc_assert (m_len < m_alloc_sz);
1582 m_text[m_len] = '\0';
1585 /* A list of corrections affecting a particular line.
1586 This is used by layout::print_trailing_fixits for planning
1587 how to print the fix-it hints affecting the line. */
1589 struct line_corrections
1591 line_corrections (const char *filename, linenum_type row)
1592 : m_filename (filename), m_row (row)
1594 ~line_corrections ();
1596 void add_hint (const fixit_hint *hint);
1598 const char *m_filename;
1599 linenum_type m_row;
1600 auto_vec <correction *> m_corrections;
1603 /* struct line_corrections. */
1605 line_corrections::~line_corrections ()
1607 unsigned i;
1608 correction *c;
1609 FOR_EACH_VEC_ELT (m_corrections, i, c)
1610 delete c;
1613 /* A struct wrapping a particular source line, allowing
1614 run-time bounds-checking of accesses in a checked build. */
1616 struct source_line
1618 source_line (const char *filename, int line);
1620 char_span as_span () { return char_span (chars, width); }
1622 const char *chars;
1623 int width;
1626 /* source_line's ctor. */
1628 source_line::source_line (const char *filename, int line)
1630 chars = location_get_source_line (filename, line, &width);
1633 /* Add HINT to the corrections for this line.
1634 Attempt to consolidate nearby hints so that they will not
1635 overlap with printed. */
1637 void
1638 line_corrections::add_hint (const fixit_hint *hint)
1640 column_range affected_columns = get_affected_columns (hint);
1641 column_range printed_columns = get_printed_columns (hint);
1643 /* Potentially consolidate. */
1644 if (!m_corrections.is_empty ())
1646 correction *last_correction
1647 = m_corrections[m_corrections.length () - 1];
1649 /* The following consolidation code assumes that the fix-it hints
1650 have been sorted by start (done within layout's ctor). */
1651 gcc_assert (affected_columns.start
1652 >= last_correction->m_affected_columns.start);
1653 gcc_assert (printed_columns.start
1654 >= last_correction->m_printed_columns.start);
1656 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1658 /* We have two hints for which the printed forms of the hints
1659 would touch or overlap, so we need to consolidate them to avoid
1660 confusing the user.
1661 Attempt to inject a "replace" correction from immediately
1662 after the end of the last hint to immediately before the start
1663 of the next hint. */
1664 column_range between (last_correction->m_affected_columns.finish + 1,
1665 printed_columns.start - 1);
1667 /* Try to read the source. */
1668 source_line line (m_filename, m_row);
1669 if (line.chars && between.finish < line.width)
1671 /* Consolidate into the last correction:
1672 add a no-op "replace" of the "between" text, and
1673 add the text from the new hint. */
1674 int old_len = last_correction->m_len;
1675 gcc_assert (old_len >= 0);
1676 int between_len = between.finish + 1 - between.start;
1677 gcc_assert (between_len >= 0);
1678 int new_len = old_len + between_len + hint->get_length ();
1679 gcc_assert (new_len >= 0);
1680 last_correction->ensure_capacity (new_len);
1681 last_correction->overwrite
1682 (old_len,
1683 line.as_span ().subspan (between.start - 1,
1684 between.finish + 1 - between.start));
1685 last_correction->overwrite (old_len + between_len,
1686 char_span (hint->get_string (),
1687 hint->get_length ()));
1688 last_correction->m_len = new_len;
1689 last_correction->ensure_terminated ();
1690 last_correction->m_affected_columns.finish
1691 = affected_columns.finish;
1692 last_correction->m_printed_columns.finish
1693 += between_len + hint->get_length ();
1694 return;
1699 /* If no consolidation happened, add a new correction instance. */
1700 m_corrections.safe_push (new correction (affected_columns,
1701 printed_columns,
1702 hint->get_string (),
1703 hint->get_length ()));
1706 /* If there are any fixit hints on source line ROW, print them.
1707 They are printed in order, attempting to combine them onto lines, but
1708 starting new lines if necessary.
1709 Fix-it hints that insert new lines are handled separately,
1710 in layout::print_leading_fixits. */
1712 void
1713 layout::print_trailing_fixits (linenum_type row)
1715 /* Build a list of correction instances for the line,
1716 potentially consolidating hints (for the sake of readability). */
1717 line_corrections corrections (m_exploc.file, row);
1718 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1720 const fixit_hint *hint = m_fixit_hints[i];
1722 /* Newline fixits are handled by layout::print_leading_fixits. */
1723 if (hint->ends_with_newline_p ())
1724 continue;
1726 if (hint->affects_line_p (m_exploc.file, row))
1727 corrections.add_hint (hint);
1730 /* Now print the corrections. */
1731 unsigned i;
1732 correction *c;
1733 int column = m_x_offset;
1735 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1737 /* For now we assume each fixit hint can only touch one line. */
1738 if (c->insertion_p ())
1740 /* This assumes the insertion just affects one line. */
1741 int start_column = c->m_printed_columns.start;
1742 move_to_column (&column, start_column);
1743 m_colorizer.set_fixit_insert ();
1744 pp_string (m_pp, c->m_text);
1745 m_colorizer.set_normal_text ();
1746 column += c->m_len;
1748 else
1750 /* If the range of the replacement wasn't printed in the
1751 annotation line, then print an extra underline to
1752 indicate exactly what is being replaced.
1753 Always show it for removals. */
1754 int start_column = c->m_affected_columns.start;
1755 int finish_column = c->m_affected_columns.finish;
1756 if (!annotation_line_showed_range_p (row, start_column,
1757 finish_column)
1758 || c->m_len == 0)
1760 move_to_column (&column, start_column);
1761 m_colorizer.set_fixit_delete ();
1762 for (; column <= finish_column; column++)
1763 pp_character (m_pp, '-');
1764 m_colorizer.set_normal_text ();
1766 /* Print the replacement text. REPLACE also covers
1767 removals, so only do this extra work (potentially starting
1768 a new line) if we have actual replacement text. */
1769 if (c->m_len > 0)
1771 move_to_column (&column, start_column);
1772 m_colorizer.set_fixit_insert ();
1773 pp_string (m_pp, c->m_text);
1774 m_colorizer.set_normal_text ();
1775 column += c->m_len;
1780 /* Add a trailing newline, if necessary. */
1781 move_to_column (&column, 0);
1784 /* Disable any colorization and emit a newline. */
1786 void
1787 layout::print_newline ()
1789 m_colorizer.set_normal_text ();
1790 pp_newline (m_pp);
1793 /* Return true if (ROW/COLUMN) is within a range of the layout.
1794 If it returns true, OUT_STATE is written to, with the
1795 range index, and whether we should draw the caret at
1796 (ROW/COLUMN) (as opposed to an underline). */
1798 bool
1799 layout::get_state_at_point (/* Inputs. */
1800 linenum_type row, int column,
1801 int first_non_ws, int last_non_ws,
1802 /* Outputs. */
1803 point_state *out_state)
1805 layout_range *range;
1806 int i;
1807 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1809 if (range->contains_point (row, column))
1811 out_state->range_idx = i;
1813 /* Are we at the range's caret? is it visible? */
1814 out_state->draw_caret_p = false;
1815 if (range->m_show_caret_p
1816 && row == range->m_caret.m_line
1817 && column == range->m_caret.m_column)
1818 out_state->draw_caret_p = true;
1820 /* Within a multiline range, don't display any underline
1821 in any leading or trailing whitespace on a line.
1822 We do display carets, however. */
1823 if (!out_state->draw_caret_p)
1824 if (column < first_non_ws || column > last_non_ws)
1825 return false;
1827 /* We are within a range. */
1828 return true;
1832 return false;
1835 /* Helper function for use by layout::print_line when printing the
1836 annotation line under the source line.
1837 Get the column beyond the rightmost one that could contain a caret or
1838 range marker, given that we stop rendering at trailing whitespace.
1839 ROW is the source line within the given file.
1840 CARET_COLUMN is the column of range 0's caret.
1841 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1842 character of source (as determined when printing the source line). */
1845 layout::get_x_bound_for_row (linenum_type row, int caret_column,
1846 int last_non_ws_column)
1848 int result = caret_column + 1;
1850 layout_range *range;
1851 int i;
1852 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1854 if (row >= range->m_start.m_line)
1856 if (range->m_finish.m_line == row)
1858 /* On the final line within a range; ensure that
1859 we render up to the end of the range. */
1860 if (result <= range->m_finish.m_column)
1861 result = range->m_finish.m_column + 1;
1863 else if (row < range->m_finish.m_line)
1865 /* Within a multiline range; ensure that we render up to the
1866 last non-whitespace column. */
1867 if (result <= last_non_ws_column)
1868 result = last_non_ws_column + 1;
1873 return result;
1876 /* Given *COLUMN as an x-coordinate, print spaces to position
1877 successive output at DEST_COLUMN, printing a newline if necessary,
1878 and updating *COLUMN. */
1880 void
1881 layout::move_to_column (int *column, int dest_column)
1883 /* Start a new line if we need to. */
1884 if (*column > dest_column)
1886 print_newline ();
1887 *column = m_x_offset;
1890 while (*column < dest_column)
1892 pp_space (m_pp);
1893 (*column)++;
1897 /* For debugging layout issues, render a ruler giving column numbers
1898 (after the 1-column indent). */
1900 void
1901 layout::show_ruler (int max_column) const
1903 /* Hundreds. */
1904 if (max_column > 99)
1906 pp_space (m_pp);
1907 for (int column = 1 + m_x_offset; column <= max_column; column++)
1908 if (column % 10 == 0)
1909 pp_character (m_pp, '0' + (column / 100) % 10);
1910 else
1911 pp_space (m_pp);
1912 pp_newline (m_pp);
1915 /* Tens. */
1916 pp_space (m_pp);
1917 for (int column = 1 + m_x_offset; column <= max_column; column++)
1918 if (column % 10 == 0)
1919 pp_character (m_pp, '0' + (column / 10) % 10);
1920 else
1921 pp_space (m_pp);
1922 pp_newline (m_pp);
1924 /* Units. */
1925 pp_space (m_pp);
1926 for (int column = 1 + m_x_offset; column <= max_column; column++)
1927 pp_character (m_pp, '0' + (column % 10));
1928 pp_newline (m_pp);
1931 /* Print leading fix-its (for new lines inserted before the source line)
1932 then the source line, followed by an annotation line
1933 consisting of any caret/underlines, then any fixits.
1934 If the source line can't be read, print nothing. */
1935 void
1936 layout::print_line (linenum_type row)
1938 int line_width;
1939 const char *line = location_get_source_line (m_exploc.file, row,
1940 &line_width);
1941 if (!line)
1942 return;
1944 line_bounds lbounds;
1945 print_leading_fixits (row);
1946 print_source_line (row, line, line_width, &lbounds);
1947 if (should_print_annotation_line_p (row))
1948 print_annotation_line (row, lbounds);
1949 print_trailing_fixits (row);
1952 } /* End of anonymous namespace. */
1954 /* If LOC is within the spans of lines that will already be printed for
1955 this gcc_rich_location, then add it as a secondary location and return true.
1957 Otherwise return false. */
1959 bool
1960 gcc_rich_location::add_location_if_nearby (location_t loc)
1962 /* Use the layout location-handling logic to sanitize LOC,
1963 filtering it to the current line spans within a temporary
1964 layout instance. */
1965 layout layout (global_dc, this, DK_ERROR);
1966 location_range loc_range;
1967 loc_range.m_loc = loc;
1968 loc_range.m_show_caret_p = false;
1969 if (!layout.maybe_add_location_range (&loc_range, true))
1970 return false;
1972 add_range (loc, false);
1973 return true;
1976 /* Print the physical source code corresponding to the location of
1977 this diagnostic, with additional annotations. */
1979 void
1980 diagnostic_show_locus (diagnostic_context * context,
1981 rich_location *richloc,
1982 diagnostic_t diagnostic_kind)
1984 pp_newline (context->printer);
1986 location_t loc = richloc->get_loc ();
1987 /* Do nothing if source-printing has been disabled. */
1988 if (!context->show_caret)
1989 return;
1991 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1992 if (loc <= BUILTINS_LOCATION)
1993 return;
1995 /* Don't print the same source location twice in a row, unless we have
1996 fix-it hints. */
1997 if (loc == context->last_location
1998 && richloc->get_num_fixit_hints () == 0)
1999 return;
2001 context->last_location = loc;
2003 const char *saved_prefix = pp_get_prefix (context->printer);
2004 pp_set_prefix (context->printer, NULL);
2006 layout layout (context, richloc, diagnostic_kind);
2007 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2008 line_span_idx++)
2010 const line_span *line_span = layout.get_line_span (line_span_idx);
2011 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2013 expanded_location exploc = layout.get_expanded_location (line_span);
2014 context->start_span (context, exploc);
2016 linenum_type last_line = line_span->get_last_line ();
2017 for (linenum_type row = line_span->get_first_line ();
2018 row <= last_line; row++)
2019 layout.print_line (row);
2022 pp_set_prefix (context->printer, saved_prefix);
2025 #if CHECKING_P
2027 namespace selftest {
2029 /* Selftests for diagnostic_show_locus. */
2031 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2033 static void
2034 test_diagnostic_show_locus_unknown_location ()
2036 test_diagnostic_context dc;
2037 rich_location richloc (line_table, UNKNOWN_LOCATION);
2038 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2039 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2042 /* Verify that diagnostic_show_locus works sanely for various
2043 single-line cases.
2045 All of these work on the following 1-line source file:
2046 .0000000001111111
2047 .1234567890123456
2048 "foo = bar.field;\n"
2049 which is set up by test_diagnostic_show_locus_one_liner and calls
2050 them. */
2052 /* Just a caret. */
2054 static void
2055 test_one_liner_simple_caret ()
2057 test_diagnostic_context dc;
2058 location_t caret = linemap_position_for_column (line_table, 10);
2059 rich_location richloc (line_table, caret);
2060 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2061 ASSERT_STREQ ("\n"
2062 " foo = bar.field;\n"
2063 " ^\n",
2064 pp_formatted_text (dc.printer));
2067 /* Caret and range. */
2069 static void
2070 test_one_liner_caret_and_range ()
2072 test_diagnostic_context dc;
2073 location_t caret = linemap_position_for_column (line_table, 10);
2074 location_t start = linemap_position_for_column (line_table, 7);
2075 location_t finish = linemap_position_for_column (line_table, 15);
2076 location_t loc = make_location (caret, start, finish);
2077 rich_location richloc (line_table, loc);
2078 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2079 ASSERT_STREQ ("\n"
2080 " foo = bar.field;\n"
2081 " ~~~^~~~~~\n",
2082 pp_formatted_text (dc.printer));
2085 /* Multiple ranges and carets. */
2087 static void
2088 test_one_liner_multiple_carets_and_ranges ()
2090 test_diagnostic_context dc;
2091 location_t foo
2092 = make_location (linemap_position_for_column (line_table, 2),
2093 linemap_position_for_column (line_table, 1),
2094 linemap_position_for_column (line_table, 3));
2095 dc.caret_chars[0] = 'A';
2097 location_t bar
2098 = make_location (linemap_position_for_column (line_table, 8),
2099 linemap_position_for_column (line_table, 7),
2100 linemap_position_for_column (line_table, 9));
2101 dc.caret_chars[1] = 'B';
2103 location_t field
2104 = make_location (linemap_position_for_column (line_table, 13),
2105 linemap_position_for_column (line_table, 11),
2106 linemap_position_for_column (line_table, 15));
2107 dc.caret_chars[2] = 'C';
2109 rich_location richloc (line_table, foo);
2110 richloc.add_range (bar, true);
2111 richloc.add_range (field, true);
2112 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2113 ASSERT_STREQ ("\n"
2114 " foo = bar.field;\n"
2115 " ~A~ ~B~ ~~C~~\n",
2116 pp_formatted_text (dc.printer));
2119 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2121 static void
2122 test_one_liner_fixit_insert_before ()
2124 test_diagnostic_context dc;
2125 location_t caret = linemap_position_for_column (line_table, 7);
2126 rich_location richloc (line_table, caret);
2127 richloc.add_fixit_insert_before ("&");
2128 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2129 ASSERT_STREQ ("\n"
2130 " foo = bar.field;\n"
2131 " ^\n"
2132 " &\n",
2133 pp_formatted_text (dc.printer));
2136 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2138 static void
2139 test_one_liner_fixit_insert_after ()
2141 test_diagnostic_context dc;
2142 location_t start = linemap_position_for_column (line_table, 1);
2143 location_t finish = linemap_position_for_column (line_table, 3);
2144 location_t foo = make_location (start, start, finish);
2145 rich_location richloc (line_table, foo);
2146 richloc.add_fixit_insert_after ("[0]");
2147 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2148 ASSERT_STREQ ("\n"
2149 " foo = bar.field;\n"
2150 " ^~~\n"
2151 " [0]\n",
2152 pp_formatted_text (dc.printer));
2155 /* Removal fix-it hint: removal of the ".field". */
2157 static void
2158 test_one_liner_fixit_remove ()
2160 test_diagnostic_context dc;
2161 location_t start = linemap_position_for_column (line_table, 10);
2162 location_t finish = linemap_position_for_column (line_table, 15);
2163 location_t dot = make_location (start, start, finish);
2164 rich_location richloc (line_table, dot);
2165 richloc.add_fixit_remove ();
2166 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2167 ASSERT_STREQ ("\n"
2168 " foo = bar.field;\n"
2169 " ^~~~~~\n"
2170 " ------\n",
2171 pp_formatted_text (dc.printer));
2174 /* Replace fix-it hint: replacing "field" with "m_field". */
2176 static void
2177 test_one_liner_fixit_replace ()
2179 test_diagnostic_context dc;
2180 location_t start = linemap_position_for_column (line_table, 11);
2181 location_t finish = linemap_position_for_column (line_table, 15);
2182 location_t field = make_location (start, start, finish);
2183 rich_location richloc (line_table, field);
2184 richloc.add_fixit_replace ("m_field");
2185 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2186 ASSERT_STREQ ("\n"
2187 " foo = bar.field;\n"
2188 " ^~~~~\n"
2189 " m_field\n",
2190 pp_formatted_text (dc.printer));
2193 /* Replace fix-it hint: replacing "field" with "m_field",
2194 but where the caret was elsewhere. */
2196 static void
2197 test_one_liner_fixit_replace_non_equal_range ()
2199 test_diagnostic_context dc;
2200 location_t equals = linemap_position_for_column (line_table, 5);
2201 location_t start = linemap_position_for_column (line_table, 11);
2202 location_t finish = linemap_position_for_column (line_table, 15);
2203 rich_location richloc (line_table, equals);
2204 source_range range;
2205 range.m_start = start;
2206 range.m_finish = finish;
2207 richloc.add_fixit_replace (range, "m_field");
2208 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2209 /* The replacement range is not indicated in the annotation line, so
2210 it should be indicated via an additional underline. */
2211 ASSERT_STREQ ("\n"
2212 " foo = bar.field;\n"
2213 " ^\n"
2214 " -----\n"
2215 " m_field\n",
2216 pp_formatted_text (dc.printer));
2219 /* Replace fix-it hint: replacing "field" with "m_field",
2220 where the caret was elsewhere, but where a secondary range
2221 exactly covers "field". */
2223 static void
2224 test_one_liner_fixit_replace_equal_secondary_range ()
2226 test_diagnostic_context dc;
2227 location_t equals = linemap_position_for_column (line_table, 5);
2228 location_t start = linemap_position_for_column (line_table, 11);
2229 location_t finish = linemap_position_for_column (line_table, 15);
2230 rich_location richloc (line_table, equals);
2231 location_t field = make_location (start, start, finish);
2232 richloc.add_range (field, false);
2233 richloc.add_fixit_replace (field, "m_field");
2234 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2235 /* The replacement range is indicated in the annotation line,
2236 so it shouldn't be indicated via an additional underline. */
2237 ASSERT_STREQ ("\n"
2238 " foo = bar.field;\n"
2239 " ^ ~~~~~\n"
2240 " m_field\n",
2241 pp_formatted_text (dc.printer));
2244 /* Verify that we can use ad-hoc locations when adding fixits to a
2245 rich_location. */
2247 static void
2248 test_one_liner_fixit_validation_adhoc_locations ()
2250 /* Generate a range that's too long to be packed, so must
2251 be stored as an ad-hoc location (given the defaults
2252 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2253 const location_t c7 = linemap_position_for_column (line_table, 7);
2254 const location_t c47 = linemap_position_for_column (line_table, 47);
2255 const location_t loc = make_location (c7, c7, c47);
2257 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2258 return;
2260 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2262 /* Insert. */
2264 rich_location richloc (line_table, loc);
2265 richloc.add_fixit_insert_before (loc, "test");
2266 /* It should not have been discarded by the validator. */
2267 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2269 test_diagnostic_context dc;
2270 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2271 ASSERT_STREQ ("\n"
2272 " foo = bar.field;\n"
2273 " ^~~~~~~~~~ \n"
2274 " test\n",
2275 pp_formatted_text (dc.printer));
2278 /* Remove. */
2280 rich_location richloc (line_table, loc);
2281 source_range range = source_range::from_locations (loc, c47);
2282 richloc.add_fixit_remove (range);
2283 /* It should not have been discarded by the validator. */
2284 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2286 test_diagnostic_context dc;
2287 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2288 ASSERT_STREQ ("\n"
2289 " foo = bar.field;\n"
2290 " ^~~~~~~~~~ \n"
2291 " -----------------------------------------\n",
2292 pp_formatted_text (dc.printer));
2295 /* Replace. */
2297 rich_location richloc (line_table, loc);
2298 source_range range = source_range::from_locations (loc, c47);
2299 richloc.add_fixit_replace (range, "test");
2300 /* It should not have been discarded by the validator. */
2301 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2303 test_diagnostic_context dc;
2304 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2305 ASSERT_STREQ ("\n"
2306 " foo = bar.field;\n"
2307 " ^~~~~~~~~~ \n"
2308 " test\n",
2309 pp_formatted_text (dc.printer));
2313 /* Test of consolidating insertions at the same location. */
2315 static void
2316 test_one_liner_many_fixits_1 ()
2318 test_diagnostic_context dc;
2319 location_t equals = linemap_position_for_column (line_table, 5);
2320 rich_location richloc (line_table, equals);
2321 for (int i = 0; i < 19; i++)
2322 richloc.add_fixit_insert_before ("a");
2323 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2324 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2325 ASSERT_STREQ ("\n"
2326 " foo = bar.field;\n"
2327 " ^\n"
2328 " aaaaaaaaaaaaaaaaaaa\n",
2329 pp_formatted_text (dc.printer));
2332 /* Ensure that we can add an arbitrary number of fix-it hints to a
2333 rich_location, even if they are not consolidated. */
2335 static void
2336 test_one_liner_many_fixits_2 ()
2338 test_diagnostic_context dc;
2339 location_t equals = linemap_position_for_column (line_table, 5);
2340 rich_location richloc (line_table, equals);
2341 for (int i = 0; i < 19; i++)
2343 location_t loc = linemap_position_for_column (line_table, i * 2);
2344 richloc.add_fixit_insert_before (loc, "a");
2346 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2347 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2348 ASSERT_STREQ ("\n"
2349 " foo = bar.field;\n"
2350 " ^\n"
2351 "a a a a a a a a a a a a a a a a a a a\n",
2352 pp_formatted_text (dc.printer));
2355 /* Run the various one-liner tests. */
2357 static void
2358 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2360 /* Create a tempfile and write some text to it.
2361 ....................0000000001111111.
2362 ....................1234567890123456. */
2363 const char *content = "foo = bar.field;\n";
2364 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2365 line_table_test ltt (case_);
2367 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2369 location_t line_end = linemap_position_for_column (line_table, 16);
2371 /* Don't attempt to run the tests if column data might be unavailable. */
2372 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2373 return;
2375 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2376 ASSERT_EQ (1, LOCATION_LINE (line_end));
2377 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2379 test_one_liner_simple_caret ();
2380 test_one_liner_caret_and_range ();
2381 test_one_liner_multiple_carets_and_ranges ();
2382 test_one_liner_fixit_insert_before ();
2383 test_one_liner_fixit_insert_after ();
2384 test_one_liner_fixit_remove ();
2385 test_one_liner_fixit_replace ();
2386 test_one_liner_fixit_replace_non_equal_range ();
2387 test_one_liner_fixit_replace_equal_secondary_range ();
2388 test_one_liner_fixit_validation_adhoc_locations ();
2389 test_one_liner_many_fixits_1 ();
2390 test_one_liner_many_fixits_2 ();
2393 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2395 static void
2396 test_add_location_if_nearby (const line_table_case &case_)
2398 /* Create a tempfile and write some text to it.
2399 ...000000000111111111122222222223333333333.
2400 ...123456789012345678901234567890123456789. */
2401 const char *content
2402 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2403 "struct different_line\n" /* line 2. */
2404 "{\n" /* line 3. */
2405 " double x;\n" /* line 4. */
2406 " double y;\n" /* line 5. */
2407 ";\n"); /* line 6. */
2408 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2409 line_table_test ltt (case_);
2411 const line_map_ordinary *ord_map
2412 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2413 tmp.get_filename (), 0));
2415 linemap_line_start (line_table, 1, 100);
2417 const location_t final_line_end
2418 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2420 /* Don't attempt to run the tests if column data might be unavailable. */
2421 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2422 return;
2424 /* Test of add_location_if_nearby on the same line as the
2425 primary location. */
2427 const location_t missing_close_brace_1_39
2428 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2429 const location_t matching_open_brace_1_18
2430 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2431 gcc_rich_location richloc (missing_close_brace_1_39);
2432 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2433 ASSERT_TRUE (added);
2434 ASSERT_EQ (2, richloc.get_num_locations ());
2435 test_diagnostic_context dc;
2436 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2437 ASSERT_STREQ ("\n"
2438 " struct same_line { double x; double y; ;\n"
2439 " ~ ^\n",
2440 pp_formatted_text (dc.printer));
2443 /* Test of add_location_if_nearby on a different line to the
2444 primary location. */
2446 const location_t missing_close_brace_6_1
2447 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2448 const location_t matching_open_brace_3_1
2449 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2450 gcc_rich_location richloc (missing_close_brace_6_1);
2451 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2452 ASSERT_FALSE (added);
2453 ASSERT_EQ (1, richloc.get_num_locations ());
2457 /* Verify that we print fixits even if they only affect lines
2458 outside those covered by the ranges in the rich_location. */
2460 static void
2461 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2463 /* Create a tempfile and write some text to it.
2464 ...000000000111111111122222222223333333333.
2465 ...123456789012345678901234567890123456789. */
2466 const char *content
2467 = ("struct point { double x; double y; };\n" /* line 1. */
2468 "struct point origin = {x: 0.0,\n" /* line 2. */
2469 " y\n" /* line 3. */
2470 "\n" /* line 4. */
2471 "\n" /* line 5. */
2472 " : 0.0};\n"); /* line 6. */
2473 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2474 line_table_test ltt (case_);
2476 const line_map_ordinary *ord_map
2477 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2478 tmp.get_filename (), 0));
2480 linemap_line_start (line_table, 1, 100);
2482 const location_t final_line_end
2483 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2485 /* Don't attempt to run the tests if column data might be unavailable. */
2486 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2487 return;
2489 /* A pair of tests for modernizing the initializers to C99-style. */
2491 /* The one-liner case (line 2). */
2493 test_diagnostic_context dc;
2494 const location_t x
2495 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2496 const location_t colon
2497 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2498 rich_location richloc (line_table, colon);
2499 richloc.add_fixit_insert_before (x, ".");
2500 richloc.add_fixit_replace (colon, "=");
2501 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2502 ASSERT_STREQ ("\n"
2503 " struct point origin = {x: 0.0,\n"
2504 " ^\n"
2505 " .=\n",
2506 pp_formatted_text (dc.printer));
2509 /* The multiline case. The caret for the rich_location is on line 6;
2510 verify that insertion fixit on line 3 is still printed (and that
2511 span starts are printed due to the gap between the span at line 3
2512 and that at line 6). */
2514 test_diagnostic_context dc;
2515 const location_t y
2516 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2517 const location_t colon
2518 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2519 rich_location richloc (line_table, colon);
2520 richloc.add_fixit_insert_before (y, ".");
2521 richloc.add_fixit_replace (colon, "=");
2522 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2523 ASSERT_STREQ ("\n"
2524 "FILENAME:3:24:\n"
2525 " y\n"
2526 " .\n"
2527 "FILENAME:6:25:\n"
2528 " : 0.0};\n"
2529 " ^\n"
2530 " =\n",
2531 pp_formatted_text (dc.printer));
2536 /* Verify that fix-it hints are appropriately consolidated.
2538 If any fix-it hints in a rich_location involve locations beyond
2539 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2540 the fix-it as a whole, so there should be none.
2542 Otherwise, verify that consecutive "replace" and "remove" fix-its
2543 are merged, and that other fix-its remain separate. */
2545 static void
2546 test_fixit_consolidation (const line_table_case &case_)
2548 line_table_test ltt (case_);
2550 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2552 const location_t c10 = linemap_position_for_column (line_table, 10);
2553 const location_t c15 = linemap_position_for_column (line_table, 15);
2554 const location_t c16 = linemap_position_for_column (line_table, 16);
2555 const location_t c17 = linemap_position_for_column (line_table, 17);
2556 const location_t c20 = linemap_position_for_column (line_table, 20);
2557 const location_t c21 = linemap_position_for_column (line_table, 21);
2558 const location_t caret = c10;
2560 /* Insert + insert. */
2562 rich_location richloc (line_table, caret);
2563 richloc.add_fixit_insert_before (c10, "foo");
2564 richloc.add_fixit_insert_before (c15, "bar");
2566 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2567 /* Bogus column info for 2nd fixit, so no fixits. */
2568 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2569 else
2570 /* They should not have been merged. */
2571 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2574 /* Insert + replace. */
2576 rich_location richloc (line_table, caret);
2577 richloc.add_fixit_insert_before (c10, "foo");
2578 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2579 "bar");
2581 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2582 /* Bogus column info for 2nd fixit, so no fixits. */
2583 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2584 else
2585 /* They should not have been merged. */
2586 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2589 /* Replace + non-consecutive insert. */
2591 rich_location richloc (line_table, caret);
2592 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2593 "bar");
2594 richloc.add_fixit_insert_before (c17, "foo");
2596 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2597 /* Bogus column info for 2nd fixit, so no fixits. */
2598 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2599 else
2600 /* They should not have been merged. */
2601 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2604 /* Replace + non-consecutive replace. */
2606 rich_location richloc (line_table, caret);
2607 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2608 "foo");
2609 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2610 "bar");
2612 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2613 /* Bogus column info for 2nd fixit, so no fixits. */
2614 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2615 else
2616 /* They should not have been merged. */
2617 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2620 /* Replace + consecutive replace. */
2622 rich_location richloc (line_table, caret);
2623 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2624 "foo");
2625 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2626 "bar");
2628 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2629 /* Bogus column info for 2nd fixit, so no fixits. */
2630 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2631 else
2633 /* They should have been merged into a single "replace". */
2634 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2635 const fixit_hint *hint = richloc.get_fixit_hint (0);
2636 ASSERT_STREQ ("foobar", hint->get_string ());
2637 ASSERT_EQ (c10, hint->get_start_loc ());
2638 ASSERT_EQ (c21, hint->get_next_loc ());
2642 /* Replace + consecutive removal. */
2644 rich_location richloc (line_table, caret);
2645 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2646 "foo");
2647 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2649 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2650 /* Bogus column info for 2nd fixit, so no fixits. */
2651 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2652 else
2654 /* They should have been merged into a single replace, with the
2655 range extended to cover that of the removal. */
2656 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2657 const fixit_hint *hint = richloc.get_fixit_hint (0);
2658 ASSERT_STREQ ("foo", hint->get_string ());
2659 ASSERT_EQ (c10, hint->get_start_loc ());
2660 ASSERT_EQ (c21, hint->get_next_loc ());
2664 /* Consecutive removals. */
2666 rich_location richloc (line_table, caret);
2667 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2668 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2670 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2671 /* Bogus column info for 2nd fixit, so no fixits. */
2672 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2673 else
2675 /* They should have been merged into a single "replace-with-empty". */
2676 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2677 const fixit_hint *hint = richloc.get_fixit_hint (0);
2678 ASSERT_STREQ ("", hint->get_string ());
2679 ASSERT_EQ (c10, hint->get_start_loc ());
2680 ASSERT_EQ (c21, hint->get_next_loc ());
2685 /* Verify that the line_corrections machinery correctly prints
2686 overlapping fixit-hints. */
2688 static void
2689 test_overlapped_fixit_printing (const line_table_case &case_)
2691 /* Create a tempfile and write some text to it.
2692 ...000000000111111111122222222223333333333.
2693 ...123456789012345678901234567890123456789. */
2694 const char *content
2695 = (" foo *f = (foo *)ptr->field;\n");
2696 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2697 line_table_test ltt (case_);
2699 const line_map_ordinary *ord_map
2700 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2701 tmp.get_filename (), 0));
2703 linemap_line_start (line_table, 1, 100);
2705 const location_t final_line_end
2706 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2708 /* Don't attempt to run the tests if column data might be unavailable. */
2709 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2710 return;
2712 /* A test for converting a C-style cast to a C++-style cast. */
2713 const location_t open_paren
2714 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2715 const location_t close_paren
2716 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2717 const location_t expr_start
2718 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2719 const location_t expr_finish
2720 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2721 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2723 /* Various examples of fix-it hints that aren't themselves consolidated,
2724 but for which the *printing* may need consolidation. */
2726 /* Example where 3 fix-it hints are printed as one. */
2728 test_diagnostic_context dc;
2729 rich_location richloc (line_table, expr);
2730 richloc.add_fixit_replace (open_paren, "const_cast<");
2731 richloc.add_fixit_replace (close_paren, "> (");
2732 richloc.add_fixit_insert_after (")");
2734 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2735 ASSERT_STREQ ("\n"
2736 " foo *f = (foo *)ptr->field;\n"
2737 " ^~~~~~~~~~\n"
2738 " -----------------\n"
2739 " const_cast<foo *> (ptr->field)\n",
2740 pp_formatted_text (dc.printer));
2742 /* Unit-test the line_corrections machinery. */
2743 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2744 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2745 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2746 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2747 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2748 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2749 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2750 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2751 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2752 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2754 /* Add each hint in turn to a line_corrections instance,
2755 and verify that they are consolidated into one correction instance
2756 as expected. */
2757 line_corrections lc (tmp.get_filename (), 1);
2759 /* The first replace hint by itself. */
2760 lc.add_hint (hint_0);
2761 ASSERT_EQ (1, lc.m_corrections.length ());
2762 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2763 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2764 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2766 /* After the second replacement hint, they are printed together
2767 as a replacement (along with the text between them). */
2768 lc.add_hint (hint_1);
2769 ASSERT_EQ (1, lc.m_corrections.length ());
2770 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2771 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2772 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2774 /* After the final insertion hint, they are all printed together
2775 as a replacement (along with the text between them). */
2776 lc.add_hint (hint_2);
2777 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2778 lc.m_corrections[0]->m_text);
2779 ASSERT_EQ (1, lc.m_corrections.length ());
2780 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2781 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2784 /* Example where two are consolidated during printing. */
2786 test_diagnostic_context dc;
2787 rich_location richloc (line_table, expr);
2788 richloc.add_fixit_replace (open_paren, "CAST (");
2789 richloc.add_fixit_replace (close_paren, ") (");
2790 richloc.add_fixit_insert_after (")");
2792 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2793 ASSERT_STREQ ("\n"
2794 " foo *f = (foo *)ptr->field;\n"
2795 " ^~~~~~~~~~\n"
2796 " -\n"
2797 " CAST (-\n"
2798 " ) ( )\n",
2799 pp_formatted_text (dc.printer));
2802 /* Example where none are consolidated during printing. */
2804 test_diagnostic_context dc;
2805 rich_location richloc (line_table, expr);
2806 richloc.add_fixit_replace (open_paren, "CST (");
2807 richloc.add_fixit_replace (close_paren, ") (");
2808 richloc.add_fixit_insert_after (")");
2810 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2811 ASSERT_STREQ ("\n"
2812 " foo *f = (foo *)ptr->field;\n"
2813 " ^~~~~~~~~~\n"
2814 " -\n"
2815 " CST ( -\n"
2816 " ) ( )\n",
2817 pp_formatted_text (dc.printer));
2820 /* Example of deletion fix-it hints. */
2822 test_diagnostic_context dc;
2823 rich_location richloc (line_table, expr);
2824 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2825 source_range victim = {open_paren, close_paren};
2826 richloc.add_fixit_remove (victim);
2828 /* This case is actually handled by fixit-consolidation,
2829 rather than by line_corrections. */
2830 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2832 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2833 ASSERT_STREQ ("\n"
2834 " foo *f = (foo *)ptr->field;\n"
2835 " ^~~~~~~~~~\n"
2836 " -------\n"
2837 " (bar *)\n",
2838 pp_formatted_text (dc.printer));
2841 /* Example of deletion fix-it hints that would overlap. */
2843 test_diagnostic_context dc;
2844 rich_location richloc (line_table, expr);
2845 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2846 source_range victim = {expr_start, expr_finish};
2847 richloc.add_fixit_remove (victim);
2849 /* These fixits are not consolidated. */
2850 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2852 /* But the corrections are. */
2853 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2854 ASSERT_STREQ ("\n"
2855 " foo *f = (foo *)ptr->field;\n"
2856 " ^~~~~~~~~~\n"
2857 " -----------------\n"
2858 " (longer *)(foo *)\n",
2859 pp_formatted_text (dc.printer));
2862 /* Example of insertion fix-it hints that would overlap. */
2864 test_diagnostic_context dc;
2865 rich_location richloc (line_table, expr);
2866 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2867 richloc.add_fixit_insert_after (close_paren, "TEST");
2869 /* The first insertion is long enough that if printed naively,
2870 it would overlap with the second.
2871 Verify that they are printed as a single replacement. */
2872 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2873 ASSERT_STREQ ("\n"
2874 " foo *f = (foo *)ptr->field;\n"
2875 " ^~~~~~~~~~\n"
2876 " -------\n"
2877 " LONGER THAN THE CAST(foo *)TEST\n",
2878 pp_formatted_text (dc.printer));
2882 /* Verify that the line_corrections machinery correctly prints
2883 overlapping fixit-hints that have been added in the wrong
2884 order.
2885 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
2887 static void
2888 test_overlapped_fixit_printing_2 (const line_table_case &case_)
2890 /* Create a tempfile and write some text to it.
2891 ...000000000111111111122222222223333333333.
2892 ...123456789012345678901234567890123456789. */
2893 const char *content
2894 = ("int a5[][0][0] = { 1, 2 };\n");
2895 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2896 line_table_test ltt (case_);
2898 const line_map_ordinary *ord_map
2899 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2900 tmp.get_filename (), 0));
2902 linemap_line_start (line_table, 1, 100);
2904 const location_t final_line_end
2905 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
2907 /* Don't attempt to run the tests if column data might be unavailable. */
2908 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2909 return;
2911 const location_t col_1
2912 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2913 const location_t col_20
2914 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
2915 const location_t col_21
2916 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
2917 const location_t col_23
2918 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
2919 const location_t col_25
2920 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
2922 /* Two insertions, in the wrong order. */
2924 rich_location richloc (line_table, col_20);
2925 richloc.add_fixit_insert_before (col_23, "{");
2926 richloc.add_fixit_insert_before (col_21, "}");
2928 /* These fixits should be accepted; they can't be consolidated. */
2929 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2930 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2931 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
2932 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
2933 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2934 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
2935 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
2937 /* Verify that they're printed correctly. */
2938 test_diagnostic_context dc;
2939 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2940 ASSERT_STREQ ("\n"
2941 " int a5[][0][0] = { 1, 2 };\n"
2942 " ^\n"
2943 " } {\n",
2944 pp_formatted_text (dc.printer));
2947 /* Various overlapping insertions, some occurring "out of order"
2948 (reproducing the fix-it hints from PR c/81405). */
2950 test_diagnostic_context dc;
2951 rich_location richloc (line_table, col_20);
2953 richloc.add_fixit_insert_before (col_20, "{{");
2954 richloc.add_fixit_insert_before (col_21, "}}");
2955 richloc.add_fixit_insert_before (col_23, "{");
2956 richloc.add_fixit_insert_before (col_21, "}");
2957 richloc.add_fixit_insert_before (col_23, "{{");
2958 richloc.add_fixit_insert_before (col_25, "}");
2959 richloc.add_fixit_insert_before (col_21, "}");
2960 richloc.add_fixit_insert_before (col_1, "{");
2961 richloc.add_fixit_insert_before (col_25, "}");
2962 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2963 ASSERT_STREQ ("\n"
2964 " int a5[][0][0] = { 1, 2 };\n"
2965 " ^\n"
2966 " { -----\n"
2967 " {{1}}}}, {{{2 }}\n",
2968 pp_formatted_text (dc.printer));
2972 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2974 static void
2975 test_fixit_insert_containing_newline (const line_table_case &case_)
2977 /* Create a tempfile and write some text to it.
2978 .........................0000000001111111.
2979 .........................1234567890123456. */
2980 const char *old_content = (" case 'a':\n" /* line 1. */
2981 " x = a;\n" /* line 2. */
2982 " case 'b':\n" /* line 3. */
2983 " x = b;\n");/* line 4. */
2985 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2986 line_table_test ltt (case_);
2987 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2989 location_t case_start = linemap_position_for_column (line_table, 5);
2990 location_t case_finish = linemap_position_for_column (line_table, 13);
2991 location_t case_loc = make_location (case_start, case_start, case_finish);
2992 location_t line_start = linemap_position_for_column (line_table, 1);
2994 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2995 return;
2997 /* Add a "break;" on a line by itself before line 3 i.e. before
2998 column 1 of line 3. */
3000 rich_location richloc (line_table, case_loc);
3001 richloc.add_fixit_insert_before (line_start, " break;\n");
3002 test_diagnostic_context dc;
3003 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3004 ASSERT_STREQ ("\n"
3005 "+ break;\n"
3006 " case 'b':\n"
3007 " ^~~~~~~~~\n",
3008 pp_formatted_text (dc.printer));
3011 /* Verify that attempts to add text with a newline fail when the
3012 insertion point is *not* at the start of a line. */
3014 rich_location richloc (line_table, case_loc);
3015 richloc.add_fixit_insert_before (case_start, "break;\n");
3016 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3017 test_diagnostic_context dc;
3018 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3019 ASSERT_STREQ ("\n"
3020 " case 'b':\n"
3021 " ^~~~~~~~~\n",
3022 pp_formatted_text (dc.printer));
3026 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3027 of the file, where the fix-it is printed in a different line-span
3028 to the primary range of the diagnostic. */
3030 static void
3031 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3033 /* Create a tempfile and write some text to it.
3034 .........................0000000001111111.
3035 .........................1234567890123456. */
3036 const char *old_content = ("test (int ch)\n" /* line 1. */
3037 "{\n" /* line 2. */
3038 " putchar (ch);\n" /* line 3. */
3039 "}\n"); /* line 4. */
3041 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3042 line_table_test ltt (case_);
3044 const line_map_ordinary *ord_map = linemap_check_ordinary
3045 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3046 linemap_line_start (line_table, 1, 100);
3048 /* The primary range is the "putchar" token. */
3049 location_t putchar_start
3050 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3051 location_t putchar_finish
3052 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3053 location_t putchar_loc
3054 = make_location (putchar_start, putchar_start, putchar_finish);
3055 rich_location richloc (line_table, putchar_loc);
3057 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3058 location_t file_start
3059 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3060 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3062 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3063 return;
3065 test_diagnostic_context dc;
3066 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3067 ASSERT_STREQ ("\n"
3068 "FILENAME:1:1:\n"
3069 "+#include <stdio.h>\n"
3070 " test (int ch)\n"
3071 "FILENAME:3:2:\n"
3072 " putchar (ch);\n"
3073 " ^~~~~~~\n",
3074 pp_formatted_text (dc.printer));
3077 /* Replacement fix-it hint containing a newline.
3078 This will fail, as newlines are only supported when inserting at the
3079 beginning of a line. */
3081 static void
3082 test_fixit_replace_containing_newline (const line_table_case &case_)
3084 /* Create a tempfile and write some text to it.
3085 .........................0000000001111.
3086 .........................1234567890123. */
3087 const char *old_content = "foo = bar ();\n";
3089 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3090 line_table_test ltt (case_);
3091 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3093 /* Replace the " = " with "\n = ", as if we were reformatting an
3094 overly long line. */
3095 location_t start = linemap_position_for_column (line_table, 4);
3096 location_t finish = linemap_position_for_column (line_table, 6);
3097 location_t loc = linemap_position_for_column (line_table, 13);
3098 rich_location richloc (line_table, loc);
3099 source_range range = source_range::from_locations (start, finish);
3100 richloc.add_fixit_replace (range, "\n =");
3102 /* Arbitrary newlines are not yet supported within fix-it hints, so
3103 the fix-it should not be displayed. */
3104 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3106 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3107 return;
3109 test_diagnostic_context dc;
3110 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3111 ASSERT_STREQ ("\n"
3112 " foo = bar ();\n"
3113 " ^\n",
3114 pp_formatted_text (dc.printer));
3117 /* Fix-it hint, attempting to delete a newline.
3118 This will fail, as we currently only support fix-it hints that
3119 affect one line at a time. */
3121 static void
3122 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3124 /* Create a tempfile and write some text to it.
3125 ..........................0000000001111.
3126 ..........................1234567890123. */
3127 const char *old_content = ("foo = bar (\n"
3128 " );\n");
3130 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3131 line_table_test ltt (case_);
3132 const line_map_ordinary *ord_map = linemap_check_ordinary
3133 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3134 linemap_line_start (line_table, 1, 100);
3136 /* Attempt to delete the " (\n...)". */
3137 location_t start
3138 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3139 location_t caret
3140 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3141 location_t finish
3142 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3143 location_t loc = make_location (caret, start, finish);
3144 rich_location richloc (line_table, loc);
3145 richloc. add_fixit_remove ();
3147 /* Fix-it hints that affect more than one line are not yet supported, so
3148 the fix-it should not be displayed. */
3149 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3151 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3152 return;
3154 test_diagnostic_context dc;
3155 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3156 ASSERT_STREQ ("\n"
3157 " foo = bar (\n"
3158 " ~^\n"
3159 " );\n"
3160 " ~ \n",
3161 pp_formatted_text (dc.printer));
3164 /* Run all of the selftests within this file. */
3166 void
3167 diagnostic_show_locus_c_tests ()
3169 test_line_span ();
3171 test_layout_range_for_single_point ();
3172 test_layout_range_for_single_line ();
3173 test_layout_range_for_multiple_lines ();
3175 test_get_line_width_without_trailing_whitespace ();
3177 test_diagnostic_show_locus_unknown_location ();
3179 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3180 for_each_line_table_case (test_add_location_if_nearby);
3181 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3182 for_each_line_table_case (test_fixit_consolidation);
3183 for_each_line_table_case (test_overlapped_fixit_printing);
3184 for_each_line_table_case (test_overlapped_fixit_printing_2);
3185 for_each_line_table_case (test_fixit_insert_containing_newline);
3186 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3187 for_each_line_table_case (test_fixit_replace_containing_newline);
3188 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3191 } // namespace selftest
3193 #endif /* #if CHECKING_P */