1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2025 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/>. */
22 #define INCLUDE_VECTOR
24 #include "coretypes.h"
28 #include "backtrace.h"
29 #include "diagnostic.h"
30 #include "diagnostic-color.h"
31 #include "gcc-rich-location.h"
32 #include "text-range-label.h"
34 #include "selftest-diagnostic.h"
35 #include "selftest-diagnostic-show-locus.h"
37 #include "text-art/types.h"
38 #include "text-art/theme.h"
39 #include "diagnostic-label-effects.h"
45 #ifdef GWINSZ_IN_SYS_IOCTL
46 # include <sys/ioctl.h>
49 /* Disable warnings about quoting issues in the pp_xxx calls below
50 that (intentionally) don't follow GCC diagnostic conventions. */
52 # pragma GCC diagnostic push
53 # pragma GCC diagnostic ignored "-Wformat-diag"
56 /* Classes for rendering source code and diagnostics, within an
58 The work is done by "class layout", which embeds and uses
59 "class colorizer" and "class layout_range" to get things done. */
63 /* The state at a given point of the source code, assuming that we're
64 in a range: which range are we in, and whether we should draw a caret at
73 /* A class to inject colorization codes when printing the diagnostic locus.
75 It has one kind of colorization for each of:
77 - range 0 (the "primary location")
81 The class caches the lookup of the color codes for the above.
83 The class also has responsibility for tracking which of the above is
84 active, filtering out unnecessary changes. This allows
85 layout_printer::print_source_line and layout_printer::print_annotation_line
86 to simply request a colorization code for *every* character they print,
87 via this class, and have the filtering be done for them here. */
92 colorizer (pretty_printer
&pp
,
93 const rich_location
&richloc
,
94 diagnostic_t diagnostic_kind
);
97 void set_range (int range_idx
)
99 /* If we have a specific highlight color for the range, use it. */
100 if (pp_show_highlight_colors (&m_pp
))
102 const location_range
*const loc_range
= m_richloc
.get_range (range_idx
);
103 if (loc_range
->m_highlight_color
)
105 set_named_color (loc_range
->m_highlight_color
);
110 /* Otherwise, we emphasize the primary location, then alternate between
111 two colors for the secondary locations.
112 But if we're printing a run of events in a diagnostic path, that
113 makes no sense, so print all of them with the same colorization. */
114 if (m_diagnostic_kind
== DK_DIAGNOSTIC_PATH
)
117 set_state (range_idx
);
119 void set_cfg_edge () { set_state (0); }
120 void set_normal_text () { set_state (STATE_NORMAL_TEXT
); }
121 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT
); }
122 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE
); }
123 void set_named_color (const char *color
);
126 void set_state (int state
);
127 void begin_state (int state
);
128 void finish_state (int state
);
129 const char *get_color_by_name (const char *);
132 static const int STATE_NORMAL_TEXT
= -1;
133 static const int STATE_FIXIT_INSERT
= -2;
134 static const int STATE_FIXIT_DELETE
= -3;
135 static const int STATE_NAMED_COLOR
= -4;
137 pretty_printer
&m_pp
;
138 const rich_location
&m_richloc
;
139 diagnostic_t m_diagnostic_kind
;
141 const char *m_range1
;
142 const char *m_range2
;
143 const char *m_fixit_insert
;
144 const char *m_fixit_delete
;
145 const char *m_stop_color
;
148 /* In order to handle multibyte sources properly, all of this logic needs to be
149 aware of the distinction between the number of bytes and the number of
150 display columns occupied by a character, which are not the same for non-ASCII
151 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
152 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
153 display column when it is output. A typical emoji, such as U+1F602 (in
154 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
156 The below example line, which is also used for selftests below, shows how the
157 display column and byte column are related:
159 0000000001111111111222222 display
160 1234567890123456789012345 columns
161 SS_foo = P_bar.SS_fieldP;
162 0000000111111111222222223 byte
163 1356789012456789134567891 columns
165 Here SS represents the two display columns for the U+1F602 emoji, and P
166 represents the one display column for the U+03C0 pi symbol. As an example, a
167 diagnostic pointing to the final P on this line is at byte column 29 and
168 display column 24. This reflects the fact that the three extended characters
169 before the final P occupy cumulatively 5 more bytes than they do display
170 columns (a difference of 2 for each of the two SSs, and one for the other P).
172 One or the other of the two column units is more useful depending on the
173 context. For instance, in order to output the caret at the correct location,
174 we need to count display columns; in order to colorize a source line, we need
175 to count the bytes. All locations are provided to us as byte counts, which
176 we augment with the display column on demand so that it can be used when
177 needed. This is not the most efficient way to do things since it requires
178 looping over the whole line each time, but it should be fine for the purpose
179 of outputting diagnostics.
181 In order to keep straight which units (byte or display) are in use at a
182 given time, the following enum lets us specify that explicitly. */
185 /* Measured in raw bytes. */
188 /* Measured in display units. */
191 /* For arrays indexed by column_unit. */
195 /* Utility class to augment an exploc with the corresponding display column. */
197 class exploc_with_display_col
: public expanded_location
200 exploc_with_display_col (file_cache
&fc
,
201 const expanded_location
&exploc
,
202 const cpp_char_column_policy
&policy
,
203 enum location_aspect aspect
)
204 : expanded_location (exploc
),
205 m_display_col (location_compute_display_column (fc
, exploc
, policy
))
207 if (exploc
.column
> 0)
209 /* m_display_col is now the final column of the byte.
210 If escaping has happened, we may want the first column instead. */
211 if (aspect
!= LOCATION_ASPECT_FINISH
)
213 expanded_location
prev_exploc (exploc
);
214 prev_exploc
.column
--;
216 = (location_compute_display_column (fc
, prev_exploc
, policy
));
217 m_display_col
= prev_display_col
+ 1;
226 /* A point within a layout_range; similar to an exploc_with_display_col,
227 but after filtering on file. */
232 layout_point (const exploc_with_display_col
&exploc
)
233 : m_line (exploc
.line
)
235 m_columns
[CU_BYTES
] = exploc
.column
;
236 m_columns
[CU_DISPLAY_COLS
] = exploc
.m_display_col
;
240 int m_columns
[CU_NUM_UNITS
];
243 /* A class for use by "class layout" below: a filtered location_range. */
248 layout_range (const exploc_with_display_col
&start_exploc
,
249 const exploc_with_display_col
&finish_exploc
,
250 enum range_display_kind range_display_kind
,
251 const exploc_with_display_col
&caret_exploc
,
252 unsigned original_idx
,
253 const range_label
*label
);
255 bool contains_point (linenum_type row
, int column
,
256 enum column_unit col_unit
) const;
257 bool intersects_line_p (linenum_type row
) const;
259 bool has_in_edge () const;
260 bool has_out_edge () const;
262 layout_point m_start
;
263 layout_point m_finish
;
264 enum range_display_kind m_range_display_kind
;
265 layout_point m_caret
;
266 unsigned m_original_idx
;
267 const range_label
*m_label
;
270 /* A struct for use by layout::print_source_line for telling
271 layout::print_annotation_line the extents of the source line that
272 it printed, so that underlines can be clipped appropriately. Units
273 are 1-based display columns. */
277 int m_first_non_ws_disp_col
;
278 int m_last_non_ws_disp_col
;
282 m_first_non_ws_disp_col
= INT_MAX
;
283 m_last_non_ws_disp_col
= 0;
287 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
288 or "line 23"). During the layout ctor, layout::calculate_line_spans
289 splits the pertinent source lines into a list of disjoint line_span
290 instances (e.g. lines 5-10, lines 15-20, line 23). */
295 line_span (linenum_type first_line
, linenum_type last_line
)
296 : m_first_line (first_line
), m_last_line (last_line
)
298 gcc_assert (first_line
<= last_line
);
300 linenum_type
get_first_line () const { return m_first_line
; }
301 linenum_type
get_last_line () const { return m_last_line
; }
303 bool contains_line_p (linenum_type line
) const
305 return line
>= m_first_line
&& line
<= m_last_line
;
308 static int comparator (const void *p1
, const void *p2
)
310 const line_span
*ls1
= (const line_span
*)p1
;
311 const line_span
*ls2
= (const line_span
*)p2
;
312 int first_line_cmp
= compare (ls1
->m_first_line
, ls2
->m_first_line
);
314 return first_line_cmp
;
315 return compare (ls1
->m_last_line
, ls2
->m_last_line
);
318 linenum_type m_first_line
;
319 linenum_type m_last_line
;
324 /* Selftests for line_span. */
329 line_span
line_one (1, 1);
330 ASSERT_EQ (1, line_one
.get_first_line ());
331 ASSERT_EQ (1, line_one
.get_last_line ());
332 ASSERT_FALSE (line_one
.contains_line_p (0));
333 ASSERT_TRUE (line_one
.contains_line_p (1));
334 ASSERT_FALSE (line_one
.contains_line_p (2));
336 line_span
lines_1_to_3 (1, 3);
337 ASSERT_EQ (1, lines_1_to_3
.get_first_line ());
338 ASSERT_EQ (3, lines_1_to_3
.get_last_line ());
339 ASSERT_TRUE (lines_1_to_3
.contains_line_p (1));
340 ASSERT_TRUE (lines_1_to_3
.contains_line_p (3));
342 ASSERT_EQ (0, line_span::comparator (&line_one
, &line_one
));
343 ASSERT_GT (line_span::comparator (&lines_1_to_3
, &line_one
), 0);
344 ASSERT_LT (line_span::comparator (&line_one
, &lines_1_to_3
), 0);
346 /* A linenum > 2^31. */
347 const linenum_type LARGEST_LINE
= 0xffffffff;
348 line_span
largest_line (LARGEST_LINE
, LARGEST_LINE
);
349 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_first_line ());
350 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_last_line ());
352 ASSERT_GT (line_span::comparator (&largest_line
, &line_one
), 0);
353 ASSERT_LT (line_span::comparator (&line_one
, &largest_line
), 0);
356 #endif /* #if CHECKING_P */
358 /* A bundle of information containing how to print unicode
359 characters and bytes when quoting source code.
361 Provides a unified place to support escaping some subset
362 of characters to some format.
364 Extends char_column_policy; printing is split out to avoid
365 libcpp having to know about pretty_printer. */
367 struct char_display_policy
: public cpp_char_column_policy
370 char_display_policy (int tabstop
,
371 int (*width_cb
) (cppchar_t c
),
372 void (*print_cb
) (pretty_printer
*pp
,
373 const cpp_decoded_char
&cp
))
374 : cpp_char_column_policy (tabstop
, width_cb
),
375 m_print_cb (print_cb
)
379 void (*m_print_cb
) (pretty_printer
*pp
,
380 const cpp_decoded_char
&cp
);
383 /* A class to control the overall layout when printing a diagnostic.
385 The layout is determined within the constructor.
387 Printing the layout is handled by class layout_printer. This separation
388 is to avoid depending on the pretty_printer in the layout.
390 We assume we have disjoint ranges. */
395 friend class layout_printer
;
397 layout (const diagnostic_source_print_policy
&source_policy
,
398 const rich_location
&richloc
,
399 diagnostic_source_effect_info
*effect_info
= nullptr);
401 bool maybe_add_location_range (const location_range
*loc_range
,
402 unsigned original_idx
,
403 bool restrict_to_current_line_spans
);
405 int get_num_line_spans () const { return m_line_spans
.length (); }
406 const line_span
*get_line_span (int idx
) const { return &m_line_spans
[idx
]; }
408 int get_linenum_width () const { return m_linenum_width
; }
409 int get_x_offset_display () const { return m_x_offset_display
; }
411 bool print_heading_for_line_span_index_p (int line_span_idx
) const;
413 expanded_location
get_expanded_location (const line_span
*) const;
415 void on_bad_codepoint (const char *ptr
, cppchar_t ch
, size_t ch_sz
);
418 bool will_show_line_p (linenum_type row
) const;
419 bool should_print_annotation_line_p (linenum_type row
) const;
421 bool annotation_line_showed_range_p (linenum_type line
, int start_column
,
422 int finish_column
) const;
423 bool validate_fixit_hint_p (const fixit_hint
*hint
);
425 void calculate_line_spans ();
426 void calculate_linenum_width ();
427 void calculate_x_offset_display ();
430 get_state_at_point (/* Inputs. */
431 linenum_type row
, int column
,
432 int first_non_ws
, int last_non_ws
,
433 enum column_unit col_unit
,
435 point_state
*out_state
) const;
438 get_x_bound_for_row (linenum_type row
, int caret_column
,
439 int last_non_ws
) const;
442 bool compatible_locations_p (location_t loc_a
, location_t loc_b
) const;
444 const diagnostic_source_printing_options
&m_options
;
445 const line_maps
*m_line_table
;
446 file_cache
&m_file_cache
;
447 const text_art::ascii_theme m_fallback_theme
;
448 const text_art::theme
&m_theme
;
449 diagnostic_source_effect_info
*m_effect_info
;
450 char_display_policy m_char_policy
;
451 location_t m_primary_loc
;
452 exploc_with_display_col m_exploc
;
453 auto_vec
<layout_range
> m_layout_ranges
;
454 auto_vec
<const fixit_hint
*> m_fixit_hints
;
455 auto_vec
<line_span
> m_line_spans
;
457 int m_x_offset_display
;
458 bool m_escape_on_output
;
461 /* A bundle of state for printing a particular layout
462 to a particular pretty_printer. */
467 layout_printer (pretty_printer
&pp
,
468 const layout
&layout
,
469 const rich_location
&richloc
,
470 diagnostic_t diagnostic_kind
);
472 void print (const diagnostic_source_print_policy
&source_policy
);
475 const diagnostic_source_printing_options
&
476 get_options () const { return m_layout
.m_options
; }
478 const text_art::theme
&
479 get_theme () const { return m_layout
.m_theme
; }
481 void show_ruler (int max_column
);
482 void print_gap_in_line_numbering ();
483 void print_leading_fixits (linenum_type row
);
484 void print_line (linenum_type row
);
485 line_bounds
print_source_line (linenum_type row
, const char *line
,
487 void print_leftmost_column ();
488 void start_annotation_line (char margin_char
= ' ');
489 void print_annotation_line (linenum_type row
, const line_bounds lbounds
);
490 void print_any_labels (linenum_type row
);
491 void print_trailing_fixits (linenum_type row
);
492 void print_newline ();
495 move_to_column (int *column
, int dest_column
, bool add_left_margin
);
497 void print_any_right_to_left_edge_lines ();
500 pretty_printer
&m_pp
;
501 const layout
&m_layout
;
502 colorizer m_colorizer
;
503 bool m_is_diagnostic_path
;
505 /* Fields for handling links between labels (e.g. for showing CFG edges
507 Note that the logic for printing such links makes various simplifying
508 assumptions about the set of labels in the rich_location, and users
509 of this code will need to split up labels into separate rich_location
510 instances to respect these assumptions, or the output will look wrong.
511 See the diagnostic_path-printing code for more information. */
513 /* An enum for describing the state of the leftmost column,
514 used for showing links between labels.
516 .x0000000001111111111222222222233333333334444444444.
517 .x1234567890123456789012345678901234567890123456789.
519 | (9) following ‘false’ branch... ->-+ <- none
522 |+----------------------------------------+ <- rewinding to lhs
523 || result->i = i; <- at lhs
524 || ~~~~~~~~~~^~~ <- at lhs
526 |+----------->(10) ...to here <- indenting to dest
529 |leftmost column ("x" above).
531 enum class link_lhs_state
{
538 /* The column of the current link on the RHS, if any, or
541 .x0000000001111111111222222222233333333334444444444.
542 .x1234567890123456789012345678901234567890123456789.
544 | (10) following ‘false’ branch... ->-+ <- 42
547 |+-----------------------------------------+ <- 42
548 || result->i = i; <- -1
549 || ~~~~~~~~~~^~~ <- -1
551 |+----------->(11) ...to here <- -1. */
552 int m_link_rhs_column
;
555 /* Implementation of "class colorizer". */
557 /* The constructor for "colorizer". Lookup and store color codes for the
558 different kinds of things we might need to print. */
560 colorizer::colorizer (pretty_printer
&pp
,
561 const rich_location
&richloc
,
562 diagnostic_t diagnostic_kind
) :
565 m_diagnostic_kind (diagnostic_kind
),
566 m_current_state (STATE_NORMAL_TEXT
)
568 m_range1
= get_color_by_name ("range1");
569 m_range2
= get_color_by_name ("range2");
570 m_fixit_insert
= get_color_by_name ("fixit-insert");
571 m_fixit_delete
= get_color_by_name ("fixit-delete");
572 m_stop_color
= colorize_stop (pp_show_color (&m_pp
));
575 /* The destructor for "colorize". If colorization is on, print a code to
578 colorizer::~colorizer ()
580 finish_state (m_current_state
);
583 /* Update state, changing to the specific named color and printing its
587 colorizer::set_named_color (const char *color
)
589 finish_state (m_current_state
);
590 m_current_state
= STATE_NAMED_COLOR
;
591 pp_string (&m_pp
, colorize_start (pp_show_color (&m_pp
), color
));
594 /* Update state, printing color codes if necessary if there's a state
598 colorizer::set_state (int new_state
)
600 if (m_current_state
!= new_state
)
602 finish_state (m_current_state
);
603 m_current_state
= new_state
;
604 begin_state (new_state
);
608 /* Turn on any colorization for STATE. */
611 colorizer::begin_state (int state
)
615 case STATE_NORMAL_TEXT
:
618 case STATE_FIXIT_INSERT
:
619 pp_string (&m_pp
, m_fixit_insert
);
622 case STATE_FIXIT_DELETE
:
623 pp_string (&m_pp
, m_fixit_delete
);
626 case STATE_NAMED_COLOR
:
627 /* Should be handled by colorizer::set_named_color. */
631 /* Make range 0 be the same color as the "kind" text
632 (error vs warning vs note). */
635 colorize_start (pp_show_color (&m_pp
),
636 diagnostic_get_color_for_kind (m_diagnostic_kind
)));
640 pp_string (&m_pp
, m_range1
);
644 pp_string (&m_pp
, m_range2
);
648 /* For ranges beyond 2, alternate between color 1 and color 2. */
650 gcc_assert (state
> 2);
652 state
% 2 ? m_range1
: m_range2
);
658 /* Turn off any colorization for STATE. */
661 colorizer::finish_state (int state
)
663 if (state
!= STATE_NORMAL_TEXT
)
664 pp_string (&m_pp
, m_stop_color
);
667 /* Get the color code for NAME (or the empty string if
668 colorization is disabled). */
671 colorizer::get_color_by_name (const char *name
)
673 return colorize_start (pp_show_color (&m_pp
), name
);
676 /* Implementation of class layout_range. */
678 /* The constructor for class layout_range.
679 Initialize various layout_point fields from expanded_location
680 equivalents; we've already filtered on file. */
682 layout_range::layout_range (const exploc_with_display_col
&start_exploc
,
683 const exploc_with_display_col
&finish_exploc
,
684 enum range_display_kind range_display_kind
,
685 const exploc_with_display_col
&caret_exploc
,
686 unsigned original_idx
,
687 const range_label
*label
)
688 : m_start (start_exploc
),
689 m_finish (finish_exploc
),
690 m_range_display_kind (range_display_kind
),
691 m_caret (caret_exploc
),
692 m_original_idx (original_idx
),
697 /* Is (column, row) within the given range?
698 We've already filtered on the file.
700 Ranges are closed (both limits are within the range).
702 Example A: a single-line range:
703 start: (col=22, line=2)
704 finish: (col=38, line=2)
706 |00000011111111112222222222333333333344444444444
707 |34567890123456789012345678901234567890123456789
708 --+-----------------------------------------------
709 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
710 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
711 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
713 Example B: a multiline range with
714 start: (col=14, line=3)
715 finish: (col=08, line=5)
717 |00000011111111112222222222333333333344444444444
718 |34567890123456789012345678901234567890123456789
719 --+-----------------------------------------------
720 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
721 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
722 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
723 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
724 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
725 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
726 --+-----------------------------------------------
729 - 'b' indicates a point *before* the range
730 - 'S' indicates the start of the range
731 - 'w' indicates a point within the range
732 - 'F' indicates the finish of the range (which is
734 - 'a' indicates a subsequent point *after* the range.
736 COL_UNIT controls whether we check the byte column or
737 the display column; one or the other is more convenient
738 depending on the context. */
741 layout_range::contains_point (linenum_type row
, int column
,
742 enum column_unit col_unit
) const
744 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
745 /* ...but the equivalent isn't true for the columns;
746 consider example B in the comment above. */
748 if (row
< m_start
.m_line
)
749 /* Points before the first line of the range are
750 outside it (corresponding to line 01 in example A
751 and lines 01 and 02 in example B above). */
754 if (row
== m_start
.m_line
)
755 /* On same line as start of range (corresponding
756 to line 02 in example A and line 03 in example B). */
758 if (column
< m_start
.m_columns
[col_unit
])
759 /* Points on the starting line of the range, but
760 before the column in which it begins. */
763 if (row
< m_finish
.m_line
)
764 /* This is a multiline range; the point
765 is within it (corresponds to line 03 in example B
766 from column 14 onwards) */
770 /* This is a single-line range. */
771 gcc_assert (row
== m_finish
.m_line
);
772 return column
<= m_finish
.m_columns
[col_unit
];
776 /* The point is in a line beyond that containing the
777 start of the range: lines 03 onwards in example A,
778 and lines 04 onwards in example B. */
779 gcc_assert (row
> m_start
.m_line
);
781 if (row
> m_finish
.m_line
)
782 /* The point is beyond the final line of the range
783 (lines 03 onwards in example A, and lines 06 onwards
787 if (row
< m_finish
.m_line
)
789 /* The point is in a line that's fully within a multiline
790 range (e.g. line 04 in example B). */
791 gcc_assert (m_start
.m_line
< m_finish
.m_line
);
795 gcc_assert (row
== m_finish
.m_line
);
797 return column
<= m_finish
.m_columns
[col_unit
];
800 /* Does this layout_range contain any part of line ROW? */
803 layout_range::intersects_line_p (linenum_type row
) const
805 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
806 if (row
< m_start
.m_line
)
808 if (row
> m_finish
.m_line
)
813 /* Return true if this layout_range should have an in-edge. */
816 layout_range::has_in_edge () const
820 const label_effects
*effects
= m_label
->get_effects (m_original_idx
);
824 return effects
->has_in_edge (m_original_idx
);
827 /* Return true if this layout_range should have an out-edge. */
830 layout_range::has_out_edge () const
834 const label_effects
*effects
= m_label
->get_effects (m_original_idx
);
838 return effects
->has_out_edge (m_original_idx
);
843 /* Default for when we don't care what the tab expansion is set to. */
844 static const int def_tabstop
= 8;
846 static cpp_char_column_policy
def_policy ()
848 return cpp_char_column_policy (def_tabstop
, cpp_wcwidth
);
851 /* Create some expanded locations for testing layout_range. The filename
852 member of the explocs is set to the empty string. This member will only be
853 inspected by the calls to location_compute_display_column() made from the
854 layout_point constructors. That function will check for an empty filename
855 argument and not attempt to open it, rather treating the non-existent data
856 as if the display width were the same as the byte count. Tests exercising a
857 real difference between byte count and display width are performed later,
858 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
861 make_range (file_cache
&fc
,
862 int start_line
, int start_col
, int end_line
, int end_col
)
864 const expanded_location start_exploc
865 = {"", start_line
, start_col
, NULL
, false};
866 const expanded_location finish_exploc
867 = {"", end_line
, end_col
, NULL
, false};
868 return layout_range (exploc_with_display_col (fc
,
869 start_exploc
, def_policy (),
870 LOCATION_ASPECT_START
),
871 exploc_with_display_col (fc
,
872 finish_exploc
, def_policy (),
873 LOCATION_ASPECT_FINISH
),
874 SHOW_RANGE_WITHOUT_CARET
,
875 exploc_with_display_col (fc
,
876 start_exploc
, def_policy (),
877 LOCATION_ASPECT_CARET
),
881 /* Selftests for layout_range::contains_point and
882 layout_range::intersects_line_p. */
884 /* Selftest for layout_range, where the layout_range
885 is a range with start==end i.e. a single point. */
888 test_layout_range_for_single_point ()
891 layout_range point
= make_range (fc
, 7, 10, 7, 10);
893 /* Tests for layout_range::contains_point. */
895 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
897 const enum column_unit col_unit
= (enum column_unit
) i
;
899 /* Before the line. */
900 ASSERT_FALSE (point
.contains_point (6, 1, col_unit
));
902 /* On the line, but before start. */
903 ASSERT_FALSE (point
.contains_point (7, 9, col_unit
));
906 ASSERT_TRUE (point
.contains_point (7, 10, col_unit
));
908 /* On the line, after the point. */
909 ASSERT_FALSE (point
.contains_point (7, 11, col_unit
));
911 /* After the line. */
912 ASSERT_FALSE (point
.contains_point (8, 1, col_unit
));
915 /* Tests for layout_range::intersects_line_p. */
916 ASSERT_FALSE (point
.intersects_line_p (6));
917 ASSERT_TRUE (point
.intersects_line_p (7));
918 ASSERT_FALSE (point
.intersects_line_p (8));
921 /* Selftest for layout_range, where the layout_range
922 is the single-line range shown as "Example A" above. */
925 test_layout_range_for_single_line ()
928 layout_range example_a
= make_range (fc
, 2, 22, 2, 38);
930 /* Tests for layout_range::contains_point. */
932 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
934 const enum column_unit col_unit
= (enum column_unit
) i
;
936 /* Before the line. */
937 ASSERT_FALSE (example_a
.contains_point (1, 1, col_unit
));
939 /* On the line, but before start. */
940 ASSERT_FALSE (example_a
.contains_point (2, 21, col_unit
));
942 /* On the line, at the start. */
943 ASSERT_TRUE (example_a
.contains_point (2, 22, col_unit
));
945 /* On the line, within the range. */
946 ASSERT_TRUE (example_a
.contains_point (2, 23, col_unit
));
948 /* On the line, at the end. */
949 ASSERT_TRUE (example_a
.contains_point (2, 38, col_unit
));
951 /* On the line, after the end. */
952 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
954 /* After the line. */
955 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
958 /* Tests for layout_range::intersects_line_p. */
959 ASSERT_FALSE (example_a
.intersects_line_p (1));
960 ASSERT_TRUE (example_a
.intersects_line_p (2));
961 ASSERT_FALSE (example_a
.intersects_line_p (3));
964 /* Selftest for layout_range, where the layout_range
965 is the multi-line range shown as "Example B" above. */
968 test_layout_range_for_multiple_lines ()
971 layout_range example_b
= make_range (fc
, 3, 14, 5, 8);
973 /* Tests for layout_range::contains_point. */
975 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
977 const enum column_unit col_unit
= (enum column_unit
) i
;
979 /* Before first line. */
980 ASSERT_FALSE (example_b
.contains_point (1, 1, col_unit
));
982 /* On the first line, but before start. */
983 ASSERT_FALSE (example_b
.contains_point (3, 13, col_unit
));
986 ASSERT_TRUE (example_b
.contains_point (3, 14, col_unit
));
988 /* On the first line, within the range. */
989 ASSERT_TRUE (example_b
.contains_point (3, 15, col_unit
));
991 /* On an interior line.
992 The column number should not matter; try various boundary
994 ASSERT_TRUE (example_b
.contains_point (4, 1, col_unit
));
995 ASSERT_TRUE (example_b
.contains_point (4, 7, col_unit
));
996 ASSERT_TRUE (example_b
.contains_point (4, 8, col_unit
));
997 ASSERT_TRUE (example_b
.contains_point (4, 9, col_unit
));
998 ASSERT_TRUE (example_b
.contains_point (4, 13, col_unit
));
999 ASSERT_TRUE (example_b
.contains_point (4, 14, col_unit
));
1000 ASSERT_TRUE (example_b
.contains_point (4, 15, col_unit
));
1002 /* On the final line, before the end. */
1003 ASSERT_TRUE (example_b
.contains_point (5, 7, col_unit
));
1005 /* On the final line, at the end. */
1006 ASSERT_TRUE (example_b
.contains_point (5, 8, col_unit
));
1008 /* On the final line, after the end. */
1009 ASSERT_FALSE (example_b
.contains_point (5, 9, col_unit
));
1011 /* After the line. */
1012 ASSERT_FALSE (example_b
.contains_point (6, 1, col_unit
));
1015 /* Tests for layout_range::intersects_line_p. */
1016 ASSERT_FALSE (example_b
.intersects_line_p (2));
1017 ASSERT_TRUE (example_b
.intersects_line_p (3));
1018 ASSERT_TRUE (example_b
.intersects_line_p (4));
1019 ASSERT_TRUE (example_b
.intersects_line_p (5));
1020 ASSERT_FALSE (example_b
.intersects_line_p (6));
1023 #endif /* #if CHECKING_P */
1025 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
1026 (still in bytes, not display cols) without any trailing whitespace. */
1029 get_line_bytes_without_trailing_whitespace (const char *line
, int line_bytes
)
1031 int result
= line_bytes
;
1034 char ch
= line
[result
- 1];
1035 if (ch
== ' ' || ch
== '\t' || ch
== '\r')
1040 gcc_assert (result
>= 0);
1041 gcc_assert (result
<= line_bytes
);
1042 gcc_assert (result
== 0 ||
1043 (line
[result
- 1] != ' '
1044 && line
[result
-1] != '\t'
1045 && line
[result
-1] != '\r'));
1051 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
1054 assert_eq (const char *line
, int expected_bytes
)
1057 = get_line_bytes_without_trailing_whitespace (line
, strlen (line
));
1058 ASSERT_EQ (actual_value
, expected_bytes
);
1061 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
1062 various inputs. It is not required to handle newlines. */
1065 test_get_line_bytes_without_trailing_whitespace ()
1069 assert_eq ("\t", 0);
1070 assert_eq ("\r", 0);
1071 assert_eq ("hello world", 11);
1072 assert_eq ("hello world ", 11);
1073 assert_eq ("hello world \t\t ", 11);
1074 assert_eq ("hello world\r", 11);
1077 #endif /* #if CHECKING_P */
1079 /* Helper function for layout's ctor, for sanitizing locations relative
1080 to the primary location within a diagnostic.
1082 Compare LOC_A and LOC_B to see if it makes sense to print underlines
1083 connecting their expanded locations. Doing so is only guaranteed to
1084 make sense if the locations share the same macro expansion "history"
1085 i.e. they can be traced through the same macro expansions, eventually
1086 reaching an ordinary map.
1088 This may be too strong a condition, but it effectively sanitizes
1089 PR c++/70105, which has an example of printing an expression where the
1090 final location of the expression is in a different macro, which
1091 erroneously was leading to hundreds of lines of irrelevant source
1095 layout::compatible_locations_p (location_t loc_a
, location_t loc_b
) const
1097 if (IS_ADHOC_LOC (loc_a
))
1098 loc_a
= get_location_from_adhoc_loc (m_line_table
, loc_a
);
1099 if (IS_ADHOC_LOC (loc_b
))
1100 loc_b
= get_location_from_adhoc_loc (m_line_table
, loc_b
);
1102 /* If either location is one of the special locations outside of a
1103 linemap, they are only compatible if they are equal. */
1104 if (loc_a
< RESERVED_LOCATION_COUNT
1105 || loc_b
< RESERVED_LOCATION_COUNT
)
1106 return loc_a
== loc_b
;
1108 const line_map
*map_a
= linemap_lookup (m_line_table
, loc_a
);
1109 linemap_assert (map_a
);
1111 const line_map
*map_b
= linemap_lookup (m_line_table
, loc_b
);
1112 linemap_assert (map_b
);
1114 /* Are they within the same map? */
1117 /* Are both within the same macro expansion? */
1118 if (linemap_macro_expansion_map_p (map_a
))
1120 /* If so, then they're only compatible if either both are
1121 from the macro definition, or both from the macro arguments. */
1122 bool loc_a_from_defn
1123 = linemap_location_from_macro_definition_p (m_line_table
, loc_a
);
1124 bool loc_b_from_defn
1125 = linemap_location_from_macro_definition_p (m_line_table
, loc_b
);
1126 if (loc_a_from_defn
!= loc_b_from_defn
)
1129 /* Expand each location towards the spelling location, and
1131 const line_map_macro
*macro_map
= linemap_check_macro (map_a
);
1132 location_t loc_a_toward_spelling
1133 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table
,
1136 location_t loc_b_toward_spelling
1137 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table
,
1140 return compatible_locations_p (loc_a_toward_spelling
,
1141 loc_b_toward_spelling
);
1144 /* Otherwise they are within the same ordinary map. */
1149 /* Within different maps. */
1151 /* If either is within a macro expansion, they are incompatible. */
1152 if (linemap_macro_expansion_map_p (map_a
)
1153 || linemap_macro_expansion_map_p (map_b
))
1156 /* Within two different ordinary maps; they are compatible iff they
1157 are in the same file. */
1158 const line_map_ordinary
*ord_map_a
= linemap_check_ordinary (map_a
);
1159 const line_map_ordinary
*ord_map_b
= linemap_check_ordinary (map_b
);
1160 return ord_map_a
->to_file
== ord_map_b
->to_file
;
1164 /* Comparator for sorting fix-it hints. */
1167 fixit_cmp (const void *p_a
, const void *p_b
)
1169 const fixit_hint
* hint_a
= *static_cast<const fixit_hint
* const *> (p_a
);
1170 const fixit_hint
* hint_b
= *static_cast<const fixit_hint
* const *> (p_b
);
1171 return hint_a
->get_start_loc () - hint_b
->get_start_loc ();
1174 /* Callbacks for use when not escaping the source. */
1176 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1178 /* Callback for char_display_policy::m_print_cb for printing source chars
1179 when not escaping the source. */
1182 default_print_decoded_ch (pretty_printer
*pp
,
1183 const cpp_decoded_char
&decoded_ch
)
1185 for (const char *ptr
= decoded_ch
.m_start_byte
;
1186 ptr
!= decoded_ch
.m_next_byte
; ptr
++)
1188 if (*ptr
== '\0' || *ptr
== '\r')
1194 pp_character (pp
, *ptr
);
1198 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1200 static const int width_per_escaped_byte
= 4;
1202 /* Callback for char_column_policy::m_width_cb for determining the
1203 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1206 escape_as_bytes_width (cppchar_t ch
)
1208 if (ch
< 0x80 && ISPRINT (ch
))
1209 return cpp_wcwidth (ch
);
1212 if (ch
<= 0x7F) return 1 * width_per_escaped_byte
;
1213 if (ch
<= 0x7FF) return 2 * width_per_escaped_byte
;
1214 if (ch
<= 0xFFFF) return 3 * width_per_escaped_byte
;
1215 return 4 * width_per_escaped_byte
;
1219 /* Callback for char_display_policy::m_print_cb for printing source chars
1220 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1223 escape_as_bytes_print (pretty_printer
*pp
,
1224 const cpp_decoded_char
&decoded_ch
)
1226 if (!decoded_ch
.m_valid_ch
)
1228 for (const char *iter
= decoded_ch
.m_start_byte
;
1229 iter
!= decoded_ch
.m_next_byte
; ++iter
)
1232 sprintf (buf
, "<%02x>", (unsigned char)*iter
);
1233 pp_string (pp
, buf
);
1238 cppchar_t ch
= decoded_ch
.m_ch
;
1239 if (ch
< 0x80 && ISPRINT (ch
))
1240 pp_character (pp
, ch
);
1243 for (const char *iter
= decoded_ch
.m_start_byte
;
1244 iter
< decoded_ch
.m_next_byte
; ++iter
)
1247 sprintf (buf
, "<%02x>", (unsigned char)*iter
);
1248 pp_string (pp
, buf
);
1253 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1255 /* Callback for char_column_policy::m_width_cb for determining the
1256 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1259 escape_as_unicode_width (cppchar_t ch
)
1261 if (ch
< 0x80 && ISPRINT (ch
))
1262 return cpp_wcwidth (ch
);
1265 // Width of "<U+%04x>"
1268 else if (ch
> 0xffff)
1275 /* Callback for char_display_policy::m_print_cb for printing source chars
1276 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1279 escape_as_unicode_print (pretty_printer
*pp
,
1280 const cpp_decoded_char
&decoded_ch
)
1282 if (!decoded_ch
.m_valid_ch
)
1284 escape_as_bytes_print (pp
, decoded_ch
);
1288 cppchar_t ch
= decoded_ch
.m_ch
;
1289 if (ch
< 0x80 && ISPRINT (ch
))
1290 pp_character (pp
, ch
);
1294 sprintf (buf
, "<U+%04X>", ch
);
1295 pp_string (pp
, buf
);
1299 /* Populate a char_display_policy based on SOURCE_POLICY and RICHLOC. */
1301 static char_display_policy
1302 make_char_policy (const diagnostic_source_print_policy
&source_policy
,
1303 const rich_location
&richloc
)
1305 /* The default is to not escape non-ASCII bytes. */
1306 char_display_policy result
1307 (source_policy
.get_column_policy ().get_tabstop (),
1309 default_print_decoded_ch
);
1311 /* If the diagnostic suggests escaping non-ASCII bytes, then
1312 use policy from user-supplied options. */
1313 if (richloc
.escape_on_output_p ())
1315 result
.m_undecoded_byte_width
= width_per_escaped_byte
;
1316 switch (source_policy
.get_escape_format ())
1320 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
:
1321 result
.m_width_cb
= escape_as_unicode_width
;
1322 result
.m_print_cb
= escape_as_unicode_print
;
1324 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES
:
1325 result
.m_width_cb
= escape_as_bytes_width
;
1326 result
.m_print_cb
= escape_as_bytes_print
;
1334 /* Implementation of class layout. */
1336 /* Constructor for class layout.
1338 Filter the ranges from the rich_location to those that we can
1339 sanely print, populating m_layout_ranges and m_fixit_hints.
1340 Determine the range of lines that we will print, splitting them
1341 up into an ordered list of disjoint spans of contiguous line numbers.
1342 Determine m_x_offset_display, to ensure that the primary caret
1343 will fit within the max_width provided by the diagnostic_context. */
1345 layout::layout (const diagnostic_source_print_policy
&source_policy
,
1346 const rich_location
&richloc
,
1347 diagnostic_source_effect_info
*effect_info
)
1348 : m_options (source_policy
.get_options ()),
1349 m_line_table (richloc
.get_line_table ()),
1350 m_file_cache (source_policy
.get_file_cache ()),
1351 /* Ensure we have a non-null m_theme. */
1352 m_theme (source_policy
.get_diagram_theme ()
1353 ? *source_policy
.get_diagram_theme ()
1354 : *static_cast <const text_art::theme
*> (&m_fallback_theme
)),
1355 m_effect_info (effect_info
),
1356 m_char_policy (make_char_policy (source_policy
, richloc
)),
1357 m_primary_loc (richloc
.get_range (0)->m_loc
),
1358 m_exploc (m_file_cache
,
1359 richloc
.get_expanded_location (0), m_char_policy
,
1360 LOCATION_ASPECT_CARET
),
1361 m_layout_ranges (richloc
.get_num_locations ()),
1362 m_fixit_hints (richloc
.get_num_fixit_hints ()),
1363 m_line_spans (1 + richloc
.get_num_locations ()),
1364 m_linenum_width (0),
1365 m_x_offset_display (0),
1366 m_escape_on_output (richloc
.escape_on_output_p ())
1368 for (unsigned int idx
= 0; idx
< richloc
.get_num_locations (); idx
++)
1370 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1371 Ignore any ranges that are awkward to handle. */
1372 const location_range
*loc_range
= richloc
.get_range (idx
);
1373 maybe_add_location_range (loc_range
, idx
, false);
1376 /* Populate m_fixit_hints, filtering to only those that are in the
1378 for (unsigned int i
= 0; i
< richloc
.get_num_fixit_hints (); i
++)
1380 const fixit_hint
*hint
= richloc
.get_fixit_hint (i
);
1381 if (validate_fixit_hint_p (hint
))
1382 m_fixit_hints
.safe_push (hint
);
1385 /* Sort m_fixit_hints. */
1386 m_fixit_hints
.qsort (fixit_cmp
);
1388 /* Populate the indicated members. */
1389 calculate_line_spans ();
1390 calculate_linenum_width ();
1391 calculate_x_offset_display ();
1395 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1396 those that we can sanely print.
1398 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1399 (for use as extrinsic state by label ranges).
1401 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1402 filtered against this layout instance's current line spans: it
1403 will only be added if the location is fully within the lines
1404 already specified by other locations.
1406 Return true iff LOC_RANGE was added. */
1409 layout::maybe_add_location_range (const location_range
*loc_range
,
1410 unsigned original_idx
,
1411 bool restrict_to_current_line_spans
)
1413 gcc_assert (loc_range
);
1415 /* Split the "range" into caret and range information. */
1416 source_range src_range
= get_range_from_loc (m_line_table
, loc_range
->m_loc
);
1418 /* Expand the various locations. */
1419 expanded_location start
1420 = linemap_client_expand_location_to_spelling_point
1421 (m_line_table
, src_range
.m_start
, LOCATION_ASPECT_START
);
1422 expanded_location finish
1423 = linemap_client_expand_location_to_spelling_point
1424 (m_line_table
, src_range
.m_finish
, LOCATION_ASPECT_FINISH
);
1425 expanded_location caret
1426 = linemap_client_expand_location_to_spelling_point
1427 (m_line_table
, loc_range
->m_loc
, LOCATION_ASPECT_CARET
);
1429 /* If any part of the range isn't in the same file as the primary
1430 location of this diagnostic, ignore the range. */
1431 if (start
.file
!= m_exploc
.file
)
1433 if (finish
.file
!= m_exploc
.file
)
1435 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1436 if (caret
.file
!= m_exploc
.file
)
1439 /* Sanitize the caret location for non-primary ranges. */
1440 if (m_layout_ranges
.length () > 0)
1441 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1442 if (!compatible_locations_p (loc_range
->m_loc
, m_primary_loc
))
1443 /* Discard any non-primary ranges that can't be printed
1444 sanely relative to the primary location. */
1447 /* If there's no column information, then don't try to print
1448 annotation lines for this range. */
1449 enum range_display_kind range_display_kind
1450 = loc_range
->m_range_display_kind
;
1451 if (start
.column
== 0
1452 || finish
.column
== 0
1453 || caret
.column
== 0)
1454 range_display_kind
= SHOW_LINES_WITHOUT_RANGE
;
1456 /* Everything is now known to be in the correct source file,
1457 but it may require further sanitization. */
1458 layout_range
ri (exploc_with_display_col (m_file_cache
,
1459 start
, m_char_policy
,
1460 LOCATION_ASPECT_START
),
1461 exploc_with_display_col (m_file_cache
,
1462 finish
, m_char_policy
,
1463 LOCATION_ASPECT_FINISH
),
1465 exploc_with_display_col (m_file_cache
,
1466 caret
, m_char_policy
,
1467 LOCATION_ASPECT_CARET
),
1468 original_idx
, loc_range
->m_label
);
1470 /* If we have a range that finishes before it starts (perhaps
1471 from something built via macro expansion), printing the
1472 range is likely to be nonsensical. Also, attempting to do so
1473 breaks assumptions within the printing code (PR c/68473).
1474 Similarly, don't attempt to print ranges if one or both ends
1475 of the range aren't sane to print relative to the
1476 primary location (PR c++/70105). */
1477 if (start
.line
> finish
.line
1478 || !compatible_locations_p (src_range
.m_start
, m_primary_loc
)
1479 || !compatible_locations_p (src_range
.m_finish
, m_primary_loc
))
1481 /* Is this the primary location? */
1482 if (m_layout_ranges
.length () == 0)
1484 /* We want to print the caret for the primary location, but
1485 we must sanitize away m_start and m_finish. */
1486 ri
.m_start
= ri
.m_caret
;
1487 ri
.m_finish
= ri
.m_caret
;
1490 /* This is a non-primary range; ignore it. */
1494 /* Potentially filter to just the lines already specified by other
1495 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1496 The layout ctor doesn't use it, and can't because m_line_spans
1497 hasn't been set up at that point. */
1498 if (restrict_to_current_line_spans
)
1500 if (!will_show_line_p (start
.line
))
1502 if (!will_show_line_p (finish
.line
))
1504 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1505 if (!will_show_line_p (caret
.line
))
1509 /* Passed all the tests; add the range to m_layout_ranges so that
1510 it will be printed. */
1511 m_layout_ranges
.safe_push (ri
);
1515 /* Return true iff ROW is within one of the line spans for this layout. */
1518 layout::will_show_line_p (linenum_type row
) const
1520 for (int line_span_idx
= 0; line_span_idx
< get_num_line_spans ();
1523 const line_span
*line_span
= get_line_span (line_span_idx
);
1524 if (line_span
->contains_line_p (row
))
1530 /* Print a line showing a gap in the line numbers, for showing the boundary
1531 between two line spans. */
1534 layout_printer::print_gap_in_line_numbering ()
1536 gcc_assert (m_layout
.m_options
.show_line_numbers_p
);
1538 pp_emit_prefix (&m_pp
);
1540 for (int i
= 0; i
< m_layout
.get_linenum_width () + 1; i
++)
1541 pp_character (&m_pp
, '.');
1546 /* Return true iff we should print a heading when starting the
1547 line span with the given index. */
1550 layout::print_heading_for_line_span_index_p (int line_span_idx
) const
1552 /* We print a heading for every change of line span, hence for every
1553 line span after the initial one. */
1554 if (line_span_idx
> 0)
1557 /* We also do it for the initial span if the primary location of the
1558 diagnostic is in a different span. */
1559 if (m_exploc
.line
> (int)get_line_span (0)->m_last_line
)
1565 /* Get an expanded_location for the first location of interest within
1566 the given line_span.
1567 Used when printing a heading to indicate a new line span. */
1570 layout::get_expanded_location (const line_span
*line_span
) const
1572 /* Whenever possible, use the caret location. */
1573 if (line_span
->contains_line_p (m_exploc
.line
))
1576 /* Otherwise, use the start of the first range that's present
1577 within the line_span. */
1578 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1580 const layout_range
*lr
= &m_layout_ranges
[i
];
1581 if (line_span
->contains_line_p (lr
->m_start
.m_line
))
1583 expanded_location exploc
= m_exploc
;
1584 exploc
.line
= lr
->m_start
.m_line
;
1585 exploc
.column
= lr
->m_start
.m_columns
[CU_BYTES
];
1590 /* Otherwise, use the location of the first fixit-hint present within
1592 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1594 const fixit_hint
*hint
= m_fixit_hints
[i
];
1595 location_t loc
= hint
->get_start_loc ();
1596 expanded_location exploc
= expand_location (loc
);
1597 if (line_span
->contains_line_p (exploc
.line
))
1601 /* It should not be possible to have a line span that didn't
1602 contain any of the layout_range or fixit_hint instances. */
1607 /* Determine if HINT is meaningful to print within this layout. */
1610 layout::validate_fixit_hint_p (const fixit_hint
*hint
)
1612 if (LOCATION_FILE (hint
->get_start_loc ()) != m_exploc
.file
)
1614 if (LOCATION_FILE (hint
->get_next_loc ()) != m_exploc
.file
)
1620 /* Determine the range of lines affected by HINT.
1621 This assumes that HINT has already been filtered by
1622 validate_fixit_hint_p, and so affects the correct source file. */
1625 get_line_span_for_fixit_hint (const fixit_hint
*hint
)
1629 int start_line
= LOCATION_LINE (hint
->get_start_loc ());
1631 /* For line-insertion fix-it hints, add the previous line to the
1632 span, to give the user more context on the proposed change. */
1633 if (hint
->ends_with_newline_p ())
1637 return line_span (start_line
,
1638 LOCATION_LINE (hint
->get_next_loc ()));
1641 /* We want to print the pertinent source code at a diagnostic. The
1642 rich_location can contain multiple locations. This will have been
1643 filtered into m_exploc (the caret for the primary location) and
1644 m_layout_ranges, for those ranges within the same source file.
1646 We will print a subset of the lines within the source file in question,
1647 as a collection of "spans" of lines.
1649 This function populates m_line_spans with an ordered, disjoint list of
1650 the line spans of interest.
1652 Printing a gap between line spans takes one line, so, when printing
1653 line numbers, we allow a gap of up to one line between spans when
1654 merging, since it makes more sense to print the source line rather than a
1655 "gap-in-line-numbering" line. When not printing line numbers, it's
1656 better to be more explicit about what's going on, so keeping them as
1657 separate spans is preferred.
1659 For example, if the primary range is on lines 8-10, with secondary ranges
1660 covering lines 5-6 and lines 13-15:
1676 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1678 With line numbering off (with span headers), we want three spans: lines 5-6,
1679 lines 8-10, and lines 13-15. */
1682 layout::calculate_line_spans ()
1684 /* This should only be called once, by the ctor. */
1685 gcc_assert (m_line_spans
.length () == 0);
1687 /* Populate tmp_spans with individual spans, for each of
1688 m_exploc, and for m_layout_ranges. */
1689 auto_vec
<line_span
> tmp_spans (1 + m_layout_ranges
.length ());
1690 tmp_spans
.safe_push (line_span (m_exploc
.line
, m_exploc
.line
));
1691 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1693 const layout_range
*lr
= &m_layout_ranges
[i
];
1694 gcc_assert (lr
->m_start
.m_line
<= lr
->m_finish
.m_line
);
1695 tmp_spans
.safe_push (line_span (lr
->m_start
.m_line
,
1696 lr
->m_finish
.m_line
));
1699 /* Also add spans for any fix-it hints, in case they cover other lines. */
1700 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1702 const fixit_hint
*hint
= m_fixit_hints
[i
];
1704 tmp_spans
.safe_push (get_line_span_for_fixit_hint (hint
));
1708 tmp_spans
.qsort(line_span::comparator
);
1710 /* Now iterate through tmp_spans, copying into m_line_spans, and
1711 combining where possible. */
1712 gcc_assert (tmp_spans
.length () > 0);
1713 m_line_spans
.safe_push (tmp_spans
[0]);
1714 for (unsigned int i
= 1; i
< tmp_spans
.length (); i
++)
1716 line_span
*current
= &m_line_spans
[m_line_spans
.length () - 1];
1717 const line_span
*next
= &tmp_spans
[i
];
1718 gcc_assert (next
->m_first_line
>= current
->m_first_line
);
1719 const int merger_distance
= m_options
.show_line_numbers_p
? 1 : 0;
1720 if ((linenum_arith_t
)next
->m_first_line
1721 <= (linenum_arith_t
)current
->m_last_line
+ 1 + merger_distance
)
1723 /* We can merge them. */
1724 if (next
->m_last_line
> current
->m_last_line
)
1725 current
->m_last_line
= next
->m_last_line
;
1729 /* No merger possible. */
1730 m_line_spans
.safe_push (*next
);
1734 /* Verify the result, in m_line_spans. */
1735 gcc_assert (m_line_spans
.length () > 0);
1736 for (unsigned int i
= 1; i
< m_line_spans
.length (); i
++)
1738 const line_span
*prev
= &m_line_spans
[i
- 1];
1739 const line_span
*next
= &m_line_spans
[i
];
1740 /* The individual spans must be sane. */
1741 gcc_assert (prev
->m_first_line
<= prev
->m_last_line
);
1742 gcc_assert (next
->m_first_line
<= next
->m_last_line
);
1743 /* The spans must be ordered. */
1744 gcc_assert (prev
->m_first_line
< next
->m_first_line
);
1745 /* There must be a gap of at least one line between separate spans. */
1746 gcc_assert ((prev
->m_last_line
+ 1) < next
->m_first_line
);
1750 /* Determine how many display columns need to be reserved for line numbers,
1751 based on the largest line number that will be needed, and populate
1755 layout::calculate_linenum_width ()
1757 gcc_assert (m_line_spans
.length () > 0);
1758 const line_span
*last_span
= &m_line_spans
[m_line_spans
.length () - 1];
1759 int highest_line
= last_span
->m_last_line
;
1760 if (highest_line
< 0)
1762 m_linenum_width
= num_digits (highest_line
);
1763 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1764 if (m_line_spans
.length () > 1)
1765 m_linenum_width
= MAX (m_linenum_width
, 3);
1766 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1767 after the line number. */
1768 m_linenum_width
= MAX (m_linenum_width
, m_options
.min_margin_width
- 1);
1771 /* Calculate m_x_offset_display, which improves readability in case the source
1772 line of interest is longer than the user's display. All lines output will be
1773 shifted to the left (so that their beginning is no longer displayed) by
1774 m_x_offset_display display columns, so that the caret is in a reasonable
1778 layout::calculate_x_offset_display ()
1780 m_x_offset_display
= 0;
1782 const int max_width
= m_options
.max_width
;
1785 /* Nothing to do, the width is not capped. */
1789 const char_span line
= m_file_cache
.get_source_line (m_exploc
.file
,
1793 /* Nothing to do, we couldn't find the source line. */
1796 int caret_display_column
= m_exploc
.m_display_col
;
1797 const int line_bytes
1798 = get_line_bytes_without_trailing_whitespace (line
.get_buffer (),
1800 int eol_display_column
1801 = cpp_display_width (line
.get_buffer (), line_bytes
, m_char_policy
);
1802 if (caret_display_column
> eol_display_column
1803 || !caret_display_column
)
1805 /* This does not make sense, so don't try to do anything in this case. */
1809 /* Adjust caret and eol positions to include the left margin. If we are
1810 outputting line numbers, then the left margin is equal to m_linenum_width
1811 plus three for the " | " which follows it. Otherwise the left margin width
1812 is equal to 1, because layout::print_source_line() will prefix each line
1814 const int source_display_cols
= eol_display_column
;
1815 int left_margin_size
= 1;
1816 if (m_options
.show_line_numbers_p
)
1817 left_margin_size
= m_linenum_width
+ 3;
1818 caret_display_column
+= left_margin_size
;
1819 eol_display_column
+= left_margin_size
;
1821 if (eol_display_column
<= max_width
)
1823 /* Nothing to do, everything fits in the display. */
1827 /* The line is too long for the display. Calculate an offset such that the
1828 caret is not too close to the right edge of the screen. It will be
1829 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1830 than that to the end of the source line anyway. */
1831 int right_margin_size
= CARET_LINE_MARGIN
;
1832 right_margin_size
= MIN (eol_display_column
- caret_display_column
,
1834 if (right_margin_size
+ left_margin_size
>= max_width
)
1836 /* The max_width is very small, so anything we try to do will not be very
1837 effective; just punt in this case and output with no offset. */
1840 const int max_caret_display_column
= max_width
- right_margin_size
;
1841 if (caret_display_column
> max_caret_display_column
)
1843 m_x_offset_display
= caret_display_column
- max_caret_display_column
;
1844 /* Make sure we don't offset the line into oblivion. */
1845 static const int min_cols_visible
= 2;
1846 if (source_display_cols
- m_x_offset_display
< min_cols_visible
)
1847 m_x_offset_display
= 0;
1851 /* Print line ROW of source code, potentially colorized at any ranges, and
1852 return the line bounds. LINE is the source line (not necessarily
1853 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1854 colorization and tab expansion, this function tracks the line position in
1855 both byte and display column units. */
1858 layout_printer::print_source_line (linenum_type row
, const char *line
, int line_bytes
)
1860 m_colorizer
.set_normal_text ();
1862 pp_emit_prefix (&m_pp
);
1863 if (m_layout
.m_options
.show_line_numbers_p
)
1865 int width
= num_digits (row
);
1866 for (int i
= 0; i
< m_layout
.get_linenum_width () - width
; i
++)
1868 pp_printf (&m_pp
, "%i |", row
);
1871 print_leftmost_column ();
1873 /* We will stop printing the source line at any trailing whitespace. */
1874 line_bytes
= get_line_bytes_without_trailing_whitespace (line
,
1877 /* This object helps to keep track of which display column we are at, which is
1878 necessary for computing the line bounds in display units, for doing
1879 tab expansion, and for implementing m_x_offset_display. */
1880 cpp_display_width_computation
dw (line
, line_bytes
, m_layout
.m_char_policy
);
1882 /* Skip the first m_x_offset_display display columns. In case the leading
1883 portion that will be skipped ends with a character with wcwidth > 1, then
1884 it is possible we skipped too much, so account for that by padding with
1885 spaces. Note that this does the right thing too in case a tab was the last
1886 character to be skipped over; the tab is effectively replaced by the
1887 correct number of trailing spaces needed to offset by the desired number of
1889 for (int skipped_display_cols
1890 = dw
.advance_display_cols (m_layout
.m_x_offset_display
);
1891 skipped_display_cols
> m_layout
.m_x_offset_display
; --skipped_display_cols
)
1894 /* Print the line and compute the line_bounds. */
1895 line_bounds lbounds
;
1898 /* Assuming colorization is enabled for the caret and underline
1899 characters, we may also colorize the associated characters
1900 within the source line.
1902 For frontends that generate range information, we color the
1903 associated characters in the source line the same as the
1904 carets and underlines in the annotation line, to make it easier
1905 for the reader to see the pertinent code.
1907 For frontends that only generate carets, we don't colorize the
1908 characters above them, since this would look strange (e.g.
1909 colorizing just the first character in a token). */
1910 if (m_layout
.m_options
.colorize_source_p
)
1914 const int start_byte_col
= dw
.bytes_processed () + 1;
1915 in_range_p
= m_layout
.get_state_at_point (row
, start_byte_col
,
1920 m_colorizer
.set_range (state
.range_idx
);
1922 m_colorizer
.set_normal_text ();
1925 /* Get the display width of the next character to be output, expanding
1926 tabs and replacing some control bytes with spaces as necessary. */
1927 const char *c
= dw
.next_byte ();
1928 const int start_disp_col
= dw
.display_cols_processed () + 1;
1929 cpp_decoded_char cp
;
1930 const int this_display_width
= dw
.process_next_codepoint (&cp
);
1933 /* The returned display width is the number of spaces into which the
1934 tab should be expanded. */
1935 for (int i
= 0; i
!= this_display_width
; ++i
)
1940 /* We have a (possibly multibyte) character to output; update the line
1941 bounds if it is not whitespace. */
1944 lbounds
.m_last_non_ws_disp_col
= dw
.display_cols_processed ();
1945 if (lbounds
.m_first_non_ws_disp_col
== INT_MAX
)
1946 lbounds
.m_first_non_ws_disp_col
= start_disp_col
;
1949 /* Output the character. */
1950 m_layout
.m_char_policy
.m_print_cb (&m_pp
, cp
);
1951 c
= dw
.next_byte ();
1957 /* Determine if we should print an annotation line for ROW.
1958 i.e. if any of m_layout_ranges contains ROW. */
1961 layout::should_print_annotation_line_p (linenum_type row
) const
1963 layout_range
*range
;
1965 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1967 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
1969 if (range
->intersects_line_p (row
))
1975 /* Print the leftmost column after the margin, which is used for showing
1976 links between labels (e.g. for CFG edges in execution paths). */
1979 layout_printer::print_leftmost_column ()
1981 if (!get_options ().show_event_links_p
)
1982 gcc_assert (m_link_lhs_state
== link_lhs_state::none
);
1984 switch (m_link_lhs_state
)
1988 case link_lhs_state::none
:
1991 case link_lhs_state::rewinding_to_lhs
:
1993 m_colorizer
.set_cfg_edge ();
1994 const cppchar_t ch
= get_theme ().get_cppchar
1995 (text_art::theme::cell_kind::CFG_FROM_LEFT_TO_DOWN
);
1996 pp_unicode_character (&m_pp
, ch
);
1997 m_colorizer
.set_normal_text ();
2000 case link_lhs_state::at_lhs
:
2002 m_colorizer
.set_cfg_edge ();
2003 const cppchar_t ch
= get_theme ().get_cppchar
2004 (text_art::theme::cell_kind::CFG_DOWN
);
2005 pp_unicode_character (&m_pp
, ch
);
2006 m_colorizer
.set_normal_text ();
2009 case link_lhs_state::indenting_to_dest
:
2011 m_colorizer
.set_cfg_edge ();
2012 const cppchar_t ch
= get_theme ().get_cppchar
2013 (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_RIGHT
);
2014 pp_unicode_character (&m_pp
, ch
);
2015 m_colorizer
.set_normal_text ();
2021 /* Begin an annotation line. If m_show_line_numbers_p, print the left
2022 margin, which is empty for annotation lines.
2023 After any left margin, print a leftmost column, which is used for
2024 showing links between labels (e.g. for CFG edges in execution paths). */
2027 layout_printer::start_annotation_line (char margin_char
)
2029 pp_emit_prefix (&m_pp
);
2030 if (get_options ().show_line_numbers_p
)
2032 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
2033 of it, right-aligned, padded with spaces. */
2035 for (i
= 0; i
< m_layout
.m_linenum_width
- 3; i
++)
2037 for (; i
< m_layout
.m_linenum_width
; i
++)
2038 pp_character (&m_pp
, margin_char
);
2039 pp_string (&m_pp
, " |");
2041 if (margin_char
== ' ')
2042 print_leftmost_column ();
2044 pp_character (&m_pp
, margin_char
);
2047 /* Print a line consisting of the caret/underlines for the given
2051 layout_printer::print_annotation_line (linenum_type row
,
2052 const line_bounds lbounds
)
2054 int x_bound
= m_layout
.get_x_bound_for_row (row
,
2055 m_layout
.m_exploc
.m_display_col
,
2056 lbounds
.m_last_non_ws_disp_col
);
2058 start_annotation_line ();
2060 for (int column
= 1 + m_layout
.m_x_offset_display
; column
< x_bound
; column
++)
2064 in_range_p
= m_layout
.get_state_at_point (row
, column
,
2065 lbounds
.m_first_non_ws_disp_col
,
2066 lbounds
.m_last_non_ws_disp_col
,
2071 /* Within a range. Draw either the caret or an underline. */
2072 m_colorizer
.set_range (state
.range_idx
);
2073 if (state
.draw_caret_p
)
2075 /* Draw the caret. */
2077 if (state
.range_idx
< rich_location::STATICALLY_ALLOCATED_RANGES
)
2078 caret_char
= get_options ().caret_chars
[state
.range_idx
];
2081 pp_character (&m_pp
, caret_char
);
2084 pp_character (&m_pp
, '~');
2088 /* Not in a range. */
2089 m_colorizer
.set_normal_text ();
2090 pp_character (&m_pp
, ' ');
2096 /* A version of label_text that can live inside a vec.
2097 Requires manual cleanup via maybe_free. */
2099 struct pod_label_text
2102 : m_buffer (NULL
), m_caller_owned (false)
2105 pod_label_text (label_text
&&other
)
2106 : m_buffer (const_cast<char*> (other
.get ())),
2107 m_caller_owned (other
.is_owner ())
2119 bool m_caller_owned
;
2122 /* Implementation detail of layout::print_any_labels.
2124 A label within the given row of source. */
2129 line_label (int state_idx
, int column
,
2133 : m_state_idx (state_idx
), m_column (column
),
2134 m_text (std::move (text
)), m_label_line (0), m_has_vbar (true),
2135 m_has_in_edge (has_in_edge
),
2136 m_has_out_edge (has_out_edge
)
2138 /* Using styled_string rather than cpp_display_width here
2139 lets us skip SGR formatting characters for color and URLs.
2140 It doesn't handle tabs and unicode escaping, but we don't
2141 expect to see either of those in labels. */
2142 text_art::style_manager sm
;
2143 text_art::styled_string
str (sm
, m_text
.m_buffer
);
2144 m_display_width
= str
.calc_canvas_width ();
2147 /* Sorting is primarily by column, then by state index. */
2148 static int comparator (const void *p1
, const void *p2
)
2150 const line_label
*ll1
= (const line_label
*)p1
;
2151 const line_label
*ll2
= (const line_label
*)p2
;
2152 int column_cmp
= compare (ll1
->m_column
, ll2
->m_column
);
2155 /* Order by reverse state index, so that labels are printed
2156 in order of insertion into the rich_location when the
2157 sorted list is walked backwards. */
2158 return -compare (ll1
->m_state_idx
, ll2
->m_state_idx
);
2163 pod_label_text m_text
;
2164 size_t m_display_width
;
2168 bool m_has_out_edge
;
2171 /* Print any labels in this row. */
2173 layout_printer::print_any_labels (linenum_type row
)
2176 auto_vec
<line_label
> labels
;
2178 /* Gather the labels that are to be printed into "labels". */
2180 layout_range
*range
;
2181 FOR_EACH_VEC_ELT (m_layout
.m_layout_ranges
, i
, range
)
2183 /* Most ranges don't have labels, so reject this first. */
2184 if (range
->m_label
== NULL
)
2187 /* The range's caret must be on this line. */
2188 if (range
->m_caret
.m_line
!= row
)
2191 /* Reject labels that aren't fully visible due to clipping
2192 by m_x_offset_display. */
2193 const int disp_col
= range
->m_caret
.m_columns
[CU_DISPLAY_COLS
];
2194 if (disp_col
<= m_layout
.m_x_offset_display
)
2198 text
= range
->m_label
->get_text (range
->m_original_idx
);
2200 /* Allow for labels that return NULL from their get_text
2201 implementation (so e.g. such labels can control their own
2203 if (text
.get () == NULL
)
2206 labels
.safe_push (line_label (i
, disp_col
, std::move (text
),
2207 range
->has_in_edge (),
2208 range
->has_out_edge ()));
2212 /* Bail out if there are no labels on this row. */
2213 if (labels
.length () == 0)
2217 labels
.qsort(line_label::comparator
);
2219 /* Figure out how many "label lines" we need, and which
2220 one each label is printed in.
2222 For example, if the labels aren't too densely packed,
2223 we can fit them on the same line, giving two "label lines":
2228 l0 l1 : label line 1
2230 If they would touch each other or overlap, then we need
2231 additional "label lines":
2236 | label 1 : label line 1
2237 label 0 : label line 2
2239 Place the final label on label line 1, and work backwards, adding
2240 label lines as needed.
2242 If multiple labels are at the same place, put them on separate
2248 label 0 : label line 2
2249 label 1 : label line 3. */
2251 int max_label_line
= 1;
2252 int label_line_with_in_edge
= -1;
2254 int next_column
= INT_MAX
;
2256 FOR_EACH_VEC_ELT_REVERSE (labels
, i
, label
)
2258 /* Would this label "touch" or overlap the next label? */
2259 if (label
->m_column
+ label
->m_display_width
>= (size_t)next_column
)
2263 /* If we've already seen labels with the same column, suppress the
2264 vertical bar for subsequent ones in this backwards iteration;
2265 hence only the one with the highest label_line has m_has_vbar set. */
2266 if (label
->m_column
== next_column
)
2267 label
->m_has_vbar
= false;
2270 label
->m_label_line
= max_label_line
;
2271 if (get_options ().show_event_links_p
)
2272 if (label
->m_has_in_edge
)
2273 label_line_with_in_edge
= max_label_line
;
2274 next_column
= label
->m_column
;
2278 gcc_assert (labels
.length () > 0);
2280 /* Print the "label lines". For each label within the line, print
2281 either a vertical bar ('|') for the labels that are lower down, or the
2282 labels themselves once we've reached their line. */
2284 for (int label_line
= 0; label_line
<= max_label_line
; label_line
++)
2286 if (label_line
== label_line_with_in_edge
)
2288 gcc_assert (get_options ().show_event_links_p
);
2289 m_link_lhs_state
= link_lhs_state::indenting_to_dest
;
2291 start_annotation_line ();
2293 int column
= 1 + m_layout
.m_x_offset_display
;
2295 FOR_EACH_VEC_ELT (labels
, i
, label
)
2297 if (label_line
> label
->m_label_line
)
2298 /* We've printed all the labels for this label line. */
2301 if (label_line
== label
->m_label_line
)
2303 gcc_assert (column
<= label
->m_column
);
2305 if (label_line
== label_line_with_in_edge
)
2307 /* Print a prefix showing an incoming
2308 link from another label.
2309 .|+----------->(10) ...to here
2312 gcc_assert (get_options ().show_event_links_p
);
2313 m_colorizer
.set_cfg_edge ();
2314 const cppchar_t right
= get_theme ().get_cppchar
2315 (text_art::theme::cell_kind::CFG_RIGHT
);
2316 while (column
< label
->m_column
- 1)
2318 pp_unicode_character (&m_pp
, right
);
2321 if (column
== label
->m_column
- 1)
2323 pp_character (&m_pp
, '>');
2326 m_colorizer
.set_normal_text ();
2327 m_link_lhs_state
= link_lhs_state::none
;
2328 label_line_with_in_edge
= -1;
2331 move_to_column (&column
, label
->m_column
, true);
2332 gcc_assert (column
== label
->m_column
);
2333 /* Colorize the text, unless it's for events in a
2335 if (!m_is_diagnostic_path
)
2336 m_colorizer
.set_range (label
->m_state_idx
);
2337 pp_string (&m_pp
, label
->m_text
.m_buffer
);
2338 m_colorizer
.set_normal_text ();
2339 column
+= label
->m_display_width
;
2340 if (get_options ().show_event_links_p
&& label
->m_has_out_edge
)
2342 /* Print a suffix showing the start of a linkage
2343 to another label e.g. " ->-+" which will be the
2345 . (9) following ‘false’ branch... ->-+ <- HERE
2349 const cppchar_t right
= get_theme ().get_cppchar
2350 (text_art::theme::cell_kind::CFG_RIGHT
);
2351 const cppchar_t from_right_to_down
= get_theme ().get_cppchar
2352 (text_art::theme::cell_kind::CFG_FROM_RIGHT_TO_DOWN
);
2353 m_colorizer
.set_cfg_edge ();
2355 pp_unicode_character (&m_pp
, right
);
2356 pp_unicode_character (&m_pp
, '>');
2357 pp_unicode_character (&m_pp
, right
);
2358 pp_unicode_character (&m_pp
, from_right_to_down
);
2359 m_colorizer
.set_normal_text ();
2361 m_link_rhs_column
= column
- 1;
2364 else if (label
->m_has_vbar
)
2366 gcc_assert (column
<= label
->m_column
);
2367 move_to_column (&column
, label
->m_column
, true);
2368 m_colorizer
.set_range (label
->m_state_idx
);
2369 pp_character (&m_pp
, '|');
2370 m_colorizer
.set_normal_text ();
2375 /* If we have a vertical link line on the RHS, print the
2376 '|' on this annotation line after the labels. */
2377 if (m_link_rhs_column
!= -1 && column
< m_link_rhs_column
)
2379 move_to_column (&column
, m_link_rhs_column
, true);
2380 m_colorizer
.set_cfg_edge ();
2381 const cppchar_t down
= get_theme ().get_cppchar
2382 (text_art::theme::cell_kind::CFG_DOWN
);
2383 pp_unicode_character (&m_pp
, down
);
2384 m_colorizer
.set_normal_text ();
2391 /* If we have a vertical link line on the RHS, print a trailing
2392 annotation line showing the vertical line. */
2393 if (m_link_rhs_column
!= -1)
2395 int column
= 1 + m_layout
.m_x_offset_display
;
2396 start_annotation_line ();
2397 move_to_column (&column
, m_link_rhs_column
, true);
2398 m_colorizer
.set_cfg_edge ();
2399 const cppchar_t down
= get_theme ().get_cppchar
2400 (text_art::theme::cell_kind::CFG_DOWN
);
2401 pp_unicode_character (&m_pp
, down
);
2402 m_colorizer
.set_normal_text ();
2409 FOR_EACH_VEC_ELT (labels
, i
, label
)
2410 label
->m_text
.maybe_free ();
2414 /* If there are any fixit hints inserting new lines before source line ROW,
2417 They are printed on lines of their own, before the source line
2418 itself, with a leading '+'. */
2421 layout_printer::print_leading_fixits (linenum_type row
)
2423 for (unsigned int i
= 0; i
< m_layout
.m_fixit_hints
.length (); i
++)
2425 const fixit_hint
*hint
= m_layout
.m_fixit_hints
[i
];
2427 if (!hint
->ends_with_newline_p ())
2428 /* Not a newline fixit; print it in print_trailing_fixits. */
2431 gcc_assert (hint
->insertion_p ());
2433 if (hint
->affects_line_p (m_layout
.m_line_table
,
2434 m_layout
.m_exploc
.file
,
2437 /* Printing the '+' with normal colorization
2438 and the inserted line with "insert" colorization
2439 helps them stand out from each other, and from
2440 the surrounding text. */
2441 m_colorizer
.set_normal_text ();
2442 start_annotation_line ('+');
2443 m_colorizer
.set_fixit_insert ();
2444 /* Print all but the trailing newline of the fix-it hint.
2445 We have to print the newline separately to avoid
2446 getting additional pp prefixes printed. */
2447 for (size_t i
= 0; i
< hint
->get_length () - 1; i
++)
2448 pp_character (&m_pp
, hint
->get_string ()[i
]);
2449 m_colorizer
.set_normal_text ();
2455 /* Subroutine of layout::print_trailing_fixits.
2457 Determine if the annotation line printed for LINE contained
2458 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2461 layout::annotation_line_showed_range_p (linenum_type line
, int start_column
,
2462 int finish_column
) const
2464 layout_range
*range
;
2466 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2467 if (range
->m_start
.m_line
== line
2468 && range
->m_start
.m_columns
[CU_DISPLAY_COLS
] == start_column
2469 && range
->m_finish
.m_line
== line
2470 && range
->m_finish
.m_columns
[CU_DISPLAY_COLS
] == finish_column
)
2475 /* Classes for printing trailing fix-it hints i.e. those that
2476 don't add new lines.
2478 For insertion, these can look like:
2482 For replacement, these can look like:
2484 ------------- : underline showing affected range
2487 For deletion, these can look like:
2489 ------------- : underline showing affected range
2491 This can become confusing if they overlap, and so we need
2492 to do some preprocessing to decide what to print.
2493 We use the list of fixit_hint instances affecting the line
2494 to build a list of "correction" instances, and print the
2497 For example, consider a set of fix-its for converting
2498 a C-style cast to a C++ const_cast.
2502 ..000000000111111111122222222223333333333.
2503 ..123456789012345678901234567890123456789.
2504 foo *f = (foo *)ptr->field;
2507 and the fix-it hints:
2508 - replace col 10 (the open paren) with "const_cast<"
2509 - replace col 16 (the close paren) with "> ("
2510 - insert ")" before col 27
2512 then we would get odd-looking output:
2514 foo *f = (foo *)ptr->field;
2521 It would be better to detect when fixit hints are going to
2522 overlap (those that require new lines), and to consolidate
2523 the printing of such fixits, giving something like:
2525 foo *f = (foo *)ptr->field;
2528 const_cast<foo *> (ptr->field)
2530 This works by detecting when the printing would overlap, and
2531 effectively injecting no-op replace hints into the gaps between
2532 such fix-its, so that the printing joins up.
2534 In the above example, the overlap of:
2535 - replace col 10 (the open paren) with "const_cast<"
2537 - replace col 16 (the close paren) with "> ("
2538 is fixed by injecting a no-op:
2539 - replace cols 11-15 with themselves ("foo *")
2540 and consolidating these, making:
2541 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2543 - replace cols 10-16 with "const_cast<foo *> ("
2545 This overlaps with the final fix-it hint:
2546 - insert ")" before col 27
2547 and so we repeat the consolidation process, by injecting
2549 - replace cols 17-26 with themselves ("ptr->field")
2551 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2553 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2555 and is thus printed as desired. */
2557 /* A range of (byte or display) columns within a line. */
2562 column_range (int start_
, int finish_
) : start (start_
), finish (finish_
)
2564 gcc_assert (valid_p (start
, finish
));
2567 bool operator== (const column_range
&other
) const
2569 return start
== other
.start
&& finish
== other
.finish
;
2572 static bool valid_p (int start
, int finish
)
2574 /* We must have either a range, or an insertion. */
2575 return (start
<= finish
|| finish
== start
- 1);
2582 /* Get the range of bytes or display columns that HINT would affect. */
2584 get_affected_range (file_cache
&fc
,
2585 const cpp_char_column_policy
&policy
,
2586 const fixit_hint
*hint
, enum column_unit col_unit
)
2588 expanded_location exploc_start
= expand_location (hint
->get_start_loc ());
2589 expanded_location exploc_finish
= expand_location (hint
->get_next_loc ());
2590 --exploc_finish
.column
;
2594 if (col_unit
== CU_DISPLAY_COLS
)
2596 start_column
= location_compute_display_column (fc
, exploc_start
, policy
);
2597 if (hint
->insertion_p ())
2598 finish_column
= start_column
- 1;
2601 = location_compute_display_column (fc
, exploc_finish
, policy
);
2605 start_column
= exploc_start
.column
;
2606 finish_column
= exploc_finish
.column
;
2608 return column_range (start_column
, finish_column
);
2611 /* Get the range of display columns that would be printed for HINT. */
2614 get_printed_columns (file_cache
&fc
,
2615 const cpp_char_column_policy
&policy
,
2616 const fixit_hint
*hint
)
2618 expanded_location exploc
= expand_location (hint
->get_start_loc ());
2619 int start_column
= location_compute_display_column (fc
, exploc
, policy
);
2620 int hint_width
= cpp_display_width (hint
->get_string (), hint
->get_length (),
2622 int final_hint_column
= start_column
+ hint_width
- 1;
2623 if (hint
->insertion_p ())
2625 return column_range (start_column
, final_hint_column
);
2629 exploc
= expand_location (hint
->get_next_loc ());
2631 int finish_column
= location_compute_display_column (fc
, exploc
, policy
);
2632 return column_range (start_column
,
2633 MAX (finish_column
, final_hint_column
));
2637 /* A correction on a particular line.
2638 This describes a plan for how to print one or more fixit_hint
2639 instances that affected the line, potentially consolidating hints
2640 into corrections to make the result easier for the user to read. */
2645 correction (column_range affected_bytes
,
2646 column_range affected_columns
,
2647 column_range printed_columns
,
2648 const char *new_text
, size_t new_text_len
,
2649 const cpp_char_column_policy
&policy
)
2650 : m_affected_bytes (affected_bytes
),
2651 m_affected_columns (affected_columns
),
2652 m_printed_columns (printed_columns
),
2653 m_text (xstrdup (new_text
)),
2654 m_byte_length (new_text_len
),
2656 m_alloc_sz (new_text_len
+ 1)
2658 compute_display_cols ();
2661 ~correction () { free (m_text
); }
2663 bool insertion_p () const
2665 return m_affected_bytes
.start
== m_affected_bytes
.finish
+ 1;
2668 void ensure_capacity (size_t len
);
2669 void ensure_terminated ();
2671 void compute_display_cols ()
2673 m_display_cols
= cpp_display_width (m_text
, m_byte_length
, m_policy
);
2676 void overwrite (int dst_offset
, const char_span
&src_span
)
2678 gcc_assert (dst_offset
>= 0);
2679 gcc_assert (dst_offset
+ src_span
.length () < m_alloc_sz
);
2680 memcpy (m_text
+ dst_offset
, src_span
.get_buffer (),
2681 src_span
.length ());
2684 /* If insert, then start: the column before which the text
2685 is to be inserted, and finish is offset by the length of
2687 If replace, then the range of columns affected. */
2688 column_range m_affected_bytes
;
2689 column_range m_affected_columns
;
2691 /* If insert, then start: the column before which the text
2692 is to be inserted, and finish is offset by the length of
2694 If replace, then the range of columns affected. */
2695 column_range m_printed_columns
;
2697 /* The text to be inserted/used as replacement. */
2699 size_t m_byte_length
; /* Not including null-terminator. */
2701 const cpp_char_column_policy
&m_policy
;
2705 /* Ensure that m_text can hold a string of length LEN
2706 (plus 1 for 0-termination). */
2709 correction::ensure_capacity (size_t len
)
2711 /* Allow 1 extra byte for 0-termination. */
2712 if (m_alloc_sz
< (len
+ 1))
2714 size_t new_alloc_sz
= (len
+ 1) * 2;
2715 m_text
= (char *)xrealloc (m_text
, new_alloc_sz
);
2716 m_alloc_sz
= new_alloc_sz
;
2720 /* Ensure that m_text is 0-terminated. */
2723 correction::ensure_terminated ()
2725 /* 0-terminate the buffer. */
2726 gcc_assert (m_byte_length
< m_alloc_sz
);
2727 m_text
[m_byte_length
] = '\0';
2730 /* A list of corrections affecting a particular line.
2731 This is used by layout::print_trailing_fixits for planning
2732 how to print the fix-it hints affecting the line. */
2734 class line_corrections
2737 line_corrections (file_cache
&fc
,
2738 const char_display_policy
&policy
,
2739 const char *filename
,
2741 : m_file_cache (fc
),
2742 m_policy (policy
), m_filename (filename
), m_row (row
)
2744 ~line_corrections ();
2746 void add_hint (const fixit_hint
*hint
);
2748 file_cache
&m_file_cache
;
2749 const char_display_policy
&m_policy
;
2750 const char *m_filename
;
2752 auto_vec
<correction
*> m_corrections
;
2755 /* struct line_corrections. */
2757 line_corrections::~line_corrections ()
2761 FOR_EACH_VEC_ELT (m_corrections
, i
, c
)
2765 /* A struct wrapping a particular source line, allowing
2766 run-time bounds-checking of accesses in a checked build. */
2771 source_line (file_cache
&fc
, const char *filename
, int line
);
2773 char_span
as_span () { return char_span (chars
, width
); }
2779 /* source_line's ctor. */
2781 source_line::source_line (file_cache
&fc
, const char *filename
, int line
)
2783 char_span span
= fc
.get_source_line (filename
, line
);
2784 chars
= span
.get_buffer ();
2785 width
= span
.length ();
2788 /* Add HINT to the corrections for this line.
2789 Attempt to consolidate nearby hints so that they will not
2790 overlap with printed. */
2793 line_corrections::add_hint (const fixit_hint
*hint
)
2795 column_range affected_bytes
2796 = get_affected_range (m_file_cache
, m_policy
, hint
, CU_BYTES
);
2797 column_range affected_columns
2798 = get_affected_range (m_file_cache
, m_policy
, hint
, CU_DISPLAY_COLS
);
2799 column_range printed_columns
2800 = get_printed_columns (m_file_cache
, m_policy
, hint
);
2802 /* Potentially consolidate. */
2803 if (!m_corrections
.is_empty ())
2805 correction
*last_correction
2806 = m_corrections
[m_corrections
.length () - 1];
2808 /* The following consolidation code assumes that the fix-it hints
2809 have been sorted by start (done within layout's ctor). */
2810 gcc_assert (affected_bytes
.start
2811 >= last_correction
->m_affected_bytes
.start
);
2812 gcc_assert (printed_columns
.start
2813 >= last_correction
->m_printed_columns
.start
);
2815 if (printed_columns
.start
<= last_correction
->m_printed_columns
.finish
2816 && column_range::valid_p (last_correction
->m_affected_bytes
.finish
+ 1,
2817 affected_bytes
.start
- 1))
2819 /* We have two hints for which the printed forms of the hints
2820 would touch or overlap, so we need to consolidate them to avoid
2822 Attempt to inject a "replace" correction from immediately
2823 after the end of the last hint to immediately before the start
2824 of the next hint. */
2825 column_range
between (last_correction
->m_affected_bytes
.finish
+ 1,
2826 affected_bytes
.start
- 1);
2828 /* Try to read the source. */
2829 source_line
line (m_file_cache
, m_filename
, m_row
);
2830 if (line
.chars
&& between
.finish
< line
.width
)
2832 /* Consolidate into the last correction:
2833 add a no-op "replace" of the "between" text, and
2834 add the text from the new hint. */
2835 int old_byte_len
= last_correction
->m_byte_length
;
2836 gcc_assert (old_byte_len
>= 0);
2837 int between_byte_len
= between
.finish
+ 1 - between
.start
;
2838 gcc_assert (between_byte_len
>= 0);
2840 = old_byte_len
+ between_byte_len
+ hint
->get_length ();
2841 gcc_assert (new_byte_len
>= 0);
2842 last_correction
->ensure_capacity (new_byte_len
);
2843 last_correction
->overwrite
2845 line
.as_span ().subspan (between
.start
- 1,
2846 between
.finish
+ 1 - between
.start
));
2847 last_correction
->overwrite (old_byte_len
+ between_byte_len
,
2848 char_span (hint
->get_string (),
2849 hint
->get_length ()));
2850 last_correction
->m_byte_length
= new_byte_len
;
2851 last_correction
->ensure_terminated ();
2852 last_correction
->m_affected_bytes
.finish
2853 = affected_bytes
.finish
;
2854 last_correction
->m_affected_columns
.finish
2855 = affected_columns
.finish
;
2856 int prev_display_cols
= last_correction
->m_display_cols
;
2857 last_correction
->compute_display_cols ();
2858 last_correction
->m_printed_columns
.finish
2859 += last_correction
->m_display_cols
- prev_display_cols
;
2865 /* If no consolidation happened, add a new correction instance. */
2866 m_corrections
.safe_push (new correction (affected_bytes
,
2869 hint
->get_string (),
2870 hint
->get_length (),
2874 /* If there are any fixit hints on source line ROW, print them.
2875 They are printed in order, attempting to combine them onto lines, but
2876 starting new lines if necessary.
2877 Fix-it hints that insert new lines are handled separately,
2878 in layout::print_leading_fixits. */
2881 layout_printer::print_trailing_fixits (linenum_type row
)
2883 /* Build a list of correction instances for the line,
2884 potentially consolidating hints (for the sake of readability). */
2885 line_corrections
corrections (m_layout
.m_file_cache
, m_layout
.m_char_policy
,
2886 m_layout
.m_exploc
.file
, row
);
2887 for (unsigned int i
= 0; i
< m_layout
.m_fixit_hints
.length (); i
++)
2889 const fixit_hint
*hint
= m_layout
.m_fixit_hints
[i
];
2891 /* Newline fixits are handled by layout::print_leading_fixits. */
2892 if (hint
->ends_with_newline_p ())
2895 if (hint
->affects_line_p (m_layout
.m_line_table
,
2896 m_layout
.m_exploc
.file
,
2898 corrections
.add_hint (hint
);
2901 /* Now print the corrections. */
2904 int column
= 1 + m_layout
.m_x_offset_display
;
2906 if (!corrections
.m_corrections
.is_empty ())
2907 start_annotation_line ();
2909 FOR_EACH_VEC_ELT (corrections
.m_corrections
, i
, c
)
2911 /* For now we assume each fixit hint can only touch one line. */
2912 if (c
->insertion_p ())
2914 /* This assumes the insertion just affects one line. */
2915 int start_column
= c
->m_printed_columns
.start
;
2916 move_to_column (&column
, start_column
, true);
2917 m_colorizer
.set_fixit_insert ();
2918 pp_string (&m_pp
, c
->m_text
);
2919 m_colorizer
.set_normal_text ();
2920 column
+= c
->m_display_cols
;
2924 /* If the range of the replacement wasn't printed in the
2925 annotation line, then print an extra underline to
2926 indicate exactly what is being replaced.
2927 Always show it for removals. */
2928 int start_column
= c
->m_affected_columns
.start
;
2929 int finish_column
= c
->m_affected_columns
.finish
;
2930 if (!m_layout
.annotation_line_showed_range_p (row
, start_column
,
2932 || c
->m_byte_length
== 0)
2934 move_to_column (&column
, start_column
, true);
2935 m_colorizer
.set_fixit_delete ();
2936 for (; column
<= finish_column
; column
++)
2937 pp_character (&m_pp
, '-');
2938 m_colorizer
.set_normal_text ();
2940 /* Print the replacement text. REPLACE also covers
2941 removals, so only do this extra work (potentially starting
2942 a new line) if we have actual replacement text. */
2943 if (c
->m_byte_length
> 0)
2945 move_to_column (&column
, start_column
, true);
2946 m_colorizer
.set_fixit_insert ();
2947 pp_string (&m_pp
, c
->m_text
);
2948 m_colorizer
.set_normal_text ();
2949 column
+= c
->m_display_cols
;
2954 /* Add a trailing newline, if necessary. */
2955 move_to_column (&column
, 1 + m_layout
.m_x_offset_display
, false);
2958 /* Disable any colorization and emit a newline. */
2961 layout_printer::print_newline ()
2963 m_colorizer
.set_normal_text ();
2967 /* Return true if (ROW/COLUMN) is within a range of the layout.
2968 If it returns true, OUT_STATE is written to, with the
2969 range index, and whether we should draw the caret at
2970 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2971 whether all inputs and outputs are in bytes or display column units. */
2974 layout::get_state_at_point (/* Inputs. */
2975 linenum_type row
, int column
,
2976 int first_non_ws
, int last_non_ws
,
2977 enum column_unit col_unit
,
2979 point_state
*out_state
) const
2981 layout_range
*range
;
2983 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2985 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
2986 /* Bail out early, so that such ranges don't affect underlining or
2987 source colorization. */
2990 if (range
->contains_point (row
, column
, col_unit
))
2992 out_state
->range_idx
= i
;
2994 /* Are we at the range's caret? is it visible? */
2995 out_state
->draw_caret_p
= false;
2996 if (range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
2997 && row
== range
->m_caret
.m_line
2998 && column
== range
->m_caret
.m_columns
[col_unit
])
2999 out_state
->draw_caret_p
= true;
3001 /* Within a multiline range, don't display any underline
3002 in any leading or trailing whitespace on a line.
3003 We do display carets, however. */
3004 if (!out_state
->draw_caret_p
)
3005 if (column
< first_non_ws
|| column
> last_non_ws
)
3008 /* We are within a range. */
3016 /* Helper function for use by layout::print_line when printing the
3017 annotation line under the source line.
3018 Get the display column beyond the rightmost one that could contain a caret
3019 or range marker, given that we stop rendering at trailing whitespace.
3020 ROW is the source line within the given file.
3021 CARET_COLUMN is the display column of range 0's caret.
3022 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
3023 character of source (as determined when printing the source line). */
3026 layout::get_x_bound_for_row (linenum_type row
, int caret_column
,
3027 int last_non_ws_column
) const
3029 int result
= caret_column
+ 1;
3031 layout_range
*range
;
3033 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
3035 if (row
>= range
->m_start
.m_line
)
3037 if (range
->m_finish
.m_line
== row
)
3039 /* On the final line within a range; ensure that
3040 we render up to the end of the range. */
3041 const int disp_col
= range
->m_finish
.m_columns
[CU_DISPLAY_COLS
];
3042 if (result
<= disp_col
)
3043 result
= disp_col
+ 1;
3045 else if (row
< range
->m_finish
.m_line
)
3047 /* Within a multiline range; ensure that we render up to the
3048 last non-whitespace column. */
3049 if (result
<= last_non_ws_column
)
3050 result
= last_non_ws_column
+ 1;
3058 /* Given *COLUMN as an x-coordinate, print spaces to position
3059 successive output at DEST_COLUMN, printing a newline if necessary,
3060 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
3061 left margin after any newline. */
3064 layout_printer::move_to_column (int *column
,
3066 bool add_left_margin
)
3068 /* Start a new line if we need to. */
3069 if (*column
> dest_column
)
3072 if (add_left_margin
)
3073 start_annotation_line ();
3074 *column
= 1 + m_layout
.m_x_offset_display
;
3077 while (*column
< dest_column
)
3079 /* For debugging column issues, it can be helpful to replace this
3081 pp_character (&m_pp, '0' + (*column % 10));
3082 to visualize the changing value of "*column". */
3088 /* For debugging layout issues, render a ruler giving column numbers
3089 (after the 1-column indent). */
3092 layout_printer::show_ruler (int max_column
)
3095 if (max_column
> 99)
3097 start_annotation_line ();
3098 for (int column
= 1 + m_layout
.m_x_offset_display
;
3099 column
<= max_column
;
3101 if (column
% 10 == 0)
3102 pp_character (&m_pp
, '0' + (column
/ 100) % 10);
3109 start_annotation_line ();
3110 for (int column
= 1 + m_layout
.m_x_offset_display
;
3111 column
<= max_column
;
3113 if (column
% 10 == 0)
3114 pp_character (&m_pp
, '0' + (column
/ 10) % 10);
3120 start_annotation_line ();
3121 for (int column
= 1 + m_layout
.m_x_offset_display
;
3122 column
<= max_column
;
3124 pp_character (&m_pp
, '0' + (column
% 10));
3128 /* Print leading fix-its (for new lines inserted before the source line)
3129 then the source line, followed by an annotation line
3130 consisting of any caret/underlines, then any fixits.
3131 If the source line can't be read, print nothing. */
3133 layout_printer::print_line (linenum_type row
)
3136 = m_layout
.m_file_cache
.get_source_line (m_layout
.m_exploc
.file
, row
);
3140 print_any_right_to_left_edge_lines ();
3141 print_leading_fixits (row
);
3142 const line_bounds lbounds
3143 = print_source_line (row
, line
.get_buffer (), line
.length ());
3144 if (m_layout
.should_print_annotation_line_p (row
))
3145 print_annotation_line (row
, lbounds
);
3146 if (get_options ().show_labels_p
)
3147 print_any_labels (row
);
3148 print_trailing_fixits (row
);
3151 /* If there's a link column in the RHS, print something like this:
3153 "┌──────────────────────────────────────────┘\n"
3154 showing the link entering at the top right and emerging
3155 at the bottom left. */
3158 layout_printer::print_any_right_to_left_edge_lines ()
3160 if (m_link_rhs_column
== -1)
3161 /* Can also happen if the out-edge had UNKNOWN_LOCATION. */
3164 gcc_assert (get_options ().show_event_links_p
);
3166 /* Print the line with "|". */
3167 start_annotation_line ();
3168 int column
= 1 + m_layout
.m_x_offset_display
;
3169 move_to_column (&column
, m_link_rhs_column
, true);
3170 m_colorizer
.set_cfg_edge ();
3171 const cppchar_t down
= get_theme ().get_cppchar
3172 (text_art::theme::cell_kind::CFG_DOWN
);
3173 pp_unicode_character (&m_pp
, down
);
3174 m_colorizer
.set_normal_text ();
3177 /* Print the line with "┌──────────────────────────────────────────┘". */
3178 m_link_lhs_state
= link_lhs_state::rewinding_to_lhs
;
3179 start_annotation_line ();
3180 m_colorizer
.set_cfg_edge ();
3181 const cppchar_t left
= get_theme ().get_cppchar
3182 (text_art::theme::cell_kind::CFG_LEFT
);
3183 for (int column
= 1 + m_layout
.m_x_offset_display
;
3184 column
< m_link_rhs_column
;
3186 pp_unicode_character (&m_pp
, left
);
3187 const cppchar_t from_down_to_left
= get_theme ().get_cppchar
3188 (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_LEFT
);
3189 pp_unicode_character (&m_pp
, from_down_to_left
);
3190 m_colorizer
.set_normal_text ();
3193 /* We now have a link line on the LHS,
3194 and no longer have one on the RHS. */
3195 m_link_lhs_state
= link_lhs_state::at_lhs
;
3196 m_link_rhs_column
= -1;
3199 layout_printer::layout_printer (pretty_printer
&pp
,
3200 const layout
&layout
,
3201 const rich_location
&richloc
,
3202 diagnostic_t diagnostic_kind
)
3205 m_colorizer (m_pp
, richloc
, diagnostic_kind
),
3206 m_is_diagnostic_path (diagnostic_kind
== DK_DIAGNOSTIC_PATH
),
3207 m_link_lhs_state (link_lhs_state::none
),
3208 m_link_rhs_column (-1)
3210 if (get_options ().show_event_links_p
)
3211 if (auto effect_info
= m_layout
.m_effect_info
)
3212 if (effect_info
->m_leading_in_edge_column
)
3213 m_link_rhs_column
= effect_info
->m_leading_in_edge_column
;
3216 } /* End of anonymous namespace. */
3218 /* If LOC is within the spans of lines that will already be printed for
3219 this gcc_rich_location, then add it as a secondary location and return true.
3221 Otherwise return false.
3223 Use POLICY for determining how spans of lines would be printed. */
3227 add_location_if_nearby (const diagnostic_source_print_policy
&policy
,
3229 bool restrict_to_current_line_spans
,
3230 const range_label
*label
)
3232 /* Use the layout location-handling logic to sanitize LOC,
3233 filtering it to the current line spans within a temporary
3236 layout
layout (policy
, *this);
3237 location_range loc_range
;
3238 loc_range
.m_loc
= loc
;
3239 loc_range
.m_range_display_kind
= SHOW_RANGE_WITHOUT_CARET
;
3240 loc_range
.m_label
= nullptr;
3241 if (!layout
.maybe_add_location_range (&loc_range
, 0,
3242 restrict_to_current_line_spans
))
3245 add_range (loc
, SHOW_RANGE_WITHOUT_CARET
, label
);
3251 add_location_if_nearby (const diagnostic_context
&dc
,
3253 bool restrict_to_current_line_spans
,
3254 const range_label
*label
)
3256 diagnostic_source_print_policy
source_policy (dc
,
3257 dc
.m_source_printing
);
3258 return add_location_if_nearby (source_policy
, loc
,
3259 restrict_to_current_line_spans
, label
);
3263 /* As per diagnostic_source_print_policy::print, but don't print anything
3264 if source printing is disabled, or if the location hasn't changed. */
3267 diagnostic_context::maybe_show_locus (const rich_location
&richloc
,
3268 const diagnostic_source_printing_options
&opts
,
3269 diagnostic_t diagnostic_kind
,
3271 diagnostic_source_effect_info
*effects
)
3273 const location_t loc
= richloc
.get_loc ();
3274 /* Do nothing if source-printing has been disabled. */
3278 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
3279 if (loc
<= BUILTINS_LOCATION
)
3282 /* Don't print the same source location twice in a row, unless we have
3283 fix-it hints, or multiple locations, or a label. */
3284 if (loc
== m_last_location
3285 && richloc
.get_num_fixit_hints () == 0
3286 && richloc
.get_num_locations () == 1
3287 && richloc
.get_range (0)->m_label
== NULL
)
3290 m_last_location
= loc
;
3292 diagnostic_source_print_policy
source_policy (*this, opts
);
3293 source_policy
.print (pp
, richloc
, diagnostic_kind
, effects
);
3296 diagnostic_source_print_policy::
3297 diagnostic_source_print_policy (const diagnostic_context
&dc
)
3298 : m_options (dc
.m_source_printing
),
3299 m_location_policy (dc
),
3300 m_start_span_cb (dc
.m_text_callbacks
.m_start_span
),
3301 m_file_cache (dc
.get_file_cache ()),
3302 m_diagram_theme (dc
.get_diagram_theme ()),
3303 m_escape_format (dc
.get_escape_format ())
3307 diagnostic_source_print_policy::
3308 diagnostic_source_print_policy (const diagnostic_context
&dc
,
3309 const diagnostic_source_printing_options
&opts
)
3311 m_location_policy (dc
),
3312 m_start_span_cb (dc
.m_text_callbacks
.m_start_span
),
3313 m_file_cache (dc
.get_file_cache ()),
3314 m_diagram_theme (dc
.get_diagram_theme ()),
3315 m_escape_format (dc
.get_escape_format ())
3319 /* Print to PP the physical source code corresponding to the location(s)
3320 in RICHLOC, with additional annotations, as if for a diagnostic of the
3321 given DIAGNOSTIC_KIND.
3322 If EFFECTS is non-null, then use and update it. */
3325 diagnostic_source_print_policy::print (pretty_printer
&pp
,
3326 const rich_location
&richloc
,
3327 diagnostic_t diagnostic_kind
,
3328 diagnostic_source_effect_info
*effects
)
3331 layout
layout (*this, richloc
, effects
);
3332 layout_printer
lp (pp
, layout
, richloc
, diagnostic_kind
);
3337 layout_printer::print (const diagnostic_source_print_policy
&source_policy
)
3339 diagnostic_prefixing_rule_t saved_rule
= pp_prefixing_rule (&m_pp
);
3340 pp_prefixing_rule (&m_pp
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3342 if (get_options ().show_ruler_p
)
3343 show_ruler (m_layout
.m_x_offset_display
+ get_options ().max_width
);
3345 for (int line_span_idx
= 0; line_span_idx
< m_layout
.get_num_line_spans ();
3348 const line_span
*line_span
= m_layout
.get_line_span (line_span_idx
);
3349 if (get_options ().show_line_numbers_p
)
3351 /* With line numbers, we should show whenever the line-numbering
3353 if (line_span_idx
> 0)
3354 print_gap_in_line_numbering ();
3358 /* Without line numbers, we print headings for some line spans. */
3359 if (m_layout
.print_heading_for_line_span_index_p (line_span_idx
))
3361 expanded_location exploc
3362 = m_layout
.get_expanded_location (line_span
);
3363 const diagnostic_location_print_policy
&
3364 loc_policy
= source_policy
.get_location_policy ();
3365 source_policy
.get_start_span_fn () (loc_policy
, &m_pp
, exploc
);
3368 /* Iterate over the lines within this span (using linenum_arith_t to
3369 avoid overflow with 0xffffffff causing an infinite loop). */
3370 linenum_arith_t last_line
= line_span
->get_last_line ();
3371 for (linenum_arith_t row
= line_span
->get_first_line ();
3372 row
<= last_line
; row
++)
3376 if (auto effect_info
= m_layout
.m_effect_info
)
3377 effect_info
->m_trailing_out_edge_column
= m_link_rhs_column
;
3379 pp_prefixing_rule (&m_pp
) = saved_rule
;
3384 namespace selftest
{
3386 /* Selftests for diagnostic_show_locus. */
3388 diagnostic_show_locus_fixture::
3389 diagnostic_show_locus_fixture (const line_table_case
&case_
,
3390 const char *content
)
3391 : m_content (content
),
3392 m_tmp_source_file (SELFTEST_LOCATION
, ".c", content
),
3396 linemap_add (line_table
, LC_ENTER
, false,
3397 m_tmp_source_file
.get_filename (), 1);
3400 /* Populate a char_display_policy based on DC and RICHLOC. */
3402 static char_display_policy
3403 make_char_policy (const diagnostic_context
&dc
,
3404 const rich_location
&richloc
)
3406 diagnostic_source_print_policy
source_policy (dc
);
3407 return ::make_char_policy (source_policy
, richloc
);
3410 /* Verify that cpp_display_width correctly handles escaping. */
3413 test_display_widths ()
3415 gcc_rich_location
richloc (UNKNOWN_LOCATION
);
3417 /* U+03C0 "GREEK SMALL LETTER PI". */
3418 const char *pi
= "\xCF\x80";
3419 /* U+1F642 "SLIGHTLY SMILING FACE". */
3420 const char *emoji
= "\xF0\x9F\x99\x82";
3421 /* Stray trailing byte of a UTF-8 character. */
3422 const char *stray
= "\xBF";
3424 const char *max_codepoint
= "\xF4\x8F\xBF\xBF";
3428 test_diagnostic_context dc
;
3429 char_display_policy
policy (make_char_policy (dc
, richloc
));
3430 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 1);
3431 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 2);
3432 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 1);
3433 /* Don't check width of U+10FFFF; it's in a private use plane. */
3436 richloc
.set_escape_on_output (true);
3439 test_diagnostic_context dc
;
3440 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
);
3441 char_display_policy
policy (make_char_policy (dc
, richloc
));
3442 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 8);
3443 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 9);
3444 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 4);
3445 ASSERT_EQ (cpp_display_width (max_codepoint
, strlen (max_codepoint
),
3447 strlen ("<U+10FFFF>"));
3451 test_diagnostic_context dc
;
3452 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES
);
3453 char_display_policy
policy (make_char_policy (dc
, richloc
));
3454 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 8);
3455 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 16);
3456 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 4);
3457 ASSERT_EQ (cpp_display_width (max_codepoint
, strlen (max_codepoint
),
3463 /* For precise tests of the layout, make clear where the source line will
3464 start. test_left_margin sets the total byte count from the left side of the
3465 screen to the start of source lines, after the line number and the separator,
3466 which consists of the three characters " | ". */
3467 static const int test_linenum_sep
= 3;
3468 static const int test_left_margin
= 7;
3470 /* Helper function for test_layout_x_offset_display_utf8(). */
3472 test_offset_impl (int caret_byte_col
, int max_width
,
3473 int expected_x_offset_display
,
3474 int left_margin
= test_left_margin
)
3476 test_diagnostic_context dc
;
3477 dc
.m_source_printing
.max_width
= max_width
;
3478 /* diagnostic_context::min_margin_width sets the minimum space reserved for
3479 the line number plus one space after. */
3480 dc
.m_source_printing
.min_margin_width
= left_margin
- test_linenum_sep
+ 1;
3481 dc
.m_source_printing
.show_line_numbers_p
= true;
3482 diagnostic_source_print_policy
source_policy (dc
);
3483 rich_location
richloc (line_table
,
3484 linemap_position_for_column (line_table
,
3486 layout
test_layout (source_policy
, richloc
, nullptr);
3487 ASSERT_EQ (left_margin
- test_linenum_sep
,
3488 test_layout
.get_linenum_width ());
3489 ASSERT_EQ (expected_x_offset_display
,
3490 test_layout
.get_x_offset_display ());
3493 /* Test that layout::calculate_x_offset_display() works. */
3495 test_layout_x_offset_display_utf8 (const line_table_case
&case_
)
3499 = "This line is very long, so that we can use it to test the logic for "
3500 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
3501 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
3504 /* Number of bytes in the line, subtracting one to remove the newline. */
3505 const int line_bytes
= strlen (content
) - 1;
3507 /* Number of display columns occupied by the line; each of the 2 emojis
3508 takes up 2 fewer display columns than it does bytes. */
3509 const int line_display_cols
= line_bytes
- 2*2;
3511 /* The column of the first emoji. Byte or display is the same as there are
3512 no multibyte characters earlier on the line. */
3513 const int emoji_col
= 102;
3515 diagnostic_show_locus_fixture
f (case_
, content
);
3517 linemap_add (line_table
, LC_ENTER
, false, f
.get_filename (), 1);
3519 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
3521 /* Don't attempt to run the tests if column data might be unavailable. */
3522 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3525 ASSERT_STREQ (f
.get_filename (), LOCATION_FILE (line_end
));
3526 ASSERT_EQ (1, LOCATION_LINE (line_end
));
3527 ASSERT_EQ (line_bytes
, LOCATION_COLUMN (line_end
));
3529 char_span lspan
= f
.m_fc
.get_source_line (f
.get_filename (), 1);
3530 ASSERT_EQ (line_display_cols
,
3531 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
3533 ASSERT_EQ (line_display_cols
,
3534 location_compute_display_column (f
.m_fc
,
3535 expand_location (line_end
),
3537 ASSERT_EQ (0, memcmp (lspan
.get_buffer () + (emoji_col
- 1),
3538 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3540 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3542 /* No constraint on the width -> no offset. */
3543 test_offset_impl (emoji_col
, 0, 0);
3545 /* Caret is before the beginning -> no offset. */
3546 test_offset_impl (0, 100, 0);
3548 /* Caret is past the end of the line -> no offset. */
3549 test_offset_impl (line_bytes
+1, 100, 0);
3551 /* Line fits in the display -> no offset. */
3552 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
, 0);
3553 test_offset_impl (emoji_col
, line_display_cols
+ test_left_margin
, 0);
3555 /* Line is too long for the display but caret location is OK
3556 anyway -> no offset. */
3557 static const int small_width
= 24;
3558 test_offset_impl (1, small_width
, 0);
3560 /* Width constraint is very small -> no offset. */
3561 test_offset_impl (emoji_col
, CARET_LINE_MARGIN
, 0);
3563 /* Line would be offset, but due to large line numbers, offsetting
3564 would remove the whole line -> no offset. */
3565 static const int huge_left_margin
= 100;
3566 test_offset_impl (emoji_col
, huge_left_margin
, 0, huge_left_margin
);
3568 /* Line is the same length as the display, but the line number makes it too
3569 long, so offset is required. Caret is at the end so padding on the right
3570 is not in effect. */
3571 for (int excess
= 1; excess
<= 3; ++excess
)
3572 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
- excess
,
3575 /* Line is much too long for the display, caret is near the end ->
3576 offset should be such that the line fits in the display and caret
3577 remains the same distance from the end that it was. */
3578 for (int caret_offset
= 0, max_offset
= MIN (CARET_LINE_MARGIN
, 10);
3579 caret_offset
<= max_offset
; ++caret_offset
)
3580 test_offset_impl (line_bytes
- caret_offset
, small_width
,
3581 line_display_cols
+ test_left_margin
- small_width
);
3583 /* As previous case but caret is closer to the middle; now we want it to end
3584 up CARET_LINE_MARGIN bytes from the end. */
3585 ASSERT_GT (line_display_cols
- emoji_col
, CARET_LINE_MARGIN
);
3586 test_offset_impl (emoji_col
, small_width
,
3587 emoji_col
+ test_left_margin
3588 - (small_width
- CARET_LINE_MARGIN
));
3590 /* Test that the source line is offset as expected when printed. */
3592 test_diagnostic_context dc
;
3593 dc
.m_source_printing
.max_width
= small_width
- 6;
3594 dc
.m_source_printing
.min_margin_width
3595 = test_left_margin
- test_linenum_sep
+ 1;
3596 dc
.m_source_printing
.show_line_numbers_p
= true;
3597 dc
.m_source_printing
.show_ruler_p
= true;
3598 diagnostic_source_print_policy
policy (dc
);
3599 rich_location
richloc (line_table
,
3600 linemap_position_for_column (line_table
,
3602 layout
test_layout (policy
, richloc
, nullptr);
3603 layout_printer
lp (*dc
.get_reference_printer (), test_layout
, richloc
, DK_ERROR
);
3605 ASSERT_STREQ (" | 1 \n"
3607 " | 234567890123456789\n"
3608 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3609 "that occupies 8 bytes and 4 display columns, starting at "
3612 pp_formatted_text (dc
.get_reference_printer ()));
3615 /* Similar to the previous example, but now the offset called for would split
3616 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3617 it with a padding space in this case. */
3619 test_diagnostic_context dc
;
3620 dc
.m_source_printing
.max_width
= small_width
- 5;
3621 dc
.m_source_printing
.min_margin_width
3622 = test_left_margin
- test_linenum_sep
+ 1;
3623 dc
.m_source_printing
.show_line_numbers_p
= true;
3624 dc
.m_source_printing
.show_ruler_p
= true;
3625 diagnostic_source_print_policy
policy (dc
);
3626 rich_location
richloc (line_table
,
3627 linemap_position_for_column (line_table
,
3629 layout
test_layout (dc
, richloc
, nullptr);
3630 layout_printer
lp (*dc
.get_reference_printer (), test_layout
, richloc
, DK_ERROR
);
3632 ASSERT_STREQ (" | 1 1 \n"
3634 " | 3456789012345678901\n"
3635 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3636 "that occupies 8 bytes and 4 display columns, starting at "
3639 pp_formatted_text (dc
.get_reference_printer ()));
3645 test_layout_x_offset_display_tab (const line_table_case
&case_
)
3648 = "This line is very long, so that we can use it to test the logic for "
3649 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3650 "a variable number of display columns, starting at column #103.\n";
3652 /* Number of bytes in the line, subtracting one to remove the newline. */
3653 const int line_bytes
= strlen (content
) - 1;
3655 /* The column where the tab begins. Byte or display is the same as there are
3656 no multibyte characters earlier on the line. */
3657 const int tab_col
= 103;
3659 /* Effective extra size of the tab beyond what a single space would have taken
3660 up, indexed by tabstop. */
3661 static const int num_tabstops
= 11;
3662 int extra_width
[num_tabstops
];
3663 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3665 const int this_tab_size
= tabstop
- (tab_col
- 1) % tabstop
;
3666 extra_width
[tabstop
] = this_tab_size
- 1;
3668 /* Example of this calculation: if tabstop is 10, the tab starting at column
3669 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3670 next character is at column #111. So it takes up 7 more columns than
3671 a space would have taken up. */
3672 ASSERT_EQ (7, extra_width
[10]);
3674 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3676 line_table_test
ltt (case_
);
3678 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3680 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
3682 /* Don't attempt to run the tests if column data might be unavailable. */
3683 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3686 /* Check that cpp_display_width handles the tabs as expected. */
3687 char_span lspan
= fc
.get_source_line (tmp
.get_filename (), 1);
3688 ASSERT_EQ ('\t', *(lspan
.get_buffer () + (tab_col
- 1)));
3689 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3691 cpp_char_column_policy
policy (tabstop
, cpp_wcwidth
);
3692 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
3693 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
3695 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
3696 location_compute_display_column (fc
,
3697 expand_location (line_end
),
3701 /* Check that the tab is expanded to the expected number of spaces. */
3702 rich_location
richloc (line_table
,
3703 linemap_position_for_column (line_table
,
3705 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3707 test_diagnostic_context dc
;
3708 dc
.m_tabstop
= tabstop
;
3709 diagnostic_source_print_policy
policy (dc
);
3710 layout
test_layout (policy
, richloc
, nullptr);
3711 layout_printer
lp (*dc
.get_reference_printer (), test_layout
, richloc
, DK_ERROR
);
3713 const char *out
= pp_formatted_text (dc
.get_reference_printer ());
3714 ASSERT_EQ (NULL
, strchr (out
, '\t'));
3715 const char *left_quote
= strchr (out
, '`');
3716 const char *right_quote
= strchr (out
, '\'');
3717 ASSERT_NE (NULL
, left_quote
);
3718 ASSERT_NE (NULL
, right_quote
);
3719 ASSERT_EQ (right_quote
- left_quote
, extra_width
[tabstop
] + 2);
3722 /* Check that the line is offset properly and that the tab is broken up
3723 into the expected number of spaces when it is the last character skipped
3725 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3727 test_diagnostic_context dc
;
3728 dc
.m_tabstop
= tabstop
;
3729 static const int small_width
= 24;
3730 dc
.m_source_printing
.max_width
= small_width
- 4;
3731 dc
.m_source_printing
.min_margin_width
3732 = test_left_margin
- test_linenum_sep
+ 1;
3733 dc
.m_source_printing
.show_line_numbers_p
= true;
3734 diagnostic_source_print_policy
policy (dc
);
3735 layout
test_layout (policy
, richloc
, nullptr);
3736 layout_printer
lp (*dc
.get_reference_printer (), test_layout
, richloc
, DK_ERROR
);
3739 /* We have arranged things so that two columns will be printed before
3740 the caret. If the tab results in more than one space, this should
3741 produce two spaces in the output; otherwise, it will be a single space
3742 preceded by the opening quote before the tab character. */
3744 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3745 "display columns, starting at column #103.\n"
3748 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3749 "display columns, starting at column #103.\n"
3751 const char *expected_output
= (extra_width
[tabstop
] ? output1
: output2
);
3752 ASSERT_STREQ (expected_output
, pp_formatted_text (dc
.get_reference_printer ()));
3757 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3760 test_diagnostic_show_locus_unknown_location ()
3762 test_diagnostic_context dc
;
3763 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3764 ASSERT_STREQ ("", dc
.test_show_locus (richloc
));
3767 /* Verify that diagnostic_show_locus works sanely for various
3770 All of these work on the following 1-line source file:
3773 "foo = bar.field;\n"
3774 which is set up by test_diagnostic_show_locus_one_liner and calls
3780 test_one_liner_simple_caret ()
3782 test_diagnostic_context dc
;
3783 location_t caret
= linemap_position_for_column (line_table
, 10);
3784 rich_location
richloc (line_table
, caret
);
3785 ASSERT_STREQ (" foo = bar.field;\n"
3787 dc
.test_show_locus (richloc
));
3790 /* No column information (column == 0).
3791 No annotation line should be printed. */
3794 test_one_liner_no_column ()
3796 test_diagnostic_context dc
;
3797 location_t caret
= linemap_position_for_column (line_table
, 0);
3798 rich_location
richloc (line_table
, caret
);
3799 ASSERT_STREQ (" foo = bar.field;\n",
3800 dc
.test_show_locus (richloc
));
3803 /* Caret and range. */
3806 test_one_liner_caret_and_range ()
3808 test_diagnostic_context dc
;
3809 location_t caret
= linemap_position_for_column (line_table
, 10);
3810 location_t start
= linemap_position_for_column (line_table
, 7);
3811 location_t finish
= linemap_position_for_column (line_table
, 15);
3812 location_t loc
= make_location (caret
, start
, finish
);
3813 rich_location
richloc (line_table
, loc
);
3814 ASSERT_STREQ (" foo = bar.field;\n"
3816 dc
.test_show_locus (richloc
));
3819 /* Multiple ranges and carets. */
3822 test_one_liner_multiple_carets_and_ranges ()
3824 test_diagnostic_context dc
;
3826 = make_location (linemap_position_for_column (line_table
, 2),
3827 linemap_position_for_column (line_table
, 1),
3828 linemap_position_for_column (line_table
, 3));
3829 dc
.m_source_printing
.caret_chars
[0] = 'A';
3832 = make_location (linemap_position_for_column (line_table
, 8),
3833 linemap_position_for_column (line_table
, 7),
3834 linemap_position_for_column (line_table
, 9));
3835 dc
.m_source_printing
.caret_chars
[1] = 'B';
3838 = make_location (linemap_position_for_column (line_table
, 13),
3839 linemap_position_for_column (line_table
, 11),
3840 linemap_position_for_column (line_table
, 15));
3841 dc
.m_source_printing
.caret_chars
[2] = 'C';
3843 rich_location
richloc (line_table
, foo
);
3844 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3845 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3846 ASSERT_STREQ (" foo = bar.field;\n"
3848 dc
.test_show_locus (richloc
));
3851 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3854 test_one_liner_fixit_insert_before ()
3856 test_diagnostic_context dc
;
3857 location_t caret
= linemap_position_for_column (line_table
, 7);
3858 rich_location
richloc (line_table
, caret
);
3859 richloc
.add_fixit_insert_before ("&");
3860 ASSERT_STREQ (" foo = bar.field;\n"
3863 dc
.test_show_locus (richloc
));
3866 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3869 test_one_liner_fixit_insert_after ()
3871 test_diagnostic_context dc
;
3872 location_t start
= linemap_position_for_column (line_table
, 1);
3873 location_t finish
= linemap_position_for_column (line_table
, 3);
3874 location_t foo
= make_location (start
, start
, finish
);
3875 rich_location
richloc (line_table
, foo
);
3876 richloc
.add_fixit_insert_after ("[0]");
3877 ASSERT_STREQ (" foo = bar.field;\n"
3880 dc
.test_show_locus (richloc
));
3883 /* Removal fix-it hint: removal of the ".field".
3884 Also verify the interaction of pp_set_prefix with rulers and
3888 test_one_liner_fixit_remove ()
3890 location_t start
= linemap_position_for_column (line_table
, 10);
3891 location_t finish
= linemap_position_for_column (line_table
, 15);
3892 location_t dot
= make_location (start
, start
, finish
);
3893 rich_location
richloc (line_table
, dot
);
3894 richloc
.add_fixit_remove ();
3898 test_diagnostic_context dc
;
3899 ASSERT_STREQ (" foo = bar.field;\n"
3902 dc
.test_show_locus (richloc
));
3905 /* Test of adding a prefix. */
3907 test_diagnostic_context dc
;
3908 pp_prefixing_rule (dc
.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3909 pp_set_prefix (dc
.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3910 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3911 "TEST PREFIX: ^~~~~~\n"
3912 "TEST PREFIX: ------\n",
3913 dc
.test_show_locus (richloc
));
3916 /* Normal, with ruler. */
3918 test_diagnostic_context dc
;
3919 dc
.m_source_printing
.show_ruler_p
= true;
3920 dc
.m_source_printing
.max_width
= 104;
3921 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3922 " 1 2 3 4 5 6 7 8 9 0 \n"
3923 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3924 " foo = bar.field;\n"
3927 dc
.test_show_locus (richloc
));
3930 /* Test of adding a prefix, with ruler. */
3932 test_diagnostic_context dc
;
3933 dc
.m_source_printing
.show_ruler_p
= true;
3934 dc
.m_source_printing
.max_width
= 50;
3935 pp_prefixing_rule (dc
.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3936 pp_set_prefix (dc
.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3937 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3938 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3939 "TEST PREFIX: foo = bar.field;\n"
3940 "TEST PREFIX: ^~~~~~\n"
3941 "TEST PREFIX: ------\n",
3942 dc
.test_show_locus (richloc
));
3945 /* Test of adding a prefix, with ruler and line numbers. */
3947 test_diagnostic_context dc
;
3948 dc
.m_source_printing
.show_ruler_p
= true;
3949 dc
.m_source_printing
.max_width
= 50;
3950 dc
.m_source_printing
.show_line_numbers_p
= true;
3951 pp_prefixing_rule (dc
.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3952 pp_set_prefix (dc
.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3953 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3954 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3955 "TEST PREFIX: 1 | foo = bar.field;\n"
3956 "TEST PREFIX: | ^~~~~~\n"
3957 "TEST PREFIX: | ------\n",
3958 dc
.test_show_locus (richloc
));
3962 /* Replace fix-it hint: replacing "field" with "m_field". */
3965 test_one_liner_fixit_replace ()
3967 test_diagnostic_context dc
;
3968 location_t start
= linemap_position_for_column (line_table
, 11);
3969 location_t finish
= linemap_position_for_column (line_table
, 15);
3970 location_t field
= make_location (start
, start
, finish
);
3971 rich_location
richloc (line_table
, field
);
3972 richloc
.add_fixit_replace ("m_field");
3973 ASSERT_STREQ (" foo = bar.field;\n"
3976 dc
.test_show_locus (richloc
));
3979 /* Replace fix-it hint: replacing "field" with "m_field",
3980 but where the caret was elsewhere. */
3983 test_one_liner_fixit_replace_non_equal_range ()
3985 test_diagnostic_context dc
;
3986 location_t equals
= linemap_position_for_column (line_table
, 5);
3987 location_t start
= linemap_position_for_column (line_table
, 11);
3988 location_t finish
= linemap_position_for_column (line_table
, 15);
3989 rich_location
richloc (line_table
, equals
);
3991 range
.m_start
= start
;
3992 range
.m_finish
= finish
;
3993 richloc
.add_fixit_replace (range
, "m_field");
3994 /* The replacement range is not indicated in the annotation line, so
3995 it should be indicated via an additional underline. */
3996 ASSERT_STREQ (" foo = bar.field;\n"
4000 dc
.test_show_locus (richloc
));
4003 /* Replace fix-it hint: replacing "field" with "m_field",
4004 where the caret was elsewhere, but where a secondary range
4005 exactly covers "field". */
4008 test_one_liner_fixit_replace_equal_secondary_range ()
4010 test_diagnostic_context dc
;
4011 location_t equals
= linemap_position_for_column (line_table
, 5);
4012 location_t start
= linemap_position_for_column (line_table
, 11);
4013 location_t finish
= linemap_position_for_column (line_table
, 15);
4014 rich_location
richloc (line_table
, equals
);
4015 location_t field
= make_location (start
, start
, finish
);
4016 richloc
.add_range (field
);
4017 richloc
.add_fixit_replace (field
, "m_field");
4018 /* The replacement range is indicated in the annotation line,
4019 so it shouldn't be indicated via an additional underline. */
4020 ASSERT_STREQ (" foo = bar.field;\n"
4023 dc
.test_show_locus (richloc
));
4026 /* Verify that we can use ad-hoc locations when adding fixits to a
4030 test_one_liner_fixit_validation_adhoc_locations ()
4032 /* Generate a range that's too long to be packed, so must
4033 be stored as an ad-hoc location (given the defaults
4034 of 5 or 7 bits or 0 bits of packed range); 150 columns > 2**7. */
4035 const location_t c7
= linemap_position_for_column (line_table
, 7);
4036 const location_t c157
= linemap_position_for_column (line_table
, 157);
4037 const location_t loc
= make_location (c7
, c7
, c157
);
4039 if (c157
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4042 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
4046 rich_location
richloc (line_table
, loc
);
4047 richloc
.add_fixit_insert_before (loc
, "test");
4048 /* It should not have been discarded by the validator. */
4049 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4051 test_diagnostic_context dc
;
4052 ASSERT_STREQ (" foo = bar.field;\n"
4066 dc
.test_show_locus (richloc
));
4071 rich_location
richloc (line_table
, loc
);
4072 source_range range
= source_range::from_locations (loc
, c157
);
4073 richloc
.add_fixit_remove (range
);
4074 /* It should not have been discarded by the validator. */
4075 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4077 test_diagnostic_context dc
;
4078 ASSERT_STREQ (" foo = bar.field;\n"
4091 " -----------------------------------------"
4103 dc
.test_show_locus (richloc
));
4108 rich_location
richloc (line_table
, loc
);
4109 source_range range
= source_range::from_locations (loc
, c157
);
4110 richloc
.add_fixit_replace (range
, "test");
4111 /* It should not have been discarded by the validator. */
4112 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4114 test_diagnostic_context dc
;
4115 ASSERT_STREQ (" foo = bar.field;\n"
4129 dc
.test_show_locus (richloc
));
4133 /* Test of consolidating insertions at the same location. */
4136 test_one_liner_many_fixits_1 ()
4138 test_diagnostic_context dc
;
4139 location_t equals
= linemap_position_for_column (line_table
, 5);
4140 rich_location
richloc (line_table
, equals
);
4141 for (int i
= 0; i
< 19; i
++)
4142 richloc
.add_fixit_insert_before ("a");
4143 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4144 ASSERT_STREQ (" foo = bar.field;\n"
4146 " aaaaaaaaaaaaaaaaaaa\n",
4147 dc
.test_show_locus (richloc
));
4150 /* Ensure that we can add an arbitrary number of fix-it hints to a
4151 rich_location, even if they are not consolidated. */
4154 test_one_liner_many_fixits_2 ()
4156 test_diagnostic_context dc
;
4157 location_t equals
= linemap_position_for_column (line_table
, 5);
4158 rich_location
richloc (line_table
, equals
);
4159 for (int i
= 0; i
< 19; i
++)
4161 location_t loc
= linemap_position_for_column (line_table
, (i
* 2) + 1);
4162 richloc
.add_fixit_insert_before (loc
, "a");
4164 ASSERT_EQ (19, richloc
.get_num_fixit_hints ());
4165 ASSERT_STREQ (" foo = bar.field;\n"
4167 " a a a a a a a a a a a a a a a a a a a\n",
4168 dc
.test_show_locus (richloc
));
4171 /* Test of labeling the ranges within a rich_location. */
4174 test_one_liner_labels ()
4177 = make_location (linemap_position_for_column (line_table
, 1),
4178 linemap_position_for_column (line_table
, 1),
4179 linemap_position_for_column (line_table
, 3));
4181 = make_location (linemap_position_for_column (line_table
, 7),
4182 linemap_position_for_column (line_table
, 7),
4183 linemap_position_for_column (line_table
, 9));
4185 = make_location (linemap_position_for_column (line_table
, 11),
4186 linemap_position_for_column (line_table
, 11),
4187 linemap_position_for_column (line_table
, 15));
4189 /* Example where all the labels fit on one line. */
4191 text_range_label
label0 ("0");
4192 text_range_label
label1 ("1");
4193 text_range_label
label2 ("2");
4194 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4195 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4196 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4199 test_diagnostic_context dc
;
4200 ASSERT_STREQ (" foo = bar.field;\n"
4204 dc
.test_show_locus (richloc
));
4207 /* Verify that we can disable label-printing. */
4209 test_diagnostic_context dc
;
4210 dc
.m_source_printing
.show_labels_p
= false;
4211 ASSERT_STREQ (" foo = bar.field;\n"
4213 dc
.test_show_locus (richloc
));
4217 /* Example where the labels need extra lines. */
4219 text_range_label
label0 ("label 0");
4220 text_range_label
label1 ("label 1");
4221 text_range_label
label2 ("label 2");
4222 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4223 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4224 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4226 test_diagnostic_context dc
;
4227 ASSERT_STREQ (" foo = bar.field;\n"
4233 dc
.test_show_locus (richloc
));
4236 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4237 but label 1 just touches label 2. */
4239 text_range_label
label0 ("aaaaa");
4240 text_range_label
label1 ("bbbb");
4241 text_range_label
label2 ("c");
4242 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4243 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4244 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4246 test_diagnostic_context dc
;
4247 ASSERT_STREQ (" foo = bar.field;\n"
4252 dc
.test_show_locus (richloc
));
4255 /* Example of out-of-order ranges (thus requiring a sort). */
4257 text_range_label
label0 ("0");
4258 text_range_label
label1 ("1");
4259 text_range_label
label2 ("2");
4260 gcc_rich_location
richloc (field
, &label0
, nullptr);
4261 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4262 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4264 test_diagnostic_context dc
;
4265 ASSERT_STREQ (" foo = bar.field;\n"
4269 dc
.test_show_locus (richloc
));
4272 /* Ensure we don't ICE if multiple ranges with labels are on
4275 text_range_label
label0 ("label 0");
4276 text_range_label
label1 ("label 1");
4277 text_range_label
label2 ("label 2");
4278 gcc_rich_location
richloc (bar
, &label0
, nullptr);
4279 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4280 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4282 test_diagnostic_context dc
;
4283 ASSERT_STREQ (" foo = bar.field;\n"
4289 dc
.test_show_locus (richloc
));
4292 /* Example of out-of-order ranges (thus requiring a sort), where
4293 they overlap, and there are multiple ranges on the same point. */
4295 text_range_label
label_0a ("label 0a");
4296 text_range_label
label_1a ("label 1a");
4297 text_range_label
label_2a ("label 2a");
4298 text_range_label
label_0b ("label 0b");
4299 text_range_label
label_1b ("label 1b");
4300 text_range_label
label_2b ("label 2b");
4301 text_range_label
label_0c ("label 0c");
4302 text_range_label
label_1c ("label 1c");
4303 text_range_label
label_2c ("label 2c");
4304 gcc_rich_location
richloc (field
, &label_0a
, nullptr);
4305 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1a
);
4306 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2a
);
4308 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0b
);
4309 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1b
);
4310 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2b
);
4312 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0c
);
4313 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1c
);
4314 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2c
);
4316 test_diagnostic_context dc
;
4317 ASSERT_STREQ (" foo = bar.field;\n"
4329 dc
.test_show_locus (richloc
));
4332 /* Verify that a NULL result from range_label::get_text is
4333 handled gracefully. */
4335 text_range_label
label (NULL
);
4336 gcc_rich_location
richloc (bar
, &label
, nullptr);
4338 test_diagnostic_context dc
;
4339 ASSERT_STREQ (" foo = bar.field;\n"
4341 dc
.test_show_locus (richloc
));
4344 /* TODO: example of formatted printing (needs to be in
4345 gcc-rich-location.cc due to Makefile.in issues). */
4348 /* Run the various one-liner tests. */
4351 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
4353 /* Create a tempfile and write some text to it.
4354 ....................0000000001111111.
4355 ....................1234567890123456. */
4356 const char *content
= "foo = bar.field;\n";
4358 diagnostic_show_locus_fixture
f (case_
, content
);
4360 location_t line_end
= linemap_position_for_column (line_table
, 16);
4362 /* Don't attempt to run the tests if column data might be unavailable. */
4363 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4366 ASSERT_STREQ (f
.get_filename (), LOCATION_FILE (line_end
));
4367 ASSERT_EQ (1, LOCATION_LINE (line_end
));
4368 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
4370 test_one_liner_simple_caret ();
4371 test_one_liner_no_column ();
4372 test_one_liner_caret_and_range ();
4373 test_one_liner_multiple_carets_and_ranges ();
4374 test_one_liner_fixit_insert_before ();
4375 test_one_liner_fixit_insert_after ();
4376 test_one_liner_fixit_remove ();
4377 test_one_liner_fixit_replace ();
4378 test_one_liner_fixit_replace_non_equal_range ();
4379 test_one_liner_fixit_replace_equal_secondary_range ();
4380 test_one_liner_fixit_validation_adhoc_locations ();
4381 test_one_liner_many_fixits_1 ();
4382 test_one_liner_many_fixits_2 ();
4383 test_one_liner_labels ();
4386 /* Version of all one-liner tests exercising multibyte awareness.
4387 These are all called from test_diagnostic_show_locus_one_liner,
4388 which uses diagnostic_show_locus_fixture_one_liner_utf8 to create
4389 the test file; see the notes in diagnostic-show-locus-selftest.h.
4391 Note: all of the below asserts would be easier to read if we used UTF-8
4392 directly in the string constants, but it seems better not to demand the
4393 host compiler support this, when it isn't otherwise necessary. Instead,
4394 whenever an extended character appears in a string, we put a line break
4395 after it so that all succeeding characters can appear visually at the
4396 correct display column. */
4401 test_one_liner_simple_caret_utf8 ()
4403 test_diagnostic_context dc
;
4404 location_t caret
= linemap_position_for_column (line_table
, 18);
4405 rich_location
richloc (line_table
, caret
);
4406 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4408 "_bar.\xf0\x9f\x98\x82"
4412 dc
.test_show_locus (richloc
));
4415 /* Caret and range. */
4417 test_one_liner_caret_and_range_utf8 ()
4419 test_diagnostic_context dc
;
4420 location_t caret
= linemap_position_for_column (line_table
, 18);
4421 location_t start
= linemap_position_for_column (line_table
, 12);
4422 location_t finish
= linemap_position_for_column (line_table
, 30);
4423 location_t loc
= make_location (caret
, start
, finish
);
4424 rich_location
richloc (line_table
, loc
);
4425 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4427 "_bar.\xf0\x9f\x98\x82"
4430 " ~~~~~^~~~~~~~~~\n",
4431 dc
.test_show_locus (richloc
));
4434 /* Multiple ranges and carets. */
4437 test_one_liner_multiple_carets_and_ranges_utf8 ()
4439 test_diagnostic_context dc
;
4441 = make_location (linemap_position_for_column (line_table
, 7),
4442 linemap_position_for_column (line_table
, 1),
4443 linemap_position_for_column (line_table
, 8));
4444 dc
.m_source_printing
.caret_chars
[0] = 'A';
4447 = make_location (linemap_position_for_column (line_table
, 16),
4448 linemap_position_for_column (line_table
, 12),
4449 linemap_position_for_column (line_table
, 17));
4450 dc
.m_source_printing
.caret_chars
[1] = 'B';
4453 = make_location (linemap_position_for_column (line_table
, 26),
4454 linemap_position_for_column (line_table
, 19),
4455 linemap_position_for_column (line_table
, 30));
4456 dc
.m_source_printing
.caret_chars
[2] = 'C';
4457 rich_location
richloc (line_table
, foo
);
4458 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
4459 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
4460 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4462 "_bar.\xf0\x9f\x98\x82"
4465 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
4466 dc
.test_show_locus (richloc
));
4469 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
4472 test_one_liner_fixit_insert_before_utf8 ()
4474 test_diagnostic_context dc
;
4475 location_t caret
= linemap_position_for_column (line_table
, 12);
4476 rich_location
richloc (line_table
, caret
);
4477 richloc
.add_fixit_insert_before ("&");
4478 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4480 "_bar.\xf0\x9f\x98\x82"
4485 dc
.test_show_locus (richloc
));
4488 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
4491 test_one_liner_fixit_insert_after_utf8 ()
4493 test_diagnostic_context dc
;
4494 location_t start
= linemap_position_for_column (line_table
, 1);
4495 location_t finish
= linemap_position_for_column (line_table
, 8);
4496 location_t foo
= make_location (start
, start
, finish
);
4497 rich_location
richloc (line_table
, foo
);
4498 richloc
.add_fixit_insert_after ("[0]");
4499 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4501 "_bar.\xf0\x9f\x98\x82"
4506 dc
.test_show_locus (richloc
));
4509 /* Removal fix-it hint: removal of the ".SS_fieldP". */
4512 test_one_liner_fixit_remove_utf8 ()
4514 test_diagnostic_context dc
;
4515 location_t start
= linemap_position_for_column (line_table
, 18);
4516 location_t finish
= linemap_position_for_column (line_table
, 30);
4517 location_t dot
= make_location (start
, start
, finish
);
4518 rich_location
richloc (line_table
, dot
);
4519 richloc
.add_fixit_remove ();
4520 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4522 "_bar.\xf0\x9f\x98\x82"
4527 dc
.test_show_locus (richloc
));
4530 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4533 test_one_liner_fixit_replace_utf8 ()
4535 test_diagnostic_context dc
;
4536 location_t start
= linemap_position_for_column (line_table
, 19);
4537 location_t finish
= linemap_position_for_column (line_table
, 30);
4538 location_t field
= make_location (start
, start
, finish
);
4539 rich_location
richloc (line_table
, field
);
4540 richloc
.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4541 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4543 "_bar.\xf0\x9f\x98\x82"
4547 " m_\xf0\x9f\x98\x82"
4549 dc
.test_show_locus (richloc
));
4552 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4553 but where the caret was elsewhere. */
4556 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4558 test_diagnostic_context dc
;
4559 location_t equals
= linemap_position_for_column (line_table
, 10);
4560 location_t start
= linemap_position_for_column (line_table
, 19);
4561 location_t finish
= linemap_position_for_column (line_table
, 30);
4562 rich_location
richloc (line_table
, equals
);
4564 range
.m_start
= start
;
4565 range
.m_finish
= finish
;
4566 richloc
.add_fixit_replace (range
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4567 /* The replacement range is not indicated in the annotation line, so
4568 it should be indicated via an additional underline. */
4569 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4571 "_bar.\xf0\x9f\x98\x82"
4576 " m_\xf0\x9f\x98\x82"
4578 dc
.test_show_locus (richloc
));
4581 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4582 where the caret was elsewhere, but where a secondary range
4583 exactly covers "field". */
4586 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4588 test_diagnostic_context dc
;
4589 location_t equals
= linemap_position_for_column (line_table
, 10);
4590 location_t start
= linemap_position_for_column (line_table
, 19);
4591 location_t finish
= linemap_position_for_column (line_table
, 30);
4592 rich_location
richloc (line_table
, equals
);
4593 location_t field
= make_location (start
, start
, finish
);
4594 richloc
.add_range (field
);
4595 richloc
.add_fixit_replace (field
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4596 /* The replacement range is indicated in the annotation line,
4597 so it shouldn't be indicated via an additional underline. */
4598 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4600 "_bar.\xf0\x9f\x98\x82"
4604 " m_\xf0\x9f\x98\x82"
4606 dc
.test_show_locus (richloc
));
4609 /* Verify that we can use ad-hoc locations when adding fixits to a
4613 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4615 /* Generate a range that's too long to be packed, so must
4616 be stored as an ad-hoc location (given the defaults
4617 of 5 bits or 7 bits or 0 bits of packed range); 150 columns > 2**7. */
4618 const location_t c12
= linemap_position_for_column (line_table
, 12);
4619 const location_t c162
= linemap_position_for_column (line_table
, 162);
4620 const location_t loc
= make_location (c12
, c12
, c162
);
4622 if (c162
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4625 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
4629 rich_location
richloc (line_table
, loc
);
4630 richloc
.add_fixit_insert_before (loc
, "test");
4631 /* It should not have been discarded by the validator. */
4632 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4634 test_diagnostic_context dc
;
4635 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4637 "_bar.\xf0\x9f\x98\x82"
4640 " ^~~~~~~~~~~~~~~~ "
4653 dc
.test_show_locus (richloc
));
4658 rich_location
richloc (line_table
, loc
);
4659 source_range range
= source_range::from_locations (loc
, c162
);
4660 richloc
.add_fixit_remove (range
);
4661 /* It should not have been discarded by the validator. */
4662 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4664 test_diagnostic_context dc
;
4665 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4667 "_bar.\xf0\x9f\x98\x82"
4670 " ^~~~~~~~~~~~~~~~ "
4682 " -------------------------------------"
4694 dc
.test_show_locus (richloc
));
4699 rich_location
richloc (line_table
, loc
);
4700 source_range range
= source_range::from_locations (loc
, c162
);
4701 richloc
.add_fixit_replace (range
, "test");
4702 /* It should not have been discarded by the validator. */
4703 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4705 test_diagnostic_context dc
;
4706 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4708 "_bar.\xf0\x9f\x98\x82"
4711 " ^~~~~~~~~~~~~~~~ "
4724 dc
.test_show_locus (richloc
));
4728 /* Test of consolidating insertions at the same location. */
4731 test_one_liner_many_fixits_1_utf8 ()
4733 test_diagnostic_context dc
;
4734 location_t equals
= linemap_position_for_column (line_table
, 10);
4735 rich_location
richloc (line_table
, equals
);
4736 for (int i
= 0; i
< 19; i
++)
4737 richloc
.add_fixit_insert_before (i
& 1 ? "@" : "\xcf\x80");
4738 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4739 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4741 "_bar.\xf0\x9f\x98\x82"
4745 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4746 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4747 dc
.test_show_locus (richloc
));
4750 /* Ensure that we can add an arbitrary number of fix-it hints to a
4751 rich_location, even if they are not consolidated. */
4754 test_one_liner_many_fixits_2_utf8 ()
4756 test_diagnostic_context dc
;
4757 location_t equals
= linemap_position_for_column (line_table
, 10);
4758 rich_location
richloc (line_table
, equals
);
4759 const int nlocs
= 19;
4760 int locs
[nlocs
] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4761 34, 36, 38, 40, 42, 44};
4762 for (int i
= 0; i
!= nlocs
; ++i
)
4764 location_t loc
= linemap_position_for_column (line_table
, locs
[i
]);
4765 richloc
.add_fixit_insert_before (loc
, i
& 1 ? "@" : "\xcf\x80");
4768 ASSERT_EQ (nlocs
, richloc
.get_num_fixit_hints ());
4769 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4771 "_bar.\xf0\x9f\x98\x82"
4775 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4776 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4777 dc
.test_show_locus (richloc
));
4780 /* Test of labeling the ranges within a rich_location. */
4783 test_one_liner_labels_utf8 ()
4786 = make_location (linemap_position_for_column (line_table
, 1),
4787 linemap_position_for_column (line_table
, 1),
4788 linemap_position_for_column (line_table
, 8));
4790 = make_location (linemap_position_for_column (line_table
, 12),
4791 linemap_position_for_column (line_table
, 12),
4792 linemap_position_for_column (line_table
, 17));
4794 = make_location (linemap_position_for_column (line_table
, 19),
4795 linemap_position_for_column (line_table
, 19),
4796 linemap_position_for_column (line_table
, 30));
4798 /* Example where all the labels fit on one line. */
4800 /* These three labels contain multibyte characters such that their byte
4801 lengths are respectively (12, 10, 18), but their display widths are only
4802 (6, 5, 9). All three fit on the line when considering the display
4803 widths, but not when considering the byte widths, so verify that we do
4804 indeed put them all on one line. */
4805 text_range_label label0
4806 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4807 text_range_label label1
4808 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4809 text_range_label label2
4810 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4812 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4813 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4814 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4817 test_diagnostic_context dc
;
4818 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4820 "_bar.\xf0\x9f\x98\x82"
4823 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4825 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4826 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4827 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4828 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4829 dc
.test_show_locus (richloc
));
4834 /* Example where the labels need extra lines. */
4836 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
4837 text_range_label
label1 ("label 1\xcf\x80");
4838 text_range_label
label2 ("label 2\xcf\x80");
4839 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4840 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4841 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4843 test_diagnostic_context dc
;
4844 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4846 "_bar.\xf0\x9f\x98\x82"
4849 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4851 " | | label 2\xcf\x80\n"
4852 " | label 1\xcf\x80\n"
4853 " label 0\xf0\x9f\x98\x82\n",
4854 dc
.test_show_locus (richloc
));
4857 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4858 but label 1 just touches label 2. */
4860 text_range_label
label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4861 text_range_label
label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4862 text_range_label
label2 ("c");
4863 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4864 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4865 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4867 test_diagnostic_context dc
;
4868 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4870 "_bar.\xf0\x9f\x98\x82"
4873 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4876 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4877 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4878 dc
.test_show_locus (richloc
));
4881 /* Example of escaping the source lines. */
4883 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
4884 text_range_label
label1 ("label 1\xcf\x80");
4885 text_range_label
label2 ("label 2\xcf\x80");
4886 gcc_rich_location
richloc (foo
, &label0
, nullptr);
4887 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4888 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4889 richloc
.set_escape_on_output (true);
4892 test_diagnostic_context dc
;
4893 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
);
4894 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4895 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4897 " label 0\xf0\x9f\x98\x82"
4898 /* ... */ " label 1\xcf\x80"
4899 /* ...................*/ " label 2\xcf\x80\n",
4900 dc
.test_show_locus (richloc
));
4903 test_diagnostic_context dc
;
4904 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES
);
4906 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4907 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4909 " label 0\xf0\x9f\x98\x82"
4910 /* ... */ " label 1\xcf\x80"
4911 /* ..........................*/ " label 2\xcf\x80\n",
4912 dc
.test_show_locus (richloc
));
4917 /* Make sure that colorization codes don't interrupt a multibyte
4918 sequence, which would corrupt it. */
4920 test_one_liner_colorized_utf8 ()
4922 test_diagnostic_context dc
;
4923 dc
.m_source_printing
.colorize_source_p
= true;
4924 diagnostic_color_init (&dc
, DIAGNOSTICS_COLOR_YES
);
4925 const location_t pi
= linemap_position_for_column (line_table
, 12);
4926 rich_location
richloc (line_table
, pi
);
4928 /* In order to avoid having the test depend on exactly how the colorization
4929 was effected, just confirm there are two pi characters in the output. */
4930 const char *result
= dc
.test_show_locus (richloc
);
4931 const char *null_term
= result
+ strlen (result
);
4932 const char *first_pi
= strstr (result
, "\xcf\x80");
4933 ASSERT_TRUE (first_pi
&& first_pi
<= null_term
- 2);
4934 ASSERT_STR_CONTAINS (first_pi
+ 2, "\xcf\x80");
4937 static const char * const one_liner_utf8_content
4939 0000000000000000000000011111111111111111111111111111112222222222222
4940 1111111122222222345678900000000123456666666677777777890123444444445 */
4941 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4942 /* 0000000000000000000001111111111111111111222222222222222222222233333
4943 1111222233334444567890122223333456789999000011112222345678999900001
4946 diagnostic_show_locus_fixture_one_liner_utf8::
4947 diagnostic_show_locus_fixture_one_liner_utf8 (const line_table_case
&case_
)
4948 : diagnostic_show_locus_fixture (case_
, one_liner_utf8_content
)
4952 /* Run the various one-liner tests. */
4955 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case
&case_
)
4957 diagnostic_show_locus_fixture_one_liner_utf8
f (case_
);
4959 location_t line_end
= linemap_position_for_column (line_table
, 31);
4961 /* Don't attempt to run the tests if column data might be unavailable. */
4962 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4965 ASSERT_STREQ (f
.get_filename (), LOCATION_FILE (line_end
));
4966 ASSERT_EQ (1, LOCATION_LINE (line_end
));
4967 ASSERT_EQ (31, LOCATION_COLUMN (line_end
));
4969 char_span lspan
= f
.m_fc
.get_source_line (f
.get_filename (), 1);
4970 ASSERT_EQ (25, cpp_display_width (lspan
.get_buffer (), lspan
.length (),
4972 ASSERT_EQ (25, location_compute_display_column (f
.m_fc
,
4973 expand_location (line_end
),
4976 test_one_liner_simple_caret_utf8 ();
4977 test_one_liner_caret_and_range_utf8 ();
4978 test_one_liner_multiple_carets_and_ranges_utf8 ();
4979 test_one_liner_fixit_insert_before_utf8 ();
4980 test_one_liner_fixit_insert_after_utf8 ();
4981 test_one_liner_fixit_remove_utf8 ();
4982 test_one_liner_fixit_replace_utf8 ();
4983 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4984 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4985 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4986 test_one_liner_many_fixits_1_utf8 ();
4987 test_one_liner_many_fixits_2_utf8 ();
4988 test_one_liner_labels_utf8 ();
4989 test_one_liner_colorized_utf8 ();
4992 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4995 test_add_location_if_nearby (const line_table_case
&case_
)
4997 /* Create a tempfile and write some text to it.
4998 ...000000000111111111122222222223333333333.
4999 ...123456789012345678901234567890123456789. */
5001 = ("struct same_line { double x; double y; ;\n" /* line 1. */
5002 "struct different_line\n" /* line 2. */
5004 " double x;\n" /* line 4. */
5005 " double y;\n" /* line 5. */
5006 ";\n"); /* line 6. */
5007 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
, nullptr);
5008 line_table_test
ltt (case_
);
5010 const line_map_ordinary
*ord_map
5011 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5012 tmp
.get_filename (), 0));
5014 linemap_line_start (line_table
, 1, 100);
5016 const location_t final_line_end
5017 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 7);
5019 /* Don't attempt to run the tests if column data might be unavailable. */
5020 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5023 /* Test of add_location_if_nearby on the same line as the
5024 primary location. */
5026 test_diagnostic_context dc
;
5027 const location_t missing_close_brace_1_39
5028 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 39);
5029 const location_t matching_open_brace_1_18
5030 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
5031 gcc_rich_location
richloc (missing_close_brace_1_39
);
5032 bool added
= richloc
.add_location_if_nearby (dc
,
5033 matching_open_brace_1_18
);
5034 ASSERT_TRUE (added
);
5035 ASSERT_EQ (2, richloc
.get_num_locations ());
5036 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
5038 dc
.test_show_locus (richloc
));
5041 /* Test of add_location_if_nearby on a different line to the
5042 primary location. */
5044 test_diagnostic_context dc
;
5045 const location_t missing_close_brace_6_1
5046 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 1);
5047 const location_t matching_open_brace_3_1
5048 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 1);
5049 gcc_rich_location
richloc (missing_close_brace_6_1
);
5050 bool added
= richloc
.add_location_if_nearby (dc
,
5051 matching_open_brace_3_1
);
5052 ASSERT_FALSE (added
);
5053 ASSERT_EQ (1, richloc
.get_num_locations ());
5057 /* Verify that we print fixits even if they only affect lines
5058 outside those covered by the ranges in the rich_location. */
5061 test_diagnostic_show_locus_fixit_lines (const line_table_case
&case_
)
5063 /* Create a tempfile and write some text to it.
5064 ...000000000111111111122222222223333333333.
5065 ...123456789012345678901234567890123456789. */
5067 = ("struct point { double x; double y; };\n" /* line 1. */
5068 "struct point origin = {x: 0.0,\n" /* line 2. */
5069 " y\n" /* line 3. */
5072 " : 0.0};\n"); /* line 6. */
5073 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5074 line_table_test
ltt (case_
);
5076 const line_map_ordinary
*ord_map
5077 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5078 tmp
.get_filename (), 0));
5080 linemap_line_start (line_table
, 1, 100);
5082 const location_t final_line_end
5083 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
5085 /* Don't attempt to run the tests if column data might be unavailable. */
5086 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5089 /* A pair of tests for modernizing the initializers to C99-style. */
5091 /* The one-liner case (line 2). */
5093 test_diagnostic_context dc
;
5095 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 24);
5096 const location_t colon
5097 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 25);
5098 rich_location
richloc (line_table
, colon
);
5099 richloc
.add_fixit_insert_before (x
, ".");
5100 richloc
.add_fixit_replace (colon
, "=");
5101 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
5104 dc
.test_show_locus (richloc
));
5107 /* The multiline case. The caret for the rich_location is on line 6;
5108 verify that insertion fixit on line 3 is still printed (and that
5109 span starts are printed due to the gap between the span at line 3
5110 and that at line 6). */
5112 test_diagnostic_context dc
;
5114 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
5115 const location_t colon
5116 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
5117 rich_location
richloc (line_table
, colon
);
5118 richloc
.add_fixit_insert_before (y
, ".");
5119 richloc
.add_fixit_replace (colon
, "=");
5120 ASSERT_STREQ ("FILENAME:3:24:\n"
5127 dc
.test_show_locus (richloc
));
5130 /* As above, but verify the behavior of multiple line spans
5131 with line-numbering enabled. */
5134 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
5135 const location_t colon
5136 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
5137 rich_location
richloc (line_table
, colon
);
5138 richloc
.add_fixit_insert_before (y
, ".");
5139 richloc
.add_fixit_replace (colon
, "=");
5140 test_diagnostic_context dc
;
5141 dc
.m_source_printing
.show_line_numbers_p
= true;
5142 ASSERT_STREQ (" 3 | y\n"
5148 dc
.test_show_locus (richloc
));
5153 /* Verify that fix-it hints are appropriately consolidated.
5155 If any fix-it hints in a rich_location involve locations beyond
5156 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
5157 the fix-it as a whole, so there should be none.
5159 Otherwise, verify that consecutive "replace" and "remove" fix-its
5160 are merged, and that other fix-its remain separate. */
5163 test_fixit_consolidation (const line_table_case
&case_
)
5165 line_table_test
ltt (case_
);
5167 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
5169 const location_t c10
= linemap_position_for_column (line_table
, 10);
5170 const location_t c15
= linemap_position_for_column (line_table
, 15);
5171 const location_t c16
= linemap_position_for_column (line_table
, 16);
5172 const location_t c17
= linemap_position_for_column (line_table
, 17);
5173 const location_t c20
= linemap_position_for_column (line_table
, 20);
5174 const location_t c21
= linemap_position_for_column (line_table
, 21);
5175 const location_t caret
= c10
;
5177 /* Insert + insert. */
5179 rich_location
richloc (line_table
, caret
);
5180 richloc
.add_fixit_insert_before (c10
, "foo");
5181 richloc
.add_fixit_insert_before (c15
, "bar");
5183 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5184 /* Bogus column info for 2nd fixit, so no fixits. */
5185 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5187 /* They should not have been merged. */
5188 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5191 /* Insert + replace. */
5193 rich_location
richloc (line_table
, caret
);
5194 richloc
.add_fixit_insert_before (c10
, "foo");
5195 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
5198 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5199 /* Bogus column info for 2nd fixit, so no fixits. */
5200 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5202 /* They should not have been merged. */
5203 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5206 /* Replace + non-consecutive insert. */
5208 rich_location
richloc (line_table
, caret
);
5209 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
5211 richloc
.add_fixit_insert_before (c17
, "foo");
5213 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5214 /* Bogus column info for 2nd fixit, so no fixits. */
5215 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5217 /* They should not have been merged. */
5218 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5221 /* Replace + non-consecutive replace. */
5223 rich_location
richloc (line_table
, caret
);
5224 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
5226 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
5229 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5230 /* Bogus column info for 2nd fixit, so no fixits. */
5231 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5233 /* They should not have been merged. */
5234 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5237 /* Replace + consecutive replace. */
5239 rich_location
richloc (line_table
, caret
);
5240 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
5242 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
5245 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5246 /* Bogus column info for 2nd fixit, so no fixits. */
5247 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5250 /* They should have been merged into a single "replace". */
5251 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5252 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
5253 ASSERT_STREQ ("foobar", hint
->get_string ());
5254 ASSERT_EQ (c10
, hint
->get_start_loc ());
5255 ASSERT_EQ (c21
, hint
->get_next_loc ());
5259 /* Replace + consecutive removal. */
5261 rich_location
richloc (line_table
, caret
);
5262 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
5264 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
5266 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5267 /* Bogus column info for 2nd fixit, so no fixits. */
5268 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5271 /* They should have been merged into a single replace, with the
5272 range extended to cover that of the removal. */
5273 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5274 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
5275 ASSERT_STREQ ("foo", hint
->get_string ());
5276 ASSERT_EQ (c10
, hint
->get_start_loc ());
5277 ASSERT_EQ (c21
, hint
->get_next_loc ());
5281 /* Consecutive removals. */
5283 rich_location
richloc (line_table
, caret
);
5284 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
5285 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
5287 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5288 /* Bogus column info for 2nd fixit, so no fixits. */
5289 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
5292 /* They should have been merged into a single "replace-with-empty". */
5293 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5294 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
5295 ASSERT_STREQ ("", hint
->get_string ());
5296 ASSERT_EQ (c10
, hint
->get_start_loc ());
5297 ASSERT_EQ (c21
, hint
->get_next_loc ());
5302 /* Verify that the line_corrections machinery correctly prints
5303 overlapping fixit-hints. */
5306 test_overlapped_fixit_printing (const line_table_case
&case_
)
5308 /* Create a tempfile and write some text to it.
5309 ...000000000111111111122222222223333333333.
5310 ...123456789012345678901234567890123456789. */
5312 = (" foo *f = (foo *)ptr->field;\n");
5313 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
5315 line_table_test
ltt (case_
);
5317 const line_map_ordinary
*ord_map
5318 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5319 tmp
.get_filename (), 0));
5321 linemap_line_start (line_table
, 1, 100);
5323 const location_t final_line_end
5324 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
5326 /* Don't attempt to run the tests if column data might be unavailable. */
5327 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5330 /* A test for converting a C-style cast to a C++-style cast. */
5331 const location_t open_paren
5332 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 12);
5333 const location_t close_paren
5334 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
5335 const location_t expr_start
5336 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 19);
5337 const location_t expr_finish
5338 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 28);
5339 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
5341 /* Various examples of fix-it hints that aren't themselves consolidated,
5342 but for which the *printing* may need consolidation. */
5344 /* Example where 3 fix-it hints are printed as one. */
5346 test_diagnostic_context dc
;
5347 rich_location
richloc (line_table
, expr
);
5348 richloc
.add_fixit_replace (open_paren
, "const_cast<");
5349 richloc
.add_fixit_replace (close_paren
, "> (");
5350 richloc
.add_fixit_insert_after (")");
5352 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5354 " -----------------\n"
5355 " const_cast<foo *> (ptr->field)\n",
5356 dc
.test_show_locus (richloc
));
5358 /* Unit-test the line_corrections machinery. */
5359 char_display_policy
policy (make_char_policy (dc
, richloc
));
5360 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
5361 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
5362 ASSERT_EQ (column_range (12, 12),
5363 get_affected_range (fc
, policy
, hint_0
, CU_BYTES
));
5364 ASSERT_EQ (column_range (12, 12),
5365 get_affected_range (fc
, policy
, hint_0
, CU_DISPLAY_COLS
));
5366 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc
, policy
, hint_0
));
5367 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
5368 ASSERT_EQ (column_range (18, 18),
5369 get_affected_range (fc
, policy
, hint_1
, CU_BYTES
));
5370 ASSERT_EQ (column_range (18, 18),
5371 get_affected_range (fc
, policy
, hint_1
, CU_DISPLAY_COLS
));
5372 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc
, policy
, hint_1
));
5373 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
5374 ASSERT_EQ (column_range (29, 28),
5375 get_affected_range (fc
, policy
, hint_2
, CU_BYTES
));
5376 ASSERT_EQ (column_range (29, 28),
5377 get_affected_range (fc
, policy
, hint_2
, CU_DISPLAY_COLS
));
5378 ASSERT_EQ (column_range (29, 29), get_printed_columns (fc
, policy
, hint_2
));
5380 /* Add each hint in turn to a line_corrections instance,
5381 and verify that they are consolidated into one correction instance
5383 line_corrections
lc (fc
, policy
, tmp
.get_filename (), 1);
5385 /* The first replace hint by itself. */
5386 lc
.add_hint (hint_0
);
5387 ASSERT_EQ (1, lc
.m_corrections
.length ());
5388 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_bytes
);
5389 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
5390 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
5391 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
5393 /* After the second replacement hint, they are printed together
5394 as a replacement (along with the text between them). */
5395 lc
.add_hint (hint_1
);
5396 ASSERT_EQ (1, lc
.m_corrections
.length ());
5397 ASSERT_STREQ ("const_cast<foo *> (", lc
.m_corrections
[0]->m_text
);
5398 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_bytes
);
5399 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
5400 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
5402 /* After the final insertion hint, they are all printed together
5403 as a replacement (along with the text between them). */
5404 lc
.add_hint (hint_2
);
5405 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
5406 lc
.m_corrections
[0]->m_text
);
5407 ASSERT_EQ (1, lc
.m_corrections
.length ());
5408 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_bytes
);
5409 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_columns
);
5410 ASSERT_EQ (column_range (12, 41), lc
.m_corrections
[0]->m_printed_columns
);
5413 /* Example where two are consolidated during printing. */
5415 test_diagnostic_context dc
;
5416 rich_location
richloc (line_table
, expr
);
5417 richloc
.add_fixit_replace (open_paren
, "CAST (");
5418 richloc
.add_fixit_replace (close_paren
, ") (");
5419 richloc
.add_fixit_insert_after (")");
5421 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5426 dc
.test_show_locus (richloc
));
5429 /* Example where none are consolidated during printing. */
5431 test_diagnostic_context dc
;
5432 rich_location
richloc (line_table
, expr
);
5433 richloc
.add_fixit_replace (open_paren
, "CST (");
5434 richloc
.add_fixit_replace (close_paren
, ") (");
5435 richloc
.add_fixit_insert_after (")");
5437 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5442 dc
.test_show_locus (richloc
));
5445 /* Example of deletion fix-it hints. */
5447 test_diagnostic_context dc
;
5448 rich_location
richloc (line_table
, expr
);
5449 richloc
.add_fixit_insert_before (open_paren
, "(bar *)");
5450 source_range victim
= {open_paren
, close_paren
};
5451 richloc
.add_fixit_remove (victim
);
5453 /* This case is actually handled by fixit-consolidation,
5454 rather than by line_corrections. */
5455 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5457 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5461 dc
.test_show_locus (richloc
));
5464 /* Example of deletion fix-it hints that would overlap. */
5466 test_diagnostic_context dc
;
5467 rich_location
richloc (line_table
, expr
);
5468 richloc
.add_fixit_insert_before (open_paren
, "(longer *)");
5469 source_range victim
= {expr_start
, expr_finish
};
5470 richloc
.add_fixit_remove (victim
);
5472 /* These fixits are not consolidated. */
5473 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5475 /* But the corrections are. */
5476 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5478 " -----------------\n"
5479 " (longer *)(foo *)\n",
5480 dc
.test_show_locus (richloc
));
5483 /* Example of insertion fix-it hints that would overlap. */
5485 test_diagnostic_context dc
;
5486 rich_location
richloc (line_table
, expr
);
5487 richloc
.add_fixit_insert_before (open_paren
, "LONGER THAN THE CAST");
5488 richloc
.add_fixit_insert_after (close_paren
, "TEST");
5490 /* The first insertion is long enough that if printed naively,
5491 it would overlap with the second.
5492 Verify that they are printed as a single replacement. */
5493 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5496 " LONGER THAN THE CAST(foo *)TEST\n",
5497 dc
.test_show_locus (richloc
));
5501 /* Multibyte-aware version of preceding tests. See comments above
5502 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
5506 test_overlapped_fixit_printing_utf8 (const line_table_case
&case_
)
5508 /* Create a tempfile and write some text to it. */
5512 00000000000000000000000111111111111111111111111222222222222222223
5513 12344444444555555556789012344444444555555556789012345678999999990 */
5514 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
5515 /* 00000000000000000000011111111111111111111112222222222333333333333
5516 12344445555666677778901234566667777888899990123456789012333344445
5519 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
5520 line_table_test
ltt (case_
);
5522 const line_map_ordinary
*ord_map
5523 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5524 tmp
.get_filename (), 0));
5526 linemap_line_start (line_table
, 1, 100);
5528 const location_t final_line_end
5529 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 50);
5531 /* Don't attempt to run the tests if column data might be unavailable. */
5532 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5535 /* A test for converting a C-style cast to a C++-style cast. */
5536 const location_t open_paren
5537 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 14);
5538 const location_t close_paren
5539 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 22);
5540 const location_t expr_start
5541 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
5542 const location_t expr_finish
5543 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 34);
5544 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
5546 /* Various examples of fix-it hints that aren't themselves consolidated,
5547 but for which the *printing* may need consolidation. */
5549 /* Example where 3 fix-it hints are printed as one. */
5551 test_diagnostic_context dc
;
5552 file_cache
&fc
= dc
.get_file_cache ();
5553 rich_location
richloc (line_table
, expr
);
5554 richloc
.add_fixit_replace (open_paren
, "const_cast<");
5555 richloc
.add_fixit_replace (close_paren
, "> (");
5556 richloc
.add_fixit_insert_after (")");
5558 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5559 " *f = (f\xf0\x9f\x98\x82"
5560 " *)ptr->field\xcf\x80"
5563 " ------------------\n"
5564 " const_cast<f\xf0\x9f\x98\x82"
5565 " *> (ptr->field\xcf\x80"
5567 dc
.test_show_locus (richloc
));
5569 /* Unit-test the line_corrections machinery. */
5570 char_display_policy
policy (make_char_policy (dc
, richloc
));
5571 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
5572 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
5573 ASSERT_EQ (column_range (14, 14),
5574 get_affected_range (fc
, policy
, hint_0
, CU_BYTES
));
5575 ASSERT_EQ (column_range (12, 12),
5576 get_affected_range (fc
, policy
, hint_0
, CU_DISPLAY_COLS
));
5577 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc
, policy
, hint_0
));
5578 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
5579 ASSERT_EQ (column_range (22, 22),
5580 get_affected_range (fc
, policy
, hint_1
, CU_BYTES
));
5581 ASSERT_EQ (column_range (18, 18),
5582 get_affected_range (fc
, policy
, hint_1
, CU_DISPLAY_COLS
));
5583 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc
, policy
, hint_1
));
5584 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
5585 ASSERT_EQ (column_range (35, 34),
5586 get_affected_range (fc
, policy
, hint_2
, CU_BYTES
));
5587 ASSERT_EQ (column_range (30, 29),
5588 get_affected_range (fc
, policy
, hint_2
, CU_DISPLAY_COLS
));
5589 ASSERT_EQ (column_range (30, 30), get_printed_columns (fc
, policy
, hint_2
));
5591 /* Add each hint in turn to a line_corrections instance,
5592 and verify that they are consolidated into one correction instance
5594 line_corrections
lc (fc
, policy
, tmp
.get_filename (), 1);
5596 /* The first replace hint by itself. */
5597 lc
.add_hint (hint_0
);
5598 ASSERT_EQ (1, lc
.m_corrections
.length ());
5599 ASSERT_EQ (column_range (14, 14), lc
.m_corrections
[0]->m_affected_bytes
);
5600 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
5601 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
5602 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
5604 /* After the second replacement hint, they are printed together
5605 as a replacement (along with the text between them). */
5606 lc
.add_hint (hint_1
);
5607 ASSERT_EQ (1, lc
.m_corrections
.length ());
5608 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5609 lc
.m_corrections
[0]->m_text
);
5610 ASSERT_EQ (column_range (14, 22), lc
.m_corrections
[0]->m_affected_bytes
);
5611 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
5612 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
5614 /* After the final insertion hint, they are all printed together
5615 as a replacement (along with the text between them). */
5616 lc
.add_hint (hint_2
);
5617 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5618 lc
.m_corrections
[0]->m_text
);
5619 ASSERT_EQ (1, lc
.m_corrections
.length ());
5620 ASSERT_EQ (column_range (14, 34), lc
.m_corrections
[0]->m_affected_bytes
);
5621 ASSERT_EQ (column_range (12, 29), lc
.m_corrections
[0]->m_affected_columns
);
5622 ASSERT_EQ (column_range (12, 42), lc
.m_corrections
[0]->m_printed_columns
);
5625 /* Example where two are consolidated during printing. */
5627 test_diagnostic_context dc
;
5628 rich_location
richloc (line_table
, expr
);
5629 richloc
.add_fixit_replace (open_paren
, "CAST (");
5630 richloc
.add_fixit_replace (close_paren
, ") (");
5631 richloc
.add_fixit_insert_after (")");
5633 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5634 " *f = (f\xf0\x9f\x98\x82"
5635 " *)ptr->field\xcf\x80"
5641 dc
.test_show_locus (richloc
));
5644 /* Example where none are consolidated during printing. */
5646 test_diagnostic_context dc
;
5647 rich_location
richloc (line_table
, expr
);
5648 richloc
.add_fixit_replace (open_paren
, "CST (");
5649 richloc
.add_fixit_replace (close_paren
, ") (");
5650 richloc
.add_fixit_insert_after (")");
5652 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5653 " *f = (f\xf0\x9f\x98\x82"
5654 " *)ptr->field\xcf\x80"
5660 dc
.test_show_locus (richloc
));
5663 /* Example of deletion fix-it hints. */
5665 test_diagnostic_context dc
;
5666 rich_location
richloc (line_table
, expr
);
5667 richloc
.add_fixit_insert_before (open_paren
, "(bar\xf0\x9f\x98\x82 *)");
5668 source_range victim
= {open_paren
, close_paren
};
5669 richloc
.add_fixit_remove (victim
);
5671 /* This case is actually handled by fixit-consolidation,
5672 rather than by line_corrections. */
5673 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5675 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5676 " *f = (f\xf0\x9f\x98\x82"
5677 " *)ptr->field\xcf\x80"
5681 " (bar\xf0\x9f\x98\x82"
5683 dc
.test_show_locus (richloc
));
5686 /* Example of deletion fix-it hints that would overlap. */
5688 test_diagnostic_context dc
;
5689 rich_location
richloc (line_table
, expr
);
5690 richloc
.add_fixit_insert_before (open_paren
, "(long\xf0\x9f\x98\x82 *)");
5691 source_range victim
= {expr_start
, expr_finish
};
5692 richloc
.add_fixit_remove (victim
);
5694 /* These fixits are not consolidated. */
5695 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5697 /* But the corrections are. */
5698 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5699 " *f = (f\xf0\x9f\x98\x82"
5700 " *)ptr->field\xcf\x80"
5703 " ------------------\n"
5704 " (long\xf0\x9f\x98\x82"
5705 " *)(f\xf0\x9f\x98\x82"
5707 dc
.test_show_locus (richloc
));
5710 /* Example of insertion fix-it hints that would overlap. */
5712 test_diagnostic_context dc
;
5713 rich_location
richloc (line_table
, expr
);
5714 richloc
.add_fixit_insert_before
5715 (open_paren
, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5716 richloc
.add_fixit_insert_after (close_paren
, "TEST");
5718 /* The first insertion is long enough that if printed naively,
5719 it would overlap with the second.
5720 Verify that they are printed as a single replacement. */
5721 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5722 " *f = (f\xf0\x9f\x98\x82"
5723 " *)ptr->field\xcf\x80"
5727 " L\xf0\x9f\x98\x82"
5728 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5730 dc
.test_show_locus (richloc
));
5734 /* Verify that the line_corrections machinery correctly prints
5735 overlapping fixit-hints that have been added in the wrong
5737 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5740 test_overlapped_fixit_printing_2 (const line_table_case
&case_
)
5742 /* Create a tempfile and write some text to it.
5743 ...000000000111111111122222222223333333333.
5744 ...123456789012345678901234567890123456789. */
5746 = ("int a5[][0][0] = { 1, 2 };\n");
5747 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5748 line_table_test
ltt (case_
);
5750 const line_map_ordinary
*ord_map
5751 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5752 tmp
.get_filename (), 0));
5754 linemap_line_start (line_table
, 1, 100);
5756 const location_t final_line_end
5757 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 100);
5759 /* Don't attempt to run the tests if column data might be unavailable. */
5760 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5763 const location_t col_1
5764 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5765 const location_t col_20
5766 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 20);
5767 const location_t col_21
5768 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 21);
5769 const location_t col_23
5770 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
5771 const location_t col_25
5772 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 25);
5774 /* Two insertions, in the wrong order. */
5776 test_diagnostic_context dc
;
5777 file_cache
&fc
= dc
.get_file_cache ();
5779 rich_location
richloc (line_table
, col_20
);
5780 richloc
.add_fixit_insert_before (col_23
, "{");
5781 richloc
.add_fixit_insert_before (col_21
, "}");
5783 /* These fixits should be accepted; they can't be consolidated. */
5784 char_display_policy
policy (make_char_policy (dc
, richloc
));
5785 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5786 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
5787 ASSERT_EQ (column_range (23, 22),
5788 get_affected_range (fc
, policy
, hint_0
, CU_BYTES
));
5789 ASSERT_EQ (column_range (23, 23), get_printed_columns (fc
, policy
, hint_0
));
5790 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
5791 ASSERT_EQ (column_range (21, 20),
5792 get_affected_range (fc
, policy
, hint_1
, CU_BYTES
));
5793 ASSERT_EQ (column_range (21, 21), get_printed_columns (fc
, policy
, hint_1
));
5795 /* Verify that they're printed correctly. */
5796 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5799 dc
.test_show_locus (richloc
));
5802 /* Various overlapping insertions, some occurring "out of order"
5803 (reproducing the fix-it hints from PR c/81405). */
5805 test_diagnostic_context dc
;
5806 rich_location
richloc (line_table
, col_20
);
5808 richloc
.add_fixit_insert_before (col_20
, "{{");
5809 richloc
.add_fixit_insert_before (col_21
, "}}");
5810 richloc
.add_fixit_insert_before (col_23
, "{");
5811 richloc
.add_fixit_insert_before (col_21
, "}");
5812 richloc
.add_fixit_insert_before (col_23
, "{{");
5813 richloc
.add_fixit_insert_before (col_25
, "}");
5814 richloc
.add_fixit_insert_before (col_21
, "}");
5815 richloc
.add_fixit_insert_before (col_1
, "{");
5816 richloc
.add_fixit_insert_before (col_25
, "}");
5818 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5821 " {{1}}}}, {{{2 }}\n",
5822 dc
.test_show_locus (richloc
));
5826 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5829 test_fixit_insert_containing_newline (const line_table_case
&case_
)
5831 /* Create a tempfile and write some text to it.
5832 .........................0000000001111111.
5833 .........................1234567890123456. */
5834 const char *old_content
= (" case 'a':\n" /* line 1. */
5835 " x = a;\n" /* line 2. */
5836 " case 'b':\n" /* line 3. */
5837 " x = b;\n");/* line 4. */
5839 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5840 line_table_test
ltt (case_
);
5841 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
5843 location_t case_start
= linemap_position_for_column (line_table
, 5);
5844 location_t case_finish
= linemap_position_for_column (line_table
, 13);
5845 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
5846 location_t line_start
= linemap_position_for_column (line_table
, 1);
5848 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5851 /* Add a "break;" on a line by itself before line 3 i.e. before
5852 column 1 of line 3. */
5854 rich_location
richloc (line_table
, case_loc
);
5855 richloc
.add_fixit_insert_before (line_start
, " break;\n");
5857 /* Without line numbers. */
5859 test_diagnostic_context dc
;
5860 ASSERT_STREQ (" x = a;\n"
5864 dc
.test_show_locus (richloc
));
5867 /* With line numbers. */
5869 test_diagnostic_context dc
;
5870 dc
.m_source_printing
.show_line_numbers_p
= true;
5871 ASSERT_STREQ (" 2 | x = a;\n"
5875 dc
.test_show_locus (richloc
));
5879 /* Verify that attempts to add text with a newline fail when the
5880 insertion point is *not* at the start of a line. */
5882 rich_location
richloc (line_table
, case_loc
);
5883 richloc
.add_fixit_insert_before (case_start
, "break;\n");
5884 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5885 test_diagnostic_context dc
;
5886 ASSERT_STREQ (" case 'b':\n"
5888 dc
.test_show_locus (richloc
));
5892 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5893 of the file, where the fix-it is printed in a different line-span
5894 to the primary range of the diagnostic. */
5897 test_fixit_insert_containing_newline_2 (const line_table_case
&case_
)
5899 /* Create a tempfile and write some text to it.
5900 .........................0000000001111111.
5901 .........................1234567890123456. */
5902 const char *old_content
= ("test (int ch)\n" /* line 1. */
5904 " putchar (ch);\n" /* line 3. */
5905 "}\n"); /* line 4. */
5907 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5908 line_table_test
ltt (case_
);
5910 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5911 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5912 linemap_line_start (line_table
, 1, 100);
5914 /* The primary range is the "putchar" token. */
5915 location_t putchar_start
5916 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 2);
5917 location_t putchar_finish
5918 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 8);
5919 location_t putchar_loc
5920 = make_location (putchar_start
, putchar_start
, putchar_finish
);
5921 rich_location
richloc (line_table
, putchar_loc
);
5923 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5924 location_t file_start
5925 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5926 richloc
.add_fixit_insert_before (file_start
, "#include <stdio.h>\n");
5928 if (putchar_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5932 test_diagnostic_context dc
;
5933 ASSERT_STREQ ("FILENAME:1:1:\n"
5934 "+#include <stdio.h>\n"
5939 dc
.test_show_locus (richloc
));
5942 /* With line-numbering, the line spans are close enough to be
5943 consolidated, since it makes little sense to skip line 2. */
5945 test_diagnostic_context dc
;
5946 dc
.m_source_printing
.show_line_numbers_p
= true;
5947 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5948 " 1 | test (int ch)\n"
5950 " 3 | putchar (ch);\n"
5952 dc
.test_show_locus (richloc
));
5956 /* Replacement fix-it hint containing a newline.
5957 This will fail, as newlines are only supported when inserting at the
5958 beginning of a line. */
5961 test_fixit_replace_containing_newline (const line_table_case
&case_
)
5963 /* Create a tempfile and write some text to it.
5964 .........................0000000001111.
5965 .........................1234567890123. */
5966 const char *old_content
= "foo = bar ();\n";
5968 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5969 line_table_test
ltt (case_
);
5970 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5972 /* Replace the " = " with "\n = ", as if we were reformatting an
5973 overly long line. */
5974 location_t start
= linemap_position_for_column (line_table
, 4);
5975 location_t finish
= linemap_position_for_column (line_table
, 6);
5976 location_t loc
= linemap_position_for_column (line_table
, 13);
5977 rich_location
richloc (line_table
, loc
);
5978 source_range range
= source_range::from_locations (start
, finish
);
5979 richloc
.add_fixit_replace (range
, "\n =");
5981 /* Arbitrary newlines are not yet supported within fix-it hints, so
5982 the fix-it should not be displayed. */
5983 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5985 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5988 test_diagnostic_context dc
;
5989 ASSERT_STREQ (" foo = bar ();\n"
5991 dc
.test_show_locus (richloc
));
5994 /* Fix-it hint, attempting to delete a newline.
5995 This will fail, as we currently only support fix-it hints that
5996 affect one line at a time. */
5999 test_fixit_deletion_affecting_newline (const line_table_case
&case_
)
6001 /* Create a tempfile and write some text to it.
6002 ..........................0000000001111.
6003 ..........................1234567890123. */
6004 const char *old_content
= ("foo = bar (\n"
6007 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
6008 line_table_test
ltt (case_
);
6009 const line_map_ordinary
*ord_map
= linemap_check_ordinary
6010 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
6011 linemap_line_start (line_table
, 1, 100);
6013 /* Attempt to delete the " (\n...)". */
6015 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 10);
6017 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
6019 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 7);
6020 location_t loc
= make_location (caret
, start
, finish
);
6021 rich_location
richloc (line_table
, loc
);
6022 richloc
. add_fixit_remove ();
6024 /* Fix-it hints that affect more than one line are not yet supported, so
6025 the fix-it should not be displayed. */
6026 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
6028 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
6031 test_diagnostic_context dc
;
6032 ASSERT_STREQ (" foo = bar (\n"
6036 dc
.test_show_locus (richloc
));
6040 test_tab_expansion (const line_table_case
&case_
)
6042 /* Create a tempfile and write some text to it. This example uses a tabstop
6043 of 8, as the column numbers attempt to indicate:
6045 .....................000.01111111111.22222333333 display
6046 .....................123.90123456789.56789012345 columns */
6047 const char *content
= " \t This: `\t' is a tab.\n";
6048 /* ....................000 00000011111 11111222222 byte
6049 ....................123 45678901234 56789012345 columns */
6051 const int tabstop
= 8;
6052 cpp_char_column_policy
policy (tabstop
, cpp_wcwidth
);
6053 const int first_non_ws_byte_col
= 7;
6054 const int right_quote_byte_col
= 15;
6055 const int last_byte_col
= 25;
6056 ASSERT_EQ (35, cpp_display_width (content
, last_byte_col
, policy
));
6058 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
6059 line_table_test
ltt (case_
);
6060 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
6062 /* Don't attempt to run the tests if column data might be unavailable. */
6063 location_t line_end
= linemap_position_for_column (line_table
, last_byte_col
);
6064 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
6067 /* Check that the leading whitespace with mixed tabs and spaces is expanded
6068 into 11 spaces. Recall that print_line() also puts one space before
6071 test_diagnostic_context dc
;
6072 dc
.m_tabstop
= tabstop
;
6073 rich_location
richloc (line_table
,
6074 linemap_position_for_column (line_table
,
6075 first_non_ws_byte_col
));
6076 ASSERT_STREQ (" This: ` ' is a tab.\n"
6078 dc
.test_show_locus (richloc
));
6081 /* Confirm the display width was tracked correctly across the internal tab
6084 test_diagnostic_context dc
;
6085 dc
.m_tabstop
= tabstop
;
6086 rich_location
richloc (line_table
,
6087 linemap_position_for_column (line_table
,
6088 right_quote_byte_col
));
6089 ASSERT_STREQ (" This: ` ' is a tab.\n"
6091 dc
.test_show_locus (richloc
));
6095 /* Verify that the escaping machinery can cope with a variety of different
6099 test_escaping_bytes_1 (const line_table_case
&case_
)
6101 const char content
[] = "before\0\1\2\3\v\x80\xff""after\n";
6102 const size_t sz
= sizeof (content
);
6103 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
, sz
);
6104 line_table_test
ltt (case_
);
6105 const line_map_ordinary
*ord_map
= linemap_check_ordinary
6106 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
6107 linemap_line_start (line_table
, 1, 100);
6110 = linemap_position_for_line_and_column (line_table
, ord_map
, 1,
6113 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
6116 /* Locations of the NUL and \v bytes. */
6118 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 7);
6120 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
6121 gcc_rich_location
richloc (nul_loc
);
6122 richloc
.add_range (v_loc
);
6125 test_diagnostic_context dc
;
6126 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
6128 dc
.test_show_locus (richloc
));
6130 richloc
.set_escape_on_output (true);
6132 test_diagnostic_context dc
;
6133 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
);
6135 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
6136 " ^~~~~~~~ ~~~~~~~~\n",
6137 dc
.test_show_locus (richloc
));
6140 test_diagnostic_context dc
;
6141 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES
);
6142 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
6144 dc
.test_show_locus (richloc
));
6148 /* As above, but verify that we handle the initial byte of a line
6152 test_escaping_bytes_2 (const line_table_case
&case_
)
6154 const char content
[] = "\0after\n";
6155 const size_t sz
= sizeof (content
);
6156 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
, sz
);
6157 line_table_test
ltt (case_
);
6158 const line_map_ordinary
*ord_map
= linemap_check_ordinary
6159 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
6160 linemap_line_start (line_table
, 1, 100);
6163 = linemap_position_for_line_and_column (line_table
, ord_map
, 1,
6166 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
6169 /* Location of the NUL byte. */
6171 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
6172 gcc_rich_location
richloc (nul_loc
);
6175 test_diagnostic_context dc
;
6176 ASSERT_STREQ (" after\n"
6178 dc
.test_show_locus (richloc
));
6180 richloc
.set_escape_on_output (true);
6182 test_diagnostic_context dc
;
6183 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
);
6184 ASSERT_STREQ (" <U+0000>after\n"
6186 dc
.test_show_locus (richloc
));
6189 test_diagnostic_context dc
;
6190 dc
.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES
);
6191 ASSERT_STREQ (" <00>after\n"
6193 dc
.test_show_locus (richloc
));
6197 /* Verify that line numbers are correctly printed for the case of
6198 a multiline range in which the width of the line numbers changes
6199 (e.g. from "9" to "10"). */
6202 test_line_numbers_multiline_range ()
6204 /* Create a tempfile and write some text to it. */
6206 for (int i
= 0; i
< 20; i
++)
6207 /* .........0000000001111111.
6208 .............1234567890123456. */
6209 pp_printf (&pp
, "this is line %i\n", i
+ 1);
6210 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", pp_formatted_text (&pp
));
6211 line_table_test ltt
;
6213 const line_map_ordinary
*ord_map
= linemap_check_ordinary
6214 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
6215 linemap_line_start (line_table
, 1, 100);
6217 /* Create a multi-line location, starting at the "line" of line 9, with
6218 a caret on the "is" of line 10, finishing on the "this" line 11. */
6221 = linemap_position_for_line_and_column (line_table
, ord_map
, 9, 9);
6223 = linemap_position_for_line_and_column (line_table
, ord_map
, 10, 6);
6225 = linemap_position_for_line_and_column (line_table
, ord_map
, 11, 4);
6226 location_t loc
= make_location (caret
, start
, finish
);
6228 test_diagnostic_context dc
;
6229 dc
.m_source_printing
.show_line_numbers_p
= true;
6230 dc
.m_source_printing
.min_margin_width
= 0;
6231 gcc_rich_location
richloc (loc
);
6232 ASSERT_STREQ (" 9 | this is line 9\n"
6234 "10 | this is line 10\n"
6235 " | ~~~~~^~~~~~~~~~\n"
6236 "11 | this is line 11\n"
6238 dc
.test_show_locus (richloc
));
6241 /* Run all of the selftests within this file. */
6244 diagnostic_show_locus_cc_tests ()
6248 test_layout_range_for_single_point ();
6249 test_layout_range_for_single_line ();
6250 test_layout_range_for_multiple_lines ();
6252 test_display_widths ();
6254 for_each_line_table_case (test_layout_x_offset_display_utf8
);
6255 for_each_line_table_case (test_layout_x_offset_display_tab
);
6257 test_get_line_bytes_without_trailing_whitespace ();
6259 test_diagnostic_show_locus_unknown_location ();
6261 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
6262 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8
);
6263 for_each_line_table_case (test_add_location_if_nearby
);
6264 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines
);
6265 for_each_line_table_case (test_fixit_consolidation
);
6266 for_each_line_table_case (test_overlapped_fixit_printing
);
6267 for_each_line_table_case (test_overlapped_fixit_printing_utf8
);
6268 for_each_line_table_case (test_overlapped_fixit_printing_2
);
6269 for_each_line_table_case (test_fixit_insert_containing_newline
);
6270 for_each_line_table_case (test_fixit_insert_containing_newline_2
);
6271 for_each_line_table_case (test_fixit_replace_containing_newline
);
6272 for_each_line_table_case (test_fixit_deletion_affecting_newline
);
6273 for_each_line_table_case (test_tab_expansion
);
6274 for_each_line_table_case (test_escaping_bytes_1
);
6275 for_each_line_table_case (test_escaping_bytes_2
);
6277 test_line_numbers_multiline_range ();
6280 } // namespace selftest
6282 #endif /* #if CHECKING_P */
6285 # pragma GCC diagnostic pop