system.h (GCC_DIAGNOSTIC_PUSH_IGNORED, [...]): Define.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob5c386ae256d9606d47d4d100e7d0b6e0622cfaf1
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 bool print_source_line (int row, line_bounds *lbounds_out);
207 bool should_print_annotation_line_p (int row) const;
208 void print_annotation_line (int row, const line_bounds lbounds);
209 bool annotation_line_showed_range_p (int line, int start_column,
210 int finish_column) const;
211 void print_any_fixits (int row);
213 void show_ruler (int max_column) const;
215 private:
216 bool validate_fixit_hint_p (const fixit_hint *hint);
218 void calculate_line_spans ();
220 void print_newline ();
222 bool
223 get_state_at_point (/* Inputs. */
224 int row, int column,
225 int first_non_ws, int last_non_ws,
226 /* Outputs. */
227 point_state *out_state);
230 get_x_bound_for_row (int row, int caret_column,
231 int last_non_ws);
233 void
234 move_to_column (int *column, int dest_column);
236 private:
237 diagnostic_context *m_context;
238 pretty_printer *m_pp;
239 diagnostic_t m_diagnostic_kind;
240 expanded_location m_exploc;
241 colorizer m_colorizer;
242 bool m_colorize_source_p;
243 auto_vec <layout_range> m_layout_ranges;
244 auto_vec <const fixit_hint *> m_fixit_hints;
245 auto_vec <line_span> m_line_spans;
246 int m_x_offset;
249 /* Implementation of "class colorizer". */
251 /* The constructor for "colorizer". Lookup and store color codes for the
252 different kinds of things we might need to print. */
254 colorizer::colorizer (diagnostic_context *context,
255 diagnostic_t diagnostic_kind) :
256 m_context (context),
257 m_diagnostic_kind (diagnostic_kind),
258 m_current_state (STATE_NORMAL_TEXT)
260 m_range1 = get_color_by_name ("range1");
261 m_range2 = get_color_by_name ("range2");
262 m_fixit_insert = get_color_by_name ("fixit-insert");
263 m_fixit_delete = get_color_by_name ("fixit-delete");
264 m_stop_color = colorize_stop (pp_show_color (context->printer));
267 /* The destructor for "colorize". If colorization is on, print a code to
268 turn it off. */
270 colorizer::~colorizer ()
272 finish_state (m_current_state);
275 /* Update state, printing color codes if necessary if there's a state
276 change. */
278 void
279 colorizer::set_state (int new_state)
281 if (m_current_state != new_state)
283 finish_state (m_current_state);
284 m_current_state = new_state;
285 begin_state (new_state);
289 /* Turn on any colorization for STATE. */
291 void
292 colorizer::begin_state (int state)
294 switch (state)
296 case STATE_NORMAL_TEXT:
297 break;
299 case STATE_FIXIT_INSERT:
300 pp_string (m_context->printer, m_fixit_insert);
301 break;
303 case STATE_FIXIT_DELETE:
304 pp_string (m_context->printer, m_fixit_delete);
305 break;
307 case 0:
308 /* Make range 0 be the same color as the "kind" text
309 (error vs warning vs note). */
310 pp_string
311 (m_context->printer,
312 colorize_start (pp_show_color (m_context->printer),
313 diagnostic_get_color_for_kind (m_diagnostic_kind)));
314 break;
316 case 1:
317 pp_string (m_context->printer, m_range1);
318 break;
320 case 2:
321 pp_string (m_context->printer, m_range2);
322 break;
324 default:
325 /* For ranges beyond 2, alternate between color 1 and color 2. */
327 gcc_assert (state > 2);
328 pp_string (m_context->printer,
329 state % 2 ? m_range1 : m_range2);
331 break;
335 /* Turn off any colorization for STATE. */
337 void
338 colorizer::finish_state (int state)
340 if (state != STATE_NORMAL_TEXT)
341 pp_string (m_context->printer, m_stop_color);
344 /* Get the color code for NAME (or the empty string if
345 colorization is disabled). */
347 const char *
348 colorizer::get_color_by_name (const char *name)
350 return colorize_start (pp_show_color (m_context->printer), name);
353 /* Implementation of class layout_range. */
355 /* The constructor for class layout_range.
356 Initialize various layout_point fields from expanded_location
357 equivalents; we've already filtered on file. */
359 layout_range::layout_range (const expanded_location *start_exploc,
360 const expanded_location *finish_exploc,
361 bool show_caret_p,
362 const expanded_location *caret_exploc)
363 : m_start (*start_exploc),
364 m_finish (*finish_exploc),
365 m_show_caret_p (show_caret_p),
366 m_caret (*caret_exploc)
370 /* Is (column, row) within the given range?
371 We've already filtered on the file.
373 Ranges are closed (both limits are within the range).
375 Example A: a single-line range:
376 start: (col=22, line=2)
377 finish: (col=38, line=2)
379 |00000011111111112222222222333333333344444444444
380 |34567890123456789012345678901234567890123456789
381 --+-----------------------------------------------
382 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
383 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
384 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
386 Example B: a multiline range with
387 start: (col=14, line=3)
388 finish: (col=08, line=5)
390 |00000011111111112222222222333333333344444444444
391 |34567890123456789012345678901234567890123456789
392 --+-----------------------------------------------
393 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
394 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
395 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
396 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
397 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
398 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
399 --+-----------------------------------------------
401 Legend:
402 - 'b' indicates a point *before* the range
403 - 'S' indicates the start of the range
404 - 'w' indicates a point within the range
405 - 'F' indicates the finish of the range (which is
406 within it).
407 - 'a' indicates a subsequent point *after* the range. */
409 bool
410 layout_range::contains_point (int row, int column) const
412 gcc_assert (m_start.m_line <= m_finish.m_line);
413 /* ...but the equivalent isn't true for the columns;
414 consider example B in the comment above. */
416 if (row < m_start.m_line)
417 /* Points before the first line of the range are
418 outside it (corresponding to line 01 in example A
419 and lines 01 and 02 in example B above). */
420 return false;
422 if (row == m_start.m_line)
423 /* On same line as start of range (corresponding
424 to line 02 in example A and line 03 in example B). */
426 if (column < m_start.m_column)
427 /* Points on the starting line of the range, but
428 before the column in which it begins. */
429 return false;
431 if (row < m_finish.m_line)
432 /* This is a multiline range; the point
433 is within it (corresponds to line 03 in example B
434 from column 14 onwards) */
435 return true;
436 else
438 /* This is a single-line range. */
439 gcc_assert (row == m_finish.m_line);
440 return column <= m_finish.m_column;
444 /* The point is in a line beyond that containing the
445 start of the range: lines 03 onwards in example A,
446 and lines 04 onwards in example B. */
447 gcc_assert (row > m_start.m_line);
449 if (row > m_finish.m_line)
450 /* The point is beyond the final line of the range
451 (lines 03 onwards in example A, and lines 06 onwards
452 in example B). */
453 return false;
455 if (row < m_finish.m_line)
457 /* The point is in a line that's fully within a multiline
458 range (e.g. line 04 in example B). */
459 gcc_assert (m_start.m_line < m_finish.m_line);
460 return true;
463 gcc_assert (row == m_finish.m_line);
465 return column <= m_finish.m_column;
468 /* Does this layout_range contain any part of line ROW? */
470 bool
471 layout_range::intersects_line_p (int row) const
473 gcc_assert (m_start.m_line <= m_finish.m_line);
474 if (row < m_start.m_line)
475 return false;
476 if (row > m_finish.m_line)
477 return false;
478 return true;
481 #if CHECKING_P
483 /* A helper function for testing layout_range. */
485 static layout_range
486 make_range (int start_line, int start_col, int end_line, int end_col)
488 const expanded_location start_exploc
489 = {"test.c", start_line, start_col, NULL, false};
490 const expanded_location finish_exploc
491 = {"test.c", end_line, end_col, NULL, false};
492 return layout_range (&start_exploc, &finish_exploc, false,
493 &start_exploc);
496 /* Selftests for layout_range::contains_point and
497 layout_range::intersects_line_p. */
499 /* Selftest for layout_range, where the layout_range
500 is a range with start==end i.e. a single point. */
502 static void
503 test_layout_range_for_single_point ()
505 layout_range point = make_range (7, 10, 7, 10);
507 /* Tests for layout_range::contains_point. */
509 /* Before the line. */
510 ASSERT_FALSE (point.contains_point (6, 1));
512 /* On the line, but before start. */
513 ASSERT_FALSE (point.contains_point (7, 9));
515 /* At the point. */
516 ASSERT_TRUE (point.contains_point (7, 10));
518 /* On the line, after the point. */
519 ASSERT_FALSE (point.contains_point (7, 11));
521 /* After the line. */
522 ASSERT_FALSE (point.contains_point (8, 1));
524 /* Tests for layout_range::intersects_line_p. */
525 ASSERT_FALSE (point.intersects_line_p (6));
526 ASSERT_TRUE (point.intersects_line_p (7));
527 ASSERT_FALSE (point.intersects_line_p (8));
530 /* Selftest for layout_range, where the layout_range
531 is the single-line range shown as "Example A" above. */
533 static void
534 test_layout_range_for_single_line ()
536 layout_range example_a = make_range (2, 22, 2, 38);
538 /* Tests for layout_range::contains_point. */
540 /* Before the line. */
541 ASSERT_FALSE (example_a.contains_point (1, 1));
543 /* On the line, but before start. */
544 ASSERT_FALSE (example_a.contains_point (2, 21));
546 /* On the line, at the start. */
547 ASSERT_TRUE (example_a.contains_point (2, 22));
549 /* On the line, within the range. */
550 ASSERT_TRUE (example_a.contains_point (2, 23));
552 /* On the line, at the end. */
553 ASSERT_TRUE (example_a.contains_point (2, 38));
555 /* On the line, after the end. */
556 ASSERT_FALSE (example_a.contains_point (2, 39));
558 /* After the line. */
559 ASSERT_FALSE (example_a.contains_point (2, 39));
561 /* Tests for layout_range::intersects_line_p. */
562 ASSERT_FALSE (example_a.intersects_line_p (1));
563 ASSERT_TRUE (example_a.intersects_line_p (2));
564 ASSERT_FALSE (example_a.intersects_line_p (3));
567 /* Selftest for layout_range, where the layout_range
568 is the multi-line range shown as "Example B" above. */
570 static void
571 test_layout_range_for_multiple_lines ()
573 layout_range example_b = make_range (3, 14, 5, 8);
575 /* Tests for layout_range::contains_point. */
577 /* Before first line. */
578 ASSERT_FALSE (example_b.contains_point (1, 1));
580 /* On the first line, but before start. */
581 ASSERT_FALSE (example_b.contains_point (3, 13));
583 /* At the start. */
584 ASSERT_TRUE (example_b.contains_point (3, 14));
586 /* On the first line, within the range. */
587 ASSERT_TRUE (example_b.contains_point (3, 15));
589 /* On an interior line.
590 The column number should not matter; try various boundary
591 values. */
592 ASSERT_TRUE (example_b.contains_point (4, 1));
593 ASSERT_TRUE (example_b.contains_point (4, 7));
594 ASSERT_TRUE (example_b.contains_point (4, 8));
595 ASSERT_TRUE (example_b.contains_point (4, 9));
596 ASSERT_TRUE (example_b.contains_point (4, 13));
597 ASSERT_TRUE (example_b.contains_point (4, 14));
598 ASSERT_TRUE (example_b.contains_point (4, 15));
600 /* On the final line, before the end. */
601 ASSERT_TRUE (example_b.contains_point (5, 7));
603 /* On the final line, at the end. */
604 ASSERT_TRUE (example_b.contains_point (5, 8));
606 /* On the final line, after the end. */
607 ASSERT_FALSE (example_b.contains_point (5, 9));
609 /* After the line. */
610 ASSERT_FALSE (example_b.contains_point (6, 1));
612 /* Tests for layout_range::intersects_line_p. */
613 ASSERT_FALSE (example_b.intersects_line_p (2));
614 ASSERT_TRUE (example_b.intersects_line_p (3));
615 ASSERT_TRUE (example_b.intersects_line_p (4));
616 ASSERT_TRUE (example_b.intersects_line_p (5));
617 ASSERT_FALSE (example_b.intersects_line_p (6));
620 #endif /* #if CHECKING_P */
622 /* Given a source line LINE of length LINE_WIDTH, determine the width
623 without any trailing whitespace. */
625 static int
626 get_line_width_without_trailing_whitespace (const char *line, int line_width)
628 int result = line_width;
629 while (result > 0)
631 char ch = line[result - 1];
632 if (ch == ' ' || ch == '\t')
633 result--;
634 else
635 break;
637 gcc_assert (result >= 0);
638 gcc_assert (result <= line_width);
639 gcc_assert (result == 0 ||
640 (line[result - 1] != ' '
641 && line[result -1] != '\t'));
642 return result;
645 #if CHECKING_P
647 /* A helper function for testing get_line_width_without_trailing_whitespace. */
649 static void
650 assert_eq (const char *line, int expected_width)
652 int actual_value
653 = get_line_width_without_trailing_whitespace (line, strlen (line));
654 ASSERT_EQ (actual_value, expected_width);
657 /* Verify that get_line_width_without_trailing_whitespace is sane for
658 various inputs. It is not required to handle newlines. */
660 static void
661 test_get_line_width_without_trailing_whitespace ()
663 assert_eq ("", 0);
664 assert_eq (" ", 0);
665 assert_eq ("\t", 0);
666 assert_eq ("hello world", 11);
667 assert_eq ("hello world ", 11);
668 assert_eq ("hello world \t\t ", 11);
671 #endif /* #if CHECKING_P */
673 /* Helper function for layout's ctor, for sanitizing locations relative
674 to the primary location within a diagnostic.
676 Compare LOC_A and LOC_B to see if it makes sense to print underlines
677 connecting their expanded locations. Doing so is only guaranteed to
678 make sense if the locations share the same macro expansion "history"
679 i.e. they can be traced through the same macro expansions, eventually
680 reaching an ordinary map.
682 This may be too strong a condition, but it effectively sanitizes
683 PR c++/70105, which has an example of printing an expression where the
684 final location of the expression is in a different macro, which
685 erroneously was leading to hundreds of lines of irrelevant source
686 being printed. */
688 static bool
689 compatible_locations_p (location_t loc_a, location_t loc_b)
691 if (IS_ADHOC_LOC (loc_a))
692 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
693 if (IS_ADHOC_LOC (loc_b))
694 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
696 /* If either location is one of the special locations outside of a
697 linemap, they are only compatible if they are equal. */
698 if (loc_a < RESERVED_LOCATION_COUNT
699 || loc_b < RESERVED_LOCATION_COUNT)
700 return loc_a == loc_b;
702 const line_map *map_a = linemap_lookup (line_table, loc_a);
703 linemap_assert (map_a);
705 const line_map *map_b = linemap_lookup (line_table, loc_b);
706 linemap_assert (map_b);
708 /* Are they within the same map? */
709 if (map_a == map_b)
711 /* Are both within the same macro expansion? */
712 if (linemap_macro_expansion_map_p (map_a))
714 /* Expand each location towards the spelling location, and
715 recurse. */
716 const line_map_macro *macro_map = linemap_check_macro (map_a);
717 source_location loc_a_toward_spelling
718 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
719 macro_map,
720 loc_a);
721 source_location loc_b_toward_spelling
722 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
723 macro_map,
724 loc_b);
725 return compatible_locations_p (loc_a_toward_spelling,
726 loc_b_toward_spelling);
729 /* Otherwise they are within the same ordinary map. */
730 return true;
732 else
734 /* Within different maps. */
736 /* If either is within a macro expansion, they are incompatible. */
737 if (linemap_macro_expansion_map_p (map_a)
738 || linemap_macro_expansion_map_p (map_b))
739 return false;
741 /* Within two different ordinary maps; they are compatible iff they
742 are in the same file. */
743 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
744 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
745 return ord_map_a->to_file == ord_map_b->to_file;
749 /* Implementation of class layout. */
751 /* Constructor for class layout.
753 Filter the ranges from the rich_location to those that we can
754 sanely print, populating m_layout_ranges and m_fixit_hints.
755 Determine the range of lines that we will print, splitting them
756 up into an ordered list of disjoint spans of contiguous line numbers.
757 Determine m_x_offset, to ensure that the primary caret
758 will fit within the max_width provided by the diagnostic_context. */
760 layout::layout (diagnostic_context * context,
761 rich_location *richloc,
762 diagnostic_t diagnostic_kind)
763 : m_context (context),
764 m_pp (context->printer),
765 m_diagnostic_kind (diagnostic_kind),
766 m_exploc (richloc->get_expanded_location (0)),
767 m_colorizer (context, diagnostic_kind),
768 m_colorize_source_p (context->colorize_source_p),
769 m_layout_ranges (richloc->get_num_locations ()),
770 m_fixit_hints (richloc->get_num_fixit_hints ()),
771 m_line_spans (1 + richloc->get_num_locations ()),
772 m_x_offset (0)
774 source_location primary_loc = richloc->get_range (0)->m_loc;
776 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
778 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
779 Ignore any ranges that are awkward to handle. */
780 const location_range *loc_range = richloc->get_range (idx);
782 /* Split the "range" into caret and range information. */
783 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
785 /* Expand the various locations. */
786 expanded_location start
787 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
788 expanded_location finish
789 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
790 expanded_location caret
791 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
793 /* If any part of the range isn't in the same file as the primary
794 location of this diagnostic, ignore the range. */
795 if (start.file != m_exploc.file)
796 continue;
797 if (finish.file != m_exploc.file)
798 continue;
799 if (loc_range->m_show_caret_p)
800 if (caret.file != m_exploc.file)
801 continue;
803 /* Sanitize the caret location for non-primary ranges. */
804 if (m_layout_ranges.length () > 0)
805 if (loc_range->m_show_caret_p)
806 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
807 /* Discard any non-primary ranges that can't be printed
808 sanely relative to the primary location. */
809 continue;
811 /* Everything is now known to be in the correct source file,
812 but it may require further sanitization. */
813 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
815 /* If we have a range that finishes before it starts (perhaps
816 from something built via macro expansion), printing the
817 range is likely to be nonsensical. Also, attempting to do so
818 breaks assumptions within the printing code (PR c/68473).
819 Similarly, don't attempt to print ranges if one or both ends
820 of the range aren't sane to print relative to the
821 primary location (PR c++/70105). */
822 if (start.line > finish.line
823 || !compatible_locations_p (src_range.m_start, primary_loc)
824 || !compatible_locations_p (src_range.m_finish, primary_loc))
826 /* Is this the primary location? */
827 if (m_layout_ranges.length () == 0)
829 /* We want to print the caret for the primary location, but
830 we must sanitize away m_start and m_finish. */
831 ri.m_start = ri.m_caret;
832 ri.m_finish = ri.m_caret;
834 else
835 /* This is a non-primary range; ignore it. */
836 continue;
839 /* Passed all the tests; add the range to m_layout_ranges so that
840 it will be printed. */
841 m_layout_ranges.safe_push (ri);
844 /* Populate m_fixit_hints, filtering to only those that are in the
845 same file. */
846 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
848 const fixit_hint *hint = richloc->get_fixit_hint (i);
849 if (validate_fixit_hint_p (hint))
850 m_fixit_hints.safe_push (hint);
853 /* Populate m_line_spans. */
854 calculate_line_spans ();
856 /* Adjust m_x_offset.
857 Center the primary caret to fit in max_width; all columns
858 will be adjusted accordingly. */
859 int max_width = m_context->caret_max_width;
860 int line_width;
861 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
862 &line_width);
863 if (line && m_exploc.column <= line_width)
865 int right_margin = CARET_LINE_MARGIN;
866 int column = m_exploc.column;
867 right_margin = MIN (line_width - column, right_margin);
868 right_margin = max_width - right_margin;
869 if (line_width >= max_width && column > right_margin)
870 m_x_offset = column - right_margin;
871 gcc_assert (m_x_offset >= 0);
874 if (context->show_ruler_p)
875 show_ruler (m_x_offset + max_width);
878 /* Return true iff we should print a heading when starting the
879 line span with the given index. */
881 bool
882 layout::print_heading_for_line_span_index_p (int line_span_idx) const
884 /* We print a heading for every change of line span, hence for every
885 line span after the initial one. */
886 if (line_span_idx > 0)
887 return true;
889 /* We also do it for the initial span if the primary location of the
890 diagnostic is in a different span. */
891 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
892 return true;
894 return false;
897 /* Get an expanded_location for the first location of interest within
898 the given line_span.
899 Used when printing a heading to indicate a new line span. */
901 expanded_location
902 layout::get_expanded_location (const line_span *line_span) const
904 /* Whenever possible, use the caret location. */
905 if (line_span->contains_line_p (m_exploc.line))
906 return m_exploc;
908 /* Otherwise, use the start of the first range that's present
909 within the line_span. */
910 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
912 const layout_range *lr = &m_layout_ranges[i];
913 if (line_span->contains_line_p (lr->m_start.m_line))
915 expanded_location exploc = m_exploc;
916 exploc.line = lr->m_start.m_line;
917 exploc.column = lr->m_start.m_column;
918 return exploc;
922 /* Otherwise, use the location of the first fixit-hint present within
923 the line_span. */
924 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
926 const fixit_hint *hint = m_fixit_hints[i];
927 location_t loc = hint->get_start_loc ();
928 expanded_location exploc = expand_location (loc);
929 if (line_span->contains_line_p (exploc.line))
930 return exploc;
933 /* It should not be possible to have a line span that didn't
934 contain any of the layout_range or fixit_hint instances. */
935 gcc_unreachable ();
936 return m_exploc;
939 /* Determine if HINT is meaningful to print within this layout. */
941 bool
942 layout::validate_fixit_hint_p (const fixit_hint *hint)
944 switch (hint->get_kind ())
946 case fixit_hint::INSERT:
948 const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
949 location_t loc = insert->get_location ();
950 if (LOCATION_FILE (loc) != m_exploc.file)
951 return false;
953 break;
955 case fixit_hint::REPLACE:
957 const fixit_replace *replace
958 = static_cast <const fixit_replace *> (hint);
959 source_range src_range = replace->get_range ();
960 if (LOCATION_FILE (src_range.m_start) != m_exploc.file)
961 return false;
962 if (LOCATION_FILE (src_range.m_finish) != m_exploc.file)
963 return false;
965 break;
967 default:
968 gcc_unreachable ();
971 return true;
974 /* Determine the range of lines affected by HINT.
975 This assumes that HINT has already been filtered by
976 validate_fixit_hint_p, and so affects the correct source file. */
978 static line_span
979 get_line_span_for_fixit_hint (const fixit_hint *hint)
981 gcc_assert (hint);
982 switch (hint->get_kind ())
984 case fixit_hint::INSERT:
986 const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
987 location_t loc = insert->get_location ();
988 int line = LOCATION_LINE (loc);
989 return line_span (line, line);
991 break;
993 case fixit_hint::REPLACE:
995 const fixit_replace *replace
996 = static_cast <const fixit_replace *> (hint);
997 source_range src_range = replace->get_range ();
998 return line_span (LOCATION_LINE (src_range.m_start),
999 LOCATION_LINE (src_range.m_finish));
1001 break;
1003 default:
1004 gcc_unreachable ();
1008 /* We want to print the pertinent source code at a diagnostic. The
1009 rich_location can contain multiple locations. This will have been
1010 filtered into m_exploc (the caret for the primary location) and
1011 m_layout_ranges, for those ranges within the same source file.
1013 We will print a subset of the lines within the source file in question,
1014 as a collection of "spans" of lines.
1016 This function populates m_line_spans with an ordered, disjoint list of
1017 the line spans of interest.
1019 For example, if the primary caret location is on line 7, with ranges
1020 covering lines 5-6 and lines 9-12:
1023 005 |RANGE 0
1024 006 |RANGE 0
1025 007 |PRIMARY CARET
1027 009 |RANGE 1
1028 010 |RANGE 1
1029 011 |RANGE 1
1030 012 |RANGE 1
1033 then we want two spans: lines 5-7 and lines 9-12. */
1035 void
1036 layout::calculate_line_spans ()
1038 /* This should only be called once, by the ctor. */
1039 gcc_assert (m_line_spans.length () == 0);
1041 /* Populate tmp_spans with individual spans, for each of
1042 m_exploc, and for m_layout_ranges. */
1043 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1044 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1045 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1047 const layout_range *lr = &m_layout_ranges[i];
1048 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1049 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1050 lr->m_finish.m_line));
1053 /* Also add spans for any fix-it hints, in case they cover other lines. */
1054 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1056 const fixit_hint *hint = m_fixit_hints[i];
1057 gcc_assert (hint);
1058 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1061 /* Sort them. */
1062 tmp_spans.qsort(line_span::comparator);
1064 /* Now iterate through tmp_spans, copying into m_line_spans, and
1065 combining where possible. */
1066 gcc_assert (tmp_spans.length () > 0);
1067 m_line_spans.safe_push (tmp_spans[0]);
1068 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1070 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1071 const line_span *next = &tmp_spans[i];
1072 gcc_assert (next->m_first_line >= current->m_first_line);
1073 if (next->m_first_line <= current->m_last_line + 1)
1075 /* We can merge them. */
1076 if (next->m_last_line > current->m_last_line)
1077 current->m_last_line = next->m_last_line;
1079 else
1081 /* No merger possible. */
1082 m_line_spans.safe_push (*next);
1086 /* Verify the result, in m_line_spans. */
1087 gcc_assert (m_line_spans.length () > 0);
1088 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1090 const line_span *prev = &m_line_spans[i - 1];
1091 const line_span *next = &m_line_spans[i];
1092 /* The individual spans must be sane. */
1093 gcc_assert (prev->m_first_line <= prev->m_last_line);
1094 gcc_assert (next->m_first_line <= next->m_last_line);
1095 /* The spans must be ordered. */
1096 gcc_assert (prev->m_first_line < next->m_first_line);
1097 /* There must be a gap of at least one line between separate spans. */
1098 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1102 /* Attempt to print line ROW of source code, potentially colorized at any
1103 ranges.
1104 Return true if the line was printed, populating *LBOUNDS_OUT.
1105 Return false if the source line could not be read, leaving *LBOUNDS_OUT
1106 untouched. */
1108 bool
1109 layout::print_source_line (int row, line_bounds *lbounds_out)
1111 int line_width;
1112 const char *line = location_get_source_line (m_exploc.file, row,
1113 &line_width);
1114 if (!line)
1115 return false;
1117 m_colorizer.set_normal_text ();
1119 /* We will stop printing the source line at any trailing
1120 whitespace. */
1121 line_width = get_line_width_without_trailing_whitespace (line,
1122 line_width);
1123 line += m_x_offset;
1125 pp_space (m_pp);
1126 int first_non_ws = INT_MAX;
1127 int last_non_ws = 0;
1128 int column;
1129 for (column = 1 + m_x_offset; column <= line_width; column++)
1131 /* Assuming colorization is enabled for the caret and underline
1132 characters, we may also colorize the associated characters
1133 within the source line.
1135 For frontends that generate range information, we color the
1136 associated characters in the source line the same as the
1137 carets and underlines in the annotation line, to make it easier
1138 for the reader to see the pertinent code.
1140 For frontends that only generate carets, we don't colorize the
1141 characters above them, since this would look strange (e.g.
1142 colorizing just the first character in a token). */
1143 if (m_colorize_source_p)
1145 bool in_range_p;
1146 point_state state;
1147 in_range_p = get_state_at_point (row, column,
1148 0, INT_MAX,
1149 &state);
1150 if (in_range_p)
1151 m_colorizer.set_range (state.range_idx);
1152 else
1153 m_colorizer.set_normal_text ();
1155 char c = *line == '\t' ? ' ' : *line;
1156 if (c == '\0')
1157 c = ' ';
1158 if (c != ' ')
1160 last_non_ws = column;
1161 if (first_non_ws == INT_MAX)
1162 first_non_ws = column;
1164 pp_character (m_pp, c);
1165 line++;
1167 print_newline ();
1169 lbounds_out->m_first_non_ws = first_non_ws;
1170 lbounds_out->m_last_non_ws = last_non_ws;
1171 return true;
1174 /* Determine if we should print an annotation line for ROW.
1175 i.e. if any of m_layout_ranges contains ROW. */
1177 bool
1178 layout::should_print_annotation_line_p (int row) const
1180 layout_range *range;
1181 int i;
1182 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1183 if (range->intersects_line_p (row))
1184 return true;
1185 return false;
1188 /* Print a line consisting of the caret/underlines for the given
1189 source line. */
1191 void
1192 layout::print_annotation_line (int row, const line_bounds lbounds)
1194 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1195 lbounds.m_last_non_ws);
1197 pp_space (m_pp);
1198 for (int column = 1 + m_x_offset; column < x_bound; column++)
1200 bool in_range_p;
1201 point_state state;
1202 in_range_p = get_state_at_point (row, column,
1203 lbounds.m_first_non_ws,
1204 lbounds.m_last_non_ws,
1205 &state);
1206 if (in_range_p)
1208 /* Within a range. Draw either the caret or an underline. */
1209 m_colorizer.set_range (state.range_idx);
1210 if (state.draw_caret_p)
1212 /* Draw the caret. */
1213 char caret_char;
1214 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1215 caret_char = m_context->caret_chars[state.range_idx];
1216 else
1217 caret_char = '^';
1218 pp_character (m_pp, caret_char);
1220 else
1221 pp_character (m_pp, '~');
1223 else
1225 /* Not in a range. */
1226 m_colorizer.set_normal_text ();
1227 pp_character (m_pp, ' ');
1230 print_newline ();
1233 /* Subroutine of layout::print_any_fixits.
1235 Determine if the annotation line printed for LINE contained
1236 the exact range from START_COLUMN to FINISH_COLUMN. */
1238 bool
1239 layout::annotation_line_showed_range_p (int line, int start_column,
1240 int finish_column) const
1242 layout_range *range;
1243 int i;
1244 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1245 if (range->m_start.m_line == line
1246 && range->m_start.m_column == start_column
1247 && range->m_finish.m_line == line
1248 && range->m_finish.m_column == finish_column)
1249 return true;
1250 return false;
1253 /* If there are any fixit hints on source line ROW, print them.
1254 They are printed in order, attempting to combine them onto lines, but
1255 starting new lines if necessary. */
1257 void
1258 layout::print_any_fixits (int row)
1260 int column = 0;
1261 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1263 const fixit_hint *hint = m_fixit_hints[i];
1264 if (hint->affects_line_p (m_exploc.file, row))
1266 /* For now we assume each fixit hint can only touch one line. */
1267 switch (hint->get_kind ())
1269 case fixit_hint::INSERT:
1271 const fixit_insert *insert
1272 = static_cast <const fixit_insert *> (hint);
1273 /* This assumes the insertion just affects one line. */
1274 int start_column
1275 = LOCATION_COLUMN (insert->get_location ());
1276 move_to_column (&column, start_column);
1277 m_colorizer.set_fixit_insert ();
1278 pp_string (m_pp, insert->get_string ());
1279 m_colorizer.set_normal_text ();
1280 column += insert->get_length ();
1282 break;
1284 case fixit_hint::REPLACE:
1286 const fixit_replace *replace
1287 = static_cast <const fixit_replace *> (hint);
1288 source_range src_range = replace->get_range ();
1289 int line = LOCATION_LINE (src_range.m_start);
1290 int start_column = LOCATION_COLUMN (src_range.m_start);
1291 int finish_column = LOCATION_COLUMN (src_range.m_finish);
1293 /* If the range of the replacement wasn't printed in the
1294 annotation line, then print an extra underline to
1295 indicate exactly what is being replaced.
1296 Always show it for removals. */
1297 if (!annotation_line_showed_range_p (line, start_column,
1298 finish_column)
1299 || replace->get_length () == 0)
1301 move_to_column (&column, start_column);
1302 m_colorizer.set_fixit_delete ();
1303 for (; column <= finish_column; column++)
1304 pp_character (m_pp, '-');
1305 m_colorizer.set_normal_text ();
1307 /* Print the replacement text. REPLACE also covers
1308 removals, so only do this extra work (potentially starting
1309 a new line) if we have actual replacement text. */
1310 if (replace->get_length () > 0)
1312 move_to_column (&column, start_column);
1313 m_colorizer.set_fixit_insert ();
1314 pp_string (m_pp, replace->get_string ());
1315 m_colorizer.set_normal_text ();
1316 column += replace->get_length ();
1319 break;
1321 default:
1322 gcc_unreachable ();
1327 /* Add a trailing newline, if necessary. */
1328 move_to_column (&column, 0);
1331 /* Disable any colorization and emit a newline. */
1333 void
1334 layout::print_newline ()
1336 m_colorizer.set_normal_text ();
1337 pp_newline (m_pp);
1340 /* Return true if (ROW/COLUMN) is within a range of the layout.
1341 If it returns true, OUT_STATE is written to, with the
1342 range index, and whether we should draw the caret at
1343 (ROW/COLUMN) (as opposed to an underline). */
1345 bool
1346 layout::get_state_at_point (/* Inputs. */
1347 int row, int column,
1348 int first_non_ws, int last_non_ws,
1349 /* Outputs. */
1350 point_state *out_state)
1352 layout_range *range;
1353 int i;
1354 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1356 if (range->contains_point (row, column))
1358 out_state->range_idx = i;
1360 /* Are we at the range's caret? is it visible? */
1361 out_state->draw_caret_p = false;
1362 if (range->m_show_caret_p
1363 && row == range->m_caret.m_line
1364 && column == range->m_caret.m_column)
1365 out_state->draw_caret_p = true;
1367 /* Within a multiline range, don't display any underline
1368 in any leading or trailing whitespace on a line.
1369 We do display carets, however. */
1370 if (!out_state->draw_caret_p)
1371 if (column < first_non_ws || column > last_non_ws)
1372 return false;
1374 /* We are within a range. */
1375 return true;
1379 return false;
1382 /* Helper function for use by layout::print_line when printing the
1383 annotation line under the source line.
1384 Get the column beyond the rightmost one that could contain a caret or
1385 range marker, given that we stop rendering at trailing whitespace.
1386 ROW is the source line within the given file.
1387 CARET_COLUMN is the column of range 0's caret.
1388 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1389 character of source (as determined when printing the source line). */
1392 layout::get_x_bound_for_row (int row, int caret_column,
1393 int last_non_ws_column)
1395 int result = caret_column + 1;
1397 layout_range *range;
1398 int i;
1399 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1401 if (row >= range->m_start.m_line)
1403 if (range->m_finish.m_line == row)
1405 /* On the final line within a range; ensure that
1406 we render up to the end of the range. */
1407 if (result <= range->m_finish.m_column)
1408 result = range->m_finish.m_column + 1;
1410 else if (row < range->m_finish.m_line)
1412 /* Within a multiline range; ensure that we render up to the
1413 last non-whitespace column. */
1414 if (result <= last_non_ws_column)
1415 result = last_non_ws_column + 1;
1420 return result;
1423 /* Given *COLUMN as an x-coordinate, print spaces to position
1424 successive output at DEST_COLUMN, printing a newline if necessary,
1425 and updating *COLUMN. */
1427 void
1428 layout::move_to_column (int *column, int dest_column)
1430 /* Start a new line if we need to. */
1431 if (*column > dest_column)
1433 print_newline ();
1434 *column = 0;
1437 while (*column < dest_column)
1439 pp_space (m_pp);
1440 (*column)++;
1444 /* For debugging layout issues, render a ruler giving column numbers
1445 (after the 1-column indent). */
1447 void
1448 layout::show_ruler (int max_column) const
1450 /* Hundreds. */
1451 if (max_column > 99)
1453 pp_space (m_pp);
1454 for (int column = 1 + m_x_offset; column <= max_column; column++)
1455 if (0 == column % 10)
1456 pp_character (m_pp, '0' + (column / 100) % 10);
1457 else
1458 pp_space (m_pp);
1459 pp_newline (m_pp);
1462 /* Tens. */
1463 pp_space (m_pp);
1464 for (int column = 1 + m_x_offset; column <= max_column; column++)
1465 if (0 == column % 10)
1466 pp_character (m_pp, '0' + (column / 10) % 10);
1467 else
1468 pp_space (m_pp);
1469 pp_newline (m_pp);
1471 /* Units. */
1472 pp_space (m_pp);
1473 for (int column = 1 + m_x_offset; column <= max_column; column++)
1474 pp_character (m_pp, '0' + (column % 10));
1475 pp_newline (m_pp);
1478 } /* End of anonymous namespace. */
1480 /* Print the physical source code corresponding to the location of
1481 this diagnostic, with additional annotations. */
1483 void
1484 diagnostic_show_locus (diagnostic_context * context,
1485 rich_location *richloc,
1486 diagnostic_t diagnostic_kind)
1488 pp_newline (context->printer);
1490 location_t loc = richloc->get_loc ();
1491 /* Do nothing if source-printing has been disabled. */
1492 if (!context->show_caret)
1493 return;
1495 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1496 if (loc <= BUILTINS_LOCATION)
1497 return;
1499 /* Don't print the same source location twice in a row, unless we have
1500 fix-it hints. */
1501 if (loc == context->last_location
1502 && richloc->get_num_fixit_hints () == 0)
1503 return;
1505 context->last_location = loc;
1507 const char *saved_prefix = pp_get_prefix (context->printer);
1508 pp_set_prefix (context->printer, NULL);
1510 layout layout (context, richloc, diagnostic_kind);
1511 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1512 line_span_idx++)
1514 const line_span *line_span = layout.get_line_span (line_span_idx);
1515 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1517 expanded_location exploc = layout.get_expanded_location (line_span);
1518 context->start_span (context, exploc);
1520 int last_line = line_span->get_last_line ();
1521 for (int row = line_span->get_first_line (); row <= last_line; row++)
1523 /* Print the source line, followed by an annotation line
1524 consisting of any caret/underlines, then any fixits.
1525 If the source line can't be read, print nothing. */
1526 line_bounds lbounds;
1527 if (layout.print_source_line (row, &lbounds))
1529 if (layout.should_print_annotation_line_p (row))
1530 layout.print_annotation_line (row, lbounds);
1531 layout.print_any_fixits (row);
1536 pp_set_prefix (context->printer, saved_prefix);
1539 #if CHECKING_P
1541 namespace selftest {
1543 /* Selftests for diagnostic_show_locus. */
1545 /* Convenience subclass of diagnostic_context for testing
1546 diagnostic_show_locus. */
1548 class test_diagnostic_context : public diagnostic_context
1550 public:
1551 test_diagnostic_context ()
1553 diagnostic_initialize (this, 0);
1554 show_caret = true;
1555 show_column = true;
1556 start_span = start_span_cb;
1558 ~test_diagnostic_context ()
1560 diagnostic_finish (this);
1563 /* Implementation of diagnostic_start_span_fn, hiding the
1564 real filename (to avoid printing the names of tempfiles). */
1565 static void
1566 start_span_cb (diagnostic_context *context, expanded_location exploc)
1568 exploc.file = "FILENAME";
1569 default_diagnostic_start_span_fn (context, exploc);
1573 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1575 static void
1576 test_diagnostic_show_locus_unknown_location ()
1578 test_diagnostic_context dc;
1579 rich_location richloc (line_table, UNKNOWN_LOCATION);
1580 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1581 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1584 /* Verify that diagnostic_show_locus works sanely for various
1585 single-line cases.
1587 All of these work on the following 1-line source file:
1588 .0000000001111111
1589 .1234567890123456
1590 "foo = bar.field;\n"
1591 which is set up by test_diagnostic_show_locus_one_liner and calls
1592 them. */
1594 /* Just a caret. */
1596 static void
1597 test_one_liner_simple_caret ()
1599 test_diagnostic_context dc;
1600 location_t caret = linemap_position_for_column (line_table, 10);
1601 rich_location richloc (line_table, caret);
1602 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1603 ASSERT_STREQ ("\n"
1604 " foo = bar.field;\n"
1605 " ^\n",
1606 pp_formatted_text (dc.printer));
1609 /* Caret and range. */
1611 static void
1612 test_one_liner_caret_and_range ()
1614 test_diagnostic_context dc;
1615 location_t caret = linemap_position_for_column (line_table, 10);
1616 location_t start = linemap_position_for_column (line_table, 7);
1617 location_t finish = linemap_position_for_column (line_table, 15);
1618 location_t loc = make_location (caret, start, finish);
1619 rich_location richloc (line_table, loc);
1620 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1621 ASSERT_STREQ ("\n"
1622 " foo = bar.field;\n"
1623 " ~~~^~~~~~\n",
1624 pp_formatted_text (dc.printer));
1627 /* Multiple ranges and carets. */
1629 static void
1630 test_one_liner_multiple_carets_and_ranges ()
1632 test_diagnostic_context dc;
1633 location_t foo
1634 = make_location (linemap_position_for_column (line_table, 2),
1635 linemap_position_for_column (line_table, 1),
1636 linemap_position_for_column (line_table, 3));
1637 dc.caret_chars[0] = 'A';
1639 location_t bar
1640 = make_location (linemap_position_for_column (line_table, 8),
1641 linemap_position_for_column (line_table, 7),
1642 linemap_position_for_column (line_table, 9));
1643 dc.caret_chars[1] = 'B';
1645 location_t field
1646 = make_location (linemap_position_for_column (line_table, 13),
1647 linemap_position_for_column (line_table, 11),
1648 linemap_position_for_column (line_table, 15));
1649 dc.caret_chars[2] = 'C';
1651 rich_location richloc (line_table, foo);
1652 richloc.add_range (bar, true);
1653 richloc.add_range (field, true);
1654 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1655 ASSERT_STREQ ("\n"
1656 " foo = bar.field;\n"
1657 " ~A~ ~B~ ~~C~~\n",
1658 pp_formatted_text (dc.printer));
1661 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1663 static void
1664 test_one_liner_fixit_insert_before ()
1666 test_diagnostic_context dc;
1667 location_t caret = linemap_position_for_column (line_table, 7);
1668 rich_location richloc (line_table, caret);
1669 richloc.add_fixit_insert_before ("&");
1670 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1671 ASSERT_STREQ ("\n"
1672 " foo = bar.field;\n"
1673 " ^\n"
1674 " &\n",
1675 pp_formatted_text (dc.printer));
1678 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1680 static void
1681 test_one_liner_fixit_insert_after ()
1683 test_diagnostic_context dc;
1684 location_t start = linemap_position_for_column (line_table, 1);
1685 location_t finish = linemap_position_for_column (line_table, 3);
1686 location_t foo = make_location (start, start, finish);
1687 rich_location richloc (line_table, foo);
1688 richloc.add_fixit_insert_after ("[0]");
1689 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1690 ASSERT_STREQ ("\n"
1691 " foo = bar.field;\n"
1692 " ^~~\n"
1693 " [0]\n",
1694 pp_formatted_text (dc.printer));
1697 /* Removal fix-it hint: removal of the ".field". */
1699 static void
1700 test_one_liner_fixit_remove ()
1702 test_diagnostic_context dc;
1703 location_t start = linemap_position_for_column (line_table, 10);
1704 location_t finish = linemap_position_for_column (line_table, 15);
1705 location_t dot = make_location (start, start, finish);
1706 rich_location richloc (line_table, dot);
1707 richloc.add_fixit_remove ();
1708 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1709 ASSERT_STREQ ("\n"
1710 " foo = bar.field;\n"
1711 " ^~~~~~\n"
1712 " ------\n",
1713 pp_formatted_text (dc.printer));
1716 /* Replace fix-it hint: replacing "field" with "m_field". */
1718 static void
1719 test_one_liner_fixit_replace ()
1721 test_diagnostic_context dc;
1722 location_t start = linemap_position_for_column (line_table, 11);
1723 location_t finish = linemap_position_for_column (line_table, 15);
1724 location_t field = make_location (start, start, finish);
1725 rich_location richloc (line_table, field);
1726 richloc.add_fixit_replace ("m_field");
1727 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1728 ASSERT_STREQ ("\n"
1729 " foo = bar.field;\n"
1730 " ^~~~~\n"
1731 " m_field\n",
1732 pp_formatted_text (dc.printer));
1735 /* Replace fix-it hint: replacing "field" with "m_field",
1736 but where the caret was elsewhere. */
1738 static void
1739 test_one_liner_fixit_replace_non_equal_range ()
1741 test_diagnostic_context dc;
1742 location_t equals = linemap_position_for_column (line_table, 5);
1743 location_t start = linemap_position_for_column (line_table, 11);
1744 location_t finish = linemap_position_for_column (line_table, 15);
1745 rich_location richloc (line_table, equals);
1746 source_range range;
1747 range.m_start = start;
1748 range.m_finish = finish;
1749 richloc.add_fixit_replace (range, "m_field");
1750 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1751 /* The replacement range is not indicated in the annotation line, so
1752 it should be indicated via an additional underline. */
1753 ASSERT_STREQ ("\n"
1754 " foo = bar.field;\n"
1755 " ^\n"
1756 " -----\n"
1757 " m_field\n",
1758 pp_formatted_text (dc.printer));
1761 /* Replace fix-it hint: replacing "field" with "m_field",
1762 where the caret was elsewhere, but where a secondary range
1763 exactly covers "field". */
1765 static void
1766 test_one_liner_fixit_replace_equal_secondary_range ()
1768 test_diagnostic_context dc;
1769 location_t equals = linemap_position_for_column (line_table, 5);
1770 location_t start = linemap_position_for_column (line_table, 11);
1771 location_t finish = linemap_position_for_column (line_table, 15);
1772 rich_location richloc (line_table, equals);
1773 location_t field = make_location (start, start, finish);
1774 richloc.add_range (field, false);
1775 richloc.add_fixit_replace (field, "m_field");
1776 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1777 /* The replacement range is indicated in the annotation line,
1778 so it shouldn't be indicated via an additional underline. */
1779 ASSERT_STREQ ("\n"
1780 " foo = bar.field;\n"
1781 " ^ ~~~~~\n"
1782 " m_field\n",
1783 pp_formatted_text (dc.printer));
1786 /* Verify that we can use ad-hoc locations when adding fixits to a
1787 rich_location. */
1789 static void
1790 test_one_liner_fixit_validation_adhoc_locations ()
1792 /* Generate a range that's too long to be packed, so must
1793 be stored as an ad-hoc location (given the defaults
1794 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
1795 const location_t c7 = linemap_position_for_column (line_table, 7);
1796 const location_t c47 = linemap_position_for_column (line_table, 47);
1797 const location_t loc = make_location (c7, c7, c47);
1799 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1800 return;
1802 ASSERT_TRUE (IS_ADHOC_LOC (loc));
1804 /* Insert. */
1806 rich_location richloc (line_table, loc);
1807 richloc.add_fixit_insert_before (loc, "test");
1808 /* It should not have been discarded by the validator. */
1809 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1811 test_diagnostic_context dc;
1812 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1813 ASSERT_STREQ ("\n"
1814 " foo = bar.field;\n"
1815 " ^~~~~~~~~~ \n"
1816 " test\n",
1817 pp_formatted_text (dc.printer));
1820 /* Remove. */
1822 rich_location richloc (line_table, loc);
1823 source_range range = source_range::from_locations (loc, c47);
1824 richloc.add_fixit_remove (range);
1825 /* It should not have been discarded by the validator. */
1826 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1828 test_diagnostic_context dc;
1829 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1830 ASSERT_STREQ ("\n"
1831 " foo = bar.field;\n"
1832 " ^~~~~~~~~~ \n"
1833 " -----------------------------------------\n",
1834 pp_formatted_text (dc.printer));
1837 /* Replace. */
1839 rich_location richloc (line_table, loc);
1840 source_range range = source_range::from_locations (loc, c47);
1841 richloc.add_fixit_replace (range, "test");
1842 /* It should not have been discarded by the validator. */
1843 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1845 test_diagnostic_context dc;
1846 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1847 ASSERT_STREQ ("\n"
1848 " foo = bar.field;\n"
1849 " ^~~~~~~~~~ \n"
1850 " test\n",
1851 pp_formatted_text (dc.printer));
1855 /* Ensure that we can add an arbitrary number of fix-it hints to a
1856 rich_location. */
1858 static void
1859 test_one_liner_many_fixits ()
1861 test_diagnostic_context dc;
1862 location_t equals = linemap_position_for_column (line_table, 5);
1863 rich_location richloc (line_table, equals);
1864 for (int i = 0; i < 19; i++)
1865 richloc.add_fixit_insert_before ("a");
1866 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
1867 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1868 ASSERT_STREQ ("\n"
1869 " foo = bar.field;\n"
1870 " ^\n"
1871 " a\n"
1872 " a\n"
1873 " a\n"
1874 " a\n"
1875 " a\n"
1876 " a\n"
1877 " a\n"
1878 " a\n"
1879 " a\n"
1880 " a\n"
1881 " a\n"
1882 " a\n"
1883 " a\n"
1884 " a\n"
1885 " a\n"
1886 " a\n"
1887 " a\n"
1888 " a\n"
1889 " a\n",
1890 pp_formatted_text (dc.printer));
1893 /* Run the various one-liner tests. */
1895 static void
1896 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1898 /* Create a tempfile and write some text to it.
1899 ....................0000000001111111.
1900 ....................1234567890123456. */
1901 const char *content = "foo = bar.field;\n";
1902 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1903 line_table_test ltt (case_);
1905 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1907 location_t line_end = linemap_position_for_column (line_table, 16);
1909 /* Don't attempt to run the tests if column data might be unavailable. */
1910 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1911 return;
1913 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1914 ASSERT_EQ (1, LOCATION_LINE (line_end));
1915 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1917 test_one_liner_simple_caret ();
1918 test_one_liner_caret_and_range ();
1919 test_one_liner_multiple_carets_and_ranges ();
1920 test_one_liner_fixit_insert_before ();
1921 test_one_liner_fixit_insert_after ();
1922 test_one_liner_fixit_remove ();
1923 test_one_liner_fixit_replace ();
1924 test_one_liner_fixit_replace_non_equal_range ();
1925 test_one_liner_fixit_replace_equal_secondary_range ();
1926 test_one_liner_fixit_validation_adhoc_locations ();
1927 test_one_liner_many_fixits ();
1930 /* Verify that we print fixits even if they only affect lines
1931 outside those covered by the ranges in the rich_location. */
1933 static void
1934 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
1936 /* Create a tempfile and write some text to it.
1937 ...000000000111111111122222222223333333333.
1938 ...123456789012345678901234567890123456789. */
1939 const char *content
1940 = ("struct point { double x; double y; };\n" /* line 1. */
1941 "struct point origin = {x: 0.0,\n" /* line 2. */
1942 " y\n" /* line 3. */
1943 "\n" /* line 4. */
1944 "\n" /* line 5. */
1945 " : 0.0};\n"); /* line 6. */
1946 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1947 line_table_test ltt (case_);
1949 const line_map_ordinary *ord_map
1950 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
1951 tmp.get_filename (), 0));
1953 linemap_line_start (line_table, 1, 100);
1955 const location_t final_line_end
1956 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
1958 /* Don't attempt to run the tests if column data might be unavailable. */
1959 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1960 return;
1962 /* A pair of tests for modernizing the initializers to C99-style. */
1964 /* The one-liner case (line 2). */
1966 test_diagnostic_context dc;
1967 const location_t x
1968 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
1969 const location_t colon
1970 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
1971 rich_location richloc (line_table, colon);
1972 richloc.add_fixit_insert_before (x, ".");
1973 richloc.add_fixit_replace (colon, "=");
1974 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1975 ASSERT_STREQ ("\n"
1976 " struct point origin = {x: 0.0,\n"
1977 " ^\n"
1978 " .=\n",
1979 pp_formatted_text (dc.printer));
1982 /* The multiline case. The caret for the rich_location is on line 6;
1983 verify that insertion fixit on line 3 is still printed (and that
1984 span starts are printed due to the gap between the span at line 3
1985 and that at line 6). */
1987 test_diagnostic_context dc;
1988 const location_t y
1989 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
1990 const location_t colon
1991 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
1992 rich_location richloc (line_table, colon);
1993 richloc.add_fixit_insert_before (y, ".");
1994 richloc.add_fixit_replace (colon, "=");
1995 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1996 ASSERT_STREQ ("\n"
1997 "FILENAME:3:24:\n"
1998 " y\n"
1999 " .\n"
2000 "FILENAME:6:25:\n"
2001 " : 0.0};\n"
2002 " ^\n"
2003 " =\n",
2004 pp_formatted_text (dc.printer));
2009 /* Verify that fix-it hints are appropriately consolidated.
2011 If any fix-it hints in a rich_location involve locations beyond
2012 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2013 the fix-it as a whole, so there should be none.
2015 Otherwise, verify that consecutive "replace" and "remove" fix-its
2016 are merged, and that other fix-its remain separate. */
2018 static void
2019 test_fixit_consolidation (const line_table_case &case_)
2021 line_table_test ltt (case_);
2023 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2025 const location_t c10 = linemap_position_for_column (line_table, 10);
2026 const location_t c15 = linemap_position_for_column (line_table, 15);
2027 const location_t c16 = linemap_position_for_column (line_table, 16);
2028 const location_t c17 = linemap_position_for_column (line_table, 17);
2029 const location_t c20 = linemap_position_for_column (line_table, 20);
2030 const location_t caret = c10;
2032 /* Insert + insert. */
2034 rich_location richloc (line_table, caret);
2035 richloc.add_fixit_insert_before (c10, "foo");
2036 richloc.add_fixit_insert_before (c15, "bar");
2038 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2039 /* Bogus column info for 2nd fixit, so no fixits. */
2040 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2041 else
2042 /* They should not have been merged. */
2043 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2046 /* Insert + replace. */
2048 rich_location richloc (line_table, caret);
2049 richloc.add_fixit_insert_before (c10, "foo");
2050 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2051 "bar");
2053 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2054 /* Bogus column info for 2nd fixit, so no fixits. */
2055 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2056 else
2057 /* They should not have been merged. */
2058 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2061 /* Replace + non-consecutive insert. */
2063 rich_location richloc (line_table, caret);
2064 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2065 "bar");
2066 richloc.add_fixit_insert_before (c17, "foo");
2068 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2069 /* Bogus column info for 2nd fixit, so no fixits. */
2070 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2071 else
2072 /* They should not have been merged. */
2073 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2076 /* Replace + non-consecutive replace. */
2078 rich_location richloc (line_table, caret);
2079 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2080 "foo");
2081 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2082 "bar");
2084 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2085 /* Bogus column info for 2nd fixit, so no fixits. */
2086 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2087 else
2088 /* They should not have been merged. */
2089 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2092 /* Replace + consecutive replace. */
2094 rich_location richloc (line_table, caret);
2095 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2096 "foo");
2097 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2098 "bar");
2100 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2101 /* Bogus column info for 2nd fixit, so no fixits. */
2102 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2103 else
2105 /* They should have been merged into a single "replace". */
2106 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2107 const fixit_hint *hint = richloc.get_fixit_hint (0);
2108 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2109 const fixit_replace *replace = (const fixit_replace *)hint;
2110 ASSERT_STREQ ("foobar", replace->get_string ());
2111 ASSERT_EQ (c10, replace->get_range ().m_start);
2112 ASSERT_EQ (c20, replace->get_range ().m_finish);
2116 /* Replace + consecutive removal. */
2118 rich_location richloc (line_table, caret);
2119 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2120 "foo");
2121 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2123 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2124 /* Bogus column info for 2nd fixit, so no fixits. */
2125 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2126 else
2128 /* They should have been merged into a single replace, with the
2129 range extended to cover that of the removal. */
2130 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2131 const fixit_hint *hint = richloc.get_fixit_hint (0);
2132 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2133 const fixit_replace *replace = (const fixit_replace *)hint;
2134 ASSERT_STREQ ("foo", replace->get_string ());
2135 ASSERT_EQ (c10, replace->get_range ().m_start);
2136 ASSERT_EQ (c20, replace->get_range ().m_finish);
2140 /* Consecutive removals. */
2142 rich_location richloc (line_table, caret);
2143 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2144 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2146 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2147 /* Bogus column info for 2nd fixit, so no fixits. */
2148 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2149 else
2151 /* They should have been merged into a single "replace-with-empty". */
2152 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2153 const fixit_hint *hint = richloc.get_fixit_hint (0);
2154 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2155 const fixit_replace *replace = (const fixit_replace *)hint;
2156 ASSERT_STREQ ("", replace->get_string ());
2157 ASSERT_EQ (c10, replace->get_range ().m_start);
2158 ASSERT_EQ (c20, replace->get_range ().m_finish);
2163 /* Insertion fix-it hint: adding a "break;" on a line by itself.
2164 This will fail, as newlines aren't yet supported. */
2166 static void
2167 test_fixit_insert_containing_newline (const line_table_case &case_)
2169 /* Create a tempfile and write some text to it.
2170 .........................0000000001111111.
2171 .........................1234567890123456. */
2172 const char *old_content = (" case 'a':\n" /* line 1. */
2173 " x = a;\n" /* line 2. */
2174 " case 'b':\n" /* line 3. */
2175 " x = b;\n");/* line 4. */
2177 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2178 line_table_test ltt (case_);
2179 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2181 /* Add a "break;" on a line by itself before line 3 i.e. before
2182 column 1 of line 3. */
2183 location_t case_start = linemap_position_for_column (line_table, 5);
2184 location_t case_finish = linemap_position_for_column (line_table, 13);
2185 location_t case_loc = make_location (case_start, case_start, case_finish);
2186 rich_location richloc (line_table, case_loc);
2187 location_t line_start = linemap_position_for_column (line_table, 1);
2188 richloc.add_fixit_insert_before (line_start, " break;\n");
2190 /* Newlines are not yet supported within fix-it hints, so
2191 the fix-it should not be displayed. */
2192 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2194 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2195 return;
2197 test_diagnostic_context dc;
2198 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2199 ASSERT_STREQ ("\n"
2200 " case 'b':\n"
2201 " ^~~~~~~~~\n",
2202 pp_formatted_text (dc.printer));
2205 /* Replacement fix-it hint containing a newline.
2206 This will fail, as newlines aren't yet supported. */
2208 static void
2209 test_fixit_replace_containing_newline (const line_table_case &case_)
2211 /* Create a tempfile and write some text to it.
2212 .........................0000000001111.
2213 .........................1234567890123. */
2214 const char *old_content = "foo = bar ();\n";
2216 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2217 line_table_test ltt (case_);
2218 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2220 /* Replace the " = " with "\n = ", as if we were reformatting an
2221 overly long line. */
2222 location_t start = linemap_position_for_column (line_table, 4);
2223 location_t finish = linemap_position_for_column (line_table, 6);
2224 location_t loc = linemap_position_for_column (line_table, 13);
2225 rich_location richloc (line_table, loc);
2226 source_range range = source_range::from_locations (start, finish);
2227 richloc.add_fixit_replace (range, "\n =");
2229 /* Newlines are not yet supported within fix-it hints, so
2230 the fix-it should not be displayed. */
2231 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2233 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2234 return;
2236 test_diagnostic_context dc;
2237 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2238 ASSERT_STREQ ("\n"
2239 " foo = bar ();\n"
2240 " ^\n",
2241 pp_formatted_text (dc.printer));
2244 /* Run all of the selftests within this file. */
2246 void
2247 diagnostic_show_locus_c_tests ()
2249 test_layout_range_for_single_point ();
2250 test_layout_range_for_single_line ();
2251 test_layout_range_for_multiple_lines ();
2253 test_get_line_width_without_trailing_whitespace ();
2255 test_diagnostic_show_locus_unknown_location ();
2257 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
2258 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
2259 for_each_line_table_case (test_fixit_consolidation);
2260 for_each_line_table_case (test_fixit_insert_containing_newline);
2261 for_each_line_table_case (test_fixit_replace_containing_newline);
2264 } // namespace selftest
2266 #endif /* #if CHECKING_P */