Support fix-it hints that add new lines
[official-gcc.git] / gcc / diagnostic-show-locus.c
blobb91c9d55da305f27d54d1d49b7f2c0c4cc7f47e8
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2017 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 "selftest.h"
32 #ifdef HAVE_TERMIOS_H
33 # include <termios.h>
34 #endif
36 #ifdef GWINSZ_IN_SYS_IOCTL
37 # include <sys/ioctl.h>
38 #endif
40 /* Classes for rendering source code and diagnostics, within an
41 anonymous namespace.
42 The work is done by "class layout", which embeds and uses
43 "class colorizer" and "class layout_range" to get things done. */
45 namespace {
47 /* The state at a given point of the source code, assuming that we're
48 in a range: which range are we in, and whether we should draw a caret at
49 this point. */
51 struct point_state
53 int range_idx;
54 bool draw_caret_p;
57 /* A class to inject colorization codes when printing the diagnostic locus.
59 It has one kind of colorization for each of:
60 - normal text
61 - range 0 (the "primary location")
62 - range 1
63 - range 2
65 The class caches the lookup of the color codes for the above.
67 The class also has responsibility for tracking which of the above is
68 active, filtering out unnecessary changes. This allows
69 layout::print_source_line and layout::print_annotation_line
70 to simply request a colorization code for *every* character they print,
71 via this class, and have the filtering be done for them here. */
73 class colorizer
75 public:
76 colorizer (diagnostic_context *context,
77 diagnostic_t diagnostic_kind);
78 ~colorizer ();
80 void set_range (int range_idx) { set_state (range_idx); }
81 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
82 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
83 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
85 private:
86 void set_state (int state);
87 void begin_state (int state);
88 void finish_state (int state);
89 const char *get_color_by_name (const char *);
91 private:
92 static const int STATE_NORMAL_TEXT = -1;
93 static const int STATE_FIXIT_INSERT = -2;
94 static const int STATE_FIXIT_DELETE = -3;
96 diagnostic_context *m_context;
97 diagnostic_t m_diagnostic_kind;
98 int m_current_state;
99 const char *m_caret;
100 const char *m_range1;
101 const char *m_range2;
102 const char *m_fixit_insert;
103 const char *m_fixit_delete;
104 const char *m_stop_color;
107 /* A point within a layout_range; similar to an expanded_location,
108 but after filtering on file. */
110 class layout_point
112 public:
113 layout_point (const expanded_location &exploc)
114 : m_line (exploc.line),
115 m_column (exploc.column) {}
117 int m_line;
118 int m_column;
121 /* A class for use by "class layout" below: a filtered location_range. */
123 class layout_range
125 public:
126 layout_range (const expanded_location *start_exploc,
127 const expanded_location *finish_exploc,
128 bool show_caret_p,
129 const expanded_location *caret_exploc);
131 bool contains_point (int row, int column) const;
132 bool intersects_line_p (int row) const;
134 layout_point m_start;
135 layout_point m_finish;
136 bool m_show_caret_p;
137 layout_point m_caret;
140 /* A struct for use by layout::print_source_line for telling
141 layout::print_annotation_line the extents of the source line that
142 it printed, so that underlines can be clipped appropriately. */
144 struct line_bounds
146 int m_first_non_ws;
147 int m_last_non_ws;
150 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
151 or "line 23"). During the layout ctor, layout::calculate_line_spans
152 splits the pertinent source lines into a list of disjoint line_span
153 instances (e.g. lines 5-10, lines 15-20, line 23). */
155 struct line_span
157 line_span (linenum_type first_line, linenum_type last_line)
158 : m_first_line (first_line), m_last_line (last_line)
160 gcc_assert (first_line <= last_line);
162 linenum_type get_first_line () const { return m_first_line; }
163 linenum_type get_last_line () const { return m_last_line; }
165 bool contains_line_p (linenum_type line) const
167 return line >= m_first_line && line <= m_last_line;
170 static int comparator (const void *p1, const void *p2)
172 const line_span *ls1 = (const line_span *)p1;
173 const line_span *ls2 = (const line_span *)p2;
174 int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
175 if (first_line_diff)
176 return first_line_diff;
177 return (int)ls1->m_last_line - (int)ls2->m_last_line;
180 linenum_type m_first_line;
181 linenum_type m_last_line;
184 /* A class to control the overall layout when printing a diagnostic.
186 The layout is determined within the constructor.
187 It is then printed by repeatedly calling the "print_source_line",
188 "print_annotation_line" and "print_any_fixits" methods.
190 We assume we have disjoint ranges. */
192 class layout
194 public:
195 layout (diagnostic_context *context,
196 rich_location *richloc,
197 diagnostic_t diagnostic_kind);
199 int get_num_line_spans () const { return m_line_spans.length (); }
200 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
202 bool print_heading_for_line_span_index_p (int line_span_idx) const;
204 expanded_location get_expanded_location (const line_span *) const;
206 void print_line (int row);
208 private:
209 void print_leading_fixits (int row);
210 void print_source_line (int row, const char *line, int line_width,
211 line_bounds *lbounds_out);
212 bool should_print_annotation_line_p (int row) const;
213 void print_annotation_line (int row, const line_bounds lbounds);
214 void print_trailing_fixits (int row);
216 bool annotation_line_showed_range_p (int line, int start_column,
217 int finish_column) const;
218 void show_ruler (int max_column) const;
220 bool validate_fixit_hint_p (const fixit_hint *hint);
222 void calculate_line_spans ();
224 void print_newline ();
226 bool
227 get_state_at_point (/* Inputs. */
228 int row, int column,
229 int first_non_ws, int last_non_ws,
230 /* Outputs. */
231 point_state *out_state);
234 get_x_bound_for_row (int row, int caret_column,
235 int last_non_ws);
237 void
238 move_to_column (int *column, int dest_column);
240 private:
241 diagnostic_context *m_context;
242 pretty_printer *m_pp;
243 diagnostic_t m_diagnostic_kind;
244 expanded_location m_exploc;
245 colorizer m_colorizer;
246 bool m_colorize_source_p;
247 auto_vec <layout_range> m_layout_ranges;
248 auto_vec <const fixit_hint *> m_fixit_hints;
249 auto_vec <line_span> m_line_spans;
250 int m_x_offset;
253 /* Implementation of "class colorizer". */
255 /* The constructor for "colorizer". Lookup and store color codes for the
256 different kinds of things we might need to print. */
258 colorizer::colorizer (diagnostic_context *context,
259 diagnostic_t diagnostic_kind) :
260 m_context (context),
261 m_diagnostic_kind (diagnostic_kind),
262 m_current_state (STATE_NORMAL_TEXT)
264 m_range1 = get_color_by_name ("range1");
265 m_range2 = get_color_by_name ("range2");
266 m_fixit_insert = get_color_by_name ("fixit-insert");
267 m_fixit_delete = get_color_by_name ("fixit-delete");
268 m_stop_color = colorize_stop (pp_show_color (context->printer));
271 /* The destructor for "colorize". If colorization is on, print a code to
272 turn it off. */
274 colorizer::~colorizer ()
276 finish_state (m_current_state);
279 /* Update state, printing color codes if necessary if there's a state
280 change. */
282 void
283 colorizer::set_state (int new_state)
285 if (m_current_state != new_state)
287 finish_state (m_current_state);
288 m_current_state = new_state;
289 begin_state (new_state);
293 /* Turn on any colorization for STATE. */
295 void
296 colorizer::begin_state (int state)
298 switch (state)
300 case STATE_NORMAL_TEXT:
301 break;
303 case STATE_FIXIT_INSERT:
304 pp_string (m_context->printer, m_fixit_insert);
305 break;
307 case STATE_FIXIT_DELETE:
308 pp_string (m_context->printer, m_fixit_delete);
309 break;
311 case 0:
312 /* Make range 0 be the same color as the "kind" text
313 (error vs warning vs note). */
314 pp_string
315 (m_context->printer,
316 colorize_start (pp_show_color (m_context->printer),
317 diagnostic_get_color_for_kind (m_diagnostic_kind)));
318 break;
320 case 1:
321 pp_string (m_context->printer, m_range1);
322 break;
324 case 2:
325 pp_string (m_context->printer, m_range2);
326 break;
328 default:
329 /* For ranges beyond 2, alternate between color 1 and color 2. */
331 gcc_assert (state > 2);
332 pp_string (m_context->printer,
333 state % 2 ? m_range1 : m_range2);
335 break;
339 /* Turn off any colorization for STATE. */
341 void
342 colorizer::finish_state (int state)
344 if (state != STATE_NORMAL_TEXT)
345 pp_string (m_context->printer, m_stop_color);
348 /* Get the color code for NAME (or the empty string if
349 colorization is disabled). */
351 const char *
352 colorizer::get_color_by_name (const char *name)
354 return colorize_start (pp_show_color (m_context->printer), name);
357 /* Implementation of class layout_range. */
359 /* The constructor for class layout_range.
360 Initialize various layout_point fields from expanded_location
361 equivalents; we've already filtered on file. */
363 layout_range::layout_range (const expanded_location *start_exploc,
364 const expanded_location *finish_exploc,
365 bool show_caret_p,
366 const expanded_location *caret_exploc)
367 : m_start (*start_exploc),
368 m_finish (*finish_exploc),
369 m_show_caret_p (show_caret_p),
370 m_caret (*caret_exploc)
374 /* Is (column, row) within the given range?
375 We've already filtered on the file.
377 Ranges are closed (both limits are within the range).
379 Example A: a single-line range:
380 start: (col=22, line=2)
381 finish: (col=38, line=2)
383 |00000011111111112222222222333333333344444444444
384 |34567890123456789012345678901234567890123456789
385 --+-----------------------------------------------
386 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
387 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
388 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
390 Example B: a multiline range with
391 start: (col=14, line=3)
392 finish: (col=08, line=5)
394 |00000011111111112222222222333333333344444444444
395 |34567890123456789012345678901234567890123456789
396 --+-----------------------------------------------
397 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
398 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
399 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
400 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
401 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
402 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
403 --+-----------------------------------------------
405 Legend:
406 - 'b' indicates a point *before* the range
407 - 'S' indicates the start of the range
408 - 'w' indicates a point within the range
409 - 'F' indicates the finish of the range (which is
410 within it).
411 - 'a' indicates a subsequent point *after* the range. */
413 bool
414 layout_range::contains_point (int row, int column) const
416 gcc_assert (m_start.m_line <= m_finish.m_line);
417 /* ...but the equivalent isn't true for the columns;
418 consider example B in the comment above. */
420 if (row < m_start.m_line)
421 /* Points before the first line of the range are
422 outside it (corresponding to line 01 in example A
423 and lines 01 and 02 in example B above). */
424 return false;
426 if (row == m_start.m_line)
427 /* On same line as start of range (corresponding
428 to line 02 in example A and line 03 in example B). */
430 if (column < m_start.m_column)
431 /* Points on the starting line of the range, but
432 before the column in which it begins. */
433 return false;
435 if (row < m_finish.m_line)
436 /* This is a multiline range; the point
437 is within it (corresponds to line 03 in example B
438 from column 14 onwards) */
439 return true;
440 else
442 /* This is a single-line range. */
443 gcc_assert (row == m_finish.m_line);
444 return column <= m_finish.m_column;
448 /* The point is in a line beyond that containing the
449 start of the range: lines 03 onwards in example A,
450 and lines 04 onwards in example B. */
451 gcc_assert (row > m_start.m_line);
453 if (row > m_finish.m_line)
454 /* The point is beyond the final line of the range
455 (lines 03 onwards in example A, and lines 06 onwards
456 in example B). */
457 return false;
459 if (row < m_finish.m_line)
461 /* The point is in a line that's fully within a multiline
462 range (e.g. line 04 in example B). */
463 gcc_assert (m_start.m_line < m_finish.m_line);
464 return true;
467 gcc_assert (row == m_finish.m_line);
469 return column <= m_finish.m_column;
472 /* Does this layout_range contain any part of line ROW? */
474 bool
475 layout_range::intersects_line_p (int row) const
477 gcc_assert (m_start.m_line <= m_finish.m_line);
478 if (row < m_start.m_line)
479 return false;
480 if (row > m_finish.m_line)
481 return false;
482 return true;
485 #if CHECKING_P
487 /* A helper function for testing layout_range. */
489 static layout_range
490 make_range (int start_line, int start_col, int end_line, int end_col)
492 const expanded_location start_exploc
493 = {"test.c", start_line, start_col, NULL, false};
494 const expanded_location finish_exploc
495 = {"test.c", end_line, end_col, NULL, false};
496 return layout_range (&start_exploc, &finish_exploc, false,
497 &start_exploc);
500 /* Selftests for layout_range::contains_point and
501 layout_range::intersects_line_p. */
503 /* Selftest for layout_range, where the layout_range
504 is a range with start==end i.e. a single point. */
506 static void
507 test_layout_range_for_single_point ()
509 layout_range point = make_range (7, 10, 7, 10);
511 /* Tests for layout_range::contains_point. */
513 /* Before the line. */
514 ASSERT_FALSE (point.contains_point (6, 1));
516 /* On the line, but before start. */
517 ASSERT_FALSE (point.contains_point (7, 9));
519 /* At the point. */
520 ASSERT_TRUE (point.contains_point (7, 10));
522 /* On the line, after the point. */
523 ASSERT_FALSE (point.contains_point (7, 11));
525 /* After the line. */
526 ASSERT_FALSE (point.contains_point (8, 1));
528 /* Tests for layout_range::intersects_line_p. */
529 ASSERT_FALSE (point.intersects_line_p (6));
530 ASSERT_TRUE (point.intersects_line_p (7));
531 ASSERT_FALSE (point.intersects_line_p (8));
534 /* Selftest for layout_range, where the layout_range
535 is the single-line range shown as "Example A" above. */
537 static void
538 test_layout_range_for_single_line ()
540 layout_range example_a = make_range (2, 22, 2, 38);
542 /* Tests for layout_range::contains_point. */
544 /* Before the line. */
545 ASSERT_FALSE (example_a.contains_point (1, 1));
547 /* On the line, but before start. */
548 ASSERT_FALSE (example_a.contains_point (2, 21));
550 /* On the line, at the start. */
551 ASSERT_TRUE (example_a.contains_point (2, 22));
553 /* On the line, within the range. */
554 ASSERT_TRUE (example_a.contains_point (2, 23));
556 /* On the line, at the end. */
557 ASSERT_TRUE (example_a.contains_point (2, 38));
559 /* On the line, after the end. */
560 ASSERT_FALSE (example_a.contains_point (2, 39));
562 /* After the line. */
563 ASSERT_FALSE (example_a.contains_point (2, 39));
565 /* Tests for layout_range::intersects_line_p. */
566 ASSERT_FALSE (example_a.intersects_line_p (1));
567 ASSERT_TRUE (example_a.intersects_line_p (2));
568 ASSERT_FALSE (example_a.intersects_line_p (3));
571 /* Selftest for layout_range, where the layout_range
572 is the multi-line range shown as "Example B" above. */
574 static void
575 test_layout_range_for_multiple_lines ()
577 layout_range example_b = make_range (3, 14, 5, 8);
579 /* Tests for layout_range::contains_point. */
581 /* Before first line. */
582 ASSERT_FALSE (example_b.contains_point (1, 1));
584 /* On the first line, but before start. */
585 ASSERT_FALSE (example_b.contains_point (3, 13));
587 /* At the start. */
588 ASSERT_TRUE (example_b.contains_point (3, 14));
590 /* On the first line, within the range. */
591 ASSERT_TRUE (example_b.contains_point (3, 15));
593 /* On an interior line.
594 The column number should not matter; try various boundary
595 values. */
596 ASSERT_TRUE (example_b.contains_point (4, 1));
597 ASSERT_TRUE (example_b.contains_point (4, 7));
598 ASSERT_TRUE (example_b.contains_point (4, 8));
599 ASSERT_TRUE (example_b.contains_point (4, 9));
600 ASSERT_TRUE (example_b.contains_point (4, 13));
601 ASSERT_TRUE (example_b.contains_point (4, 14));
602 ASSERT_TRUE (example_b.contains_point (4, 15));
604 /* On the final line, before the end. */
605 ASSERT_TRUE (example_b.contains_point (5, 7));
607 /* On the final line, at the end. */
608 ASSERT_TRUE (example_b.contains_point (5, 8));
610 /* On the final line, after the end. */
611 ASSERT_FALSE (example_b.contains_point (5, 9));
613 /* After the line. */
614 ASSERT_FALSE (example_b.contains_point (6, 1));
616 /* Tests for layout_range::intersects_line_p. */
617 ASSERT_FALSE (example_b.intersects_line_p (2));
618 ASSERT_TRUE (example_b.intersects_line_p (3));
619 ASSERT_TRUE (example_b.intersects_line_p (4));
620 ASSERT_TRUE (example_b.intersects_line_p (5));
621 ASSERT_FALSE (example_b.intersects_line_p (6));
624 #endif /* #if CHECKING_P */
626 /* Given a source line LINE of length LINE_WIDTH, determine the width
627 without any trailing whitespace. */
629 static int
630 get_line_width_without_trailing_whitespace (const char *line, int line_width)
632 int result = line_width;
633 while (result > 0)
635 char ch = line[result - 1];
636 if (ch == ' ' || ch == '\t')
637 result--;
638 else
639 break;
641 gcc_assert (result >= 0);
642 gcc_assert (result <= line_width);
643 gcc_assert (result == 0 ||
644 (line[result - 1] != ' '
645 && line[result -1] != '\t'));
646 return result;
649 #if CHECKING_P
651 /* A helper function for testing get_line_width_without_trailing_whitespace. */
653 static void
654 assert_eq (const char *line, int expected_width)
656 int actual_value
657 = get_line_width_without_trailing_whitespace (line, strlen (line));
658 ASSERT_EQ (actual_value, expected_width);
661 /* Verify that get_line_width_without_trailing_whitespace is sane for
662 various inputs. It is not required to handle newlines. */
664 static void
665 test_get_line_width_without_trailing_whitespace ()
667 assert_eq ("", 0);
668 assert_eq (" ", 0);
669 assert_eq ("\t", 0);
670 assert_eq ("hello world", 11);
671 assert_eq ("hello world ", 11);
672 assert_eq ("hello world \t\t ", 11);
675 #endif /* #if CHECKING_P */
677 /* Helper function for layout's ctor, for sanitizing locations relative
678 to the primary location within a diagnostic.
680 Compare LOC_A and LOC_B to see if it makes sense to print underlines
681 connecting their expanded locations. Doing so is only guaranteed to
682 make sense if the locations share the same macro expansion "history"
683 i.e. they can be traced through the same macro expansions, eventually
684 reaching an ordinary map.
686 This may be too strong a condition, but it effectively sanitizes
687 PR c++/70105, which has an example of printing an expression where the
688 final location of the expression is in a different macro, which
689 erroneously was leading to hundreds of lines of irrelevant source
690 being printed. */
692 static bool
693 compatible_locations_p (location_t loc_a, location_t loc_b)
695 if (IS_ADHOC_LOC (loc_a))
696 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
697 if (IS_ADHOC_LOC (loc_b))
698 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
700 /* If either location is one of the special locations outside of a
701 linemap, they are only compatible if they are equal. */
702 if (loc_a < RESERVED_LOCATION_COUNT
703 || loc_b < RESERVED_LOCATION_COUNT)
704 return loc_a == loc_b;
706 const line_map *map_a = linemap_lookup (line_table, loc_a);
707 linemap_assert (map_a);
709 const line_map *map_b = linemap_lookup (line_table, loc_b);
710 linemap_assert (map_b);
712 /* Are they within the same map? */
713 if (map_a == map_b)
715 /* Are both within the same macro expansion? */
716 if (linemap_macro_expansion_map_p (map_a))
718 /* Expand each location towards the spelling location, and
719 recurse. */
720 const line_map_macro *macro_map = linemap_check_macro (map_a);
721 source_location loc_a_toward_spelling
722 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
723 macro_map,
724 loc_a);
725 source_location loc_b_toward_spelling
726 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
727 macro_map,
728 loc_b);
729 return compatible_locations_p (loc_a_toward_spelling,
730 loc_b_toward_spelling);
733 /* Otherwise they are within the same ordinary map. */
734 return true;
736 else
738 /* Within different maps. */
740 /* If either is within a macro expansion, they are incompatible. */
741 if (linemap_macro_expansion_map_p (map_a)
742 || linemap_macro_expansion_map_p (map_b))
743 return false;
745 /* Within two different ordinary maps; they are compatible iff they
746 are in the same file. */
747 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
748 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
749 return ord_map_a->to_file == ord_map_b->to_file;
753 /* Implementation of class layout. */
755 /* Constructor for class layout.
757 Filter the ranges from the rich_location to those that we can
758 sanely print, populating m_layout_ranges and m_fixit_hints.
759 Determine the range of lines that we will print, splitting them
760 up into an ordered list of disjoint spans of contiguous line numbers.
761 Determine m_x_offset, to ensure that the primary caret
762 will fit within the max_width provided by the diagnostic_context. */
764 layout::layout (diagnostic_context * context,
765 rich_location *richloc,
766 diagnostic_t diagnostic_kind)
767 : m_context (context),
768 m_pp (context->printer),
769 m_diagnostic_kind (diagnostic_kind),
770 m_exploc (richloc->get_expanded_location (0)),
771 m_colorizer (context, diagnostic_kind),
772 m_colorize_source_p (context->colorize_source_p),
773 m_layout_ranges (richloc->get_num_locations ()),
774 m_fixit_hints (richloc->get_num_fixit_hints ()),
775 m_line_spans (1 + richloc->get_num_locations ()),
776 m_x_offset (0)
778 source_location primary_loc = richloc->get_range (0)->m_loc;
780 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
782 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
783 Ignore any ranges that are awkward to handle. */
784 const location_range *loc_range = richloc->get_range (idx);
786 /* Split the "range" into caret and range information. */
787 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
789 /* Expand the various locations. */
790 expanded_location start
791 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
792 expanded_location finish
793 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
794 expanded_location caret
795 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
797 /* If any part of the range isn't in the same file as the primary
798 location of this diagnostic, ignore the range. */
799 if (start.file != m_exploc.file)
800 continue;
801 if (finish.file != m_exploc.file)
802 continue;
803 if (loc_range->m_show_caret_p)
804 if (caret.file != m_exploc.file)
805 continue;
807 /* Sanitize the caret location for non-primary ranges. */
808 if (m_layout_ranges.length () > 0)
809 if (loc_range->m_show_caret_p)
810 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
811 /* Discard any non-primary ranges that can't be printed
812 sanely relative to the primary location. */
813 continue;
815 /* Everything is now known to be in the correct source file,
816 but it may require further sanitization. */
817 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
819 /* If we have a range that finishes before it starts (perhaps
820 from something built via macro expansion), printing the
821 range is likely to be nonsensical. Also, attempting to do so
822 breaks assumptions within the printing code (PR c/68473).
823 Similarly, don't attempt to print ranges if one or both ends
824 of the range aren't sane to print relative to the
825 primary location (PR c++/70105). */
826 if (start.line > finish.line
827 || !compatible_locations_p (src_range.m_start, primary_loc)
828 || !compatible_locations_p (src_range.m_finish, primary_loc))
830 /* Is this the primary location? */
831 if (m_layout_ranges.length () == 0)
833 /* We want to print the caret for the primary location, but
834 we must sanitize away m_start and m_finish. */
835 ri.m_start = ri.m_caret;
836 ri.m_finish = ri.m_caret;
838 else
839 /* This is a non-primary range; ignore it. */
840 continue;
843 /* Passed all the tests; add the range to m_layout_ranges so that
844 it will be printed. */
845 m_layout_ranges.safe_push (ri);
848 /* Populate m_fixit_hints, filtering to only those that are in the
849 same file. */
850 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
852 const fixit_hint *hint = richloc->get_fixit_hint (i);
853 if (validate_fixit_hint_p (hint))
854 m_fixit_hints.safe_push (hint);
857 /* Populate m_line_spans. */
858 calculate_line_spans ();
860 /* Adjust m_x_offset.
861 Center the primary caret to fit in max_width; all columns
862 will be adjusted accordingly. */
863 int max_width = m_context->caret_max_width;
864 int line_width;
865 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
866 &line_width);
867 if (line && m_exploc.column <= line_width)
869 int right_margin = CARET_LINE_MARGIN;
870 int column = m_exploc.column;
871 right_margin = MIN (line_width - column, right_margin);
872 right_margin = max_width - right_margin;
873 if (line_width >= max_width && column > right_margin)
874 m_x_offset = column - right_margin;
875 gcc_assert (m_x_offset >= 0);
878 if (context->show_ruler_p)
879 show_ruler (m_x_offset + max_width);
882 /* Return true iff we should print a heading when starting the
883 line span with the given index. */
885 bool
886 layout::print_heading_for_line_span_index_p (int line_span_idx) const
888 /* We print a heading for every change of line span, hence for every
889 line span after the initial one. */
890 if (line_span_idx > 0)
891 return true;
893 /* We also do it for the initial span if the primary location of the
894 diagnostic is in a different span. */
895 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
896 return true;
898 return false;
901 /* Get an expanded_location for the first location of interest within
902 the given line_span.
903 Used when printing a heading to indicate a new line span. */
905 expanded_location
906 layout::get_expanded_location (const line_span *line_span) const
908 /* Whenever possible, use the caret location. */
909 if (line_span->contains_line_p (m_exploc.line))
910 return m_exploc;
912 /* Otherwise, use the start of the first range that's present
913 within the line_span. */
914 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
916 const layout_range *lr = &m_layout_ranges[i];
917 if (line_span->contains_line_p (lr->m_start.m_line))
919 expanded_location exploc = m_exploc;
920 exploc.line = lr->m_start.m_line;
921 exploc.column = lr->m_start.m_column;
922 return exploc;
926 /* Otherwise, use the location of the first fixit-hint present within
927 the line_span. */
928 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
930 const fixit_hint *hint = m_fixit_hints[i];
931 location_t loc = hint->get_start_loc ();
932 expanded_location exploc = expand_location (loc);
933 if (line_span->contains_line_p (exploc.line))
934 return exploc;
937 /* It should not be possible to have a line span that didn't
938 contain any of the layout_range or fixit_hint instances. */
939 gcc_unreachable ();
940 return m_exploc;
943 /* Determine if HINT is meaningful to print within this layout. */
945 bool
946 layout::validate_fixit_hint_p (const fixit_hint *hint)
948 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
949 return false;
950 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
951 return false;
953 return true;
956 /* Determine the range of lines affected by HINT.
957 This assumes that HINT has already been filtered by
958 validate_fixit_hint_p, and so affects the correct source file. */
960 static line_span
961 get_line_span_for_fixit_hint (const fixit_hint *hint)
963 gcc_assert (hint);
964 return line_span (LOCATION_LINE (hint->get_start_loc ()),
965 LOCATION_LINE (hint->get_next_loc ()));
968 /* We want to print the pertinent source code at a diagnostic. The
969 rich_location can contain multiple locations. This will have been
970 filtered into m_exploc (the caret for the primary location) and
971 m_layout_ranges, for those ranges within the same source file.
973 We will print a subset of the lines within the source file in question,
974 as a collection of "spans" of lines.
976 This function populates m_line_spans with an ordered, disjoint list of
977 the line spans of interest.
979 For example, if the primary caret location is on line 7, with ranges
980 covering lines 5-6 and lines 9-12:
983 005 |RANGE 0
984 006 |RANGE 0
985 007 |PRIMARY CARET
987 009 |RANGE 1
988 010 |RANGE 1
989 011 |RANGE 1
990 012 |RANGE 1
993 then we want two spans: lines 5-7 and lines 9-12. */
995 void
996 layout::calculate_line_spans ()
998 /* This should only be called once, by the ctor. */
999 gcc_assert (m_line_spans.length () == 0);
1001 /* Populate tmp_spans with individual spans, for each of
1002 m_exploc, and for m_layout_ranges. */
1003 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1004 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1005 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1007 const layout_range *lr = &m_layout_ranges[i];
1008 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1009 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1010 lr->m_finish.m_line));
1013 /* Also add spans for any fix-it hints, in case they cover other lines. */
1014 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1016 const fixit_hint *hint = m_fixit_hints[i];
1017 gcc_assert (hint);
1018 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1021 /* Sort them. */
1022 tmp_spans.qsort(line_span::comparator);
1024 /* Now iterate through tmp_spans, copying into m_line_spans, and
1025 combining where possible. */
1026 gcc_assert (tmp_spans.length () > 0);
1027 m_line_spans.safe_push (tmp_spans[0]);
1028 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1030 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1031 const line_span *next = &tmp_spans[i];
1032 gcc_assert (next->m_first_line >= current->m_first_line);
1033 if (next->m_first_line <= current->m_last_line + 1)
1035 /* We can merge them. */
1036 if (next->m_last_line > current->m_last_line)
1037 current->m_last_line = next->m_last_line;
1039 else
1041 /* No merger possible. */
1042 m_line_spans.safe_push (*next);
1046 /* Verify the result, in m_line_spans. */
1047 gcc_assert (m_line_spans.length () > 0);
1048 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1050 const line_span *prev = &m_line_spans[i - 1];
1051 const line_span *next = &m_line_spans[i];
1052 /* The individual spans must be sane. */
1053 gcc_assert (prev->m_first_line <= prev->m_last_line);
1054 gcc_assert (next->m_first_line <= next->m_last_line);
1055 /* The spans must be ordered. */
1056 gcc_assert (prev->m_first_line < next->m_first_line);
1057 /* There must be a gap of at least one line between separate spans. */
1058 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1062 /* Print line ROW of source code, potentially colorized at any ranges, and
1063 populate *LBOUNDS_OUT.
1064 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1065 is its width. */
1067 void
1068 layout::print_source_line (int row, const char *line, int line_width,
1069 line_bounds *lbounds_out)
1071 m_colorizer.set_normal_text ();
1073 /* We will stop printing the source line at any trailing
1074 whitespace. */
1075 line_width = get_line_width_without_trailing_whitespace (line,
1076 line_width);
1077 line += m_x_offset;
1079 pp_space (m_pp);
1080 int first_non_ws = INT_MAX;
1081 int last_non_ws = 0;
1082 int column;
1083 for (column = 1 + m_x_offset; column <= line_width; column++)
1085 /* Assuming colorization is enabled for the caret and underline
1086 characters, we may also colorize the associated characters
1087 within the source line.
1089 For frontends that generate range information, we color the
1090 associated characters in the source line the same as the
1091 carets and underlines in the annotation line, to make it easier
1092 for the reader to see the pertinent code.
1094 For frontends that only generate carets, we don't colorize the
1095 characters above them, since this would look strange (e.g.
1096 colorizing just the first character in a token). */
1097 if (m_colorize_source_p)
1099 bool in_range_p;
1100 point_state state;
1101 in_range_p = get_state_at_point (row, column,
1102 0, INT_MAX,
1103 &state);
1104 if (in_range_p)
1105 m_colorizer.set_range (state.range_idx);
1106 else
1107 m_colorizer.set_normal_text ();
1109 char c = *line == '\t' ? ' ' : *line;
1110 if (c == '\0')
1111 c = ' ';
1112 if (c != ' ')
1114 last_non_ws = column;
1115 if (first_non_ws == INT_MAX)
1116 first_non_ws = column;
1118 pp_character (m_pp, c);
1119 line++;
1121 print_newline ();
1123 lbounds_out->m_first_non_ws = first_non_ws;
1124 lbounds_out->m_last_non_ws = last_non_ws;
1127 /* Determine if we should print an annotation line for ROW.
1128 i.e. if any of m_layout_ranges contains ROW. */
1130 bool
1131 layout::should_print_annotation_line_p (int row) const
1133 layout_range *range;
1134 int i;
1135 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1136 if (range->intersects_line_p (row))
1137 return true;
1138 return false;
1141 /* Print a line consisting of the caret/underlines for the given
1142 source line. */
1144 void
1145 layout::print_annotation_line (int row, const line_bounds lbounds)
1147 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1148 lbounds.m_last_non_ws);
1150 pp_space (m_pp);
1151 for (int column = 1 + m_x_offset; column < x_bound; column++)
1153 bool in_range_p;
1154 point_state state;
1155 in_range_p = get_state_at_point (row, column,
1156 lbounds.m_first_non_ws,
1157 lbounds.m_last_non_ws,
1158 &state);
1159 if (in_range_p)
1161 /* Within a range. Draw either the caret or an underline. */
1162 m_colorizer.set_range (state.range_idx);
1163 if (state.draw_caret_p)
1165 /* Draw the caret. */
1166 char caret_char;
1167 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1168 caret_char = m_context->caret_chars[state.range_idx];
1169 else
1170 caret_char = '^';
1171 pp_character (m_pp, caret_char);
1173 else
1174 pp_character (m_pp, '~');
1176 else
1178 /* Not in a range. */
1179 m_colorizer.set_normal_text ();
1180 pp_character (m_pp, ' ');
1183 print_newline ();
1186 /* If there are any fixit hints inserting new lines before source line ROW,
1187 print them.
1189 They are printed on lines of their own, before the source line
1190 itself, with a leading '+'. */
1192 void
1193 layout::print_leading_fixits (int row)
1195 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1197 const fixit_hint *hint = m_fixit_hints[i];
1199 if (!hint->ends_with_newline_p ())
1200 /* Not a newline fixit; print it in print_trailing_fixits. */
1201 continue;
1203 gcc_assert (hint->insertion_p ());
1205 if (hint->affects_line_p (m_exploc.file, row))
1207 /* Printing the '+' with normal colorization
1208 and the inserted line with "insert" colorization
1209 helps them stand out from each other, and from
1210 the surrounding text. */
1211 m_colorizer.set_normal_text ();
1212 pp_character (m_pp, '+');
1213 m_colorizer.set_fixit_insert ();
1214 /* Print all but the trailing newline of the fix-it hint.
1215 We have to print the newline separately to avoid
1216 getting additional pp prefixes printed. */
1217 for (size_t i = 0; i < hint->get_length () - 1; i++)
1218 pp_character (m_pp, hint->get_string ()[i]);
1219 m_colorizer.set_normal_text ();
1220 pp_newline (m_pp);
1225 /* Subroutine of layout::print_trailing_fixits.
1227 Determine if the annotation line printed for LINE contained
1228 the exact range from START_COLUMN to FINISH_COLUMN. */
1230 bool
1231 layout::annotation_line_showed_range_p (int line, int start_column,
1232 int finish_column) const
1234 layout_range *range;
1235 int i;
1236 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1237 if (range->m_start.m_line == line
1238 && range->m_start.m_column == start_column
1239 && range->m_finish.m_line == line
1240 && range->m_finish.m_column == finish_column)
1241 return true;
1242 return false;
1245 /* If there are any fixit hints on source line ROW, print them.
1246 They are printed in order, attempting to combine them onto lines, but
1247 starting new lines if necessary.
1248 Fix-it hints that insert new lines are handled separately,
1249 in layout::print_leading_fixits. */
1251 void
1252 layout::print_trailing_fixits (int row)
1254 int column = 0;
1255 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1257 const fixit_hint *hint = m_fixit_hints[i];
1259 if (hint->ends_with_newline_p ())
1260 continue;
1262 if (hint->affects_line_p (m_exploc.file, row))
1264 /* For now we assume each fixit hint can only touch one line. */
1265 if (hint->insertion_p ())
1267 /* This assumes the insertion just affects one line. */
1268 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1269 move_to_column (&column, start_column);
1270 m_colorizer.set_fixit_insert ();
1271 pp_string (m_pp, hint->get_string ());
1272 m_colorizer.set_normal_text ();
1273 column += hint->get_length ();
1275 else
1277 int line = LOCATION_LINE (hint->get_start_loc ());
1278 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1279 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1281 /* If the range of the replacement wasn't printed in the
1282 annotation line, then print an extra underline to
1283 indicate exactly what is being replaced.
1284 Always show it for removals. */
1285 if (!annotation_line_showed_range_p (line, start_column,
1286 finish_column)
1287 || hint->get_length () == 0)
1289 move_to_column (&column, start_column);
1290 m_colorizer.set_fixit_delete ();
1291 for (; column <= finish_column; column++)
1292 pp_character (m_pp, '-');
1293 m_colorizer.set_normal_text ();
1295 /* Print the replacement text. REPLACE also covers
1296 removals, so only do this extra work (potentially starting
1297 a new line) if we have actual replacement text. */
1298 if (hint->get_length () > 0)
1300 move_to_column (&column, start_column);
1301 m_colorizer.set_fixit_insert ();
1302 pp_string (m_pp, hint->get_string ());
1303 m_colorizer.set_normal_text ();
1304 column += hint->get_length ();
1310 /* Add a trailing newline, if necessary. */
1311 move_to_column (&column, 0);
1314 /* Disable any colorization and emit a newline. */
1316 void
1317 layout::print_newline ()
1319 m_colorizer.set_normal_text ();
1320 pp_newline (m_pp);
1323 /* Return true if (ROW/COLUMN) is within a range of the layout.
1324 If it returns true, OUT_STATE is written to, with the
1325 range index, and whether we should draw the caret at
1326 (ROW/COLUMN) (as opposed to an underline). */
1328 bool
1329 layout::get_state_at_point (/* Inputs. */
1330 int row, int column,
1331 int first_non_ws, int last_non_ws,
1332 /* Outputs. */
1333 point_state *out_state)
1335 layout_range *range;
1336 int i;
1337 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1339 if (range->contains_point (row, column))
1341 out_state->range_idx = i;
1343 /* Are we at the range's caret? is it visible? */
1344 out_state->draw_caret_p = false;
1345 if (range->m_show_caret_p
1346 && row == range->m_caret.m_line
1347 && column == range->m_caret.m_column)
1348 out_state->draw_caret_p = true;
1350 /* Within a multiline range, don't display any underline
1351 in any leading or trailing whitespace on a line.
1352 We do display carets, however. */
1353 if (!out_state->draw_caret_p)
1354 if (column < first_non_ws || column > last_non_ws)
1355 return false;
1357 /* We are within a range. */
1358 return true;
1362 return false;
1365 /* Helper function for use by layout::print_line when printing the
1366 annotation line under the source line.
1367 Get the column beyond the rightmost one that could contain a caret or
1368 range marker, given that we stop rendering at trailing whitespace.
1369 ROW is the source line within the given file.
1370 CARET_COLUMN is the column of range 0's caret.
1371 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1372 character of source (as determined when printing the source line). */
1375 layout::get_x_bound_for_row (int row, int caret_column,
1376 int last_non_ws_column)
1378 int result = caret_column + 1;
1380 layout_range *range;
1381 int i;
1382 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1384 if (row >= range->m_start.m_line)
1386 if (range->m_finish.m_line == row)
1388 /* On the final line within a range; ensure that
1389 we render up to the end of the range. */
1390 if (result <= range->m_finish.m_column)
1391 result = range->m_finish.m_column + 1;
1393 else if (row < range->m_finish.m_line)
1395 /* Within a multiline range; ensure that we render up to the
1396 last non-whitespace column. */
1397 if (result <= last_non_ws_column)
1398 result = last_non_ws_column + 1;
1403 return result;
1406 /* Given *COLUMN as an x-coordinate, print spaces to position
1407 successive output at DEST_COLUMN, printing a newline if necessary,
1408 and updating *COLUMN. */
1410 void
1411 layout::move_to_column (int *column, int dest_column)
1413 /* Start a new line if we need to. */
1414 if (*column > dest_column)
1416 print_newline ();
1417 *column = 0;
1420 while (*column < dest_column)
1422 pp_space (m_pp);
1423 (*column)++;
1427 /* For debugging layout issues, render a ruler giving column numbers
1428 (after the 1-column indent). */
1430 void
1431 layout::show_ruler (int max_column) const
1433 /* Hundreds. */
1434 if (max_column > 99)
1436 pp_space (m_pp);
1437 for (int column = 1 + m_x_offset; column <= max_column; column++)
1438 if (0 == column % 10)
1439 pp_character (m_pp, '0' + (column / 100) % 10);
1440 else
1441 pp_space (m_pp);
1442 pp_newline (m_pp);
1445 /* Tens. */
1446 pp_space (m_pp);
1447 for (int column = 1 + m_x_offset; column <= max_column; column++)
1448 if (0 == column % 10)
1449 pp_character (m_pp, '0' + (column / 10) % 10);
1450 else
1451 pp_space (m_pp);
1452 pp_newline (m_pp);
1454 /* Units. */
1455 pp_space (m_pp);
1456 for (int column = 1 + m_x_offset; column <= max_column; column++)
1457 pp_character (m_pp, '0' + (column % 10));
1458 pp_newline (m_pp);
1461 /* Print leading fix-its (for new lines inserted before the source line)
1462 then the source line, followed by an annotation line
1463 consisting of any caret/underlines, then any fixits.
1464 If the source line can't be read, print nothing. */
1465 void
1466 layout::print_line (int row)
1468 int line_width;
1469 const char *line = location_get_source_line (m_exploc.file, row,
1470 &line_width);
1471 if (!line)
1472 return;
1474 line_bounds lbounds;
1475 print_leading_fixits (row);
1476 print_source_line (row, line, line_width, &lbounds);
1477 if (should_print_annotation_line_p (row))
1478 print_annotation_line (row, lbounds);
1479 print_trailing_fixits (row);
1482 } /* End of anonymous namespace. */
1484 /* Print the physical source code corresponding to the location of
1485 this diagnostic, with additional annotations. */
1487 void
1488 diagnostic_show_locus (diagnostic_context * context,
1489 rich_location *richloc,
1490 diagnostic_t diagnostic_kind)
1492 pp_newline (context->printer);
1494 location_t loc = richloc->get_loc ();
1495 /* Do nothing if source-printing has been disabled. */
1496 if (!context->show_caret)
1497 return;
1499 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1500 if (loc <= BUILTINS_LOCATION)
1501 return;
1503 /* Don't print the same source location twice in a row, unless we have
1504 fix-it hints. */
1505 if (loc == context->last_location
1506 && richloc->get_num_fixit_hints () == 0)
1507 return;
1509 context->last_location = loc;
1511 const char *saved_prefix = pp_get_prefix (context->printer);
1512 pp_set_prefix (context->printer, NULL);
1514 layout layout (context, richloc, diagnostic_kind);
1515 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1516 line_span_idx++)
1518 const line_span *line_span = layout.get_line_span (line_span_idx);
1519 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1521 expanded_location exploc = layout.get_expanded_location (line_span);
1522 context->start_span (context, exploc);
1524 int last_line = line_span->get_last_line ();
1525 for (int row = line_span->get_first_line (); row <= last_line; row++)
1526 layout.print_line (row);
1529 pp_set_prefix (context->printer, saved_prefix);
1532 #if CHECKING_P
1534 namespace selftest {
1536 /* Selftests for diagnostic_show_locus. */
1538 /* Convenience subclass of diagnostic_context for testing
1539 diagnostic_show_locus. */
1541 class test_diagnostic_context : public diagnostic_context
1543 public:
1544 test_diagnostic_context ()
1546 diagnostic_initialize (this, 0);
1547 show_caret = true;
1548 show_column = true;
1549 start_span = start_span_cb;
1551 ~test_diagnostic_context ()
1553 diagnostic_finish (this);
1556 /* Implementation of diagnostic_start_span_fn, hiding the
1557 real filename (to avoid printing the names of tempfiles). */
1558 static void
1559 start_span_cb (diagnostic_context *context, expanded_location exploc)
1561 exploc.file = "FILENAME";
1562 default_diagnostic_start_span_fn (context, exploc);
1566 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1568 static void
1569 test_diagnostic_show_locus_unknown_location ()
1571 test_diagnostic_context dc;
1572 rich_location richloc (line_table, UNKNOWN_LOCATION);
1573 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1574 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1577 /* Verify that diagnostic_show_locus works sanely for various
1578 single-line cases.
1580 All of these work on the following 1-line source file:
1581 .0000000001111111
1582 .1234567890123456
1583 "foo = bar.field;\n"
1584 which is set up by test_diagnostic_show_locus_one_liner and calls
1585 them. */
1587 /* Just a caret. */
1589 static void
1590 test_one_liner_simple_caret ()
1592 test_diagnostic_context dc;
1593 location_t caret = linemap_position_for_column (line_table, 10);
1594 rich_location richloc (line_table, caret);
1595 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1596 ASSERT_STREQ ("\n"
1597 " foo = bar.field;\n"
1598 " ^\n",
1599 pp_formatted_text (dc.printer));
1602 /* Caret and range. */
1604 static void
1605 test_one_liner_caret_and_range ()
1607 test_diagnostic_context dc;
1608 location_t caret = linemap_position_for_column (line_table, 10);
1609 location_t start = linemap_position_for_column (line_table, 7);
1610 location_t finish = linemap_position_for_column (line_table, 15);
1611 location_t loc = make_location (caret, start, finish);
1612 rich_location richloc (line_table, loc);
1613 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1614 ASSERT_STREQ ("\n"
1615 " foo = bar.field;\n"
1616 " ~~~^~~~~~\n",
1617 pp_formatted_text (dc.printer));
1620 /* Multiple ranges and carets. */
1622 static void
1623 test_one_liner_multiple_carets_and_ranges ()
1625 test_diagnostic_context dc;
1626 location_t foo
1627 = make_location (linemap_position_for_column (line_table, 2),
1628 linemap_position_for_column (line_table, 1),
1629 linemap_position_for_column (line_table, 3));
1630 dc.caret_chars[0] = 'A';
1632 location_t bar
1633 = make_location (linemap_position_for_column (line_table, 8),
1634 linemap_position_for_column (line_table, 7),
1635 linemap_position_for_column (line_table, 9));
1636 dc.caret_chars[1] = 'B';
1638 location_t field
1639 = make_location (linemap_position_for_column (line_table, 13),
1640 linemap_position_for_column (line_table, 11),
1641 linemap_position_for_column (line_table, 15));
1642 dc.caret_chars[2] = 'C';
1644 rich_location richloc (line_table, foo);
1645 richloc.add_range (bar, true);
1646 richloc.add_range (field, true);
1647 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1648 ASSERT_STREQ ("\n"
1649 " foo = bar.field;\n"
1650 " ~A~ ~B~ ~~C~~\n",
1651 pp_formatted_text (dc.printer));
1654 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1656 static void
1657 test_one_liner_fixit_insert_before ()
1659 test_diagnostic_context dc;
1660 location_t caret = linemap_position_for_column (line_table, 7);
1661 rich_location richloc (line_table, caret);
1662 richloc.add_fixit_insert_before ("&");
1663 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1664 ASSERT_STREQ ("\n"
1665 " foo = bar.field;\n"
1666 " ^\n"
1667 " &\n",
1668 pp_formatted_text (dc.printer));
1671 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1673 static void
1674 test_one_liner_fixit_insert_after ()
1676 test_diagnostic_context dc;
1677 location_t start = linemap_position_for_column (line_table, 1);
1678 location_t finish = linemap_position_for_column (line_table, 3);
1679 location_t foo = make_location (start, start, finish);
1680 rich_location richloc (line_table, foo);
1681 richloc.add_fixit_insert_after ("[0]");
1682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1683 ASSERT_STREQ ("\n"
1684 " foo = bar.field;\n"
1685 " ^~~\n"
1686 " [0]\n",
1687 pp_formatted_text (dc.printer));
1690 /* Removal fix-it hint: removal of the ".field". */
1692 static void
1693 test_one_liner_fixit_remove ()
1695 test_diagnostic_context dc;
1696 location_t start = linemap_position_for_column (line_table, 10);
1697 location_t finish = linemap_position_for_column (line_table, 15);
1698 location_t dot = make_location (start, start, finish);
1699 rich_location richloc (line_table, dot);
1700 richloc.add_fixit_remove ();
1701 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1702 ASSERT_STREQ ("\n"
1703 " foo = bar.field;\n"
1704 " ^~~~~~\n"
1705 " ------\n",
1706 pp_formatted_text (dc.printer));
1709 /* Replace fix-it hint: replacing "field" with "m_field". */
1711 static void
1712 test_one_liner_fixit_replace ()
1714 test_diagnostic_context dc;
1715 location_t start = linemap_position_for_column (line_table, 11);
1716 location_t finish = linemap_position_for_column (line_table, 15);
1717 location_t field = make_location (start, start, finish);
1718 rich_location richloc (line_table, field);
1719 richloc.add_fixit_replace ("m_field");
1720 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1721 ASSERT_STREQ ("\n"
1722 " foo = bar.field;\n"
1723 " ^~~~~\n"
1724 " m_field\n",
1725 pp_formatted_text (dc.printer));
1728 /* Replace fix-it hint: replacing "field" with "m_field",
1729 but where the caret was elsewhere. */
1731 static void
1732 test_one_liner_fixit_replace_non_equal_range ()
1734 test_diagnostic_context dc;
1735 location_t equals = linemap_position_for_column (line_table, 5);
1736 location_t start = linemap_position_for_column (line_table, 11);
1737 location_t finish = linemap_position_for_column (line_table, 15);
1738 rich_location richloc (line_table, equals);
1739 source_range range;
1740 range.m_start = start;
1741 range.m_finish = finish;
1742 richloc.add_fixit_replace (range, "m_field");
1743 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1744 /* The replacement range is not indicated in the annotation line, so
1745 it should be indicated via an additional underline. */
1746 ASSERT_STREQ ("\n"
1747 " foo = bar.field;\n"
1748 " ^\n"
1749 " -----\n"
1750 " m_field\n",
1751 pp_formatted_text (dc.printer));
1754 /* Replace fix-it hint: replacing "field" with "m_field",
1755 where the caret was elsewhere, but where a secondary range
1756 exactly covers "field". */
1758 static void
1759 test_one_liner_fixit_replace_equal_secondary_range ()
1761 test_diagnostic_context dc;
1762 location_t equals = linemap_position_for_column (line_table, 5);
1763 location_t start = linemap_position_for_column (line_table, 11);
1764 location_t finish = linemap_position_for_column (line_table, 15);
1765 rich_location richloc (line_table, equals);
1766 location_t field = make_location (start, start, finish);
1767 richloc.add_range (field, false);
1768 richloc.add_fixit_replace (field, "m_field");
1769 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1770 /* The replacement range is indicated in the annotation line,
1771 so it shouldn't be indicated via an additional underline. */
1772 ASSERT_STREQ ("\n"
1773 " foo = bar.field;\n"
1774 " ^ ~~~~~\n"
1775 " m_field\n",
1776 pp_formatted_text (dc.printer));
1779 /* Verify that we can use ad-hoc locations when adding fixits to a
1780 rich_location. */
1782 static void
1783 test_one_liner_fixit_validation_adhoc_locations ()
1785 /* Generate a range that's too long to be packed, so must
1786 be stored as an ad-hoc location (given the defaults
1787 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
1788 const location_t c7 = linemap_position_for_column (line_table, 7);
1789 const location_t c47 = linemap_position_for_column (line_table, 47);
1790 const location_t loc = make_location (c7, c7, c47);
1792 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1793 return;
1795 ASSERT_TRUE (IS_ADHOC_LOC (loc));
1797 /* Insert. */
1799 rich_location richloc (line_table, loc);
1800 richloc.add_fixit_insert_before (loc, "test");
1801 /* It should not have been discarded by the validator. */
1802 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1804 test_diagnostic_context dc;
1805 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1806 ASSERT_STREQ ("\n"
1807 " foo = bar.field;\n"
1808 " ^~~~~~~~~~ \n"
1809 " test\n",
1810 pp_formatted_text (dc.printer));
1813 /* Remove. */
1815 rich_location richloc (line_table, loc);
1816 source_range range = source_range::from_locations (loc, c47);
1817 richloc.add_fixit_remove (range);
1818 /* It should not have been discarded by the validator. */
1819 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1821 test_diagnostic_context dc;
1822 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1823 ASSERT_STREQ ("\n"
1824 " foo = bar.field;\n"
1825 " ^~~~~~~~~~ \n"
1826 " -----------------------------------------\n",
1827 pp_formatted_text (dc.printer));
1830 /* Replace. */
1832 rich_location richloc (line_table, loc);
1833 source_range range = source_range::from_locations (loc, c47);
1834 richloc.add_fixit_replace (range, "test");
1835 /* It should not have been discarded by the validator. */
1836 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1838 test_diagnostic_context dc;
1839 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1840 ASSERT_STREQ ("\n"
1841 " foo = bar.field;\n"
1842 " ^~~~~~~~~~ \n"
1843 " test\n",
1844 pp_formatted_text (dc.printer));
1848 /* Test of consolidating insertions at the same location. */
1850 static void
1851 test_one_liner_many_fixits_1 ()
1853 test_diagnostic_context dc;
1854 location_t equals = linemap_position_for_column (line_table, 5);
1855 rich_location richloc (line_table, equals);
1856 for (int i = 0; i < 19; i++)
1857 richloc.add_fixit_insert_before ("a");
1858 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1859 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1860 ASSERT_STREQ ("\n"
1861 " foo = bar.field;\n"
1862 " ^\n"
1863 " aaaaaaaaaaaaaaaaaaa\n",
1864 pp_formatted_text (dc.printer));
1867 /* Ensure that we can add an arbitrary number of fix-it hints to a
1868 rich_location, even if they are not consolidated. */
1870 static void
1871 test_one_liner_many_fixits_2 ()
1873 test_diagnostic_context dc;
1874 location_t equals = linemap_position_for_column (line_table, 5);
1875 rich_location richloc (line_table, equals);
1876 for (int i = 0; i < 19; i++)
1878 location_t loc = linemap_position_for_column (line_table, i * 2);
1879 richloc.add_fixit_insert_before (loc, "a");
1881 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
1882 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1883 ASSERT_STREQ ("\n"
1884 " foo = bar.field;\n"
1885 " ^\n"
1886 "a a a a a a a a a a a a a a a a a a a\n",
1887 pp_formatted_text (dc.printer));
1890 /* Run the various one-liner tests. */
1892 static void
1893 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1895 /* Create a tempfile and write some text to it.
1896 ....................0000000001111111.
1897 ....................1234567890123456. */
1898 const char *content = "foo = bar.field;\n";
1899 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1900 line_table_test ltt (case_);
1902 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1904 location_t line_end = linemap_position_for_column (line_table, 16);
1906 /* Don't attempt to run the tests if column data might be unavailable. */
1907 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1908 return;
1910 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1911 ASSERT_EQ (1, LOCATION_LINE (line_end));
1912 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1914 test_one_liner_simple_caret ();
1915 test_one_liner_caret_and_range ();
1916 test_one_liner_multiple_carets_and_ranges ();
1917 test_one_liner_fixit_insert_before ();
1918 test_one_liner_fixit_insert_after ();
1919 test_one_liner_fixit_remove ();
1920 test_one_liner_fixit_replace ();
1921 test_one_liner_fixit_replace_non_equal_range ();
1922 test_one_liner_fixit_replace_equal_secondary_range ();
1923 test_one_liner_fixit_validation_adhoc_locations ();
1924 test_one_liner_many_fixits_1 ();
1925 test_one_liner_many_fixits_2 ();
1928 /* Verify that we print fixits even if they only affect lines
1929 outside those covered by the ranges in the rich_location. */
1931 static void
1932 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
1934 /* Create a tempfile and write some text to it.
1935 ...000000000111111111122222222223333333333.
1936 ...123456789012345678901234567890123456789. */
1937 const char *content
1938 = ("struct point { double x; double y; };\n" /* line 1. */
1939 "struct point origin = {x: 0.0,\n" /* line 2. */
1940 " y\n" /* line 3. */
1941 "\n" /* line 4. */
1942 "\n" /* line 5. */
1943 " : 0.0};\n"); /* line 6. */
1944 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1945 line_table_test ltt (case_);
1947 const line_map_ordinary *ord_map
1948 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
1949 tmp.get_filename (), 0));
1951 linemap_line_start (line_table, 1, 100);
1953 const location_t final_line_end
1954 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
1956 /* Don't attempt to run the tests if column data might be unavailable. */
1957 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1958 return;
1960 /* A pair of tests for modernizing the initializers to C99-style. */
1962 /* The one-liner case (line 2). */
1964 test_diagnostic_context dc;
1965 const location_t x
1966 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
1967 const location_t colon
1968 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
1969 rich_location richloc (line_table, colon);
1970 richloc.add_fixit_insert_before (x, ".");
1971 richloc.add_fixit_replace (colon, "=");
1972 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1973 ASSERT_STREQ ("\n"
1974 " struct point origin = {x: 0.0,\n"
1975 " ^\n"
1976 " .=\n",
1977 pp_formatted_text (dc.printer));
1980 /* The multiline case. The caret for the rich_location is on line 6;
1981 verify that insertion fixit on line 3 is still printed (and that
1982 span starts are printed due to the gap between the span at line 3
1983 and that at line 6). */
1985 test_diagnostic_context dc;
1986 const location_t y
1987 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
1988 const location_t colon
1989 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
1990 rich_location richloc (line_table, colon);
1991 richloc.add_fixit_insert_before (y, ".");
1992 richloc.add_fixit_replace (colon, "=");
1993 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1994 ASSERT_STREQ ("\n"
1995 "FILENAME:3:24:\n"
1996 " y\n"
1997 " .\n"
1998 "FILENAME:6:25:\n"
1999 " : 0.0};\n"
2000 " ^\n"
2001 " =\n",
2002 pp_formatted_text (dc.printer));
2007 /* Verify that fix-it hints are appropriately consolidated.
2009 If any fix-it hints in a rich_location involve locations beyond
2010 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2011 the fix-it as a whole, so there should be none.
2013 Otherwise, verify that consecutive "replace" and "remove" fix-its
2014 are merged, and that other fix-its remain separate. */
2016 static void
2017 test_fixit_consolidation (const line_table_case &case_)
2019 line_table_test ltt (case_);
2021 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2023 const location_t c10 = linemap_position_for_column (line_table, 10);
2024 const location_t c15 = linemap_position_for_column (line_table, 15);
2025 const location_t c16 = linemap_position_for_column (line_table, 16);
2026 const location_t c17 = linemap_position_for_column (line_table, 17);
2027 const location_t c20 = linemap_position_for_column (line_table, 20);
2028 const location_t c21 = linemap_position_for_column (line_table, 21);
2029 const location_t caret = c10;
2031 /* Insert + insert. */
2033 rich_location richloc (line_table, caret);
2034 richloc.add_fixit_insert_before (c10, "foo");
2035 richloc.add_fixit_insert_before (c15, "bar");
2037 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2038 /* Bogus column info for 2nd fixit, so no fixits. */
2039 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2040 else
2041 /* They should not have been merged. */
2042 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2045 /* Insert + replace. */
2047 rich_location richloc (line_table, caret);
2048 richloc.add_fixit_insert_before (c10, "foo");
2049 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2050 "bar");
2052 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2053 /* Bogus column info for 2nd fixit, so no fixits. */
2054 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2055 else
2056 /* They should not have been merged. */
2057 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2060 /* Replace + non-consecutive insert. */
2062 rich_location richloc (line_table, caret);
2063 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2064 "bar");
2065 richloc.add_fixit_insert_before (c17, "foo");
2067 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2068 /* Bogus column info for 2nd fixit, so no fixits. */
2069 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2070 else
2071 /* They should not have been merged. */
2072 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2075 /* Replace + non-consecutive replace. */
2077 rich_location richloc (line_table, caret);
2078 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2079 "foo");
2080 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2081 "bar");
2083 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2084 /* Bogus column info for 2nd fixit, so no fixits. */
2085 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2086 else
2087 /* They should not have been merged. */
2088 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2091 /* Replace + consecutive replace. */
2093 rich_location richloc (line_table, caret);
2094 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2095 "foo");
2096 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2097 "bar");
2099 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2100 /* Bogus column info for 2nd fixit, so no fixits. */
2101 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2102 else
2104 /* They should have been merged into a single "replace". */
2105 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2106 const fixit_hint *hint = richloc.get_fixit_hint (0);
2107 ASSERT_STREQ ("foobar", hint->get_string ());
2108 ASSERT_EQ (c10, hint->get_start_loc ());
2109 ASSERT_EQ (c21, hint->get_next_loc ());
2113 /* Replace + consecutive removal. */
2115 rich_location richloc (line_table, caret);
2116 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2117 "foo");
2118 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2120 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2121 /* Bogus column info for 2nd fixit, so no fixits. */
2122 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2123 else
2125 /* They should have been merged into a single replace, with the
2126 range extended to cover that of the removal. */
2127 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2128 const fixit_hint *hint = richloc.get_fixit_hint (0);
2129 ASSERT_STREQ ("foo", hint->get_string ());
2130 ASSERT_EQ (c10, hint->get_start_loc ());
2131 ASSERT_EQ (c21, hint->get_next_loc ());
2135 /* Consecutive removals. */
2137 rich_location richloc (line_table, caret);
2138 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2139 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2141 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2142 /* Bogus column info for 2nd fixit, so no fixits. */
2143 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2144 else
2146 /* They should have been merged into a single "replace-with-empty". */
2147 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2148 const fixit_hint *hint = richloc.get_fixit_hint (0);
2149 ASSERT_STREQ ("", hint->get_string ());
2150 ASSERT_EQ (c10, hint->get_start_loc ());
2151 ASSERT_EQ (c21, hint->get_next_loc ());
2156 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2158 static void
2159 test_fixit_insert_containing_newline (const line_table_case &case_)
2161 /* Create a tempfile and write some text to it.
2162 .........................0000000001111111.
2163 .........................1234567890123456. */
2164 const char *old_content = (" case 'a':\n" /* line 1. */
2165 " x = a;\n" /* line 2. */
2166 " case 'b':\n" /* line 3. */
2167 " x = b;\n");/* line 4. */
2169 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2170 line_table_test ltt (case_);
2171 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2173 location_t case_start = linemap_position_for_column (line_table, 5);
2174 location_t case_finish = linemap_position_for_column (line_table, 13);
2175 location_t case_loc = make_location (case_start, case_start, case_finish);
2176 location_t line_start = linemap_position_for_column (line_table, 1);
2178 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2179 return;
2181 /* Add a "break;" on a line by itself before line 3 i.e. before
2182 column 1 of line 3. */
2184 rich_location richloc (line_table, case_loc);
2185 richloc.add_fixit_insert_before (line_start, " break;\n");
2186 test_diagnostic_context dc;
2187 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2188 ASSERT_STREQ ("\n"
2189 "+ break;\n"
2190 " case 'b':\n"
2191 " ^~~~~~~~~\n",
2192 pp_formatted_text (dc.printer));
2195 /* Verify that attempts to add text with a newline fail when the
2196 insertion point is *not* at the start of a line. */
2198 rich_location richloc (line_table, case_loc);
2199 richloc.add_fixit_insert_before (case_start, "break;\n");
2200 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2201 test_diagnostic_context dc;
2202 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2203 ASSERT_STREQ ("\n"
2204 " case 'b':\n"
2205 " ^~~~~~~~~\n",
2206 pp_formatted_text (dc.printer));
2210 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
2211 of the file, where the fix-it is printed in a different line-span
2212 to the primary range of the diagnostic. */
2214 static void
2215 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
2217 /* Create a tempfile and write some text to it.
2218 .........................0000000001111111.
2219 .........................1234567890123456. */
2220 const char *old_content = ("test (int ch)\n" /* line 1. */
2221 "{\n" /* line 2. */
2222 " putchar (ch);\n" /* line 3. */
2223 "}\n"); /* line 4. */
2225 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2226 line_table_test ltt (case_);
2228 const line_map_ordinary *ord_map = linemap_check_ordinary
2229 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
2230 linemap_line_start (line_table, 1, 100);
2232 /* The primary range is the "putchar" token. */
2233 location_t putchar_start
2234 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
2235 location_t putchar_finish
2236 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
2237 location_t putchar_loc
2238 = make_location (putchar_start, putchar_start, putchar_finish);
2239 rich_location richloc (line_table, putchar_loc);
2241 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
2242 location_t file_start
2243 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2244 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
2246 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2247 return;
2249 test_diagnostic_context dc;
2250 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2251 ASSERT_STREQ ("\n"
2252 "FILENAME:1:1:\n"
2253 "+#include <stdio.h>\n"
2254 " test (int ch)\n"
2255 "FILENAME:3:2:\n"
2256 " putchar (ch);\n"
2257 " ^~~~~~~\n",
2258 pp_formatted_text (dc.printer));
2261 /* Replacement fix-it hint containing a newline.
2262 This will fail, as newlines are only supported when inserting at the
2263 beginning of a line. */
2265 static void
2266 test_fixit_replace_containing_newline (const line_table_case &case_)
2268 /* Create a tempfile and write some text to it.
2269 .........................0000000001111.
2270 .........................1234567890123. */
2271 const char *old_content = "foo = bar ();\n";
2273 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2274 line_table_test ltt (case_);
2275 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2277 /* Replace the " = " with "\n = ", as if we were reformatting an
2278 overly long line. */
2279 location_t start = linemap_position_for_column (line_table, 4);
2280 location_t finish = linemap_position_for_column (line_table, 6);
2281 location_t loc = linemap_position_for_column (line_table, 13);
2282 rich_location richloc (line_table, loc);
2283 source_range range = source_range::from_locations (start, finish);
2284 richloc.add_fixit_replace (range, "\n =");
2286 /* Arbitrary newlines are not yet supported within fix-it hints, so
2287 the fix-it should not be displayed. */
2288 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2290 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2291 return;
2293 test_diagnostic_context dc;
2294 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2295 ASSERT_STREQ ("\n"
2296 " foo = bar ();\n"
2297 " ^\n",
2298 pp_formatted_text (dc.printer));
2301 /* Run all of the selftests within this file. */
2303 void
2304 diagnostic_show_locus_c_tests ()
2306 test_layout_range_for_single_point ();
2307 test_layout_range_for_single_line ();
2308 test_layout_range_for_multiple_lines ();
2310 test_get_line_width_without_trailing_whitespace ();
2312 test_diagnostic_show_locus_unknown_location ();
2314 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
2315 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
2316 for_each_line_table_case (test_fixit_consolidation);
2317 for_each_line_table_case (test_fixit_insert_containing_newline);
2318 for_each_line_table_case (test_fixit_insert_containing_newline_2);
2319 for_each_line_table_case (test_fixit_replace_containing_newline);
2322 } // namespace selftest
2324 #endif /* #if CHECKING_P */