2016-08-24 Michael Collison <michael.collison@linaro.org>
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob32b107833a8346b6eff9e0f4edb7e2aa8da5a2a5
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2016 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_hint () { set_state (0); }
84 private:
85 void set_state (int state);
86 void begin_state (int state);
87 void finish_state (int state);
89 private:
90 static const int STATE_NORMAL_TEXT = -1;
92 diagnostic_context *m_context;
93 diagnostic_t m_diagnostic_kind;
94 int m_current_state;
95 const char *m_caret_cs;
96 const char *m_caret_ce;
97 const char *m_range1_cs;
98 const char *m_range2_cs;
99 const char *m_range_ce;
102 /* A point within a layout_range; similar to an expanded_location,
103 but after filtering on file. */
105 class layout_point
107 public:
108 layout_point (const expanded_location &exploc)
109 : m_line (exploc.line),
110 m_column (exploc.column) {}
112 int m_line;
113 int m_column;
116 /* A class for use by "class layout" below: a filtered location_range. */
118 class layout_range
120 public:
121 layout_range (const expanded_location *start_exploc,
122 const expanded_location *finish_exploc,
123 bool show_caret_p,
124 const expanded_location *caret_exploc);
126 bool contains_point (int row, int column) const;
128 layout_point m_start;
129 layout_point m_finish;
130 bool m_show_caret_p;
131 layout_point m_caret;
134 /* A struct for use by layout::print_source_line for telling
135 layout::print_annotation_line the extents of the source line that
136 it printed, so that underlines can be clipped appropriately. */
138 struct line_bounds
140 int m_first_non_ws;
141 int m_last_non_ws;
144 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
145 or "line 23"). During the layout ctor, layout::calculate_line_spans
146 splits the pertinent source lines into a list of disjoint line_span
147 instances (e.g. lines 5-10, lines 15-20, line 23). */
149 struct line_span
151 line_span (linenum_type first_line, linenum_type last_line)
152 : m_first_line (first_line), m_last_line (last_line)
154 gcc_assert (first_line <= last_line);
156 linenum_type get_first_line () const { return m_first_line; }
157 linenum_type get_last_line () const { return m_last_line; }
159 bool contains_line_p (linenum_type line) const
161 return line >= m_first_line && line <= m_last_line;
164 static int comparator (const void *p1, const void *p2)
166 const line_span *ls1 = (const line_span *)p1;
167 const line_span *ls2 = (const line_span *)p2;
168 int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
169 if (first_line_diff)
170 return first_line_diff;
171 return (int)ls1->m_last_line - (int)ls2->m_last_line;
174 linenum_type m_first_line;
175 linenum_type m_last_line;
178 /* A class to control the overall layout when printing a diagnostic.
180 The layout is determined within the constructor.
181 It is then printed by repeatedly calling the "print_source_line",
182 "print_annotation_line" and "print_any_fixits" methods.
184 We assume we have disjoint ranges. */
186 class layout
188 public:
189 layout (diagnostic_context *context,
190 rich_location *richloc,
191 diagnostic_t diagnostic_kind);
193 int get_num_line_spans () const { return m_line_spans.length (); }
194 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
196 bool print_heading_for_line_span_index_p (int line_span_idx) const;
198 expanded_location get_expanded_location (const line_span *) const;
200 bool print_source_line (int row, line_bounds *lbounds_out);
201 void print_annotation_line (int row, const line_bounds lbounds);
202 bool annotation_line_showed_range_p (int line, int start_column,
203 int finish_column) const;
204 void print_any_fixits (int row, const rich_location *richloc);
206 void show_ruler (int max_column) const;
208 private:
209 void calculate_line_spans ();
211 void print_newline ();
213 bool
214 get_state_at_point (/* Inputs. */
215 int row, int column,
216 int first_non_ws, int last_non_ws,
217 /* Outputs. */
218 point_state *out_state);
221 get_x_bound_for_row (int row, int caret_column,
222 int last_non_ws);
224 void
225 move_to_column (int *column, int dest_column);
227 private:
228 diagnostic_context *m_context;
229 pretty_printer *m_pp;
230 diagnostic_t m_diagnostic_kind;
231 expanded_location m_exploc;
232 colorizer m_colorizer;
233 bool m_colorize_source_p;
234 auto_vec <layout_range> m_layout_ranges;
235 auto_vec <line_span> m_line_spans;
236 int m_x_offset;
239 /* Implementation of "class colorizer". */
241 /* The constructor for "colorizer". Lookup and store color codes for the
242 different kinds of things we might need to print. */
244 colorizer::colorizer (diagnostic_context *context,
245 diagnostic_t diagnostic_kind) :
246 m_context (context),
247 m_diagnostic_kind (diagnostic_kind),
248 m_current_state (STATE_NORMAL_TEXT)
250 m_caret_ce = colorize_stop (pp_show_color (context->printer));
251 m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
252 m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
253 m_range_ce = colorize_stop (pp_show_color (context->printer));
256 /* The destructor for "colorize". If colorization is on, print a code to
257 turn it off. */
259 colorizer::~colorizer ()
261 finish_state (m_current_state);
264 /* Update state, printing color codes if necessary if there's a state
265 change. */
267 void
268 colorizer::set_state (int new_state)
270 if (m_current_state != new_state)
272 finish_state (m_current_state);
273 m_current_state = new_state;
274 begin_state (new_state);
278 /* Turn on any colorization for STATE. */
280 void
281 colorizer::begin_state (int state)
283 switch (state)
285 case STATE_NORMAL_TEXT:
286 break;
288 case 0:
289 /* Make range 0 be the same color as the "kind" text
290 (error vs warning vs note). */
291 pp_string
292 (m_context->printer,
293 colorize_start (pp_show_color (m_context->printer),
294 diagnostic_get_color_for_kind (m_diagnostic_kind)));
295 break;
297 case 1:
298 pp_string (m_context->printer, m_range1_cs);
299 break;
301 case 2:
302 pp_string (m_context->printer, m_range2_cs);
303 break;
305 default:
306 /* We don't expect more than 3 ranges per diagnostic. */
307 gcc_unreachable ();
308 break;
312 /* Turn off any colorization for STATE. */
314 void
315 colorizer::finish_state (int state)
317 switch (state)
319 case STATE_NORMAL_TEXT:
320 break;
322 case 0:
323 pp_string (m_context->printer, m_caret_ce);
324 break;
326 default:
327 /* Within a range. */
328 gcc_assert (state > 0);
329 pp_string (m_context->printer, m_range_ce);
330 break;
334 /* Implementation of class layout_range. */
336 /* The constructor for class layout_range.
337 Initialize various layout_point fields from expanded_location
338 equivalents; we've already filtered on file. */
340 layout_range::layout_range (const expanded_location *start_exploc,
341 const expanded_location *finish_exploc,
342 bool show_caret_p,
343 const expanded_location *caret_exploc)
344 : m_start (*start_exploc),
345 m_finish (*finish_exploc),
346 m_show_caret_p (show_caret_p),
347 m_caret (*caret_exploc)
351 /* Is (column, row) within the given range?
352 We've already filtered on the file.
354 Ranges are closed (both limits are within the range).
356 Example A: a single-line range:
357 start: (col=22, line=2)
358 finish: (col=38, line=2)
360 |00000011111111112222222222333333333344444444444
361 |34567890123456789012345678901234567890123456789
362 --+-----------------------------------------------
363 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
364 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
365 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
367 Example B: a multiline range with
368 start: (col=14, line=3)
369 finish: (col=08, line=5)
371 |00000011111111112222222222333333333344444444444
372 |34567890123456789012345678901234567890123456789
373 --+-----------------------------------------------
374 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
375 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
376 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
377 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
378 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
379 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
380 --+-----------------------------------------------
382 Legend:
383 - 'b' indicates a point *before* the range
384 - 'S' indicates the start of the range
385 - 'w' indicates a point within the range
386 - 'F' indicates the finish of the range (which is
387 within it).
388 - 'a' indicates a subsequent point *after* the range. */
390 bool
391 layout_range::contains_point (int row, int column) const
393 gcc_assert (m_start.m_line <= m_finish.m_line);
394 /* ...but the equivalent isn't true for the columns;
395 consider example B in the comment above. */
397 if (row < m_start.m_line)
398 /* Points before the first line of the range are
399 outside it (corresponding to line 01 in example A
400 and lines 01 and 02 in example B above). */
401 return false;
403 if (row == m_start.m_line)
404 /* On same line as start of range (corresponding
405 to line 02 in example A and line 03 in example B). */
407 if (column < m_start.m_column)
408 /* Points on the starting line of the range, but
409 before the column in which it begins. */
410 return false;
412 if (row < m_finish.m_line)
413 /* This is a multiline range; the point
414 is within it (corresponds to line 03 in example B
415 from column 14 onwards) */
416 return true;
417 else
419 /* This is a single-line range. */
420 gcc_assert (row == m_finish.m_line);
421 return column <= m_finish.m_column;
425 /* The point is in a line beyond that containing the
426 start of the range: lines 03 onwards in example A,
427 and lines 04 onwards in example B. */
428 gcc_assert (row > m_start.m_line);
430 if (row > m_finish.m_line)
431 /* The point is beyond the final line of the range
432 (lines 03 onwards in example A, and lines 06 onwards
433 in example B). */
434 return false;
436 if (row < m_finish.m_line)
438 /* The point is in a line that's fully within a multiline
439 range (e.g. line 04 in example B). */
440 gcc_assert (m_start.m_line < m_finish.m_line);
441 return true;
444 gcc_assert (row == m_finish.m_line);
446 return column <= m_finish.m_column;
449 #if CHECKING_P
451 /* A helper function for testing layout_range::contains_point. */
453 static layout_range
454 make_range (int start_line, int start_col, int end_line, int end_col)
456 const expanded_location start_exploc
457 = {"test.c", start_line, start_col, NULL, false};
458 const expanded_location finish_exploc
459 = {"test.c", end_line, end_col, NULL, false};
460 return layout_range (&start_exploc, &finish_exploc, false,
461 &start_exploc);
464 /* Selftests for layout_range::contains_point. */
466 /* Selftest for layout_range::contains_point where the layout_range
467 is a range with start==end i.e. a single point. */
469 static void
470 test_range_contains_point_for_single_point ()
472 layout_range point = make_range (7, 10, 7, 10);
474 /* Before the line. */
475 ASSERT_FALSE (point.contains_point (6, 1));
477 /* On the line, but before start. */
478 ASSERT_FALSE (point.contains_point (7, 9));
480 /* At the point. */
481 ASSERT_TRUE (point.contains_point (7, 10));
483 /* On the line, after the point. */
484 ASSERT_FALSE (point.contains_point (7, 11));
486 /* After the line. */
487 ASSERT_FALSE (point.contains_point (8, 1));
490 /* Selftest for layout_range::contains_point where the layout_range
491 is the single-line range shown as "Example A" above. */
493 static void
494 test_range_contains_point_for_single_line ()
496 layout_range example_a = make_range (2, 22, 2, 38);
498 /* Before the line. */
499 ASSERT_FALSE (example_a.contains_point (1, 1));
501 /* On the line, but before start. */
502 ASSERT_FALSE (example_a.contains_point (2, 21));
504 /* On the line, at the start. */
505 ASSERT_TRUE (example_a.contains_point (2, 22));
507 /* On the line, within the range. */
508 ASSERT_TRUE (example_a.contains_point (2, 23));
510 /* On the line, at the end. */
511 ASSERT_TRUE (example_a.contains_point (2, 38));
513 /* On the line, after the end. */
514 ASSERT_FALSE (example_a.contains_point (2, 39));
516 /* After the line. */
517 ASSERT_FALSE (example_a.contains_point (2, 39));
520 /* Selftest for layout_range::contains_point where the layout_range
521 is the multi-line range shown as "Example B" above. */
523 static void
524 test_range_contains_point_for_multiple_lines ()
526 layout_range example_b = make_range (3, 14, 5, 8);
528 /* Before first line. */
529 ASSERT_FALSE (example_b.contains_point (1, 1));
531 /* On the first line, but before start. */
532 ASSERT_FALSE (example_b.contains_point (3, 13));
534 /* At the start. */
535 ASSERT_TRUE (example_b.contains_point (3, 14));
537 /* On the first line, within the range. */
538 ASSERT_TRUE (example_b.contains_point (3, 15));
540 /* On an interior line.
541 The column number should not matter; try various boundary
542 values. */
543 ASSERT_TRUE (example_b.contains_point (4, 1));
544 ASSERT_TRUE (example_b.contains_point (4, 7));
545 ASSERT_TRUE (example_b.contains_point (4, 8));
546 ASSERT_TRUE (example_b.contains_point (4, 9));
547 ASSERT_TRUE (example_b.contains_point (4, 13));
548 ASSERT_TRUE (example_b.contains_point (4, 14));
549 ASSERT_TRUE (example_b.contains_point (4, 15));
551 /* On the final line, before the end. */
552 ASSERT_TRUE (example_b.contains_point (5, 7));
554 /* On the final line, at the end. */
555 ASSERT_TRUE (example_b.contains_point (5, 8));
557 /* On the final line, after the end. */
558 ASSERT_FALSE (example_b.contains_point (5, 9));
560 /* After the line. */
561 ASSERT_FALSE (example_b.contains_point (6, 1));
564 #endif /* #if CHECKING_P */
566 /* Given a source line LINE of length LINE_WIDTH, determine the width
567 without any trailing whitespace. */
569 static int
570 get_line_width_without_trailing_whitespace (const char *line, int line_width)
572 int result = line_width;
573 while (result > 0)
575 char ch = line[result - 1];
576 if (ch == ' ' || ch == '\t')
577 result--;
578 else
579 break;
581 gcc_assert (result >= 0);
582 gcc_assert (result <= line_width);
583 gcc_assert (result == 0 ||
584 (line[result - 1] != ' '
585 && line[result -1] != '\t'));
586 return result;
589 #if CHECKING_P
591 /* A helper function for testing get_line_width_without_trailing_whitespace. */
593 static void
594 assert_eq (const char *line, int expected_width)
596 int actual_value
597 = get_line_width_without_trailing_whitespace (line, strlen (line));
598 ASSERT_EQ (actual_value, expected_width);
601 /* Verify that get_line_width_without_trailing_whitespace is sane for
602 various inputs. It is not required to handle newlines. */
604 static void
605 test_get_line_width_without_trailing_whitespace ()
607 assert_eq ("", 0);
608 assert_eq (" ", 0);
609 assert_eq ("\t", 0);
610 assert_eq ("hello world", 11);
611 assert_eq ("hello world ", 11);
612 assert_eq ("hello world \t\t ", 11);
615 #endif /* #if CHECKING_P */
617 /* Helper function for layout's ctor, for sanitizing locations relative
618 to the primary location within a diagnostic.
620 Compare LOC_A and LOC_B to see if it makes sense to print underlines
621 connecting their expanded locations. Doing so is only guaranteed to
622 make sense if the locations share the same macro expansion "history"
623 i.e. they can be traced through the same macro expansions, eventually
624 reaching an ordinary map.
626 This may be too strong a condition, but it effectively sanitizes
627 PR c++/70105, which has an example of printing an expression where the
628 final location of the expression is in a different macro, which
629 erroneously was leading to hundreds of lines of irrelevant source
630 being printed. */
632 static bool
633 compatible_locations_p (location_t loc_a, location_t loc_b)
635 if (IS_ADHOC_LOC (loc_a))
636 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
637 if (IS_ADHOC_LOC (loc_b))
638 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
640 /* If either location is one of the special locations outside of a
641 linemap, they are only compatible if they are equal. */
642 if (loc_a < RESERVED_LOCATION_COUNT
643 || loc_b < RESERVED_LOCATION_COUNT)
644 return loc_a == loc_b;
646 const line_map *map_a = linemap_lookup (line_table, loc_a);
647 linemap_assert (map_a);
649 const line_map *map_b = linemap_lookup (line_table, loc_b);
650 linemap_assert (map_b);
652 /* Are they within the same map? */
653 if (map_a == map_b)
655 /* Are both within the same macro expansion? */
656 if (linemap_macro_expansion_map_p (map_a))
658 /* Expand each location towards the spelling location, and
659 recurse. */
660 const line_map_macro *macro_map = linemap_check_macro (map_a);
661 source_location loc_a_toward_spelling
662 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
663 macro_map,
664 loc_a);
665 source_location loc_b_toward_spelling
666 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
667 macro_map,
668 loc_b);
669 return compatible_locations_p (loc_a_toward_spelling,
670 loc_b_toward_spelling);
673 /* Otherwise they are within the same ordinary map. */
674 return true;
676 else
678 /* Within different maps. */
680 /* If either is within a macro expansion, they are incompatible. */
681 if (linemap_macro_expansion_map_p (map_a)
682 || linemap_macro_expansion_map_p (map_b))
683 return false;
685 /* Within two different ordinary maps; they are compatible iff they
686 are in the same file. */
687 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
688 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
689 return ord_map_a->to_file == ord_map_b->to_file;
693 /* Implementation of class layout. */
695 /* Constructor for class layout.
697 Filter the ranges from the rich_location to those that we can
698 sanely print, populating m_layout_ranges.
699 Determine the range of lines that we will print, splitting them
700 up into an ordered list of disjoint spans of contiguous line numbers.
701 Determine m_x_offset, to ensure that the primary caret
702 will fit within the max_width provided by the diagnostic_context. */
704 layout::layout (diagnostic_context * context,
705 rich_location *richloc,
706 diagnostic_t diagnostic_kind)
707 : m_context (context),
708 m_pp (context->printer),
709 m_diagnostic_kind (diagnostic_kind),
710 m_exploc (richloc->get_expanded_location (0)),
711 m_colorizer (context, diagnostic_kind),
712 m_colorize_source_p (context->colorize_source_p),
713 m_layout_ranges (rich_location::MAX_RANGES),
714 m_line_spans (1 + rich_location::MAX_RANGES),
715 m_x_offset (0)
717 source_location primary_loc = richloc->get_range (0)->m_loc;
719 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
721 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
722 Ignore any ranges that are awkward to handle. */
723 const location_range *loc_range = richloc->get_range (idx);
725 /* Split the "range" into caret and range information. */
726 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
728 /* Expand the various locations. */
729 expanded_location start
730 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
731 expanded_location finish
732 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
733 expanded_location caret
734 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
736 /* If any part of the range isn't in the same file as the primary
737 location of this diagnostic, ignore the range. */
738 if (start.file != m_exploc.file)
739 continue;
740 if (finish.file != m_exploc.file)
741 continue;
742 if (loc_range->m_show_caret_p)
743 if (caret.file != m_exploc.file)
744 continue;
746 /* Sanitize the caret location for non-primary ranges. */
747 if (m_layout_ranges.length () > 0)
748 if (loc_range->m_show_caret_p)
749 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
750 /* Discard any non-primary ranges that can't be printed
751 sanely relative to the primary location. */
752 continue;
754 /* Everything is now known to be in the correct source file,
755 but it may require further sanitization. */
756 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
758 /* If we have a range that finishes before it starts (perhaps
759 from something built via macro expansion), printing the
760 range is likely to be nonsensical. Also, attempting to do so
761 breaks assumptions within the printing code (PR c/68473).
762 Similarly, don't attempt to print ranges if one or both ends
763 of the range aren't sane to print relative to the
764 primary location (PR c++/70105). */
765 if (start.line > finish.line
766 || !compatible_locations_p (src_range.m_start, primary_loc)
767 || !compatible_locations_p (src_range.m_finish, primary_loc))
769 /* Is this the primary location? */
770 if (m_layout_ranges.length () == 0)
772 /* We want to print the caret for the primary location, but
773 we must sanitize away m_start and m_finish. */
774 ri.m_start = ri.m_caret;
775 ri.m_finish = ri.m_caret;
777 else
778 /* This is a non-primary range; ignore it. */
779 continue;
782 /* Passed all the tests; add the range to m_layout_ranges so that
783 it will be printed. */
784 m_layout_ranges.safe_push (ri);
787 /* Populate m_line_spans. */
788 calculate_line_spans ();
790 /* Adjust m_x_offset.
791 Center the primary caret to fit in max_width; all columns
792 will be adjusted accordingly. */
793 int max_width = m_context->caret_max_width;
794 int line_width;
795 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
796 &line_width);
797 if (line && m_exploc.column <= line_width)
799 int right_margin = CARET_LINE_MARGIN;
800 int column = m_exploc.column;
801 right_margin = MIN (line_width - column, right_margin);
802 right_margin = max_width - right_margin;
803 if (line_width >= max_width && column > right_margin)
804 m_x_offset = column - right_margin;
805 gcc_assert (m_x_offset >= 0);
808 if (context->show_ruler_p)
809 show_ruler (m_x_offset + max_width);
812 /* Return true iff we should print a heading when starting the
813 line span with the given index. */
815 bool
816 layout::print_heading_for_line_span_index_p (int line_span_idx) const
818 /* We print a heading for every change of line span, hence for every
819 line span after the initial one. */
820 if (line_span_idx > 0)
821 return true;
823 /* We also do it for the initial span if the primary location of the
824 diagnostic is in a different span. */
825 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
826 return true;
828 return false;
831 /* Get an expanded_location for the first location of interest within
832 the given line_span.
833 Used when printing a heading to indicate a new line span. */
835 expanded_location
836 layout::get_expanded_location (const line_span *line_span) const
838 /* Whenever possible, use the caret location. */
839 if (line_span->contains_line_p (m_exploc.line))
840 return m_exploc;
842 /* Otherwise, use the start of the first range that's present
843 within the line_span. */
844 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
846 const layout_range *lr = &m_layout_ranges[i];
847 if (line_span->contains_line_p (lr->m_start.m_line))
849 expanded_location exploc = m_exploc;
850 exploc.line = lr->m_start.m_line;
851 exploc.column = lr->m_start.m_column;
852 return exploc;
856 /* It should not be possible to have a line span that didn't
857 contain any of the layout_range instances. */
858 gcc_unreachable ();
859 return m_exploc;
862 /* We want to print the pertinent source code at a diagnostic. The
863 rich_location can contain multiple locations. This will have been
864 filtered into m_exploc (the caret for the primary location) and
865 m_layout_ranges, for those ranges within the same source file.
867 We will print a subset of the lines within the source file in question,
868 as a collection of "spans" of lines.
870 This function populates m_line_spans with an ordered, disjoint list of
871 the line spans of interest.
873 For example, if the primary caret location is on line 7, with ranges
874 covering lines 5-6 and lines 9-12:
877 005 |RANGE 0
878 006 |RANGE 0
879 007 |PRIMARY CARET
881 009 |RANGE 1
882 010 |RANGE 1
883 011 |RANGE 1
884 012 |RANGE 1
887 then we want two spans: lines 5-7 and lines 9-12. */
889 void
890 layout::calculate_line_spans ()
892 /* This should only be called once, by the ctor. */
893 gcc_assert (m_line_spans.length () == 0);
895 /* Populate tmp_spans with individual spans, for each of
896 m_exploc, and for m_layout_ranges. */
897 auto_vec<line_span> tmp_spans (1 + rich_location::MAX_RANGES);
898 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
899 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
901 const layout_range *lr = &m_layout_ranges[i];
902 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
903 tmp_spans.safe_push (line_span (lr->m_start.m_line,
904 lr->m_finish.m_line));
907 /* Sort them. */
908 tmp_spans.qsort(line_span::comparator);
910 /* Now iterate through tmp_spans, copying into m_line_spans, and
911 combining where possible. */
912 gcc_assert (tmp_spans.length () > 0);
913 m_line_spans.safe_push (tmp_spans[0]);
914 for (unsigned int i = 1; i < tmp_spans.length (); i++)
916 line_span *current = &m_line_spans[m_line_spans.length () - 1];
917 const line_span *next = &tmp_spans[i];
918 gcc_assert (next->m_first_line >= current->m_first_line);
919 if (next->m_first_line <= current->m_last_line + 1)
921 /* We can merge them. */
922 if (next->m_last_line > current->m_last_line)
923 current->m_last_line = next->m_last_line;
925 else
927 /* No merger possible. */
928 m_line_spans.safe_push (*next);
932 /* Verify the result, in m_line_spans. */
933 gcc_assert (m_line_spans.length () > 0);
934 for (unsigned int i = 1; i < m_line_spans.length (); i++)
936 const line_span *prev = &m_line_spans[i - 1];
937 const line_span *next = &m_line_spans[i];
938 /* The individual spans must be sane. */
939 gcc_assert (prev->m_first_line <= prev->m_last_line);
940 gcc_assert (next->m_first_line <= next->m_last_line);
941 /* The spans must be ordered. */
942 gcc_assert (prev->m_first_line < next->m_first_line);
943 /* There must be a gap of at least one line between separate spans. */
944 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
948 /* Attempt to print line ROW of source code, potentially colorized at any
949 ranges.
950 Return true if the line was printed, populating *LBOUNDS_OUT.
951 Return false if the source line could not be read, leaving *LBOUNDS_OUT
952 untouched. */
954 bool
955 layout::print_source_line (int row, line_bounds *lbounds_out)
957 int line_width;
958 const char *line = location_get_source_line (m_exploc.file, row,
959 &line_width);
960 if (!line)
961 return false;
963 m_colorizer.set_normal_text ();
965 /* We will stop printing the source line at any trailing
966 whitespace. */
967 line_width = get_line_width_without_trailing_whitespace (line,
968 line_width);
969 line += m_x_offset;
971 pp_space (m_pp);
972 int first_non_ws = INT_MAX;
973 int last_non_ws = 0;
974 int column;
975 for (column = 1 + m_x_offset; column <= line_width; column++)
977 /* Assuming colorization is enabled for the caret and underline
978 characters, we may also colorize the associated characters
979 within the source line.
981 For frontends that generate range information, we color the
982 associated characters in the source line the same as the
983 carets and underlines in the annotation line, to make it easier
984 for the reader to see the pertinent code.
986 For frontends that only generate carets, we don't colorize the
987 characters above them, since this would look strange (e.g.
988 colorizing just the first character in a token). */
989 if (m_colorize_source_p)
991 bool in_range_p;
992 point_state state;
993 in_range_p = get_state_at_point (row, column,
994 0, INT_MAX,
995 &state);
996 if (in_range_p)
997 m_colorizer.set_range (state.range_idx);
998 else
999 m_colorizer.set_normal_text ();
1001 char c = *line == '\t' ? ' ' : *line;
1002 if (c == '\0')
1003 c = ' ';
1004 if (c != ' ')
1006 last_non_ws = column;
1007 if (first_non_ws == INT_MAX)
1008 first_non_ws = column;
1010 pp_character (m_pp, c);
1011 line++;
1013 print_newline ();
1015 lbounds_out->m_first_non_ws = first_non_ws;
1016 lbounds_out->m_last_non_ws = last_non_ws;
1017 return true;
1020 /* Print a line consisting of the caret/underlines for the given
1021 source line. */
1023 void
1024 layout::print_annotation_line (int row, const line_bounds lbounds)
1026 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1027 lbounds.m_last_non_ws);
1029 pp_space (m_pp);
1030 for (int column = 1 + m_x_offset; column < x_bound; column++)
1032 bool in_range_p;
1033 point_state state;
1034 in_range_p = get_state_at_point (row, column,
1035 lbounds.m_first_non_ws,
1036 lbounds.m_last_non_ws,
1037 &state);
1038 if (in_range_p)
1040 /* Within a range. Draw either the caret or an underline. */
1041 m_colorizer.set_range (state.range_idx);
1042 if (state.draw_caret_p)
1043 /* Draw the caret. */
1044 pp_character (m_pp, m_context->caret_chars[state.range_idx]);
1045 else
1046 pp_character (m_pp, '~');
1048 else
1050 /* Not in a range. */
1051 m_colorizer.set_normal_text ();
1052 pp_character (m_pp, ' ');
1055 print_newline ();
1058 /* Subroutine of layout::print_any_fixits.
1060 Determine if the annotation line printed for LINE contained
1061 the exact range from START_COLUMN to FINISH_COLUMN. */
1063 bool
1064 layout::annotation_line_showed_range_p (int line, int start_column,
1065 int finish_column) const
1067 layout_range *range;
1068 int i;
1069 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1070 if (range->m_start.m_line == line
1071 && range->m_start.m_column == start_column
1072 && range->m_finish.m_line == line
1073 && range->m_finish.m_column == finish_column)
1074 return true;
1075 return false;
1078 /* If there are any fixit hints on source line ROW within RICHLOC, print them.
1079 They are printed in order, attempting to combine them onto lines, but
1080 starting new lines if necessary. */
1082 void
1083 layout::print_any_fixits (int row, const rich_location *richloc)
1085 int column = 0;
1086 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1088 fixit_hint *hint = richloc->get_fixit_hint (i);
1089 if (hint->affects_line_p (m_exploc.file, row))
1091 /* For now we assume each fixit hint can only touch one line. */
1092 switch (hint->get_kind ())
1094 case fixit_hint::INSERT:
1096 fixit_insert *insert = static_cast <fixit_insert *> (hint);
1097 /* This assumes the insertion just affects one line. */
1098 int start_column
1099 = LOCATION_COLUMN (insert->get_location ());
1100 move_to_column (&column, start_column);
1101 m_colorizer.set_fixit_hint ();
1102 pp_string (m_pp, insert->get_string ());
1103 m_colorizer.set_normal_text ();
1104 column += insert->get_length ();
1106 break;
1108 case fixit_hint::REPLACE:
1110 fixit_replace *replace = static_cast <fixit_replace *> (hint);
1111 source_range src_range = replace->get_range ();
1112 int line = LOCATION_LINE (src_range.m_start);
1113 int start_column = LOCATION_COLUMN (src_range.m_start);
1114 int finish_column = LOCATION_COLUMN (src_range.m_finish);
1116 /* If the range of the replacement wasn't printed in the
1117 annotation line, then print an extra underline to
1118 indicate exactly what is being replaced.
1119 Always show it for removals. */
1120 if (!annotation_line_showed_range_p (line, start_column,
1121 finish_column)
1122 || replace->get_length () == 0)
1124 move_to_column (&column, start_column);
1125 m_colorizer.set_fixit_hint ();
1126 for (; column <= finish_column; column++)
1127 pp_character (m_pp, '-');
1128 m_colorizer.set_normal_text ();
1130 /* Print the replacement text. REPLACE also covers
1131 removals, so only do this extra work (potentially starting
1132 a new line) if we have actual replacement text. */
1133 if (replace->get_length () > 0)
1135 move_to_column (&column, start_column);
1136 m_colorizer.set_fixit_hint ();
1137 pp_string (m_pp, replace->get_string ());
1138 m_colorizer.set_normal_text ();
1139 column += replace->get_length ();
1142 break;
1144 default:
1145 gcc_unreachable ();
1150 /* Add a trailing newline, if necessary. */
1151 move_to_column (&column, 0);
1154 /* Disable any colorization and emit a newline. */
1156 void
1157 layout::print_newline ()
1159 m_colorizer.set_normal_text ();
1160 pp_newline (m_pp);
1163 /* Return true if (ROW/COLUMN) is within a range of the layout.
1164 If it returns true, OUT_STATE is written to, with the
1165 range index, and whether we should draw the caret at
1166 (ROW/COLUMN) (as opposed to an underline). */
1168 bool
1169 layout::get_state_at_point (/* Inputs. */
1170 int row, int column,
1171 int first_non_ws, int last_non_ws,
1172 /* Outputs. */
1173 point_state *out_state)
1175 layout_range *range;
1176 int i;
1177 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1179 if (range->contains_point (row, column))
1181 out_state->range_idx = i;
1183 /* Are we at the range's caret? is it visible? */
1184 out_state->draw_caret_p = false;
1185 if (range->m_show_caret_p
1186 && row == range->m_caret.m_line
1187 && column == range->m_caret.m_column)
1188 out_state->draw_caret_p = true;
1190 /* Within a multiline range, don't display any underline
1191 in any leading or trailing whitespace on a line.
1192 We do display carets, however. */
1193 if (!out_state->draw_caret_p)
1194 if (column < first_non_ws || column > last_non_ws)
1195 return false;
1197 /* We are within a range. */
1198 return true;
1202 return false;
1205 /* Helper function for use by layout::print_line when printing the
1206 annotation line under the source line.
1207 Get the column beyond the rightmost one that could contain a caret or
1208 range marker, given that we stop rendering at trailing whitespace.
1209 ROW is the source line within the given file.
1210 CARET_COLUMN is the column of range 0's caret.
1211 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1212 character of source (as determined when printing the source line). */
1215 layout::get_x_bound_for_row (int row, int caret_column,
1216 int last_non_ws_column)
1218 int result = caret_column + 1;
1220 layout_range *range;
1221 int i;
1222 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1224 if (row >= range->m_start.m_line)
1226 if (range->m_finish.m_line == row)
1228 /* On the final line within a range; ensure that
1229 we render up to the end of the range. */
1230 if (result <= range->m_finish.m_column)
1231 result = range->m_finish.m_column + 1;
1233 else if (row < range->m_finish.m_line)
1235 /* Within a multiline range; ensure that we render up to the
1236 last non-whitespace column. */
1237 if (result <= last_non_ws_column)
1238 result = last_non_ws_column + 1;
1243 return result;
1246 /* Given *COLUMN as an x-coordinate, print spaces to position
1247 successive output at DEST_COLUMN, printing a newline if necessary,
1248 and updating *COLUMN. */
1250 void
1251 layout::move_to_column (int *column, int dest_column)
1253 /* Start a new line if we need to. */
1254 if (*column > dest_column)
1256 print_newline ();
1257 *column = 0;
1260 while (*column < dest_column)
1262 pp_space (m_pp);
1263 (*column)++;
1267 /* For debugging layout issues, render a ruler giving column numbers
1268 (after the 1-column indent). */
1270 void
1271 layout::show_ruler (int max_column) const
1273 /* Hundreds. */
1274 if (max_column > 99)
1276 pp_space (m_pp);
1277 for (int column = 1 + m_x_offset; column <= max_column; column++)
1278 if (0 == column % 10)
1279 pp_character (m_pp, '0' + (column / 100) % 10);
1280 else
1281 pp_space (m_pp);
1282 pp_newline (m_pp);
1285 /* Tens. */
1286 pp_space (m_pp);
1287 for (int column = 1 + m_x_offset; column <= max_column; column++)
1288 if (0 == column % 10)
1289 pp_character (m_pp, '0' + (column / 10) % 10);
1290 else
1291 pp_space (m_pp);
1292 pp_newline (m_pp);
1294 /* Units. */
1295 pp_space (m_pp);
1296 for (int column = 1 + m_x_offset; column <= max_column; column++)
1297 pp_character (m_pp, '0' + (column % 10));
1298 pp_newline (m_pp);
1301 } /* End of anonymous namespace. */
1303 /* Print the physical source code corresponding to the location of
1304 this diagnostic, with additional annotations. */
1306 void
1307 diagnostic_show_locus (diagnostic_context * context,
1308 rich_location *richloc,
1309 diagnostic_t diagnostic_kind)
1311 pp_newline (context->printer);
1313 location_t loc = richloc->get_loc ();
1314 /* Do nothing if source-printing has been disabled. */
1315 if (!context->show_caret)
1316 return;
1318 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1319 if (loc <= BUILTINS_LOCATION)
1320 return;
1322 /* Don't print the same source location twice in a row, unless we have
1323 fix-it hints. */
1324 if (loc == context->last_location
1325 && richloc->get_num_fixit_hints () == 0)
1326 return;
1328 context->last_location = loc;
1330 const char *saved_prefix = pp_get_prefix (context->printer);
1331 pp_set_prefix (context->printer, NULL);
1333 layout layout (context, richloc, diagnostic_kind);
1334 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1335 line_span_idx++)
1337 const line_span *line_span = layout.get_line_span (line_span_idx);
1338 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1340 expanded_location exploc = layout.get_expanded_location (line_span);
1341 context->start_span (context, exploc);
1343 int last_line = line_span->get_last_line ();
1344 for (int row = line_span->get_first_line (); row <= last_line; row++)
1346 /* Print the source line, followed by an annotation line
1347 consisting of any caret/underlines, then any fixits.
1348 If the source line can't be read, print nothing. */
1349 line_bounds lbounds;
1350 if (layout.print_source_line (row, &lbounds))
1352 layout.print_annotation_line (row, lbounds);
1353 layout.print_any_fixits (row, richloc);
1358 pp_set_prefix (context->printer, saved_prefix);
1361 #if CHECKING_P
1363 namespace selftest {
1365 /* Selftests for diagnostic_show_locus. */
1367 /* Convenience subclass of diagnostic_context for testing
1368 diagnostic_show_locus. */
1370 class test_diagnostic_context : public diagnostic_context
1372 public:
1373 test_diagnostic_context ()
1375 diagnostic_initialize (this, 0);
1376 show_caret = true;
1378 ~test_diagnostic_context ()
1380 diagnostic_finish (this);
1384 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1386 static void
1387 test_diagnostic_show_locus_unknown_location ()
1389 test_diagnostic_context dc;
1390 rich_location richloc (line_table, UNKNOWN_LOCATION);
1391 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1392 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1395 /* Verify that diagnostic_show_locus works sanely for various
1396 single-line cases.
1398 All of these work on the following 1-line source file:
1399 .0000000001111111
1400 .1234567890123456
1401 "foo = bar.field;\n"
1402 which is set up by test_diagnostic_show_locus_one_liner and calls
1403 them. */
1405 /* Just a caret. */
1407 static void
1408 test_one_liner_simple_caret ()
1410 test_diagnostic_context dc;
1411 location_t caret = linemap_position_for_column (line_table, 10);
1412 rich_location richloc (line_table, caret);
1413 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1414 ASSERT_STREQ ("\n"
1415 " foo = bar.field;\n"
1416 " ^\n",
1417 pp_formatted_text (dc.printer));
1420 /* Caret and range. */
1422 static void
1423 test_one_liner_caret_and_range ()
1425 test_diagnostic_context dc;
1426 location_t caret = linemap_position_for_column (line_table, 10);
1427 location_t start = linemap_position_for_column (line_table, 7);
1428 location_t finish = linemap_position_for_column (line_table, 15);
1429 location_t loc = make_location (caret, start, finish);
1430 rich_location richloc (line_table, loc);
1431 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1432 ASSERT_STREQ ("\n"
1433 " foo = bar.field;\n"
1434 " ~~~^~~~~~\n",
1435 pp_formatted_text (dc.printer));
1438 /* Multiple ranges and carets. */
1440 static void
1441 test_one_liner_multiple_carets_and_ranges ()
1443 test_diagnostic_context dc;
1444 location_t foo
1445 = make_location (linemap_position_for_column (line_table, 2),
1446 linemap_position_for_column (line_table, 1),
1447 linemap_position_for_column (line_table, 3));
1448 dc.caret_chars[0] = 'A';
1450 location_t bar
1451 = make_location (linemap_position_for_column (line_table, 8),
1452 linemap_position_for_column (line_table, 7),
1453 linemap_position_for_column (line_table, 9));
1454 dc.caret_chars[1] = 'B';
1456 location_t field
1457 = make_location (linemap_position_for_column (line_table, 13),
1458 linemap_position_for_column (line_table, 11),
1459 linemap_position_for_column (line_table, 15));
1460 dc.caret_chars[2] = 'C';
1462 rich_location richloc (line_table, foo);
1463 richloc.add_range (bar, true);
1464 richloc.add_range (field, true);
1465 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1466 ASSERT_STREQ ("\n"
1467 " foo = bar.field;\n"
1468 " ~A~ ~B~ ~~C~~\n",
1469 pp_formatted_text (dc.printer));
1472 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1474 static void
1475 test_one_liner_fixit_insert ()
1477 test_diagnostic_context dc;
1478 location_t caret = linemap_position_for_column (line_table, 7);
1479 rich_location richloc (line_table, caret);
1480 richloc.add_fixit_insert (caret, "&");
1481 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1482 ASSERT_STREQ ("\n"
1483 " foo = bar.field;\n"
1484 " ^\n"
1485 " &\n",
1486 pp_formatted_text (dc.printer));
1489 /* Removal fix-it hint: removal of the ".field". */
1491 static void
1492 test_one_liner_fixit_remove ()
1494 test_diagnostic_context dc;
1495 location_t start = linemap_position_for_column (line_table, 10);
1496 location_t finish = linemap_position_for_column (line_table, 15);
1497 location_t dot = make_location (start, start, finish);
1498 rich_location richloc (line_table, dot);
1499 source_range range;
1500 range.m_start = start;
1501 range.m_finish = finish;
1502 richloc.add_fixit_remove (range);
1503 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1504 ASSERT_STREQ ("\n"
1505 " foo = bar.field;\n"
1506 " ^~~~~~\n"
1507 " ------\n",
1508 pp_formatted_text (dc.printer));
1511 /* Replace fix-it hint: replacing "field" with "m_field". */
1513 static void
1514 test_one_liner_fixit_replace ()
1516 test_diagnostic_context dc;
1517 location_t start = linemap_position_for_column (line_table, 11);
1518 location_t finish = linemap_position_for_column (line_table, 15);
1519 location_t field = make_location (start, start, finish);
1520 rich_location richloc (line_table, field);
1521 source_range range;
1522 range.m_start = start;
1523 range.m_finish = finish;
1524 richloc.add_fixit_replace (range, "m_field");
1525 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1526 ASSERT_STREQ ("\n"
1527 " foo = bar.field;\n"
1528 " ^~~~~\n"
1529 " m_field\n",
1530 pp_formatted_text (dc.printer));
1533 /* Replace fix-it hint: replacing "field" with "m_field",
1534 but where the caret was elsewhere. */
1536 static void
1537 test_one_liner_fixit_replace_non_equal_range ()
1539 test_diagnostic_context dc;
1540 location_t equals = linemap_position_for_column (line_table, 5);
1541 location_t start = linemap_position_for_column (line_table, 11);
1542 location_t finish = linemap_position_for_column (line_table, 15);
1543 rich_location richloc (line_table, equals);
1544 source_range range;
1545 range.m_start = start;
1546 range.m_finish = finish;
1547 richloc.add_fixit_replace (range, "m_field");
1548 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1549 /* The replacement range is not indicated in the annotation line, so
1550 it should be indicated via an additional underline. */
1551 ASSERT_STREQ ("\n"
1552 " foo = bar.field;\n"
1553 " ^\n"
1554 " -----\n"
1555 " m_field\n",
1556 pp_formatted_text (dc.printer));
1559 /* Replace fix-it hint: replacing "field" with "m_field",
1560 where the caret was elsewhere, but where a secondary range
1561 exactly covers "field". */
1563 static void
1564 test_one_liner_fixit_replace_equal_secondary_range ()
1566 test_diagnostic_context dc;
1567 location_t equals = linemap_position_for_column (line_table, 5);
1568 location_t start = linemap_position_for_column (line_table, 11);
1569 location_t finish = linemap_position_for_column (line_table, 15);
1570 rich_location richloc (line_table, equals);
1571 location_t field = make_location (start, start, finish);
1572 richloc.add_range (field, false);
1573 source_range range;
1574 range.m_start = start;
1575 range.m_finish = finish;
1576 richloc.add_fixit_replace (range, "m_field");
1577 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1578 /* The replacement range is indicated in the annotation line,
1579 so it shouldn't be indicated via an additional underline. */
1580 ASSERT_STREQ ("\n"
1581 " foo = bar.field;\n"
1582 " ^ ~~~~~\n"
1583 " m_field\n",
1584 pp_formatted_text (dc.printer));
1587 /* Run the various one-liner tests. */
1589 static void
1590 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1592 /* Create a tempfile and write some text to it.
1593 ....................0000000001111111.
1594 ....................1234567890123456. */
1595 const char *content = "foo = bar.field;\n";
1596 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1597 line_table_test ltt (case_);
1599 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1601 location_t line_end = linemap_position_for_column (line_table, 16);
1603 /* Don't attempt to run the tests if column data might be unavailable. */
1604 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1605 return;
1607 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1608 ASSERT_EQ (1, LOCATION_LINE (line_end));
1609 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1611 test_one_liner_simple_caret ();
1612 test_one_liner_caret_and_range ();
1613 test_one_liner_multiple_carets_and_ranges ();
1614 test_one_liner_fixit_insert ();
1615 test_one_liner_fixit_remove ();
1616 test_one_liner_fixit_replace ();
1617 test_one_liner_fixit_replace_non_equal_range ();
1618 test_one_liner_fixit_replace_equal_secondary_range ();
1621 /* Run all of the selftests within this file. */
1623 void
1624 diagnostic_show_locus_c_tests ()
1626 test_range_contains_point_for_single_point ();
1627 test_range_contains_point_for_single_line ();
1628 test_range_contains_point_for_multiple_lines ();
1630 test_get_line_width_without_trailing_whitespace ();
1632 test_diagnostic_show_locus_unknown_location ();
1634 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
1637 } // namespace selftest
1639 #endif /* #if CHECKING_P */