x86: Add a test for PR rtl-optimization/111673
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob898efe74acf53962d115fa3424a860bfce4edafa
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
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #define INCLUDE_VECTOR
23 #include "system.h"
24 #include "coretypes.h"
25 #include "version.h"
26 #include "demangle.h"
27 #include "intl.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"
33 #include "selftest.h"
34 #include "selftest-diagnostic.h"
35 #include "selftest-diagnostic-show-locus.h"
36 #include "cpplib.h"
37 #include "text-art/types.h"
38 #include "text-art/theme.h"
39 #include "diagnostic-label-effects.h"
41 #ifdef HAVE_TERMIOS_H
42 # include <termios.h>
43 #endif
45 #ifdef GWINSZ_IN_SYS_IOCTL
46 # include <sys/ioctl.h>
47 #endif
49 /* Disable warnings about quoting issues in the pp_xxx calls below
50 that (intentionally) don't follow GCC diagnostic conventions. */
51 #if __GNUC__ >= 10
52 # pragma GCC diagnostic push
53 # pragma GCC diagnostic ignored "-Wformat-diag"
54 #endif
56 /* Classes for rendering source code and diagnostics, within an
57 anonymous namespace.
58 The work is done by "class layout", which embeds and uses
59 "class colorizer" and "class layout_range" to get things done. */
61 namespace {
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
65 this point. */
67 struct point_state
69 int range_idx;
70 bool draw_caret_p;
73 /* A class to inject colorization codes when printing the diagnostic locus.
75 It has one kind of colorization for each of:
76 - normal text
77 - range 0 (the "primary location")
78 - range 1
79 - range 2
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. */
89 class colorizer
91 public:
92 colorizer (pretty_printer &pp,
93 const rich_location &richloc,
94 diagnostic_t diagnostic_kind);
95 ~colorizer ();
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);
106 return;
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)
115 set_state (0);
116 else
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);
125 private:
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 *);
131 private:
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;
140 int m_current_state;
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. */
184 enum column_unit {
185 /* Measured in raw bytes. */
186 CU_BYTES = 0,
188 /* Measured in display units. */
189 CU_DISPLAY_COLS,
191 /* For arrays indexed by column_unit. */
192 CU_NUM_UNITS
195 /* Utility class to augment an exploc with the corresponding display column. */
197 class exploc_with_display_col : public expanded_location
199 public:
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--;
215 int prev_display_col
216 = (location_compute_display_column (fc, prev_exploc, policy));
217 m_display_col = prev_display_col + 1;
222 int m_display_col;
226 /* A point within a layout_range; similar to an exploc_with_display_col,
227 but after filtering on file. */
229 class layout_point
231 public:
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;
239 linenum_type m_line;
240 int m_columns[CU_NUM_UNITS];
243 /* A class for use by "class layout" below: a filtered location_range. */
245 class layout_range
247 public:
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. */
275 struct line_bounds
277 int m_first_non_ws_disp_col;
278 int m_last_non_ws_disp_col;
280 line_bounds ()
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). */
292 class line_span
294 public:
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);
313 if (first_line_cmp)
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;
322 #if CHECKING_P
324 /* Selftests for line_span. */
326 static void
327 test_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
369 public:
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. */
392 class layout
394 public:
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);
417 private:
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 ();
429 bool
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,
434 /* Outputs. */
435 point_state *out_state) const;
438 get_x_bound_for_row (linenum_type row, int caret_column,
439 int last_non_ws) const;
441 private:
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;
456 int m_linenum_width;
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. */
464 class layout_printer
466 public:
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);
474 private:
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,
486 int line_bytes);
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 ();
494 void
495 move_to_column (int *column, int dest_column, bool add_left_margin);
497 void print_any_right_to_left_edge_lines ();
499 private:
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
506 in execution paths).
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.
515 Consider e.g.
516 .x0000000001111111111222222222233333333334444444444.
517 .x1234567890123456789012345678901234567890123456789.
518 | | <- none
519 | (9) following ‘false’ branch... ->-+ <- none
520 | | <- none
521 | | <- none
522 |+----------------------------------------+ <- rewinding to lhs
523 || result->i = i; <- at lhs
524 || ~~~~~~~~~~^~~ <- at lhs
525 || | <- at lhs
526 |+----------->(10) ...to here <- indenting to dest
529 |leftmost column ("x" above).
530 "margin". */
531 enum class link_lhs_state {
532 none,
533 rewinding_to_lhs,
534 at_lhs,
535 indenting_to_dest
536 } m_link_lhs_state;
538 /* The column of the current link on the RHS, if any, or
539 -1 if there is none.
540 Consider e.g.
541 .x0000000001111111111222222222233333333334444444444.
542 .x1234567890123456789012345678901234567890123456789.
543 | | <- -1
544 | (10) following ‘false’ branch... ->-+ <- 42
545 | | <- 42
546 | | <- 42
547 |+-----------------------------------------+ <- 42
548 || result->i = i; <- -1
549 || ~~~~~~~~~~^~~ <- -1
550 || | <- -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) :
563 m_pp (pp),
564 m_richloc (richloc),
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
576 turn it off. */
578 colorizer::~colorizer ()
580 finish_state (m_current_state);
583 /* Update state, changing to the specific named color and printing its
584 color codes. */
586 void
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
595 change. */
597 void
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. */
610 void
611 colorizer::begin_state (int state)
613 switch (state)
615 case STATE_NORMAL_TEXT:
616 break;
618 case STATE_FIXIT_INSERT:
619 pp_string (&m_pp, m_fixit_insert);
620 break;
622 case STATE_FIXIT_DELETE:
623 pp_string (&m_pp, m_fixit_delete);
624 break;
626 case STATE_NAMED_COLOR:
627 /* Should be handled by colorizer::set_named_color. */
628 gcc_unreachable ();
630 case 0:
631 /* Make range 0 be the same color as the "kind" text
632 (error vs warning vs note). */
633 pp_string
634 (&m_pp,
635 colorize_start (pp_show_color (&m_pp),
636 diagnostic_get_color_for_kind (m_diagnostic_kind)));
637 break;
639 case 1:
640 pp_string (&m_pp, m_range1);
641 break;
643 case 2:
644 pp_string (&m_pp, m_range2);
645 break;
647 default:
648 /* For ranges beyond 2, alternate between color 1 and color 2. */
650 gcc_assert (state > 2);
651 pp_string (&m_pp,
652 state % 2 ? m_range1 : m_range2);
654 break;
658 /* Turn off any colorization for STATE. */
660 void
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). */
670 const char *
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),
693 m_label (label)
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 --+-----------------------------------------------
728 Legend:
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
733 within it).
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. */
740 bool
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). */
752 return false;
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. */
761 return false;
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) */
767 return true;
768 else
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
784 in example B). */
785 return false;
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);
792 return true;
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? */
802 bool
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)
807 return false;
808 if (row > m_finish.m_line)
809 return false;
810 return true;
813 /* Return true if this layout_range should have an in-edge. */
815 bool
816 layout_range::has_in_edge () const
818 if (!m_label)
819 return false;
820 const label_effects *effects = m_label->get_effects (m_original_idx);
821 if (!effects)
822 return false;
824 return effects->has_in_edge (m_original_idx);
827 /* Return true if this layout_range should have an out-edge. */
829 bool
830 layout_range::has_out_edge () const
832 if (!m_label)
833 return false;
834 const label_effects *effects = m_label->get_effects (m_original_idx);
835 if (!effects)
836 return false;
838 return effects->has_out_edge (m_original_idx);
841 #if CHECKING_P
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(). */
860 static layout_range
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),
878 0, NULL);
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. */
887 static void
888 test_layout_range_for_single_point ()
890 file_cache fc;
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));
905 /* At the point. */
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. */
924 static void
925 test_layout_range_for_single_line ()
927 file_cache fc;
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. */
967 static void
968 test_layout_range_for_multiple_lines ()
970 file_cache fc;
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));
985 /* At the start. */
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
993 values. */
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. */
1028 static int
1029 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
1031 int result = line_bytes;
1032 while (result > 0)
1034 char ch = line[result - 1];
1035 if (ch == ' ' || ch == '\t' || ch == '\r')
1036 result--;
1037 else
1038 break;
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'));
1046 return result;
1049 #if CHECKING_P
1051 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
1053 static void
1054 assert_eq (const char *line, int expected_bytes)
1056 int actual_value
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. */
1064 static void
1065 test_get_line_bytes_without_trailing_whitespace ()
1067 assert_eq ("", 0);
1068 assert_eq (" ", 0);
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
1092 being printed. */
1094 bool
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? */
1115 if (map_a == map_b)
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)
1127 return false;
1129 /* Expand each location towards the spelling location, and
1130 recurse. */
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,
1134 macro_map,
1135 loc_a);
1136 location_t loc_b_toward_spelling
1137 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
1138 macro_map,
1139 loc_b);
1140 return compatible_locations_p (loc_a_toward_spelling,
1141 loc_b_toward_spelling);
1144 /* Otherwise they are within the same ordinary map. */
1145 return true;
1147 else
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))
1154 return false;
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. */
1166 static int
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. */
1181 static void
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')
1190 pp_space (pp);
1191 continue;
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. */
1205 static int
1206 escape_as_bytes_width (cppchar_t ch)
1208 if (ch < 0x80 && ISPRINT (ch))
1209 return cpp_wcwidth (ch);
1210 else
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. */
1222 static void
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)
1231 char buf[16];
1232 sprintf (buf, "<%02x>", (unsigned char)*iter);
1233 pp_string (pp, buf);
1235 return;
1238 cppchar_t ch = decoded_ch.m_ch;
1239 if (ch < 0x80 && ISPRINT (ch))
1240 pp_character (pp, ch);
1241 else
1243 for (const char *iter = decoded_ch.m_start_byte;
1244 iter < decoded_ch.m_next_byte; ++iter)
1246 char buf[16];
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. */
1258 static int
1259 escape_as_unicode_width (cppchar_t ch)
1261 if (ch < 0x80 && ISPRINT (ch))
1262 return cpp_wcwidth (ch);
1263 else
1265 // Width of "<U+%04x>"
1266 if (ch > 0xfffff)
1267 return 10;
1268 else if (ch > 0xffff)
1269 return 9;
1270 else
1271 return 8;
1275 /* Callback for char_display_policy::m_print_cb for printing source chars
1276 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1278 static void
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);
1285 return;
1288 cppchar_t ch = decoded_ch.m_ch;
1289 if (ch < 0x80 && ISPRINT (ch))
1290 pp_character (pp, ch);
1291 else
1293 char buf[16];
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 (),
1308 cpp_wcwidth,
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 ())
1318 default:
1319 gcc_unreachable ();
1320 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1321 result.m_width_cb = escape_as_unicode_width;
1322 result.m_print_cb = escape_as_unicode_print;
1323 break;
1324 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1325 result.m_width_cb = escape_as_bytes_width;
1326 result.m_print_cb = escape_as_bytes_print;
1327 break;
1331 return result;
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
1377 same file. */
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. */
1408 bool
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)
1432 return false;
1433 if (finish.file != m_exploc.file)
1434 return false;
1435 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1436 if (caret.file != m_exploc.file)
1437 return false;
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. */
1445 return false;
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),
1464 range_display_kind,
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;
1489 else
1490 /* This is a non-primary range; ignore it. */
1491 return false;
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))
1501 return false;
1502 if (!will_show_line_p (finish.line))
1503 return false;
1504 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1505 if (!will_show_line_p (caret.line))
1506 return false;
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);
1512 return true;
1515 /* Return true iff ROW is within one of the line spans for this layout. */
1517 bool
1518 layout::will_show_line_p (linenum_type row) const
1520 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1521 line_span_idx++)
1523 const line_span *line_span = get_line_span (line_span_idx);
1524 if (line_span->contains_line_p (row))
1525 return true;
1527 return false;
1530 /* Print a line showing a gap in the line numbers, for showing the boundary
1531 between two line spans. */
1533 void
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, '.');
1543 pp_newline (&m_pp);
1546 /* Return true iff we should print a heading when starting the
1547 line span with the given index. */
1549 bool
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)
1555 return true;
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)
1560 return true;
1562 return false;
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. */
1569 expanded_location
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))
1574 return m_exploc;
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];
1586 return exploc;
1590 /* Otherwise, use the location of the first fixit-hint present within
1591 the line_span. */
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))
1598 return exploc;
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. */
1603 gcc_unreachable ();
1604 return m_exploc;
1607 /* Determine if HINT is meaningful to print within this layout. */
1609 bool
1610 layout::validate_fixit_hint_p (const fixit_hint *hint)
1612 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1613 return false;
1614 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1615 return false;
1617 return true;
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. */
1624 static line_span
1625 get_line_span_for_fixit_hint (const fixit_hint *hint)
1627 gcc_assert (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 ())
1634 if (start_line > 1)
1635 start_line--;
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:
1663 005 |RANGE 1
1664 006 |RANGE 1
1666 008 |PRIMARY RANGE
1667 009 |PRIMARY CARET
1668 010 |PRIMARY RANGE
1671 013 |RANGE 2
1672 014 |RANGE 2
1673 015 |RANGE 2
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. */
1681 void
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];
1703 gcc_assert (hint);
1704 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1707 /* Sort them. */
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;
1727 else
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
1752 m_linenum_width. */
1754 void
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)
1761 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
1775 location. */
1777 void
1778 layout::calculate_x_offset_display ()
1780 m_x_offset_display = 0;
1782 const int max_width = m_options.max_width;
1783 if (!max_width)
1785 /* Nothing to do, the width is not capped. */
1786 return;
1789 const char_span line = m_file_cache.get_source_line (m_exploc.file,
1790 m_exploc.line);
1791 if (!line)
1793 /* Nothing to do, we couldn't find the source line. */
1794 return;
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 (),
1799 line.length ());
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. */
1806 return;
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
1813 with a space. */
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. */
1824 return;
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,
1833 right_margin_size);
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. */
1838 return;
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. */
1857 line_bounds
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++)
1867 pp_space (&m_pp);
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,
1875 line_bytes);
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
1888 display columns. */
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)
1892 pp_space (&m_pp);
1894 /* Print the line and compute the line_bounds. */
1895 line_bounds lbounds;
1896 while (!dw.done ())
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)
1912 bool in_range_p;
1913 point_state state;
1914 const int start_byte_col = dw.bytes_processed () + 1;
1915 in_range_p = m_layout.get_state_at_point (row, start_byte_col,
1916 0, INT_MAX,
1917 CU_BYTES,
1918 &state);
1919 if (in_range_p)
1920 m_colorizer.set_range (state.range_idx);
1921 else
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);
1931 if (*c == '\t')
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)
1936 pp_space (&m_pp);
1937 continue;
1940 /* We have a (possibly multibyte) character to output; update the line
1941 bounds if it is not whitespace. */
1942 if (*c != ' ')
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 ();
1953 print_newline ();
1954 return lbounds;
1957 /* Determine if we should print an annotation line for ROW.
1958 i.e. if any of m_layout_ranges contains ROW. */
1960 bool
1961 layout::should_print_annotation_line_p (linenum_type row) const
1963 layout_range *range;
1964 int i;
1965 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1967 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1968 return false;
1969 if (range->intersects_line_p (row))
1970 return true;
1972 return false;
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). */
1978 void
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)
1986 default:
1987 gcc_unreachable ();
1988 case link_lhs_state::none:
1989 pp_space (&m_pp);
1990 break;
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 ();
1999 break;
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 ();
2008 break;
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 ();
2017 break;
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). */
2026 void
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. */
2034 int i;
2035 for (i = 0; i < m_layout.m_linenum_width - 3; i++)
2036 pp_space (&m_pp);
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 ();
2043 else
2044 pp_character (&m_pp, margin_char);
2047 /* Print a line consisting of the caret/underlines for the given
2048 source line. */
2050 void
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++)
2062 bool in_range_p;
2063 point_state state;
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,
2067 CU_DISPLAY_COLS,
2068 &state);
2069 if (in_range_p)
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. */
2076 char caret_char;
2077 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
2078 caret_char = get_options ().caret_chars[state.range_idx];
2079 else
2080 caret_char = '^';
2081 pp_character (&m_pp, caret_char);
2083 else
2084 pp_character (&m_pp, '~');
2086 else
2088 /* Not in a range. */
2089 m_colorizer.set_normal_text ();
2090 pp_character (&m_pp, ' ');
2093 print_newline ();
2096 /* A version of label_text that can live inside a vec.
2097 Requires manual cleanup via maybe_free. */
2099 struct pod_label_text
2101 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 ())
2109 other.release ();
2112 void maybe_free ()
2114 if (m_caller_owned)
2115 free (m_buffer);
2118 char *m_buffer;
2119 bool m_caller_owned;
2122 /* Implementation detail of layout::print_any_labels.
2124 A label within the given row of source. */
2126 class line_label
2128 public:
2129 line_label (int state_idx, int column,
2130 label_text text,
2131 bool has_in_edge,
2132 bool has_out_edge)
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);
2153 if (column_cmp)
2154 return column_cmp;
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);
2161 int m_state_idx;
2162 int m_column;
2163 pod_label_text m_text;
2164 size_t m_display_width;
2165 int m_label_line;
2166 bool m_has_vbar;
2167 bool m_has_in_edge;
2168 bool m_has_out_edge;
2171 /* Print any labels in this row. */
2172 void
2173 layout_printer::print_any_labels (linenum_type row)
2175 int i;
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)
2185 continue;
2187 /* The range's caret must be on this line. */
2188 if (range->m_caret.m_line != row)
2189 continue;
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)
2195 continue;
2197 label_text text;
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
2202 visibility). */
2203 if (text.get () == NULL)
2204 continue;
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)
2214 return;
2216 /* Sort them. */
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":
2225 foo + bar
2226 ~~~ ~~~
2227 | | : label line 0
2228 l0 l1 : label line 1
2230 If they would touch each other or overlap, then we need
2231 additional "label lines":
2233 foo + bar
2234 ~~~ ~~~
2235 | | : label line 0
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
2243 label lines:
2245 foo + bar
2246 ^ : label line 0
2247 | : label line 1
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;
2255 line_label *label;
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)
2261 max_label_line++;
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;
2294 line_label *label;
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. */
2299 break;
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
2310 . ^~~~~~~~~~~~~
2311 . this text. */
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);
2319 column++;
2321 if (column == label->m_column - 1)
2323 pp_character (&m_pp, '>');
2324 column++;
2326 m_colorizer.set_normal_text ();
2327 m_link_lhs_state = link_lhs_state::none;
2328 label_line_with_in_edge = -1;
2330 else
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
2334 diagnostic_path. */
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
2344 first part of e.g.
2345 . (9) following ‘false’ branch... ->-+ <- HERE
2348 . */
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 ();
2354 pp_space (&m_pp);
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 ();
2360 column += 5;
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 ();
2371 column++;
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 ();
2387 print_newline ();
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 ();
2403 print_newline ();
2406 /* Clean up. */
2408 line_label *label;
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,
2415 print them.
2417 They are printed on lines of their own, before the source line
2418 itself, with a leading '+'. */
2420 void
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. */
2429 continue;
2431 gcc_assert (hint->insertion_p ());
2433 if (hint->affects_line_p (m_layout.m_line_table,
2434 m_layout.m_exploc.file,
2435 row))
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 ();
2450 pp_newline (&m_pp);
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). */
2460 bool
2461 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2462 int finish_column) const
2464 layout_range *range;
2465 int i;
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)
2471 return true;
2472 return false;
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:
2480 new_text
2482 For replacement, these can look like:
2484 ------------- : underline showing affected range
2485 new_text
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
2495 latter.
2497 For example, consider a set of fix-its for converting
2498 a C-style cast to a C++ const_cast.
2500 Given:
2502 ..000000000111111111122222222223333333333.
2503 ..123456789012345678901234567890123456789.
2504 foo *f = (foo *)ptr->field;
2505 ^~~~~
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;
2515 ^~~~~
2517 const_cast<
2519 > ( )
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;
2526 ^~~~~
2527 -----------------
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<"
2536 and:
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 *" + "> ("
2542 i.e.:
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
2548 a no-op:
2549 - replace cols 17-26 with themselves ("ptr->field")
2550 giving:
2551 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2552 i.e.:
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. */
2559 class column_range
2561 public:
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);
2578 int start;
2579 int finish;
2582 /* Get the range of bytes or display columns that HINT would affect. */
2583 static column_range
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;
2592 int start_column;
2593 int 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;
2599 else
2600 finish_column
2601 = location_compute_display_column (fc, exploc_finish, policy);
2603 else
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. */
2613 static column_range
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 (),
2621 policy);
2622 int final_hint_column = start_column + hint_width - 1;
2623 if (hint->insertion_p ())
2625 return column_range (start_column, final_hint_column);
2627 else
2629 exploc = expand_location (hint->get_next_loc ());
2630 --exploc.column;
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. */
2642 class correction
2644 public:
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),
2655 m_policy (policy),
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
2686 the replacement.
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
2693 the replacement.
2694 If replace, then the range of columns affected. */
2695 column_range m_printed_columns;
2697 /* The text to be inserted/used as replacement. */
2698 char *m_text;
2699 size_t m_byte_length; /* Not including null-terminator. */
2700 int m_display_cols;
2701 const cpp_char_column_policy &m_policy;
2702 size_t m_alloc_sz;
2705 /* Ensure that m_text can hold a string of length LEN
2706 (plus 1 for 0-termination). */
2708 void
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. */
2722 void
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
2736 public:
2737 line_corrections (file_cache &fc,
2738 const char_display_policy &policy,
2739 const char *filename,
2740 linenum_type row)
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;
2751 linenum_type m_row;
2752 auto_vec <correction *> m_corrections;
2755 /* struct line_corrections. */
2757 line_corrections::~line_corrections ()
2759 unsigned i;
2760 correction *c;
2761 FOR_EACH_VEC_ELT (m_corrections, i, c)
2762 delete c;
2765 /* A struct wrapping a particular source line, allowing
2766 run-time bounds-checking of accesses in a checked build. */
2768 class source_line
2770 public:
2771 source_line (file_cache &fc, const char *filename, int line);
2773 char_span as_span () { return char_span (chars, width); }
2775 const char *chars;
2776 int 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. */
2792 void
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
2821 confusing the user.
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);
2839 int new_byte_len
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
2844 (old_byte_len,
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;
2860 return;
2865 /* If no consolidation happened, add a new correction instance. */
2866 m_corrections.safe_push (new correction (affected_bytes,
2867 affected_columns,
2868 printed_columns,
2869 hint->get_string (),
2870 hint->get_length (),
2871 m_policy));
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. */
2880 void
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 ())
2893 continue;
2895 if (hint->affects_line_p (m_layout.m_line_table,
2896 m_layout.m_exploc.file,
2897 row))
2898 corrections.add_hint (hint);
2901 /* Now print the corrections. */
2902 unsigned i;
2903 correction *c;
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;
2922 else
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,
2931 finish_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. */
2960 void
2961 layout_printer::print_newline ()
2963 m_colorizer.set_normal_text ();
2964 pp_newline (&m_pp);
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. */
2973 bool
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,
2978 /* Outputs. */
2979 point_state *out_state) const
2981 layout_range *range;
2982 int i;
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. */
2988 continue;
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)
3006 return false;
3008 /* We are within a range. */
3009 return true;
3013 return false;
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;
3032 int i;
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;
3055 return result;
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. */
3063 void
3064 layout_printer::move_to_column (int *column,
3065 int dest_column,
3066 bool add_left_margin)
3068 /* Start a new line if we need to. */
3069 if (*column > dest_column)
3071 print_newline ();
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
3080 pp_space call with
3081 pp_character (&m_pp, '0' + (*column % 10));
3082 to visualize the changing value of "*column". */
3083 pp_space (&m_pp);
3084 (*column)++;
3088 /* For debugging layout issues, render a ruler giving column numbers
3089 (after the 1-column indent). */
3091 void
3092 layout_printer::show_ruler (int max_column)
3094 /* Hundreds. */
3095 if (max_column > 99)
3097 start_annotation_line ();
3098 for (int column = 1 + m_layout.m_x_offset_display;
3099 column <= max_column;
3100 ++column)
3101 if (column % 10 == 0)
3102 pp_character (&m_pp, '0' + (column / 100) % 10);
3103 else
3104 pp_space (&m_pp);
3105 pp_newline (&m_pp);
3108 /* Tens. */
3109 start_annotation_line ();
3110 for (int column = 1 + m_layout.m_x_offset_display;
3111 column <= max_column;
3112 ++column)
3113 if (column % 10 == 0)
3114 pp_character (&m_pp, '0' + (column / 10) % 10);
3115 else
3116 pp_space (&m_pp);
3117 pp_newline (&m_pp);
3119 /* Units. */
3120 start_annotation_line ();
3121 for (int column = 1 + m_layout.m_x_offset_display;
3122 column <= max_column;
3123 ++column)
3124 pp_character (&m_pp, '0' + (column % 10));
3125 pp_newline (&m_pp);
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. */
3132 void
3133 layout_printer::print_line (linenum_type row)
3135 char_span line
3136 = m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row);
3137 if (!line)
3138 return;
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:
3152 " │\n"
3153 "┌──────────────────────────────────────────┘\n"
3154 showing the link entering at the top right and emerging
3155 at the bottom left. */
3157 void
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. */
3162 return;
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 ();
3175 pp_newline (&m_pp);
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;
3185 ++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 ();
3191 pp_newline (&m_pp);
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)
3203 : m_pp (pp),
3204 m_layout (layout),
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. */
3225 bool
3226 gcc_rich_location::
3227 add_location_if_nearby (const diagnostic_source_print_policy &policy,
3228 location_t loc,
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
3234 layout instance. */
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))
3243 return false;
3245 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
3246 return true;
3249 bool
3250 gcc_rich_location::
3251 add_location_if_nearby (const diagnostic_context &dc,
3252 location_t loc,
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. */
3266 void
3267 diagnostic_context::maybe_show_locus (const rich_location &richloc,
3268 const diagnostic_source_printing_options &opts,
3269 diagnostic_t diagnostic_kind,
3270 pretty_printer &pp,
3271 diagnostic_source_effect_info *effects)
3273 const location_t loc = richloc.get_loc ();
3274 /* Do nothing if source-printing has been disabled. */
3275 if (!opts.enabled)
3276 return;
3278 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
3279 if (loc <= BUILTINS_LOCATION)
3280 return;
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)
3288 return;
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)
3310 : m_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. */
3324 void
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)
3329 const
3331 layout layout (*this, richloc, effects);
3332 layout_printer lp (pp, layout, richloc, diagnostic_kind);
3333 lp.print (*this);
3336 void
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 ();
3346 line_span_idx++)
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
3352 "jumps". */
3353 if (line_span_idx > 0)
3354 print_gap_in_line_numbering ();
3356 else
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++)
3373 print_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;
3382 #if CHECKING_P
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),
3393 m_ltt (case_),
3394 m_fc ()
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. */
3412 static void
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";
3423 /* U+10FFFF. */
3424 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
3426 /* No escaping. */
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),
3446 policy),
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),
3458 policy),
3459 16);
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(). */
3471 static void
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,
3485 caret_byte_col));
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. */
3494 static void
3495 test_layout_x_offset_display_utf8 (const line_table_case &case_)
3498 const char *content
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 "
3502 "column #102.\n";
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)
3523 return;
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 (),
3532 def_policy ()));
3533 ASSERT_EQ (line_display_cols,
3534 location_compute_display_column (f.m_fc,
3535 expand_location (line_end),
3536 def_policy ()));
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,
3573 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,
3601 emoji_col));
3602 layout test_layout (policy, richloc, nullptr);
3603 layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3604 lp.print (policy);
3605 ASSERT_STREQ (" | 1 \n"
3606 " | 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 "
3610 "column #102.\n"
3611 " | ^\n",
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,
3628 emoji_col + 2));
3629 layout test_layout (dc, richloc, nullptr);
3630 layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3631 lp.print (policy);
3632 ASSERT_STREQ (" | 1 1 \n"
3633 " | 1 2 \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 "
3637 "column #102.\n"
3638 " | ^\n",
3639 pp_formatted_text (dc.get_reference_printer ()));
3644 static void
3645 test_layout_x_offset_display_tab (const line_table_case &case_)
3647 const char *content
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);
3675 file_cache fc;
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)
3684 return;
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 (),
3694 policy));
3695 ASSERT_EQ (line_bytes + extra_width[tabstop],
3696 location_compute_display_column (fc,
3697 expand_location (line_end),
3698 policy));
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,
3704 tab_col + 1));
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);
3712 lp.print (policy);
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
3724 over. */
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);
3737 lp.print (policy);
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. */
3743 const char *output1
3744 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3745 "display columns, starting at column #103.\n"
3746 " | ^\n";
3747 const char *output2
3748 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3749 "display columns, starting at column #103.\n"
3750 " | ^\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. */
3759 static void
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
3768 single-line cases.
3770 All of these work on the following 1-line source file:
3771 .0000000001111111
3772 .1234567890123456
3773 "foo = bar.field;\n"
3774 which is set up by test_diagnostic_show_locus_one_liner and calls
3775 them. */
3777 /* Just a caret. */
3779 static void
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"
3786 " ^\n",
3787 dc.test_show_locus (richloc));
3790 /* No column information (column == 0).
3791 No annotation line should be printed. */
3793 static void
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. */
3805 static void
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"
3815 " ~~~^~~~~~\n",
3816 dc.test_show_locus (richloc));
3819 /* Multiple ranges and carets. */
3821 static void
3822 test_one_liner_multiple_carets_and_ranges ()
3824 test_diagnostic_context dc;
3825 location_t foo
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';
3831 location_t bar
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';
3837 location_t field
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"
3847 " ~A~ ~B~ ~~C~~\n",
3848 dc.test_show_locus (richloc));
3851 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3853 static void
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"
3861 " ^\n"
3862 " &\n",
3863 dc.test_show_locus (richloc));
3866 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3868 static void
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"
3878 " ^~~\n"
3879 " [0]\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
3885 fix-it hints. */
3887 static void
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 ();
3896 /* Normal. */
3898 test_diagnostic_context dc;
3899 ASSERT_STREQ (" foo = bar.field;\n"
3900 " ^~~~~~\n"
3901 " ------\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"
3925 " ^~~~~~\n"
3926 " ------\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". */
3964 static void
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"
3974 " ^~~~~\n"
3975 " m_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. */
3982 static void
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);
3990 source_range range;
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"
3997 " ^\n"
3998 " -----\n"
3999 " m_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". */
4007 static void
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"
4021 " ^ ~~~~~\n"
4022 " m_field\n",
4023 dc.test_show_locus (richloc));
4026 /* Verify that we can use ad-hoc locations when adding fixits to a
4027 rich_location. */
4029 static void
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)
4040 return;
4042 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4044 /* Insert. */
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"
4053 " ^~~~~~~~~~ "
4064 " \n"
4065 " test\n",
4066 dc.test_show_locus (richloc));
4069 /* Remove. */
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"
4079 " ^~~~~~~~~~ "
4090 " \n"
4091 " -----------------------------------------"
4092 "----------"
4093 "----------"
4094 "----------"
4095 "----------"
4096 "----------"
4097 "----------"
4098 "----------"
4099 "----------"
4100 "----------"
4101 "----------"
4102 "----------\n",
4103 dc.test_show_locus (richloc));
4106 /* Replace. */
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"
4116 " ^~~~~~~~~~ "
4127 " \n"
4128 " test\n",
4129 dc.test_show_locus (richloc));
4133 /* Test of consolidating insertions at the same location. */
4135 static void
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"
4145 " ^\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. */
4153 static void
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"
4166 " ^\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. */
4173 static void
4174 test_one_liner_labels ()
4176 location_t foo
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));
4180 location_t bar
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));
4184 location_t field
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"
4201 " ^~~ ~~~ ~~~~~\n"
4202 " | | |\n"
4203 " 0 1 2\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"
4212 " ^~~ ~~~ ~~~~~\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"
4228 " ^~~ ~~~ ~~~~~\n"
4229 " | | |\n"
4230 " | | label 2\n"
4231 " | label 1\n"
4232 " label 0\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"
4248 " ^~~ ~~~ ~~~~~\n"
4249 " | | |\n"
4250 " | | c\n"
4251 " aaaaa bbbb\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"
4266 " ~~~ ~~~ ^~~~~\n"
4267 " | | |\n"
4268 " 2 1 0\n",
4269 dc.test_show_locus (richloc));
4272 /* Ensure we don't ICE if multiple ranges with labels are on
4273 the same point. */
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"
4284 " ^~~\n"
4285 " |\n"
4286 " label 0\n"
4287 " label 1\n"
4288 " label 2\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"
4318 " ~~~ ~~~ ^~~~~\n"
4319 " | | |\n"
4320 " | | label 0a\n"
4321 " | | label 0b\n"
4322 " | | label 0c\n"
4323 " | label 1a\n"
4324 " | label 1b\n"
4325 " | label 1c\n"
4326 " label 2a\n"
4327 " label 2b\n"
4328 " label 2c\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"
4340 " ^~~\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. */
4350 static void
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)
4364 return;
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. */
4398 /* Just a caret. */
4400 static void
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"
4407 "_foo = \xcf\x80"
4408 "_bar.\xf0\x9f\x98\x82"
4409 "_field\xcf\x80"
4410 ";\n"
4411 " ^\n",
4412 dc.test_show_locus (richloc));
4415 /* Caret and range. */
4416 static void
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"
4426 "_foo = \xcf\x80"
4427 "_bar.\xf0\x9f\x98\x82"
4428 "_field\xcf\x80"
4429 ";\n"
4430 " ~~~~~^~~~~~~~~~\n",
4431 dc.test_show_locus (richloc));
4434 /* Multiple ranges and carets. */
4436 static void
4437 test_one_liner_multiple_carets_and_ranges_utf8 ()
4439 test_diagnostic_context dc;
4440 location_t foo
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';
4446 location_t bar
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';
4452 location_t field
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"
4461 "_foo = \xcf\x80"
4462 "_bar.\xf0\x9f\x98\x82"
4463 "_field\xcf\x80"
4464 ";\n"
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". */
4471 static void
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"
4479 "_foo = \xcf\x80"
4480 "_bar.\xf0\x9f\x98\x82"
4481 "_field\xcf\x80"
4482 ";\n"
4483 " ^\n"
4484 " &\n",
4485 dc.test_show_locus (richloc));
4488 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
4490 static void
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"
4500 "_foo = \xcf\x80"
4501 "_bar.\xf0\x9f\x98\x82"
4502 "_field\xcf\x80"
4503 ";\n"
4504 " ^~~~~~\n"
4505 " [0]\n",
4506 dc.test_show_locus (richloc));
4509 /* Removal fix-it hint: removal of the ".SS_fieldP". */
4511 static void
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"
4521 "_foo = \xcf\x80"
4522 "_bar.\xf0\x9f\x98\x82"
4523 "_field\xcf\x80"
4524 ";\n"
4525 " ^~~~~~~~~~\n"
4526 " ----------\n",
4527 dc.test_show_locus (richloc));
4530 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4532 static void
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"
4542 "_foo = \xcf\x80"
4543 "_bar.\xf0\x9f\x98\x82"
4544 "_field\xcf\x80"
4545 ";\n"
4546 " ^~~~~~~~~\n"
4547 " m_\xf0\x9f\x98\x82"
4548 "_field\xcf\x80\n",
4549 dc.test_show_locus (richloc));
4552 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4553 but where the caret was elsewhere. */
4555 static void
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);
4563 source_range range;
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"
4570 "_foo = \xcf\x80"
4571 "_bar.\xf0\x9f\x98\x82"
4572 "_field\xcf\x80"
4573 ";\n"
4574 " ^\n"
4575 " ---------\n"
4576 " m_\xf0\x9f\x98\x82"
4577 "_field\xcf\x80\n",
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". */
4585 static void
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"
4599 "_foo = \xcf\x80"
4600 "_bar.\xf0\x9f\x98\x82"
4601 "_field\xcf\x80"
4602 ";\n"
4603 " ^ ~~~~~~~~~\n"
4604 " m_\xf0\x9f\x98\x82"
4605 "_field\xcf\x80\n",
4606 dc.test_show_locus (richloc));
4609 /* Verify that we can use ad-hoc locations when adding fixits to a
4610 rich_location. */
4612 static void
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)
4623 return;
4625 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4627 /* Insert. */
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"
4636 "_foo = \xcf\x80"
4637 "_bar.\xf0\x9f\x98\x82"
4638 "_field\xcf\x80"
4639 ";\n"
4640 " ^~~~~~~~~~~~~~~~ "
4651 " \n"
4652 " test\n",
4653 dc.test_show_locus (richloc));
4656 /* Remove. */
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"
4666 "_foo = \xcf\x80"
4667 "_bar.\xf0\x9f\x98\x82"
4668 "_field\xcf\x80"
4669 ";\n"
4670 " ^~~~~~~~~~~~~~~~ "
4681 " \n"
4682 " -------------------------------------"
4683 "----------"
4684 "----------"
4685 "----------"
4686 "----------"
4687 "----------"
4688 "----------"
4689 "----------"
4690 "----------"
4691 "----------"
4692 "----------"
4693 "----------\n",
4694 dc.test_show_locus (richloc));
4697 /* Replace. */
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"
4707 "_foo = \xcf\x80"
4708 "_bar.\xf0\x9f\x98\x82"
4709 "_field\xcf\x80"
4710 ";\n"
4711 " ^~~~~~~~~~~~~~~~ "
4722 " \n"
4723 " test\n",
4724 dc.test_show_locus (richloc));
4728 /* Test of consolidating insertions at the same location. */
4730 static void
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"
4740 "_foo = \xcf\x80"
4741 "_bar.\xf0\x9f\x98\x82"
4742 "_field\xcf\x80"
4743 ";\n"
4744 " ^\n"
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. */
4753 static void
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"
4770 "_foo = \xcf\x80"
4771 "_bar.\xf0\x9f\x98\x82"
4772 "_field\xcf\x80"
4773 ";\n"
4774 " ^\n"
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. */
4782 static void
4783 test_one_liner_labels_utf8 ()
4785 location_t foo
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));
4789 location_t bar
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));
4793 location_t field
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"
4811 "\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"
4819 "_foo = \xcf\x80"
4820 "_bar.\xf0\x9f\x98\x82"
4821 "_field\xcf\x80"
4822 ";\n"
4823 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4824 " | | |\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"
4845 "_foo = \xcf\x80"
4846 "_bar.\xf0\x9f\x98\x82"
4847 "_field\xcf\x80"
4848 ";\n"
4849 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4850 " | | |\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"
4869 "_foo = \xcf\x80"
4870 "_bar.\xf0\x9f\x98\x82"
4871 "_field\xcf\x80"
4872 ";\n"
4873 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4874 " | | |\n"
4875 " | | c\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"
4896 " | | |\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);
4905 ASSERT_STREQ
4906 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4907 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4908 " | | |\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. */
4919 static void
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
4938 /* Display columns.
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
4944 Byte columns. */
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. */
4954 static void
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)
4963 return;
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 (),
4971 def_policy ()));
4972 ASSERT_EQ (25, location_compute_display_column (f.m_fc,
4973 expand_location (line_end),
4974 def_policy ()));
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. */
4994 static void
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. */
5000 const char *content
5001 = ("struct same_line { double x; double y; ;\n" /* line 1. */
5002 "struct different_line\n" /* line 2. */
5003 "{\n" /* line 3. */
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)
5021 return;
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"
5037 " ~ ^\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. */
5060 static void
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. */
5066 const char *content
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. */
5070 "\n" /* line 4. */
5071 "\n" /* line 5. */
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)
5087 return;
5089 /* A pair of tests for modernizing the initializers to C99-style. */
5091 /* The one-liner case (line 2). */
5093 test_diagnostic_context dc;
5094 const location_t x
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"
5102 " ^\n"
5103 " .=\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;
5113 const location_t y
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"
5121 " y\n"
5122 " .\n"
5123 "FILENAME:6:25:\n"
5124 " : 0.0};\n"
5125 " ^\n"
5126 " =\n",
5127 dc.test_show_locus (richloc));
5130 /* As above, but verify the behavior of multiple line spans
5131 with line-numbering enabled. */
5133 const location_t y
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"
5143 " | .\n"
5144 "......\n"
5145 " 6 | : 0.0};\n"
5146 " | ^\n"
5147 " | =\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. */
5162 static void
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 ());
5186 else
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),
5196 "bar");
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 ());
5201 else
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),
5210 "bar");
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 ());
5216 else
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),
5225 "foo");
5226 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
5227 "bar");
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 ());
5232 else
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),
5241 "foo");
5242 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
5243 "bar");
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 ());
5248 else
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),
5263 "foo");
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 ());
5269 else
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 ());
5290 else
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. */
5305 static void
5306 test_overlapped_fixit_printing (const line_table_case &case_)
5308 /* Create a tempfile and write some text to it.
5309 ...000000000111111111122222222223333333333.
5310 ...123456789012345678901234567890123456789. */
5311 const char *content
5312 = (" foo *f = (foo *)ptr->field;\n");
5313 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
5314 file_cache fc;
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)
5328 return;
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"
5353 " ^~~~~~~~~~\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
5382 as expected. */
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"
5422 " ^~~~~~~~~~\n"
5423 " -\n"
5424 " CAST (-\n"
5425 " ) ( )\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"
5438 " ^~~~~~~~~~\n"
5439 " -\n"
5440 " CST ( -\n"
5441 " ) ( )\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"
5458 " ^~~~~~~~~~\n"
5459 " -------\n"
5460 " (bar *)\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"
5477 " ^~~~~~~~~~\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"
5494 " ^~~~~~~~~~\n"
5495 " -------\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
5503 characters here. */
5505 static void
5506 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
5508 /* Create a tempfile and write some text to it. */
5510 const char *content
5511 /* Display columns.
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
5517 Byte columns. */
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)
5533 return;
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"
5561 ";\n"
5562 " ^~~~~~~~~~~\n"
5563 " ------------------\n"
5564 " const_cast<f\xf0\x9f\x98\x82"
5565 " *> (ptr->field\xcf\x80"
5566 ")\n",
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
5593 as expected. */
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"
5636 ";\n"
5637 " ^~~~~~~~~~~\n"
5638 " -\n"
5639 " CAST (-\n"
5640 " ) ( )\n",
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"
5655 ";\n"
5656 " ^~~~~~~~~~~\n"
5657 " -\n"
5658 " CST ( -\n"
5659 " ) ( )\n",
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"
5678 ";\n"
5679 " ^~~~~~~~~~~\n"
5680 " -------\n"
5681 " (bar\xf0\x9f\x98\x82"
5682 " *)\n",
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"
5701 ";\n"
5702 " ^~~~~~~~~~~\n"
5703 " ------------------\n"
5704 " (long\xf0\x9f\x98\x82"
5705 " *)(f\xf0\x9f\x98\x82"
5706 " *)\n",
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"
5724 ";\n"
5725 " ^~~~~~~~~~~\n"
5726 " -------\n"
5727 " L\xf0\x9f\x98\x82"
5728 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5729 " *)TEST\n",
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
5736 order.
5737 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5739 static void
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. */
5745 const char *content
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)
5761 return;
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"
5797 " ^\n"
5798 " } {\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"
5819 " ^\n"
5820 " { -----\n"
5821 " {{1}}}}, {{{2 }}\n",
5822 dc.test_show_locus (richloc));
5826 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5828 static void
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)
5849 return;
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"
5861 "+ break;\n"
5862 " case 'b':\n"
5863 " ^~~~~~~~~\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"
5872 " +++ |+ break;\n"
5873 " 3 | case 'b':\n"
5874 " | ^~~~~~~~~\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"
5887 " ^~~~~~~~~\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. */
5896 static void
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. */
5903 "{\n" /* line 2. */
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)
5929 return;
5932 test_diagnostic_context dc;
5933 ASSERT_STREQ ("FILENAME:1:1:\n"
5934 "+#include <stdio.h>\n"
5935 " test (int ch)\n"
5936 "FILENAME:3:2:\n"
5937 " putchar (ch);\n"
5938 " ^~~~~~~\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"
5949 " 2 | {\n"
5950 " 3 | putchar (ch);\n"
5951 " | ^~~~~~~\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. */
5960 static void
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)
5986 return;
5988 test_diagnostic_context dc;
5989 ASSERT_STREQ (" foo = bar ();\n"
5990 " ^\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. */
5998 static void
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"
6005 " );\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...)". */
6014 location_t start
6015 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
6016 location_t caret
6017 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
6018 location_t finish
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)
6029 return;
6031 test_diagnostic_context dc;
6032 ASSERT_STREQ (" foo = bar (\n"
6033 " ~^\n"
6034 " );\n"
6035 " ~ \n",
6036 dc.test_show_locus (richloc));
6039 static void
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)
6065 return;
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
6069 everything too. */
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"
6077 " ^\n",
6078 dc.test_show_locus (richloc));
6081 /* Confirm the display width was tracked correctly across the internal tab
6082 as well. */
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"
6090 " ^\n",
6091 dc.test_show_locus (richloc));
6095 /* Verify that the escaping machinery can cope with a variety of different
6096 invalid bytes. */
6098 static void
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);
6109 location_t finish
6110 = linemap_position_for_line_and_column (line_table, ord_map, 1,
6111 strlen (content));
6113 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
6114 return;
6116 /* Locations of the NUL and \v bytes. */
6117 location_t nul_loc
6118 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
6119 location_t v_loc
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"
6127 " ^ ~\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);
6134 ASSERT_STREQ
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"
6143 " ^~~~ ~~~~\n",
6144 dc.test_show_locus (richloc));
6148 /* As above, but verify that we handle the initial byte of a line
6149 correctly. */
6151 static void
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);
6162 location_t finish
6163 = linemap_position_for_line_and_column (line_table, ord_map, 1,
6164 strlen (content));
6166 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
6167 return;
6169 /* Location of the NUL byte. */
6170 location_t nul_loc
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"
6177 " ^\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"
6185 " ^~~~~~~~\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"
6192 " ^~~~\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"). */
6201 static void
6202 test_line_numbers_multiline_range ()
6204 /* Create a tempfile and write some text to it. */
6205 pretty_printer pp;
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. */
6220 location_t start
6221 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
6222 location_t caret
6223 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
6224 location_t finish
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"
6233 " | ~~~~~~\n"
6234 "10 | this is line 10\n"
6235 " | ~~~~~^~~~~~~~~~\n"
6236 "11 | this is line 11\n"
6237 " | ~~~~ \n",
6238 dc.test_show_locus (richloc));
6241 /* Run all of the selftests within this file. */
6243 void
6244 diagnostic_show_locus_cc_tests ()
6246 test_line_span ();
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 */
6284 #if __GNUC__ >= 10
6285 # pragma GCC diagnostic pop
6286 #endif