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
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
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/>. */
23 #include "coretypes.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
36 #ifdef GWINSZ_IN_SYS_IOCTL
37 # include <sys/ioctl.h>
40 /* Classes for rendering source code and diagnostics, within an
42 The work is done by "class layout", which embeds and uses
43 "class colorizer" and "class layout_range" to get things done. */
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
57 /* A class to inject colorization codes when printing the diagnostic locus.
59 It has one kind of colorization for each of:
61 - range 0 (the "primary location")
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. */
76 colorizer (diagnostic_context
*context
,
77 diagnostic_t diagnostic_kind
);
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
); }
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 *);
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
;
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. */
113 layout_point (const expanded_location
&exploc
)
114 : m_line (exploc
.line
),
115 m_column (exploc
.column
) {}
121 /* A class for use by "class layout" below: a filtered location_range. */
126 layout_range (const expanded_location
*start_exploc
,
127 const expanded_location
*finish_exploc
,
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
;
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. */
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). */
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
;
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. */
195 layout (diagnostic_context
*context
,
196 rich_location
*richloc
,
197 diagnostic_t diagnostic_kind
);
199 int get_num_line_spans () const { return m_line_spans
.length (); }
200 const line_span
*get_line_span (int idx
) const { return &m_line_spans
[idx
]; }
202 bool print_heading_for_line_span_index_p (int line_span_idx
) const;
204 expanded_location
get_expanded_location (const line_span
*) const;
206 void print_line (int row
);
209 void print_leading_fixits (int row
);
210 void print_source_line (int row
, const char *line
, int line_width
,
211 line_bounds
*lbounds_out
);
212 bool should_print_annotation_line_p (int row
) const;
213 void print_annotation_line (int row
, const line_bounds lbounds
);
214 void print_trailing_fixits (int row
);
216 bool annotation_line_showed_range_p (int line
, int start_column
,
217 int finish_column
) const;
218 void show_ruler (int max_column
) const;
220 bool validate_fixit_hint_p (const fixit_hint
*hint
);
222 void calculate_line_spans ();
224 void print_newline ();
227 get_state_at_point (/* Inputs. */
229 int first_non_ws
, int last_non_ws
,
231 point_state
*out_state
);
234 get_x_bound_for_row (int row
, int caret_column
,
238 move_to_column (int *column
, int dest_column
);
241 diagnostic_context
*m_context
;
242 pretty_printer
*m_pp
;
243 diagnostic_t m_diagnostic_kind
;
244 expanded_location m_exploc
;
245 colorizer m_colorizer
;
246 bool m_colorize_source_p
;
247 auto_vec
<layout_range
> m_layout_ranges
;
248 auto_vec
<const fixit_hint
*> m_fixit_hints
;
249 auto_vec
<line_span
> m_line_spans
;
253 /* Implementation of "class colorizer". */
255 /* The constructor for "colorizer". Lookup and store color codes for the
256 different kinds of things we might need to print. */
258 colorizer::colorizer (diagnostic_context
*context
,
259 diagnostic_t diagnostic_kind
) :
261 m_diagnostic_kind (diagnostic_kind
),
262 m_current_state (STATE_NORMAL_TEXT
)
264 m_range1
= get_color_by_name ("range1");
265 m_range2
= get_color_by_name ("range2");
266 m_fixit_insert
= get_color_by_name ("fixit-insert");
267 m_fixit_delete
= get_color_by_name ("fixit-delete");
268 m_stop_color
= colorize_stop (pp_show_color (context
->printer
));
271 /* The destructor for "colorize". If colorization is on, print a code to
274 colorizer::~colorizer ()
276 finish_state (m_current_state
);
279 /* Update state, printing color codes if necessary if there's a state
283 colorizer::set_state (int new_state
)
285 if (m_current_state
!= new_state
)
287 finish_state (m_current_state
);
288 m_current_state
= new_state
;
289 begin_state (new_state
);
293 /* Turn on any colorization for STATE. */
296 colorizer::begin_state (int state
)
300 case STATE_NORMAL_TEXT
:
303 case STATE_FIXIT_INSERT
:
304 pp_string (m_context
->printer
, m_fixit_insert
);
307 case STATE_FIXIT_DELETE
:
308 pp_string (m_context
->printer
, m_fixit_delete
);
312 /* Make range 0 be the same color as the "kind" text
313 (error vs warning vs note). */
316 colorize_start (pp_show_color (m_context
->printer
),
317 diagnostic_get_color_for_kind (m_diagnostic_kind
)));
321 pp_string (m_context
->printer
, m_range1
);
325 pp_string (m_context
->printer
, m_range2
);
329 /* For ranges beyond 2, alternate between color 1 and color 2. */
331 gcc_assert (state
> 2);
332 pp_string (m_context
->printer
,
333 state
% 2 ? m_range1
: m_range2
);
339 /* Turn off any colorization for STATE. */
342 colorizer::finish_state (int state
)
344 if (state
!= STATE_NORMAL_TEXT
)
345 pp_string (m_context
->printer
, m_stop_color
);
348 /* Get the color code for NAME (or the empty string if
349 colorization is disabled). */
352 colorizer::get_color_by_name (const char *name
)
354 return colorize_start (pp_show_color (m_context
->printer
), name
);
357 /* Implementation of class layout_range. */
359 /* The constructor for class layout_range.
360 Initialize various layout_point fields from expanded_location
361 equivalents; we've already filtered on file. */
363 layout_range::layout_range (const expanded_location
*start_exploc
,
364 const expanded_location
*finish_exploc
,
366 const expanded_location
*caret_exploc
)
367 : m_start (*start_exploc
),
368 m_finish (*finish_exploc
),
369 m_show_caret_p (show_caret_p
),
370 m_caret (*caret_exploc
)
374 /* Is (column, row) within the given range?
375 We've already filtered on the file.
377 Ranges are closed (both limits are within the range).
379 Example A: a single-line range:
380 start: (col=22, line=2)
381 finish: (col=38, line=2)
383 |00000011111111112222222222333333333344444444444
384 |34567890123456789012345678901234567890123456789
385 --+-----------------------------------------------
386 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
387 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
388 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
390 Example B: a multiline range with
391 start: (col=14, line=3)
392 finish: (col=08, line=5)
394 |00000011111111112222222222333333333344444444444
395 |34567890123456789012345678901234567890123456789
396 --+-----------------------------------------------
397 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
398 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
399 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
400 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
401 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
402 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
403 --+-----------------------------------------------
406 - 'b' indicates a point *before* the range
407 - 'S' indicates the start of the range
408 - 'w' indicates a point within the range
409 - 'F' indicates the finish of the range (which is
411 - 'a' indicates a subsequent point *after* the range. */
414 layout_range::contains_point (int row
, int column
) const
416 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
417 /* ...but the equivalent isn't true for the columns;
418 consider example B in the comment above. */
420 if (row
< m_start
.m_line
)
421 /* Points before the first line of the range are
422 outside it (corresponding to line 01 in example A
423 and lines 01 and 02 in example B above). */
426 if (row
== m_start
.m_line
)
427 /* On same line as start of range (corresponding
428 to line 02 in example A and line 03 in example B). */
430 if (column
< m_start
.m_column
)
431 /* Points on the starting line of the range, but
432 before the column in which it begins. */
435 if (row
< m_finish
.m_line
)
436 /* This is a multiline range; the point
437 is within it (corresponds to line 03 in example B
438 from column 14 onwards) */
442 /* This is a single-line range. */
443 gcc_assert (row
== m_finish
.m_line
);
444 return column
<= m_finish
.m_column
;
448 /* The point is in a line beyond that containing the
449 start of the range: lines 03 onwards in example A,
450 and lines 04 onwards in example B. */
451 gcc_assert (row
> m_start
.m_line
);
453 if (row
> m_finish
.m_line
)
454 /* The point is beyond the final line of the range
455 (lines 03 onwards in example A, and lines 06 onwards
459 if (row
< m_finish
.m_line
)
461 /* The point is in a line that's fully within a multiline
462 range (e.g. line 04 in example B). */
463 gcc_assert (m_start
.m_line
< m_finish
.m_line
);
467 gcc_assert (row
== m_finish
.m_line
);
469 return column
<= m_finish
.m_column
;
472 /* Does this layout_range contain any part of line ROW? */
475 layout_range::intersects_line_p (int row
) const
477 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
478 if (row
< m_start
.m_line
)
480 if (row
> m_finish
.m_line
)
487 /* A helper function for testing layout_range. */
490 make_range (int start_line
, int start_col
, int end_line
, int end_col
)
492 const expanded_location start_exploc
493 = {"test.c", start_line
, start_col
, NULL
, false};
494 const expanded_location finish_exploc
495 = {"test.c", end_line
, end_col
, NULL
, false};
496 return layout_range (&start_exploc
, &finish_exploc
, false,
500 /* Selftests for layout_range::contains_point and
501 layout_range::intersects_line_p. */
503 /* Selftest for layout_range, where the layout_range
504 is a range with start==end i.e. a single point. */
507 test_layout_range_for_single_point ()
509 layout_range point
= make_range (7, 10, 7, 10);
511 /* Tests for layout_range::contains_point. */
513 /* Before the line. */
514 ASSERT_FALSE (point
.contains_point (6, 1));
516 /* On the line, but before start. */
517 ASSERT_FALSE (point
.contains_point (7, 9));
520 ASSERT_TRUE (point
.contains_point (7, 10));
522 /* On the line, after the point. */
523 ASSERT_FALSE (point
.contains_point (7, 11));
525 /* After the line. */
526 ASSERT_FALSE (point
.contains_point (8, 1));
528 /* Tests for layout_range::intersects_line_p. */
529 ASSERT_FALSE (point
.intersects_line_p (6));
530 ASSERT_TRUE (point
.intersects_line_p (7));
531 ASSERT_FALSE (point
.intersects_line_p (8));
534 /* Selftest for layout_range, where the layout_range
535 is the single-line range shown as "Example A" above. */
538 test_layout_range_for_single_line ()
540 layout_range example_a
= make_range (2, 22, 2, 38);
542 /* Tests for layout_range::contains_point. */
544 /* Before the line. */
545 ASSERT_FALSE (example_a
.contains_point (1, 1));
547 /* On the line, but before start. */
548 ASSERT_FALSE (example_a
.contains_point (2, 21));
550 /* On the line, at the start. */
551 ASSERT_TRUE (example_a
.contains_point (2, 22));
553 /* On the line, within the range. */
554 ASSERT_TRUE (example_a
.contains_point (2, 23));
556 /* On the line, at the end. */
557 ASSERT_TRUE (example_a
.contains_point (2, 38));
559 /* On the line, after the end. */
560 ASSERT_FALSE (example_a
.contains_point (2, 39));
562 /* After the line. */
563 ASSERT_FALSE (example_a
.contains_point (2, 39));
565 /* Tests for layout_range::intersects_line_p. */
566 ASSERT_FALSE (example_a
.intersects_line_p (1));
567 ASSERT_TRUE (example_a
.intersects_line_p (2));
568 ASSERT_FALSE (example_a
.intersects_line_p (3));
571 /* Selftest for layout_range, where the layout_range
572 is the multi-line range shown as "Example B" above. */
575 test_layout_range_for_multiple_lines ()
577 layout_range example_b
= make_range (3, 14, 5, 8);
579 /* Tests for layout_range::contains_point. */
581 /* Before first line. */
582 ASSERT_FALSE (example_b
.contains_point (1, 1));
584 /* On the first line, but before start. */
585 ASSERT_FALSE (example_b
.contains_point (3, 13));
588 ASSERT_TRUE (example_b
.contains_point (3, 14));
590 /* On the first line, within the range. */
591 ASSERT_TRUE (example_b
.contains_point (3, 15));
593 /* On an interior line.
594 The column number should not matter; try various boundary
596 ASSERT_TRUE (example_b
.contains_point (4, 1));
597 ASSERT_TRUE (example_b
.contains_point (4, 7));
598 ASSERT_TRUE (example_b
.contains_point (4, 8));
599 ASSERT_TRUE (example_b
.contains_point (4, 9));
600 ASSERT_TRUE (example_b
.contains_point (4, 13));
601 ASSERT_TRUE (example_b
.contains_point (4, 14));
602 ASSERT_TRUE (example_b
.contains_point (4, 15));
604 /* On the final line, before the end. */
605 ASSERT_TRUE (example_b
.contains_point (5, 7));
607 /* On the final line, at the end. */
608 ASSERT_TRUE (example_b
.contains_point (5, 8));
610 /* On the final line, after the end. */
611 ASSERT_FALSE (example_b
.contains_point (5, 9));
613 /* After the line. */
614 ASSERT_FALSE (example_b
.contains_point (6, 1));
616 /* Tests for layout_range::intersects_line_p. */
617 ASSERT_FALSE (example_b
.intersects_line_p (2));
618 ASSERT_TRUE (example_b
.intersects_line_p (3));
619 ASSERT_TRUE (example_b
.intersects_line_p (4));
620 ASSERT_TRUE (example_b
.intersects_line_p (5));
621 ASSERT_FALSE (example_b
.intersects_line_p (6));
624 #endif /* #if CHECKING_P */
626 /* Given a source line LINE of length LINE_WIDTH, determine the width
627 without any trailing whitespace. */
630 get_line_width_without_trailing_whitespace (const char *line
, int line_width
)
632 int result
= line_width
;
635 char ch
= line
[result
- 1];
636 if (ch
== ' ' || ch
== '\t')
641 gcc_assert (result
>= 0);
642 gcc_assert (result
<= line_width
);
643 gcc_assert (result
== 0 ||
644 (line
[result
- 1] != ' '
645 && line
[result
-1] != '\t'));
651 /* A helper function for testing get_line_width_without_trailing_whitespace. */
654 assert_eq (const char *line
, int expected_width
)
657 = get_line_width_without_trailing_whitespace (line
, strlen (line
));
658 ASSERT_EQ (actual_value
, expected_width
);
661 /* Verify that get_line_width_without_trailing_whitespace is sane for
662 various inputs. It is not required to handle newlines. */
665 test_get_line_width_without_trailing_whitespace ()
670 assert_eq ("hello world", 11);
671 assert_eq ("hello world ", 11);
672 assert_eq ("hello world \t\t ", 11);
675 #endif /* #if CHECKING_P */
677 /* Helper function for layout's ctor, for sanitizing locations relative
678 to the primary location within a diagnostic.
680 Compare LOC_A and LOC_B to see if it makes sense to print underlines
681 connecting their expanded locations. Doing so is only guaranteed to
682 make sense if the locations share the same macro expansion "history"
683 i.e. they can be traced through the same macro expansions, eventually
684 reaching an ordinary map.
686 This may be too strong a condition, but it effectively sanitizes
687 PR c++/70105, which has an example of printing an expression where the
688 final location of the expression is in a different macro, which
689 erroneously was leading to hundreds of lines of irrelevant source
693 compatible_locations_p (location_t loc_a
, location_t loc_b
)
695 if (IS_ADHOC_LOC (loc_a
))
696 loc_a
= get_location_from_adhoc_loc (line_table
, loc_a
);
697 if (IS_ADHOC_LOC (loc_b
))
698 loc_b
= get_location_from_adhoc_loc (line_table
, loc_b
);
700 /* If either location is one of the special locations outside of a
701 linemap, they are only compatible if they are equal. */
702 if (loc_a
< RESERVED_LOCATION_COUNT
703 || loc_b
< RESERVED_LOCATION_COUNT
)
704 return loc_a
== loc_b
;
706 const line_map
*map_a
= linemap_lookup (line_table
, loc_a
);
707 linemap_assert (map_a
);
709 const line_map
*map_b
= linemap_lookup (line_table
, loc_b
);
710 linemap_assert (map_b
);
712 /* Are they within the same map? */
715 /* Are both within the same macro expansion? */
716 if (linemap_macro_expansion_map_p (map_a
))
718 /* Expand each location towards the spelling location, and
720 const line_map_macro
*macro_map
= linemap_check_macro (map_a
);
721 source_location loc_a_toward_spelling
722 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
725 source_location loc_b_toward_spelling
726 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
729 return compatible_locations_p (loc_a_toward_spelling
,
730 loc_b_toward_spelling
);
733 /* Otherwise they are within the same ordinary map. */
738 /* Within different maps. */
740 /* If either is within a macro expansion, they are incompatible. */
741 if (linemap_macro_expansion_map_p (map_a
)
742 || linemap_macro_expansion_map_p (map_b
))
745 /* Within two different ordinary maps; they are compatible iff they
746 are in the same file. */
747 const line_map_ordinary
*ord_map_a
= linemap_check_ordinary (map_a
);
748 const line_map_ordinary
*ord_map_b
= linemap_check_ordinary (map_b
);
749 return ord_map_a
->to_file
== ord_map_b
->to_file
;
753 /* Implementation of class layout. */
755 /* Constructor for class layout.
757 Filter the ranges from the rich_location to those that we can
758 sanely print, populating m_layout_ranges and m_fixit_hints.
759 Determine the range of lines that we will print, splitting them
760 up into an ordered list of disjoint spans of contiguous line numbers.
761 Determine m_x_offset, to ensure that the primary caret
762 will fit within the max_width provided by the diagnostic_context. */
764 layout::layout (diagnostic_context
* context
,
765 rich_location
*richloc
,
766 diagnostic_t diagnostic_kind
)
767 : m_context (context
),
768 m_pp (context
->printer
),
769 m_diagnostic_kind (diagnostic_kind
),
770 m_exploc (richloc
->get_expanded_location (0)),
771 m_colorizer (context
, diagnostic_kind
),
772 m_colorize_source_p (context
->colorize_source_p
),
773 m_layout_ranges (richloc
->get_num_locations ()),
774 m_fixit_hints (richloc
->get_num_fixit_hints ()),
775 m_line_spans (1 + richloc
->get_num_locations ()),
778 source_location primary_loc
= richloc
->get_range (0)->m_loc
;
780 for (unsigned int idx
= 0; idx
< richloc
->get_num_locations (); idx
++)
782 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
783 Ignore any ranges that are awkward to handle. */
784 const location_range
*loc_range
= richloc
->get_range (idx
);
786 /* Split the "range" into caret and range information. */
787 source_range src_range
= get_range_from_loc (line_table
, loc_range
->m_loc
);
789 /* Expand the various locations. */
790 expanded_location start
791 = linemap_client_expand_location_to_spelling_point
792 (src_range
.m_start
, LOCATION_ASPECT_START
);
793 expanded_location finish
794 = linemap_client_expand_location_to_spelling_point
795 (src_range
.m_finish
, LOCATION_ASPECT_FINISH
);
796 expanded_location caret
797 = linemap_client_expand_location_to_spelling_point
798 (loc_range
->m_loc
, LOCATION_ASPECT_CARET
);
800 /* If any part of the range isn't in the same file as the primary
801 location of this diagnostic, ignore the range. */
802 if (start
.file
!= m_exploc
.file
)
804 if (finish
.file
!= m_exploc
.file
)
806 if (loc_range
->m_show_caret_p
)
807 if (caret
.file
!= m_exploc
.file
)
810 /* Sanitize the caret location for non-primary ranges. */
811 if (m_layout_ranges
.length () > 0)
812 if (loc_range
->m_show_caret_p
)
813 if (!compatible_locations_p (loc_range
->m_loc
, primary_loc
))
814 /* Discard any non-primary ranges that can't be printed
815 sanely relative to the primary location. */
818 /* Everything is now known to be in the correct source file,
819 but it may require further sanitization. */
820 layout_range
ri (&start
, &finish
, loc_range
->m_show_caret_p
, &caret
);
822 /* If we have a range that finishes before it starts (perhaps
823 from something built via macro expansion), printing the
824 range is likely to be nonsensical. Also, attempting to do so
825 breaks assumptions within the printing code (PR c/68473).
826 Similarly, don't attempt to print ranges if one or both ends
827 of the range aren't sane to print relative to the
828 primary location (PR c++/70105). */
829 if (start
.line
> finish
.line
830 || !compatible_locations_p (src_range
.m_start
, primary_loc
)
831 || !compatible_locations_p (src_range
.m_finish
, primary_loc
))
833 /* Is this the primary location? */
834 if (m_layout_ranges
.length () == 0)
836 /* We want to print the caret for the primary location, but
837 we must sanitize away m_start and m_finish. */
838 ri
.m_start
= ri
.m_caret
;
839 ri
.m_finish
= ri
.m_caret
;
842 /* This is a non-primary range; ignore it. */
846 /* Passed all the tests; add the range to m_layout_ranges so that
847 it will be printed. */
848 m_layout_ranges
.safe_push (ri
);
851 /* Populate m_fixit_hints, filtering to only those that are in the
853 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
855 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
856 if (validate_fixit_hint_p (hint
))
857 m_fixit_hints
.safe_push (hint
);
860 /* Populate m_line_spans. */
861 calculate_line_spans ();
863 /* Adjust m_x_offset.
864 Center the primary caret to fit in max_width; all columns
865 will be adjusted accordingly. */
866 int max_width
= m_context
->caret_max_width
;
868 const char *line
= location_get_source_line (m_exploc
.file
, m_exploc
.line
,
870 if (line
&& m_exploc
.column
<= line_width
)
872 int right_margin
= CARET_LINE_MARGIN
;
873 int column
= m_exploc
.column
;
874 right_margin
= MIN (line_width
- column
, right_margin
);
875 right_margin
= max_width
- right_margin
;
876 if (line_width
>= max_width
&& column
> right_margin
)
877 m_x_offset
= column
- right_margin
;
878 gcc_assert (m_x_offset
>= 0);
881 if (context
->show_ruler_p
)
882 show_ruler (m_x_offset
+ max_width
);
885 /* Return true iff we should print a heading when starting the
886 line span with the given index. */
889 layout::print_heading_for_line_span_index_p (int line_span_idx
) const
891 /* We print a heading for every change of line span, hence for every
892 line span after the initial one. */
893 if (line_span_idx
> 0)
896 /* We also do it for the initial span if the primary location of the
897 diagnostic is in a different span. */
898 if (m_exploc
.line
> (int)get_line_span (0)->m_last_line
)
904 /* Get an expanded_location for the first location of interest within
906 Used when printing a heading to indicate a new line span. */
909 layout::get_expanded_location (const line_span
*line_span
) const
911 /* Whenever possible, use the caret location. */
912 if (line_span
->contains_line_p (m_exploc
.line
))
915 /* Otherwise, use the start of the first range that's present
916 within the line_span. */
917 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
919 const layout_range
*lr
= &m_layout_ranges
[i
];
920 if (line_span
->contains_line_p (lr
->m_start
.m_line
))
922 expanded_location exploc
= m_exploc
;
923 exploc
.line
= lr
->m_start
.m_line
;
924 exploc
.column
= lr
->m_start
.m_column
;
929 /* Otherwise, use the location of the first fixit-hint present within
931 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
933 const fixit_hint
*hint
= m_fixit_hints
[i
];
934 location_t loc
= hint
->get_start_loc ();
935 expanded_location exploc
= expand_location (loc
);
936 if (line_span
->contains_line_p (exploc
.line
))
940 /* It should not be possible to have a line span that didn't
941 contain any of the layout_range or fixit_hint instances. */
946 /* Determine if HINT is meaningful to print within this layout. */
949 layout::validate_fixit_hint_p (const fixit_hint
*hint
)
951 if (LOCATION_FILE (hint
->get_start_loc ()) != m_exploc
.file
)
953 if (LOCATION_FILE (hint
->get_next_loc ()) != m_exploc
.file
)
959 /* Determine the range of lines affected by HINT.
960 This assumes that HINT has already been filtered by
961 validate_fixit_hint_p, and so affects the correct source file. */
964 get_line_span_for_fixit_hint (const fixit_hint
*hint
)
967 return line_span (LOCATION_LINE (hint
->get_start_loc ()),
968 LOCATION_LINE (hint
->get_next_loc ()));
971 /* We want to print the pertinent source code at a diagnostic. The
972 rich_location can contain multiple locations. This will have been
973 filtered into m_exploc (the caret for the primary location) and
974 m_layout_ranges, for those ranges within the same source file.
976 We will print a subset of the lines within the source file in question,
977 as a collection of "spans" of lines.
979 This function populates m_line_spans with an ordered, disjoint list of
980 the line spans of interest.
982 For example, if the primary caret location is on line 7, with ranges
983 covering lines 5-6 and lines 9-12:
996 then we want two spans: lines 5-7 and lines 9-12. */
999 layout::calculate_line_spans ()
1001 /* This should only be called once, by the ctor. */
1002 gcc_assert (m_line_spans
.length () == 0);
1004 /* Populate tmp_spans with individual spans, for each of
1005 m_exploc, and for m_layout_ranges. */
1006 auto_vec
<line_span
> tmp_spans (1 + m_layout_ranges
.length ());
1007 tmp_spans
.safe_push (line_span (m_exploc
.line
, m_exploc
.line
));
1008 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1010 const layout_range
*lr
= &m_layout_ranges
[i
];
1011 gcc_assert (lr
->m_start
.m_line
<= lr
->m_finish
.m_line
);
1012 tmp_spans
.safe_push (line_span (lr
->m_start
.m_line
,
1013 lr
->m_finish
.m_line
));
1016 /* Also add spans for any fix-it hints, in case they cover other lines. */
1017 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1019 const fixit_hint
*hint
= m_fixit_hints
[i
];
1021 tmp_spans
.safe_push (get_line_span_for_fixit_hint (hint
));
1025 tmp_spans
.qsort(line_span::comparator
);
1027 /* Now iterate through tmp_spans, copying into m_line_spans, and
1028 combining where possible. */
1029 gcc_assert (tmp_spans
.length () > 0);
1030 m_line_spans
.safe_push (tmp_spans
[0]);
1031 for (unsigned int i
= 1; i
< tmp_spans
.length (); i
++)
1033 line_span
*current
= &m_line_spans
[m_line_spans
.length () - 1];
1034 const line_span
*next
= &tmp_spans
[i
];
1035 gcc_assert (next
->m_first_line
>= current
->m_first_line
);
1036 if (next
->m_first_line
<= current
->m_last_line
+ 1)
1038 /* We can merge them. */
1039 if (next
->m_last_line
> current
->m_last_line
)
1040 current
->m_last_line
= next
->m_last_line
;
1044 /* No merger possible. */
1045 m_line_spans
.safe_push (*next
);
1049 /* Verify the result, in m_line_spans. */
1050 gcc_assert (m_line_spans
.length () > 0);
1051 for (unsigned int i
= 1; i
< m_line_spans
.length (); i
++)
1053 const line_span
*prev
= &m_line_spans
[i
- 1];
1054 const line_span
*next
= &m_line_spans
[i
];
1055 /* The individual spans must be sane. */
1056 gcc_assert (prev
->m_first_line
<= prev
->m_last_line
);
1057 gcc_assert (next
->m_first_line
<= next
->m_last_line
);
1058 /* The spans must be ordered. */
1059 gcc_assert (prev
->m_first_line
< next
->m_first_line
);
1060 /* There must be a gap of at least one line between separate spans. */
1061 gcc_assert ((prev
->m_last_line
+ 1) < next
->m_first_line
);
1065 /* Print line ROW of source code, potentially colorized at any ranges, and
1066 populate *LBOUNDS_OUT.
1067 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1071 layout::print_source_line (int row
, const char *line
, int line_width
,
1072 line_bounds
*lbounds_out
)
1074 m_colorizer
.set_normal_text ();
1076 /* We will stop printing the source line at any trailing
1078 line_width
= get_line_width_without_trailing_whitespace (line
,
1083 int first_non_ws
= INT_MAX
;
1084 int last_non_ws
= 0;
1086 for (column
= 1 + m_x_offset
; column
<= line_width
; column
++)
1088 /* Assuming colorization is enabled for the caret and underline
1089 characters, we may also colorize the associated characters
1090 within the source line.
1092 For frontends that generate range information, we color the
1093 associated characters in the source line the same as the
1094 carets and underlines in the annotation line, to make it easier
1095 for the reader to see the pertinent code.
1097 For frontends that only generate carets, we don't colorize the
1098 characters above them, since this would look strange (e.g.
1099 colorizing just the first character in a token). */
1100 if (m_colorize_source_p
)
1104 in_range_p
= get_state_at_point (row
, column
,
1108 m_colorizer
.set_range (state
.range_idx
);
1110 m_colorizer
.set_normal_text ();
1112 char c
= *line
== '\t' ? ' ' : *line
;
1117 last_non_ws
= column
;
1118 if (first_non_ws
== INT_MAX
)
1119 first_non_ws
= column
;
1121 pp_character (m_pp
, c
);
1126 lbounds_out
->m_first_non_ws
= first_non_ws
;
1127 lbounds_out
->m_last_non_ws
= last_non_ws
;
1130 /* Determine if we should print an annotation line for ROW.
1131 i.e. if any of m_layout_ranges contains ROW. */
1134 layout::should_print_annotation_line_p (int row
) const
1136 layout_range
*range
;
1138 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1139 if (range
->intersects_line_p (row
))
1144 /* Print a line consisting of the caret/underlines for the given
1148 layout::print_annotation_line (int row
, const line_bounds lbounds
)
1150 int x_bound
= get_x_bound_for_row (row
, m_exploc
.column
,
1151 lbounds
.m_last_non_ws
);
1154 for (int column
= 1 + m_x_offset
; column
< x_bound
; column
++)
1158 in_range_p
= get_state_at_point (row
, column
,
1159 lbounds
.m_first_non_ws
,
1160 lbounds
.m_last_non_ws
,
1164 /* Within a range. Draw either the caret or an underline. */
1165 m_colorizer
.set_range (state
.range_idx
);
1166 if (state
.draw_caret_p
)
1168 /* Draw the caret. */
1170 if (state
.range_idx
< rich_location::STATICALLY_ALLOCATED_RANGES
)
1171 caret_char
= m_context
->caret_chars
[state
.range_idx
];
1174 pp_character (m_pp
, caret_char
);
1177 pp_character (m_pp
, '~');
1181 /* Not in a range. */
1182 m_colorizer
.set_normal_text ();
1183 pp_character (m_pp
, ' ');
1189 /* If there are any fixit hints inserting new lines before source line ROW,
1192 They are printed on lines of their own, before the source line
1193 itself, with a leading '+'. */
1196 layout::print_leading_fixits (int row
)
1198 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1200 const fixit_hint
*hint
= m_fixit_hints
[i
];
1202 if (!hint
->ends_with_newline_p ())
1203 /* Not a newline fixit; print it in print_trailing_fixits. */
1206 gcc_assert (hint
->insertion_p ());
1208 if (hint
->affects_line_p (m_exploc
.file
, row
))
1210 /* Printing the '+' with normal colorization
1211 and the inserted line with "insert" colorization
1212 helps them stand out from each other, and from
1213 the surrounding text. */
1214 m_colorizer
.set_normal_text ();
1215 pp_character (m_pp
, '+');
1216 m_colorizer
.set_fixit_insert ();
1217 /* Print all but the trailing newline of the fix-it hint.
1218 We have to print the newline separately to avoid
1219 getting additional pp prefixes printed. */
1220 for (size_t i
= 0; i
< hint
->get_length () - 1; i
++)
1221 pp_character (m_pp
, hint
->get_string ()[i
]);
1222 m_colorizer
.set_normal_text ();
1228 /* Subroutine of layout::print_trailing_fixits.
1230 Determine if the annotation line printed for LINE contained
1231 the exact range from START_COLUMN to FINISH_COLUMN. */
1234 layout::annotation_line_showed_range_p (int line
, int start_column
,
1235 int finish_column
) const
1237 layout_range
*range
;
1239 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1240 if (range
->m_start
.m_line
== line
1241 && range
->m_start
.m_column
== start_column
1242 && range
->m_finish
.m_line
== line
1243 && range
->m_finish
.m_column
== finish_column
)
1248 /* Classes for printing trailing fix-it hints i.e. those that
1249 don't add new lines.
1251 For insertion, these can look like:
1255 For replacement, these can look like:
1257 ------------- : underline showing affected range
1260 For deletion, these can look like:
1262 ------------- : underline showing affected range
1264 This can become confusing if they overlap, and so we need
1265 to do some preprocessing to decide what to print.
1266 We use the list of fixit_hint instances affecting the line
1267 to build a list of "correction" instances, and print the
1270 For example, consider a set of fix-its for converting
1271 a C-style cast to a C++ const_cast.
1275 ..000000000111111111122222222223333333333.
1276 ..123456789012345678901234567890123456789.
1277 foo *f = (foo *)ptr->field;
1280 and the fix-it hints:
1281 - replace col 10 (the open paren) with "const_cast<"
1282 - replace col 16 (the close paren) with "> ("
1283 - insert ")" before col 27
1285 then we would get odd-looking output:
1287 foo *f = (foo *)ptr->field;
1294 It would be better to detect when fixit hints are going to
1295 overlap (those that require new lines), and to consolidate
1296 the printing of such fixits, giving something like:
1298 foo *f = (foo *)ptr->field;
1301 const_cast<foo *> (ptr->field)
1303 This works by detecting when the printing would overlap, and
1304 effectively injecting no-op replace hints into the gaps between
1305 such fix-its, so that the printing joins up.
1307 In the above example, the overlap of:
1308 - replace col 10 (the open paren) with "const_cast<"
1310 - replace col 16 (the close paren) with "> ("
1311 is fixed by injecting a no-op:
1312 - replace cols 11-15 with themselves ("foo *")
1313 and consolidating these, making:
1314 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1316 - replace cols 10-16 with "const_cast<foo *> ("
1318 This overlaps with the final fix-it hint:
1319 - insert ")" before col 27
1320 and so we repeat the consolidation process, by injecting
1322 - replace cols 17-26 with themselves ("ptr->field")
1324 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1326 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1328 and is thus printed as desired. */
1330 /* A range of columns within a line. */
1334 column_range (int start_
, int finish_
) : start (start_
), finish (finish_
) {}
1336 bool operator== (const column_range
&other
) const
1338 return start
== other
.start
&& finish
== other
.finish
;
1345 /* Get the range of columns that HINT would affect. */
1348 get_affected_columns (const fixit_hint
*hint
)
1350 int start_column
= LOCATION_COLUMN (hint
->get_start_loc ());
1351 int finish_column
= LOCATION_COLUMN (hint
->get_next_loc ()) - 1;
1353 return column_range (start_column
, finish_column
);
1356 /* Get the range of columns that would be printed for HINT. */
1359 get_printed_columns (const fixit_hint
*hint
)
1361 int start_column
= LOCATION_COLUMN (hint
->get_start_loc ());
1362 int final_hint_column
= start_column
+ hint
->get_length () - 1;
1363 if (hint
->insertion_p ())
1365 return column_range (start_column
, final_hint_column
);
1369 int finish_column
= LOCATION_COLUMN (hint
->get_next_loc ()) - 1;
1371 return column_range (start_column
,
1372 MAX (finish_column
, final_hint_column
));
1376 /* A correction on a particular line.
1377 This describes a plan for how to print one or more fixit_hint
1378 instances that affected the line, potentially consolidating hints
1379 into corrections to make the result easier for the user to read. */
1383 correction (column_range affected_columns
,
1384 column_range printed_columns
,
1385 const char *new_text
, size_t new_text_len
)
1386 : m_affected_columns (affected_columns
),
1387 m_printed_columns (printed_columns
),
1388 m_text (xstrdup (new_text
)),
1389 m_len (new_text_len
),
1390 m_alloc_sz (new_text_len
+ 1)
1394 ~correction () { free (m_text
); }
1396 bool insertion_p () const
1398 return m_affected_columns
.start
== m_affected_columns
.finish
+ 1;
1401 void ensure_capacity (size_t len
);
1402 void ensure_terminated ();
1404 /* If insert, then start: the column before which the text
1405 is to be inserted, and finish is offset by the length of
1407 If replace, then the range of columns affected. */
1408 column_range m_affected_columns
;
1410 /* If insert, then start: the column before which the text
1411 is to be inserted, and finish is offset by the length of
1413 If replace, then the range of columns affected. */
1414 column_range m_printed_columns
;
1416 /* The text to be inserted/used as replacement. */
1422 /* Ensure that m_text can hold a string of length LEN
1423 (plus 1 for 0-termination). */
1426 correction::ensure_capacity (size_t len
)
1428 /* Allow 1 extra byte for 0-termination. */
1429 if (m_alloc_sz
< (len
+ 1))
1431 size_t new_alloc_sz
= (len
+ 1) * 2;
1432 m_text
= (char *)xrealloc (m_text
, new_alloc_sz
);
1433 m_alloc_sz
= new_alloc_sz
;
1437 /* Ensure that m_text is 0-terminated. */
1440 correction::ensure_terminated ()
1442 /* 0-terminate the buffer. */
1443 gcc_assert (m_len
< m_alloc_sz
);
1444 m_text
[m_len
] = '\0';
1447 /* A list of corrections affecting a particular line.
1448 This is used by layout::print_trailing_fixits for planning
1449 how to print the fix-it hints affecting the line. */
1451 struct line_corrections
1453 line_corrections (const char *filename
, int row
)
1454 : m_filename (filename
), m_row (row
)
1456 ~line_corrections ();
1458 void add_hint (const fixit_hint
*hint
);
1460 const char *m_filename
;
1462 auto_vec
<correction
*> m_corrections
;
1465 /* struct line_corrections. */
1467 line_corrections::~line_corrections ()
1471 FOR_EACH_VEC_ELT (m_corrections
, i
, c
)
1475 /* Add HINT to the corrections for this line.
1476 Attempt to consolidate nearby hints so that they will not
1477 overlap with printed. */
1480 line_corrections::add_hint (const fixit_hint
*hint
)
1482 column_range affected_columns
= get_affected_columns (hint
);
1483 column_range printed_columns
= get_printed_columns (hint
);
1485 /* Potentially consolidate. */
1486 if (!m_corrections
.is_empty ())
1488 correction
*last_correction
1489 = m_corrections
[m_corrections
.length () - 1];
1490 if (printed_columns
.start
<= last_correction
->m_printed_columns
.finish
)
1492 /* We have two hints for which the printed forms of the hints
1493 would touch or overlap, so we need to consolidate them to avoid
1495 Attempt to inject a "replace" correction from immediately
1496 after the end of the last hint to immediately before the start
1497 of the next hint. */
1498 column_range
between (last_correction
->m_affected_columns
.finish
+ 1,
1499 printed_columns
.start
- 1);
1501 /* Try to read the source. */
1503 const char *line
= location_get_source_line (m_filename
, m_row
,
1505 if (line
&& between
.finish
< line_width
)
1507 /* Consolidate into the last correction:
1508 add a no-op "replace" of the "between" text, and
1509 add the text from the new hint. */
1510 size_t old_len
= last_correction
->m_len
;
1511 size_t between_len
= between
.finish
+ 1 - between
.start
;
1512 size_t new_len
= old_len
+ between_len
+ hint
->get_length ();
1513 last_correction
->ensure_capacity (new_len
);
1514 memcpy (last_correction
->m_text
+ old_len
,
1515 line
+ between
.start
- 1,
1516 between
.finish
+ 1 - between
.start
);
1517 memcpy (last_correction
->m_text
+ old_len
+ between_len
,
1518 hint
->get_string (), hint
->get_length ());
1519 last_correction
->m_len
= new_len
;
1520 last_correction
->ensure_terminated ();
1521 last_correction
->m_affected_columns
.finish
1522 = affected_columns
.finish
;
1523 last_correction
->m_printed_columns
.finish
1524 += between_len
+ hint
->get_length ();
1530 /* If no consolidation happened, add a new correction instance. */
1531 m_corrections
.safe_push (new correction (affected_columns
,
1533 hint
->get_string (),
1534 hint
->get_length ()));
1537 /* If there are any fixit hints on source line ROW, print them.
1538 They are printed in order, attempting to combine them onto lines, but
1539 starting new lines if necessary.
1540 Fix-it hints that insert new lines are handled separately,
1541 in layout::print_leading_fixits. */
1544 layout::print_trailing_fixits (int row
)
1546 /* Build a list of correction instances for the line,
1547 potentially consolidating hints (for the sake of readability). */
1548 line_corrections
corrections (m_exploc
.file
, row
);
1549 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1551 const fixit_hint
*hint
= m_fixit_hints
[i
];
1553 /* Newline fixits are handled by layout::print_leading_fixits. */
1554 if (hint
->ends_with_newline_p ())
1557 if (hint
->affects_line_p (m_exploc
.file
, row
))
1558 corrections
.add_hint (hint
);
1561 /* Now print the corrections. */
1566 FOR_EACH_VEC_ELT (corrections
.m_corrections
, i
, c
)
1568 /* For now we assume each fixit hint can only touch one line. */
1569 if (c
->insertion_p ())
1571 /* This assumes the insertion just affects one line. */
1572 int start_column
= c
->m_printed_columns
.start
;
1573 move_to_column (&column
, start_column
);
1574 m_colorizer
.set_fixit_insert ();
1575 pp_string (m_pp
, c
->m_text
);
1576 m_colorizer
.set_normal_text ();
1581 /* If the range of the replacement wasn't printed in the
1582 annotation line, then print an extra underline to
1583 indicate exactly what is being replaced.
1584 Always show it for removals. */
1585 int start_column
= c
->m_affected_columns
.start
;
1586 int finish_column
= c
->m_affected_columns
.finish
;
1587 if (!annotation_line_showed_range_p (row
, start_column
,
1591 move_to_column (&column
, start_column
);
1592 m_colorizer
.set_fixit_delete ();
1593 for (; column
<= finish_column
; column
++)
1594 pp_character (m_pp
, '-');
1595 m_colorizer
.set_normal_text ();
1597 /* Print the replacement text. REPLACE also covers
1598 removals, so only do this extra work (potentially starting
1599 a new line) if we have actual replacement text. */
1602 move_to_column (&column
, start_column
);
1603 m_colorizer
.set_fixit_insert ();
1604 pp_string (m_pp
, c
->m_text
);
1605 m_colorizer
.set_normal_text ();
1611 /* Add a trailing newline, if necessary. */
1612 move_to_column (&column
, 0);
1615 /* Disable any colorization and emit a newline. */
1618 layout::print_newline ()
1620 m_colorizer
.set_normal_text ();
1624 /* Return true if (ROW/COLUMN) is within a range of the layout.
1625 If it returns true, OUT_STATE is written to, with the
1626 range index, and whether we should draw the caret at
1627 (ROW/COLUMN) (as opposed to an underline). */
1630 layout::get_state_at_point (/* Inputs. */
1631 int row
, int column
,
1632 int first_non_ws
, int last_non_ws
,
1634 point_state
*out_state
)
1636 layout_range
*range
;
1638 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1640 if (range
->contains_point (row
, column
))
1642 out_state
->range_idx
= i
;
1644 /* Are we at the range's caret? is it visible? */
1645 out_state
->draw_caret_p
= false;
1646 if (range
->m_show_caret_p
1647 && row
== range
->m_caret
.m_line
1648 && column
== range
->m_caret
.m_column
)
1649 out_state
->draw_caret_p
= true;
1651 /* Within a multiline range, don't display any underline
1652 in any leading or trailing whitespace on a line.
1653 We do display carets, however. */
1654 if (!out_state
->draw_caret_p
)
1655 if (column
< first_non_ws
|| column
> last_non_ws
)
1658 /* We are within a range. */
1666 /* Helper function for use by layout::print_line when printing the
1667 annotation line under the source line.
1668 Get the column beyond the rightmost one that could contain a caret or
1669 range marker, given that we stop rendering at trailing whitespace.
1670 ROW is the source line within the given file.
1671 CARET_COLUMN is the column of range 0's caret.
1672 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1673 character of source (as determined when printing the source line). */
1676 layout::get_x_bound_for_row (int row
, int caret_column
,
1677 int last_non_ws_column
)
1679 int result
= caret_column
+ 1;
1681 layout_range
*range
;
1683 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1685 if (row
>= range
->m_start
.m_line
)
1687 if (range
->m_finish
.m_line
== row
)
1689 /* On the final line within a range; ensure that
1690 we render up to the end of the range. */
1691 if (result
<= range
->m_finish
.m_column
)
1692 result
= range
->m_finish
.m_column
+ 1;
1694 else if (row
< range
->m_finish
.m_line
)
1696 /* Within a multiline range; ensure that we render up to the
1697 last non-whitespace column. */
1698 if (result
<= last_non_ws_column
)
1699 result
= last_non_ws_column
+ 1;
1707 /* Given *COLUMN as an x-coordinate, print spaces to position
1708 successive output at DEST_COLUMN, printing a newline if necessary,
1709 and updating *COLUMN. */
1712 layout::move_to_column (int *column
, int dest_column
)
1714 /* Start a new line if we need to. */
1715 if (*column
> dest_column
)
1721 while (*column
< dest_column
)
1728 /* For debugging layout issues, render a ruler giving column numbers
1729 (after the 1-column indent). */
1732 layout::show_ruler (int max_column
) const
1735 if (max_column
> 99)
1738 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1739 if (0 == column
% 10)
1740 pp_character (m_pp
, '0' + (column
/ 100) % 10);
1748 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1749 if (0 == column
% 10)
1750 pp_character (m_pp
, '0' + (column
/ 10) % 10);
1757 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1758 pp_character (m_pp
, '0' + (column
% 10));
1762 /* Print leading fix-its (for new lines inserted before the source line)
1763 then the source line, followed by an annotation line
1764 consisting of any caret/underlines, then any fixits.
1765 If the source line can't be read, print nothing. */
1767 layout::print_line (int row
)
1770 const char *line
= location_get_source_line (m_exploc
.file
, row
,
1775 line_bounds lbounds
;
1776 print_leading_fixits (row
);
1777 print_source_line (row
, line
, line_width
, &lbounds
);
1778 if (should_print_annotation_line_p (row
))
1779 print_annotation_line (row
, lbounds
);
1780 print_trailing_fixits (row
);
1783 } /* End of anonymous namespace. */
1785 /* Print the physical source code corresponding to the location of
1786 this diagnostic, with additional annotations. */
1789 diagnostic_show_locus (diagnostic_context
* context
,
1790 rich_location
*richloc
,
1791 diagnostic_t diagnostic_kind
)
1793 pp_newline (context
->printer
);
1795 location_t loc
= richloc
->get_loc ();
1796 /* Do nothing if source-printing has been disabled. */
1797 if (!context
->show_caret
)
1800 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1801 if (loc
<= BUILTINS_LOCATION
)
1804 /* Don't print the same source location twice in a row, unless we have
1806 if (loc
== context
->last_location
1807 && richloc
->get_num_fixit_hints () == 0)
1810 context
->last_location
= loc
;
1812 const char *saved_prefix
= pp_get_prefix (context
->printer
);
1813 pp_set_prefix (context
->printer
, NULL
);
1815 layout
layout (context
, richloc
, diagnostic_kind
);
1816 for (int line_span_idx
= 0; line_span_idx
< layout
.get_num_line_spans ();
1819 const line_span
*line_span
= layout
.get_line_span (line_span_idx
);
1820 if (layout
.print_heading_for_line_span_index_p (line_span_idx
))
1822 expanded_location exploc
= layout
.get_expanded_location (line_span
);
1823 context
->start_span (context
, exploc
);
1825 int last_line
= line_span
->get_last_line ();
1826 for (int row
= line_span
->get_first_line (); row
<= last_line
; row
++)
1827 layout
.print_line (row
);
1830 pp_set_prefix (context
->printer
, saved_prefix
);
1835 namespace selftest
{
1837 /* Selftests for diagnostic_show_locus. */
1839 /* Convenience subclass of diagnostic_context for testing
1840 diagnostic_show_locus. */
1842 class test_diagnostic_context
: public diagnostic_context
1845 test_diagnostic_context ()
1847 diagnostic_initialize (this, 0);
1850 start_span
= start_span_cb
;
1852 ~test_diagnostic_context ()
1854 diagnostic_finish (this);
1857 /* Implementation of diagnostic_start_span_fn, hiding the
1858 real filename (to avoid printing the names of tempfiles). */
1860 start_span_cb (diagnostic_context
*context
, expanded_location exploc
)
1862 exploc
.file
= "FILENAME";
1863 default_diagnostic_start_span_fn (context
, exploc
);
1867 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1870 test_diagnostic_show_locus_unknown_location ()
1872 test_diagnostic_context dc
;
1873 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
1874 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1875 ASSERT_STREQ ("\n", pp_formatted_text (dc
.printer
));
1878 /* Verify that diagnostic_show_locus works sanely for various
1881 All of these work on the following 1-line source file:
1884 "foo = bar.field;\n"
1885 which is set up by test_diagnostic_show_locus_one_liner and calls
1891 test_one_liner_simple_caret ()
1893 test_diagnostic_context dc
;
1894 location_t caret
= linemap_position_for_column (line_table
, 10);
1895 rich_location
richloc (line_table
, caret
);
1896 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1898 " foo = bar.field;\n"
1900 pp_formatted_text (dc
.printer
));
1903 /* Caret and range. */
1906 test_one_liner_caret_and_range ()
1908 test_diagnostic_context dc
;
1909 location_t caret
= linemap_position_for_column (line_table
, 10);
1910 location_t start
= linemap_position_for_column (line_table
, 7);
1911 location_t finish
= linemap_position_for_column (line_table
, 15);
1912 location_t loc
= make_location (caret
, start
, finish
);
1913 rich_location
richloc (line_table
, loc
);
1914 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1916 " foo = bar.field;\n"
1918 pp_formatted_text (dc
.printer
));
1921 /* Multiple ranges and carets. */
1924 test_one_liner_multiple_carets_and_ranges ()
1926 test_diagnostic_context dc
;
1928 = make_location (linemap_position_for_column (line_table
, 2),
1929 linemap_position_for_column (line_table
, 1),
1930 linemap_position_for_column (line_table
, 3));
1931 dc
.caret_chars
[0] = 'A';
1934 = make_location (linemap_position_for_column (line_table
, 8),
1935 linemap_position_for_column (line_table
, 7),
1936 linemap_position_for_column (line_table
, 9));
1937 dc
.caret_chars
[1] = 'B';
1940 = make_location (linemap_position_for_column (line_table
, 13),
1941 linemap_position_for_column (line_table
, 11),
1942 linemap_position_for_column (line_table
, 15));
1943 dc
.caret_chars
[2] = 'C';
1945 rich_location
richloc (line_table
, foo
);
1946 richloc
.add_range (bar
, true);
1947 richloc
.add_range (field
, true);
1948 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1950 " foo = bar.field;\n"
1952 pp_formatted_text (dc
.printer
));
1955 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1958 test_one_liner_fixit_insert_before ()
1960 test_diagnostic_context dc
;
1961 location_t caret
= linemap_position_for_column (line_table
, 7);
1962 rich_location
richloc (line_table
, caret
);
1963 richloc
.add_fixit_insert_before ("&");
1964 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1966 " foo = bar.field;\n"
1969 pp_formatted_text (dc
.printer
));
1972 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1975 test_one_liner_fixit_insert_after ()
1977 test_diagnostic_context dc
;
1978 location_t start
= linemap_position_for_column (line_table
, 1);
1979 location_t finish
= linemap_position_for_column (line_table
, 3);
1980 location_t foo
= make_location (start
, start
, finish
);
1981 rich_location
richloc (line_table
, foo
);
1982 richloc
.add_fixit_insert_after ("[0]");
1983 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1985 " foo = bar.field;\n"
1988 pp_formatted_text (dc
.printer
));
1991 /* Removal fix-it hint: removal of the ".field". */
1994 test_one_liner_fixit_remove ()
1996 test_diagnostic_context dc
;
1997 location_t start
= linemap_position_for_column (line_table
, 10);
1998 location_t finish
= linemap_position_for_column (line_table
, 15);
1999 location_t dot
= make_location (start
, start
, finish
);
2000 rich_location
richloc (line_table
, dot
);
2001 richloc
.add_fixit_remove ();
2002 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2004 " foo = bar.field;\n"
2007 pp_formatted_text (dc
.printer
));
2010 /* Replace fix-it hint: replacing "field" with "m_field". */
2013 test_one_liner_fixit_replace ()
2015 test_diagnostic_context dc
;
2016 location_t start
= linemap_position_for_column (line_table
, 11);
2017 location_t finish
= linemap_position_for_column (line_table
, 15);
2018 location_t field
= make_location (start
, start
, finish
);
2019 rich_location
richloc (line_table
, field
);
2020 richloc
.add_fixit_replace ("m_field");
2021 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2023 " foo = bar.field;\n"
2026 pp_formatted_text (dc
.printer
));
2029 /* Replace fix-it hint: replacing "field" with "m_field",
2030 but where the caret was elsewhere. */
2033 test_one_liner_fixit_replace_non_equal_range ()
2035 test_diagnostic_context dc
;
2036 location_t equals
= linemap_position_for_column (line_table
, 5);
2037 location_t start
= linemap_position_for_column (line_table
, 11);
2038 location_t finish
= linemap_position_for_column (line_table
, 15);
2039 rich_location
richloc (line_table
, equals
);
2041 range
.m_start
= start
;
2042 range
.m_finish
= finish
;
2043 richloc
.add_fixit_replace (range
, "m_field");
2044 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2045 /* The replacement range is not indicated in the annotation line, so
2046 it should be indicated via an additional underline. */
2048 " foo = bar.field;\n"
2052 pp_formatted_text (dc
.printer
));
2055 /* Replace fix-it hint: replacing "field" with "m_field",
2056 where the caret was elsewhere, but where a secondary range
2057 exactly covers "field". */
2060 test_one_liner_fixit_replace_equal_secondary_range ()
2062 test_diagnostic_context dc
;
2063 location_t equals
= linemap_position_for_column (line_table
, 5);
2064 location_t start
= linemap_position_for_column (line_table
, 11);
2065 location_t finish
= linemap_position_for_column (line_table
, 15);
2066 rich_location
richloc (line_table
, equals
);
2067 location_t field
= make_location (start
, start
, finish
);
2068 richloc
.add_range (field
, false);
2069 richloc
.add_fixit_replace (field
, "m_field");
2070 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2071 /* The replacement range is indicated in the annotation line,
2072 so it shouldn't be indicated via an additional underline. */
2074 " foo = bar.field;\n"
2077 pp_formatted_text (dc
.printer
));
2080 /* Verify that we can use ad-hoc locations when adding fixits to a
2084 test_one_liner_fixit_validation_adhoc_locations ()
2086 /* Generate a range that's too long to be packed, so must
2087 be stored as an ad-hoc location (given the defaults
2088 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2089 const location_t c7
= linemap_position_for_column (line_table
, 7);
2090 const location_t c47
= linemap_position_for_column (line_table
, 47);
2091 const location_t loc
= make_location (c7
, c7
, c47
);
2093 if (c47
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2096 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
2100 rich_location
richloc (line_table
, loc
);
2101 richloc
.add_fixit_insert_before (loc
, "test");
2102 /* It should not have been discarded by the validator. */
2103 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2105 test_diagnostic_context dc
;
2106 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2108 " foo = bar.field;\n"
2111 pp_formatted_text (dc
.printer
));
2116 rich_location
richloc (line_table
, loc
);
2117 source_range range
= source_range::from_locations (loc
, c47
);
2118 richloc
.add_fixit_remove (range
);
2119 /* It should not have been discarded by the validator. */
2120 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2122 test_diagnostic_context dc
;
2123 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2125 " foo = bar.field;\n"
2127 " -----------------------------------------\n",
2128 pp_formatted_text (dc
.printer
));
2133 rich_location
richloc (line_table
, loc
);
2134 source_range range
= source_range::from_locations (loc
, c47
);
2135 richloc
.add_fixit_replace (range
, "test");
2136 /* It should not have been discarded by the validator. */
2137 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2139 test_diagnostic_context dc
;
2140 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2142 " foo = bar.field;\n"
2145 pp_formatted_text (dc
.printer
));
2149 /* Test of consolidating insertions at the same location. */
2152 test_one_liner_many_fixits_1 ()
2154 test_diagnostic_context dc
;
2155 location_t equals
= linemap_position_for_column (line_table
, 5);
2156 rich_location
richloc (line_table
, equals
);
2157 for (int i
= 0; i
< 19; i
++)
2158 richloc
.add_fixit_insert_before ("a");
2159 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2160 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2162 " foo = bar.field;\n"
2164 " aaaaaaaaaaaaaaaaaaa\n",
2165 pp_formatted_text (dc
.printer
));
2168 /* Ensure that we can add an arbitrary number of fix-it hints to a
2169 rich_location, even if they are not consolidated. */
2172 test_one_liner_many_fixits_2 ()
2174 test_diagnostic_context dc
;
2175 location_t equals
= linemap_position_for_column (line_table
, 5);
2176 rich_location
richloc (line_table
, equals
);
2177 for (int i
= 0; i
< 19; i
++)
2179 location_t loc
= linemap_position_for_column (line_table
, i
* 2);
2180 richloc
.add_fixit_insert_before (loc
, "a");
2182 ASSERT_EQ (19, richloc
.get_num_fixit_hints ());
2183 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2185 " foo = bar.field;\n"
2187 "a a a a a a a a a a a a a a a a a a a\n",
2188 pp_formatted_text (dc
.printer
));
2191 /* Run the various one-liner tests. */
2194 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
2196 /* Create a tempfile and write some text to it.
2197 ....................0000000001111111.
2198 ....................1234567890123456. */
2199 const char *content
= "foo = bar.field;\n";
2200 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2201 line_table_test
ltt (case_
);
2203 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2205 location_t line_end
= linemap_position_for_column (line_table
, 16);
2207 /* Don't attempt to run the tests if column data might be unavailable. */
2208 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2211 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
2212 ASSERT_EQ (1, LOCATION_LINE (line_end
));
2213 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
2215 test_one_liner_simple_caret ();
2216 test_one_liner_caret_and_range ();
2217 test_one_liner_multiple_carets_and_ranges ();
2218 test_one_liner_fixit_insert_before ();
2219 test_one_liner_fixit_insert_after ();
2220 test_one_liner_fixit_remove ();
2221 test_one_liner_fixit_replace ();
2222 test_one_liner_fixit_replace_non_equal_range ();
2223 test_one_liner_fixit_replace_equal_secondary_range ();
2224 test_one_liner_fixit_validation_adhoc_locations ();
2225 test_one_liner_many_fixits_1 ();
2226 test_one_liner_many_fixits_2 ();
2229 /* Verify that we print fixits even if they only affect lines
2230 outside those covered by the ranges in the rich_location. */
2233 test_diagnostic_show_locus_fixit_lines (const line_table_case
&case_
)
2235 /* Create a tempfile and write some text to it.
2236 ...000000000111111111122222222223333333333.
2237 ...123456789012345678901234567890123456789. */
2239 = ("struct point { double x; double y; };\n" /* line 1. */
2240 "struct point origin = {x: 0.0,\n" /* line 2. */
2241 " y\n" /* line 3. */
2244 " : 0.0};\n"); /* line 6. */
2245 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2246 line_table_test
ltt (case_
);
2248 const line_map_ordinary
*ord_map
2249 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
2250 tmp
.get_filename (), 0));
2252 linemap_line_start (line_table
, 1, 100);
2254 const location_t final_line_end
2255 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
2257 /* Don't attempt to run the tests if column data might be unavailable. */
2258 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2261 /* A pair of tests for modernizing the initializers to C99-style. */
2263 /* The one-liner case (line 2). */
2265 test_diagnostic_context dc
;
2267 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 24);
2268 const location_t colon
2269 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 25);
2270 rich_location
richloc (line_table
, colon
);
2271 richloc
.add_fixit_insert_before (x
, ".");
2272 richloc
.add_fixit_replace (colon
, "=");
2273 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2275 " struct point origin = {x: 0.0,\n"
2278 pp_formatted_text (dc
.printer
));
2281 /* The multiline case. The caret for the rich_location is on line 6;
2282 verify that insertion fixit on line 3 is still printed (and that
2283 span starts are printed due to the gap between the span at line 3
2284 and that at line 6). */
2286 test_diagnostic_context dc
;
2288 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
2289 const location_t colon
2290 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
2291 rich_location
richloc (line_table
, colon
);
2292 richloc
.add_fixit_insert_before (y
, ".");
2293 richloc
.add_fixit_replace (colon
, "=");
2294 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2303 pp_formatted_text (dc
.printer
));
2308 /* Verify that fix-it hints are appropriately consolidated.
2310 If any fix-it hints in a rich_location involve locations beyond
2311 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2312 the fix-it as a whole, so there should be none.
2314 Otherwise, verify that consecutive "replace" and "remove" fix-its
2315 are merged, and that other fix-its remain separate. */
2318 test_fixit_consolidation (const line_table_case
&case_
)
2320 line_table_test
ltt (case_
);
2322 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
2324 const location_t c10
= linemap_position_for_column (line_table
, 10);
2325 const location_t c15
= linemap_position_for_column (line_table
, 15);
2326 const location_t c16
= linemap_position_for_column (line_table
, 16);
2327 const location_t c17
= linemap_position_for_column (line_table
, 17);
2328 const location_t c20
= linemap_position_for_column (line_table
, 20);
2329 const location_t c21
= linemap_position_for_column (line_table
, 21);
2330 const location_t caret
= c10
;
2332 /* Insert + insert. */
2334 rich_location
richloc (line_table
, caret
);
2335 richloc
.add_fixit_insert_before (c10
, "foo");
2336 richloc
.add_fixit_insert_before (c15
, "bar");
2338 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2339 /* Bogus column info for 2nd fixit, so no fixits. */
2340 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2342 /* They should not have been merged. */
2343 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
2346 /* Insert + replace. */
2348 rich_location
richloc (line_table
, caret
);
2349 richloc
.add_fixit_insert_before (c10
, "foo");
2350 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
2353 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2354 /* Bogus column info for 2nd fixit, so no fixits. */
2355 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2357 /* They should not have been merged. */
2358 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
2361 /* Replace + non-consecutive insert. */
2363 rich_location
richloc (line_table
, caret
);
2364 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
2366 richloc
.add_fixit_insert_before (c17
, "foo");
2368 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2369 /* Bogus column info for 2nd fixit, so no fixits. */
2370 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2372 /* They should not have been merged. */
2373 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
2376 /* Replace + non-consecutive replace. */
2378 rich_location
richloc (line_table
, caret
);
2379 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
2381 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
2384 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2385 /* Bogus column info for 2nd fixit, so no fixits. */
2386 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2388 /* They should not have been merged. */
2389 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
2392 /* Replace + consecutive replace. */
2394 rich_location
richloc (line_table
, caret
);
2395 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
2397 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
2400 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2401 /* Bogus column info for 2nd fixit, so no fixits. */
2402 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2405 /* They should have been merged into a single "replace". */
2406 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2407 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
2408 ASSERT_STREQ ("foobar", hint
->get_string ());
2409 ASSERT_EQ (c10
, hint
->get_start_loc ());
2410 ASSERT_EQ (c21
, hint
->get_next_loc ());
2414 /* Replace + consecutive removal. */
2416 rich_location
richloc (line_table
, caret
);
2417 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
2419 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
2421 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2422 /* Bogus column info for 2nd fixit, so no fixits. */
2423 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2426 /* They should have been merged into a single replace, with the
2427 range extended to cover that of the removal. */
2428 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2429 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
2430 ASSERT_STREQ ("foo", hint
->get_string ());
2431 ASSERT_EQ (c10
, hint
->get_start_loc ());
2432 ASSERT_EQ (c21
, hint
->get_next_loc ());
2436 /* Consecutive removals. */
2438 rich_location
richloc (line_table
, caret
);
2439 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
2440 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
2442 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2443 /* Bogus column info for 2nd fixit, so no fixits. */
2444 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
2447 /* They should have been merged into a single "replace-with-empty". */
2448 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2449 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
2450 ASSERT_STREQ ("", hint
->get_string ());
2451 ASSERT_EQ (c10
, hint
->get_start_loc ());
2452 ASSERT_EQ (c21
, hint
->get_next_loc ());
2457 /* Verify that the line_corrections machinery correctly prints
2458 overlapping fixit-hints. */
2461 test_overlapped_fixit_printing (const line_table_case
&case_
)
2463 /* Create a tempfile and write some text to it.
2464 ...000000000111111111122222222223333333333.
2465 ...123456789012345678901234567890123456789. */
2467 = (" foo *f = (foo *)ptr->field;\n");
2468 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
2469 line_table_test
ltt (case_
);
2471 const line_map_ordinary
*ord_map
2472 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
2473 tmp
.get_filename (), 0));
2475 linemap_line_start (line_table
, 1, 100);
2477 const location_t final_line_end
2478 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
2480 /* Don't attempt to run the tests if column data might be unavailable. */
2481 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2484 /* A test for converting a C-style cast to a C++-style cast. */
2485 const location_t open_paren
2486 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 12);
2487 const location_t close_paren
2488 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
2489 const location_t expr_start
2490 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 19);
2491 const location_t expr_finish
2492 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 28);
2493 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
2495 /* Various examples of fix-it hints that aren't themselves consolidated,
2496 but for which the *printing* may need consolidation. */
2498 /* Example where 3 fix-it hints are printed as one. */
2500 test_diagnostic_context dc
;
2501 rich_location
richloc (line_table
, expr
);
2502 richloc
.add_fixit_replace (open_paren
, "const_cast<");
2503 richloc
.add_fixit_replace (close_paren
, "> (");
2504 richloc
.add_fixit_insert_after (")");
2506 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2508 " foo *f = (foo *)ptr->field;\n"
2510 " -----------------\n"
2511 " const_cast<foo *> (ptr->field)\n",
2512 pp_formatted_text (dc
.printer
));
2514 /* Unit-test the line_corrections machinery. */
2515 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
2516 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
2517 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0
));
2518 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0
));
2519 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
2520 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1
));
2521 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1
));
2522 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
2523 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2
));
2524 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2
));
2526 /* Add each hint in turn to a line_corrections instance,
2527 and verify that they are consolidated into one correction instance
2529 line_corrections
lc (tmp
.get_filename (), 1);
2531 /* The first replace hint by itself. */
2532 lc
.add_hint (hint_0
);
2533 ASSERT_EQ (1, lc
.m_corrections
.length ());
2534 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
2535 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
2536 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
2538 /* After the second replacement hint, they are printed together
2539 as a replacement (along with the text between them). */
2540 lc
.add_hint (hint_1
);
2541 ASSERT_EQ (1, lc
.m_corrections
.length ());
2542 ASSERT_STREQ ("const_cast<foo *> (", lc
.m_corrections
[0]->m_text
);
2543 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
2544 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
2546 /* After the final insertion hint, they are all printed together
2547 as a replacement (along with the text between them). */
2548 lc
.add_hint (hint_2
);
2549 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2550 lc
.m_corrections
[0]->m_text
);
2551 ASSERT_EQ (1, lc
.m_corrections
.length ());
2552 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_columns
);
2553 ASSERT_EQ (column_range (12, 41), lc
.m_corrections
[0]->m_printed_columns
);
2556 /* Example where two are consolidated during printing. */
2558 test_diagnostic_context dc
;
2559 rich_location
richloc (line_table
, expr
);
2560 richloc
.add_fixit_replace (open_paren
, "CAST (");
2561 richloc
.add_fixit_replace (close_paren
, ") (");
2562 richloc
.add_fixit_insert_after (")");
2564 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2566 " foo *f = (foo *)ptr->field;\n"
2571 pp_formatted_text (dc
.printer
));
2574 /* Example where none are consolidated during printing. */
2576 test_diagnostic_context dc
;
2577 rich_location
richloc (line_table
, expr
);
2578 richloc
.add_fixit_replace (open_paren
, "CST (");
2579 richloc
.add_fixit_replace (close_paren
, ") (");
2580 richloc
.add_fixit_insert_after (")");
2582 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2584 " foo *f = (foo *)ptr->field;\n"
2589 pp_formatted_text (dc
.printer
));
2592 /* Example of deletion fix-it hints. */
2594 test_diagnostic_context dc
;
2595 rich_location
richloc (line_table
, expr
);
2596 richloc
.add_fixit_insert_before (open_paren
, "(bar *)");
2597 source_range victim
= {open_paren
, close_paren
};
2598 richloc
.add_fixit_remove (victim
);
2600 /* This case is actually handled by fixit-consolidation,
2601 rather than by line_corrections. */
2602 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
2604 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2606 " foo *f = (foo *)ptr->field;\n"
2610 pp_formatted_text (dc
.printer
));
2613 /* Example of deletion fix-it hints that would overlap. */
2615 test_diagnostic_context dc
;
2616 rich_location
richloc (line_table
, expr
);
2617 richloc
.add_fixit_insert_before (open_paren
, "(longer *)");
2618 source_range victim
= {expr_start
, expr_finish
};
2619 richloc
.add_fixit_remove (victim
);
2621 /* These fixits are not consolidated. */
2622 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
2624 /* But the corrections are. */
2625 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2627 " foo *f = (foo *)ptr->field;\n"
2629 " -----------------\n"
2630 " (longer *)(foo *)\n",
2631 pp_formatted_text (dc
.printer
));
2634 /* Example of insertion fix-it hints that would overlap. */
2636 test_diagnostic_context dc
;
2637 rich_location
richloc (line_table
, expr
);
2638 richloc
.add_fixit_insert_before (open_paren
, "LONGER THAN THE CAST");
2639 richloc
.add_fixit_insert_after (close_paren
, "TEST");
2641 /* The first insertion is long enough that if printed naively,
2642 it would overlap with the second.
2643 Verify that they are printed as a single replacement. */
2644 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2646 " foo *f = (foo *)ptr->field;\n"
2649 " LONGER THAN THE CAST(foo *)TEST\n",
2650 pp_formatted_text (dc
.printer
));
2654 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2657 test_fixit_insert_containing_newline (const line_table_case
&case_
)
2659 /* Create a tempfile and write some text to it.
2660 .........................0000000001111111.
2661 .........................1234567890123456. */
2662 const char *old_content
= (" case 'a':\n" /* line 1. */
2663 " x = a;\n" /* line 2. */
2664 " case 'b':\n" /* line 3. */
2665 " x = b;\n");/* line 4. */
2667 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
2668 line_table_test
ltt (case_
);
2669 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
2671 location_t case_start
= linemap_position_for_column (line_table
, 5);
2672 location_t case_finish
= linemap_position_for_column (line_table
, 13);
2673 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
2674 location_t line_start
= linemap_position_for_column (line_table
, 1);
2676 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2679 /* Add a "break;" on a line by itself before line 3 i.e. before
2680 column 1 of line 3. */
2682 rich_location
richloc (line_table
, case_loc
);
2683 richloc
.add_fixit_insert_before (line_start
, " break;\n");
2684 test_diagnostic_context dc
;
2685 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2690 pp_formatted_text (dc
.printer
));
2693 /* Verify that attempts to add text with a newline fail when the
2694 insertion point is *not* at the start of a line. */
2696 rich_location
richloc (line_table
, case_loc
);
2697 richloc
.add_fixit_insert_before (case_start
, "break;\n");
2698 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
2699 test_diagnostic_context dc
;
2700 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2704 pp_formatted_text (dc
.printer
));
2708 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
2709 of the file, where the fix-it is printed in a different line-span
2710 to the primary range of the diagnostic. */
2713 test_fixit_insert_containing_newline_2 (const line_table_case
&case_
)
2715 /* Create a tempfile and write some text to it.
2716 .........................0000000001111111.
2717 .........................1234567890123456. */
2718 const char *old_content
= ("test (int ch)\n" /* line 1. */
2720 " putchar (ch);\n" /* line 3. */
2721 "}\n"); /* line 4. */
2723 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
2724 line_table_test
ltt (case_
);
2726 const line_map_ordinary
*ord_map
= linemap_check_ordinary
2727 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
2728 linemap_line_start (line_table
, 1, 100);
2730 /* The primary range is the "putchar" token. */
2731 location_t putchar_start
2732 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 2);
2733 location_t putchar_finish
2734 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 8);
2735 location_t putchar_loc
2736 = make_location (putchar_start
, putchar_start
, putchar_finish
);
2737 rich_location
richloc (line_table
, putchar_loc
);
2739 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
2740 location_t file_start
2741 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
2742 richloc
.add_fixit_insert_before (file_start
, "#include <stdio.h>\n");
2744 if (putchar_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2747 test_diagnostic_context dc
;
2748 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2751 "+#include <stdio.h>\n"
2756 pp_formatted_text (dc
.printer
));
2759 /* Replacement fix-it hint containing a newline.
2760 This will fail, as newlines are only supported when inserting at the
2761 beginning of a line. */
2764 test_fixit_replace_containing_newline (const line_table_case
&case_
)
2766 /* Create a tempfile and write some text to it.
2767 .........................0000000001111.
2768 .........................1234567890123. */
2769 const char *old_content
= "foo = bar ();\n";
2771 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
2772 line_table_test
ltt (case_
);
2773 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2775 /* Replace the " = " with "\n = ", as if we were reformatting an
2776 overly long line. */
2777 location_t start
= linemap_position_for_column (line_table
, 4);
2778 location_t finish
= linemap_position_for_column (line_table
, 6);
2779 location_t loc
= linemap_position_for_column (line_table
, 13);
2780 rich_location
richloc (line_table
, loc
);
2781 source_range range
= source_range::from_locations (start
, finish
);
2782 richloc
.add_fixit_replace (range
, "\n =");
2784 /* Arbitrary newlines are not yet supported within fix-it hints, so
2785 the fix-it should not be displayed. */
2786 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
2788 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2791 test_diagnostic_context dc
;
2792 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2796 pp_formatted_text (dc
.printer
));
2799 /* Fix-it hint, attempting to delete a newline.
2800 This will fail, as we currently only support fix-it hints that
2801 affect one line at a time. */
2804 test_fixit_deletion_affecting_newline (const line_table_case
&case_
)
2806 /* Create a tempfile and write some text to it.
2807 ..........................0000000001111.
2808 ..........................1234567890123. */
2809 const char *old_content
= ("foo = bar (\n"
2812 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
2813 line_table_test
ltt (case_
);
2814 const line_map_ordinary
*ord_map
= linemap_check_ordinary
2815 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
2816 linemap_line_start (line_table
, 1, 100);
2818 /* Attempt to delete the " (\n...)". */
2820 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 10);
2822 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
2824 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 7);
2825 location_t loc
= make_location (caret
, start
, finish
);
2826 rich_location
richloc (line_table
, loc
);
2827 richloc
. add_fixit_remove ();
2829 /* Fix-it hints that affect more than one line are not yet supported, so
2830 the fix-it should not be displayed. */
2831 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
2833 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2836 test_diagnostic_context dc
;
2837 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2843 pp_formatted_text (dc
.printer
));
2846 /* Run all of the selftests within this file. */
2849 diagnostic_show_locus_c_tests ()
2851 test_layout_range_for_single_point ();
2852 test_layout_range_for_single_line ();
2853 test_layout_range_for_multiple_lines ();
2855 test_get_line_width_without_trailing_whitespace ();
2857 test_diagnostic_show_locus_unknown_location ();
2859 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
2860 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines
);
2861 for_each_line_table_case (test_fixit_consolidation
);
2862 for_each_line_table_case (test_overlapped_fixit_printing
);
2863 for_each_line_table_case (test_fixit_insert_containing_newline
);
2864 for_each_line_table_case (test_fixit_insert_containing_newline_2
);
2865 for_each_line_table_case (test_fixit_replace_containing_newline
);
2866 for_each_line_table_case (test_fixit_deletion_affecting_newline
);
2869 } // namespace selftest
2871 #endif /* #if CHECKING_P */