Use non-throwing is_directory in filesystem::create_directory
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob5eee3cc144991339db35a0670f4ab4b3808936eb
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 int 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 (int row, int column) const;
133 bool intersects_line_p (int 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_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
176 if (first_line_diff)
177 return first_line_diff;
178 return (int)ls1->m_last_line - (int)ls2->m_last_line;
181 linenum_type m_first_line;
182 linenum_type m_last_line;
185 /* A class to control the overall layout when printing a diagnostic.
187 The layout is determined within the constructor.
188 It is then printed by repeatedly calling the "print_source_line",
189 "print_annotation_line" and "print_any_fixits" methods.
191 We assume we have disjoint ranges. */
193 class layout
195 public:
196 layout (diagnostic_context *context,
197 rich_location *richloc,
198 diagnostic_t diagnostic_kind);
200 bool maybe_add_location_range (const location_range *loc_range,
201 bool restrict_to_current_line_spans);
203 int get_num_line_spans () const { return m_line_spans.length (); }
204 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
206 bool print_heading_for_line_span_index_p (int line_span_idx) const;
208 expanded_location get_expanded_location (const line_span *) const;
210 void print_line (int row);
212 private:
213 bool will_show_line_p (int row) const;
214 void print_leading_fixits (int row);
215 void print_source_line (int row, const char *line, int line_width,
216 line_bounds *lbounds_out);
217 bool should_print_annotation_line_p (int row) const;
218 void print_annotation_line (int row, const line_bounds lbounds);
219 void print_trailing_fixits (int row);
221 bool annotation_line_showed_range_p (int line, int start_column,
222 int finish_column) const;
223 void show_ruler (int max_column) const;
225 bool validate_fixit_hint_p (const fixit_hint *hint);
227 void calculate_line_spans ();
229 void print_newline ();
231 bool
232 get_state_at_point (/* Inputs. */
233 int row, int column,
234 int first_non_ws, int last_non_ws,
235 /* Outputs. */
236 point_state *out_state);
239 get_x_bound_for_row (int row, int caret_column,
240 int last_non_ws);
242 void
243 move_to_column (int *column, int dest_column);
245 private:
246 diagnostic_context *m_context;
247 pretty_printer *m_pp;
248 diagnostic_t m_diagnostic_kind;
249 location_t m_primary_loc;
250 expanded_location m_exploc;
251 colorizer m_colorizer;
252 bool m_colorize_source_p;
253 auto_vec <layout_range> m_layout_ranges;
254 auto_vec <const fixit_hint *> m_fixit_hints;
255 auto_vec <line_span> m_line_spans;
256 int m_x_offset;
259 /* Implementation of "class colorizer". */
261 /* The constructor for "colorizer". Lookup and store color codes for the
262 different kinds of things we might need to print. */
264 colorizer::colorizer (diagnostic_context *context,
265 diagnostic_t diagnostic_kind) :
266 m_context (context),
267 m_diagnostic_kind (diagnostic_kind),
268 m_current_state (STATE_NORMAL_TEXT)
270 m_range1 = get_color_by_name ("range1");
271 m_range2 = get_color_by_name ("range2");
272 m_fixit_insert = get_color_by_name ("fixit-insert");
273 m_fixit_delete = get_color_by_name ("fixit-delete");
274 m_stop_color = colorize_stop (pp_show_color (context->printer));
277 /* The destructor for "colorize". If colorization is on, print a code to
278 turn it off. */
280 colorizer::~colorizer ()
282 finish_state (m_current_state);
285 /* Update state, printing color codes if necessary if there's a state
286 change. */
288 void
289 colorizer::set_state (int new_state)
291 if (m_current_state != new_state)
293 finish_state (m_current_state);
294 m_current_state = new_state;
295 begin_state (new_state);
299 /* Turn on any colorization for STATE. */
301 void
302 colorizer::begin_state (int state)
304 switch (state)
306 case STATE_NORMAL_TEXT:
307 break;
309 case STATE_FIXIT_INSERT:
310 pp_string (m_context->printer, m_fixit_insert);
311 break;
313 case STATE_FIXIT_DELETE:
314 pp_string (m_context->printer, m_fixit_delete);
315 break;
317 case 0:
318 /* Make range 0 be the same color as the "kind" text
319 (error vs warning vs note). */
320 pp_string
321 (m_context->printer,
322 colorize_start (pp_show_color (m_context->printer),
323 diagnostic_get_color_for_kind (m_diagnostic_kind)));
324 break;
326 case 1:
327 pp_string (m_context->printer, m_range1);
328 break;
330 case 2:
331 pp_string (m_context->printer, m_range2);
332 break;
334 default:
335 /* For ranges beyond 2, alternate between color 1 and color 2. */
337 gcc_assert (state > 2);
338 pp_string (m_context->printer,
339 state % 2 ? m_range1 : m_range2);
341 break;
345 /* Turn off any colorization for STATE. */
347 void
348 colorizer::finish_state (int state)
350 if (state != STATE_NORMAL_TEXT)
351 pp_string (m_context->printer, m_stop_color);
354 /* Get the color code for NAME (or the empty string if
355 colorization is disabled). */
357 const char *
358 colorizer::get_color_by_name (const char *name)
360 return colorize_start (pp_show_color (m_context->printer), name);
363 /* Implementation of class layout_range. */
365 /* The constructor for class layout_range.
366 Initialize various layout_point fields from expanded_location
367 equivalents; we've already filtered on file. */
369 layout_range::layout_range (const expanded_location *start_exploc,
370 const expanded_location *finish_exploc,
371 bool show_caret_p,
372 const expanded_location *caret_exploc)
373 : m_start (*start_exploc),
374 m_finish (*finish_exploc),
375 m_show_caret_p (show_caret_p),
376 m_caret (*caret_exploc)
380 /* Is (column, row) within the given range?
381 We've already filtered on the file.
383 Ranges are closed (both limits are within the range).
385 Example A: a single-line range:
386 start: (col=22, line=2)
387 finish: (col=38, line=2)
389 |00000011111111112222222222333333333344444444444
390 |34567890123456789012345678901234567890123456789
391 --+-----------------------------------------------
392 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
393 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
394 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
396 Example B: a multiline range with
397 start: (col=14, line=3)
398 finish: (col=08, line=5)
400 |00000011111111112222222222333333333344444444444
401 |34567890123456789012345678901234567890123456789
402 --+-----------------------------------------------
403 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
404 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
405 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
406 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
407 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
408 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
409 --+-----------------------------------------------
411 Legend:
412 - 'b' indicates a point *before* the range
413 - 'S' indicates the start of the range
414 - 'w' indicates a point within the range
415 - 'F' indicates the finish of the range (which is
416 within it).
417 - 'a' indicates a subsequent point *after* the range. */
419 bool
420 layout_range::contains_point (int row, int column) const
422 gcc_assert (m_start.m_line <= m_finish.m_line);
423 /* ...but the equivalent isn't true for the columns;
424 consider example B in the comment above. */
426 if (row < m_start.m_line)
427 /* Points before the first line of the range are
428 outside it (corresponding to line 01 in example A
429 and lines 01 and 02 in example B above). */
430 return false;
432 if (row == m_start.m_line)
433 /* On same line as start of range (corresponding
434 to line 02 in example A and line 03 in example B). */
436 if (column < m_start.m_column)
437 /* Points on the starting line of the range, but
438 before the column in which it begins. */
439 return false;
441 if (row < m_finish.m_line)
442 /* This is a multiline range; the point
443 is within it (corresponds to line 03 in example B
444 from column 14 onwards) */
445 return true;
446 else
448 /* This is a single-line range. */
449 gcc_assert (row == m_finish.m_line);
450 return column <= m_finish.m_column;
454 /* The point is in a line beyond that containing the
455 start of the range: lines 03 onwards in example A,
456 and lines 04 onwards in example B. */
457 gcc_assert (row > m_start.m_line);
459 if (row > m_finish.m_line)
460 /* The point is beyond the final line of the range
461 (lines 03 onwards in example A, and lines 06 onwards
462 in example B). */
463 return false;
465 if (row < m_finish.m_line)
467 /* The point is in a line that's fully within a multiline
468 range (e.g. line 04 in example B). */
469 gcc_assert (m_start.m_line < m_finish.m_line);
470 return true;
473 gcc_assert (row == m_finish.m_line);
475 return column <= m_finish.m_column;
478 /* Does this layout_range contain any part of line ROW? */
480 bool
481 layout_range::intersects_line_p (int row) const
483 gcc_assert (m_start.m_line <= m_finish.m_line);
484 if (row < m_start.m_line)
485 return false;
486 if (row > m_finish.m_line)
487 return false;
488 return true;
491 #if CHECKING_P
493 /* A helper function for testing layout_range. */
495 static layout_range
496 make_range (int start_line, int start_col, int end_line, int end_col)
498 const expanded_location start_exploc
499 = {"test.c", start_line, start_col, NULL, false};
500 const expanded_location finish_exploc
501 = {"test.c", end_line, end_col, NULL, false};
502 return layout_range (&start_exploc, &finish_exploc, false,
503 &start_exploc);
506 /* Selftests for layout_range::contains_point and
507 layout_range::intersects_line_p. */
509 /* Selftest for layout_range, where the layout_range
510 is a range with start==end i.e. a single point. */
512 static void
513 test_layout_range_for_single_point ()
515 layout_range point = make_range (7, 10, 7, 10);
517 /* Tests for layout_range::contains_point. */
519 /* Before the line. */
520 ASSERT_FALSE (point.contains_point (6, 1));
522 /* On the line, but before start. */
523 ASSERT_FALSE (point.contains_point (7, 9));
525 /* At the point. */
526 ASSERT_TRUE (point.contains_point (7, 10));
528 /* On the line, after the point. */
529 ASSERT_FALSE (point.contains_point (7, 11));
531 /* After the line. */
532 ASSERT_FALSE (point.contains_point (8, 1));
534 /* Tests for layout_range::intersects_line_p. */
535 ASSERT_FALSE (point.intersects_line_p (6));
536 ASSERT_TRUE (point.intersects_line_p (7));
537 ASSERT_FALSE (point.intersects_line_p (8));
540 /* Selftest for layout_range, where the layout_range
541 is the single-line range shown as "Example A" above. */
543 static void
544 test_layout_range_for_single_line ()
546 layout_range example_a = make_range (2, 22, 2, 38);
548 /* Tests for layout_range::contains_point. */
550 /* Before the line. */
551 ASSERT_FALSE (example_a.contains_point (1, 1));
553 /* On the line, but before start. */
554 ASSERT_FALSE (example_a.contains_point (2, 21));
556 /* On the line, at the start. */
557 ASSERT_TRUE (example_a.contains_point (2, 22));
559 /* On the line, within the range. */
560 ASSERT_TRUE (example_a.contains_point (2, 23));
562 /* On the line, at the end. */
563 ASSERT_TRUE (example_a.contains_point (2, 38));
565 /* On the line, after the end. */
566 ASSERT_FALSE (example_a.contains_point (2, 39));
568 /* After the line. */
569 ASSERT_FALSE (example_a.contains_point (2, 39));
571 /* Tests for layout_range::intersects_line_p. */
572 ASSERT_FALSE (example_a.intersects_line_p (1));
573 ASSERT_TRUE (example_a.intersects_line_p (2));
574 ASSERT_FALSE (example_a.intersects_line_p (3));
577 /* Selftest for layout_range, where the layout_range
578 is the multi-line range shown as "Example B" above. */
580 static void
581 test_layout_range_for_multiple_lines ()
583 layout_range example_b = make_range (3, 14, 5, 8);
585 /* Tests for layout_range::contains_point. */
587 /* Before first line. */
588 ASSERT_FALSE (example_b.contains_point (1, 1));
590 /* On the first line, but before start. */
591 ASSERT_FALSE (example_b.contains_point (3, 13));
593 /* At the start. */
594 ASSERT_TRUE (example_b.contains_point (3, 14));
596 /* On the first line, within the range. */
597 ASSERT_TRUE (example_b.contains_point (3, 15));
599 /* On an interior line.
600 The column number should not matter; try various boundary
601 values. */
602 ASSERT_TRUE (example_b.contains_point (4, 1));
603 ASSERT_TRUE (example_b.contains_point (4, 7));
604 ASSERT_TRUE (example_b.contains_point (4, 8));
605 ASSERT_TRUE (example_b.contains_point (4, 9));
606 ASSERT_TRUE (example_b.contains_point (4, 13));
607 ASSERT_TRUE (example_b.contains_point (4, 14));
608 ASSERT_TRUE (example_b.contains_point (4, 15));
610 /* On the final line, before the end. */
611 ASSERT_TRUE (example_b.contains_point (5, 7));
613 /* On the final line, at the end. */
614 ASSERT_TRUE (example_b.contains_point (5, 8));
616 /* On the final line, after the end. */
617 ASSERT_FALSE (example_b.contains_point (5, 9));
619 /* After the line. */
620 ASSERT_FALSE (example_b.contains_point (6, 1));
622 /* Tests for layout_range::intersects_line_p. */
623 ASSERT_FALSE (example_b.intersects_line_p (2));
624 ASSERT_TRUE (example_b.intersects_line_p (3));
625 ASSERT_TRUE (example_b.intersects_line_p (4));
626 ASSERT_TRUE (example_b.intersects_line_p (5));
627 ASSERT_FALSE (example_b.intersects_line_p (6));
630 #endif /* #if CHECKING_P */
632 /* Given a source line LINE of length LINE_WIDTH, determine the width
633 without any trailing whitespace. */
635 static int
636 get_line_width_without_trailing_whitespace (const char *line, int line_width)
638 int result = line_width;
639 while (result > 0)
641 char ch = line[result - 1];
642 if (ch == ' ' || ch == '\t' || ch == '\r')
643 result--;
644 else
645 break;
647 gcc_assert (result >= 0);
648 gcc_assert (result <= line_width);
649 gcc_assert (result == 0 ||
650 (line[result - 1] != ' '
651 && line[result -1] != '\t'
652 && line[result -1] != '\r'));
653 return result;
656 #if CHECKING_P
658 /* A helper function for testing get_line_width_without_trailing_whitespace. */
660 static void
661 assert_eq (const char *line, int expected_width)
663 int actual_value
664 = get_line_width_without_trailing_whitespace (line, strlen (line));
665 ASSERT_EQ (actual_value, expected_width);
668 /* Verify that get_line_width_without_trailing_whitespace is sane for
669 various inputs. It is not required to handle newlines. */
671 static void
672 test_get_line_width_without_trailing_whitespace ()
674 assert_eq ("", 0);
675 assert_eq (" ", 0);
676 assert_eq ("\t", 0);
677 assert_eq ("\r", 0);
678 assert_eq ("hello world", 11);
679 assert_eq ("hello world ", 11);
680 assert_eq ("hello world \t\t ", 11);
681 assert_eq ("hello world\r", 11);
684 #endif /* #if CHECKING_P */
686 /* Helper function for layout's ctor, for sanitizing locations relative
687 to the primary location within a diagnostic.
689 Compare LOC_A and LOC_B to see if it makes sense to print underlines
690 connecting their expanded locations. Doing so is only guaranteed to
691 make sense if the locations share the same macro expansion "history"
692 i.e. they can be traced through the same macro expansions, eventually
693 reaching an ordinary map.
695 This may be too strong a condition, but it effectively sanitizes
696 PR c++/70105, which has an example of printing an expression where the
697 final location of the expression is in a different macro, which
698 erroneously was leading to hundreds of lines of irrelevant source
699 being printed. */
701 static bool
702 compatible_locations_p (location_t loc_a, location_t loc_b)
704 if (IS_ADHOC_LOC (loc_a))
705 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
706 if (IS_ADHOC_LOC (loc_b))
707 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
709 /* If either location is one of the special locations outside of a
710 linemap, they are only compatible if they are equal. */
711 if (loc_a < RESERVED_LOCATION_COUNT
712 || loc_b < RESERVED_LOCATION_COUNT)
713 return loc_a == loc_b;
715 const line_map *map_a = linemap_lookup (line_table, loc_a);
716 linemap_assert (map_a);
718 const line_map *map_b = linemap_lookup (line_table, loc_b);
719 linemap_assert (map_b);
721 /* Are they within the same map? */
722 if (map_a == map_b)
724 /* Are both within the same macro expansion? */
725 if (linemap_macro_expansion_map_p (map_a))
727 /* Expand each location towards the spelling location, and
728 recurse. */
729 const line_map_macro *macro_map = linemap_check_macro (map_a);
730 source_location loc_a_toward_spelling
731 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
732 macro_map,
733 loc_a);
734 source_location loc_b_toward_spelling
735 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
736 macro_map,
737 loc_b);
738 return compatible_locations_p (loc_a_toward_spelling,
739 loc_b_toward_spelling);
742 /* Otherwise they are within the same ordinary map. */
743 return true;
745 else
747 /* Within different maps. */
749 /* If either is within a macro expansion, they are incompatible. */
750 if (linemap_macro_expansion_map_p (map_a)
751 || linemap_macro_expansion_map_p (map_b))
752 return false;
754 /* Within two different ordinary maps; they are compatible iff they
755 are in the same file. */
756 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
757 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
758 return ord_map_a->to_file == ord_map_b->to_file;
762 /* Comparator for sorting fix-it hints. */
764 static int
765 fixit_cmp (const void *p_a, const void *p_b)
767 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
768 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
769 return hint_a->get_start_loc () - hint_b->get_start_loc ();
772 /* Implementation of class layout. */
774 /* Constructor for class layout.
776 Filter the ranges from the rich_location to those that we can
777 sanely print, populating m_layout_ranges and m_fixit_hints.
778 Determine the range of lines that we will print, splitting them
779 up into an ordered list of disjoint spans of contiguous line numbers.
780 Determine m_x_offset, to ensure that the primary caret
781 will fit within the max_width provided by the diagnostic_context. */
783 layout::layout (diagnostic_context * context,
784 rich_location *richloc,
785 diagnostic_t diagnostic_kind)
786 : m_context (context),
787 m_pp (context->printer),
788 m_diagnostic_kind (diagnostic_kind),
789 m_primary_loc (richloc->get_range (0)->m_loc),
790 m_exploc (richloc->get_expanded_location (0)),
791 m_colorizer (context, diagnostic_kind),
792 m_colorize_source_p (context->colorize_source_p),
793 m_layout_ranges (richloc->get_num_locations ()),
794 m_fixit_hints (richloc->get_num_fixit_hints ()),
795 m_line_spans (1 + richloc->get_num_locations ()),
796 m_x_offset (0)
798 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
800 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
801 Ignore any ranges that are awkward to handle. */
802 const location_range *loc_range = richloc->get_range (idx);
803 maybe_add_location_range (loc_range, false);
806 /* Populate m_fixit_hints, filtering to only those that are in the
807 same file. */
808 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
810 const fixit_hint *hint = richloc->get_fixit_hint (i);
811 if (validate_fixit_hint_p (hint))
812 m_fixit_hints.safe_push (hint);
815 /* Sort m_fixit_hints. */
816 m_fixit_hints.qsort (fixit_cmp);
818 /* Populate m_line_spans. */
819 calculate_line_spans ();
821 /* Adjust m_x_offset.
822 Center the primary caret to fit in max_width; all columns
823 will be adjusted accordingly. */
824 int max_width = m_context->caret_max_width;
825 int line_width;
826 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
827 &line_width);
828 if (line && m_exploc.column <= line_width)
830 int right_margin = CARET_LINE_MARGIN;
831 int column = m_exploc.column;
832 right_margin = MIN (line_width - column, right_margin);
833 right_margin = max_width - right_margin;
834 if (line_width >= max_width && column > right_margin)
835 m_x_offset = column - right_margin;
836 gcc_assert (m_x_offset >= 0);
839 if (context->show_ruler_p)
840 show_ruler (m_x_offset + max_width);
843 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
844 those that we can sanely print.
846 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
847 filtered against this layout instance's current line spans: it
848 will only be added if the location is fully within the lines
849 already specified by other locations.
851 Return true iff LOC_RANGE was added. */
853 bool
854 layout::maybe_add_location_range (const location_range *loc_range,
855 bool restrict_to_current_line_spans)
857 gcc_assert (loc_range);
859 /* Split the "range" into caret and range information. */
860 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
862 /* Expand the various locations. */
863 expanded_location start
864 = linemap_client_expand_location_to_spelling_point
865 (src_range.m_start, LOCATION_ASPECT_START);
866 expanded_location finish
867 = linemap_client_expand_location_to_spelling_point
868 (src_range.m_finish, LOCATION_ASPECT_FINISH);
869 expanded_location caret
870 = linemap_client_expand_location_to_spelling_point
871 (loc_range->m_loc, LOCATION_ASPECT_CARET);
873 /* If any part of the range isn't in the same file as the primary
874 location of this diagnostic, ignore the range. */
875 if (start.file != m_exploc.file)
876 return false;
877 if (finish.file != m_exploc.file)
878 return false;
879 if (loc_range->m_show_caret_p)
880 if (caret.file != m_exploc.file)
881 return false;
883 /* Sanitize the caret location for non-primary ranges. */
884 if (m_layout_ranges.length () > 0)
885 if (loc_range->m_show_caret_p)
886 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
887 /* Discard any non-primary ranges that can't be printed
888 sanely relative to the primary location. */
889 return false;
891 /* Everything is now known to be in the correct source file,
892 but it may require further sanitization. */
893 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
895 /* If we have a range that finishes before it starts (perhaps
896 from something built via macro expansion), printing the
897 range is likely to be nonsensical. Also, attempting to do so
898 breaks assumptions within the printing code (PR c/68473).
899 Similarly, don't attempt to print ranges if one or both ends
900 of the range aren't sane to print relative to the
901 primary location (PR c++/70105). */
902 if (start.line > finish.line
903 || !compatible_locations_p (src_range.m_start, m_primary_loc)
904 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
906 /* Is this the primary location? */
907 if (m_layout_ranges.length () == 0)
909 /* We want to print the caret for the primary location, but
910 we must sanitize away m_start and m_finish. */
911 ri.m_start = ri.m_caret;
912 ri.m_finish = ri.m_caret;
914 else
915 /* This is a non-primary range; ignore it. */
916 return false;
919 /* Potentially filter to just the lines already specified by other
920 locations. This is for use by gcc_rich_location::add_location_if_nearby.
921 The layout ctor doesn't use it, and can't because m_line_spans
922 hasn't been set up at that point. */
923 if (restrict_to_current_line_spans)
925 if (!will_show_line_p (start.line))
926 return false;
927 if (!will_show_line_p (finish.line))
928 return false;
929 if (loc_range->m_show_caret_p)
930 if (!will_show_line_p (caret.line))
931 return false;
934 /* Passed all the tests; add the range to m_layout_ranges so that
935 it will be printed. */
936 m_layout_ranges.safe_push (ri);
937 return true;
940 /* Return true iff ROW is within one of the line spans for this layout. */
942 bool
943 layout::will_show_line_p (int row) const
945 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
946 line_span_idx++)
948 const line_span *line_span = get_line_span (line_span_idx);
949 if (line_span->contains_line_p (row))
950 return true;
952 return false;
955 /* Return true iff we should print a heading when starting the
956 line span with the given index. */
958 bool
959 layout::print_heading_for_line_span_index_p (int line_span_idx) const
961 /* We print a heading for every change of line span, hence for every
962 line span after the initial one. */
963 if (line_span_idx > 0)
964 return true;
966 /* We also do it for the initial span if the primary location of the
967 diagnostic is in a different span. */
968 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
969 return true;
971 return false;
974 /* Get an expanded_location for the first location of interest within
975 the given line_span.
976 Used when printing a heading to indicate a new line span. */
978 expanded_location
979 layout::get_expanded_location (const line_span *line_span) const
981 /* Whenever possible, use the caret location. */
982 if (line_span->contains_line_p (m_exploc.line))
983 return m_exploc;
985 /* Otherwise, use the start of the first range that's present
986 within the line_span. */
987 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
989 const layout_range *lr = &m_layout_ranges[i];
990 if (line_span->contains_line_p (lr->m_start.m_line))
992 expanded_location exploc = m_exploc;
993 exploc.line = lr->m_start.m_line;
994 exploc.column = lr->m_start.m_column;
995 return exploc;
999 /* Otherwise, use the location of the first fixit-hint present within
1000 the line_span. */
1001 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1003 const fixit_hint *hint = m_fixit_hints[i];
1004 location_t loc = hint->get_start_loc ();
1005 expanded_location exploc = expand_location (loc);
1006 if (line_span->contains_line_p (exploc.line))
1007 return exploc;
1010 /* It should not be possible to have a line span that didn't
1011 contain any of the layout_range or fixit_hint instances. */
1012 gcc_unreachable ();
1013 return m_exploc;
1016 /* Determine if HINT is meaningful to print within this layout. */
1018 bool
1019 layout::validate_fixit_hint_p (const fixit_hint *hint)
1021 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1022 return false;
1023 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1024 return false;
1026 return true;
1029 /* Determine the range of lines affected by HINT.
1030 This assumes that HINT has already been filtered by
1031 validate_fixit_hint_p, and so affects the correct source file. */
1033 static line_span
1034 get_line_span_for_fixit_hint (const fixit_hint *hint)
1036 gcc_assert (hint);
1037 return line_span (LOCATION_LINE (hint->get_start_loc ()),
1038 LOCATION_LINE (hint->get_next_loc ()));
1041 /* We want to print the pertinent source code at a diagnostic. The
1042 rich_location can contain multiple locations. This will have been
1043 filtered into m_exploc (the caret for the primary location) and
1044 m_layout_ranges, for those ranges within the same source file.
1046 We will print a subset of the lines within the source file in question,
1047 as a collection of "spans" of lines.
1049 This function populates m_line_spans with an ordered, disjoint list of
1050 the line spans of interest.
1052 For example, if the primary caret location is on line 7, with ranges
1053 covering lines 5-6 and lines 9-12:
1056 005 |RANGE 0
1057 006 |RANGE 0
1058 007 |PRIMARY CARET
1060 009 |RANGE 1
1061 010 |RANGE 1
1062 011 |RANGE 1
1063 012 |RANGE 1
1066 then we want two spans: lines 5-7 and lines 9-12. */
1068 void
1069 layout::calculate_line_spans ()
1071 /* This should only be called once, by the ctor. */
1072 gcc_assert (m_line_spans.length () == 0);
1074 /* Populate tmp_spans with individual spans, for each of
1075 m_exploc, and for m_layout_ranges. */
1076 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1077 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1078 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1080 const layout_range *lr = &m_layout_ranges[i];
1081 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1082 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1083 lr->m_finish.m_line));
1086 /* Also add spans for any fix-it hints, in case they cover other lines. */
1087 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1089 const fixit_hint *hint = m_fixit_hints[i];
1090 gcc_assert (hint);
1091 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1094 /* Sort them. */
1095 tmp_spans.qsort(line_span::comparator);
1097 /* Now iterate through tmp_spans, copying into m_line_spans, and
1098 combining where possible. */
1099 gcc_assert (tmp_spans.length () > 0);
1100 m_line_spans.safe_push (tmp_spans[0]);
1101 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1103 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1104 const line_span *next = &tmp_spans[i];
1105 gcc_assert (next->m_first_line >= current->m_first_line);
1106 if (next->m_first_line <= current->m_last_line + 1)
1108 /* We can merge them. */
1109 if (next->m_last_line > current->m_last_line)
1110 current->m_last_line = next->m_last_line;
1112 else
1114 /* No merger possible. */
1115 m_line_spans.safe_push (*next);
1119 /* Verify the result, in m_line_spans. */
1120 gcc_assert (m_line_spans.length () > 0);
1121 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1123 const line_span *prev = &m_line_spans[i - 1];
1124 const line_span *next = &m_line_spans[i];
1125 /* The individual spans must be sane. */
1126 gcc_assert (prev->m_first_line <= prev->m_last_line);
1127 gcc_assert (next->m_first_line <= next->m_last_line);
1128 /* The spans must be ordered. */
1129 gcc_assert (prev->m_first_line < next->m_first_line);
1130 /* There must be a gap of at least one line between separate spans. */
1131 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1135 /* Print line ROW of source code, potentially colorized at any ranges, and
1136 populate *LBOUNDS_OUT.
1137 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1138 is its width. */
1140 void
1141 layout::print_source_line (int row, const char *line, int line_width,
1142 line_bounds *lbounds_out)
1144 m_colorizer.set_normal_text ();
1146 /* We will stop printing the source line at any trailing
1147 whitespace. */
1148 line_width = get_line_width_without_trailing_whitespace (line,
1149 line_width);
1150 line += m_x_offset;
1152 pp_space (m_pp);
1153 int first_non_ws = INT_MAX;
1154 int last_non_ws = 0;
1155 int column;
1156 for (column = 1 + m_x_offset; column <= line_width; column++)
1158 /* Assuming colorization is enabled for the caret and underline
1159 characters, we may also colorize the associated characters
1160 within the source line.
1162 For frontends that generate range information, we color the
1163 associated characters in the source line the same as the
1164 carets and underlines in the annotation line, to make it easier
1165 for the reader to see the pertinent code.
1167 For frontends that only generate carets, we don't colorize the
1168 characters above them, since this would look strange (e.g.
1169 colorizing just the first character in a token). */
1170 if (m_colorize_source_p)
1172 bool in_range_p;
1173 point_state state;
1174 in_range_p = get_state_at_point (row, column,
1175 0, INT_MAX,
1176 &state);
1177 if (in_range_p)
1178 m_colorizer.set_range (state.range_idx);
1179 else
1180 m_colorizer.set_normal_text ();
1182 char c = *line;
1183 if (c == '\0' || c == '\t' || c == '\r')
1184 c = ' ';
1185 if (c != ' ')
1187 last_non_ws = column;
1188 if (first_non_ws == INT_MAX)
1189 first_non_ws = column;
1191 pp_character (m_pp, c);
1192 line++;
1194 print_newline ();
1196 lbounds_out->m_first_non_ws = first_non_ws;
1197 lbounds_out->m_last_non_ws = last_non_ws;
1200 /* Determine if we should print an annotation line for ROW.
1201 i.e. if any of m_layout_ranges contains ROW. */
1203 bool
1204 layout::should_print_annotation_line_p (int row) const
1206 layout_range *range;
1207 int i;
1208 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1209 if (range->intersects_line_p (row))
1210 return true;
1211 return false;
1214 /* Print a line consisting of the caret/underlines for the given
1215 source line. */
1217 void
1218 layout::print_annotation_line (int row, const line_bounds lbounds)
1220 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1221 lbounds.m_last_non_ws);
1223 pp_space (m_pp);
1224 for (int column = 1 + m_x_offset; column < x_bound; column++)
1226 bool in_range_p;
1227 point_state state;
1228 in_range_p = get_state_at_point (row, column,
1229 lbounds.m_first_non_ws,
1230 lbounds.m_last_non_ws,
1231 &state);
1232 if (in_range_p)
1234 /* Within a range. Draw either the caret or an underline. */
1235 m_colorizer.set_range (state.range_idx);
1236 if (state.draw_caret_p)
1238 /* Draw the caret. */
1239 char caret_char;
1240 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1241 caret_char = m_context->caret_chars[state.range_idx];
1242 else
1243 caret_char = '^';
1244 pp_character (m_pp, caret_char);
1246 else
1247 pp_character (m_pp, '~');
1249 else
1251 /* Not in a range. */
1252 m_colorizer.set_normal_text ();
1253 pp_character (m_pp, ' ');
1256 print_newline ();
1259 /* If there are any fixit hints inserting new lines before source line ROW,
1260 print them.
1262 They are printed on lines of their own, before the source line
1263 itself, with a leading '+'. */
1265 void
1266 layout::print_leading_fixits (int row)
1268 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1270 const fixit_hint *hint = m_fixit_hints[i];
1272 if (!hint->ends_with_newline_p ())
1273 /* Not a newline fixit; print it in print_trailing_fixits. */
1274 continue;
1276 gcc_assert (hint->insertion_p ());
1278 if (hint->affects_line_p (m_exploc.file, row))
1280 /* Printing the '+' with normal colorization
1281 and the inserted line with "insert" colorization
1282 helps them stand out from each other, and from
1283 the surrounding text. */
1284 m_colorizer.set_normal_text ();
1285 pp_character (m_pp, '+');
1286 m_colorizer.set_fixit_insert ();
1287 /* Print all but the trailing newline of the fix-it hint.
1288 We have to print the newline separately to avoid
1289 getting additional pp prefixes printed. */
1290 for (size_t i = 0; i < hint->get_length () - 1; i++)
1291 pp_character (m_pp, hint->get_string ()[i]);
1292 m_colorizer.set_normal_text ();
1293 pp_newline (m_pp);
1298 /* Subroutine of layout::print_trailing_fixits.
1300 Determine if the annotation line printed for LINE contained
1301 the exact range from START_COLUMN to FINISH_COLUMN. */
1303 bool
1304 layout::annotation_line_showed_range_p (int line, int start_column,
1305 int finish_column) const
1307 layout_range *range;
1308 int i;
1309 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1310 if (range->m_start.m_line == line
1311 && range->m_start.m_column == start_column
1312 && range->m_finish.m_line == line
1313 && range->m_finish.m_column == finish_column)
1314 return true;
1315 return false;
1318 /* Classes for printing trailing fix-it hints i.e. those that
1319 don't add new lines.
1321 For insertion, these can look like:
1323 new_text
1325 For replacement, these can look like:
1327 ------------- : underline showing affected range
1328 new_text
1330 For deletion, these can look like:
1332 ------------- : underline showing affected range
1334 This can become confusing if they overlap, and so we need
1335 to do some preprocessing to decide what to print.
1336 We use the list of fixit_hint instances affecting the line
1337 to build a list of "correction" instances, and print the
1338 latter.
1340 For example, consider a set of fix-its for converting
1341 a C-style cast to a C++ const_cast.
1343 Given:
1345 ..000000000111111111122222222223333333333.
1346 ..123456789012345678901234567890123456789.
1347 foo *f = (foo *)ptr->field;
1348 ^~~~~
1350 and the fix-it hints:
1351 - replace col 10 (the open paren) with "const_cast<"
1352 - replace col 16 (the close paren) with "> ("
1353 - insert ")" before col 27
1355 then we would get odd-looking output:
1357 foo *f = (foo *)ptr->field;
1358 ^~~~~
1360 const_cast<
1362 > ( )
1364 It would be better to detect when fixit hints are going to
1365 overlap (those that require new lines), and to consolidate
1366 the printing of such fixits, giving something like:
1368 foo *f = (foo *)ptr->field;
1369 ^~~~~
1370 -----------------
1371 const_cast<foo *> (ptr->field)
1373 This works by detecting when the printing would overlap, and
1374 effectively injecting no-op replace hints into the gaps between
1375 such fix-its, so that the printing joins up.
1377 In the above example, the overlap of:
1378 - replace col 10 (the open paren) with "const_cast<"
1379 and:
1380 - replace col 16 (the close paren) with "> ("
1381 is fixed by injecting a no-op:
1382 - replace cols 11-15 with themselves ("foo *")
1383 and consolidating these, making:
1384 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1385 i.e.:
1386 - replace cols 10-16 with "const_cast<foo *> ("
1388 This overlaps with the final fix-it hint:
1389 - insert ")" before col 27
1390 and so we repeat the consolidation process, by injecting
1391 a no-op:
1392 - replace cols 17-26 with themselves ("ptr->field")
1393 giving:
1394 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1395 i.e.:
1396 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1398 and is thus printed as desired. */
1400 /* A range of columns within a line. */
1402 struct column_range
1404 column_range (int start_, int finish_) : start (start_), finish (finish_)
1406 /* We must have either a range, or an insertion. */
1407 gcc_assert (start <= finish || finish == start - 1);
1410 bool operator== (const column_range &other) const
1412 return start == other.start && finish == other.finish;
1415 int start;
1416 int finish;
1419 /* Get the range of columns that HINT would affect. */
1421 static column_range
1422 get_affected_columns (const fixit_hint *hint)
1424 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1425 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1427 return column_range (start_column, finish_column);
1430 /* Get the range of columns that would be printed for HINT. */
1432 static column_range
1433 get_printed_columns (const fixit_hint *hint)
1435 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1436 int final_hint_column = start_column + hint->get_length () - 1;
1437 if (hint->insertion_p ())
1439 return column_range (start_column, final_hint_column);
1441 else
1443 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1445 return column_range (start_column,
1446 MAX (finish_column, final_hint_column));
1450 /* A struct capturing the bounds of a buffer, to allow for run-time
1451 bounds-checking in a checked build. */
1453 struct char_span
1455 char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {}
1457 char_span subspan (int offset, int n_elts)
1459 gcc_assert (offset >= 0);
1460 gcc_assert (offset < (int)m_n_elts);
1461 gcc_assert (n_elts >= 0);
1462 gcc_assert (offset + n_elts <= (int)m_n_elts);
1463 return char_span (m_ptr + offset, n_elts);
1466 const char *m_ptr;
1467 size_t m_n_elts;
1470 /* A correction on a particular line.
1471 This describes a plan for how to print one or more fixit_hint
1472 instances that affected the line, potentially consolidating hints
1473 into corrections to make the result easier for the user to read. */
1475 struct correction
1477 correction (column_range affected_columns,
1478 column_range printed_columns,
1479 const char *new_text, size_t new_text_len)
1480 : m_affected_columns (affected_columns),
1481 m_printed_columns (printed_columns),
1482 m_text (xstrdup (new_text)),
1483 m_len (new_text_len),
1484 m_alloc_sz (new_text_len + 1)
1488 ~correction () { free (m_text); }
1490 bool insertion_p () const
1492 return m_affected_columns.start == m_affected_columns.finish + 1;
1495 void ensure_capacity (size_t len);
1496 void ensure_terminated ();
1498 void overwrite (int dst_offset, const char_span &src_span)
1500 gcc_assert (dst_offset >= 0);
1501 gcc_assert (dst_offset + src_span.m_n_elts < m_alloc_sz);
1502 memcpy (m_text + dst_offset, src_span.m_ptr,
1503 src_span.m_n_elts);
1506 /* If insert, then start: the column before which the text
1507 is to be inserted, and finish is offset by the length of
1508 the replacement.
1509 If replace, then the range of columns affected. */
1510 column_range m_affected_columns;
1512 /* If insert, then start: the column before which the text
1513 is to be inserted, and finish is offset by the length of
1514 the replacement.
1515 If replace, then the range of columns affected. */
1516 column_range m_printed_columns;
1518 /* The text to be inserted/used as replacement. */
1519 char *m_text;
1520 size_t m_len;
1521 size_t m_alloc_sz;
1524 /* Ensure that m_text can hold a string of length LEN
1525 (plus 1 for 0-termination). */
1527 void
1528 correction::ensure_capacity (size_t len)
1530 /* Allow 1 extra byte for 0-termination. */
1531 if (m_alloc_sz < (len + 1))
1533 size_t new_alloc_sz = (len + 1) * 2;
1534 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1535 m_alloc_sz = new_alloc_sz;
1539 /* Ensure that m_text is 0-terminated. */
1541 void
1542 correction::ensure_terminated ()
1544 /* 0-terminate the buffer. */
1545 gcc_assert (m_len < m_alloc_sz);
1546 m_text[m_len] = '\0';
1549 /* A list of corrections affecting a particular line.
1550 This is used by layout::print_trailing_fixits for planning
1551 how to print the fix-it hints affecting the line. */
1553 struct line_corrections
1555 line_corrections (const char *filename, int row)
1556 : m_filename (filename), m_row (row)
1558 ~line_corrections ();
1560 void add_hint (const fixit_hint *hint);
1562 const char *m_filename;
1563 int m_row;
1564 auto_vec <correction *> m_corrections;
1567 /* struct line_corrections. */
1569 line_corrections::~line_corrections ()
1571 unsigned i;
1572 correction *c;
1573 FOR_EACH_VEC_ELT (m_corrections, i, c)
1574 delete c;
1577 /* A struct wrapping a particular source line, allowing
1578 run-time bounds-checking of accesses in a checked build. */
1580 struct source_line
1582 source_line (const char *filename, int line);
1584 char_span as_span () { return char_span (chars, width); }
1586 const char *chars;
1587 int width;
1590 /* source_line's ctor. */
1592 source_line::source_line (const char *filename, int line)
1594 chars = location_get_source_line (filename, line, &width);
1597 /* Add HINT to the corrections for this line.
1598 Attempt to consolidate nearby hints so that they will not
1599 overlap with printed. */
1601 void
1602 line_corrections::add_hint (const fixit_hint *hint)
1604 column_range affected_columns = get_affected_columns (hint);
1605 column_range printed_columns = get_printed_columns (hint);
1607 /* Potentially consolidate. */
1608 if (!m_corrections.is_empty ())
1610 correction *last_correction
1611 = m_corrections[m_corrections.length () - 1];
1613 /* The following consolidation code assumes that the fix-it hints
1614 have been sorted by start (done within layout's ctor). */
1615 gcc_assert (affected_columns.start
1616 >= last_correction->m_affected_columns.start);
1617 gcc_assert (printed_columns.start
1618 >= last_correction->m_printed_columns.start);
1620 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1622 /* We have two hints for which the printed forms of the hints
1623 would touch or overlap, so we need to consolidate them to avoid
1624 confusing the user.
1625 Attempt to inject a "replace" correction from immediately
1626 after the end of the last hint to immediately before the start
1627 of the next hint. */
1628 column_range between (last_correction->m_affected_columns.finish + 1,
1629 printed_columns.start - 1);
1631 /* Try to read the source. */
1632 source_line line (m_filename, m_row);
1633 if (line.chars && between.finish < line.width)
1635 /* Consolidate into the last correction:
1636 add a no-op "replace" of the "between" text, and
1637 add the text from the new hint. */
1638 int old_len = last_correction->m_len;
1639 gcc_assert (old_len >= 0);
1640 int between_len = between.finish + 1 - between.start;
1641 gcc_assert (between_len >= 0);
1642 int new_len = old_len + between_len + hint->get_length ();
1643 gcc_assert (new_len >= 0);
1644 last_correction->ensure_capacity (new_len);
1645 last_correction->overwrite
1646 (old_len,
1647 line.as_span ().subspan (between.start - 1,
1648 between.finish + 1 - between.start));
1649 last_correction->overwrite (old_len + between_len,
1650 char_span (hint->get_string (),
1651 hint->get_length ()));
1652 last_correction->m_len = new_len;
1653 last_correction->ensure_terminated ();
1654 last_correction->m_affected_columns.finish
1655 = affected_columns.finish;
1656 last_correction->m_printed_columns.finish
1657 += between_len + hint->get_length ();
1658 return;
1663 /* If no consolidation happened, add a new correction instance. */
1664 m_corrections.safe_push (new correction (affected_columns,
1665 printed_columns,
1666 hint->get_string (),
1667 hint->get_length ()));
1670 /* If there are any fixit hints on source line ROW, print them.
1671 They are printed in order, attempting to combine them onto lines, but
1672 starting new lines if necessary.
1673 Fix-it hints that insert new lines are handled separately,
1674 in layout::print_leading_fixits. */
1676 void
1677 layout::print_trailing_fixits (int row)
1679 /* Build a list of correction instances for the line,
1680 potentially consolidating hints (for the sake of readability). */
1681 line_corrections corrections (m_exploc.file, row);
1682 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1684 const fixit_hint *hint = m_fixit_hints[i];
1686 /* Newline fixits are handled by layout::print_leading_fixits. */
1687 if (hint->ends_with_newline_p ())
1688 continue;
1690 if (hint->affects_line_p (m_exploc.file, row))
1691 corrections.add_hint (hint);
1694 /* Now print the corrections. */
1695 unsigned i;
1696 correction *c;
1697 int column = m_x_offset;
1699 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1701 /* For now we assume each fixit hint can only touch one line. */
1702 if (c->insertion_p ())
1704 /* This assumes the insertion just affects one line. */
1705 int start_column = c->m_printed_columns.start;
1706 move_to_column (&column, start_column);
1707 m_colorizer.set_fixit_insert ();
1708 pp_string (m_pp, c->m_text);
1709 m_colorizer.set_normal_text ();
1710 column += c->m_len;
1712 else
1714 /* If the range of the replacement wasn't printed in the
1715 annotation line, then print an extra underline to
1716 indicate exactly what is being replaced.
1717 Always show it for removals. */
1718 int start_column = c->m_affected_columns.start;
1719 int finish_column = c->m_affected_columns.finish;
1720 if (!annotation_line_showed_range_p (row, start_column,
1721 finish_column)
1722 || c->m_len == 0)
1724 move_to_column (&column, start_column);
1725 m_colorizer.set_fixit_delete ();
1726 for (; column <= finish_column; column++)
1727 pp_character (m_pp, '-');
1728 m_colorizer.set_normal_text ();
1730 /* Print the replacement text. REPLACE also covers
1731 removals, so only do this extra work (potentially starting
1732 a new line) if we have actual replacement text. */
1733 if (c->m_len > 0)
1735 move_to_column (&column, start_column);
1736 m_colorizer.set_fixit_insert ();
1737 pp_string (m_pp, c->m_text);
1738 m_colorizer.set_normal_text ();
1739 column += c->m_len;
1744 /* Add a trailing newline, if necessary. */
1745 move_to_column (&column, 0);
1748 /* Disable any colorization and emit a newline. */
1750 void
1751 layout::print_newline ()
1753 m_colorizer.set_normal_text ();
1754 pp_newline (m_pp);
1757 /* Return true if (ROW/COLUMN) is within a range of the layout.
1758 If it returns true, OUT_STATE is written to, with the
1759 range index, and whether we should draw the caret at
1760 (ROW/COLUMN) (as opposed to an underline). */
1762 bool
1763 layout::get_state_at_point (/* Inputs. */
1764 int row, int column,
1765 int first_non_ws, int last_non_ws,
1766 /* Outputs. */
1767 point_state *out_state)
1769 layout_range *range;
1770 int i;
1771 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1773 if (range->contains_point (row, column))
1775 out_state->range_idx = i;
1777 /* Are we at the range's caret? is it visible? */
1778 out_state->draw_caret_p = false;
1779 if (range->m_show_caret_p
1780 && row == range->m_caret.m_line
1781 && column == range->m_caret.m_column)
1782 out_state->draw_caret_p = true;
1784 /* Within a multiline range, don't display any underline
1785 in any leading or trailing whitespace on a line.
1786 We do display carets, however. */
1787 if (!out_state->draw_caret_p)
1788 if (column < first_non_ws || column > last_non_ws)
1789 return false;
1791 /* We are within a range. */
1792 return true;
1796 return false;
1799 /* Helper function for use by layout::print_line when printing the
1800 annotation line under the source line.
1801 Get the column beyond the rightmost one that could contain a caret or
1802 range marker, given that we stop rendering at trailing whitespace.
1803 ROW is the source line within the given file.
1804 CARET_COLUMN is the column of range 0's caret.
1805 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1806 character of source (as determined when printing the source line). */
1809 layout::get_x_bound_for_row (int row, int caret_column,
1810 int last_non_ws_column)
1812 int result = caret_column + 1;
1814 layout_range *range;
1815 int i;
1816 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1818 if (row >= range->m_start.m_line)
1820 if (range->m_finish.m_line == row)
1822 /* On the final line within a range; ensure that
1823 we render up to the end of the range. */
1824 if (result <= range->m_finish.m_column)
1825 result = range->m_finish.m_column + 1;
1827 else if (row < range->m_finish.m_line)
1829 /* Within a multiline range; ensure that we render up to the
1830 last non-whitespace column. */
1831 if (result <= last_non_ws_column)
1832 result = last_non_ws_column + 1;
1837 return result;
1840 /* Given *COLUMN as an x-coordinate, print spaces to position
1841 successive output at DEST_COLUMN, printing a newline if necessary,
1842 and updating *COLUMN. */
1844 void
1845 layout::move_to_column (int *column, int dest_column)
1847 /* Start a new line if we need to. */
1848 if (*column > dest_column)
1850 print_newline ();
1851 *column = m_x_offset;
1854 while (*column < dest_column)
1856 pp_space (m_pp);
1857 (*column)++;
1861 /* For debugging layout issues, render a ruler giving column numbers
1862 (after the 1-column indent). */
1864 void
1865 layout::show_ruler (int max_column) const
1867 /* Hundreds. */
1868 if (max_column > 99)
1870 pp_space (m_pp);
1871 for (int column = 1 + m_x_offset; column <= max_column; column++)
1872 if (column % 10 == 0)
1873 pp_character (m_pp, '0' + (column / 100) % 10);
1874 else
1875 pp_space (m_pp);
1876 pp_newline (m_pp);
1879 /* Tens. */
1880 pp_space (m_pp);
1881 for (int column = 1 + m_x_offset; column <= max_column; column++)
1882 if (column % 10 == 0)
1883 pp_character (m_pp, '0' + (column / 10) % 10);
1884 else
1885 pp_space (m_pp);
1886 pp_newline (m_pp);
1888 /* Units. */
1889 pp_space (m_pp);
1890 for (int column = 1 + m_x_offset; column <= max_column; column++)
1891 pp_character (m_pp, '0' + (column % 10));
1892 pp_newline (m_pp);
1895 /* Print leading fix-its (for new lines inserted before the source line)
1896 then the source line, followed by an annotation line
1897 consisting of any caret/underlines, then any fixits.
1898 If the source line can't be read, print nothing. */
1899 void
1900 layout::print_line (int row)
1902 int line_width;
1903 const char *line = location_get_source_line (m_exploc.file, row,
1904 &line_width);
1905 if (!line)
1906 return;
1908 line_bounds lbounds;
1909 print_leading_fixits (row);
1910 print_source_line (row, line, line_width, &lbounds);
1911 if (should_print_annotation_line_p (row))
1912 print_annotation_line (row, lbounds);
1913 print_trailing_fixits (row);
1916 } /* End of anonymous namespace. */
1918 /* If LOC is within the spans of lines that will already be printed for
1919 this gcc_rich_location, then add it as a secondary location and return true.
1921 Otherwise return false. */
1923 bool
1924 gcc_rich_location::add_location_if_nearby (location_t loc)
1926 /* Use the layout location-handling logic to sanitize LOC,
1927 filtering it to the current line spans within a temporary
1928 layout instance. */
1929 layout layout (global_dc, this, DK_ERROR);
1930 location_range loc_range;
1931 loc_range.m_loc = loc;
1932 loc_range.m_show_caret_p = false;
1933 if (!layout.maybe_add_location_range (&loc_range, true))
1934 return false;
1936 add_range (loc, false);
1937 return true;
1940 /* Print the physical source code corresponding to the location of
1941 this diagnostic, with additional annotations. */
1943 void
1944 diagnostic_show_locus (diagnostic_context * context,
1945 rich_location *richloc,
1946 diagnostic_t diagnostic_kind)
1948 pp_newline (context->printer);
1950 location_t loc = richloc->get_loc ();
1951 /* Do nothing if source-printing has been disabled. */
1952 if (!context->show_caret)
1953 return;
1955 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1956 if (loc <= BUILTINS_LOCATION)
1957 return;
1959 /* Don't print the same source location twice in a row, unless we have
1960 fix-it hints. */
1961 if (loc == context->last_location
1962 && richloc->get_num_fixit_hints () == 0)
1963 return;
1965 context->last_location = loc;
1967 const char *saved_prefix = pp_get_prefix (context->printer);
1968 pp_set_prefix (context->printer, NULL);
1970 layout layout (context, richloc, diagnostic_kind);
1971 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1972 line_span_idx++)
1974 const line_span *line_span = layout.get_line_span (line_span_idx);
1975 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1977 expanded_location exploc = layout.get_expanded_location (line_span);
1978 context->start_span (context, exploc);
1980 int last_line = line_span->get_last_line ();
1981 for (int row = line_span->get_first_line (); row <= last_line; row++)
1982 layout.print_line (row);
1985 pp_set_prefix (context->printer, saved_prefix);
1988 #if CHECKING_P
1990 namespace selftest {
1992 /* Selftests for diagnostic_show_locus. */
1994 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1996 static void
1997 test_diagnostic_show_locus_unknown_location ()
1999 test_diagnostic_context dc;
2000 rich_location richloc (line_table, UNKNOWN_LOCATION);
2001 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2002 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2005 /* Verify that diagnostic_show_locus works sanely for various
2006 single-line cases.
2008 All of these work on the following 1-line source file:
2009 .0000000001111111
2010 .1234567890123456
2011 "foo = bar.field;\n"
2012 which is set up by test_diagnostic_show_locus_one_liner and calls
2013 them. */
2015 /* Just a caret. */
2017 static void
2018 test_one_liner_simple_caret ()
2020 test_diagnostic_context dc;
2021 location_t caret = linemap_position_for_column (line_table, 10);
2022 rich_location richloc (line_table, caret);
2023 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2024 ASSERT_STREQ ("\n"
2025 " foo = bar.field;\n"
2026 " ^\n",
2027 pp_formatted_text (dc.printer));
2030 /* Caret and range. */
2032 static void
2033 test_one_liner_caret_and_range ()
2035 test_diagnostic_context dc;
2036 location_t caret = linemap_position_for_column (line_table, 10);
2037 location_t start = linemap_position_for_column (line_table, 7);
2038 location_t finish = linemap_position_for_column (line_table, 15);
2039 location_t loc = make_location (caret, start, finish);
2040 rich_location richloc (line_table, loc);
2041 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2042 ASSERT_STREQ ("\n"
2043 " foo = bar.field;\n"
2044 " ~~~^~~~~~\n",
2045 pp_formatted_text (dc.printer));
2048 /* Multiple ranges and carets. */
2050 static void
2051 test_one_liner_multiple_carets_and_ranges ()
2053 test_diagnostic_context dc;
2054 location_t foo
2055 = make_location (linemap_position_for_column (line_table, 2),
2056 linemap_position_for_column (line_table, 1),
2057 linemap_position_for_column (line_table, 3));
2058 dc.caret_chars[0] = 'A';
2060 location_t bar
2061 = make_location (linemap_position_for_column (line_table, 8),
2062 linemap_position_for_column (line_table, 7),
2063 linemap_position_for_column (line_table, 9));
2064 dc.caret_chars[1] = 'B';
2066 location_t field
2067 = make_location (linemap_position_for_column (line_table, 13),
2068 linemap_position_for_column (line_table, 11),
2069 linemap_position_for_column (line_table, 15));
2070 dc.caret_chars[2] = 'C';
2072 rich_location richloc (line_table, foo);
2073 richloc.add_range (bar, true);
2074 richloc.add_range (field, true);
2075 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2076 ASSERT_STREQ ("\n"
2077 " foo = bar.field;\n"
2078 " ~A~ ~B~ ~~C~~\n",
2079 pp_formatted_text (dc.printer));
2082 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2084 static void
2085 test_one_liner_fixit_insert_before ()
2087 test_diagnostic_context dc;
2088 location_t caret = linemap_position_for_column (line_table, 7);
2089 rich_location richloc (line_table, caret);
2090 richloc.add_fixit_insert_before ("&");
2091 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2092 ASSERT_STREQ ("\n"
2093 " foo = bar.field;\n"
2094 " ^\n"
2095 " &\n",
2096 pp_formatted_text (dc.printer));
2099 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2101 static void
2102 test_one_liner_fixit_insert_after ()
2104 test_diagnostic_context dc;
2105 location_t start = linemap_position_for_column (line_table, 1);
2106 location_t finish = linemap_position_for_column (line_table, 3);
2107 location_t foo = make_location (start, start, finish);
2108 rich_location richloc (line_table, foo);
2109 richloc.add_fixit_insert_after ("[0]");
2110 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2111 ASSERT_STREQ ("\n"
2112 " foo = bar.field;\n"
2113 " ^~~\n"
2114 " [0]\n",
2115 pp_formatted_text (dc.printer));
2118 /* Removal fix-it hint: removal of the ".field". */
2120 static void
2121 test_one_liner_fixit_remove ()
2123 test_diagnostic_context dc;
2124 location_t start = linemap_position_for_column (line_table, 10);
2125 location_t finish = linemap_position_for_column (line_table, 15);
2126 location_t dot = make_location (start, start, finish);
2127 rich_location richloc (line_table, dot);
2128 richloc.add_fixit_remove ();
2129 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2130 ASSERT_STREQ ("\n"
2131 " foo = bar.field;\n"
2132 " ^~~~~~\n"
2133 " ------\n",
2134 pp_formatted_text (dc.printer));
2137 /* Replace fix-it hint: replacing "field" with "m_field". */
2139 static void
2140 test_one_liner_fixit_replace ()
2142 test_diagnostic_context dc;
2143 location_t start = linemap_position_for_column (line_table, 11);
2144 location_t finish = linemap_position_for_column (line_table, 15);
2145 location_t field = make_location (start, start, finish);
2146 rich_location richloc (line_table, field);
2147 richloc.add_fixit_replace ("m_field");
2148 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2149 ASSERT_STREQ ("\n"
2150 " foo = bar.field;\n"
2151 " ^~~~~\n"
2152 " m_field\n",
2153 pp_formatted_text (dc.printer));
2156 /* Replace fix-it hint: replacing "field" with "m_field",
2157 but where the caret was elsewhere. */
2159 static void
2160 test_one_liner_fixit_replace_non_equal_range ()
2162 test_diagnostic_context dc;
2163 location_t equals = linemap_position_for_column (line_table, 5);
2164 location_t start = linemap_position_for_column (line_table, 11);
2165 location_t finish = linemap_position_for_column (line_table, 15);
2166 rich_location richloc (line_table, equals);
2167 source_range range;
2168 range.m_start = start;
2169 range.m_finish = finish;
2170 richloc.add_fixit_replace (range, "m_field");
2171 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2172 /* The replacement range is not indicated in the annotation line, so
2173 it should be indicated via an additional underline. */
2174 ASSERT_STREQ ("\n"
2175 " foo = bar.field;\n"
2176 " ^\n"
2177 " -----\n"
2178 " m_field\n",
2179 pp_formatted_text (dc.printer));
2182 /* Replace fix-it hint: replacing "field" with "m_field",
2183 where the caret was elsewhere, but where a secondary range
2184 exactly covers "field". */
2186 static void
2187 test_one_liner_fixit_replace_equal_secondary_range ()
2189 test_diagnostic_context dc;
2190 location_t equals = linemap_position_for_column (line_table, 5);
2191 location_t start = linemap_position_for_column (line_table, 11);
2192 location_t finish = linemap_position_for_column (line_table, 15);
2193 rich_location richloc (line_table, equals);
2194 location_t field = make_location (start, start, finish);
2195 richloc.add_range (field, false);
2196 richloc.add_fixit_replace (field, "m_field");
2197 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2198 /* The replacement range is indicated in the annotation line,
2199 so it shouldn't be indicated via an additional underline. */
2200 ASSERT_STREQ ("\n"
2201 " foo = bar.field;\n"
2202 " ^ ~~~~~\n"
2203 " m_field\n",
2204 pp_formatted_text (dc.printer));
2207 /* Verify that we can use ad-hoc locations when adding fixits to a
2208 rich_location. */
2210 static void
2211 test_one_liner_fixit_validation_adhoc_locations ()
2213 /* Generate a range that's too long to be packed, so must
2214 be stored as an ad-hoc location (given the defaults
2215 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2216 const location_t c7 = linemap_position_for_column (line_table, 7);
2217 const location_t c47 = linemap_position_for_column (line_table, 47);
2218 const location_t loc = make_location (c7, c7, c47);
2220 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2221 return;
2223 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2225 /* Insert. */
2227 rich_location richloc (line_table, loc);
2228 richloc.add_fixit_insert_before (loc, "test");
2229 /* It should not have been discarded by the validator. */
2230 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2232 test_diagnostic_context dc;
2233 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2234 ASSERT_STREQ ("\n"
2235 " foo = bar.field;\n"
2236 " ^~~~~~~~~~ \n"
2237 " test\n",
2238 pp_formatted_text (dc.printer));
2241 /* Remove. */
2243 rich_location richloc (line_table, loc);
2244 source_range range = source_range::from_locations (loc, c47);
2245 richloc.add_fixit_remove (range);
2246 /* It should not have been discarded by the validator. */
2247 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2249 test_diagnostic_context dc;
2250 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2251 ASSERT_STREQ ("\n"
2252 " foo = bar.field;\n"
2253 " ^~~~~~~~~~ \n"
2254 " -----------------------------------------\n",
2255 pp_formatted_text (dc.printer));
2258 /* Replace. */
2260 rich_location richloc (line_table, loc);
2261 source_range range = source_range::from_locations (loc, c47);
2262 richloc.add_fixit_replace (range, "test");
2263 /* It should not have been discarded by the validator. */
2264 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2266 test_diagnostic_context dc;
2267 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2268 ASSERT_STREQ ("\n"
2269 " foo = bar.field;\n"
2270 " ^~~~~~~~~~ \n"
2271 " test\n",
2272 pp_formatted_text (dc.printer));
2276 /* Test of consolidating insertions at the same location. */
2278 static void
2279 test_one_liner_many_fixits_1 ()
2281 test_diagnostic_context dc;
2282 location_t equals = linemap_position_for_column (line_table, 5);
2283 rich_location richloc (line_table, equals);
2284 for (int i = 0; i < 19; i++)
2285 richloc.add_fixit_insert_before ("a");
2286 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2287 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2288 ASSERT_STREQ ("\n"
2289 " foo = bar.field;\n"
2290 " ^\n"
2291 " aaaaaaaaaaaaaaaaaaa\n",
2292 pp_formatted_text (dc.printer));
2295 /* Ensure that we can add an arbitrary number of fix-it hints to a
2296 rich_location, even if they are not consolidated. */
2298 static void
2299 test_one_liner_many_fixits_2 ()
2301 test_diagnostic_context dc;
2302 location_t equals = linemap_position_for_column (line_table, 5);
2303 rich_location richloc (line_table, equals);
2304 for (int i = 0; i < 19; i++)
2306 location_t loc = linemap_position_for_column (line_table, i * 2);
2307 richloc.add_fixit_insert_before (loc, "a");
2309 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2310 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2311 ASSERT_STREQ ("\n"
2312 " foo = bar.field;\n"
2313 " ^\n"
2314 "a a a a a a a a a a a a a a a a a a a\n",
2315 pp_formatted_text (dc.printer));
2318 /* Run the various one-liner tests. */
2320 static void
2321 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2323 /* Create a tempfile and write some text to it.
2324 ....................0000000001111111.
2325 ....................1234567890123456. */
2326 const char *content = "foo = bar.field;\n";
2327 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2328 line_table_test ltt (case_);
2330 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2332 location_t line_end = linemap_position_for_column (line_table, 16);
2334 /* Don't attempt to run the tests if column data might be unavailable. */
2335 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2336 return;
2338 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2339 ASSERT_EQ (1, LOCATION_LINE (line_end));
2340 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2342 test_one_liner_simple_caret ();
2343 test_one_liner_caret_and_range ();
2344 test_one_liner_multiple_carets_and_ranges ();
2345 test_one_liner_fixit_insert_before ();
2346 test_one_liner_fixit_insert_after ();
2347 test_one_liner_fixit_remove ();
2348 test_one_liner_fixit_replace ();
2349 test_one_liner_fixit_replace_non_equal_range ();
2350 test_one_liner_fixit_replace_equal_secondary_range ();
2351 test_one_liner_fixit_validation_adhoc_locations ();
2352 test_one_liner_many_fixits_1 ();
2353 test_one_liner_many_fixits_2 ();
2356 /* Verify that gcc_rich_location::add_location_if_nearby works. */
2358 static void
2359 test_add_location_if_nearby (const line_table_case &case_)
2361 /* Create a tempfile and write some text to it.
2362 ...000000000111111111122222222223333333333.
2363 ...123456789012345678901234567890123456789. */
2364 const char *content
2365 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2366 "struct different_line\n" /* line 2. */
2367 "{\n" /* line 3. */
2368 " double x;\n" /* line 4. */
2369 " double y;\n" /* line 5. */
2370 ";\n"); /* line 6. */
2371 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2372 line_table_test ltt (case_);
2374 const line_map_ordinary *ord_map
2375 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2376 tmp.get_filename (), 0));
2378 linemap_line_start (line_table, 1, 100);
2380 const location_t final_line_end
2381 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2383 /* Don't attempt to run the tests if column data might be unavailable. */
2384 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2385 return;
2387 /* Test of add_location_if_nearby on the same line as the
2388 primary location. */
2390 const location_t missing_close_brace_1_39
2391 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2392 const location_t matching_open_brace_1_18
2393 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2394 gcc_rich_location richloc (missing_close_brace_1_39);
2395 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2396 ASSERT_TRUE (added);
2397 ASSERT_EQ (2, richloc.get_num_locations ());
2398 test_diagnostic_context dc;
2399 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2400 ASSERT_STREQ ("\n"
2401 " struct same_line { double x; double y; ;\n"
2402 " ~ ^\n",
2403 pp_formatted_text (dc.printer));
2406 /* Test of add_location_if_nearby on a different line to the
2407 primary location. */
2409 const location_t missing_close_brace_6_1
2410 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2411 const location_t matching_open_brace_3_1
2412 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2413 gcc_rich_location richloc (missing_close_brace_6_1);
2414 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2415 ASSERT_FALSE (added);
2416 ASSERT_EQ (1, richloc.get_num_locations ());
2420 /* Verify that we print fixits even if they only affect lines
2421 outside those covered by the ranges in the rich_location. */
2423 static void
2424 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2426 /* Create a tempfile and write some text to it.
2427 ...000000000111111111122222222223333333333.
2428 ...123456789012345678901234567890123456789. */
2429 const char *content
2430 = ("struct point { double x; double y; };\n" /* line 1. */
2431 "struct point origin = {x: 0.0,\n" /* line 2. */
2432 " y\n" /* line 3. */
2433 "\n" /* line 4. */
2434 "\n" /* line 5. */
2435 " : 0.0};\n"); /* line 6. */
2436 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2437 line_table_test ltt (case_);
2439 const line_map_ordinary *ord_map
2440 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2441 tmp.get_filename (), 0));
2443 linemap_line_start (line_table, 1, 100);
2445 const location_t final_line_end
2446 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2448 /* Don't attempt to run the tests if column data might be unavailable. */
2449 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2450 return;
2452 /* A pair of tests for modernizing the initializers to C99-style. */
2454 /* The one-liner case (line 2). */
2456 test_diagnostic_context dc;
2457 const location_t x
2458 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2459 const location_t colon
2460 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2461 rich_location richloc (line_table, colon);
2462 richloc.add_fixit_insert_before (x, ".");
2463 richloc.add_fixit_replace (colon, "=");
2464 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2465 ASSERT_STREQ ("\n"
2466 " struct point origin = {x: 0.0,\n"
2467 " ^\n"
2468 " .=\n",
2469 pp_formatted_text (dc.printer));
2472 /* The multiline case. The caret for the rich_location is on line 6;
2473 verify that insertion fixit on line 3 is still printed (and that
2474 span starts are printed due to the gap between the span at line 3
2475 and that at line 6). */
2477 test_diagnostic_context dc;
2478 const location_t y
2479 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2480 const location_t colon
2481 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2482 rich_location richloc (line_table, colon);
2483 richloc.add_fixit_insert_before (y, ".");
2484 richloc.add_fixit_replace (colon, "=");
2485 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2486 ASSERT_STREQ ("\n"
2487 "FILENAME:3:24:\n"
2488 " y\n"
2489 " .\n"
2490 "FILENAME:6:25:\n"
2491 " : 0.0};\n"
2492 " ^\n"
2493 " =\n",
2494 pp_formatted_text (dc.printer));
2499 /* Verify that fix-it hints are appropriately consolidated.
2501 If any fix-it hints in a rich_location involve locations beyond
2502 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2503 the fix-it as a whole, so there should be none.
2505 Otherwise, verify that consecutive "replace" and "remove" fix-its
2506 are merged, and that other fix-its remain separate. */
2508 static void
2509 test_fixit_consolidation (const line_table_case &case_)
2511 line_table_test ltt (case_);
2513 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2515 const location_t c10 = linemap_position_for_column (line_table, 10);
2516 const location_t c15 = linemap_position_for_column (line_table, 15);
2517 const location_t c16 = linemap_position_for_column (line_table, 16);
2518 const location_t c17 = linemap_position_for_column (line_table, 17);
2519 const location_t c20 = linemap_position_for_column (line_table, 20);
2520 const location_t c21 = linemap_position_for_column (line_table, 21);
2521 const location_t caret = c10;
2523 /* Insert + insert. */
2525 rich_location richloc (line_table, caret);
2526 richloc.add_fixit_insert_before (c10, "foo");
2527 richloc.add_fixit_insert_before (c15, "bar");
2529 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2530 /* Bogus column info for 2nd fixit, so no fixits. */
2531 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2532 else
2533 /* They should not have been merged. */
2534 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2537 /* Insert + replace. */
2539 rich_location richloc (line_table, caret);
2540 richloc.add_fixit_insert_before (c10, "foo");
2541 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2542 "bar");
2544 if (c17 > 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 /* Replace + non-consecutive insert. */
2554 rich_location richloc (line_table, caret);
2555 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2556 "bar");
2557 richloc.add_fixit_insert_before (c17, "foo");
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 replace. */
2569 rich_location richloc (line_table, caret);
2570 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2571 "foo");
2572 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2573 "bar");
2575 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2576 /* Bogus column info for 2nd fixit, so no fixits. */
2577 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2578 else
2579 /* They should not have been merged. */
2580 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2583 /* Replace + consecutive replace. */
2585 rich_location richloc (line_table, caret);
2586 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2587 "foo");
2588 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2589 "bar");
2591 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2592 /* Bogus column info for 2nd fixit, so no fixits. */
2593 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2594 else
2596 /* They should have been merged into a single "replace". */
2597 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2598 const fixit_hint *hint = richloc.get_fixit_hint (0);
2599 ASSERT_STREQ ("foobar", hint->get_string ());
2600 ASSERT_EQ (c10, hint->get_start_loc ());
2601 ASSERT_EQ (c21, hint->get_next_loc ());
2605 /* Replace + consecutive removal. */
2607 rich_location richloc (line_table, caret);
2608 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2609 "foo");
2610 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2612 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2613 /* Bogus column info for 2nd fixit, so no fixits. */
2614 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2615 else
2617 /* They should have been merged into a single replace, with the
2618 range extended to cover that of the removal. */
2619 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2620 const fixit_hint *hint = richloc.get_fixit_hint (0);
2621 ASSERT_STREQ ("foo", hint->get_string ());
2622 ASSERT_EQ (c10, hint->get_start_loc ());
2623 ASSERT_EQ (c21, hint->get_next_loc ());
2627 /* Consecutive removals. */
2629 rich_location richloc (line_table, caret);
2630 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2631 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2633 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2634 /* Bogus column info for 2nd fixit, so no fixits. */
2635 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2636 else
2638 /* They should have been merged into a single "replace-with-empty". */
2639 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2640 const fixit_hint *hint = richloc.get_fixit_hint (0);
2641 ASSERT_STREQ ("", hint->get_string ());
2642 ASSERT_EQ (c10, hint->get_start_loc ());
2643 ASSERT_EQ (c21, hint->get_next_loc ());
2648 /* Verify that the line_corrections machinery correctly prints
2649 overlapping fixit-hints. */
2651 static void
2652 test_overlapped_fixit_printing (const line_table_case &case_)
2654 /* Create a tempfile and write some text to it.
2655 ...000000000111111111122222222223333333333.
2656 ...123456789012345678901234567890123456789. */
2657 const char *content
2658 = (" foo *f = (foo *)ptr->field;\n");
2659 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2660 line_table_test ltt (case_);
2662 const line_map_ordinary *ord_map
2663 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2664 tmp.get_filename (), 0));
2666 linemap_line_start (line_table, 1, 100);
2668 const location_t final_line_end
2669 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2671 /* Don't attempt to run the tests if column data might be unavailable. */
2672 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2673 return;
2675 /* A test for converting a C-style cast to a C++-style cast. */
2676 const location_t open_paren
2677 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2678 const location_t close_paren
2679 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2680 const location_t expr_start
2681 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2682 const location_t expr_finish
2683 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2684 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2686 /* Various examples of fix-it hints that aren't themselves consolidated,
2687 but for which the *printing* may need consolidation. */
2689 /* Example where 3 fix-it hints are printed as one. */
2691 test_diagnostic_context dc;
2692 rich_location richloc (line_table, expr);
2693 richloc.add_fixit_replace (open_paren, "const_cast<");
2694 richloc.add_fixit_replace (close_paren, "> (");
2695 richloc.add_fixit_insert_after (")");
2697 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2698 ASSERT_STREQ ("\n"
2699 " foo *f = (foo *)ptr->field;\n"
2700 " ^~~~~~~~~~\n"
2701 " -----------------\n"
2702 " const_cast<foo *> (ptr->field)\n",
2703 pp_formatted_text (dc.printer));
2705 /* Unit-test the line_corrections machinery. */
2706 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2707 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2708 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2709 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2710 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2711 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2712 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2713 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2714 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2715 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2717 /* Add each hint in turn to a line_corrections instance,
2718 and verify that they are consolidated into one correction instance
2719 as expected. */
2720 line_corrections lc (tmp.get_filename (), 1);
2722 /* The first replace hint by itself. */
2723 lc.add_hint (hint_0);
2724 ASSERT_EQ (1, lc.m_corrections.length ());
2725 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2726 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2727 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2729 /* After the second replacement hint, they are printed together
2730 as a replacement (along with the text between them). */
2731 lc.add_hint (hint_1);
2732 ASSERT_EQ (1, lc.m_corrections.length ());
2733 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2734 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2735 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2737 /* After the final insertion hint, they are all printed together
2738 as a replacement (along with the text between them). */
2739 lc.add_hint (hint_2);
2740 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2741 lc.m_corrections[0]->m_text);
2742 ASSERT_EQ (1, lc.m_corrections.length ());
2743 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2744 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2747 /* Example where two are consolidated during printing. */
2749 test_diagnostic_context dc;
2750 rich_location richloc (line_table, expr);
2751 richloc.add_fixit_replace (open_paren, "CAST (");
2752 richloc.add_fixit_replace (close_paren, ") (");
2753 richloc.add_fixit_insert_after (")");
2755 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2756 ASSERT_STREQ ("\n"
2757 " foo *f = (foo *)ptr->field;\n"
2758 " ^~~~~~~~~~\n"
2759 " -\n"
2760 " CAST (-\n"
2761 " ) ( )\n",
2762 pp_formatted_text (dc.printer));
2765 /* Example where none are consolidated during printing. */
2767 test_diagnostic_context dc;
2768 rich_location richloc (line_table, expr);
2769 richloc.add_fixit_replace (open_paren, "CST (");
2770 richloc.add_fixit_replace (close_paren, ") (");
2771 richloc.add_fixit_insert_after (")");
2773 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2774 ASSERT_STREQ ("\n"
2775 " foo *f = (foo *)ptr->field;\n"
2776 " ^~~~~~~~~~\n"
2777 " -\n"
2778 " CST ( -\n"
2779 " ) ( )\n",
2780 pp_formatted_text (dc.printer));
2783 /* Example of deletion fix-it hints. */
2785 test_diagnostic_context dc;
2786 rich_location richloc (line_table, expr);
2787 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2788 source_range victim = {open_paren, close_paren};
2789 richloc.add_fixit_remove (victim);
2791 /* This case is actually handled by fixit-consolidation,
2792 rather than by line_corrections. */
2793 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2795 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2796 ASSERT_STREQ ("\n"
2797 " foo *f = (foo *)ptr->field;\n"
2798 " ^~~~~~~~~~\n"
2799 " -------\n"
2800 " (bar *)\n",
2801 pp_formatted_text (dc.printer));
2804 /* Example of deletion fix-it hints that would overlap. */
2806 test_diagnostic_context dc;
2807 rich_location richloc (line_table, expr);
2808 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2809 source_range victim = {expr_start, expr_finish};
2810 richloc.add_fixit_remove (victim);
2812 /* These fixits are not consolidated. */
2813 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2815 /* But the corrections are. */
2816 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2817 ASSERT_STREQ ("\n"
2818 " foo *f = (foo *)ptr->field;\n"
2819 " ^~~~~~~~~~\n"
2820 " -----------------\n"
2821 " (longer *)(foo *)\n",
2822 pp_formatted_text (dc.printer));
2825 /* Example of insertion fix-it hints that would overlap. */
2827 test_diagnostic_context dc;
2828 rich_location richloc (line_table, expr);
2829 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2830 richloc.add_fixit_insert_after (close_paren, "TEST");
2832 /* The first insertion is long enough that if printed naively,
2833 it would overlap with the second.
2834 Verify that they are printed as a single replacement. */
2835 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2836 ASSERT_STREQ ("\n"
2837 " foo *f = (foo *)ptr->field;\n"
2838 " ^~~~~~~~~~\n"
2839 " -------\n"
2840 " LONGER THAN THE CAST(foo *)TEST\n",
2841 pp_formatted_text (dc.printer));
2845 /* Verify that the line_corrections machinery correctly prints
2846 overlapping fixit-hints that have been added in the wrong
2847 order.
2848 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
2850 static void
2851 test_overlapped_fixit_printing_2 (const line_table_case &case_)
2853 /* Create a tempfile and write some text to it.
2854 ...000000000111111111122222222223333333333.
2855 ...123456789012345678901234567890123456789. */
2856 const char *content
2857 = ("int a5[][0][0] = { 1, 2 };\n");
2858 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2859 line_table_test ltt (case_);
2861 const line_map_ordinary *ord_map
2862 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2863 tmp.get_filename (), 0));
2865 linemap_line_start (line_table, 1, 100);
2867 const location_t final_line_end
2868 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
2870 /* Don't attempt to run the tests if column data might be unavailable. */
2871 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2872 return;
2874 const location_t col_1
2875 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2876 const location_t col_20
2877 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
2878 const location_t col_21
2879 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
2880 const location_t col_23
2881 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
2882 const location_t col_25
2883 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
2885 /* Two insertions, in the wrong order. */
2887 rich_location richloc (line_table, col_20);
2888 richloc.add_fixit_insert_before (col_23, "{");
2889 richloc.add_fixit_insert_before (col_21, "}");
2891 /* These fixits should be accepted; they can't be consolidated. */
2892 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2893 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2894 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
2895 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
2896 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2897 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
2898 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
2900 /* Verify that they're printed correctly. */
2901 test_diagnostic_context dc;
2902 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2903 ASSERT_STREQ ("\n"
2904 " int a5[][0][0] = { 1, 2 };\n"
2905 " ^\n"
2906 " } {\n",
2907 pp_formatted_text (dc.printer));
2910 /* Various overlapping insertions, some occurring "out of order"
2911 (reproducing the fix-it hints from PR c/81405). */
2913 test_diagnostic_context dc;
2914 rich_location richloc (line_table, col_20);
2916 richloc.add_fixit_insert_before (col_20, "{{");
2917 richloc.add_fixit_insert_before (col_21, "}}");
2918 richloc.add_fixit_insert_before (col_23, "{");
2919 richloc.add_fixit_insert_before (col_21, "}");
2920 richloc.add_fixit_insert_before (col_23, "{{");
2921 richloc.add_fixit_insert_before (col_25, "}");
2922 richloc.add_fixit_insert_before (col_21, "}");
2923 richloc.add_fixit_insert_before (col_1, "{");
2924 richloc.add_fixit_insert_before (col_25, "}");
2925 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2926 ASSERT_STREQ ("\n"
2927 " int a5[][0][0] = { 1, 2 };\n"
2928 " ^\n"
2929 " { -----\n"
2930 " {{1}}}}, {{{2 }}\n",
2931 pp_formatted_text (dc.printer));
2935 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2937 static void
2938 test_fixit_insert_containing_newline (const line_table_case &case_)
2940 /* Create a tempfile and write some text to it.
2941 .........................0000000001111111.
2942 .........................1234567890123456. */
2943 const char *old_content = (" case 'a':\n" /* line 1. */
2944 " x = a;\n" /* line 2. */
2945 " case 'b':\n" /* line 3. */
2946 " x = b;\n");/* line 4. */
2948 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2949 line_table_test ltt (case_);
2950 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2952 location_t case_start = linemap_position_for_column (line_table, 5);
2953 location_t case_finish = linemap_position_for_column (line_table, 13);
2954 location_t case_loc = make_location (case_start, case_start, case_finish);
2955 location_t line_start = linemap_position_for_column (line_table, 1);
2957 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2958 return;
2960 /* Add a "break;" on a line by itself before line 3 i.e. before
2961 column 1 of line 3. */
2963 rich_location richloc (line_table, case_loc);
2964 richloc.add_fixit_insert_before (line_start, " break;\n");
2965 test_diagnostic_context dc;
2966 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2967 ASSERT_STREQ ("\n"
2968 "+ break;\n"
2969 " case 'b':\n"
2970 " ^~~~~~~~~\n",
2971 pp_formatted_text (dc.printer));
2974 /* Verify that attempts to add text with a newline fail when the
2975 insertion point is *not* at the start of a line. */
2977 rich_location richloc (line_table, case_loc);
2978 richloc.add_fixit_insert_before (case_start, "break;\n");
2979 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2980 test_diagnostic_context dc;
2981 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2982 ASSERT_STREQ ("\n"
2983 " case 'b':\n"
2984 " ^~~~~~~~~\n",
2985 pp_formatted_text (dc.printer));
2989 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
2990 of the file, where the fix-it is printed in a different line-span
2991 to the primary range of the diagnostic. */
2993 static void
2994 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
2996 /* Create a tempfile and write some text to it.
2997 .........................0000000001111111.
2998 .........................1234567890123456. */
2999 const char *old_content = ("test (int ch)\n" /* line 1. */
3000 "{\n" /* line 2. */
3001 " putchar (ch);\n" /* line 3. */
3002 "}\n"); /* line 4. */
3004 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3005 line_table_test ltt (case_);
3007 const line_map_ordinary *ord_map = linemap_check_ordinary
3008 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3009 linemap_line_start (line_table, 1, 100);
3011 /* The primary range is the "putchar" token. */
3012 location_t putchar_start
3013 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3014 location_t putchar_finish
3015 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3016 location_t putchar_loc
3017 = make_location (putchar_start, putchar_start, putchar_finish);
3018 rich_location richloc (line_table, putchar_loc);
3020 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3021 location_t file_start
3022 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3023 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3025 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3026 return;
3028 test_diagnostic_context dc;
3029 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3030 ASSERT_STREQ ("\n"
3031 "FILENAME:1:1:\n"
3032 "+#include <stdio.h>\n"
3033 " test (int ch)\n"
3034 "FILENAME:3:2:\n"
3035 " putchar (ch);\n"
3036 " ^~~~~~~\n",
3037 pp_formatted_text (dc.printer));
3040 /* Replacement fix-it hint containing a newline.
3041 This will fail, as newlines are only supported when inserting at the
3042 beginning of a line. */
3044 static void
3045 test_fixit_replace_containing_newline (const line_table_case &case_)
3047 /* Create a tempfile and write some text to it.
3048 .........................0000000001111.
3049 .........................1234567890123. */
3050 const char *old_content = "foo = bar ();\n";
3052 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3053 line_table_test ltt (case_);
3054 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3056 /* Replace the " = " with "\n = ", as if we were reformatting an
3057 overly long line. */
3058 location_t start = linemap_position_for_column (line_table, 4);
3059 location_t finish = linemap_position_for_column (line_table, 6);
3060 location_t loc = linemap_position_for_column (line_table, 13);
3061 rich_location richloc (line_table, loc);
3062 source_range range = source_range::from_locations (start, finish);
3063 richloc.add_fixit_replace (range, "\n =");
3065 /* Arbitrary newlines are not yet supported within fix-it hints, so
3066 the fix-it should not be displayed. */
3067 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3069 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3070 return;
3072 test_diagnostic_context dc;
3073 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3074 ASSERT_STREQ ("\n"
3075 " foo = bar ();\n"
3076 " ^\n",
3077 pp_formatted_text (dc.printer));
3080 /* Fix-it hint, attempting to delete a newline.
3081 This will fail, as we currently only support fix-it hints that
3082 affect one line at a time. */
3084 static void
3085 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3087 /* Create a tempfile and write some text to it.
3088 ..........................0000000001111.
3089 ..........................1234567890123. */
3090 const char *old_content = ("foo = bar (\n"
3091 " );\n");
3093 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3094 line_table_test ltt (case_);
3095 const line_map_ordinary *ord_map = linemap_check_ordinary
3096 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3097 linemap_line_start (line_table, 1, 100);
3099 /* Attempt to delete the " (\n...)". */
3100 location_t start
3101 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3102 location_t caret
3103 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3104 location_t finish
3105 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3106 location_t loc = make_location (caret, start, finish);
3107 rich_location richloc (line_table, loc);
3108 richloc. add_fixit_remove ();
3110 /* Fix-it hints that affect more than one line are not yet supported, so
3111 the fix-it should not be displayed. */
3112 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3114 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3115 return;
3117 test_diagnostic_context dc;
3118 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3119 ASSERT_STREQ ("\n"
3120 " foo = bar (\n"
3121 " ~^\n"
3122 " );\n"
3123 " ~ \n",
3124 pp_formatted_text (dc.printer));
3127 /* Run all of the selftests within this file. */
3129 void
3130 diagnostic_show_locus_c_tests ()
3132 test_layout_range_for_single_point ();
3133 test_layout_range_for_single_line ();
3134 test_layout_range_for_multiple_lines ();
3136 test_get_line_width_without_trailing_whitespace ();
3138 test_diagnostic_show_locus_unknown_location ();
3140 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3141 for_each_line_table_case (test_add_location_if_nearby);
3142 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3143 for_each_line_table_case (test_fixit_consolidation);
3144 for_each_line_table_case (test_overlapped_fixit_printing);
3145 for_each_line_table_case (test_overlapped_fixit_printing_2);
3146 for_each_line_table_case (test_fixit_insert_containing_newline);
3147 for_each_line_table_case (test_fixit_insert_containing_newline_2);
3148 for_each_line_table_case (test_fixit_replace_containing_newline);
3149 for_each_line_table_case (test_fixit_deletion_affecting_newline);
3152 } // namespace selftest
3154 #endif /* #if CHECKING_P */