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
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;
133 layout_point m_start
;
134 layout_point m_finish
;
136 layout_point m_caret
;
139 /* A struct for use by layout::print_source_line for telling
140 layout::print_annotation_line the extents of the source line that
141 it printed, so that underlines can be clipped appropriately. */
149 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
150 or "line 23"). During the layout ctor, layout::calculate_line_spans
151 splits the pertinent source lines into a list of disjoint line_span
152 instances (e.g. lines 5-10, lines 15-20, line 23). */
156 line_span (linenum_type first_line
, linenum_type last_line
)
157 : m_first_line (first_line
), m_last_line (last_line
)
159 gcc_assert (first_line
<= last_line
);
161 linenum_type
get_first_line () const { return m_first_line
; }
162 linenum_type
get_last_line () const { return m_last_line
; }
164 bool contains_line_p (linenum_type line
) const
166 return line
>= m_first_line
&& line
<= m_last_line
;
169 static int comparator (const void *p1
, const void *p2
)
171 const line_span
*ls1
= (const line_span
*)p1
;
172 const line_span
*ls2
= (const line_span
*)p2
;
173 int first_line_diff
= (int)ls1
->m_first_line
- (int)ls2
->m_first_line
;
175 return first_line_diff
;
176 return (int)ls1
->m_last_line
- (int)ls2
->m_last_line
;
179 linenum_type m_first_line
;
180 linenum_type m_last_line
;
183 /* A class to control the overall layout when printing a diagnostic.
185 The layout is determined within the constructor.
186 It is then printed by repeatedly calling the "print_source_line",
187 "print_annotation_line" and "print_any_fixits" methods.
189 We assume we have disjoint ranges. */
194 layout (diagnostic_context
*context
,
195 rich_location
*richloc
,
196 diagnostic_t diagnostic_kind
);
198 int get_num_line_spans () const { return m_line_spans
.length (); }
199 const line_span
*get_line_span (int idx
) const { return &m_line_spans
[idx
]; }
201 bool print_heading_for_line_span_index_p (int line_span_idx
) const;
203 expanded_location
get_expanded_location (const line_span
*) const;
205 bool print_source_line (int row
, line_bounds
*lbounds_out
);
206 void print_annotation_line (int row
, const line_bounds lbounds
);
207 bool annotation_line_showed_range_p (int line
, int start_column
,
208 int finish_column
) const;
209 void print_any_fixits (int row
, const rich_location
*richloc
);
211 void show_ruler (int max_column
) const;
214 void calculate_line_spans ();
216 void print_newline ();
219 get_state_at_point (/* Inputs. */
221 int first_non_ws
, int last_non_ws
,
223 point_state
*out_state
);
226 get_x_bound_for_row (int row
, int caret_column
,
230 move_to_column (int *column
, int dest_column
);
233 diagnostic_context
*m_context
;
234 pretty_printer
*m_pp
;
235 diagnostic_t m_diagnostic_kind
;
236 expanded_location m_exploc
;
237 colorizer m_colorizer
;
238 bool m_colorize_source_p
;
239 auto_vec
<layout_range
> m_layout_ranges
;
240 auto_vec
<line_span
> m_line_spans
;
244 /* Implementation of "class colorizer". */
246 /* The constructor for "colorizer". Lookup and store color codes for the
247 different kinds of things we might need to print. */
249 colorizer::colorizer (diagnostic_context
*context
,
250 diagnostic_t diagnostic_kind
) :
252 m_diagnostic_kind (diagnostic_kind
),
253 m_current_state (STATE_NORMAL_TEXT
)
255 m_range1
= get_color_by_name ("range1");
256 m_range2
= get_color_by_name ("range2");
257 m_fixit_insert
= get_color_by_name ("fixit-insert");
258 m_fixit_delete
= get_color_by_name ("fixit-delete");
259 m_stop_color
= colorize_stop (pp_show_color (context
->printer
));
262 /* The destructor for "colorize". If colorization is on, print a code to
265 colorizer::~colorizer ()
267 finish_state (m_current_state
);
270 /* Update state, printing color codes if necessary if there's a state
274 colorizer::set_state (int new_state
)
276 if (m_current_state
!= new_state
)
278 finish_state (m_current_state
);
279 m_current_state
= new_state
;
280 begin_state (new_state
);
284 /* Turn on any colorization for STATE. */
287 colorizer::begin_state (int state
)
291 case STATE_NORMAL_TEXT
:
294 case STATE_FIXIT_INSERT
:
295 pp_string (m_context
->printer
, m_fixit_insert
);
298 case STATE_FIXIT_DELETE
:
299 pp_string (m_context
->printer
, m_fixit_delete
);
303 /* Make range 0 be the same color as the "kind" text
304 (error vs warning vs note). */
307 colorize_start (pp_show_color (m_context
->printer
),
308 diagnostic_get_color_for_kind (m_diagnostic_kind
)));
312 pp_string (m_context
->printer
, m_range1
);
316 pp_string (m_context
->printer
, m_range2
);
320 /* We don't expect more than 3 ranges per diagnostic. */
326 /* Turn off any colorization for STATE. */
329 colorizer::finish_state (int state
)
331 if (state
!= STATE_NORMAL_TEXT
)
332 pp_string (m_context
->printer
, m_stop_color
);
335 /* Get the color code for NAME (or the empty string if
336 colorization is disabled). */
339 colorizer::get_color_by_name (const char *name
)
341 return colorize_start (pp_show_color (m_context
->printer
), name
);
344 /* Implementation of class layout_range. */
346 /* The constructor for class layout_range.
347 Initialize various layout_point fields from expanded_location
348 equivalents; we've already filtered on file. */
350 layout_range::layout_range (const expanded_location
*start_exploc
,
351 const expanded_location
*finish_exploc
,
353 const expanded_location
*caret_exploc
)
354 : m_start (*start_exploc
),
355 m_finish (*finish_exploc
),
356 m_show_caret_p (show_caret_p
),
357 m_caret (*caret_exploc
)
361 /* Is (column, row) within the given range?
362 We've already filtered on the file.
364 Ranges are closed (both limits are within the range).
366 Example A: a single-line range:
367 start: (col=22, line=2)
368 finish: (col=38, line=2)
370 |00000011111111112222222222333333333344444444444
371 |34567890123456789012345678901234567890123456789
372 --+-----------------------------------------------
373 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
374 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
375 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
377 Example B: a multiline range with
378 start: (col=14, line=3)
379 finish: (col=08, line=5)
381 |00000011111111112222222222333333333344444444444
382 |34567890123456789012345678901234567890123456789
383 --+-----------------------------------------------
384 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
385 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
386 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
387 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
388 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
389 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
390 --+-----------------------------------------------
393 - 'b' indicates a point *before* the range
394 - 'S' indicates the start of the range
395 - 'w' indicates a point within the range
396 - 'F' indicates the finish of the range (which is
398 - 'a' indicates a subsequent point *after* the range. */
401 layout_range::contains_point (int row
, int column
) const
403 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
404 /* ...but the equivalent isn't true for the columns;
405 consider example B in the comment above. */
407 if (row
< m_start
.m_line
)
408 /* Points before the first line of the range are
409 outside it (corresponding to line 01 in example A
410 and lines 01 and 02 in example B above). */
413 if (row
== m_start
.m_line
)
414 /* On same line as start of range (corresponding
415 to line 02 in example A and line 03 in example B). */
417 if (column
< m_start
.m_column
)
418 /* Points on the starting line of the range, but
419 before the column in which it begins. */
422 if (row
< m_finish
.m_line
)
423 /* This is a multiline range; the point
424 is within it (corresponds to line 03 in example B
425 from column 14 onwards) */
429 /* This is a single-line range. */
430 gcc_assert (row
== m_finish
.m_line
);
431 return column
<= m_finish
.m_column
;
435 /* The point is in a line beyond that containing the
436 start of the range: lines 03 onwards in example A,
437 and lines 04 onwards in example B. */
438 gcc_assert (row
> m_start
.m_line
);
440 if (row
> m_finish
.m_line
)
441 /* The point is beyond the final line of the range
442 (lines 03 onwards in example A, and lines 06 onwards
446 if (row
< m_finish
.m_line
)
448 /* The point is in a line that's fully within a multiline
449 range (e.g. line 04 in example B). */
450 gcc_assert (m_start
.m_line
< m_finish
.m_line
);
454 gcc_assert (row
== m_finish
.m_line
);
456 return column
<= m_finish
.m_column
;
461 /* A helper function for testing layout_range::contains_point. */
464 make_range (int start_line
, int start_col
, int end_line
, int end_col
)
466 const expanded_location start_exploc
467 = {"test.c", start_line
, start_col
, NULL
, false};
468 const expanded_location finish_exploc
469 = {"test.c", end_line
, end_col
, NULL
, false};
470 return layout_range (&start_exploc
, &finish_exploc
, false,
474 /* Selftests for layout_range::contains_point. */
476 /* Selftest for layout_range::contains_point where the layout_range
477 is a range with start==end i.e. a single point. */
480 test_range_contains_point_for_single_point ()
482 layout_range point
= make_range (7, 10, 7, 10);
484 /* Before the line. */
485 ASSERT_FALSE (point
.contains_point (6, 1));
487 /* On the line, but before start. */
488 ASSERT_FALSE (point
.contains_point (7, 9));
491 ASSERT_TRUE (point
.contains_point (7, 10));
493 /* On the line, after the point. */
494 ASSERT_FALSE (point
.contains_point (7, 11));
496 /* After the line. */
497 ASSERT_FALSE (point
.contains_point (8, 1));
500 /* Selftest for layout_range::contains_point where the layout_range
501 is the single-line range shown as "Example A" above. */
504 test_range_contains_point_for_single_line ()
506 layout_range example_a
= make_range (2, 22, 2, 38);
508 /* Before the line. */
509 ASSERT_FALSE (example_a
.contains_point (1, 1));
511 /* On the line, but before start. */
512 ASSERT_FALSE (example_a
.contains_point (2, 21));
514 /* On the line, at the start. */
515 ASSERT_TRUE (example_a
.contains_point (2, 22));
517 /* On the line, within the range. */
518 ASSERT_TRUE (example_a
.contains_point (2, 23));
520 /* On the line, at the end. */
521 ASSERT_TRUE (example_a
.contains_point (2, 38));
523 /* On the line, after the end. */
524 ASSERT_FALSE (example_a
.contains_point (2, 39));
526 /* After the line. */
527 ASSERT_FALSE (example_a
.contains_point (2, 39));
530 /* Selftest for layout_range::contains_point where the layout_range
531 is the multi-line range shown as "Example B" above. */
534 test_range_contains_point_for_multiple_lines ()
536 layout_range example_b
= make_range (3, 14, 5, 8);
538 /* Before first line. */
539 ASSERT_FALSE (example_b
.contains_point (1, 1));
541 /* On the first line, but before start. */
542 ASSERT_FALSE (example_b
.contains_point (3, 13));
545 ASSERT_TRUE (example_b
.contains_point (3, 14));
547 /* On the first line, within the range. */
548 ASSERT_TRUE (example_b
.contains_point (3, 15));
550 /* On an interior line.
551 The column number should not matter; try various boundary
553 ASSERT_TRUE (example_b
.contains_point (4, 1));
554 ASSERT_TRUE (example_b
.contains_point (4, 7));
555 ASSERT_TRUE (example_b
.contains_point (4, 8));
556 ASSERT_TRUE (example_b
.contains_point (4, 9));
557 ASSERT_TRUE (example_b
.contains_point (4, 13));
558 ASSERT_TRUE (example_b
.contains_point (4, 14));
559 ASSERT_TRUE (example_b
.contains_point (4, 15));
561 /* On the final line, before the end. */
562 ASSERT_TRUE (example_b
.contains_point (5, 7));
564 /* On the final line, at the end. */
565 ASSERT_TRUE (example_b
.contains_point (5, 8));
567 /* On the final line, after the end. */
568 ASSERT_FALSE (example_b
.contains_point (5, 9));
570 /* After the line. */
571 ASSERT_FALSE (example_b
.contains_point (6, 1));
574 #endif /* #if CHECKING_P */
576 /* Given a source line LINE of length LINE_WIDTH, determine the width
577 without any trailing whitespace. */
580 get_line_width_without_trailing_whitespace (const char *line
, int line_width
)
582 int result
= line_width
;
585 char ch
= line
[result
- 1];
586 if (ch
== ' ' || ch
== '\t')
591 gcc_assert (result
>= 0);
592 gcc_assert (result
<= line_width
);
593 gcc_assert (result
== 0 ||
594 (line
[result
- 1] != ' '
595 && line
[result
-1] != '\t'));
601 /* A helper function for testing get_line_width_without_trailing_whitespace. */
604 assert_eq (const char *line
, int expected_width
)
607 = get_line_width_without_trailing_whitespace (line
, strlen (line
));
608 ASSERT_EQ (actual_value
, expected_width
);
611 /* Verify that get_line_width_without_trailing_whitespace is sane for
612 various inputs. It is not required to handle newlines. */
615 test_get_line_width_without_trailing_whitespace ()
620 assert_eq ("hello world", 11);
621 assert_eq ("hello world ", 11);
622 assert_eq ("hello world \t\t ", 11);
625 #endif /* #if CHECKING_P */
627 /* Helper function for layout's ctor, for sanitizing locations relative
628 to the primary location within a diagnostic.
630 Compare LOC_A and LOC_B to see if it makes sense to print underlines
631 connecting their expanded locations. Doing so is only guaranteed to
632 make sense if the locations share the same macro expansion "history"
633 i.e. they can be traced through the same macro expansions, eventually
634 reaching an ordinary map.
636 This may be too strong a condition, but it effectively sanitizes
637 PR c++/70105, which has an example of printing an expression where the
638 final location of the expression is in a different macro, which
639 erroneously was leading to hundreds of lines of irrelevant source
643 compatible_locations_p (location_t loc_a
, location_t loc_b
)
645 if (IS_ADHOC_LOC (loc_a
))
646 loc_a
= get_location_from_adhoc_loc (line_table
, loc_a
);
647 if (IS_ADHOC_LOC (loc_b
))
648 loc_b
= get_location_from_adhoc_loc (line_table
, loc_b
);
650 /* If either location is one of the special locations outside of a
651 linemap, they are only compatible if they are equal. */
652 if (loc_a
< RESERVED_LOCATION_COUNT
653 || loc_b
< RESERVED_LOCATION_COUNT
)
654 return loc_a
== loc_b
;
656 const line_map
*map_a
= linemap_lookup (line_table
, loc_a
);
657 linemap_assert (map_a
);
659 const line_map
*map_b
= linemap_lookup (line_table
, loc_b
);
660 linemap_assert (map_b
);
662 /* Are they within the same map? */
665 /* Are both within the same macro expansion? */
666 if (linemap_macro_expansion_map_p (map_a
))
668 /* Expand each location towards the spelling location, and
670 const line_map_macro
*macro_map
= linemap_check_macro (map_a
);
671 source_location loc_a_toward_spelling
672 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
675 source_location loc_b_toward_spelling
676 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
679 return compatible_locations_p (loc_a_toward_spelling
,
680 loc_b_toward_spelling
);
683 /* Otherwise they are within the same ordinary map. */
688 /* Within different maps. */
690 /* If either is within a macro expansion, they are incompatible. */
691 if (linemap_macro_expansion_map_p (map_a
)
692 || linemap_macro_expansion_map_p (map_b
))
695 /* Within two different ordinary maps; they are compatible iff they
696 are in the same file. */
697 const line_map_ordinary
*ord_map_a
= linemap_check_ordinary (map_a
);
698 const line_map_ordinary
*ord_map_b
= linemap_check_ordinary (map_b
);
699 return ord_map_a
->to_file
== ord_map_b
->to_file
;
703 /* Implementation of class layout. */
705 /* Constructor for class layout.
707 Filter the ranges from the rich_location to those that we can
708 sanely print, populating m_layout_ranges.
709 Determine the range of lines that we will print, splitting them
710 up into an ordered list of disjoint spans of contiguous line numbers.
711 Determine m_x_offset, to ensure that the primary caret
712 will fit within the max_width provided by the diagnostic_context. */
714 layout::layout (diagnostic_context
* context
,
715 rich_location
*richloc
,
716 diagnostic_t diagnostic_kind
)
717 : m_context (context
),
718 m_pp (context
->printer
),
719 m_diagnostic_kind (diagnostic_kind
),
720 m_exploc (richloc
->get_expanded_location (0)),
721 m_colorizer (context
, diagnostic_kind
),
722 m_colorize_source_p (context
->colorize_source_p
),
723 m_layout_ranges (rich_location::MAX_RANGES
),
724 m_line_spans (1 + rich_location::MAX_RANGES
),
727 source_location primary_loc
= richloc
->get_range (0)->m_loc
;
729 for (unsigned int idx
= 0; idx
< richloc
->get_num_locations (); idx
++)
731 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
732 Ignore any ranges that are awkward to handle. */
733 const location_range
*loc_range
= richloc
->get_range (idx
);
735 /* Split the "range" into caret and range information. */
736 source_range src_range
= get_range_from_loc (line_table
, loc_range
->m_loc
);
738 /* Expand the various locations. */
739 expanded_location start
740 = linemap_client_expand_location_to_spelling_point (src_range
.m_start
);
741 expanded_location finish
742 = linemap_client_expand_location_to_spelling_point (src_range
.m_finish
);
743 expanded_location caret
744 = linemap_client_expand_location_to_spelling_point (loc_range
->m_loc
);
746 /* If any part of the range isn't in the same file as the primary
747 location of this diagnostic, ignore the range. */
748 if (start
.file
!= m_exploc
.file
)
750 if (finish
.file
!= m_exploc
.file
)
752 if (loc_range
->m_show_caret_p
)
753 if (caret
.file
!= m_exploc
.file
)
756 /* Sanitize the caret location for non-primary ranges. */
757 if (m_layout_ranges
.length () > 0)
758 if (loc_range
->m_show_caret_p
)
759 if (!compatible_locations_p (loc_range
->m_loc
, primary_loc
))
760 /* Discard any non-primary ranges that can't be printed
761 sanely relative to the primary location. */
764 /* Everything is now known to be in the correct source file,
765 but it may require further sanitization. */
766 layout_range
ri (&start
, &finish
, loc_range
->m_show_caret_p
, &caret
);
768 /* If we have a range that finishes before it starts (perhaps
769 from something built via macro expansion), printing the
770 range is likely to be nonsensical. Also, attempting to do so
771 breaks assumptions within the printing code (PR c/68473).
772 Similarly, don't attempt to print ranges if one or both ends
773 of the range aren't sane to print relative to the
774 primary location (PR c++/70105). */
775 if (start
.line
> finish
.line
776 || !compatible_locations_p (src_range
.m_start
, primary_loc
)
777 || !compatible_locations_p (src_range
.m_finish
, primary_loc
))
779 /* Is this the primary location? */
780 if (m_layout_ranges
.length () == 0)
782 /* We want to print the caret for the primary location, but
783 we must sanitize away m_start and m_finish. */
784 ri
.m_start
= ri
.m_caret
;
785 ri
.m_finish
= ri
.m_caret
;
788 /* This is a non-primary range; ignore it. */
792 /* Passed all the tests; add the range to m_layout_ranges so that
793 it will be printed. */
794 m_layout_ranges
.safe_push (ri
);
797 /* Populate m_line_spans. */
798 calculate_line_spans ();
800 /* Adjust m_x_offset.
801 Center the primary caret to fit in max_width; all columns
802 will be adjusted accordingly. */
803 int max_width
= m_context
->caret_max_width
;
805 const char *line
= location_get_source_line (m_exploc
.file
, m_exploc
.line
,
807 if (line
&& m_exploc
.column
<= line_width
)
809 int right_margin
= CARET_LINE_MARGIN
;
810 int column
= m_exploc
.column
;
811 right_margin
= MIN (line_width
- column
, right_margin
);
812 right_margin
= max_width
- right_margin
;
813 if (line_width
>= max_width
&& column
> right_margin
)
814 m_x_offset
= column
- right_margin
;
815 gcc_assert (m_x_offset
>= 0);
818 if (context
->show_ruler_p
)
819 show_ruler (m_x_offset
+ max_width
);
822 /* Return true iff we should print a heading when starting the
823 line span with the given index. */
826 layout::print_heading_for_line_span_index_p (int line_span_idx
) const
828 /* We print a heading for every change of line span, hence for every
829 line span after the initial one. */
830 if (line_span_idx
> 0)
833 /* We also do it for the initial span if the primary location of the
834 diagnostic is in a different span. */
835 if (m_exploc
.line
> (int)get_line_span (0)->m_last_line
)
841 /* Get an expanded_location for the first location of interest within
843 Used when printing a heading to indicate a new line span. */
846 layout::get_expanded_location (const line_span
*line_span
) const
848 /* Whenever possible, use the caret location. */
849 if (line_span
->contains_line_p (m_exploc
.line
))
852 /* Otherwise, use the start of the first range that's present
853 within the line_span. */
854 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
856 const layout_range
*lr
= &m_layout_ranges
[i
];
857 if (line_span
->contains_line_p (lr
->m_start
.m_line
))
859 expanded_location exploc
= m_exploc
;
860 exploc
.line
= lr
->m_start
.m_line
;
861 exploc
.column
= lr
->m_start
.m_column
;
866 /* It should not be possible to have a line span that didn't
867 contain any of the layout_range instances. */
872 /* We want to print the pertinent source code at a diagnostic. The
873 rich_location can contain multiple locations. This will have been
874 filtered into m_exploc (the caret for the primary location) and
875 m_layout_ranges, for those ranges within the same source file.
877 We will print a subset of the lines within the source file in question,
878 as a collection of "spans" of lines.
880 This function populates m_line_spans with an ordered, disjoint list of
881 the line spans of interest.
883 For example, if the primary caret location is on line 7, with ranges
884 covering lines 5-6 and lines 9-12:
897 then we want two spans: lines 5-7 and lines 9-12. */
900 layout::calculate_line_spans ()
902 /* This should only be called once, by the ctor. */
903 gcc_assert (m_line_spans
.length () == 0);
905 /* Populate tmp_spans with individual spans, for each of
906 m_exploc, and for m_layout_ranges. */
907 auto_vec
<line_span
> tmp_spans (1 + rich_location::MAX_RANGES
);
908 tmp_spans
.safe_push (line_span (m_exploc
.line
, m_exploc
.line
));
909 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
911 const layout_range
*lr
= &m_layout_ranges
[i
];
912 gcc_assert (lr
->m_start
.m_line
<= lr
->m_finish
.m_line
);
913 tmp_spans
.safe_push (line_span (lr
->m_start
.m_line
,
914 lr
->m_finish
.m_line
));
918 tmp_spans
.qsort(line_span::comparator
);
920 /* Now iterate through tmp_spans, copying into m_line_spans, and
921 combining where possible. */
922 gcc_assert (tmp_spans
.length () > 0);
923 m_line_spans
.safe_push (tmp_spans
[0]);
924 for (unsigned int i
= 1; i
< tmp_spans
.length (); i
++)
926 line_span
*current
= &m_line_spans
[m_line_spans
.length () - 1];
927 const line_span
*next
= &tmp_spans
[i
];
928 gcc_assert (next
->m_first_line
>= current
->m_first_line
);
929 if (next
->m_first_line
<= current
->m_last_line
+ 1)
931 /* We can merge them. */
932 if (next
->m_last_line
> current
->m_last_line
)
933 current
->m_last_line
= next
->m_last_line
;
937 /* No merger possible. */
938 m_line_spans
.safe_push (*next
);
942 /* Verify the result, in m_line_spans. */
943 gcc_assert (m_line_spans
.length () > 0);
944 for (unsigned int i
= 1; i
< m_line_spans
.length (); i
++)
946 const line_span
*prev
= &m_line_spans
[i
- 1];
947 const line_span
*next
= &m_line_spans
[i
];
948 /* The individual spans must be sane. */
949 gcc_assert (prev
->m_first_line
<= prev
->m_last_line
);
950 gcc_assert (next
->m_first_line
<= next
->m_last_line
);
951 /* The spans must be ordered. */
952 gcc_assert (prev
->m_first_line
< next
->m_first_line
);
953 /* There must be a gap of at least one line between separate spans. */
954 gcc_assert ((prev
->m_last_line
+ 1) < next
->m_first_line
);
958 /* Attempt to print line ROW of source code, potentially colorized at any
960 Return true if the line was printed, populating *LBOUNDS_OUT.
961 Return false if the source line could not be read, leaving *LBOUNDS_OUT
965 layout::print_source_line (int row
, line_bounds
*lbounds_out
)
968 const char *line
= location_get_source_line (m_exploc
.file
, row
,
973 m_colorizer
.set_normal_text ();
975 /* We will stop printing the source line at any trailing
977 line_width
= get_line_width_without_trailing_whitespace (line
,
982 int first_non_ws
= INT_MAX
;
985 for (column
= 1 + m_x_offset
; column
<= line_width
; column
++)
987 /* Assuming colorization is enabled for the caret and underline
988 characters, we may also colorize the associated characters
989 within the source line.
991 For frontends that generate range information, we color the
992 associated characters in the source line the same as the
993 carets and underlines in the annotation line, to make it easier
994 for the reader to see the pertinent code.
996 For frontends that only generate carets, we don't colorize the
997 characters above them, since this would look strange (e.g.
998 colorizing just the first character in a token). */
999 if (m_colorize_source_p
)
1003 in_range_p
= get_state_at_point (row
, column
,
1007 m_colorizer
.set_range (state
.range_idx
);
1009 m_colorizer
.set_normal_text ();
1011 char c
= *line
== '\t' ? ' ' : *line
;
1016 last_non_ws
= column
;
1017 if (first_non_ws
== INT_MAX
)
1018 first_non_ws
= column
;
1020 pp_character (m_pp
, c
);
1025 lbounds_out
->m_first_non_ws
= first_non_ws
;
1026 lbounds_out
->m_last_non_ws
= last_non_ws
;
1030 /* Print a line consisting of the caret/underlines for the given
1034 layout::print_annotation_line (int row
, const line_bounds lbounds
)
1036 int x_bound
= get_x_bound_for_row (row
, m_exploc
.column
,
1037 lbounds
.m_last_non_ws
);
1040 for (int column
= 1 + m_x_offset
; column
< x_bound
; column
++)
1044 in_range_p
= get_state_at_point (row
, column
,
1045 lbounds
.m_first_non_ws
,
1046 lbounds
.m_last_non_ws
,
1050 /* Within a range. Draw either the caret or an underline. */
1051 m_colorizer
.set_range (state
.range_idx
);
1052 if (state
.draw_caret_p
)
1053 /* Draw the caret. */
1054 pp_character (m_pp
, m_context
->caret_chars
[state
.range_idx
]);
1056 pp_character (m_pp
, '~');
1060 /* Not in a range. */
1061 m_colorizer
.set_normal_text ();
1062 pp_character (m_pp
, ' ');
1068 /* Subroutine of layout::print_any_fixits.
1070 Determine if the annotation line printed for LINE contained
1071 the exact range from START_COLUMN to FINISH_COLUMN. */
1074 layout::annotation_line_showed_range_p (int line
, int start_column
,
1075 int finish_column
) const
1077 layout_range
*range
;
1079 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1080 if (range
->m_start
.m_line
== line
1081 && range
->m_start
.m_column
== start_column
1082 && range
->m_finish
.m_line
== line
1083 && range
->m_finish
.m_column
== finish_column
)
1088 /* If there are any fixit hints on source line ROW within RICHLOC, print them.
1089 They are printed in order, attempting to combine them onto lines, but
1090 starting new lines if necessary. */
1093 layout::print_any_fixits (int row
, const rich_location
*richloc
)
1096 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
1098 fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
1099 if (hint
->affects_line_p (m_exploc
.file
, row
))
1101 /* For now we assume each fixit hint can only touch one line. */
1102 switch (hint
->get_kind ())
1104 case fixit_hint::INSERT
:
1106 fixit_insert
*insert
= static_cast <fixit_insert
*> (hint
);
1107 /* This assumes the insertion just affects one line. */
1109 = LOCATION_COLUMN (insert
->get_location ());
1110 move_to_column (&column
, start_column
);
1111 m_colorizer
.set_fixit_insert ();
1112 pp_string (m_pp
, insert
->get_string ());
1113 m_colorizer
.set_normal_text ();
1114 column
+= insert
->get_length ();
1118 case fixit_hint::REPLACE
:
1120 fixit_replace
*replace
= static_cast <fixit_replace
*> (hint
);
1121 source_range src_range
= replace
->get_range ();
1122 int line
= LOCATION_LINE (src_range
.m_start
);
1123 int start_column
= LOCATION_COLUMN (src_range
.m_start
);
1124 int finish_column
= LOCATION_COLUMN (src_range
.m_finish
);
1126 /* If the range of the replacement wasn't printed in the
1127 annotation line, then print an extra underline to
1128 indicate exactly what is being replaced.
1129 Always show it for removals. */
1130 if (!annotation_line_showed_range_p (line
, start_column
,
1132 || replace
->get_length () == 0)
1134 move_to_column (&column
, start_column
);
1135 m_colorizer
.set_fixit_delete ();
1136 for (; column
<= finish_column
; column
++)
1137 pp_character (m_pp
, '-');
1138 m_colorizer
.set_normal_text ();
1140 /* Print the replacement text. REPLACE also covers
1141 removals, so only do this extra work (potentially starting
1142 a new line) if we have actual replacement text. */
1143 if (replace
->get_length () > 0)
1145 move_to_column (&column
, start_column
);
1146 m_colorizer
.set_fixit_insert ();
1147 pp_string (m_pp
, replace
->get_string ());
1148 m_colorizer
.set_normal_text ();
1149 column
+= replace
->get_length ();
1160 /* Add a trailing newline, if necessary. */
1161 move_to_column (&column
, 0);
1164 /* Disable any colorization and emit a newline. */
1167 layout::print_newline ()
1169 m_colorizer
.set_normal_text ();
1173 /* Return true if (ROW/COLUMN) is within a range of the layout.
1174 If it returns true, OUT_STATE is written to, with the
1175 range index, and whether we should draw the caret at
1176 (ROW/COLUMN) (as opposed to an underline). */
1179 layout::get_state_at_point (/* Inputs. */
1180 int row
, int column
,
1181 int first_non_ws
, int last_non_ws
,
1183 point_state
*out_state
)
1185 layout_range
*range
;
1187 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1189 if (range
->contains_point (row
, column
))
1191 out_state
->range_idx
= i
;
1193 /* Are we at the range's caret? is it visible? */
1194 out_state
->draw_caret_p
= false;
1195 if (range
->m_show_caret_p
1196 && row
== range
->m_caret
.m_line
1197 && column
== range
->m_caret
.m_column
)
1198 out_state
->draw_caret_p
= true;
1200 /* Within a multiline range, don't display any underline
1201 in any leading or trailing whitespace on a line.
1202 We do display carets, however. */
1203 if (!out_state
->draw_caret_p
)
1204 if (column
< first_non_ws
|| column
> last_non_ws
)
1207 /* We are within a range. */
1215 /* Helper function for use by layout::print_line when printing the
1216 annotation line under the source line.
1217 Get the column beyond the rightmost one that could contain a caret or
1218 range marker, given that we stop rendering at trailing whitespace.
1219 ROW is the source line within the given file.
1220 CARET_COLUMN is the column of range 0's caret.
1221 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1222 character of source (as determined when printing the source line). */
1225 layout::get_x_bound_for_row (int row
, int caret_column
,
1226 int last_non_ws_column
)
1228 int result
= caret_column
+ 1;
1230 layout_range
*range
;
1232 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1234 if (row
>= range
->m_start
.m_line
)
1236 if (range
->m_finish
.m_line
== row
)
1238 /* On the final line within a range; ensure that
1239 we render up to the end of the range. */
1240 if (result
<= range
->m_finish
.m_column
)
1241 result
= range
->m_finish
.m_column
+ 1;
1243 else if (row
< range
->m_finish
.m_line
)
1245 /* Within a multiline range; ensure that we render up to the
1246 last non-whitespace column. */
1247 if (result
<= last_non_ws_column
)
1248 result
= last_non_ws_column
+ 1;
1256 /* Given *COLUMN as an x-coordinate, print spaces to position
1257 successive output at DEST_COLUMN, printing a newline if necessary,
1258 and updating *COLUMN. */
1261 layout::move_to_column (int *column
, int dest_column
)
1263 /* Start a new line if we need to. */
1264 if (*column
> dest_column
)
1270 while (*column
< dest_column
)
1277 /* For debugging layout issues, render a ruler giving column numbers
1278 (after the 1-column indent). */
1281 layout::show_ruler (int max_column
) const
1284 if (max_column
> 99)
1287 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1288 if (0 == column
% 10)
1289 pp_character (m_pp
, '0' + (column
/ 100) % 10);
1297 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1298 if (0 == column
% 10)
1299 pp_character (m_pp
, '0' + (column
/ 10) % 10);
1306 for (int column
= 1 + m_x_offset
; column
<= max_column
; column
++)
1307 pp_character (m_pp
, '0' + (column
% 10));
1311 } /* End of anonymous namespace. */
1313 /* Print the physical source code corresponding to the location of
1314 this diagnostic, with additional annotations. */
1317 diagnostic_show_locus (diagnostic_context
* context
,
1318 rich_location
*richloc
,
1319 diagnostic_t diagnostic_kind
)
1321 pp_newline (context
->printer
);
1323 location_t loc
= richloc
->get_loc ();
1324 /* Do nothing if source-printing has been disabled. */
1325 if (!context
->show_caret
)
1328 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1329 if (loc
<= BUILTINS_LOCATION
)
1332 /* Don't print the same source location twice in a row, unless we have
1334 if (loc
== context
->last_location
1335 && richloc
->get_num_fixit_hints () == 0)
1338 context
->last_location
= loc
;
1340 const char *saved_prefix
= pp_get_prefix (context
->printer
);
1341 pp_set_prefix (context
->printer
, NULL
);
1343 layout
layout (context
, richloc
, diagnostic_kind
);
1344 for (int line_span_idx
= 0; line_span_idx
< layout
.get_num_line_spans ();
1347 const line_span
*line_span
= layout
.get_line_span (line_span_idx
);
1348 if (layout
.print_heading_for_line_span_index_p (line_span_idx
))
1350 expanded_location exploc
= layout
.get_expanded_location (line_span
);
1351 context
->start_span (context
, exploc
);
1353 int last_line
= line_span
->get_last_line ();
1354 for (int row
= line_span
->get_first_line (); row
<= last_line
; row
++)
1356 /* Print the source line, followed by an annotation line
1357 consisting of any caret/underlines, then any fixits.
1358 If the source line can't be read, print nothing. */
1359 line_bounds lbounds
;
1360 if (layout
.print_source_line (row
, &lbounds
))
1362 layout
.print_annotation_line (row
, lbounds
);
1363 layout
.print_any_fixits (row
, richloc
);
1368 pp_set_prefix (context
->printer
, saved_prefix
);
1373 namespace selftest
{
1375 /* Selftests for diagnostic_show_locus. */
1377 /* Convenience subclass of diagnostic_context for testing
1378 diagnostic_show_locus. */
1380 class test_diagnostic_context
: public diagnostic_context
1383 test_diagnostic_context ()
1385 diagnostic_initialize (this, 0);
1388 ~test_diagnostic_context ()
1390 diagnostic_finish (this);
1394 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1397 test_diagnostic_show_locus_unknown_location ()
1399 test_diagnostic_context dc
;
1400 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
1401 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1402 ASSERT_STREQ ("\n", pp_formatted_text (dc
.printer
));
1405 /* Verify that diagnostic_show_locus works sanely for various
1408 All of these work on the following 1-line source file:
1411 "foo = bar.field;\n"
1412 which is set up by test_diagnostic_show_locus_one_liner and calls
1418 test_one_liner_simple_caret ()
1420 test_diagnostic_context dc
;
1421 location_t caret
= linemap_position_for_column (line_table
, 10);
1422 rich_location
richloc (line_table
, caret
);
1423 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1425 " foo = bar.field;\n"
1427 pp_formatted_text (dc
.printer
));
1430 /* Caret and range. */
1433 test_one_liner_caret_and_range ()
1435 test_diagnostic_context dc
;
1436 location_t caret
= linemap_position_for_column (line_table
, 10);
1437 location_t start
= linemap_position_for_column (line_table
, 7);
1438 location_t finish
= linemap_position_for_column (line_table
, 15);
1439 location_t loc
= make_location (caret
, start
, finish
);
1440 rich_location
richloc (line_table
, loc
);
1441 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1443 " foo = bar.field;\n"
1445 pp_formatted_text (dc
.printer
));
1448 /* Multiple ranges and carets. */
1451 test_one_liner_multiple_carets_and_ranges ()
1453 test_diagnostic_context dc
;
1455 = make_location (linemap_position_for_column (line_table
, 2),
1456 linemap_position_for_column (line_table
, 1),
1457 linemap_position_for_column (line_table
, 3));
1458 dc
.caret_chars
[0] = 'A';
1461 = make_location (linemap_position_for_column (line_table
, 8),
1462 linemap_position_for_column (line_table
, 7),
1463 linemap_position_for_column (line_table
, 9));
1464 dc
.caret_chars
[1] = 'B';
1467 = make_location (linemap_position_for_column (line_table
, 13),
1468 linemap_position_for_column (line_table
, 11),
1469 linemap_position_for_column (line_table
, 15));
1470 dc
.caret_chars
[2] = 'C';
1472 rich_location
richloc (line_table
, foo
);
1473 richloc
.add_range (bar
, true);
1474 richloc
.add_range (field
, true);
1475 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1477 " foo = bar.field;\n"
1479 pp_formatted_text (dc
.printer
));
1482 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1485 test_one_liner_fixit_insert ()
1487 test_diagnostic_context dc
;
1488 location_t caret
= linemap_position_for_column (line_table
, 7);
1489 rich_location
richloc (line_table
, caret
);
1490 richloc
.add_fixit_insert (caret
, "&");
1491 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1493 " foo = bar.field;\n"
1496 pp_formatted_text (dc
.printer
));
1499 /* Removal fix-it hint: removal of the ".field". */
1502 test_one_liner_fixit_remove ()
1504 test_diagnostic_context dc
;
1505 location_t start
= linemap_position_for_column (line_table
, 10);
1506 location_t finish
= linemap_position_for_column (line_table
, 15);
1507 location_t dot
= make_location (start
, start
, finish
);
1508 rich_location
richloc (line_table
, dot
);
1510 range
.m_start
= start
;
1511 range
.m_finish
= finish
;
1512 richloc
.add_fixit_remove (range
);
1513 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1515 " foo = bar.field;\n"
1518 pp_formatted_text (dc
.printer
));
1521 /* Replace fix-it hint: replacing "field" with "m_field". */
1524 test_one_liner_fixit_replace ()
1526 test_diagnostic_context dc
;
1527 location_t start
= linemap_position_for_column (line_table
, 11);
1528 location_t finish
= linemap_position_for_column (line_table
, 15);
1529 location_t field
= make_location (start
, start
, finish
);
1530 rich_location
richloc (line_table
, field
);
1532 range
.m_start
= start
;
1533 range
.m_finish
= finish
;
1534 richloc
.add_fixit_replace (range
, "m_field");
1535 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1537 " foo = bar.field;\n"
1540 pp_formatted_text (dc
.printer
));
1543 /* Replace fix-it hint: replacing "field" with "m_field",
1544 but where the caret was elsewhere. */
1547 test_one_liner_fixit_replace_non_equal_range ()
1549 test_diagnostic_context dc
;
1550 location_t equals
= linemap_position_for_column (line_table
, 5);
1551 location_t start
= linemap_position_for_column (line_table
, 11);
1552 location_t finish
= linemap_position_for_column (line_table
, 15);
1553 rich_location
richloc (line_table
, equals
);
1555 range
.m_start
= start
;
1556 range
.m_finish
= finish
;
1557 richloc
.add_fixit_replace (range
, "m_field");
1558 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1559 /* The replacement range is not indicated in the annotation line, so
1560 it should be indicated via an additional underline. */
1562 " foo = bar.field;\n"
1566 pp_formatted_text (dc
.printer
));
1569 /* Replace fix-it hint: replacing "field" with "m_field",
1570 where the caret was elsewhere, but where a secondary range
1571 exactly covers "field". */
1574 test_one_liner_fixit_replace_equal_secondary_range ()
1576 test_diagnostic_context dc
;
1577 location_t equals
= linemap_position_for_column (line_table
, 5);
1578 location_t start
= linemap_position_for_column (line_table
, 11);
1579 location_t finish
= linemap_position_for_column (line_table
, 15);
1580 rich_location
richloc (line_table
, equals
);
1581 location_t field
= make_location (start
, start
, finish
);
1582 richloc
.add_range (field
, false);
1584 range
.m_start
= start
;
1585 range
.m_finish
= finish
;
1586 richloc
.add_fixit_replace (range
, "m_field");
1587 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
1588 /* The replacement range is indicated in the annotation line,
1589 so it shouldn't be indicated via an additional underline. */
1591 " foo = bar.field;\n"
1594 pp_formatted_text (dc
.printer
));
1597 /* Run the various one-liner tests. */
1600 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
1602 /* Create a tempfile and write some text to it.
1603 ....................0000000001111111.
1604 ....................1234567890123456. */
1605 const char *content
= "foo = bar.field;\n";
1606 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
1607 line_table_test
ltt (case_
);
1609 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
1611 location_t line_end
= linemap_position_for_column (line_table
, 16);
1613 /* Don't attempt to run the tests if column data might be unavailable. */
1614 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1617 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
1618 ASSERT_EQ (1, LOCATION_LINE (line_end
));
1619 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
1621 test_one_liner_simple_caret ();
1622 test_one_liner_caret_and_range ();
1623 test_one_liner_multiple_carets_and_ranges ();
1624 test_one_liner_fixit_insert ();
1625 test_one_liner_fixit_remove ();
1626 test_one_liner_fixit_replace ();
1627 test_one_liner_fixit_replace_non_equal_range ();
1628 test_one_liner_fixit_replace_equal_secondary_range ();
1631 /* Verify that fix-it hints are appropriately consolidated.
1633 If any fix-it hints in a rich_location involve locations beyond
1634 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
1635 the fix-it as a whole, so there should be none.
1637 Otherwise, verify that consecutive "replace" and "remove" fix-its
1638 are merged, and that other fix-its remain separate. */
1641 test_fixit_consolidation (const line_table_case
&case_
)
1643 line_table_test
ltt (case_
);
1645 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
1647 const location_t c10
= linemap_position_for_column (line_table
, 10);
1648 const location_t c15
= linemap_position_for_column (line_table
, 15);
1649 const location_t c16
= linemap_position_for_column (line_table
, 16);
1650 const location_t c17
= linemap_position_for_column (line_table
, 17);
1651 const location_t c20
= linemap_position_for_column (line_table
, 20);
1652 const location_t caret
= c10
;
1654 /* Insert + insert. */
1656 rich_location
richloc (line_table
, caret
);
1657 richloc
.add_fixit_insert (c10
, "foo");
1658 richloc
.add_fixit_insert (c15
, "bar");
1660 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1661 /* Bogus column info for 2nd fixit, so no fixits. */
1662 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1664 /* They should not have been merged. */
1665 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
1668 /* Insert + replace. */
1670 rich_location
richloc (line_table
, caret
);
1671 richloc
.add_fixit_insert (c10
, "foo");
1672 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
1675 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1676 /* Bogus column info for 2nd fixit, so no fixits. */
1677 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1679 /* They should not have been merged. */
1680 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
1683 /* Replace + non-consecutive insert. */
1685 rich_location
richloc (line_table
, caret
);
1686 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
1688 richloc
.add_fixit_insert (c17
, "foo");
1690 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1691 /* Bogus column info for 2nd fixit, so no fixits. */
1692 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1694 /* They should not have been merged. */
1695 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
1698 /* Replace + non-consecutive replace. */
1700 rich_location
richloc (line_table
, caret
);
1701 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
1703 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
1706 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1707 /* Bogus column info for 2nd fixit, so no fixits. */
1708 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1710 /* They should not have been merged. */
1711 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
1714 /* Replace + consecutive replace. */
1716 rich_location
richloc (line_table
, caret
);
1717 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
1719 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
1722 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1723 /* Bogus column info for 2nd fixit, so no fixits. */
1724 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1727 /* They should have been merged into a single "replace". */
1728 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
1729 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
1730 ASSERT_EQ (fixit_hint::REPLACE
, hint
->get_kind ());
1731 const fixit_replace
*replace
= (const fixit_replace
*)hint
;
1732 ASSERT_STREQ ("foobar", replace
->get_string ());
1733 ASSERT_EQ (c10
, replace
->get_range ().m_start
);
1734 ASSERT_EQ (c20
, replace
->get_range ().m_finish
);
1738 /* Replace + consecutive removal. */
1740 rich_location
richloc (line_table
, caret
);
1741 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
1743 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
1745 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1746 /* Bogus column info for 2nd fixit, so no fixits. */
1747 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1750 /* They should have been merged into a single replace, with the
1751 range extended to cover that of the removal. */
1752 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
1753 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
1754 ASSERT_EQ (fixit_hint::REPLACE
, hint
->get_kind ());
1755 const fixit_replace
*replace
= (const fixit_replace
*)hint
;
1756 ASSERT_STREQ ("foo", replace
->get_string ());
1757 ASSERT_EQ (c10
, replace
->get_range ().m_start
);
1758 ASSERT_EQ (c20
, replace
->get_range ().m_finish
);
1762 /* Consecutive removals. */
1764 rich_location
richloc (line_table
, caret
);
1765 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
1766 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
1768 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
1769 /* Bogus column info for 2nd fixit, so no fixits. */
1770 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
1773 /* They should have been merged into a single "replace-with-empty". */
1774 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
1775 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
1776 ASSERT_EQ (fixit_hint::REPLACE
, hint
->get_kind ());
1777 const fixit_replace
*replace
= (const fixit_replace
*)hint
;
1778 ASSERT_STREQ ("", replace
->get_string ());
1779 ASSERT_EQ (c10
, replace
->get_range ().m_start
);
1780 ASSERT_EQ (c20
, replace
->get_range ().m_finish
);
1785 /* Run all of the selftests within this file. */
1788 diagnostic_show_locus_c_tests ()
1790 test_range_contains_point_for_single_point ();
1791 test_range_contains_point_for_single_line ();
1792 test_range_contains_point_for_multiple_lines ();
1794 test_get_line_width_without_trailing_whitespace ();
1796 test_diagnostic_show_locus_unknown_location ();
1798 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
1799 for_each_line_table_case (test_fixit_consolidation
);
1802 } // namespace selftest
1804 #endif /* #if CHECKING_P */