Daily bump.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blobf188ee9938b48b05d9d82215b3421f6bea745957
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 size_t max_width = m_context->caret_max_width;
861 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
862 if (line && (size_t)m_exploc.column <= line.length ())
864 size_t right_margin = CARET_LINE_MARGIN;
865 size_t column = m_exploc.column;
866 right_margin = MIN (line.length () - column, right_margin);
867 right_margin = max_width - right_margin;
868 if (line.length () >= max_width && column > right_margin)
869 m_x_offset = column - right_margin;
870 gcc_assert (m_x_offset >= 0);
873 if (context->show_ruler_p)
874 show_ruler (m_x_offset + max_width);
877 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
878 those that we can sanely print.
880 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
881 filtered against this layout instance's current line spans: it
882 will only be added if the location is fully within the lines
883 already specified by other locations.
885 Return true iff LOC_RANGE was added. */
887 bool
888 layout::maybe_add_location_range (const location_range *loc_range,
889 bool restrict_to_current_line_spans)
891 gcc_assert (loc_range);
893 /* Split the "range" into caret and range information. */
894 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
896 /* Expand the various locations. */
897 expanded_location start
898 = linemap_client_expand_location_to_spelling_point
899 (src_range.m_start, LOCATION_ASPECT_START);
900 expanded_location finish
901 = linemap_client_expand_location_to_spelling_point
902 (src_range.m_finish, LOCATION_ASPECT_FINISH);
903 expanded_location caret
904 = linemap_client_expand_location_to_spelling_point
905 (loc_range->m_loc, LOCATION_ASPECT_CARET);
907 /* If any part of the range isn't in the same file as the primary
908 location of this diagnostic, ignore the range. */
909 if (start.file != m_exploc.file)
910 return false;
911 if (finish.file != m_exploc.file)
912 return false;
913 if (loc_range->m_show_caret_p)
914 if (caret.file != m_exploc.file)
915 return false;
917 /* Sanitize the caret location for non-primary ranges. */
918 if (m_layout_ranges.length () > 0)
919 if (loc_range->m_show_caret_p)
920 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
921 /* Discard any non-primary ranges that can't be printed
922 sanely relative to the primary location. */
923 return false;
925 /* Everything is now known to be in the correct source file,
926 but it may require further sanitization. */
927 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
929 /* If we have a range that finishes before it starts (perhaps
930 from something built via macro expansion), printing the
931 range is likely to be nonsensical. Also, attempting to do so
932 breaks assumptions within the printing code (PR c/68473).
933 Similarly, don't attempt to print ranges if one or both ends
934 of the range aren't sane to print relative to the
935 primary location (PR c++/70105). */
936 if (start.line > finish.line
937 || !compatible_locations_p (src_range.m_start, m_primary_loc)
938 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
940 /* Is this the primary location? */
941 if (m_layout_ranges.length () == 0)
943 /* We want to print the caret for the primary location, but
944 we must sanitize away m_start and m_finish. */
945 ri.m_start = ri.m_caret;
946 ri.m_finish = ri.m_caret;
948 else
949 /* This is a non-primary range; ignore it. */
950 return false;
953 /* Potentially filter to just the lines already specified by other
954 locations. This is for use by gcc_rich_location::add_location_if_nearby.
955 The layout ctor doesn't use it, and can't because m_line_spans
956 hasn't been set up at that point. */
957 if (restrict_to_current_line_spans)
959 if (!will_show_line_p (start.line))
960 return false;
961 if (!will_show_line_p (finish.line))
962 return false;
963 if (loc_range->m_show_caret_p)
964 if (!will_show_line_p (caret.line))
965 return false;
968 /* Passed all the tests; add the range to m_layout_ranges so that
969 it will be printed. */
970 m_layout_ranges.safe_push (ri);
971 return true;
974 /* Return true iff ROW is within one of the line spans for this layout. */
976 bool
977 layout::will_show_line_p (linenum_type row) const
979 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
980 line_span_idx++)
982 const line_span *line_span = get_line_span (line_span_idx);
983 if (line_span->contains_line_p (row))
984 return true;
986 return false;
989 /* Return true iff we should print a heading when starting the
990 line span with the given index. */
992 bool
993 layout::print_heading_for_line_span_index_p (int line_span_idx) const
995 /* We print a heading for every change of line span, hence for every
996 line span after the initial one. */
997 if (line_span_idx > 0)
998 return true;
1000 /* We also do it for the initial span if the primary location of the
1001 diagnostic is in a different span. */
1002 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1003 return true;
1005 return false;
1008 /* Get an expanded_location for the first location of interest within
1009 the given line_span.
1010 Used when printing a heading to indicate a new line span. */
1012 expanded_location
1013 layout::get_expanded_location (const line_span *line_span) const
1015 /* Whenever possible, use the caret location. */
1016 if (line_span->contains_line_p (m_exploc.line))
1017 return m_exploc;
1019 /* Otherwise, use the start of the first range that's present
1020 within the line_span. */
1021 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1023 const layout_range *lr = &m_layout_ranges[i];
1024 if (line_span->contains_line_p (lr->m_start.m_line))
1026 expanded_location exploc = m_exploc;
1027 exploc.line = lr->m_start.m_line;
1028 exploc.column = lr->m_start.m_column;
1029 return exploc;
1033 /* Otherwise, use the location of the first fixit-hint present within
1034 the line_span. */
1035 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1037 const fixit_hint *hint = m_fixit_hints[i];
1038 location_t loc = hint->get_start_loc ();
1039 expanded_location exploc = expand_location (loc);
1040 if (line_span->contains_line_p (exploc.line))
1041 return exploc;
1044 /* It should not be possible to have a line span that didn't
1045 contain any of the layout_range or fixit_hint instances. */
1046 gcc_unreachable ();
1047 return m_exploc;
1050 /* Determine if HINT is meaningful to print within this layout. */
1052 bool
1053 layout::validate_fixit_hint_p (const fixit_hint *hint)
1055 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1056 return false;
1057 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1058 return false;
1060 return true;
1063 /* Determine the range of lines affected by HINT.
1064 This assumes that HINT has already been filtered by
1065 validate_fixit_hint_p, and so affects the correct source file. */
1067 static line_span
1068 get_line_span_for_fixit_hint (const fixit_hint *hint)
1070 gcc_assert (hint);
1071 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1072 LOCATION_LINE (hint->get_next_loc ()));
1075 /* We want to print the pertinent source code at a diagnostic. The
1076 rich_location can contain multiple locations. This will have been
1077 filtered into m_exploc (the caret for the primary location) and
1078 m_layout_ranges, for those ranges within the same source file.
1080 We will print a subset of the lines within the source file in question,
1081 as a collection of "spans" of lines.
1083 This function populates m_line_spans with an ordered, disjoint list of
1084 the line spans of interest.
1086 For example, if the primary caret location is on line 7, with ranges
1087 covering lines 5-6 and lines 9-12:
1090 005 |RANGE 0
1091 006 |RANGE 0
1092 007 |PRIMARY CARET
1094 009 |RANGE 1
1095 010 |RANGE 1
1096 011 |RANGE 1
1097 012 |RANGE 1
1100 then we want two spans: lines 5-7 and lines 9-12. */
1102 void
1103 layout::calculate_line_spans ()
1105 /* This should only be called once, by the ctor. */
1106 gcc_assert (m_line_spans.length () == 0);
1108 /* Populate tmp_spans with individual spans, for each of
1109 m_exploc, and for m_layout_ranges. */
1110 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1111 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1112 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1114 const layout_range *lr = &m_layout_ranges[i];
1115 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1116 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1117 lr->m_finish.m_line));
1120 /* Also add spans for any fix-it hints, in case they cover other lines. */
1121 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1123 const fixit_hint *hint = m_fixit_hints[i];
1124 gcc_assert (hint);
1125 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1128 /* Sort them. */
1129 tmp_spans.qsort(line_span::comparator);
1131 /* Now iterate through tmp_spans, copying into m_line_spans, and
1132 combining where possible. */
1133 gcc_assert (tmp_spans.length () > 0);
1134 m_line_spans.safe_push (tmp_spans[0]);
1135 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1137 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1138 const line_span *next = &tmp_spans[i];
1139 gcc_assert (next->m_first_line >= current->m_first_line);
1140 if (next->m_first_line <= current->m_last_line + 1)
1142 /* We can merge them. */
1143 if (next->m_last_line > current->m_last_line)
1144 current->m_last_line = next->m_last_line;
1146 else
1148 /* No merger possible. */
1149 m_line_spans.safe_push (*next);
1153 /* Verify the result, in m_line_spans. */
1154 gcc_assert (m_line_spans.length () > 0);
1155 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1157 const line_span *prev = &m_line_spans[i - 1];
1158 const line_span *next = &m_line_spans[i];
1159 /* The individual spans must be sane. */
1160 gcc_assert (prev->m_first_line <= prev->m_last_line);
1161 gcc_assert (next->m_first_line <= next->m_last_line);
1162 /* The spans must be ordered. */
1163 gcc_assert (prev->m_first_line < next->m_first_line);
1164 /* There must be a gap of at least one line between separate spans. */
1165 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1169 /* Print line ROW of source code, potentially colorized at any ranges, and
1170 populate *LBOUNDS_OUT.
1171 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1172 is its width. */
1174 void
1175 layout::print_source_line (linenum_type row, const char *line, int line_width,
1176 line_bounds *lbounds_out)
1178 m_colorizer.set_normal_text ();
1180 /* We will stop printing the source line at any trailing
1181 whitespace. */
1182 line_width = get_line_width_without_trailing_whitespace (line,
1183 line_width);
1184 line += m_x_offset;
1186 pp_space (m_pp);
1187 int first_non_ws = INT_MAX;
1188 int last_non_ws = 0;
1189 int column;
1190 for (column = 1 + m_x_offset; column <= line_width; column++)
1192 /* Assuming colorization is enabled for the caret and underline
1193 characters, we may also colorize the associated characters
1194 within the source line.
1196 For frontends that generate range information, we color the
1197 associated characters in the source line the same as the
1198 carets and underlines in the annotation line, to make it easier
1199 for the reader to see the pertinent code.
1201 For frontends that only generate carets, we don't colorize the
1202 characters above them, since this would look strange (e.g.
1203 colorizing just the first character in a token). */
1204 if (m_colorize_source_p)
1206 bool in_range_p;
1207 point_state state;
1208 in_range_p = get_state_at_point (row, column,
1209 0, INT_MAX,
1210 &state);
1211 if (in_range_p)
1212 m_colorizer.set_range (state.range_idx);
1213 else
1214 m_colorizer.set_normal_text ();
1216 char c = *line;
1217 if (c == '\0' || c == '\t' || c == '\r')
1218 c = ' ';
1219 if (c != ' ')
1221 last_non_ws = column;
1222 if (first_non_ws == INT_MAX)
1223 first_non_ws = column;
1225 pp_character (m_pp, c);
1226 line++;
1228 print_newline ();
1230 lbounds_out->m_first_non_ws = first_non_ws;
1231 lbounds_out->m_last_non_ws = last_non_ws;
1234 /* Determine if we should print an annotation line for ROW.
1235 i.e. if any of m_layout_ranges contains ROW. */
1237 bool
1238 layout::should_print_annotation_line_p (linenum_type row) const
1240 layout_range *range;
1241 int i;
1242 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1243 if (range->intersects_line_p (row))
1244 return true;
1245 return false;
1248 /* Print a line consisting of the caret/underlines for the given
1249 source line. */
1251 void
1252 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1254 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1255 lbounds.m_last_non_ws);
1257 pp_space (m_pp);
1258 for (int column = 1 + m_x_offset; column < x_bound; column++)
1260 bool in_range_p;
1261 point_state state;
1262 in_range_p = get_state_at_point (row, column,
1263 lbounds.m_first_non_ws,
1264 lbounds.m_last_non_ws,
1265 &state);
1266 if (in_range_p)
1268 /* Within a range. Draw either the caret or an underline. */
1269 m_colorizer.set_range (state.range_idx);
1270 if (state.draw_caret_p)
1272 /* Draw the caret. */
1273 char caret_char;
1274 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1275 caret_char = m_context->caret_chars[state.range_idx];
1276 else
1277 caret_char = '^';
1278 pp_character (m_pp, caret_char);
1280 else
1281 pp_character (m_pp, '~');
1283 else
1285 /* Not in a range. */
1286 m_colorizer.set_normal_text ();
1287 pp_character (m_pp, ' ');
1290 print_newline ();
1293 /* If there are any fixit hints inserting new lines before source line ROW,
1294 print them.
1296 They are printed on lines of their own, before the source line
1297 itself, with a leading '+'. */
1299 void
1300 layout::print_leading_fixits (linenum_type row)
1302 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1304 const fixit_hint *hint = m_fixit_hints[i];
1306 if (!hint->ends_with_newline_p ())
1307 /* Not a newline fixit; print it in print_trailing_fixits. */
1308 continue;
1310 gcc_assert (hint->insertion_p ());
1312 if (hint->affects_line_p (m_exploc.file, row))
1314 /* Printing the '+' with normal colorization
1315 and the inserted line with "insert" colorization
1316 helps them stand out from each other, and from
1317 the surrounding text. */
1318 m_colorizer.set_normal_text ();
1319 pp_character (m_pp, '+');
1320 m_colorizer.set_fixit_insert ();
1321 /* Print all but the trailing newline of the fix-it hint.
1322 We have to print the newline separately to avoid
1323 getting additional pp prefixes printed. */
1324 for (size_t i = 0; i < hint->get_length () - 1; i++)
1325 pp_character (m_pp, hint->get_string ()[i]);
1326 m_colorizer.set_normal_text ();
1327 pp_newline (m_pp);
1332 /* Subroutine of layout::print_trailing_fixits.
1334 Determine if the annotation line printed for LINE contained
1335 the exact range from START_COLUMN to FINISH_COLUMN. */
1337 bool
1338 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1339 int finish_column) const
1341 layout_range *range;
1342 int i;
1343 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1344 if (range->m_start.m_line == line
1345 && range->m_start.m_column == start_column
1346 && range->m_finish.m_line == line
1347 && range->m_finish.m_column == finish_column)
1348 return true;
1349 return false;
1352 /* Classes for printing trailing fix-it hints i.e. those that
1353 don't add new lines.
1355 For insertion, these can look like:
1357 new_text
1359 For replacement, these can look like:
1361 ------------- : underline showing affected range
1362 new_text
1364 For deletion, these can look like:
1366 ------------- : underline showing affected range
1368 This can become confusing if they overlap, and so we need
1369 to do some preprocessing to decide what to print.
1370 We use the list of fixit_hint instances affecting the line
1371 to build a list of "correction" instances, and print the
1372 latter.
1374 For example, consider a set of fix-its for converting
1375 a C-style cast to a C++ const_cast.
1377 Given:
1379 ..000000000111111111122222222223333333333.
1380 ..123456789012345678901234567890123456789.
1381 foo *f = (foo *)ptr->field;
1382 ^~~~~
1384 and the fix-it hints:
1385 - replace col 10 (the open paren) with "const_cast<"
1386 - replace col 16 (the close paren) with "> ("
1387 - insert ")" before col 27
1389 then we would get odd-looking output:
1391 foo *f = (foo *)ptr->field;
1392 ^~~~~
1394 const_cast<
1396 > ( )
1398 It would be better to detect when fixit hints are going to
1399 overlap (those that require new lines), and to consolidate
1400 the printing of such fixits, giving something like:
1402 foo *f = (foo *)ptr->field;
1403 ^~~~~
1404 -----------------
1405 const_cast<foo *> (ptr->field)
1407 This works by detecting when the printing would overlap, and
1408 effectively injecting no-op replace hints into the gaps between
1409 such fix-its, so that the printing joins up.
1411 In the above example, the overlap of:
1412 - replace col 10 (the open paren) with "const_cast<"
1413 and:
1414 - replace col 16 (the close paren) with "> ("
1415 is fixed by injecting a no-op:
1416 - replace cols 11-15 with themselves ("foo *")
1417 and consolidating these, making:
1418 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1419 i.e.:
1420 - replace cols 10-16 with "const_cast<foo *> ("
1422 This overlaps with the final fix-it hint:
1423 - insert ")" before col 27
1424 and so we repeat the consolidation process, by injecting
1425 a no-op:
1426 - replace cols 17-26 with themselves ("ptr->field")
1427 giving:
1428 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1429 i.e.:
1430 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1432 and is thus printed as desired. */
1434 /* A range of columns within a line. */
1436 struct column_range
1438 column_range (int start_, int finish_) : start (start_), finish (finish_)
1440 /* We must have either a range, or an insertion. */
1441 gcc_assert (start <= finish || finish == start - 1);
1444 bool operator== (const column_range &other) const
1446 return start == other.start && finish == other.finish;
1449 int start;
1450 int finish;
1453 /* Get the range of columns that HINT would affect. */
1455 static column_range
1456 get_affected_columns (const fixit_hint *hint)
1458 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1459 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1461 return column_range (start_column, finish_column);
1464 /* Get the range of columns that would be printed for HINT. */
1466 static column_range
1467 get_printed_columns (const fixit_hint *hint)
1469 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1470 int final_hint_column = start_column + hint->get_length () - 1;
1471 if (hint->insertion_p ())
1473 return column_range (start_column, final_hint_column);
1475 else
1477 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1479 return column_range (start_column,
1480 MAX (finish_column, final_hint_column));
1484 /* A correction on a particular line.
1485 This describes a plan for how to print one or more fixit_hint
1486 instances that affected the line, potentially consolidating hints
1487 into corrections to make the result easier for the user to read. */
1489 struct correction
1491 correction (column_range affected_columns,
1492 column_range printed_columns,
1493 const char *new_text, size_t new_text_len)
1494 : m_affected_columns (affected_columns),
1495 m_printed_columns (printed_columns),
1496 m_text (xstrdup (new_text)),
1497 m_len (new_text_len),
1498 m_alloc_sz (new_text_len + 1)
1502 ~correction () { free (m_text); }
1504 bool insertion_p () const
1506 return m_affected_columns.start == m_affected_columns.finish + 1;
1509 void ensure_capacity (size_t len);
1510 void ensure_terminated ();
1512 void overwrite (int dst_offset, const char_span &src_span)
1514 gcc_assert (dst_offset >= 0);
1515 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1516 memcpy (m_text + dst_offset, src_span.get_buffer (),
1517 src_span.length ());
1520 /* If insert, then start: the column before which the text
1521 is to be inserted, and finish is offset by the length of
1522 the replacement.
1523 If replace, then the range of columns affected. */
1524 column_range m_affected_columns;
1526 /* If insert, then start: the column before which the text
1527 is to be inserted, and finish is offset by the length of
1528 the replacement.
1529 If replace, then the range of columns affected. */
1530 column_range m_printed_columns;
1532 /* The text to be inserted/used as replacement. */
1533 char *m_text;
1534 size_t m_len;
1535 size_t m_alloc_sz;
1538 /* Ensure that m_text can hold a string of length LEN
1539 (plus 1 for 0-termination). */
1541 void
1542 correction::ensure_capacity (size_t len)
1544 /* Allow 1 extra byte for 0-termination. */
1545 if (m_alloc_sz < (len + 1))
1547 size_t new_alloc_sz = (len + 1) * 2;
1548 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1549 m_alloc_sz = new_alloc_sz;
1553 /* Ensure that m_text is 0-terminated. */
1555 void
1556 correction::ensure_terminated ()
1558 /* 0-terminate the buffer. */
1559 gcc_assert (m_len < m_alloc_sz);
1560 m_text[m_len] = '\0';
1563 /* A list of corrections affecting a particular line.
1564 This is used by layout::print_trailing_fixits for planning
1565 how to print the fix-it hints affecting the line. */
1567 struct line_corrections
1569 line_corrections (const char *filename, linenum_type row)
1570 : m_filename (filename), m_row (row)
1572 ~line_corrections ();
1574 void add_hint (const fixit_hint *hint);
1576 const char *m_filename;
1577 linenum_type m_row;
1578 auto_vec <correction *> m_corrections;
1581 /* struct line_corrections. */
1583 line_corrections::~line_corrections ()
1585 unsigned i;
1586 correction *c;
1587 FOR_EACH_VEC_ELT (m_corrections, i, c)
1588 delete c;
1591 /* A struct wrapping a particular source line, allowing
1592 run-time bounds-checking of accesses in a checked build. */
1594 struct source_line
1596 source_line (const char *filename, int line);
1598 char_span as_span () { return char_span (chars, width); }
1600 const char *chars;
1601 int width;
1604 /* source_line's ctor. */
1606 source_line::source_line (const char *filename, int line)
1608 char_span span = location_get_source_line (filename, line);
1609 chars = span.get_buffer ();
1610 width = span.length ();
1613 /* Add HINT to the corrections for this line.
1614 Attempt to consolidate nearby hints so that they will not
1615 overlap with printed. */
1617 void
1618 line_corrections::add_hint (const fixit_hint *hint)
1620 column_range affected_columns = get_affected_columns (hint);
1621 column_range printed_columns = get_printed_columns (hint);
1623 /* Potentially consolidate. */
1624 if (!m_corrections.is_empty ())
1626 correction *last_correction
1627 = m_corrections[m_corrections.length () - 1];
1629 /* The following consolidation code assumes that the fix-it hints
1630 have been sorted by start (done within layout's ctor). */
1631 gcc_assert (affected_columns.start
1632 >= last_correction->m_affected_columns.start);
1633 gcc_assert (printed_columns.start
1634 >= last_correction->m_printed_columns.start);
1636 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1638 /* We have two hints for which the printed forms of the hints
1639 would touch or overlap, so we need to consolidate them to avoid
1640 confusing the user.
1641 Attempt to inject a "replace" correction from immediately
1642 after the end of the last hint to immediately before the start
1643 of the next hint. */
1644 column_range between (last_correction->m_affected_columns.finish + 1,
1645 printed_columns.start - 1);
1647 /* Try to read the source. */
1648 source_line line (m_filename, m_row);
1649 if (line.chars && between.finish < line.width)
1651 /* Consolidate into the last correction:
1652 add a no-op "replace" of the "between" text, and
1653 add the text from the new hint. */
1654 int old_len = last_correction->m_len;
1655 gcc_assert (old_len >= 0);
1656 int between_len = between.finish + 1 - between.start;
1657 gcc_assert (between_len >= 0);
1658 int new_len = old_len + between_len + hint->get_length ();
1659 gcc_assert (new_len >= 0);
1660 last_correction->ensure_capacity (new_len);
1661 last_correction->overwrite
1662 (old_len,
1663 line.as_span ().subspan (between.start - 1,
1664 between.finish + 1 - between.start));
1665 last_correction->overwrite (old_len + between_len,
1666 char_span (hint->get_string (),
1667 hint->get_length ()));
1668 last_correction->m_len = new_len;
1669 last_correction->ensure_terminated ();
1670 last_correction->m_affected_columns.finish
1671 = affected_columns.finish;
1672 last_correction->m_printed_columns.finish
1673 += between_len + hint->get_length ();
1674 return;
1679 /* If no consolidation happened, add a new correction instance. */
1680 m_corrections.safe_push (new correction (affected_columns,
1681 printed_columns,
1682 hint->get_string (),
1683 hint->get_length ()));
1686 /* If there are any fixit hints on source line ROW, print them.
1687 They are printed in order, attempting to combine them onto lines, but
1688 starting new lines if necessary.
1689 Fix-it hints that insert new lines are handled separately,
1690 in layout::print_leading_fixits. */
1692 void
1693 layout::print_trailing_fixits (linenum_type row)
1695 /* Build a list of correction instances for the line,
1696 potentially consolidating hints (for the sake of readability). */
1697 line_corrections corrections (m_exploc.file, row);
1698 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1700 const fixit_hint *hint = m_fixit_hints[i];
1702 /* Newline fixits are handled by layout::print_leading_fixits. */
1703 if (hint->ends_with_newline_p ())
1704 continue;
1706 if (hint->affects_line_p (m_exploc.file, row))
1707 corrections.add_hint (hint);
1710 /* Now print the corrections. */
1711 unsigned i;
1712 correction *c;
1713 int column = m_x_offset;
1715 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1717 /* For now we assume each fixit hint can only touch one line. */
1718 if (c->insertion_p ())
1720 /* This assumes the insertion just affects one line. */
1721 int start_column = c->m_printed_columns.start;
1722 move_to_column (&column, start_column);
1723 m_colorizer.set_fixit_insert ();
1724 pp_string (m_pp, c->m_text);
1725 m_colorizer.set_normal_text ();
1726 column += c->m_len;
1728 else
1730 /* If the range of the replacement wasn't printed in the
1731 annotation line, then print an extra underline to
1732 indicate exactly what is being replaced.
1733 Always show it for removals. */
1734 int start_column = c->m_affected_columns.start;
1735 int finish_column = c->m_affected_columns.finish;
1736 if (!annotation_line_showed_range_p (row, start_column,
1737 finish_column)
1738 || c->m_len == 0)
1740 move_to_column (&column, start_column);
1741 m_colorizer.set_fixit_delete ();
1742 for (; column <= finish_column; column++)
1743 pp_character (m_pp, '-');
1744 m_colorizer.set_normal_text ();
1746 /* Print the replacement text. REPLACE also covers
1747 removals, so only do this extra work (potentially starting
1748 a new line) if we have actual replacement text. */
1749 if (c->m_len > 0)
1751 move_to_column (&column, start_column);
1752 m_colorizer.set_fixit_insert ();
1753 pp_string (m_pp, c->m_text);
1754 m_colorizer.set_normal_text ();
1755 column += c->m_len;
1760 /* Add a trailing newline, if necessary. */
1761 move_to_column (&column, 0);
1764 /* Disable any colorization and emit a newline. */
1766 void
1767 layout::print_newline ()
1769 m_colorizer.set_normal_text ();
1770 pp_newline (m_pp);
1773 /* Return true if (ROW/COLUMN) is within a range of the layout.
1774 If it returns true, OUT_STATE is written to, with the
1775 range index, and whether we should draw the caret at
1776 (ROW/COLUMN) (as opposed to an underline). */
1778 bool
1779 layout::get_state_at_point (/* Inputs. */
1780 linenum_type row, int column,
1781 int first_non_ws, int last_non_ws,
1782 /* Outputs. */
1783 point_state *out_state)
1785 layout_range *range;
1786 int i;
1787 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1789 if (range->contains_point (row, column))
1791 out_state->range_idx = i;
1793 /* Are we at the range's caret? is it visible? */
1794 out_state->draw_caret_p = false;
1795 if (range->m_show_caret_p
1796 && row == range->m_caret.m_line
1797 && column == range->m_caret.m_column)
1798 out_state->draw_caret_p = true;
1800 /* Within a multiline range, don't display any underline
1801 in any leading or trailing whitespace on a line.
1802 We do display carets, however. */
1803 if (!out_state->draw_caret_p)
1804 if (column < first_non_ws || column > last_non_ws)
1805 return false;
1807 /* We are within a range. */
1808 return true;
1812 return false;
1815 /* Helper function for use by layout::print_line when printing the
1816 annotation line under the source line.
1817 Get the column beyond the rightmost one that could contain a caret or
1818 range marker, given that we stop rendering at trailing whitespace.
1819 ROW is the source line within the given file.
1820 CARET_COLUMN is the column of range 0's caret.
1821 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1822 character of source (as determined when printing the source line). */
1825 layout::get_x_bound_for_row (linenum_type row, int caret_column,
1826 int last_non_ws_column)
1828 int result = caret_column + 1;
1830 layout_range *range;
1831 int i;
1832 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1834 if (row >= range->m_start.m_line)
1836 if (range->m_finish.m_line == row)
1838 /* On the final line within a range; ensure that
1839 we render up to the end of the range. */
1840 if (result <= range->m_finish.m_column)
1841 result = range->m_finish.m_column + 1;
1843 else if (row < range->m_finish.m_line)
1845 /* Within a multiline range; ensure that we render up to the
1846 last non-whitespace column. */
1847 if (result <= last_non_ws_column)
1848 result = last_non_ws_column + 1;
1853 return result;
1856 /* Given *COLUMN as an x-coordinate, print spaces to position
1857 successive output at DEST_COLUMN, printing a newline if necessary,
1858 and updating *COLUMN. */
1860 void
1861 layout::move_to_column (int *column, int dest_column)
1863 /* Start a new line if we need to. */
1864 if (*column > dest_column)
1866 print_newline ();
1867 *column = m_x_offset;
1870 while (*column < dest_column)
1872 pp_space (m_pp);
1873 (*column)++;
1877 /* For debugging layout issues, render a ruler giving column numbers
1878 (after the 1-column indent). */
1880 void
1881 layout::show_ruler (int max_column) const
1883 /* Hundreds. */
1884 if (max_column > 99)
1886 pp_space (m_pp);
1887 for (int column = 1 + m_x_offset; column <= max_column; column++)
1888 if (column % 10 == 0)
1889 pp_character (m_pp, '0' + (column / 100) % 10);
1890 else
1891 pp_space (m_pp);
1892 pp_newline (m_pp);
1895 /* Tens. */
1896 pp_space (m_pp);
1897 for (int column = 1 + m_x_offset; column <= max_column; column++)
1898 if (column % 10 == 0)
1899 pp_character (m_pp, '0' + (column / 10) % 10);
1900 else
1901 pp_space (m_pp);
1902 pp_newline (m_pp);
1904 /* Units. */
1905 pp_space (m_pp);
1906 for (int column = 1 + m_x_offset; column <= max_column; column++)
1907 pp_character (m_pp, '0' + (column % 10));
1908 pp_newline (m_pp);
1911 /* Print leading fix-its (for new lines inserted before the source line)
1912 then the source line, followed by an annotation line
1913 consisting of any caret/underlines, then any fixits.
1914 If the source line can't be read, print nothing. */
1915 void
1916 layout::print_line (linenum_type row)
1918 char_span line = location_get_source_line (m_exploc.file, row);
1919 if (!line)
1920 return;
1922 line_bounds lbounds;
1923 print_leading_fixits (row);
1924 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
1925 if (should_print_annotation_line_p (row))
1926 print_annotation_line (row, lbounds);
1927 print_trailing_fixits (row);
1930 } /* End of anonymous namespace. */
1932 /* If LOC is within the spans of lines that will already be printed for
1933 this gcc_rich_location, then add it as a secondary location and return true.
1935 Otherwise return false. */
1937 bool
1938 gcc_rich_location::add_location_if_nearby (location_t loc)
1940 /* Use the layout location-handling logic to sanitize LOC,
1941 filtering it to the current line spans within a temporary
1942 layout instance. */
1943 layout layout (global_dc, this, DK_ERROR);
1944 location_range loc_range;
1945 loc_range.m_loc = loc;
1946 loc_range.m_show_caret_p = false;
1947 if (!layout.maybe_add_location_range (&loc_range, true))
1948 return false;
1950 add_range (loc, false);
1951 return true;
1954 /* Print the physical source code corresponding to the location of
1955 this diagnostic, with additional annotations. */
1957 void
1958 diagnostic_show_locus (diagnostic_context * context,
1959 rich_location *richloc,
1960 diagnostic_t diagnostic_kind)
1962 pp_newline (context->printer);
1964 location_t loc = richloc->get_loc ();
1965 /* Do nothing if source-printing has been disabled. */
1966 if (!context->show_caret)
1967 return;
1969 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1970 if (loc <= BUILTINS_LOCATION)
1971 return;
1973 /* Don't print the same source location twice in a row, unless we have
1974 fix-it hints. */
1975 if (loc == context->last_location
1976 && richloc->get_num_fixit_hints () == 0)
1977 return;
1979 context->last_location = loc;
1981 const char *saved_prefix = pp_get_prefix (context->printer);
1982 pp_set_prefix (context->printer, NULL);
1984 layout layout (context, richloc, diagnostic_kind);
1985 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1986 line_span_idx++)
1988 const line_span *line_span = layout.get_line_span (line_span_idx);
1989 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1991 expanded_location exploc = layout.get_expanded_location (line_span);
1992 context->start_span (context, exploc);
1994 linenum_type last_line = line_span->get_last_line ();
1995 for (linenum_type row = line_span->get_first_line ();
1996 row <= last_line; row++)
1997 layout.print_line (row);
2000 pp_set_prefix (context->printer, saved_prefix);
2003 #if CHECKING_P
2005 namespace selftest {
2007 /* Selftests for diagnostic_show_locus. */
2009 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2011 static void
2012 test_diagnostic_show_locus_unknown_location ()
2014 test_diagnostic_context dc;
2015 rich_location richloc (line_table, UNKNOWN_LOCATION);
2016 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2017 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2020 /* Verify that diagnostic_show_locus works sanely for various
2021 single-line cases.
2023 All of these work on the following 1-line source file:
2024 .0000000001111111
2025 .1234567890123456
2026 "foo = bar.field;\n"
2027 which is set up by test_diagnostic_show_locus_one_liner and calls
2028 them. */
2030 /* Just a caret. */
2032 static void
2033 test_one_liner_simple_caret ()
2035 test_diagnostic_context dc;
2036 location_t caret = linemap_position_for_column (line_table, 10);
2037 rich_location richloc (line_table, caret);
2038 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2039 ASSERT_STREQ ("\n"
2040 " foo = bar.field;\n"
2041 " ^\n",
2042 pp_formatted_text (dc.printer));
2045 /* Caret and range. */
2047 static void
2048 test_one_liner_caret_and_range ()
2050 test_diagnostic_context dc;
2051 location_t caret = linemap_position_for_column (line_table, 10);
2052 location_t start = linemap_position_for_column (line_table, 7);
2053 location_t finish = linemap_position_for_column (line_table, 15);
2054 location_t loc = make_location (caret, start, finish);
2055 rich_location richloc (line_table, loc);
2056 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2057 ASSERT_STREQ ("\n"
2058 " foo = bar.field;\n"
2059 " ~~~^~~~~~\n",
2060 pp_formatted_text (dc.printer));
2063 /* Multiple ranges and carets. */
2065 static void
2066 test_one_liner_multiple_carets_and_ranges ()
2068 test_diagnostic_context dc;
2069 location_t foo
2070 = make_location (linemap_position_for_column (line_table, 2),
2071 linemap_position_for_column (line_table, 1),
2072 linemap_position_for_column (line_table, 3));
2073 dc.caret_chars[0] = 'A';
2075 location_t bar
2076 = make_location (linemap_position_for_column (line_table, 8),
2077 linemap_position_for_column (line_table, 7),
2078 linemap_position_for_column (line_table, 9));
2079 dc.caret_chars[1] = 'B';
2081 location_t field
2082 = make_location (linemap_position_for_column (line_table, 13),
2083 linemap_position_for_column (line_table, 11),
2084 linemap_position_for_column (line_table, 15));
2085 dc.caret_chars[2] = 'C';
2087 rich_location richloc (line_table, foo);
2088 richloc.add_range (bar, true);
2089 richloc.add_range (field, true);
2090 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2091 ASSERT_STREQ ("\n"
2092 " foo = bar.field;\n"
2093 " ~A~ ~B~ ~~C~~\n",
2094 pp_formatted_text (dc.printer));
2097 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2099 static void
2100 test_one_liner_fixit_insert_before ()
2102 test_diagnostic_context dc;
2103 location_t caret = linemap_position_for_column (line_table, 7);
2104 rich_location richloc (line_table, caret);
2105 richloc.add_fixit_insert_before ("&");
2106 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2107 ASSERT_STREQ ("\n"
2108 " foo = bar.field;\n"
2109 " ^\n"
2110 " &\n",
2111 pp_formatted_text (dc.printer));
2114 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2116 static void
2117 test_one_liner_fixit_insert_after ()
2119 test_diagnostic_context dc;
2120 location_t start = linemap_position_for_column (line_table, 1);
2121 location_t finish = linemap_position_for_column (line_table, 3);
2122 location_t foo = make_location (start, start, finish);
2123 rich_location richloc (line_table, foo);
2124 richloc.add_fixit_insert_after ("[0]");
2125 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2126 ASSERT_STREQ ("\n"
2127 " foo = bar.field;\n"
2128 " ^~~\n"
2129 " [0]\n",
2130 pp_formatted_text (dc.printer));
2133 /* Removal fix-it hint: removal of the ".field". */
2135 static void
2136 test_one_liner_fixit_remove ()
2138 test_diagnostic_context dc;
2139 location_t start = linemap_position_for_column (line_table, 10);
2140 location_t finish = linemap_position_for_column (line_table, 15);
2141 location_t dot = make_location (start, start, finish);
2142 rich_location richloc (line_table, dot);
2143 richloc.add_fixit_remove ();
2144 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2145 ASSERT_STREQ ("\n"
2146 " foo = bar.field;\n"
2147 " ^~~~~~\n"
2148 " ------\n",
2149 pp_formatted_text (dc.printer));
2152 /* Replace fix-it hint: replacing "field" with "m_field". */
2154 static void
2155 test_one_liner_fixit_replace ()
2157 test_diagnostic_context dc;
2158 location_t start = linemap_position_for_column (line_table, 11);
2159 location_t finish = linemap_position_for_column (line_table, 15);
2160 location_t field = make_location (start, start, finish);
2161 rich_location richloc (line_table, field);
2162 richloc.add_fixit_replace ("m_field");
2163 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2164 ASSERT_STREQ ("\n"
2165 " foo = bar.field;\n"
2166 " ^~~~~\n"
2167 " m_field\n",
2168 pp_formatted_text (dc.printer));
2171 /* Replace fix-it hint: replacing "field" with "m_field",
2172 but where the caret was elsewhere. */
2174 static void
2175 test_one_liner_fixit_replace_non_equal_range ()
2177 test_diagnostic_context dc;
2178 location_t equals = linemap_position_for_column (line_table, 5);
2179 location_t start = linemap_position_for_column (line_table, 11);
2180 location_t finish = linemap_position_for_column (line_table, 15);
2181 rich_location richloc (line_table, equals);
2182 source_range range;
2183 range.m_start = start;
2184 range.m_finish = finish;
2185 richloc.add_fixit_replace (range, "m_field");
2186 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2187 /* The replacement range is not indicated in the annotation line, so
2188 it should be indicated via an additional underline. */
2189 ASSERT_STREQ ("\n"
2190 " foo = bar.field;\n"
2191 " ^\n"
2192 " -----\n"
2193 " m_field\n",
2194 pp_formatted_text (dc.printer));
2197 /* Replace fix-it hint: replacing "field" with "m_field",
2198 where the caret was elsewhere, but where a secondary range
2199 exactly covers "field". */
2201 static void
2202 test_one_liner_fixit_replace_equal_secondary_range ()
2204 test_diagnostic_context dc;
2205 location_t equals = linemap_position_for_column (line_table, 5);
2206 location_t start = linemap_position_for_column (line_table, 11);
2207 location_t finish = linemap_position_for_column (line_table, 15);
2208 rich_location richloc (line_table, equals);
2209 location_t field = make_location (start, start, finish);
2210 richloc.add_range (field, false);
2211 richloc.add_fixit_replace (field, "m_field");
2212 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2213 /* The replacement range is indicated in the annotation line,
2214 so it shouldn't be indicated via an additional underline. */
2215 ASSERT_STREQ ("\n"
2216 " foo = bar.field;\n"
2217 " ^ ~~~~~\n"
2218 " m_field\n",
2219 pp_formatted_text (dc.printer));
2222 /* Verify that we can use ad-hoc locations when adding fixits to a
2223 rich_location. */
2225 static void
2226 test_one_liner_fixit_validation_adhoc_locations ()
2228 /* Generate a range that's too long to be packed, so must
2229 be stored as an ad-hoc location (given the defaults
2230 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2231 const location_t c7 = linemap_position_for_column (line_table, 7);
2232 const location_t c47 = linemap_position_for_column (line_table, 47);
2233 const location_t loc = make_location (c7, c7, c47);
2235 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2236 return;
2238 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2240 /* Insert. */
2242 rich_location richloc (line_table, loc);
2243 richloc.add_fixit_insert_before (loc, "test");
2244 /* It should not have been discarded by the validator. */
2245 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2247 test_diagnostic_context dc;
2248 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2249 ASSERT_STREQ ("\n"
2250 " foo = bar.field;\n"
2251 " ^~~~~~~~~~ \n"
2252 " test\n",
2253 pp_formatted_text (dc.printer));
2256 /* Remove. */
2258 rich_location richloc (line_table, loc);
2259 source_range range = source_range::from_locations (loc, c47);
2260 richloc.add_fixit_remove (range);
2261 /* It should not have been discarded by the validator. */
2262 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2264 test_diagnostic_context dc;
2265 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2266 ASSERT_STREQ ("\n"
2267 " foo = bar.field;\n"
2268 " ^~~~~~~~~~ \n"
2269 " -----------------------------------------\n",
2270 pp_formatted_text (dc.printer));
2273 /* Replace. */
2275 rich_location richloc (line_table, loc);
2276 source_range range = source_range::from_locations (loc, c47);
2277 richloc.add_fixit_replace (range, "test");
2278 /* It should not have been discarded by the validator. */
2279 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2281 test_diagnostic_context dc;
2282 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2283 ASSERT_STREQ ("\n"
2284 " foo = bar.field;\n"
2285 " ^~~~~~~~~~ \n"
2286 " test\n",
2287 pp_formatted_text (dc.printer));
2291 /* Test of consolidating insertions at the same location. */
2293 static void
2294 test_one_liner_many_fixits_1 ()
2296 test_diagnostic_context dc;
2297 location_t equals = linemap_position_for_column (line_table, 5);
2298 rich_location richloc (line_table, equals);
2299 for (int i = 0; i < 19; i++)
2300 richloc.add_fixit_insert_before ("a");
2301 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2302 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2303 ASSERT_STREQ ("\n"
2304 " foo = bar.field;\n"
2305 " ^\n"
2306 " aaaaaaaaaaaaaaaaaaa\n",
2307 pp_formatted_text (dc.printer));
2310 /* Ensure that we can add an arbitrary number of fix-it hints to a
2311 rich_location, even if they are not consolidated. */
2313 static void
2314 test_one_liner_many_fixits_2 ()
2316 test_diagnostic_context dc;
2317 location_t equals = linemap_position_for_column (line_table, 5);
2318 rich_location richloc (line_table, equals);
2319 for (int i = 0; i < 19; i++)
2321 location_t loc = linemap_position_for_column (line_table, i * 2);
2322 richloc.add_fixit_insert_before (loc, "a");
2324 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2325 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2326 ASSERT_STREQ ("\n"
2327 " foo = bar.field;\n"
2328 " ^\n"
2329 "a a a a a a a a a a a a a a a a a a a\n",
2330 pp_formatted_text (dc.printer));
2333 /* Run the various one-liner tests. */
2335 static void
2336 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2338 /* Create a tempfile and write some text to it.
2339 ....................0000000001111111.
2340 ....................1234567890123456. */
2341 const char *content = "foo = bar.field;\n";
2342 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2343 line_table_test ltt (case_);
2345 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2347 location_t line_end = linemap_position_for_column (line_table, 16);
2349 /* Don't attempt to run the tests if column data might be unavailable. */
2350 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2351 return;
2353 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2354 ASSERT_EQ (1, LOCATION_LINE (line_end));
2355 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2357 test_one_liner_simple_caret ();
2358 test_one_liner_caret_and_range ();
2359 test_one_liner_multiple_carets_and_ranges ();
2360 test_one_liner_fixit_insert_before ();
2361 test_one_liner_fixit_insert_after ();
2362 test_one_liner_fixit_remove ();
2363 test_one_liner_fixit_replace ();
2364 test_one_liner_fixit_replace_non_equal_range ();
2365 test_one_liner_fixit_replace_equal_secondary_range ();
2366 test_one_liner_fixit_validation_adhoc_locations ();
2367 test_one_liner_many_fixits_1 ();
2368 test_one_liner_many_fixits_2 ();
2371 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2373 static void
2374 test_add_location_if_nearby (const line_table_case &case_)
2376 /* Create a tempfile and write some text to it.
2377 ...000000000111111111122222222223333333333.
2378 ...123456789012345678901234567890123456789. */
2379 const char *content
2380 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2381 "struct different_line\n" /* line 2. */
2382 "{\n" /* line 3. */
2383 " double x;\n" /* line 4. */
2384 " double y;\n" /* line 5. */
2385 ";\n"); /* line 6. */
2386 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2387 line_table_test ltt (case_);
2389 const line_map_ordinary *ord_map
2390 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2391 tmp.get_filename (), 0));
2393 linemap_line_start (line_table, 1, 100);
2395 const location_t final_line_end
2396 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2398 /* Don't attempt to run the tests if column data might be unavailable. */
2399 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2400 return;
2402 /* Test of add_location_if_nearby on the same line as the
2403 primary location. */
2405 const location_t missing_close_brace_1_39
2406 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2407 const location_t matching_open_brace_1_18
2408 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2409 gcc_rich_location richloc (missing_close_brace_1_39);
2410 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2411 ASSERT_TRUE (added);
2412 ASSERT_EQ (2, richloc.get_num_locations ());
2413 test_diagnostic_context dc;
2414 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2415 ASSERT_STREQ ("\n"
2416 " struct same_line { double x; double y; ;\n"
2417 " ~ ^\n",
2418 pp_formatted_text (dc.printer));
2421 /* Test of add_location_if_nearby on a different line to the
2422 primary location. */
2424 const location_t missing_close_brace_6_1
2425 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2426 const location_t matching_open_brace_3_1
2427 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2428 gcc_rich_location richloc (missing_close_brace_6_1);
2429 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2430 ASSERT_FALSE (added);
2431 ASSERT_EQ (1, richloc.get_num_locations ());
2435 /* Verify that we print fixits even if they only affect lines
2436 outside those covered by the ranges in the rich_location. */
2438 static void
2439 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2441 /* Create a tempfile and write some text to it.
2442 ...000000000111111111122222222223333333333.
2443 ...123456789012345678901234567890123456789. */
2444 const char *content
2445 = ("struct point { double x; double y; };\n" /* line 1. */
2446 "struct point origin = {x: 0.0,\n" /* line 2. */
2447 " y\n" /* line 3. */
2448 "\n" /* line 4. */
2449 "\n" /* line 5. */
2450 " : 0.0};\n"); /* line 6. */
2451 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2452 line_table_test ltt (case_);
2454 const line_map_ordinary *ord_map
2455 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2456 tmp.get_filename (), 0));
2458 linemap_line_start (line_table, 1, 100);
2460 const location_t final_line_end
2461 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2463 /* Don't attempt to run the tests if column data might be unavailable. */
2464 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2465 return;
2467 /* A pair of tests for modernizing the initializers to C99-style. */
2469 /* The one-liner case (line 2). */
2471 test_diagnostic_context dc;
2472 const location_t x
2473 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2474 const location_t colon
2475 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2476 rich_location richloc (line_table, colon);
2477 richloc.add_fixit_insert_before (x, ".");
2478 richloc.add_fixit_replace (colon, "=");
2479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2480 ASSERT_STREQ ("\n"
2481 " struct point origin = {x: 0.0,\n"
2482 " ^\n"
2483 " .=\n",
2484 pp_formatted_text (dc.printer));
2487 /* The multiline case. The caret for the rich_location is on line 6;
2488 verify that insertion fixit on line 3 is still printed (and that
2489 span starts are printed due to the gap between the span at line 3
2490 and that at line 6). */
2492 test_diagnostic_context dc;
2493 const location_t y
2494 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2495 const location_t colon
2496 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2497 rich_location richloc (line_table, colon);
2498 richloc.add_fixit_insert_before (y, ".");
2499 richloc.add_fixit_replace (colon, "=");
2500 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2501 ASSERT_STREQ ("\n"
2502 "FILENAME:3:24:\n"
2503 " y\n"
2504 " .\n"
2505 "FILENAME:6:25:\n"
2506 " : 0.0};\n"
2507 " ^\n"
2508 " =\n",
2509 pp_formatted_text (dc.printer));
2514 /* Verify that fix-it hints are appropriately consolidated.
2516 If any fix-it hints in a rich_location involve locations beyond
2517 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2518 the fix-it as a whole, so there should be none.
2520 Otherwise, verify that consecutive "replace" and "remove" fix-its
2521 are merged, and that other fix-its remain separate. */
2523 static void
2524 test_fixit_consolidation (const line_table_case &case_)
2526 line_table_test ltt (case_);
2528 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2530 const location_t c10 = linemap_position_for_column (line_table, 10);
2531 const location_t c15 = linemap_position_for_column (line_table, 15);
2532 const location_t c16 = linemap_position_for_column (line_table, 16);
2533 const location_t c17 = linemap_position_for_column (line_table, 17);
2534 const location_t c20 = linemap_position_for_column (line_table, 20);
2535 const location_t c21 = linemap_position_for_column (line_table, 21);
2536 const location_t caret = c10;
2538 /* Insert + insert. */
2540 rich_location richloc (line_table, caret);
2541 richloc.add_fixit_insert_before (c10, "foo");
2542 richloc.add_fixit_insert_before (c15, "bar");
2544 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2545 /* Bogus column info for 2nd fixit, so no fixits. */
2546 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2547 else
2548 /* They should not have been merged. */
2549 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2552 /* Insert + replace. */
2554 rich_location richloc (line_table, caret);
2555 richloc.add_fixit_insert_before (c10, "foo");
2556 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2557 "bar");
2559 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2560 /* Bogus column info for 2nd fixit, so no fixits. */
2561 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2562 else
2563 /* They should not have been merged. */
2564 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2567 /* Replace + non-consecutive insert. */
2569 rich_location richloc (line_table, caret);
2570 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2571 "bar");
2572 richloc.add_fixit_insert_before (c17, "foo");
2574 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2575 /* Bogus column info for 2nd fixit, so no fixits. */
2576 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2577 else
2578 /* They should not have been merged. */
2579 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2582 /* Replace + non-consecutive replace. */
2584 rich_location richloc (line_table, caret);
2585 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2586 "foo");
2587 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2588 "bar");
2590 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2591 /* Bogus column info for 2nd fixit, so no fixits. */
2592 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2593 else
2594 /* They should not have been merged. */
2595 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2598 /* Replace + consecutive replace. */
2600 rich_location richloc (line_table, caret);
2601 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2602 "foo");
2603 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2604 "bar");
2606 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2607 /* Bogus column info for 2nd fixit, so no fixits. */
2608 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2609 else
2611 /* They should have been merged into a single "replace". */
2612 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2613 const fixit_hint *hint = richloc.get_fixit_hint (0);
2614 ASSERT_STREQ ("foobar", hint->get_string ());
2615 ASSERT_EQ (c10, hint->get_start_loc ());
2616 ASSERT_EQ (c21, hint->get_next_loc ());
2620 /* Replace + consecutive removal. */
2622 rich_location richloc (line_table, caret);
2623 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2624 "foo");
2625 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2627 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2628 /* Bogus column info for 2nd fixit, so no fixits. */
2629 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2630 else
2632 /* They should have been merged into a single replace, with the
2633 range extended to cover that of the removal. */
2634 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2635 const fixit_hint *hint = richloc.get_fixit_hint (0);
2636 ASSERT_STREQ ("foo", hint->get_string ());
2637 ASSERT_EQ (c10, hint->get_start_loc ());
2638 ASSERT_EQ (c21, hint->get_next_loc ());
2642 /* Consecutive removals. */
2644 rich_location richloc (line_table, caret);
2645 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2646 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2648 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2649 /* Bogus column info for 2nd fixit, so no fixits. */
2650 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2651 else
2653 /* They should have been merged into a single "replace-with-empty". */
2654 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2655 const fixit_hint *hint = richloc.get_fixit_hint (0);
2656 ASSERT_STREQ ("", hint->get_string ());
2657 ASSERT_EQ (c10, hint->get_start_loc ());
2658 ASSERT_EQ (c21, hint->get_next_loc ());
2663 /* Verify that the line_corrections machinery correctly prints
2664 overlapping fixit-hints. */
2666 static void
2667 test_overlapped_fixit_printing (const line_table_case &case_)
2669 /* Create a tempfile and write some text to it.
2670 ...000000000111111111122222222223333333333.
2671 ...123456789012345678901234567890123456789. */
2672 const char *content
2673 = (" foo *f = (foo *)ptr->field;\n");
2674 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2675 line_table_test ltt (case_);
2677 const line_map_ordinary *ord_map
2678 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2679 tmp.get_filename (), 0));
2681 linemap_line_start (line_table, 1, 100);
2683 const location_t final_line_end
2684 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2686 /* Don't attempt to run the tests if column data might be unavailable. */
2687 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2688 return;
2690 /* A test for converting a C-style cast to a C++-style cast. */
2691 const location_t open_paren
2692 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2693 const location_t close_paren
2694 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2695 const location_t expr_start
2696 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2697 const location_t expr_finish
2698 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2699 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2701 /* Various examples of fix-it hints that aren't themselves consolidated,
2702 but for which the *printing* may need consolidation. */
2704 /* Example where 3 fix-it hints are printed as one. */
2706 test_diagnostic_context dc;
2707 rich_location richloc (line_table, expr);
2708 richloc.add_fixit_replace (open_paren, "const_cast<");
2709 richloc.add_fixit_replace (close_paren, "> (");
2710 richloc.add_fixit_insert_after (")");
2712 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2713 ASSERT_STREQ ("\n"
2714 " foo *f = (foo *)ptr->field;\n"
2715 " ^~~~~~~~~~\n"
2716 " -----------------\n"
2717 " const_cast<foo *> (ptr->field)\n",
2718 pp_formatted_text (dc.printer));
2720 /* Unit-test the line_corrections machinery. */
2721 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2722 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2723 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2724 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2725 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2726 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2727 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2728 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2729 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2730 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2732 /* Add each hint in turn to a line_corrections instance,
2733 and verify that they are consolidated into one correction instance
2734 as expected. */
2735 line_corrections lc (tmp.get_filename (), 1);
2737 /* The first replace hint by itself. */
2738 lc.add_hint (hint_0);
2739 ASSERT_EQ (1, lc.m_corrections.length ());
2740 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2741 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2742 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2744 /* After the second replacement hint, they are printed together
2745 as a replacement (along with the text between them). */
2746 lc.add_hint (hint_1);
2747 ASSERT_EQ (1, lc.m_corrections.length ());
2748 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2749 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2750 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2752 /* After the final insertion hint, they are all printed together
2753 as a replacement (along with the text between them). */
2754 lc.add_hint (hint_2);
2755 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2756 lc.m_corrections[0]->m_text);
2757 ASSERT_EQ (1, lc.m_corrections.length ());
2758 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2759 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2762 /* Example where two are consolidated during printing. */
2764 test_diagnostic_context dc;
2765 rich_location richloc (line_table, expr);
2766 richloc.add_fixit_replace (open_paren, "CAST (");
2767 richloc.add_fixit_replace (close_paren, ") (");
2768 richloc.add_fixit_insert_after (")");
2770 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2771 ASSERT_STREQ ("\n"
2772 " foo *f = (foo *)ptr->field;\n"
2773 " ^~~~~~~~~~\n"
2774 " -\n"
2775 " CAST (-\n"
2776 " ) ( )\n",
2777 pp_formatted_text (dc.printer));
2780 /* Example where none are consolidated during printing. */
2782 test_diagnostic_context dc;
2783 rich_location richloc (line_table, expr);
2784 richloc.add_fixit_replace (open_paren, "CST (");
2785 richloc.add_fixit_replace (close_paren, ") (");
2786 richloc.add_fixit_insert_after (")");
2788 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2789 ASSERT_STREQ ("\n"
2790 " foo *f = (foo *)ptr->field;\n"
2791 " ^~~~~~~~~~\n"
2792 " -\n"
2793 " CST ( -\n"
2794 " ) ( )\n",
2795 pp_formatted_text (dc.printer));
2798 /* Example of deletion fix-it hints. */
2800 test_diagnostic_context dc;
2801 rich_location richloc (line_table, expr);
2802 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2803 source_range victim = {open_paren, close_paren};
2804 richloc.add_fixit_remove (victim);
2806 /* This case is actually handled by fixit-consolidation,
2807 rather than by line_corrections. */
2808 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2810 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2811 ASSERT_STREQ ("\n"
2812 " foo *f = (foo *)ptr->field;\n"
2813 " ^~~~~~~~~~\n"
2814 " -------\n"
2815 " (bar *)\n",
2816 pp_formatted_text (dc.printer));
2819 /* Example of deletion fix-it hints that would overlap. */
2821 test_diagnostic_context dc;
2822 rich_location richloc (line_table, expr);
2823 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2824 source_range victim = {expr_start, expr_finish};
2825 richloc.add_fixit_remove (victim);
2827 /* These fixits are not consolidated. */
2828 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2830 /* But the corrections are. */
2831 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2832 ASSERT_STREQ ("\n"
2833 " foo *f = (foo *)ptr->field;\n"
2834 " ^~~~~~~~~~\n"
2835 " -----------------\n"
2836 " (longer *)(foo *)\n",
2837 pp_formatted_text (dc.printer));
2840 /* Example of insertion fix-it hints that would overlap. */
2842 test_diagnostic_context dc;
2843 rich_location richloc (line_table, expr);
2844 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2845 richloc.add_fixit_insert_after (close_paren, "TEST");
2847 /* The first insertion is long enough that if printed naively,
2848 it would overlap with the second.
2849 Verify that they are printed as a single replacement. */
2850 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2851 ASSERT_STREQ ("\n"
2852 " foo *f = (foo *)ptr->field;\n"
2853 " ^~~~~~~~~~\n"
2854 " -------\n"
2855 " LONGER THAN THE CAST(foo *)TEST\n",
2856 pp_formatted_text (dc.printer));
2860 /* Verify that the line_corrections machinery correctly prints
2861 overlapping fixit-hints that have been added in the wrong
2862 order.
2863 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
2865 static void
2866 test_overlapped_fixit_printing_2 (const line_table_case &case_)
2868 /* Create a tempfile and write some text to it.
2869 ...000000000111111111122222222223333333333.
2870 ...123456789012345678901234567890123456789. */
2871 const char *content
2872 = ("int a5[][0][0] = { 1, 2 };\n");
2873 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2874 line_table_test ltt (case_);
2876 const line_map_ordinary *ord_map
2877 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2878 tmp.get_filename (), 0));
2880 linemap_line_start (line_table, 1, 100);
2882 const location_t final_line_end
2883 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
2885 /* Don't attempt to run the tests if column data might be unavailable. */
2886 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2887 return;
2889 const location_t col_1
2890 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2891 const location_t col_20
2892 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
2893 const location_t col_21
2894 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
2895 const location_t col_23
2896 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
2897 const location_t col_25
2898 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
2900 /* Two insertions, in the wrong order. */
2902 rich_location richloc (line_table, col_20);
2903 richloc.add_fixit_insert_before (col_23, "{");
2904 richloc.add_fixit_insert_before (col_21, "}");
2906 /* These fixits should be accepted; they can't be consolidated. */
2907 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2908 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2909 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
2910 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
2911 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2912 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
2913 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
2915 /* Verify that they're printed correctly. */
2916 test_diagnostic_context dc;
2917 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2918 ASSERT_STREQ ("\n"
2919 " int a5[][0][0] = { 1, 2 };\n"
2920 " ^\n"
2921 " } {\n",
2922 pp_formatted_text (dc.printer));
2925 /* Various overlapping insertions, some occurring "out of order"
2926 (reproducing the fix-it hints from PR c/81405). */
2928 test_diagnostic_context dc;
2929 rich_location richloc (line_table, col_20);
2931 richloc.add_fixit_insert_before (col_20, "{{");
2932 richloc.add_fixit_insert_before (col_21, "}}");
2933 richloc.add_fixit_insert_before (col_23, "{");
2934 richloc.add_fixit_insert_before (col_21, "}");
2935 richloc.add_fixit_insert_before (col_23, "{{");
2936 richloc.add_fixit_insert_before (col_25, "}");
2937 richloc.add_fixit_insert_before (col_21, "}");
2938 richloc.add_fixit_insert_before (col_1, "{");
2939 richloc.add_fixit_insert_before (col_25, "}");
2940 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2941 ASSERT_STREQ ("\n"
2942 " int a5[][0][0] = { 1, 2 };\n"
2943 " ^\n"
2944 " { -----\n"
2945 " {{1}}}}, {{{2 }}\n",
2946 pp_formatted_text (dc.printer));
2950 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2952 static void
2953 test_fixit_insert_containing_newline (const line_table_case &case_)
2955 /* Create a tempfile and write some text to it.
2956 .........................0000000001111111.
2957 .........................1234567890123456. */
2958 const char *old_content = (" case 'a':\n" /* line 1. */
2959 " x = a;\n" /* line 2. */
2960 " case 'b':\n" /* line 3. */
2961 " x = b;\n");/* line 4. */
2963 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2964 line_table_test ltt (case_);
2965 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2967 location_t case_start = linemap_position_for_column (line_table, 5);
2968 location_t case_finish = linemap_position_for_column (line_table, 13);
2969 location_t case_loc = make_location (case_start, case_start, case_finish);
2970 location_t line_start = linemap_position_for_column (line_table, 1);
2972 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2973 return;
2975 /* Add a "break;" on a line by itself before line 3 i.e. before
2976 column 1 of line 3. */
2978 rich_location richloc (line_table, case_loc);
2979 richloc.add_fixit_insert_before (line_start, " break;\n");
2980 test_diagnostic_context dc;
2981 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2982 ASSERT_STREQ ("\n"
2983 "+ break;\n"
2984 " case 'b':\n"
2985 " ^~~~~~~~~\n",
2986 pp_formatted_text (dc.printer));
2989 /* Verify that attempts to add text with a newline fail when the
2990 insertion point is *not* at the start of a line. */
2992 rich_location richloc (line_table, case_loc);
2993 richloc.add_fixit_insert_before (case_start, "break;\n");
2994 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2995 test_diagnostic_context dc;
2996 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2997 ASSERT_STREQ ("\n"
2998 " case 'b':\n"
2999 " ^~~~~~~~~\n",
3000 pp_formatted_text (dc.printer));
3004 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3005 of the file, where the fix-it is printed in a different line-span
3006 to the primary range of the diagnostic. */
3008 static void
3009 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3011 /* Create a tempfile and write some text to it.
3012 .........................0000000001111111.
3013 .........................1234567890123456. */
3014 const char *old_content = ("test (int ch)\n" /* line 1. */
3015 "{\n" /* line 2. */
3016 " putchar (ch);\n" /* line 3. */
3017 "}\n"); /* line 4. */
3019 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3020 line_table_test ltt (case_);
3022 const line_map_ordinary *ord_map = linemap_check_ordinary
3023 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3024 linemap_line_start (line_table, 1, 100);
3026 /* The primary range is the "putchar" token. */
3027 location_t putchar_start
3028 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3029 location_t putchar_finish
3030 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3031 location_t putchar_loc
3032 = make_location (putchar_start, putchar_start, putchar_finish);
3033 rich_location richloc (line_table, putchar_loc);
3035 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3036 location_t file_start
3037 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3038 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3040 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3041 return;
3043 test_diagnostic_context dc;
3044 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3045 ASSERT_STREQ ("\n"
3046 "FILENAME:1:1:\n"
3047 "+#include <stdio.h>\n"
3048 " test (int ch)\n"
3049 "FILENAME:3:2:\n"
3050 " putchar (ch);\n"
3051 " ^~~~~~~\n",
3052 pp_formatted_text (dc.printer));
3055 /* Replacement fix-it hint containing a newline.
3056 This will fail, as newlines are only supported when inserting at the
3057 beginning of a line. */
3059 static void
3060 test_fixit_replace_containing_newline (const line_table_case &case_)
3062 /* Create a tempfile and write some text to it.
3063 .........................0000000001111.
3064 .........................1234567890123. */
3065 const char *old_content = "foo = bar ();\n";
3067 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3068 line_table_test ltt (case_);
3069 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3071 /* Replace the " = " with "\n = ", as if we were reformatting an
3072 overly long line. */
3073 location_t start = linemap_position_for_column (line_table, 4);
3074 location_t finish = linemap_position_for_column (line_table, 6);
3075 location_t loc = linemap_position_for_column (line_table, 13);
3076 rich_location richloc (line_table, loc);
3077 source_range range = source_range::from_locations (start, finish);
3078 richloc.add_fixit_replace (range, "\n =");
3080 /* Arbitrary newlines are not yet supported within fix-it hints, so
3081 the fix-it should not be displayed. */
3082 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3084 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3085 return;
3087 test_diagnostic_context dc;
3088 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3089 ASSERT_STREQ ("\n"
3090 " foo = bar ();\n"
3091 " ^\n",
3092 pp_formatted_text (dc.printer));
3095 /* Fix-it hint, attempting to delete a newline.
3096 This will fail, as we currently only support fix-it hints that
3097 affect one line at a time. */
3099 static void
3100 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3102 /* Create a tempfile and write some text to it.
3103 ..........................0000000001111.
3104 ..........................1234567890123. */
3105 const char *old_content = ("foo = bar (\n"
3106 " );\n");
3108 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3109 line_table_test ltt (case_);
3110 const line_map_ordinary *ord_map = linemap_check_ordinary
3111 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3112 linemap_line_start (line_table, 1, 100);
3114 /* Attempt to delete the " (\n...)". */
3115 location_t start
3116 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3117 location_t caret
3118 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3119 location_t finish
3120 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3121 location_t loc = make_location (caret, start, finish);
3122 rich_location richloc (line_table, loc);
3123 richloc. add_fixit_remove ();
3125 /* Fix-it hints that affect more than one line are not yet supported, so
3126 the fix-it should not be displayed. */
3127 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3129 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3130 return;
3132 test_diagnostic_context dc;
3133 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3134 ASSERT_STREQ ("\n"
3135 " foo = bar (\n"
3136 " ~^\n"
3137 " );\n"
3138 " ~ \n",
3139 pp_formatted_text (dc.printer));
3142 /* Run all of the selftests within this file. */
3144 void
3145 diagnostic_show_locus_c_tests ()
3147 test_line_span ();
3149 test_layout_range_for_single_point ();
3150 test_layout_range_for_single_line ();
3151 test_layout_range_for_multiple_lines ();
3153 test_get_line_width_without_trailing_whitespace ();
3155 test_diagnostic_show_locus_unknown_location ();
3157 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3158 for_each_line_table_case (test_add_location_if_nearby);
3159 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3160 for_each_line_table_case (test_fixit_consolidation);
3161 for_each_line_table_case (test_overlapped_fixit_printing);
3162 for_each_line_table_case (test_overlapped_fixit_printing_2);
3163 for_each_line_table_case (test_fixit_insert_containing_newline);
3164 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3165 for_each_line_table_case (test_fixit_replace_containing_newline);
3166 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3169 } // namespace selftest
3171 #endif /* #if CHECKING_P */