hppa: Revise REG+D address support to allow long displacements before reload
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob563d2826f249fe6ce1a1028c328dd9cd41a5ada9
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2023 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 #include "cpplib.h"
35 #ifdef HAVE_TERMIOS_H
36 # include <termios.h>
37 #endif
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
41 #endif
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
45 #if __GNUC__ >= 10
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
48 #endif
50 /* Classes for rendering source code and diagnostics, within an
51 anonymous namespace.
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
55 namespace {
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
59 this point. */
61 struct point_state
63 int range_idx;
64 bool draw_caret_p;
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
70 - normal text
71 - range 0 (the "primary location")
72 - range 1
73 - range 2
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
83 class colorizer
85 public:
86 colorizer (pretty_printer *pp,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
90 void set_range (int range_idx)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
97 set_state (0);
98 else
99 set_state (range_idx);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
105 private:
106 void set_state (int state);
107 void begin_state (int state);
108 void finish_state (int state);
109 const char *get_color_by_name (const char *);
111 private:
112 static const int STATE_NORMAL_TEXT = -1;
113 static const int STATE_FIXIT_INSERT = -2;
114 static const int STATE_FIXIT_DELETE = -3;
116 pretty_printer *m_pp;
117 diagnostic_t m_diagnostic_kind;
118 int m_current_state;
119 const char *m_range1;
120 const char *m_range2;
121 const char *m_fixit_insert;
122 const char *m_fixit_delete;
123 const char *m_stop_color;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
162 enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col : public expanded_location
177 public:
178 exploc_with_display_col (file_cache &fc,
179 const expanded_location &exploc,
180 const cpp_char_column_policy &policy,
181 enum location_aspect aspect)
182 : expanded_location (exploc),
183 m_display_col (location_compute_display_column (fc, exploc, policy))
185 if (exploc.column > 0)
187 /* m_display_col is now the final column of the byte.
188 If escaping has happened, we may want the first column instead. */
189 if (aspect != LOCATION_ASPECT_FINISH)
191 expanded_location prev_exploc (exploc);
192 prev_exploc.column--;
193 int prev_display_col
194 = (location_compute_display_column (fc, prev_exploc, policy));
195 m_display_col = prev_display_col + 1;
200 int m_display_col;
204 /* A point within a layout_range; similar to an exploc_with_display_col,
205 but after filtering on file. */
207 class layout_point
209 public:
210 layout_point (const exploc_with_display_col &exploc)
211 : m_line (exploc.line)
213 m_columns[CU_BYTES] = exploc.column;
214 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
217 linenum_type m_line;
218 int m_columns[CU_NUM_UNITS];
221 /* A class for use by "class layout" below: a filtered location_range. */
223 class layout_range
225 public:
226 layout_range (const exploc_with_display_col &start_exploc,
227 const exploc_with_display_col &finish_exploc,
228 enum range_display_kind range_display_kind,
229 const exploc_with_display_col &caret_exploc,
230 unsigned original_idx,
231 const range_label *label);
233 bool contains_point (linenum_type row, int column,
234 enum column_unit col_unit) const;
235 bool intersects_line_p (linenum_type row) const;
237 layout_point m_start;
238 layout_point m_finish;
239 enum range_display_kind m_range_display_kind;
240 layout_point m_caret;
241 unsigned m_original_idx;
242 const range_label *m_label;
245 /* A struct for use by layout::print_source_line for telling
246 layout::print_annotation_line the extents of the source line that
247 it printed, so that underlines can be clipped appropriately. Units
248 are 1-based display columns. */
250 struct line_bounds
252 int m_first_non_ws_disp_col;
253 int m_last_non_ws_disp_col;
255 line_bounds ()
257 m_first_non_ws_disp_col = INT_MAX;
258 m_last_non_ws_disp_col = 0;
262 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
263 or "line 23"). During the layout ctor, layout::calculate_line_spans
264 splits the pertinent source lines into a list of disjoint line_span
265 instances (e.g. lines 5-10, lines 15-20, line 23). */
267 class line_span
269 public:
270 line_span (linenum_type first_line, linenum_type last_line)
271 : m_first_line (first_line), m_last_line (last_line)
273 gcc_assert (first_line <= last_line);
275 linenum_type get_first_line () const { return m_first_line; }
276 linenum_type get_last_line () const { return m_last_line; }
278 bool contains_line_p (linenum_type line) const
280 return line >= m_first_line && line <= m_last_line;
283 static int comparator (const void *p1, const void *p2)
285 const line_span *ls1 = (const line_span *)p1;
286 const line_span *ls2 = (const line_span *)p2;
287 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
288 if (first_line_cmp)
289 return first_line_cmp;
290 return compare (ls1->m_last_line, ls2->m_last_line);
293 linenum_type m_first_line;
294 linenum_type m_last_line;
297 #if CHECKING_P
299 /* Selftests for line_span. */
301 static void
302 test_line_span ()
304 line_span line_one (1, 1);
305 ASSERT_EQ (1, line_one.get_first_line ());
306 ASSERT_EQ (1, line_one.get_last_line ());
307 ASSERT_FALSE (line_one.contains_line_p (0));
308 ASSERT_TRUE (line_one.contains_line_p (1));
309 ASSERT_FALSE (line_one.contains_line_p (2));
311 line_span lines_1_to_3 (1, 3);
312 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
313 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
315 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
317 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
318 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
319 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
321 /* A linenum > 2^31. */
322 const linenum_type LARGEST_LINE = 0xffffffff;
323 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
325 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
327 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
328 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
331 #endif /* #if CHECKING_P */
333 /* A bundle of information containing how to print unicode
334 characters and bytes when quoting source code.
336 Provides a unified place to support escaping some subset
337 of characters to some format.
339 Extends char_column_policy; printing is split out to avoid
340 libcpp having to know about pretty_printer. */
342 struct char_display_policy : public cpp_char_column_policy
344 public:
345 char_display_policy (int tabstop,
346 int (*width_cb) (cppchar_t c),
347 void (*print_cb) (pretty_printer *pp,
348 const cpp_decoded_char &cp))
349 : cpp_char_column_policy (tabstop, width_cb),
350 m_print_cb (print_cb)
354 void (*m_print_cb) (pretty_printer *pp,
355 const cpp_decoded_char &cp);
358 /* A class to control the overall layout when printing a diagnostic.
360 The layout is determined within the constructor.
361 It is then printed by repeatedly calling the "print_source_line",
362 "print_annotation_line" and "print_any_fixits" methods.
364 We assume we have disjoint ranges. */
366 class layout
368 public:
369 layout (const diagnostic_context &context,
370 const rich_location &richloc,
371 diagnostic_t diagnostic_kind,
372 pretty_printer *pp);
374 bool maybe_add_location_range (const location_range *loc_range,
375 unsigned original_idx,
376 bool restrict_to_current_line_spans);
378 int get_num_line_spans () const { return m_line_spans.length (); }
379 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
381 int get_linenum_width () const { return m_linenum_width; }
382 int get_x_offset_display () const { return m_x_offset_display; }
384 void print_gap_in_line_numbering ();
385 bool print_heading_for_line_span_index_p (int line_span_idx) const;
387 expanded_location get_expanded_location (const line_span *) const;
389 void print_line (linenum_type row);
391 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
393 private:
394 bool will_show_line_p (linenum_type row) const;
395 void print_leading_fixits (linenum_type row);
396 line_bounds print_source_line (linenum_type row, const char *line,
397 int line_bytes);
398 bool should_print_annotation_line_p (linenum_type row) const;
399 void start_annotation_line (char margin_char = ' ') const;
400 void print_annotation_line (linenum_type row, const line_bounds lbounds);
401 void print_any_labels (linenum_type row);
402 void print_trailing_fixits (linenum_type row);
404 bool annotation_line_showed_range_p (linenum_type line, int start_column,
405 int finish_column) const;
406 void show_ruler (int max_column) const;
408 bool validate_fixit_hint_p (const fixit_hint *hint);
410 void calculate_line_spans ();
411 void calculate_linenum_width ();
412 void calculate_x_offset_display ();
414 void print_newline ();
416 bool
417 get_state_at_point (/* Inputs. */
418 linenum_type row, int column,
419 int first_non_ws, int last_non_ws,
420 enum column_unit col_unit,
421 /* Outputs. */
422 point_state *out_state);
425 get_x_bound_for_row (linenum_type row, int caret_column,
426 int last_non_ws);
428 void
429 move_to_column (int *column, int dest_column, bool add_left_margin);
431 private:
432 bool compatible_locations_p (location_t loc_a, location_t loc_b) const;
434 const diagnostic_source_printing_options &m_options;
435 const line_maps *m_line_table;
436 file_cache &m_file_cache;
437 pretty_printer *m_pp;
438 char_display_policy m_policy;
439 location_t m_primary_loc;
440 exploc_with_display_col m_exploc;
441 colorizer m_colorizer;
442 bool m_diagnostic_path_p;
443 auto_vec <layout_range> m_layout_ranges;
444 auto_vec <const fixit_hint *> m_fixit_hints;
445 auto_vec <line_span> m_line_spans;
446 int m_linenum_width;
447 int m_x_offset_display;
448 bool m_escape_on_output;
451 /* Implementation of "class colorizer". */
453 /* The constructor for "colorizer". Lookup and store color codes for the
454 different kinds of things we might need to print. */
456 colorizer::colorizer (pretty_printer *pp,
457 diagnostic_t diagnostic_kind) :
458 m_pp (pp),
459 m_diagnostic_kind (diagnostic_kind),
460 m_current_state (STATE_NORMAL_TEXT)
462 m_range1 = get_color_by_name ("range1");
463 m_range2 = get_color_by_name ("range2");
464 m_fixit_insert = get_color_by_name ("fixit-insert");
465 m_fixit_delete = get_color_by_name ("fixit-delete");
466 m_stop_color = colorize_stop (pp_show_color (m_pp));
469 /* The destructor for "colorize". If colorization is on, print a code to
470 turn it off. */
472 colorizer::~colorizer ()
474 finish_state (m_current_state);
477 /* Update state, printing color codes if necessary if there's a state
478 change. */
480 void
481 colorizer::set_state (int new_state)
483 if (m_current_state != new_state)
485 finish_state (m_current_state);
486 m_current_state = new_state;
487 begin_state (new_state);
491 /* Turn on any colorization for STATE. */
493 void
494 colorizer::begin_state (int state)
496 switch (state)
498 case STATE_NORMAL_TEXT:
499 break;
501 case STATE_FIXIT_INSERT:
502 pp_string (m_pp, m_fixit_insert);
503 break;
505 case STATE_FIXIT_DELETE:
506 pp_string (m_pp, m_fixit_delete);
507 break;
509 case 0:
510 /* Make range 0 be the same color as the "kind" text
511 (error vs warning vs note). */
512 pp_string
513 (m_pp,
514 colorize_start (pp_show_color (m_pp),
515 diagnostic_get_color_for_kind (m_diagnostic_kind)));
516 break;
518 case 1:
519 pp_string (m_pp, m_range1);
520 break;
522 case 2:
523 pp_string (m_pp, m_range2);
524 break;
526 default:
527 /* For ranges beyond 2, alternate between color 1 and color 2. */
529 gcc_assert (state > 2);
530 pp_string (m_pp,
531 state % 2 ? m_range1 : m_range2);
533 break;
537 /* Turn off any colorization for STATE. */
539 void
540 colorizer::finish_state (int state)
542 if (state != STATE_NORMAL_TEXT)
543 pp_string (m_pp, m_stop_color);
546 /* Get the color code for NAME (or the empty string if
547 colorization is disabled). */
549 const char *
550 colorizer::get_color_by_name (const char *name)
552 return colorize_start (pp_show_color (m_pp), name);
555 /* Implementation of class layout_range. */
557 /* The constructor for class layout_range.
558 Initialize various layout_point fields from expanded_location
559 equivalents; we've already filtered on file. */
561 layout_range::layout_range (const exploc_with_display_col &start_exploc,
562 const exploc_with_display_col &finish_exploc,
563 enum range_display_kind range_display_kind,
564 const exploc_with_display_col &caret_exploc,
565 unsigned original_idx,
566 const range_label *label)
567 : m_start (start_exploc),
568 m_finish (finish_exploc),
569 m_range_display_kind (range_display_kind),
570 m_caret (caret_exploc),
571 m_original_idx (original_idx),
572 m_label (label)
576 /* Is (column, row) within the given range?
577 We've already filtered on the file.
579 Ranges are closed (both limits are within the range).
581 Example A: a single-line range:
582 start: (col=22, line=2)
583 finish: (col=38, line=2)
585 |00000011111111112222222222333333333344444444444
586 |34567890123456789012345678901234567890123456789
587 --+-----------------------------------------------
588 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
589 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
590 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
592 Example B: a multiline range with
593 start: (col=14, line=3)
594 finish: (col=08, line=5)
596 |00000011111111112222222222333333333344444444444
597 |34567890123456789012345678901234567890123456789
598 --+-----------------------------------------------
599 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
600 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
601 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
602 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
603 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
604 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
605 --+-----------------------------------------------
607 Legend:
608 - 'b' indicates a point *before* the range
609 - 'S' indicates the start of the range
610 - 'w' indicates a point within the range
611 - 'F' indicates the finish of the range (which is
612 within it).
613 - 'a' indicates a subsequent point *after* the range.
615 COL_UNIT controls whether we check the byte column or
616 the display column; one or the other is more convenient
617 depending on the context. */
619 bool
620 layout_range::contains_point (linenum_type row, int column,
621 enum column_unit col_unit) const
623 gcc_assert (m_start.m_line <= m_finish.m_line);
624 /* ...but the equivalent isn't true for the columns;
625 consider example B in the comment above. */
627 if (row < m_start.m_line)
628 /* Points before the first line of the range are
629 outside it (corresponding to line 01 in example A
630 and lines 01 and 02 in example B above). */
631 return false;
633 if (row == m_start.m_line)
634 /* On same line as start of range (corresponding
635 to line 02 in example A and line 03 in example B). */
637 if (column < m_start.m_columns[col_unit])
638 /* Points on the starting line of the range, but
639 before the column in which it begins. */
640 return false;
642 if (row < m_finish.m_line)
643 /* This is a multiline range; the point
644 is within it (corresponds to line 03 in example B
645 from column 14 onwards) */
646 return true;
647 else
649 /* This is a single-line range. */
650 gcc_assert (row == m_finish.m_line);
651 return column <= m_finish.m_columns[col_unit];
655 /* The point is in a line beyond that containing the
656 start of the range: lines 03 onwards in example A,
657 and lines 04 onwards in example B. */
658 gcc_assert (row > m_start.m_line);
660 if (row > m_finish.m_line)
661 /* The point is beyond the final line of the range
662 (lines 03 onwards in example A, and lines 06 onwards
663 in example B). */
664 return false;
666 if (row < m_finish.m_line)
668 /* The point is in a line that's fully within a multiline
669 range (e.g. line 04 in example B). */
670 gcc_assert (m_start.m_line < m_finish.m_line);
671 return true;
674 gcc_assert (row == m_finish.m_line);
676 return column <= m_finish.m_columns[col_unit];
679 /* Does this layout_range contain any part of line ROW? */
681 bool
682 layout_range::intersects_line_p (linenum_type row) const
684 gcc_assert (m_start.m_line <= m_finish.m_line);
685 if (row < m_start.m_line)
686 return false;
687 if (row > m_finish.m_line)
688 return false;
689 return true;
692 #if CHECKING_P
694 /* Default for when we don't care what the tab expansion is set to. */
695 static const int def_tabstop = 8;
697 static cpp_char_column_policy def_policy ()
699 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
702 /* Create some expanded locations for testing layout_range. The filename
703 member of the explocs is set to the empty string. This member will only be
704 inspected by the calls to location_compute_display_column() made from the
705 layout_point constructors. That function will check for an empty filename
706 argument and not attempt to open it, rather treating the non-existent data
707 as if the display width were the same as the byte count. Tests exercising a
708 real difference between byte count and display width are performed later,
709 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
711 static layout_range
712 make_range (file_cache &fc,
713 int start_line, int start_col, int end_line, int end_col)
715 const expanded_location start_exploc
716 = {"", start_line, start_col, NULL, false};
717 const expanded_location finish_exploc
718 = {"", end_line, end_col, NULL, false};
719 return layout_range (exploc_with_display_col (fc,
720 start_exploc, def_policy (),
721 LOCATION_ASPECT_START),
722 exploc_with_display_col (fc,
723 finish_exploc, def_policy (),
724 LOCATION_ASPECT_FINISH),
725 SHOW_RANGE_WITHOUT_CARET,
726 exploc_with_display_col (fc,
727 start_exploc, def_policy (),
728 LOCATION_ASPECT_CARET),
729 0, NULL);
732 /* Selftests for layout_range::contains_point and
733 layout_range::intersects_line_p. */
735 /* Selftest for layout_range, where the layout_range
736 is a range with start==end i.e. a single point. */
738 static void
739 test_layout_range_for_single_point ()
741 file_cache fc;
742 layout_range point = make_range (fc, 7, 10, 7, 10);
744 /* Tests for layout_range::contains_point. */
746 for (int i = 0; i != CU_NUM_UNITS; ++i)
748 const enum column_unit col_unit = (enum column_unit) i;
750 /* Before the line. */
751 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
753 /* On the line, but before start. */
754 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
756 /* At the point. */
757 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
759 /* On the line, after the point. */
760 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
762 /* After the line. */
763 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
766 /* Tests for layout_range::intersects_line_p. */
767 ASSERT_FALSE (point.intersects_line_p (6));
768 ASSERT_TRUE (point.intersects_line_p (7));
769 ASSERT_FALSE (point.intersects_line_p (8));
772 /* Selftest for layout_range, where the layout_range
773 is the single-line range shown as "Example A" above. */
775 static void
776 test_layout_range_for_single_line ()
778 file_cache fc;
779 layout_range example_a = make_range (fc, 2, 22, 2, 38);
781 /* Tests for layout_range::contains_point. */
783 for (int i = 0; i != CU_NUM_UNITS; ++i)
785 const enum column_unit col_unit = (enum column_unit) i;
787 /* Before the line. */
788 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
790 /* On the line, but before start. */
791 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
793 /* On the line, at the start. */
794 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
796 /* On the line, within the range. */
797 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
799 /* On the line, at the end. */
800 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
802 /* On the line, after the end. */
803 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
805 /* After the line. */
806 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
809 /* Tests for layout_range::intersects_line_p. */
810 ASSERT_FALSE (example_a.intersects_line_p (1));
811 ASSERT_TRUE (example_a.intersects_line_p (2));
812 ASSERT_FALSE (example_a.intersects_line_p (3));
815 /* Selftest for layout_range, where the layout_range
816 is the multi-line range shown as "Example B" above. */
818 static void
819 test_layout_range_for_multiple_lines ()
821 file_cache fc;
822 layout_range example_b = make_range (fc, 3, 14, 5, 8);
824 /* Tests for layout_range::contains_point. */
826 for (int i = 0; i != CU_NUM_UNITS; ++i)
828 const enum column_unit col_unit = (enum column_unit) i;
830 /* Before first line. */
831 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
833 /* On the first line, but before start. */
834 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
836 /* At the start. */
837 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
839 /* On the first line, within the range. */
840 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
842 /* On an interior line.
843 The column number should not matter; try various boundary
844 values. */
845 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
846 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
847 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
848 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
849 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
850 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
851 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
853 /* On the final line, before the end. */
854 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
856 /* On the final line, at the end. */
857 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
859 /* On the final line, after the end. */
860 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
862 /* After the line. */
863 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
866 /* Tests for layout_range::intersects_line_p. */
867 ASSERT_FALSE (example_b.intersects_line_p (2));
868 ASSERT_TRUE (example_b.intersects_line_p (3));
869 ASSERT_TRUE (example_b.intersects_line_p (4));
870 ASSERT_TRUE (example_b.intersects_line_p (5));
871 ASSERT_FALSE (example_b.intersects_line_p (6));
874 #endif /* #if CHECKING_P */
876 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
877 (still in bytes, not display cols) without any trailing whitespace. */
879 static int
880 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
882 int result = line_bytes;
883 while (result > 0)
885 char ch = line[result - 1];
886 if (ch == ' ' || ch == '\t' || ch == '\r')
887 result--;
888 else
889 break;
891 gcc_assert (result >= 0);
892 gcc_assert (result <= line_bytes);
893 gcc_assert (result == 0 ||
894 (line[result - 1] != ' '
895 && line[result -1] != '\t'
896 && line[result -1] != '\r'));
897 return result;
900 #if CHECKING_P
902 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
904 static void
905 assert_eq (const char *line, int expected_bytes)
907 int actual_value
908 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
909 ASSERT_EQ (actual_value, expected_bytes);
912 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
913 various inputs. It is not required to handle newlines. */
915 static void
916 test_get_line_bytes_without_trailing_whitespace ()
918 assert_eq ("", 0);
919 assert_eq (" ", 0);
920 assert_eq ("\t", 0);
921 assert_eq ("\r", 0);
922 assert_eq ("hello world", 11);
923 assert_eq ("hello world ", 11);
924 assert_eq ("hello world \t\t ", 11);
925 assert_eq ("hello world\r", 11);
928 #endif /* #if CHECKING_P */
930 /* Helper function for layout's ctor, for sanitizing locations relative
931 to the primary location within a diagnostic.
933 Compare LOC_A and LOC_B to see if it makes sense to print underlines
934 connecting their expanded locations. Doing so is only guaranteed to
935 make sense if the locations share the same macro expansion "history"
936 i.e. they can be traced through the same macro expansions, eventually
937 reaching an ordinary map.
939 This may be too strong a condition, but it effectively sanitizes
940 PR c++/70105, which has an example of printing an expression where the
941 final location of the expression is in a different macro, which
942 erroneously was leading to hundreds of lines of irrelevant source
943 being printed. */
945 bool
946 layout::compatible_locations_p (location_t loc_a, location_t loc_b) const
948 if (IS_ADHOC_LOC (loc_a))
949 loc_a = get_location_from_adhoc_loc (m_line_table, loc_a);
950 if (IS_ADHOC_LOC (loc_b))
951 loc_b = get_location_from_adhoc_loc (m_line_table, loc_b);
953 /* If either location is one of the special locations outside of a
954 linemap, they are only compatible if they are equal. */
955 if (loc_a < RESERVED_LOCATION_COUNT
956 || loc_b < RESERVED_LOCATION_COUNT)
957 return loc_a == loc_b;
959 const line_map *map_a = linemap_lookup (m_line_table, loc_a);
960 linemap_assert (map_a);
962 const line_map *map_b = linemap_lookup (m_line_table, loc_b);
963 linemap_assert (map_b);
965 /* Are they within the same map? */
966 if (map_a == map_b)
968 /* Are both within the same macro expansion? */
969 if (linemap_macro_expansion_map_p (map_a))
971 /* If so, then they're only compatible if either both are
972 from the macro definition, or both from the macro arguments. */
973 bool loc_a_from_defn
974 = linemap_location_from_macro_definition_p (m_line_table, loc_a);
975 bool loc_b_from_defn
976 = linemap_location_from_macro_definition_p (m_line_table, loc_b);
977 if (loc_a_from_defn != loc_b_from_defn)
978 return false;
980 /* Expand each location towards the spelling location, and
981 recurse. */
982 const line_map_macro *macro_map = linemap_check_macro (map_a);
983 location_t loc_a_toward_spelling
984 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
985 macro_map,
986 loc_a);
987 location_t loc_b_toward_spelling
988 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
989 macro_map,
990 loc_b);
991 return compatible_locations_p (loc_a_toward_spelling,
992 loc_b_toward_spelling);
995 /* Otherwise they are within the same ordinary map. */
996 return true;
998 else
1000 /* Within different maps. */
1002 /* If either is within a macro expansion, they are incompatible. */
1003 if (linemap_macro_expansion_map_p (map_a)
1004 || linemap_macro_expansion_map_p (map_b))
1005 return false;
1007 /* Within two different ordinary maps; they are compatible iff they
1008 are in the same file. */
1009 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1010 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1011 return ord_map_a->to_file == ord_map_b->to_file;
1015 /* Comparator for sorting fix-it hints. */
1017 static int
1018 fixit_cmp (const void *p_a, const void *p_b)
1020 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1021 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1022 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1025 /* Callbacks for use when not escaping the source. */
1027 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1029 /* Callback for char_display_policy::m_print_cb for printing source chars
1030 when not escaping the source. */
1032 static void
1033 default_print_decoded_ch (pretty_printer *pp,
1034 const cpp_decoded_char &decoded_ch)
1036 for (const char *ptr = decoded_ch.m_start_byte;
1037 ptr != decoded_ch.m_next_byte; ptr++)
1039 if (*ptr == '\0' || *ptr == '\r')
1041 pp_space (pp);
1042 continue;
1045 pp_character (pp, *ptr);
1049 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1051 static const int width_per_escaped_byte = 4;
1053 /* Callback for char_column_policy::m_width_cb for determining the
1054 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1056 static int
1057 escape_as_bytes_width (cppchar_t ch)
1059 if (ch < 0x80 && ISPRINT (ch))
1060 return cpp_wcwidth (ch);
1061 else
1063 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1064 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1065 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1066 return 4 * width_per_escaped_byte;
1070 /* Callback for char_display_policy::m_print_cb for printing source chars
1071 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1073 static void
1074 escape_as_bytes_print (pretty_printer *pp,
1075 const cpp_decoded_char &decoded_ch)
1077 if (!decoded_ch.m_valid_ch)
1079 for (const char *iter = decoded_ch.m_start_byte;
1080 iter != decoded_ch.m_next_byte; ++iter)
1082 char buf[16];
1083 sprintf (buf, "<%02x>", (unsigned char)*iter);
1084 pp_string (pp, buf);
1086 return;
1089 cppchar_t ch = decoded_ch.m_ch;
1090 if (ch < 0x80 && ISPRINT (ch))
1091 pp_character (pp, ch);
1092 else
1094 for (const char *iter = decoded_ch.m_start_byte;
1095 iter < decoded_ch.m_next_byte; ++iter)
1097 char buf[16];
1098 sprintf (buf, "<%02x>", (unsigned char)*iter);
1099 pp_string (pp, buf);
1104 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1106 /* Callback for char_column_policy::m_width_cb for determining the
1107 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1109 static int
1110 escape_as_unicode_width (cppchar_t ch)
1112 if (ch < 0x80 && ISPRINT (ch))
1113 return cpp_wcwidth (ch);
1114 else
1116 // Width of "<U+%04x>"
1117 if (ch > 0xfffff)
1118 return 10;
1119 else if (ch > 0xffff)
1120 return 9;
1121 else
1122 return 8;
1126 /* Callback for char_display_policy::m_print_cb for printing source chars
1127 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1129 static void
1130 escape_as_unicode_print (pretty_printer *pp,
1131 const cpp_decoded_char &decoded_ch)
1133 if (!decoded_ch.m_valid_ch)
1135 escape_as_bytes_print (pp, decoded_ch);
1136 return;
1139 cppchar_t ch = decoded_ch.m_ch;
1140 if (ch < 0x80 && ISPRINT (ch))
1141 pp_character (pp, ch);
1142 else
1144 char buf[16];
1145 sprintf (buf, "<U+%04X>", ch);
1146 pp_string (pp, buf);
1150 /* Populate a char_display_policy based on DC and RICHLOC. */
1152 static char_display_policy
1153 make_policy (const diagnostic_context &dc,
1154 const rich_location &richloc)
1156 /* The default is to not escape non-ASCII bytes. */
1157 char_display_policy result
1158 (dc.m_tabstop, cpp_wcwidth, default_print_decoded_ch);
1160 /* If the diagnostic suggests escaping non-ASCII bytes, then
1161 use policy from user-supplied options. */
1162 if (richloc.escape_on_output_p ())
1164 result.m_undecoded_byte_width = width_per_escaped_byte;
1165 switch (dc.get_escape_format ())
1167 default:
1168 gcc_unreachable ();
1169 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1170 result.m_width_cb = escape_as_unicode_width;
1171 result.m_print_cb = escape_as_unicode_print;
1172 break;
1173 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1174 result.m_width_cb = escape_as_bytes_width;
1175 result.m_print_cb = escape_as_bytes_print;
1176 break;
1180 return result;
1183 /* Implementation of class layout. */
1185 /* Constructor for class layout.
1187 Filter the ranges from the rich_location to those that we can
1188 sanely print, populating m_layout_ranges and m_fixit_hints.
1189 Determine the range of lines that we will print, splitting them
1190 up into an ordered list of disjoint spans of contiguous line numbers.
1191 Determine m_x_offset_display, to ensure that the primary caret
1192 will fit within the max_width provided by the diagnostic_context. */
1194 layout::layout (const diagnostic_context &context,
1195 const rich_location &richloc,
1196 diagnostic_t diagnostic_kind,
1197 pretty_printer *pp)
1198 : m_options (context.m_source_printing),
1199 m_line_table (richloc.get_line_table ()),
1200 m_file_cache (context.get_file_cache ()),
1201 m_pp (pp ? pp : context.printer),
1202 m_policy (make_policy (context, richloc)),
1203 m_primary_loc (richloc.get_range (0)->m_loc),
1204 m_exploc (m_file_cache,
1205 richloc.get_expanded_location (0), m_policy,
1206 LOCATION_ASPECT_CARET),
1207 m_colorizer (m_pp, diagnostic_kind),
1208 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1209 m_layout_ranges (richloc.get_num_locations ()),
1210 m_fixit_hints (richloc.get_num_fixit_hints ()),
1211 m_line_spans (1 + richloc.get_num_locations ()),
1212 m_linenum_width (0),
1213 m_x_offset_display (0),
1214 m_escape_on_output (richloc.escape_on_output_p ())
1216 for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
1218 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1219 Ignore any ranges that are awkward to handle. */
1220 const location_range *loc_range = richloc.get_range (idx);
1221 maybe_add_location_range (loc_range, idx, false);
1224 /* Populate m_fixit_hints, filtering to only those that are in the
1225 same file. */
1226 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1228 const fixit_hint *hint = richloc.get_fixit_hint (i);
1229 if (validate_fixit_hint_p (hint))
1230 m_fixit_hints.safe_push (hint);
1233 /* Sort m_fixit_hints. */
1234 m_fixit_hints.qsort (fixit_cmp);
1236 /* Populate the indicated members. */
1237 calculate_line_spans ();
1238 calculate_linenum_width ();
1239 calculate_x_offset_display ();
1241 if (m_options.show_ruler_p)
1242 show_ruler (m_x_offset_display + m_options.max_width);
1246 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1247 those that we can sanely print.
1249 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1250 (for use as extrinsic state by label ranges FIXME).
1252 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1253 filtered against this layout instance's current line spans: it
1254 will only be added if the location is fully within the lines
1255 already specified by other locations.
1257 Return true iff LOC_RANGE was added. */
1259 bool
1260 layout::maybe_add_location_range (const location_range *loc_range,
1261 unsigned original_idx,
1262 bool restrict_to_current_line_spans)
1264 gcc_assert (loc_range);
1266 /* Split the "range" into caret and range information. */
1267 source_range src_range = get_range_from_loc (m_line_table, loc_range->m_loc);
1269 /* Expand the various locations. */
1270 expanded_location start
1271 = linemap_client_expand_location_to_spelling_point
1272 (m_line_table, src_range.m_start, LOCATION_ASPECT_START);
1273 expanded_location finish
1274 = linemap_client_expand_location_to_spelling_point
1275 (m_line_table, src_range.m_finish, LOCATION_ASPECT_FINISH);
1276 expanded_location caret
1277 = linemap_client_expand_location_to_spelling_point
1278 (m_line_table, loc_range->m_loc, LOCATION_ASPECT_CARET);
1280 /* If any part of the range isn't in the same file as the primary
1281 location of this diagnostic, ignore the range. */
1282 if (start.file != m_exploc.file)
1283 return false;
1284 if (finish.file != m_exploc.file)
1285 return false;
1286 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1287 if (caret.file != m_exploc.file)
1288 return false;
1290 /* Sanitize the caret location for non-primary ranges. */
1291 if (m_layout_ranges.length () > 0)
1292 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1293 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1294 /* Discard any non-primary ranges that can't be printed
1295 sanely relative to the primary location. */
1296 return false;
1298 /* Everything is now known to be in the correct source file,
1299 but it may require further sanitization. */
1300 layout_range ri (exploc_with_display_col (m_file_cache,
1301 start, m_policy,
1302 LOCATION_ASPECT_START),
1303 exploc_with_display_col (m_file_cache,
1304 finish, m_policy,
1305 LOCATION_ASPECT_FINISH),
1306 loc_range->m_range_display_kind,
1307 exploc_with_display_col (m_file_cache,
1308 caret, m_policy,
1309 LOCATION_ASPECT_CARET),
1310 original_idx, loc_range->m_label);
1312 /* If we have a range that finishes before it starts (perhaps
1313 from something built via macro expansion), printing the
1314 range is likely to be nonsensical. Also, attempting to do so
1315 breaks assumptions within the printing code (PR c/68473).
1316 Similarly, don't attempt to print ranges if one or both ends
1317 of the range aren't sane to print relative to the
1318 primary location (PR c++/70105). */
1319 if (start.line > finish.line
1320 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1321 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1323 /* Is this the primary location? */
1324 if (m_layout_ranges.length () == 0)
1326 /* We want to print the caret for the primary location, but
1327 we must sanitize away m_start and m_finish. */
1328 ri.m_start = ri.m_caret;
1329 ri.m_finish = ri.m_caret;
1331 else
1332 /* This is a non-primary range; ignore it. */
1333 return false;
1336 /* Potentially filter to just the lines already specified by other
1337 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1338 The layout ctor doesn't use it, and can't because m_line_spans
1339 hasn't been set up at that point. */
1340 if (restrict_to_current_line_spans)
1342 if (!will_show_line_p (start.line))
1343 return false;
1344 if (!will_show_line_p (finish.line))
1345 return false;
1346 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1347 if (!will_show_line_p (caret.line))
1348 return false;
1351 /* Passed all the tests; add the range to m_layout_ranges so that
1352 it will be printed. */
1353 m_layout_ranges.safe_push (ri);
1354 return true;
1357 /* Return true iff ROW is within one of the line spans for this layout. */
1359 bool
1360 layout::will_show_line_p (linenum_type row) const
1362 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1363 line_span_idx++)
1365 const line_span *line_span = get_line_span (line_span_idx);
1366 if (line_span->contains_line_p (row))
1367 return true;
1369 return false;
1372 /* Print a line showing a gap in the line numbers, for showing the boundary
1373 between two line spans. */
1375 void
1376 layout::print_gap_in_line_numbering ()
1378 gcc_assert (m_options.show_line_numbers_p);
1380 pp_emit_prefix (m_pp);
1382 for (int i = 0; i < m_linenum_width + 1; i++)
1383 pp_character (m_pp, '.');
1385 pp_newline (m_pp);
1388 /* Return true iff we should print a heading when starting the
1389 line span with the given index. */
1391 bool
1392 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1394 /* We print a heading for every change of line span, hence for every
1395 line span after the initial one. */
1396 if (line_span_idx > 0)
1397 return true;
1399 /* We also do it for the initial span if the primary location of the
1400 diagnostic is in a different span. */
1401 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1402 return true;
1404 return false;
1407 /* Get an expanded_location for the first location of interest within
1408 the given line_span.
1409 Used when printing a heading to indicate a new line span. */
1411 expanded_location
1412 layout::get_expanded_location (const line_span *line_span) const
1414 /* Whenever possible, use the caret location. */
1415 if (line_span->contains_line_p (m_exploc.line))
1416 return m_exploc;
1418 /* Otherwise, use the start of the first range that's present
1419 within the line_span. */
1420 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1422 const layout_range *lr = &m_layout_ranges[i];
1423 if (line_span->contains_line_p (lr->m_start.m_line))
1425 expanded_location exploc = m_exploc;
1426 exploc.line = lr->m_start.m_line;
1427 exploc.column = lr->m_start.m_columns[CU_BYTES];
1428 return exploc;
1432 /* Otherwise, use the location of the first fixit-hint present within
1433 the line_span. */
1434 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1436 const fixit_hint *hint = m_fixit_hints[i];
1437 location_t loc = hint->get_start_loc ();
1438 expanded_location exploc = expand_location (loc);
1439 if (line_span->contains_line_p (exploc.line))
1440 return exploc;
1443 /* It should not be possible to have a line span that didn't
1444 contain any of the layout_range or fixit_hint instances. */
1445 gcc_unreachable ();
1446 return m_exploc;
1449 /* Determine if HINT is meaningful to print within this layout. */
1451 bool
1452 layout::validate_fixit_hint_p (const fixit_hint *hint)
1454 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1455 return false;
1456 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1457 return false;
1459 return true;
1462 /* Determine the range of lines affected by HINT.
1463 This assumes that HINT has already been filtered by
1464 validate_fixit_hint_p, and so affects the correct source file. */
1466 static line_span
1467 get_line_span_for_fixit_hint (const fixit_hint *hint)
1469 gcc_assert (hint);
1471 int start_line = LOCATION_LINE (hint->get_start_loc ());
1473 /* For line-insertion fix-it hints, add the previous line to the
1474 span, to give the user more context on the proposed change. */
1475 if (hint->ends_with_newline_p ())
1476 if (start_line > 1)
1477 start_line--;
1479 return line_span (start_line,
1480 LOCATION_LINE (hint->get_next_loc ()));
1483 /* We want to print the pertinent source code at a diagnostic. The
1484 rich_location can contain multiple locations. This will have been
1485 filtered into m_exploc (the caret for the primary location) and
1486 m_layout_ranges, for those ranges within the same source file.
1488 We will print a subset of the lines within the source file in question,
1489 as a collection of "spans" of lines.
1491 This function populates m_line_spans with an ordered, disjoint list of
1492 the line spans of interest.
1494 Printing a gap between line spans takes one line, so, when printing
1495 line numbers, we allow a gap of up to one line between spans when
1496 merging, since it makes more sense to print the source line rather than a
1497 "gap-in-line-numbering" line. When not printing line numbers, it's
1498 better to be more explicit about what's going on, so keeping them as
1499 separate spans is preferred.
1501 For example, if the primary range is on lines 8-10, with secondary ranges
1502 covering lines 5-6 and lines 13-15:
1505 005 |RANGE 1
1506 006 |RANGE 1
1508 008 |PRIMARY RANGE
1509 009 |PRIMARY CARET
1510 010 |PRIMARY RANGE
1513 013 |RANGE 2
1514 014 |RANGE 2
1515 015 |RANGE 2
1518 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1520 With line numbering off (with span headers), we want three spans: lines 5-6,
1521 lines 8-10, and lines 13-15. */
1523 void
1524 layout::calculate_line_spans ()
1526 /* This should only be called once, by the ctor. */
1527 gcc_assert (m_line_spans.length () == 0);
1529 /* Populate tmp_spans with individual spans, for each of
1530 m_exploc, and for m_layout_ranges. */
1531 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1532 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1533 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1535 const layout_range *lr = &m_layout_ranges[i];
1536 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1537 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1538 lr->m_finish.m_line));
1541 /* Also add spans for any fix-it hints, in case they cover other lines. */
1542 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1544 const fixit_hint *hint = m_fixit_hints[i];
1545 gcc_assert (hint);
1546 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1549 /* Sort them. */
1550 tmp_spans.qsort(line_span::comparator);
1552 /* Now iterate through tmp_spans, copying into m_line_spans, and
1553 combining where possible. */
1554 gcc_assert (tmp_spans.length () > 0);
1555 m_line_spans.safe_push (tmp_spans[0]);
1556 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1558 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1559 const line_span *next = &tmp_spans[i];
1560 gcc_assert (next->m_first_line >= current->m_first_line);
1561 const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
1562 if ((linenum_arith_t)next->m_first_line
1563 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1565 /* We can merge them. */
1566 if (next->m_last_line > current->m_last_line)
1567 current->m_last_line = next->m_last_line;
1569 else
1571 /* No merger possible. */
1572 m_line_spans.safe_push (*next);
1576 /* Verify the result, in m_line_spans. */
1577 gcc_assert (m_line_spans.length () > 0);
1578 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1580 const line_span *prev = &m_line_spans[i - 1];
1581 const line_span *next = &m_line_spans[i];
1582 /* The individual spans must be sane. */
1583 gcc_assert (prev->m_first_line <= prev->m_last_line);
1584 gcc_assert (next->m_first_line <= next->m_last_line);
1585 /* The spans must be ordered. */
1586 gcc_assert (prev->m_first_line < next->m_first_line);
1587 /* There must be a gap of at least one line between separate spans. */
1588 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1592 /* Determine how many display columns need to be reserved for line numbers,
1593 based on the largest line number that will be needed, and populate
1594 m_linenum_width. */
1596 void
1597 layout::calculate_linenum_width ()
1599 gcc_assert (m_line_spans.length () > 0);
1600 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1601 int highest_line = last_span->m_last_line;
1602 if (highest_line < 0)
1603 highest_line = 0;
1604 m_linenum_width = num_digits (highest_line);
1605 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1606 if (m_line_spans.length () > 1)
1607 m_linenum_width = MAX (m_linenum_width, 3);
1608 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1609 after the line number. */
1610 m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
1613 /* Calculate m_x_offset_display, which improves readability in case the source
1614 line of interest is longer than the user's display. All lines output will be
1615 shifted to the left (so that their beginning is no longer displayed) by
1616 m_x_offset_display display columns, so that the caret is in a reasonable
1617 location. */
1619 void
1620 layout::calculate_x_offset_display ()
1622 m_x_offset_display = 0;
1624 const int max_width = m_options.max_width;
1625 if (!max_width)
1627 /* Nothing to do, the width is not capped. */
1628 return;
1631 const char_span line = m_file_cache.get_source_line (m_exploc.file,
1632 m_exploc.line);
1633 if (!line)
1635 /* Nothing to do, we couldn't find the source line. */
1636 return;
1638 int caret_display_column = m_exploc.m_display_col;
1639 const int line_bytes
1640 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1641 line.length ());
1642 int eol_display_column
1643 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1644 if (caret_display_column > eol_display_column
1645 || !caret_display_column)
1647 /* This does not make sense, so don't try to do anything in this case. */
1648 return;
1651 /* Adjust caret and eol positions to include the left margin. If we are
1652 outputting line numbers, then the left margin is equal to m_linenum_width
1653 plus three for the " | " which follows it. Otherwise the left margin width
1654 is equal to 1, because layout::print_source_line() will prefix each line
1655 with a space. */
1656 const int source_display_cols = eol_display_column;
1657 int left_margin_size = 1;
1658 if (m_options.show_line_numbers_p)
1659 left_margin_size = m_linenum_width + 3;
1660 caret_display_column += left_margin_size;
1661 eol_display_column += left_margin_size;
1663 if (eol_display_column <= max_width)
1665 /* Nothing to do, everything fits in the display. */
1666 return;
1669 /* The line is too long for the display. Calculate an offset such that the
1670 caret is not too close to the right edge of the screen. It will be
1671 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1672 than that to the end of the source line anyway. */
1673 int right_margin_size = CARET_LINE_MARGIN;
1674 right_margin_size = MIN (eol_display_column - caret_display_column,
1675 right_margin_size);
1676 if (right_margin_size + left_margin_size >= max_width)
1678 /* The max_width is very small, so anything we try to do will not be very
1679 effective; just punt in this case and output with no offset. */
1680 return;
1682 const int max_caret_display_column = max_width - right_margin_size;
1683 if (caret_display_column > max_caret_display_column)
1685 m_x_offset_display = caret_display_column - max_caret_display_column;
1686 /* Make sure we don't offset the line into oblivion. */
1687 static const int min_cols_visible = 2;
1688 if (source_display_cols - m_x_offset_display < min_cols_visible)
1689 m_x_offset_display = 0;
1693 /* Print line ROW of source code, potentially colorized at any ranges, and
1694 return the line bounds. LINE is the source line (not necessarily
1695 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1696 colorization and tab expansion, this function tracks the line position in
1697 both byte and display column units. */
1699 line_bounds
1700 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1702 m_colorizer.set_normal_text ();
1704 pp_emit_prefix (m_pp);
1705 if (m_options.show_line_numbers_p)
1707 int width = num_digits (row);
1708 for (int i = 0; i < m_linenum_width - width; i++)
1709 pp_space (m_pp);
1710 pp_printf (m_pp, "%i | ", row);
1712 else
1713 pp_space (m_pp);
1715 /* We will stop printing the source line at any trailing whitespace. */
1716 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1717 line_bytes);
1719 /* This object helps to keep track of which display column we are at, which is
1720 necessary for computing the line bounds in display units, for doing
1721 tab expansion, and for implementing m_x_offset_display. */
1722 cpp_display_width_computation dw (line, line_bytes, m_policy);
1724 /* Skip the first m_x_offset_display display columns. In case the leading
1725 portion that will be skipped ends with a character with wcwidth > 1, then
1726 it is possible we skipped too much, so account for that by padding with
1727 spaces. Note that this does the right thing too in case a tab was the last
1728 character to be skipped over; the tab is effectively replaced by the
1729 correct number of trailing spaces needed to offset by the desired number of
1730 display columns. */
1731 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1732 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1733 pp_space (m_pp);
1735 /* Print the line and compute the line_bounds. */
1736 line_bounds lbounds;
1737 while (!dw.done ())
1739 /* Assuming colorization is enabled for the caret and underline
1740 characters, we may also colorize the associated characters
1741 within the source line.
1743 For frontends that generate range information, we color the
1744 associated characters in the source line the same as the
1745 carets and underlines in the annotation line, to make it easier
1746 for the reader to see the pertinent code.
1748 For frontends that only generate carets, we don't colorize the
1749 characters above them, since this would look strange (e.g.
1750 colorizing just the first character in a token). */
1751 if (m_options.colorize_source_p)
1753 bool in_range_p;
1754 point_state state;
1755 const int start_byte_col = dw.bytes_processed () + 1;
1756 in_range_p = get_state_at_point (row, start_byte_col,
1757 0, INT_MAX,
1758 CU_BYTES,
1759 &state);
1760 if (in_range_p)
1761 m_colorizer.set_range (state.range_idx);
1762 else
1763 m_colorizer.set_normal_text ();
1766 /* Get the display width of the next character to be output, expanding
1767 tabs and replacing some control bytes with spaces as necessary. */
1768 const char *c = dw.next_byte ();
1769 const int start_disp_col = dw.display_cols_processed () + 1;
1770 cpp_decoded_char cp;
1771 const int this_display_width = dw.process_next_codepoint (&cp);
1772 if (*c == '\t')
1774 /* The returned display width is the number of spaces into which the
1775 tab should be expanded. */
1776 for (int i = 0; i != this_display_width; ++i)
1777 pp_space (m_pp);
1778 continue;
1781 /* We have a (possibly multibyte) character to output; update the line
1782 bounds if it is not whitespace. */
1783 if (*c != ' ')
1785 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1786 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1787 lbounds.m_first_non_ws_disp_col = start_disp_col;
1790 /* Output the character. */
1791 m_policy.m_print_cb (m_pp, cp);
1792 c = dw.next_byte ();
1794 print_newline ();
1795 return lbounds;
1798 /* Determine if we should print an annotation line for ROW.
1799 i.e. if any of m_layout_ranges contains ROW. */
1801 bool
1802 layout::should_print_annotation_line_p (linenum_type row) const
1804 layout_range *range;
1805 int i;
1806 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1808 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1809 return false;
1810 if (range->intersects_line_p (row))
1811 return true;
1813 return false;
1816 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1817 margin, which is empty for annotation lines. Otherwise, do nothing. */
1819 void
1820 layout::start_annotation_line (char margin_char) const
1822 pp_emit_prefix (m_pp);
1823 if (m_options.show_line_numbers_p)
1825 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1826 of it, right-aligned, padded with spaces. */
1827 int i;
1828 for (i = 0; i < m_linenum_width - 3; i++)
1829 pp_space (m_pp);
1830 for (; i < m_linenum_width; i++)
1831 pp_character (m_pp, margin_char);
1832 pp_string (m_pp, " |");
1836 /* Print a line consisting of the caret/underlines for the given
1837 source line. */
1839 void
1840 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1842 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1843 lbounds.m_last_non_ws_disp_col);
1845 start_annotation_line ();
1846 pp_space (m_pp);
1848 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1850 bool in_range_p;
1851 point_state state;
1852 in_range_p = get_state_at_point (row, column,
1853 lbounds.m_first_non_ws_disp_col,
1854 lbounds.m_last_non_ws_disp_col,
1855 CU_DISPLAY_COLS,
1856 &state);
1857 if (in_range_p)
1859 /* Within a range. Draw either the caret or an underline. */
1860 m_colorizer.set_range (state.range_idx);
1861 if (state.draw_caret_p)
1863 /* Draw the caret. */
1864 char caret_char;
1865 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1866 caret_char = m_options.caret_chars[state.range_idx];
1867 else
1868 caret_char = '^';
1869 pp_character (m_pp, caret_char);
1871 else
1872 pp_character (m_pp, '~');
1874 else
1876 /* Not in a range. */
1877 m_colorizer.set_normal_text ();
1878 pp_character (m_pp, ' ');
1881 print_newline ();
1884 /* A version of label_text that can live inside a vec.
1885 Requires manual cleanup via maybe_free. */
1887 struct pod_label_text
1889 pod_label_text ()
1890 : m_buffer (NULL), m_caller_owned (false)
1893 pod_label_text (label_text &&other)
1894 : m_buffer (const_cast<char*> (other.get ())),
1895 m_caller_owned (other.is_owner ())
1897 other.release ();
1900 void maybe_free ()
1902 if (m_caller_owned)
1903 free (m_buffer);
1906 char *m_buffer;
1907 bool m_caller_owned;
1910 /* Implementation detail of layout::print_any_labels.
1912 A label within the given row of source. */
1914 class line_label
1916 public:
1917 line_label (const cpp_char_column_policy &policy,
1918 int state_idx, int column,
1919 label_text text)
1920 : m_state_idx (state_idx), m_column (column),
1921 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1923 const int bytes = strlen (m_text.m_buffer);
1924 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1927 /* Sorting is primarily by column, then by state index. */
1928 static int comparator (const void *p1, const void *p2)
1930 const line_label *ll1 = (const line_label *)p1;
1931 const line_label *ll2 = (const line_label *)p2;
1932 int column_cmp = compare (ll1->m_column, ll2->m_column);
1933 if (column_cmp)
1934 return column_cmp;
1935 /* Order by reverse state index, so that labels are printed
1936 in order of insertion into the rich_location when the
1937 sorted list is walked backwards. */
1938 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1941 int m_state_idx;
1942 int m_column;
1943 pod_label_text m_text;
1944 size_t m_display_width;
1945 int m_label_line;
1946 bool m_has_vbar;
1949 /* Print any labels in this row. */
1950 void
1951 layout::print_any_labels (linenum_type row)
1953 int i;
1954 auto_vec<line_label> labels;
1956 /* Gather the labels that are to be printed into "labels". */
1958 layout_range *range;
1959 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1961 /* Most ranges don't have labels, so reject this first. */
1962 if (range->m_label == NULL)
1963 continue;
1965 /* The range's caret must be on this line. */
1966 if (range->m_caret.m_line != row)
1967 continue;
1969 /* Reject labels that aren't fully visible due to clipping
1970 by m_x_offset_display. */
1971 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1972 if (disp_col <= m_x_offset_display)
1973 continue;
1975 label_text text;
1976 text = range->m_label->get_text (range->m_original_idx);
1978 /* Allow for labels that return NULL from their get_text
1979 implementation (so e.g. such labels can control their own
1980 visibility). */
1981 if (text.get () == NULL)
1982 continue;
1984 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1988 /* Bail out if there are no labels on this row. */
1989 if (labels.length () == 0)
1990 return;
1992 /* Sort them. */
1993 labels.qsort(line_label::comparator);
1995 /* Figure out how many "label lines" we need, and which
1996 one each label is printed in.
1998 For example, if the labels aren't too densely packed,
1999 we can fit them on the same line, giving two "label lines":
2001 foo + bar
2002 ~~~ ~~~
2003 | | : label line 0
2004 l0 l1 : label line 1
2006 If they would touch each other or overlap, then we need
2007 additional "label lines":
2009 foo + bar
2010 ~~~ ~~~
2011 | | : label line 0
2012 | label 1 : label line 1
2013 label 0 : label line 2
2015 Place the final label on label line 1, and work backwards, adding
2016 label lines as needed.
2018 If multiple labels are at the same place, put them on separate
2019 label lines:
2021 foo + bar
2022 ^ : label line 0
2023 | : label line 1
2024 label 0 : label line 2
2025 label 1 : label line 3. */
2027 int max_label_line = 1;
2029 int next_column = INT_MAX;
2030 line_label *label;
2031 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2033 /* Would this label "touch" or overlap the next label? */
2034 if (label->m_column + label->m_display_width >= (size_t)next_column)
2036 max_label_line++;
2038 /* If we've already seen labels with the same column, suppress the
2039 vertical bar for subsequent ones in this backwards iteration;
2040 hence only the one with the highest label_line has m_has_vbar set. */
2041 if (label->m_column == next_column)
2042 label->m_has_vbar = false;
2045 label->m_label_line = max_label_line;
2046 next_column = label->m_column;
2050 /* Print the "label lines". For each label within the line, print
2051 either a vertical bar ('|') for the labels that are lower down, or the
2052 labels themselves once we've reached their line. */
2054 for (int label_line = 0; label_line <= max_label_line; label_line++)
2056 start_annotation_line ();
2057 pp_space (m_pp);
2058 int column = 1 + m_x_offset_display;
2059 line_label *label;
2060 FOR_EACH_VEC_ELT (labels, i, label)
2062 if (label_line > label->m_label_line)
2063 /* We've printed all the labels for this label line. */
2064 break;
2066 if (label_line == label->m_label_line)
2068 gcc_assert (column <= label->m_column);
2069 move_to_column (&column, label->m_column, true);
2070 /* Colorize the text, unless it's for events in a
2071 diagnostic_path. */
2072 if (!m_diagnostic_path_p)
2073 m_colorizer.set_range (label->m_state_idx);
2074 pp_string (m_pp, label->m_text.m_buffer);
2075 m_colorizer.set_normal_text ();
2076 column += label->m_display_width;
2078 else if (label->m_has_vbar)
2080 gcc_assert (column <= label->m_column);
2081 move_to_column (&column, label->m_column, true);
2082 m_colorizer.set_range (label->m_state_idx);
2083 pp_character (m_pp, '|');
2084 m_colorizer.set_normal_text ();
2085 column++;
2088 print_newline ();
2092 /* Clean up. */
2094 line_label *label;
2095 FOR_EACH_VEC_ELT (labels, i, label)
2096 label->m_text.maybe_free ();
2100 /* If there are any fixit hints inserting new lines before source line ROW,
2101 print them.
2103 They are printed on lines of their own, before the source line
2104 itself, with a leading '+'. */
2106 void
2107 layout::print_leading_fixits (linenum_type row)
2109 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2111 const fixit_hint *hint = m_fixit_hints[i];
2113 if (!hint->ends_with_newline_p ())
2114 /* Not a newline fixit; print it in print_trailing_fixits. */
2115 continue;
2117 gcc_assert (hint->insertion_p ());
2119 if (hint->affects_line_p (m_line_table, m_exploc.file, row))
2121 /* Printing the '+' with normal colorization
2122 and the inserted line with "insert" colorization
2123 helps them stand out from each other, and from
2124 the surrounding text. */
2125 m_colorizer.set_normal_text ();
2126 start_annotation_line ('+');
2127 pp_character (m_pp, '+');
2128 m_colorizer.set_fixit_insert ();
2129 /* Print all but the trailing newline of the fix-it hint.
2130 We have to print the newline separately to avoid
2131 getting additional pp prefixes printed. */
2132 for (size_t i = 0; i < hint->get_length () - 1; i++)
2133 pp_character (m_pp, hint->get_string ()[i]);
2134 m_colorizer.set_normal_text ();
2135 pp_newline (m_pp);
2140 /* Subroutine of layout::print_trailing_fixits.
2142 Determine if the annotation line printed for LINE contained
2143 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2145 bool
2146 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2147 int finish_column) const
2149 layout_range *range;
2150 int i;
2151 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2152 if (range->m_start.m_line == line
2153 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2154 && range->m_finish.m_line == line
2155 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2156 return true;
2157 return false;
2160 /* Classes for printing trailing fix-it hints i.e. those that
2161 don't add new lines.
2163 For insertion, these can look like:
2165 new_text
2167 For replacement, these can look like:
2169 ------------- : underline showing affected range
2170 new_text
2172 For deletion, these can look like:
2174 ------------- : underline showing affected range
2176 This can become confusing if they overlap, and so we need
2177 to do some preprocessing to decide what to print.
2178 We use the list of fixit_hint instances affecting the line
2179 to build a list of "correction" instances, and print the
2180 latter.
2182 For example, consider a set of fix-its for converting
2183 a C-style cast to a C++ const_cast.
2185 Given:
2187 ..000000000111111111122222222223333333333.
2188 ..123456789012345678901234567890123456789.
2189 foo *f = (foo *)ptr->field;
2190 ^~~~~
2192 and the fix-it hints:
2193 - replace col 10 (the open paren) with "const_cast<"
2194 - replace col 16 (the close paren) with "> ("
2195 - insert ")" before col 27
2197 then we would get odd-looking output:
2199 foo *f = (foo *)ptr->field;
2200 ^~~~~
2202 const_cast<
2204 > ( )
2206 It would be better to detect when fixit hints are going to
2207 overlap (those that require new lines), and to consolidate
2208 the printing of such fixits, giving something like:
2210 foo *f = (foo *)ptr->field;
2211 ^~~~~
2212 -----------------
2213 const_cast<foo *> (ptr->field)
2215 This works by detecting when the printing would overlap, and
2216 effectively injecting no-op replace hints into the gaps between
2217 such fix-its, so that the printing joins up.
2219 In the above example, the overlap of:
2220 - replace col 10 (the open paren) with "const_cast<"
2221 and:
2222 - replace col 16 (the close paren) with "> ("
2223 is fixed by injecting a no-op:
2224 - replace cols 11-15 with themselves ("foo *")
2225 and consolidating these, making:
2226 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2227 i.e.:
2228 - replace cols 10-16 with "const_cast<foo *> ("
2230 This overlaps with the final fix-it hint:
2231 - insert ")" before col 27
2232 and so we repeat the consolidation process, by injecting
2233 a no-op:
2234 - replace cols 17-26 with themselves ("ptr->field")
2235 giving:
2236 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2237 i.e.:
2238 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2240 and is thus printed as desired. */
2242 /* A range of (byte or display) columns within a line. */
2244 class column_range
2246 public:
2247 column_range (int start_, int finish_) : start (start_), finish (finish_)
2249 gcc_assert (valid_p (start, finish));
2252 bool operator== (const column_range &other) const
2254 return start == other.start && finish == other.finish;
2257 static bool valid_p (int start, int finish)
2259 /* We must have either a range, or an insertion. */
2260 return (start <= finish || finish == start - 1);
2263 int start;
2264 int finish;
2267 /* Get the range of bytes or display columns that HINT would affect. */
2268 static column_range
2269 get_affected_range (file_cache &fc,
2270 const cpp_char_column_policy &policy,
2271 const fixit_hint *hint, enum column_unit col_unit)
2273 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2274 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2275 --exploc_finish.column;
2277 int start_column;
2278 int finish_column;
2279 if (col_unit == CU_DISPLAY_COLS)
2281 start_column = location_compute_display_column (fc, exploc_start, policy);
2282 if (hint->insertion_p ())
2283 finish_column = start_column - 1;
2284 else
2285 finish_column
2286 = location_compute_display_column (fc, exploc_finish, policy);
2288 else
2290 start_column = exploc_start.column;
2291 finish_column = exploc_finish.column;
2293 return column_range (start_column, finish_column);
2296 /* Get the range of display columns that would be printed for HINT. */
2298 static column_range
2299 get_printed_columns (file_cache &fc,
2300 const cpp_char_column_policy &policy,
2301 const fixit_hint *hint)
2303 expanded_location exploc = expand_location (hint->get_start_loc ());
2304 int start_column = location_compute_display_column (fc, exploc, policy);
2305 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2306 policy);
2307 int final_hint_column = start_column + hint_width - 1;
2308 if (hint->insertion_p ())
2310 return column_range (start_column, final_hint_column);
2312 else
2314 exploc = expand_location (hint->get_next_loc ());
2315 --exploc.column;
2316 int finish_column = location_compute_display_column (fc, exploc, policy);
2317 return column_range (start_column,
2318 MAX (finish_column, final_hint_column));
2322 /* A correction on a particular line.
2323 This describes a plan for how to print one or more fixit_hint
2324 instances that affected the line, potentially consolidating hints
2325 into corrections to make the result easier for the user to read. */
2327 class correction
2329 public:
2330 correction (column_range affected_bytes,
2331 column_range affected_columns,
2332 column_range printed_columns,
2333 const char *new_text, size_t new_text_len,
2334 const cpp_char_column_policy &policy)
2335 : m_affected_bytes (affected_bytes),
2336 m_affected_columns (affected_columns),
2337 m_printed_columns (printed_columns),
2338 m_text (xstrdup (new_text)),
2339 m_byte_length (new_text_len),
2340 m_policy (policy),
2341 m_alloc_sz (new_text_len + 1)
2343 compute_display_cols ();
2346 ~correction () { free (m_text); }
2348 bool insertion_p () const
2350 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2353 void ensure_capacity (size_t len);
2354 void ensure_terminated ();
2356 void compute_display_cols ()
2358 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2361 void overwrite (int dst_offset, const char_span &src_span)
2363 gcc_assert (dst_offset >= 0);
2364 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2365 memcpy (m_text + dst_offset, src_span.get_buffer (),
2366 src_span.length ());
2369 /* If insert, then start: the column before which the text
2370 is to be inserted, and finish is offset by the length of
2371 the replacement.
2372 If replace, then the range of columns affected. */
2373 column_range m_affected_bytes;
2374 column_range m_affected_columns;
2376 /* If insert, then start: the column before which the text
2377 is to be inserted, and finish is offset by the length of
2378 the replacement.
2379 If replace, then the range of columns affected. */
2380 column_range m_printed_columns;
2382 /* The text to be inserted/used as replacement. */
2383 char *m_text;
2384 size_t m_byte_length; /* Not including null-terminator. */
2385 int m_display_cols;
2386 const cpp_char_column_policy &m_policy;
2387 size_t m_alloc_sz;
2390 /* Ensure that m_text can hold a string of length LEN
2391 (plus 1 for 0-termination). */
2393 void
2394 correction::ensure_capacity (size_t len)
2396 /* Allow 1 extra byte for 0-termination. */
2397 if (m_alloc_sz < (len + 1))
2399 size_t new_alloc_sz = (len + 1) * 2;
2400 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2401 m_alloc_sz = new_alloc_sz;
2405 /* Ensure that m_text is 0-terminated. */
2407 void
2408 correction::ensure_terminated ()
2410 /* 0-terminate the buffer. */
2411 gcc_assert (m_byte_length < m_alloc_sz);
2412 m_text[m_byte_length] = '\0';
2415 /* A list of corrections affecting a particular line.
2416 This is used by layout::print_trailing_fixits for planning
2417 how to print the fix-it hints affecting the line. */
2419 class line_corrections
2421 public:
2422 line_corrections (file_cache &fc,
2423 const char_display_policy &policy,
2424 const char *filename,
2425 linenum_type row)
2426 : m_file_cache (fc),
2427 m_policy (policy), m_filename (filename), m_row (row)
2429 ~line_corrections ();
2431 void add_hint (const fixit_hint *hint);
2433 file_cache &m_file_cache;
2434 const char_display_policy &m_policy;
2435 const char *m_filename;
2436 linenum_type m_row;
2437 auto_vec <correction *> m_corrections;
2440 /* struct line_corrections. */
2442 line_corrections::~line_corrections ()
2444 unsigned i;
2445 correction *c;
2446 FOR_EACH_VEC_ELT (m_corrections, i, c)
2447 delete c;
2450 /* A struct wrapping a particular source line, allowing
2451 run-time bounds-checking of accesses in a checked build. */
2453 class source_line
2455 public:
2456 source_line (file_cache &fc, const char *filename, int line);
2458 char_span as_span () { return char_span (chars, width); }
2460 const char *chars;
2461 int width;
2464 /* source_line's ctor. */
2466 source_line::source_line (file_cache &fc, const char *filename, int line)
2468 char_span span = fc.get_source_line (filename, line);
2469 chars = span.get_buffer ();
2470 width = span.length ();
2473 /* Add HINT to the corrections for this line.
2474 Attempt to consolidate nearby hints so that they will not
2475 overlap with printed. */
2477 void
2478 line_corrections::add_hint (const fixit_hint *hint)
2480 column_range affected_bytes
2481 = get_affected_range (m_file_cache, m_policy, hint, CU_BYTES);
2482 column_range affected_columns
2483 = get_affected_range (m_file_cache, m_policy, hint, CU_DISPLAY_COLS);
2484 column_range printed_columns
2485 = get_printed_columns (m_file_cache, m_policy, hint);
2487 /* Potentially consolidate. */
2488 if (!m_corrections.is_empty ())
2490 correction *last_correction
2491 = m_corrections[m_corrections.length () - 1];
2493 /* The following consolidation code assumes that the fix-it hints
2494 have been sorted by start (done within layout's ctor). */
2495 gcc_assert (affected_bytes.start
2496 >= last_correction->m_affected_bytes.start);
2497 gcc_assert (printed_columns.start
2498 >= last_correction->m_printed_columns.start);
2500 if (printed_columns.start <= last_correction->m_printed_columns.finish
2501 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2502 affected_bytes.start - 1))
2504 /* We have two hints for which the printed forms of the hints
2505 would touch or overlap, so we need to consolidate them to avoid
2506 confusing the user.
2507 Attempt to inject a "replace" correction from immediately
2508 after the end of the last hint to immediately before the start
2509 of the next hint. */
2510 column_range between (last_correction->m_affected_bytes.finish + 1,
2511 affected_bytes.start - 1);
2513 /* Try to read the source. */
2514 source_line line (m_file_cache, m_filename, m_row);
2515 if (line.chars && between.finish < line.width)
2517 /* Consolidate into the last correction:
2518 add a no-op "replace" of the "between" text, and
2519 add the text from the new hint. */
2520 int old_byte_len = last_correction->m_byte_length;
2521 gcc_assert (old_byte_len >= 0);
2522 int between_byte_len = between.finish + 1 - between.start;
2523 gcc_assert (between_byte_len >= 0);
2524 int new_byte_len
2525 = old_byte_len + between_byte_len + hint->get_length ();
2526 gcc_assert (new_byte_len >= 0);
2527 last_correction->ensure_capacity (new_byte_len);
2528 last_correction->overwrite
2529 (old_byte_len,
2530 line.as_span ().subspan (between.start - 1,
2531 between.finish + 1 - between.start));
2532 last_correction->overwrite (old_byte_len + between_byte_len,
2533 char_span (hint->get_string (),
2534 hint->get_length ()));
2535 last_correction->m_byte_length = new_byte_len;
2536 last_correction->ensure_terminated ();
2537 last_correction->m_affected_bytes.finish
2538 = affected_bytes.finish;
2539 last_correction->m_affected_columns.finish
2540 = affected_columns.finish;
2541 int prev_display_cols = last_correction->m_display_cols;
2542 last_correction->compute_display_cols ();
2543 last_correction->m_printed_columns.finish
2544 += last_correction->m_display_cols - prev_display_cols;
2545 return;
2550 /* If no consolidation happened, add a new correction instance. */
2551 m_corrections.safe_push (new correction (affected_bytes,
2552 affected_columns,
2553 printed_columns,
2554 hint->get_string (),
2555 hint->get_length (),
2556 m_policy));
2559 /* If there are any fixit hints on source line ROW, print them.
2560 They are printed in order, attempting to combine them onto lines, but
2561 starting new lines if necessary.
2562 Fix-it hints that insert new lines are handled separately,
2563 in layout::print_leading_fixits. */
2565 void
2566 layout::print_trailing_fixits (linenum_type row)
2568 /* Build a list of correction instances for the line,
2569 potentially consolidating hints (for the sake of readability). */
2570 line_corrections corrections (m_file_cache, m_policy, m_exploc.file, row);
2571 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2573 const fixit_hint *hint = m_fixit_hints[i];
2575 /* Newline fixits are handled by layout::print_leading_fixits. */
2576 if (hint->ends_with_newline_p ())
2577 continue;
2579 if (hint->affects_line_p (m_line_table, m_exploc.file, row))
2580 corrections.add_hint (hint);
2583 /* Now print the corrections. */
2584 unsigned i;
2585 correction *c;
2586 int column = m_x_offset_display;
2588 if (!corrections.m_corrections.is_empty ())
2589 start_annotation_line ();
2591 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2593 /* For now we assume each fixit hint can only touch one line. */
2594 if (c->insertion_p ())
2596 /* This assumes the insertion just affects one line. */
2597 int start_column = c->m_printed_columns.start;
2598 move_to_column (&column, start_column, true);
2599 m_colorizer.set_fixit_insert ();
2600 pp_string (m_pp, c->m_text);
2601 m_colorizer.set_normal_text ();
2602 column += c->m_display_cols;
2604 else
2606 /* If the range of the replacement wasn't printed in the
2607 annotation line, then print an extra underline to
2608 indicate exactly what is being replaced.
2609 Always show it for removals. */
2610 int start_column = c->m_affected_columns.start;
2611 int finish_column = c->m_affected_columns.finish;
2612 if (!annotation_line_showed_range_p (row, start_column,
2613 finish_column)
2614 || c->m_byte_length == 0)
2616 move_to_column (&column, start_column, true);
2617 m_colorizer.set_fixit_delete ();
2618 for (; column <= finish_column; column++)
2619 pp_character (m_pp, '-');
2620 m_colorizer.set_normal_text ();
2622 /* Print the replacement text. REPLACE also covers
2623 removals, so only do this extra work (potentially starting
2624 a new line) if we have actual replacement text. */
2625 if (c->m_byte_length > 0)
2627 move_to_column (&column, start_column, true);
2628 m_colorizer.set_fixit_insert ();
2629 pp_string (m_pp, c->m_text);
2630 m_colorizer.set_normal_text ();
2631 column += c->m_display_cols;
2636 /* Add a trailing newline, if necessary. */
2637 move_to_column (&column, 0, false);
2640 /* Disable any colorization and emit a newline. */
2642 void
2643 layout::print_newline ()
2645 m_colorizer.set_normal_text ();
2646 pp_newline (m_pp);
2649 /* Return true if (ROW/COLUMN) is within a range of the layout.
2650 If it returns true, OUT_STATE is written to, with the
2651 range index, and whether we should draw the caret at
2652 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2653 whether all inputs and outputs are in bytes or display column units. */
2655 bool
2656 layout::get_state_at_point (/* Inputs. */
2657 linenum_type row, int column,
2658 int first_non_ws, int last_non_ws,
2659 enum column_unit col_unit,
2660 /* Outputs. */
2661 point_state *out_state)
2663 layout_range *range;
2664 int i;
2665 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2667 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2668 /* Bail out early, so that such ranges don't affect underlining or
2669 source colorization. */
2670 continue;
2672 if (range->contains_point (row, column, col_unit))
2674 out_state->range_idx = i;
2676 /* Are we at the range's caret? is it visible? */
2677 out_state->draw_caret_p = false;
2678 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2679 && row == range->m_caret.m_line
2680 && column == range->m_caret.m_columns[col_unit])
2681 out_state->draw_caret_p = true;
2683 /* Within a multiline range, don't display any underline
2684 in any leading or trailing whitespace on a line.
2685 We do display carets, however. */
2686 if (!out_state->draw_caret_p)
2687 if (column < first_non_ws || column > last_non_ws)
2688 return false;
2690 /* We are within a range. */
2691 return true;
2695 return false;
2698 /* Helper function for use by layout::print_line when printing the
2699 annotation line under the source line.
2700 Get the display column beyond the rightmost one that could contain a caret
2701 or range marker, given that we stop rendering at trailing whitespace.
2702 ROW is the source line within the given file.
2703 CARET_COLUMN is the display column of range 0's caret.
2704 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2705 character of source (as determined when printing the source line). */
2708 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2709 int last_non_ws_column)
2711 int result = caret_column + 1;
2713 layout_range *range;
2714 int i;
2715 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2717 if (row >= range->m_start.m_line)
2719 if (range->m_finish.m_line == row)
2721 /* On the final line within a range; ensure that
2722 we render up to the end of the range. */
2723 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2724 if (result <= disp_col)
2725 result = disp_col + 1;
2727 else if (row < range->m_finish.m_line)
2729 /* Within a multiline range; ensure that we render up to the
2730 last non-whitespace column. */
2731 if (result <= last_non_ws_column)
2732 result = last_non_ws_column + 1;
2737 return result;
2740 /* Given *COLUMN as an x-coordinate, print spaces to position
2741 successive output at DEST_COLUMN, printing a newline if necessary,
2742 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2743 left margin after any newline. */
2745 void
2746 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2748 /* Start a new line if we need to. */
2749 if (*column > dest_column)
2751 print_newline ();
2752 if (add_left_margin)
2753 start_annotation_line ();
2754 *column = m_x_offset_display;
2757 while (*column < dest_column)
2759 pp_space (m_pp);
2760 (*column)++;
2764 /* For debugging layout issues, render a ruler giving column numbers
2765 (after the 1-column indent). */
2767 void
2768 layout::show_ruler (int max_column) const
2770 /* Hundreds. */
2771 if (max_column > 99)
2773 start_annotation_line ();
2774 pp_space (m_pp);
2775 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2776 if (column % 10 == 0)
2777 pp_character (m_pp, '0' + (column / 100) % 10);
2778 else
2779 pp_space (m_pp);
2780 pp_newline (m_pp);
2783 /* Tens. */
2784 start_annotation_line ();
2785 pp_space (m_pp);
2786 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2787 if (column % 10 == 0)
2788 pp_character (m_pp, '0' + (column / 10) % 10);
2789 else
2790 pp_space (m_pp);
2791 pp_newline (m_pp);
2793 /* Units. */
2794 start_annotation_line ();
2795 pp_space (m_pp);
2796 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2797 pp_character (m_pp, '0' + (column % 10));
2798 pp_newline (m_pp);
2801 /* Print leading fix-its (for new lines inserted before the source line)
2802 then the source line, followed by an annotation line
2803 consisting of any caret/underlines, then any fixits.
2804 If the source line can't be read, print nothing. */
2805 void
2806 layout::print_line (linenum_type row)
2808 char_span line = m_file_cache.get_source_line (m_exploc.file, row);
2809 if (!line)
2810 return;
2812 print_leading_fixits (row);
2813 const line_bounds lbounds
2814 = print_source_line (row, line.get_buffer (), line.length ());
2815 if (should_print_annotation_line_p (row))
2816 print_annotation_line (row, lbounds);
2817 if (m_options.show_labels_p)
2818 print_any_labels (row);
2819 print_trailing_fixits (row);
2822 } /* End of anonymous namespace. */
2824 /* If LOC is within the spans of lines that will already be printed for
2825 this gcc_rich_location, then add it as a secondary location and return true.
2827 Otherwise return false. */
2829 bool
2830 gcc_rich_location::add_location_if_nearby (location_t loc,
2831 bool restrict_to_current_line_spans,
2832 const range_label *label)
2834 /* Use the layout location-handling logic to sanitize LOC,
2835 filtering it to the current line spans within a temporary
2836 layout instance. */
2837 layout layout (*global_dc, *this, DK_ERROR, nullptr);
2838 location_range loc_range;
2839 loc_range.m_loc = loc;
2840 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2841 if (!layout.maybe_add_location_range (&loc_range, 0,
2842 restrict_to_current_line_spans))
2843 return false;
2845 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2846 return true;
2849 /* As per diagnostic_context::show_locus, but don't print anything
2850 if source printing is disabled, or if the location hasn't changed. */
2852 void
2853 diagnostic_context::maybe_show_locus (const rich_location &richloc,
2854 diagnostic_t diagnostic_kind,
2855 pretty_printer *pp)
2857 const location_t loc = richloc.get_loc ();
2858 /* Do nothing if source-printing has been disabled. */
2859 if (!m_source_printing.enabled)
2860 return;
2862 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2863 if (loc <= BUILTINS_LOCATION)
2864 return;
2866 /* Don't print the same source location twice in a row, unless we have
2867 fix-it hints, or multiple locations, or a label. */
2868 if (loc == m_last_location
2869 && richloc.get_num_fixit_hints () == 0
2870 && richloc.get_num_locations () == 1
2871 && richloc.get_range (0)->m_label == NULL)
2872 return;
2874 m_last_location = loc;
2876 show_locus (richloc, diagnostic_kind, pp);
2879 /* Print the physical source code corresponding to the location of
2880 this diagnostic, with additional annotations.
2881 If PP is non-null, then use it rather than this context's printer. */
2883 void
2884 diagnostic_context::show_locus (const rich_location &richloc,
2885 diagnostic_t diagnostic_kind,
2886 pretty_printer *pp)
2888 layout layout (*this, richloc, diagnostic_kind, pp);
2889 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2890 line_span_idx++)
2892 const line_span *line_span = layout.get_line_span (line_span_idx);
2893 if (m_source_printing.show_line_numbers_p)
2895 /* With line numbers, we should show whenever the line-numbering
2896 "jumps". */
2897 if (line_span_idx > 0)
2898 layout.print_gap_in_line_numbering ();
2900 else
2902 /* Without line numbers, we print headings for some line spans. */
2903 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2905 expanded_location exploc
2906 = layout.get_expanded_location (line_span);
2907 m_text_callbacks.m_start_span (this, exploc);
2910 /* Iterate over the lines within this span (using linenum_arith_t to
2911 avoid overflow with 0xffffffff causing an infinite loop). */
2912 linenum_arith_t last_line = line_span->get_last_line ();
2913 for (linenum_arith_t row = line_span->get_first_line ();
2914 row <= last_line; row++)
2915 layout.print_line (row);
2919 #if CHECKING_P
2921 namespace selftest {
2923 /* Selftests for diagnostic_show_locus. */
2925 /* Verify that cpp_display_width correctly handles escaping. */
2927 static void
2928 test_display_widths ()
2930 gcc_rich_location richloc (UNKNOWN_LOCATION);
2932 /* U+03C0 "GREEK SMALL LETTER PI". */
2933 const char *pi = "\xCF\x80";
2934 /* U+1F642 "SLIGHTLY SMILING FACE". */
2935 const char *emoji = "\xF0\x9F\x99\x82";
2936 /* Stray trailing byte of a UTF-8 character. */
2937 const char *stray = "\xBF";
2938 /* U+10FFFF. */
2939 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2941 /* No escaping. */
2943 test_diagnostic_context dc;
2944 char_display_policy policy (make_policy (dc, richloc));
2945 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2946 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2947 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2948 /* Don't check width of U+10FFFF; it's in a private use plane. */
2951 richloc.set_escape_on_output (true);
2954 test_diagnostic_context dc;
2955 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
2956 char_display_policy policy (make_policy (dc, richloc));
2957 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2958 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2959 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2960 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2961 policy),
2962 strlen ("<U+10FFFF>"));
2966 test_diagnostic_context dc;
2967 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
2968 char_display_policy policy (make_policy (dc, richloc));
2969 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2970 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2971 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2972 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2973 policy),
2974 16);
2978 /* For precise tests of the layout, make clear where the source line will
2979 start. test_left_margin sets the total byte count from the left side of the
2980 screen to the start of source lines, after the line number and the separator,
2981 which consists of the three characters " | ". */
2982 static const int test_linenum_sep = 3;
2983 static const int test_left_margin = 7;
2985 /* Helper function for test_layout_x_offset_display_utf8(). */
2986 static void
2987 test_offset_impl (int caret_byte_col, int max_width,
2988 int expected_x_offset_display,
2989 int left_margin = test_left_margin)
2991 test_diagnostic_context dc;
2992 dc.m_source_printing.max_width = max_width;
2993 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2994 the line number plus one space after. */
2995 dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
2996 dc.m_source_printing.show_line_numbers_p = true;
2997 rich_location richloc (line_table,
2998 linemap_position_for_column (line_table,
2999 caret_byte_col));
3000 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3001 ASSERT_EQ (left_margin - test_linenum_sep,
3002 test_layout.get_linenum_width ());
3003 ASSERT_EQ (expected_x_offset_display,
3004 test_layout.get_x_offset_display ());
3007 /* Test that layout::calculate_x_offset_display() works. */
3008 static void
3009 test_layout_x_offset_display_utf8 (const line_table_case &case_)
3012 const char *content
3013 = "This line is very long, so that we can use it to test the logic for "
3014 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
3015 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
3016 "column #102.\n";
3018 /* Number of bytes in the line, subtracting one to remove the newline. */
3019 const int line_bytes = strlen (content) - 1;
3021 /* Number of display columns occupied by the line; each of the 2 emojis
3022 takes up 2 fewer display columns than it does bytes. */
3023 const int line_display_cols = line_bytes - 2*2;
3025 /* The column of the first emoji. Byte or display is the same as there are
3026 no multibyte characters earlier on the line. */
3027 const int emoji_col = 102;
3029 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3030 file_cache fc;
3031 line_table_test ltt (case_);
3033 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3035 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3037 /* Don't attempt to run the tests if column data might be unavailable. */
3038 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3039 return;
3041 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3042 ASSERT_EQ (1, LOCATION_LINE (line_end));
3043 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3045 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
3046 ASSERT_EQ (line_display_cols,
3047 cpp_display_width (lspan.get_buffer (), lspan.length (),
3048 def_policy ()));
3049 ASSERT_EQ (line_display_cols,
3050 location_compute_display_column (fc,
3051 expand_location (line_end),
3052 def_policy ()));
3053 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3054 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3056 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3058 /* No constraint on the width -> no offset. */
3059 test_offset_impl (emoji_col, 0, 0);
3061 /* Caret is before the beginning -> no offset. */
3062 test_offset_impl (0, 100, 0);
3064 /* Caret is past the end of the line -> no offset. */
3065 test_offset_impl (line_bytes+1, 100, 0);
3067 /* Line fits in the display -> no offset. */
3068 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3069 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3071 /* Line is too long for the display but caret location is OK
3072 anyway -> no offset. */
3073 static const int small_width = 24;
3074 test_offset_impl (1, small_width, 0);
3076 /* Width constraint is very small -> no offset. */
3077 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3079 /* Line would be offset, but due to large line numbers, offsetting
3080 would remove the whole line -> no offset. */
3081 static const int huge_left_margin = 100;
3082 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3084 /* Line is the same length as the display, but the line number makes it too
3085 long, so offset is required. Caret is at the end so padding on the right
3086 is not in effect. */
3087 for (int excess = 1; excess <= 3; ++excess)
3088 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3089 excess);
3091 /* Line is much too long for the display, caret is near the end ->
3092 offset should be such that the line fits in the display and caret
3093 remains the same distance from the end that it was. */
3094 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3095 caret_offset <= max_offset; ++caret_offset)
3096 test_offset_impl (line_bytes - caret_offset, small_width,
3097 line_display_cols + test_left_margin - small_width);
3099 /* As previous case but caret is closer to the middle; now we want it to end
3100 up CARET_LINE_MARGIN bytes from the end. */
3101 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3102 test_offset_impl (emoji_col, small_width,
3103 emoji_col + test_left_margin
3104 - (small_width - CARET_LINE_MARGIN));
3106 /* Test that the source line is offset as expected when printed. */
3108 test_diagnostic_context dc;
3109 dc.m_source_printing.max_width = small_width - 6;
3110 dc.m_source_printing.min_margin_width
3111 = test_left_margin - test_linenum_sep + 1;
3112 dc.m_source_printing.show_line_numbers_p = true;
3113 dc.m_source_printing.show_ruler_p = true;
3114 rich_location richloc (line_table,
3115 linemap_position_for_column (line_table,
3116 emoji_col));
3117 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3118 test_layout.print_line (1);
3119 ASSERT_STREQ (" | 1 \n"
3120 " | 1 \n"
3121 " | 234567890123456789\n"
3122 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3123 "that occupies 8 bytes and 4 display columns, starting at "
3124 "column #102.\n"
3125 " | ^\n\n",
3126 pp_formatted_text (dc.printer));
3129 /* Similar to the previous example, but now the offset called for would split
3130 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3131 it with a padding space in this case. */
3133 test_diagnostic_context dc;
3134 dc.m_source_printing.max_width = small_width - 5;
3135 dc.m_source_printing.min_margin_width
3136 = test_left_margin - test_linenum_sep + 1;
3137 dc.m_source_printing.show_line_numbers_p = true;
3138 dc.m_source_printing.show_ruler_p = true;
3139 rich_location richloc (line_table,
3140 linemap_position_for_column (line_table,
3141 emoji_col + 2));
3142 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3143 test_layout.print_line (1);
3144 ASSERT_STREQ (" | 1 1 \n"
3145 " | 1 2 \n"
3146 " | 3456789012345678901\n"
3147 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3148 "that occupies 8 bytes and 4 display columns, starting at "
3149 "column #102.\n"
3150 " | ^\n\n",
3151 pp_formatted_text (dc.printer));
3156 static void
3157 test_layout_x_offset_display_tab (const line_table_case &case_)
3159 const char *content
3160 = "This line is very long, so that we can use it to test the logic for "
3161 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3162 "a variable number of display columns, starting at column #103.\n";
3164 /* Number of bytes in the line, subtracting one to remove the newline. */
3165 const int line_bytes = strlen (content) - 1;
3167 /* The column where the tab begins. Byte or display is the same as there are
3168 no multibyte characters earlier on the line. */
3169 const int tab_col = 103;
3171 /* Effective extra size of the tab beyond what a single space would have taken
3172 up, indexed by tabstop. */
3173 static const int num_tabstops = 11;
3174 int extra_width[num_tabstops];
3175 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3177 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3178 extra_width[tabstop] = this_tab_size - 1;
3180 /* Example of this calculation: if tabstop is 10, the tab starting at column
3181 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3182 next character is at column #111. So it takes up 7 more columns than
3183 a space would have taken up. */
3184 ASSERT_EQ (7, extra_width[10]);
3186 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3187 file_cache fc;
3188 line_table_test ltt (case_);
3190 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3192 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3194 /* Don't attempt to run the tests if column data might be unavailable. */
3195 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3196 return;
3198 /* Check that cpp_display_width handles the tabs as expected. */
3199 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
3200 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3201 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3203 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3204 ASSERT_EQ (line_bytes + extra_width[tabstop],
3205 cpp_display_width (lspan.get_buffer (), lspan.length (),
3206 policy));
3207 ASSERT_EQ (line_bytes + extra_width[tabstop],
3208 location_compute_display_column (fc,
3209 expand_location (line_end),
3210 policy));
3213 /* Check that the tab is expanded to the expected number of spaces. */
3214 rich_location richloc (line_table,
3215 linemap_position_for_column (line_table,
3216 tab_col + 1));
3217 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3219 test_diagnostic_context dc;
3220 dc.m_tabstop = tabstop;
3221 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3222 test_layout.print_line (1);
3223 const char *out = pp_formatted_text (dc.printer);
3224 ASSERT_EQ (NULL, strchr (out, '\t'));
3225 const char *left_quote = strchr (out, '`');
3226 const char *right_quote = strchr (out, '\'');
3227 ASSERT_NE (NULL, left_quote);
3228 ASSERT_NE (NULL, right_quote);
3229 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3232 /* Check that the line is offset properly and that the tab is broken up
3233 into the expected number of spaces when it is the last character skipped
3234 over. */
3235 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3237 test_diagnostic_context dc;
3238 dc.m_tabstop = tabstop;
3239 static const int small_width = 24;
3240 dc.m_source_printing.max_width = small_width - 4;
3241 dc.m_source_printing.min_margin_width
3242 = test_left_margin - test_linenum_sep + 1;
3243 dc.m_source_printing.show_line_numbers_p = true;
3244 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3245 test_layout.print_line (1);
3247 /* We have arranged things so that two columns will be printed before
3248 the caret. If the tab results in more than one space, this should
3249 produce two spaces in the output; otherwise, it will be a single space
3250 preceded by the opening quote before the tab character. */
3251 const char *output1
3252 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3253 "display columns, starting at column #103.\n"
3254 " | ^\n\n";
3255 const char *output2
3256 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3257 "display columns, starting at column #103.\n"
3258 " | ^\n\n";
3259 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3260 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3265 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3267 static void
3268 test_diagnostic_show_locus_unknown_location ()
3270 test_diagnostic_context dc;
3271 rich_location richloc (line_table, UNKNOWN_LOCATION);
3272 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3273 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3276 /* Verify that diagnostic_show_locus works sanely for various
3277 single-line cases.
3279 All of these work on the following 1-line source file:
3280 .0000000001111111
3281 .1234567890123456
3282 "foo = bar.field;\n"
3283 which is set up by test_diagnostic_show_locus_one_liner and calls
3284 them. */
3286 /* Just a caret. */
3288 static void
3289 test_one_liner_simple_caret ()
3291 test_diagnostic_context dc;
3292 location_t caret = linemap_position_for_column (line_table, 10);
3293 rich_location richloc (line_table, caret);
3294 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3295 ASSERT_STREQ (" foo = bar.field;\n"
3296 " ^\n",
3297 pp_formatted_text (dc.printer));
3300 /* Caret and range. */
3302 static void
3303 test_one_liner_caret_and_range ()
3305 test_diagnostic_context dc;
3306 location_t caret = linemap_position_for_column (line_table, 10);
3307 location_t start = linemap_position_for_column (line_table, 7);
3308 location_t finish = linemap_position_for_column (line_table, 15);
3309 location_t loc = make_location (caret, start, finish);
3310 rich_location richloc (line_table, loc);
3311 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3312 ASSERT_STREQ (" foo = bar.field;\n"
3313 " ~~~^~~~~~\n",
3314 pp_formatted_text (dc.printer));
3317 /* Multiple ranges and carets. */
3319 static void
3320 test_one_liner_multiple_carets_and_ranges ()
3322 test_diagnostic_context dc;
3323 location_t foo
3324 = make_location (linemap_position_for_column (line_table, 2),
3325 linemap_position_for_column (line_table, 1),
3326 linemap_position_for_column (line_table, 3));
3327 dc.m_source_printing.caret_chars[0] = 'A';
3329 location_t bar
3330 = make_location (linemap_position_for_column (line_table, 8),
3331 linemap_position_for_column (line_table, 7),
3332 linemap_position_for_column (line_table, 9));
3333 dc.m_source_printing.caret_chars[1] = 'B';
3335 location_t field
3336 = make_location (linemap_position_for_column (line_table, 13),
3337 linemap_position_for_column (line_table, 11),
3338 linemap_position_for_column (line_table, 15));
3339 dc.m_source_printing.caret_chars[2] = 'C';
3341 rich_location richloc (line_table, foo);
3342 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3343 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3344 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3345 ASSERT_STREQ (" foo = bar.field;\n"
3346 " ~A~ ~B~ ~~C~~\n",
3347 pp_formatted_text (dc.printer));
3350 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3352 static void
3353 test_one_liner_fixit_insert_before ()
3355 test_diagnostic_context dc;
3356 location_t caret = linemap_position_for_column (line_table, 7);
3357 rich_location richloc (line_table, caret);
3358 richloc.add_fixit_insert_before ("&");
3359 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3360 ASSERT_STREQ (" foo = bar.field;\n"
3361 " ^\n"
3362 " &\n",
3363 pp_formatted_text (dc.printer));
3366 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3368 static void
3369 test_one_liner_fixit_insert_after ()
3371 test_diagnostic_context dc;
3372 location_t start = linemap_position_for_column (line_table, 1);
3373 location_t finish = linemap_position_for_column (line_table, 3);
3374 location_t foo = make_location (start, start, finish);
3375 rich_location richloc (line_table, foo);
3376 richloc.add_fixit_insert_after ("[0]");
3377 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3378 ASSERT_STREQ (" foo = bar.field;\n"
3379 " ^~~\n"
3380 " [0]\n",
3381 pp_formatted_text (dc.printer));
3384 /* Removal fix-it hint: removal of the ".field".
3385 Also verify the interaction of pp_set_prefix with rulers and
3386 fix-it hints. */
3388 static void
3389 test_one_liner_fixit_remove ()
3391 location_t start = linemap_position_for_column (line_table, 10);
3392 location_t finish = linemap_position_for_column (line_table, 15);
3393 location_t dot = make_location (start, start, finish);
3394 rich_location richloc (line_table, dot);
3395 richloc.add_fixit_remove ();
3397 /* Normal. */
3399 test_diagnostic_context dc;
3400 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3401 ASSERT_STREQ (" foo = bar.field;\n"
3402 " ^~~~~~\n"
3403 " ------\n",
3404 pp_formatted_text (dc.printer));
3407 /* Test of adding a prefix. */
3409 test_diagnostic_context dc;
3410 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3411 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3412 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3413 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3414 "TEST PREFIX: ^~~~~~\n"
3415 "TEST PREFIX: ------\n",
3416 pp_formatted_text (dc.printer));
3419 /* Normal, with ruler. */
3421 test_diagnostic_context dc;
3422 dc.m_source_printing.show_ruler_p = true;
3423 dc.m_source_printing.max_width = 104;
3424 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3425 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3426 " 1 2 3 4 5 6 7 8 9 0 \n"
3427 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3428 " foo = bar.field;\n"
3429 " ^~~~~~\n"
3430 " ------\n",
3431 pp_formatted_text (dc.printer));
3434 /* Test of adding a prefix, with ruler. */
3436 test_diagnostic_context dc;
3437 dc.m_source_printing.show_ruler_p = true;
3438 dc.m_source_printing.max_width = 50;
3439 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3440 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3441 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3442 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3443 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3444 "TEST PREFIX: foo = bar.field;\n"
3445 "TEST PREFIX: ^~~~~~\n"
3446 "TEST PREFIX: ------\n",
3447 pp_formatted_text (dc.printer));
3450 /* Test of adding a prefix, with ruler and line numbers. */
3452 test_diagnostic_context dc;
3453 dc.m_source_printing.show_ruler_p = true;
3454 dc.m_source_printing.max_width = 50;
3455 dc.m_source_printing.show_line_numbers_p = true;
3456 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3457 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3458 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3459 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3460 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3461 "TEST PREFIX: 1 | foo = bar.field;\n"
3462 "TEST PREFIX: | ^~~~~~\n"
3463 "TEST PREFIX: | ------\n",
3464 pp_formatted_text (dc.printer));
3468 /* Replace fix-it hint: replacing "field" with "m_field". */
3470 static void
3471 test_one_liner_fixit_replace ()
3473 test_diagnostic_context dc;
3474 location_t start = linemap_position_for_column (line_table, 11);
3475 location_t finish = linemap_position_for_column (line_table, 15);
3476 location_t field = make_location (start, start, finish);
3477 rich_location richloc (line_table, field);
3478 richloc.add_fixit_replace ("m_field");
3479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3480 ASSERT_STREQ (" foo = bar.field;\n"
3481 " ^~~~~\n"
3482 " m_field\n",
3483 pp_formatted_text (dc.printer));
3486 /* Replace fix-it hint: replacing "field" with "m_field",
3487 but where the caret was elsewhere. */
3489 static void
3490 test_one_liner_fixit_replace_non_equal_range ()
3492 test_diagnostic_context dc;
3493 location_t equals = linemap_position_for_column (line_table, 5);
3494 location_t start = linemap_position_for_column (line_table, 11);
3495 location_t finish = linemap_position_for_column (line_table, 15);
3496 rich_location richloc (line_table, equals);
3497 source_range range;
3498 range.m_start = start;
3499 range.m_finish = finish;
3500 richloc.add_fixit_replace (range, "m_field");
3501 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3502 /* The replacement range is not indicated in the annotation line, so
3503 it should be indicated via an additional underline. */
3504 ASSERT_STREQ (" foo = bar.field;\n"
3505 " ^\n"
3506 " -----\n"
3507 " m_field\n",
3508 pp_formatted_text (dc.printer));
3511 /* Replace fix-it hint: replacing "field" with "m_field",
3512 where the caret was elsewhere, but where a secondary range
3513 exactly covers "field". */
3515 static void
3516 test_one_liner_fixit_replace_equal_secondary_range ()
3518 test_diagnostic_context dc;
3519 location_t equals = linemap_position_for_column (line_table, 5);
3520 location_t start = linemap_position_for_column (line_table, 11);
3521 location_t finish = linemap_position_for_column (line_table, 15);
3522 rich_location richloc (line_table, equals);
3523 location_t field = make_location (start, start, finish);
3524 richloc.add_range (field);
3525 richloc.add_fixit_replace (field, "m_field");
3526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3527 /* The replacement range is indicated in the annotation line,
3528 so it shouldn't be indicated via an additional underline. */
3529 ASSERT_STREQ (" foo = bar.field;\n"
3530 " ^ ~~~~~\n"
3531 " m_field\n",
3532 pp_formatted_text (dc.printer));
3535 /* Verify that we can use ad-hoc locations when adding fixits to a
3536 rich_location. */
3538 static void
3539 test_one_liner_fixit_validation_adhoc_locations ()
3541 /* Generate a range that's too long to be packed, so must
3542 be stored as an ad-hoc location (given the defaults
3543 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3544 const location_t c7 = linemap_position_for_column (line_table, 7);
3545 const location_t c47 = linemap_position_for_column (line_table, 47);
3546 const location_t loc = make_location (c7, c7, c47);
3548 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3549 return;
3551 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3553 /* Insert. */
3555 rich_location richloc (line_table, loc);
3556 richloc.add_fixit_insert_before (loc, "test");
3557 /* It should not have been discarded by the validator. */
3558 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3560 test_diagnostic_context dc;
3561 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3562 ASSERT_STREQ (" foo = bar.field;\n"
3563 " ^~~~~~~~~~ \n"
3564 " test\n",
3565 pp_formatted_text (dc.printer));
3568 /* Remove. */
3570 rich_location richloc (line_table, loc);
3571 source_range range = source_range::from_locations (loc, c47);
3572 richloc.add_fixit_remove (range);
3573 /* It should not have been discarded by the validator. */
3574 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3576 test_diagnostic_context dc;
3577 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3578 ASSERT_STREQ (" foo = bar.field;\n"
3579 " ^~~~~~~~~~ \n"
3580 " -----------------------------------------\n",
3581 pp_formatted_text (dc.printer));
3584 /* Replace. */
3586 rich_location richloc (line_table, loc);
3587 source_range range = source_range::from_locations (loc, c47);
3588 richloc.add_fixit_replace (range, "test");
3589 /* It should not have been discarded by the validator. */
3590 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3592 test_diagnostic_context dc;
3593 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3594 ASSERT_STREQ (" foo = bar.field;\n"
3595 " ^~~~~~~~~~ \n"
3596 " test\n",
3597 pp_formatted_text (dc.printer));
3601 /* Test of consolidating insertions at the same location. */
3603 static void
3604 test_one_liner_many_fixits_1 ()
3606 test_diagnostic_context dc;
3607 location_t equals = linemap_position_for_column (line_table, 5);
3608 rich_location richloc (line_table, equals);
3609 for (int i = 0; i < 19; i++)
3610 richloc.add_fixit_insert_before ("a");
3611 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3612 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3613 ASSERT_STREQ (" foo = bar.field;\n"
3614 " ^\n"
3615 " aaaaaaaaaaaaaaaaaaa\n",
3616 pp_formatted_text (dc.printer));
3619 /* Ensure that we can add an arbitrary number of fix-it hints to a
3620 rich_location, even if they are not consolidated. */
3622 static void
3623 test_one_liner_many_fixits_2 ()
3625 test_diagnostic_context dc;
3626 location_t equals = linemap_position_for_column (line_table, 5);
3627 rich_location richloc (line_table, equals);
3628 for (int i = 0; i < 19; i++)
3630 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3631 richloc.add_fixit_insert_before (loc, "a");
3633 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3634 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3635 ASSERT_STREQ (" foo = bar.field;\n"
3636 " ^\n"
3637 " a a a a a a a a a a a a a a a a a a a\n",
3638 pp_formatted_text (dc.printer));
3641 /* Test of labeling the ranges within a rich_location. */
3643 static void
3644 test_one_liner_labels ()
3646 location_t foo
3647 = make_location (linemap_position_for_column (line_table, 1),
3648 linemap_position_for_column (line_table, 1),
3649 linemap_position_for_column (line_table, 3));
3650 location_t bar
3651 = make_location (linemap_position_for_column (line_table, 7),
3652 linemap_position_for_column (line_table, 7),
3653 linemap_position_for_column (line_table, 9));
3654 location_t field
3655 = make_location (linemap_position_for_column (line_table, 11),
3656 linemap_position_for_column (line_table, 11),
3657 linemap_position_for_column (line_table, 15));
3659 /* Example where all the labels fit on one line. */
3661 text_range_label label0 ("0");
3662 text_range_label label1 ("1");
3663 text_range_label label2 ("2");
3664 gcc_rich_location richloc (foo, &label0);
3665 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3666 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3669 test_diagnostic_context dc;
3670 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3671 ASSERT_STREQ (" foo = bar.field;\n"
3672 " ^~~ ~~~ ~~~~~\n"
3673 " | | |\n"
3674 " 0 1 2\n",
3675 pp_formatted_text (dc.printer));
3678 /* Verify that we can disable label-printing. */
3680 test_diagnostic_context dc;
3681 dc.m_source_printing.show_labels_p = false;
3682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3683 ASSERT_STREQ (" foo = bar.field;\n"
3684 " ^~~ ~~~ ~~~~~\n",
3685 pp_formatted_text (dc.printer));
3689 /* Example where the labels need extra lines. */
3691 text_range_label label0 ("label 0");
3692 text_range_label label1 ("label 1");
3693 text_range_label label2 ("label 2");
3694 gcc_rich_location richloc (foo, &label0);
3695 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3696 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3698 test_diagnostic_context dc;
3699 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3700 ASSERT_STREQ (" foo = bar.field;\n"
3701 " ^~~ ~~~ ~~~~~\n"
3702 " | | |\n"
3703 " | | label 2\n"
3704 " | label 1\n"
3705 " label 0\n",
3706 pp_formatted_text (dc.printer));
3709 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3710 but label 1 just touches label 2. */
3712 text_range_label label0 ("aaaaa");
3713 text_range_label label1 ("bbbb");
3714 text_range_label label2 ("c");
3715 gcc_rich_location richloc (foo, &label0);
3716 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3717 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3719 test_diagnostic_context dc;
3720 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3721 ASSERT_STREQ (" foo = bar.field;\n"
3722 " ^~~ ~~~ ~~~~~\n"
3723 " | | |\n"
3724 " | | c\n"
3725 " aaaaa bbbb\n",
3726 pp_formatted_text (dc.printer));
3729 /* Example of out-of-order ranges (thus requiring a sort). */
3731 text_range_label label0 ("0");
3732 text_range_label label1 ("1");
3733 text_range_label label2 ("2");
3734 gcc_rich_location richloc (field, &label0);
3735 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3736 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3738 test_diagnostic_context dc;
3739 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3740 ASSERT_STREQ (" foo = bar.field;\n"
3741 " ~~~ ~~~ ^~~~~\n"
3742 " | | |\n"
3743 " 2 1 0\n",
3744 pp_formatted_text (dc.printer));
3747 /* Ensure we don't ICE if multiple ranges with labels are on
3748 the same point. */
3750 text_range_label label0 ("label 0");
3751 text_range_label label1 ("label 1");
3752 text_range_label label2 ("label 2");
3753 gcc_rich_location richloc (bar, &label0);
3754 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3755 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3757 test_diagnostic_context dc;
3758 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3759 ASSERT_STREQ (" foo = bar.field;\n"
3760 " ^~~\n"
3761 " |\n"
3762 " label 0\n"
3763 " label 1\n"
3764 " label 2\n",
3765 pp_formatted_text (dc.printer));
3768 /* Example of out-of-order ranges (thus requiring a sort), where
3769 they overlap, and there are multiple ranges on the same point. */
3771 text_range_label label_0a ("label 0a");
3772 text_range_label label_1a ("label 1a");
3773 text_range_label label_2a ("label 2a");
3774 text_range_label label_0b ("label 0b");
3775 text_range_label label_1b ("label 1b");
3776 text_range_label label_2b ("label 2b");
3777 text_range_label label_0c ("label 0c");
3778 text_range_label label_1c ("label 1c");
3779 text_range_label label_2c ("label 2c");
3780 gcc_rich_location richloc (field, &label_0a);
3781 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3782 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3784 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3785 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3786 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3788 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3789 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3790 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3792 test_diagnostic_context dc;
3793 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3794 ASSERT_STREQ (" foo = bar.field;\n"
3795 " ~~~ ~~~ ^~~~~\n"
3796 " | | |\n"
3797 " | | label 0a\n"
3798 " | | label 0b\n"
3799 " | | label 0c\n"
3800 " | label 1a\n"
3801 " | label 1b\n"
3802 " | label 1c\n"
3803 " label 2a\n"
3804 " label 2b\n"
3805 " label 2c\n",
3806 pp_formatted_text (dc.printer));
3809 /* Verify that a NULL result from range_label::get_text is
3810 handled gracefully. */
3812 text_range_label label (NULL);
3813 gcc_rich_location richloc (bar, &label);
3815 test_diagnostic_context dc;
3816 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3817 ASSERT_STREQ (" foo = bar.field;\n"
3818 " ^~~\n",
3819 pp_formatted_text (dc.printer));
3822 /* TODO: example of formatted printing (needs to be in
3823 gcc-rich-location.cc due to Makefile.in issues). */
3826 /* Run the various one-liner tests. */
3828 static void
3829 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3831 /* Create a tempfile and write some text to it.
3832 ....................0000000001111111.
3833 ....................1234567890123456. */
3834 const char *content = "foo = bar.field;\n";
3835 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3836 line_table_test ltt (case_);
3838 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3840 location_t line_end = linemap_position_for_column (line_table, 16);
3842 /* Don't attempt to run the tests if column data might be unavailable. */
3843 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3844 return;
3846 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3847 ASSERT_EQ (1, LOCATION_LINE (line_end));
3848 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3850 test_one_liner_simple_caret ();
3851 test_one_liner_caret_and_range ();
3852 test_one_liner_multiple_carets_and_ranges ();
3853 test_one_liner_fixit_insert_before ();
3854 test_one_liner_fixit_insert_after ();
3855 test_one_liner_fixit_remove ();
3856 test_one_liner_fixit_replace ();
3857 test_one_liner_fixit_replace_non_equal_range ();
3858 test_one_liner_fixit_replace_equal_secondary_range ();
3859 test_one_liner_fixit_validation_adhoc_locations ();
3860 test_one_liner_many_fixits_1 ();
3861 test_one_liner_many_fixits_2 ();
3862 test_one_liner_labels ();
3865 /* Version of all one-liner tests exercising multibyte awareness. For
3866 simplicity we stick to using two multibyte characters in the test, U+1F602
3867 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3868 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3869 below asserts would be easier to read if we used UTF-8 directly in the
3870 string constants, but it seems better not to demand the host compiler
3871 support this, when it isn't otherwise necessary. Instead, whenever an
3872 extended character appears in a string, we put a line break after it so that
3873 all succeeding characters can appear visually at the correct display column.
3875 All of these work on the following 1-line source file:
3877 .0000000001111111111222222 display
3878 .1234567890123456789012345 columns
3879 "SS_foo = P_bar.SS_fieldP;\n"
3880 .0000000111111111222222223 byte
3881 .1356789012456789134567891 columns
3883 which is set up by test_diagnostic_show_locus_one_liner and calls
3884 them. Here SS represents the two display columns for the U+1F602 emoji and
3885 P represents the one display column for the U+03C0 pi symbol. */
3887 /* Just a caret. */
3889 static void
3890 test_one_liner_simple_caret_utf8 ()
3892 test_diagnostic_context dc;
3893 location_t caret = linemap_position_for_column (line_table, 18);
3894 rich_location richloc (line_table, caret);
3895 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3896 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3897 "_foo = \xcf\x80"
3898 "_bar.\xf0\x9f\x98\x82"
3899 "_field\xcf\x80"
3900 ";\n"
3901 " ^\n",
3902 pp_formatted_text (dc.printer));
3905 /* Caret and range. */
3906 static void
3907 test_one_liner_caret_and_range_utf8 ()
3909 test_diagnostic_context dc;
3910 location_t caret = linemap_position_for_column (line_table, 18);
3911 location_t start = linemap_position_for_column (line_table, 12);
3912 location_t finish = linemap_position_for_column (line_table, 30);
3913 location_t loc = make_location (caret, start, finish);
3914 rich_location richloc (line_table, loc);
3915 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3916 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3917 "_foo = \xcf\x80"
3918 "_bar.\xf0\x9f\x98\x82"
3919 "_field\xcf\x80"
3920 ";\n"
3921 " ~~~~~^~~~~~~~~~\n",
3922 pp_formatted_text (dc.printer));
3925 /* Multiple ranges and carets. */
3927 static void
3928 test_one_liner_multiple_carets_and_ranges_utf8 ()
3930 test_diagnostic_context dc;
3931 location_t foo
3932 = make_location (linemap_position_for_column (line_table, 7),
3933 linemap_position_for_column (line_table, 1),
3934 linemap_position_for_column (line_table, 8));
3935 dc.m_source_printing.caret_chars[0] = 'A';
3937 location_t bar
3938 = make_location (linemap_position_for_column (line_table, 16),
3939 linemap_position_for_column (line_table, 12),
3940 linemap_position_for_column (line_table, 17));
3941 dc.m_source_printing.caret_chars[1] = 'B';
3943 location_t field
3944 = make_location (linemap_position_for_column (line_table, 26),
3945 linemap_position_for_column (line_table, 19),
3946 linemap_position_for_column (line_table, 30));
3947 dc.m_source_printing.caret_chars[2] = 'C';
3948 rich_location richloc (line_table, foo);
3949 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3950 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3951 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3952 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3953 "_foo = \xcf\x80"
3954 "_bar.\xf0\x9f\x98\x82"
3955 "_field\xcf\x80"
3956 ";\n"
3957 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3958 pp_formatted_text (dc.printer));
3961 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3963 static void
3964 test_one_liner_fixit_insert_before_utf8 ()
3966 test_diagnostic_context dc;
3967 location_t caret = linemap_position_for_column (line_table, 12);
3968 rich_location richloc (line_table, caret);
3969 richloc.add_fixit_insert_before ("&");
3970 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3971 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3972 "_foo = \xcf\x80"
3973 "_bar.\xf0\x9f\x98\x82"
3974 "_field\xcf\x80"
3975 ";\n"
3976 " ^\n"
3977 " &\n",
3978 pp_formatted_text (dc.printer));
3981 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3983 static void
3984 test_one_liner_fixit_insert_after_utf8 ()
3986 test_diagnostic_context dc;
3987 location_t start = linemap_position_for_column (line_table, 1);
3988 location_t finish = linemap_position_for_column (line_table, 8);
3989 location_t foo = make_location (start, start, finish);
3990 rich_location richloc (line_table, foo);
3991 richloc.add_fixit_insert_after ("[0]");
3992 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3993 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3994 "_foo = \xcf\x80"
3995 "_bar.\xf0\x9f\x98\x82"
3996 "_field\xcf\x80"
3997 ";\n"
3998 " ^~~~~~\n"
3999 " [0]\n",
4000 pp_formatted_text (dc.printer));
4003 /* Removal fix-it hint: removal of the ".SS_fieldP". */
4005 static void
4006 test_one_liner_fixit_remove_utf8 ()
4008 test_diagnostic_context dc;
4009 location_t start = linemap_position_for_column (line_table, 18);
4010 location_t finish = linemap_position_for_column (line_table, 30);
4011 location_t dot = make_location (start, start, finish);
4012 rich_location richloc (line_table, dot);
4013 richloc.add_fixit_remove ();
4014 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4015 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4016 "_foo = \xcf\x80"
4017 "_bar.\xf0\x9f\x98\x82"
4018 "_field\xcf\x80"
4019 ";\n"
4020 " ^~~~~~~~~~\n"
4021 " ----------\n",
4022 pp_formatted_text (dc.printer));
4025 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4027 static void
4028 test_one_liner_fixit_replace_utf8 ()
4030 test_diagnostic_context dc;
4031 location_t start = linemap_position_for_column (line_table, 19);
4032 location_t finish = linemap_position_for_column (line_table, 30);
4033 location_t field = make_location (start, start, finish);
4034 rich_location richloc (line_table, field);
4035 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4036 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4037 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4038 "_foo = \xcf\x80"
4039 "_bar.\xf0\x9f\x98\x82"
4040 "_field\xcf\x80"
4041 ";\n"
4042 " ^~~~~~~~~\n"
4043 " m_\xf0\x9f\x98\x82"
4044 "_field\xcf\x80\n",
4045 pp_formatted_text (dc.printer));
4048 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4049 but where the caret was elsewhere. */
4051 static void
4052 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4054 test_diagnostic_context dc;
4055 location_t equals = linemap_position_for_column (line_table, 10);
4056 location_t start = linemap_position_for_column (line_table, 19);
4057 location_t finish = linemap_position_for_column (line_table, 30);
4058 rich_location richloc (line_table, equals);
4059 source_range range;
4060 range.m_start = start;
4061 range.m_finish = finish;
4062 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4063 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4064 /* The replacement range is not indicated in the annotation line, so
4065 it should be indicated via an additional underline. */
4066 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4067 "_foo = \xcf\x80"
4068 "_bar.\xf0\x9f\x98\x82"
4069 "_field\xcf\x80"
4070 ";\n"
4071 " ^\n"
4072 " ---------\n"
4073 " m_\xf0\x9f\x98\x82"
4074 "_field\xcf\x80\n",
4075 pp_formatted_text (dc.printer));
4078 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4079 where the caret was elsewhere, but where a secondary range
4080 exactly covers "field". */
4082 static void
4083 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4085 test_diagnostic_context dc;
4086 location_t equals = linemap_position_for_column (line_table, 10);
4087 location_t start = linemap_position_for_column (line_table, 19);
4088 location_t finish = linemap_position_for_column (line_table, 30);
4089 rich_location richloc (line_table, equals);
4090 location_t field = make_location (start, start, finish);
4091 richloc.add_range (field);
4092 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4093 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4094 /* The replacement range is indicated in the annotation line,
4095 so it shouldn't be indicated via an additional underline. */
4096 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4097 "_foo = \xcf\x80"
4098 "_bar.\xf0\x9f\x98\x82"
4099 "_field\xcf\x80"
4100 ";\n"
4101 " ^ ~~~~~~~~~\n"
4102 " m_\xf0\x9f\x98\x82"
4103 "_field\xcf\x80\n",
4104 pp_formatted_text (dc.printer));
4107 /* Verify that we can use ad-hoc locations when adding fixits to a
4108 rich_location. */
4110 static void
4111 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4113 /* Generate a range that's too long to be packed, so must
4114 be stored as an ad-hoc location (given the defaults
4115 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4116 const location_t c12 = linemap_position_for_column (line_table, 12);
4117 const location_t c52 = linemap_position_for_column (line_table, 52);
4118 const location_t loc = make_location (c12, c12, c52);
4120 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4121 return;
4123 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4125 /* Insert. */
4127 rich_location richloc (line_table, loc);
4128 richloc.add_fixit_insert_before (loc, "test");
4129 /* It should not have been discarded by the validator. */
4130 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4132 test_diagnostic_context dc;
4133 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4134 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4135 "_foo = \xcf\x80"
4136 "_bar.\xf0\x9f\x98\x82"
4137 "_field\xcf\x80"
4138 ";\n"
4139 " ^~~~~~~~~~~~~~~~ \n"
4140 " test\n",
4141 pp_formatted_text (dc.printer));
4144 /* Remove. */
4146 rich_location richloc (line_table, loc);
4147 source_range range = source_range::from_locations (loc, c52);
4148 richloc.add_fixit_remove (range);
4149 /* It should not have been discarded by the validator. */
4150 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4152 test_diagnostic_context dc;
4153 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4154 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4155 "_foo = \xcf\x80"
4156 "_bar.\xf0\x9f\x98\x82"
4157 "_field\xcf\x80"
4158 ";\n"
4159 " ^~~~~~~~~~~~~~~~ \n"
4160 " -------------------------------------\n",
4161 pp_formatted_text (dc.printer));
4164 /* Replace. */
4166 rich_location richloc (line_table, loc);
4167 source_range range = source_range::from_locations (loc, c52);
4168 richloc.add_fixit_replace (range, "test");
4169 /* It should not have been discarded by the validator. */
4170 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4172 test_diagnostic_context dc;
4173 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4174 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4175 "_foo = \xcf\x80"
4176 "_bar.\xf0\x9f\x98\x82"
4177 "_field\xcf\x80"
4178 ";\n"
4179 " ^~~~~~~~~~~~~~~~ \n"
4180 " test\n",
4181 pp_formatted_text (dc.printer));
4185 /* Test of consolidating insertions at the same location. */
4187 static void
4188 test_one_liner_many_fixits_1_utf8 ()
4190 test_diagnostic_context dc;
4191 location_t equals = linemap_position_for_column (line_table, 10);
4192 rich_location richloc (line_table, equals);
4193 for (int i = 0; i < 19; i++)
4194 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4195 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4196 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4197 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4198 "_foo = \xcf\x80"
4199 "_bar.\xf0\x9f\x98\x82"
4200 "_field\xcf\x80"
4201 ";\n"
4202 " ^\n"
4203 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4204 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4205 pp_formatted_text (dc.printer));
4208 /* Ensure that we can add an arbitrary number of fix-it hints to a
4209 rich_location, even if they are not consolidated. */
4211 static void
4212 test_one_liner_many_fixits_2_utf8 ()
4214 test_diagnostic_context dc;
4215 location_t equals = linemap_position_for_column (line_table, 10);
4216 rich_location richloc (line_table, equals);
4217 const int nlocs = 19;
4218 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4219 34, 36, 38, 40, 42, 44};
4220 for (int i = 0; i != nlocs; ++i)
4222 location_t loc = linemap_position_for_column (line_table, locs[i]);
4223 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4226 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4227 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4228 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4229 "_foo = \xcf\x80"
4230 "_bar.\xf0\x9f\x98\x82"
4231 "_field\xcf\x80"
4232 ";\n"
4233 " ^\n"
4234 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4235 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4236 pp_formatted_text (dc.printer));
4239 /* Test of labeling the ranges within a rich_location. */
4241 static void
4242 test_one_liner_labels_utf8 ()
4244 location_t foo
4245 = make_location (linemap_position_for_column (line_table, 1),
4246 linemap_position_for_column (line_table, 1),
4247 linemap_position_for_column (line_table, 8));
4248 location_t bar
4249 = make_location (linemap_position_for_column (line_table, 12),
4250 linemap_position_for_column (line_table, 12),
4251 linemap_position_for_column (line_table, 17));
4252 location_t field
4253 = make_location (linemap_position_for_column (line_table, 19),
4254 linemap_position_for_column (line_table, 19),
4255 linemap_position_for_column (line_table, 30));
4257 /* Example where all the labels fit on one line. */
4259 /* These three labels contain multibyte characters such that their byte
4260 lengths are respectively (12, 10, 18), but their display widths are only
4261 (6, 5, 9). All three fit on the line when considering the display
4262 widths, but not when considering the byte widths, so verify that we do
4263 indeed put them all on one line. */
4264 text_range_label label0
4265 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4266 text_range_label label1
4267 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4268 text_range_label label2
4269 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4270 "\xcf\x80");
4271 gcc_rich_location richloc (foo, &label0);
4272 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4273 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4276 test_diagnostic_context dc;
4277 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4278 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4279 "_foo = \xcf\x80"
4280 "_bar.\xf0\x9f\x98\x82"
4281 "_field\xcf\x80"
4282 ";\n"
4283 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4284 " | | |\n"
4285 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4286 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4287 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4288 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4289 pp_formatted_text (dc.printer));
4294 /* Example where the labels need extra lines. */
4296 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4297 text_range_label label1 ("label 1\xcf\x80");
4298 text_range_label label2 ("label 2\xcf\x80");
4299 gcc_rich_location richloc (foo, &label0);
4300 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4301 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4303 test_diagnostic_context dc;
4304 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4306 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4307 "_foo = \xcf\x80"
4308 "_bar.\xf0\x9f\x98\x82"
4309 "_field\xcf\x80"
4310 ";\n"
4311 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4312 " | | |\n"
4313 " | | label 2\xcf\x80\n"
4314 " | label 1\xcf\x80\n"
4315 " label 0\xf0\x9f\x98\x82\n",
4316 pp_formatted_text (dc.printer));
4319 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4320 but label 1 just touches label 2. */
4322 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4323 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4324 text_range_label label2 ("c");
4325 gcc_rich_location richloc (foo, &label0);
4326 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4327 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4329 test_diagnostic_context dc;
4330 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4331 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4332 "_foo = \xcf\x80"
4333 "_bar.\xf0\x9f\x98\x82"
4334 "_field\xcf\x80"
4335 ";\n"
4336 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4337 " | | |\n"
4338 " | | c\n"
4339 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4340 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4341 pp_formatted_text (dc.printer));
4344 /* Example of escaping the source lines. */
4346 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4347 text_range_label label1 ("label 1\xcf\x80");
4348 text_range_label label2 ("label 2\xcf\x80");
4349 gcc_rich_location richloc (foo, &label0);
4350 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4351 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4352 richloc.set_escape_on_output (true);
4355 test_diagnostic_context dc;
4356 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
4357 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4358 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4359 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4360 " | | |\n"
4361 " | | label 2\xcf\x80\n"
4362 " | label 1\xcf\x80\n"
4363 " label 0\xf0\x9f\x98\x82\n",
4364 pp_formatted_text (dc.printer));
4367 test_diagnostic_context dc;
4368 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
4369 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4370 ASSERT_STREQ
4371 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4372 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4373 " | | |\n"
4374 " | | label 2\xcf\x80\n"
4375 " | label 1\xcf\x80\n"
4376 " label 0\xf0\x9f\x98\x82\n",
4377 pp_formatted_text (dc.printer));
4382 /* Make sure that colorization codes don't interrupt a multibyte
4383 sequence, which would corrupt it. */
4384 static void
4385 test_one_liner_colorized_utf8 ()
4387 test_diagnostic_context dc;
4388 dc.m_source_printing.colorize_source_p = true;
4389 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4390 const location_t pi = linemap_position_for_column (line_table, 12);
4391 rich_location richloc (line_table, pi);
4392 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4394 /* In order to avoid having the test depend on exactly how the colorization
4395 was effected, just confirm there are two pi characters in the output. */
4396 const char *result = pp_formatted_text (dc.printer);
4397 const char *null_term = result + strlen (result);
4398 const char *first_pi = strstr (result, "\xcf\x80");
4399 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4400 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4403 /* Run the various one-liner tests. */
4405 static void
4406 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4408 /* Create a tempfile and write some text to it. */
4409 const char *content
4410 /* Display columns.
4411 0000000000000000000000011111111111111111111111111111112222222222222
4412 1111111122222222345678900000000123456666666677777777890123444444445 */
4413 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4414 /* 0000000000000000000001111111111111111111222222222222222222222233333
4415 1111222233334444567890122223333456789999000011112222345678999900001
4416 Byte columns. */
4417 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4418 file_cache fc;
4419 line_table_test ltt (case_);
4421 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4423 location_t line_end = linemap_position_for_column (line_table, 31);
4425 /* Don't attempt to run the tests if column data might be unavailable. */
4426 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4427 return;
4429 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4430 ASSERT_EQ (1, LOCATION_LINE (line_end));
4431 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4433 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
4434 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4435 def_policy ()));
4436 ASSERT_EQ (25, location_compute_display_column (fc,
4437 expand_location (line_end),
4438 def_policy ()));
4440 test_one_liner_simple_caret_utf8 ();
4441 test_one_liner_caret_and_range_utf8 ();
4442 test_one_liner_multiple_carets_and_ranges_utf8 ();
4443 test_one_liner_fixit_insert_before_utf8 ();
4444 test_one_liner_fixit_insert_after_utf8 ();
4445 test_one_liner_fixit_remove_utf8 ();
4446 test_one_liner_fixit_replace_utf8 ();
4447 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4448 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4449 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4450 test_one_liner_many_fixits_1_utf8 ();
4451 test_one_liner_many_fixits_2_utf8 ();
4452 test_one_liner_labels_utf8 ();
4453 test_one_liner_colorized_utf8 ();
4456 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4458 static void
4459 test_add_location_if_nearby (const line_table_case &case_)
4461 /* Create a tempfile and write some text to it.
4462 ...000000000111111111122222222223333333333.
4463 ...123456789012345678901234567890123456789. */
4464 const char *content
4465 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4466 "struct different_line\n" /* line 2. */
4467 "{\n" /* line 3. */
4468 " double x;\n" /* line 4. */
4469 " double y;\n" /* line 5. */
4470 ";\n"); /* line 6. */
4471 temp_source_file tmp (SELFTEST_LOCATION, ".c", content,
4473 /* gcc_rich_location::add_location_if_nearby implicitly
4474 uses global_dc's file_cache, so we need to evict
4475 tmp when we're done. */
4476 &global_dc->get_file_cache ());
4477 line_table_test ltt (case_);
4479 const line_map_ordinary *ord_map
4480 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4481 tmp.get_filename (), 0));
4483 linemap_line_start (line_table, 1, 100);
4485 const location_t final_line_end
4486 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4488 /* Don't attempt to run the tests if column data might be unavailable. */
4489 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4490 return;
4492 /* Test of add_location_if_nearby on the same line as the
4493 primary location. */
4495 const location_t missing_close_brace_1_39
4496 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4497 const location_t matching_open_brace_1_18
4498 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4499 gcc_rich_location richloc (missing_close_brace_1_39);
4500 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4501 ASSERT_TRUE (added);
4502 ASSERT_EQ (2, richloc.get_num_locations ());
4503 test_diagnostic_context dc;
4504 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4505 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4506 " ~ ^\n",
4507 pp_formatted_text (dc.printer));
4510 /* Test of add_location_if_nearby on a different line to the
4511 primary location. */
4513 const location_t missing_close_brace_6_1
4514 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4515 const location_t matching_open_brace_3_1
4516 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4517 gcc_rich_location richloc (missing_close_brace_6_1);
4518 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4519 ASSERT_FALSE (added);
4520 ASSERT_EQ (1, richloc.get_num_locations ());
4524 /* Verify that we print fixits even if they only affect lines
4525 outside those covered by the ranges in the rich_location. */
4527 static void
4528 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4530 /* Create a tempfile and write some text to it.
4531 ...000000000111111111122222222223333333333.
4532 ...123456789012345678901234567890123456789. */
4533 const char *content
4534 = ("struct point { double x; double y; };\n" /* line 1. */
4535 "struct point origin = {x: 0.0,\n" /* line 2. */
4536 " y\n" /* line 3. */
4537 "\n" /* line 4. */
4538 "\n" /* line 5. */
4539 " : 0.0};\n"); /* line 6. */
4540 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4541 line_table_test ltt (case_);
4543 const line_map_ordinary *ord_map
4544 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4545 tmp.get_filename (), 0));
4547 linemap_line_start (line_table, 1, 100);
4549 const location_t final_line_end
4550 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4552 /* Don't attempt to run the tests if column data might be unavailable. */
4553 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4554 return;
4556 /* A pair of tests for modernizing the initializers to C99-style. */
4558 /* The one-liner case (line 2). */
4560 test_diagnostic_context dc;
4561 const location_t x
4562 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4563 const location_t colon
4564 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4565 rich_location richloc (line_table, colon);
4566 richloc.add_fixit_insert_before (x, ".");
4567 richloc.add_fixit_replace (colon, "=");
4568 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4569 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4570 " ^\n"
4571 " .=\n",
4572 pp_formatted_text (dc.printer));
4575 /* The multiline case. The caret for the rich_location is on line 6;
4576 verify that insertion fixit on line 3 is still printed (and that
4577 span starts are printed due to the gap between the span at line 3
4578 and that at line 6). */
4580 test_diagnostic_context dc;
4581 const location_t y
4582 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4583 const location_t colon
4584 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4585 rich_location richloc (line_table, colon);
4586 richloc.add_fixit_insert_before (y, ".");
4587 richloc.add_fixit_replace (colon, "=");
4588 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4589 ASSERT_STREQ ("FILENAME:3:24:\n"
4590 " y\n"
4591 " .\n"
4592 "FILENAME:6:25:\n"
4593 " : 0.0};\n"
4594 " ^\n"
4595 " =\n",
4596 pp_formatted_text (dc.printer));
4599 /* As above, but verify the behavior of multiple line spans
4600 with line-numbering enabled. */
4602 const location_t y
4603 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4604 const location_t colon
4605 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4606 rich_location richloc (line_table, colon);
4607 richloc.add_fixit_insert_before (y, ".");
4608 richloc.add_fixit_replace (colon, "=");
4609 test_diagnostic_context dc;
4610 dc.m_source_printing.show_line_numbers_p = true;
4611 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4612 ASSERT_STREQ (" 3 | y\n"
4613 " | .\n"
4614 "......\n"
4615 " 6 | : 0.0};\n"
4616 " | ^\n"
4617 " | =\n",
4618 pp_formatted_text (dc.printer));
4623 /* Verify that fix-it hints are appropriately consolidated.
4625 If any fix-it hints in a rich_location involve locations beyond
4626 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4627 the fix-it as a whole, so there should be none.
4629 Otherwise, verify that consecutive "replace" and "remove" fix-its
4630 are merged, and that other fix-its remain separate. */
4632 static void
4633 test_fixit_consolidation (const line_table_case &case_)
4635 line_table_test ltt (case_);
4637 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4639 const location_t c10 = linemap_position_for_column (line_table, 10);
4640 const location_t c15 = linemap_position_for_column (line_table, 15);
4641 const location_t c16 = linemap_position_for_column (line_table, 16);
4642 const location_t c17 = linemap_position_for_column (line_table, 17);
4643 const location_t c20 = linemap_position_for_column (line_table, 20);
4644 const location_t c21 = linemap_position_for_column (line_table, 21);
4645 const location_t caret = c10;
4647 /* Insert + insert. */
4649 rich_location richloc (line_table, caret);
4650 richloc.add_fixit_insert_before (c10, "foo");
4651 richloc.add_fixit_insert_before (c15, "bar");
4653 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4654 /* Bogus column info for 2nd fixit, so no fixits. */
4655 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4656 else
4657 /* They should not have been merged. */
4658 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4661 /* Insert + replace. */
4663 rich_location richloc (line_table, caret);
4664 richloc.add_fixit_insert_before (c10, "foo");
4665 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4666 "bar");
4668 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4669 /* Bogus column info for 2nd fixit, so no fixits. */
4670 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4671 else
4672 /* They should not have been merged. */
4673 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4676 /* Replace + non-consecutive insert. */
4678 rich_location richloc (line_table, caret);
4679 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4680 "bar");
4681 richloc.add_fixit_insert_before (c17, "foo");
4683 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4684 /* Bogus column info for 2nd fixit, so no fixits. */
4685 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4686 else
4687 /* They should not have been merged. */
4688 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4691 /* Replace + non-consecutive replace. */
4693 rich_location richloc (line_table, caret);
4694 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4695 "foo");
4696 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4697 "bar");
4699 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4700 /* Bogus column info for 2nd fixit, so no fixits. */
4701 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4702 else
4703 /* They should not have been merged. */
4704 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4707 /* Replace + consecutive replace. */
4709 rich_location richloc (line_table, caret);
4710 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4711 "foo");
4712 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4713 "bar");
4715 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4716 /* Bogus column info for 2nd fixit, so no fixits. */
4717 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4718 else
4720 /* They should have been merged into a single "replace". */
4721 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4722 const fixit_hint *hint = richloc.get_fixit_hint (0);
4723 ASSERT_STREQ ("foobar", hint->get_string ());
4724 ASSERT_EQ (c10, hint->get_start_loc ());
4725 ASSERT_EQ (c21, hint->get_next_loc ());
4729 /* Replace + consecutive removal. */
4731 rich_location richloc (line_table, caret);
4732 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4733 "foo");
4734 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4736 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4737 /* Bogus column info for 2nd fixit, so no fixits. */
4738 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4739 else
4741 /* They should have been merged into a single replace, with the
4742 range extended to cover that of the removal. */
4743 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4744 const fixit_hint *hint = richloc.get_fixit_hint (0);
4745 ASSERT_STREQ ("foo", hint->get_string ());
4746 ASSERT_EQ (c10, hint->get_start_loc ());
4747 ASSERT_EQ (c21, hint->get_next_loc ());
4751 /* Consecutive removals. */
4753 rich_location richloc (line_table, caret);
4754 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4755 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4757 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4758 /* Bogus column info for 2nd fixit, so no fixits. */
4759 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4760 else
4762 /* They should have been merged into a single "replace-with-empty". */
4763 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4764 const fixit_hint *hint = richloc.get_fixit_hint (0);
4765 ASSERT_STREQ ("", hint->get_string ());
4766 ASSERT_EQ (c10, hint->get_start_loc ());
4767 ASSERT_EQ (c21, hint->get_next_loc ());
4772 /* Verify that the line_corrections machinery correctly prints
4773 overlapping fixit-hints. */
4775 static void
4776 test_overlapped_fixit_printing (const line_table_case &case_)
4778 /* Create a tempfile and write some text to it.
4779 ...000000000111111111122222222223333333333.
4780 ...123456789012345678901234567890123456789. */
4781 const char *content
4782 = (" foo *f = (foo *)ptr->field;\n");
4783 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4784 file_cache fc;
4785 line_table_test ltt (case_);
4787 const line_map_ordinary *ord_map
4788 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4789 tmp.get_filename (), 0));
4791 linemap_line_start (line_table, 1, 100);
4793 const location_t final_line_end
4794 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4796 /* Don't attempt to run the tests if column data might be unavailable. */
4797 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4798 return;
4800 /* A test for converting a C-style cast to a C++-style cast. */
4801 const location_t open_paren
4802 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4803 const location_t close_paren
4804 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4805 const location_t expr_start
4806 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4807 const location_t expr_finish
4808 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4809 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4811 /* Various examples of fix-it hints that aren't themselves consolidated,
4812 but for which the *printing* may need consolidation. */
4814 /* Example where 3 fix-it hints are printed as one. */
4816 test_diagnostic_context dc;
4817 rich_location richloc (line_table, expr);
4818 richloc.add_fixit_replace (open_paren, "const_cast<");
4819 richloc.add_fixit_replace (close_paren, "> (");
4820 richloc.add_fixit_insert_after (")");
4822 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4823 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4824 " ^~~~~~~~~~\n"
4825 " -----------------\n"
4826 " const_cast<foo *> (ptr->field)\n",
4827 pp_formatted_text (dc.printer));
4829 /* Unit-test the line_corrections machinery. */
4830 char_display_policy policy (make_policy (dc, richloc));
4831 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4832 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4833 ASSERT_EQ (column_range (12, 12),
4834 get_affected_range (fc, policy, hint_0, CU_BYTES));
4835 ASSERT_EQ (column_range (12, 12),
4836 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
4837 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
4838 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4839 ASSERT_EQ (column_range (18, 18),
4840 get_affected_range (fc, policy, hint_1, CU_BYTES));
4841 ASSERT_EQ (column_range (18, 18),
4842 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
4843 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
4844 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4845 ASSERT_EQ (column_range (29, 28),
4846 get_affected_range (fc, policy, hint_2, CU_BYTES));
4847 ASSERT_EQ (column_range (29, 28),
4848 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
4849 ASSERT_EQ (column_range (29, 29), get_printed_columns (fc, policy, hint_2));
4851 /* Add each hint in turn to a line_corrections instance,
4852 and verify that they are consolidated into one correction instance
4853 as expected. */
4854 line_corrections lc (fc, policy, tmp.get_filename (), 1);
4856 /* The first replace hint by itself. */
4857 lc.add_hint (hint_0);
4858 ASSERT_EQ (1, lc.m_corrections.length ());
4859 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4860 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4861 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4862 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4864 /* After the second replacement hint, they are printed together
4865 as a replacement (along with the text between them). */
4866 lc.add_hint (hint_1);
4867 ASSERT_EQ (1, lc.m_corrections.length ());
4868 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4869 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4870 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4871 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4873 /* After the final insertion hint, they are all printed together
4874 as a replacement (along with the text between them). */
4875 lc.add_hint (hint_2);
4876 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4877 lc.m_corrections[0]->m_text);
4878 ASSERT_EQ (1, lc.m_corrections.length ());
4879 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4880 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4881 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4884 /* Example where two are consolidated during printing. */
4886 test_diagnostic_context dc;
4887 rich_location richloc (line_table, expr);
4888 richloc.add_fixit_replace (open_paren, "CAST (");
4889 richloc.add_fixit_replace (close_paren, ") (");
4890 richloc.add_fixit_insert_after (")");
4892 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4893 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4894 " ^~~~~~~~~~\n"
4895 " -\n"
4896 " CAST (-\n"
4897 " ) ( )\n",
4898 pp_formatted_text (dc.printer));
4901 /* Example where none are consolidated during printing. */
4903 test_diagnostic_context dc;
4904 rich_location richloc (line_table, expr);
4905 richloc.add_fixit_replace (open_paren, "CST (");
4906 richloc.add_fixit_replace (close_paren, ") (");
4907 richloc.add_fixit_insert_after (")");
4909 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4910 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4911 " ^~~~~~~~~~\n"
4912 " -\n"
4913 " CST ( -\n"
4914 " ) ( )\n",
4915 pp_formatted_text (dc.printer));
4918 /* Example of deletion fix-it hints. */
4920 test_diagnostic_context dc;
4921 rich_location richloc (line_table, expr);
4922 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4923 source_range victim = {open_paren, close_paren};
4924 richloc.add_fixit_remove (victim);
4926 /* This case is actually handled by fixit-consolidation,
4927 rather than by line_corrections. */
4928 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4930 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4931 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4932 " ^~~~~~~~~~\n"
4933 " -------\n"
4934 " (bar *)\n",
4935 pp_formatted_text (dc.printer));
4938 /* Example of deletion fix-it hints that would overlap. */
4940 test_diagnostic_context dc;
4941 rich_location richloc (line_table, expr);
4942 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4943 source_range victim = {expr_start, expr_finish};
4944 richloc.add_fixit_remove (victim);
4946 /* These fixits are not consolidated. */
4947 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4949 /* But the corrections are. */
4950 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4951 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4952 " ^~~~~~~~~~\n"
4953 " -----------------\n"
4954 " (longer *)(foo *)\n",
4955 pp_formatted_text (dc.printer));
4958 /* Example of insertion fix-it hints that would overlap. */
4960 test_diagnostic_context dc;
4961 rich_location richloc (line_table, expr);
4962 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4963 richloc.add_fixit_insert_after (close_paren, "TEST");
4965 /* The first insertion is long enough that if printed naively,
4966 it would overlap with the second.
4967 Verify that they are printed as a single replacement. */
4968 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4969 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4970 " ^~~~~~~~~~\n"
4971 " -------\n"
4972 " LONGER THAN THE CAST(foo *)TEST\n",
4973 pp_formatted_text (dc.printer));
4977 /* Multibyte-aware version of preceding tests. See comments above
4978 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4979 characters here. */
4981 static void
4982 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4984 /* Create a tempfile and write some text to it. */
4986 const char *content
4987 /* Display columns.
4988 00000000000000000000000111111111111111111111111222222222222222223
4989 12344444444555555556789012344444444555555556789012345678999999990 */
4990 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4991 /* 00000000000000000000011111111111111111111112222222222333333333333
4992 12344445555666677778901234566667777888899990123456789012333344445
4993 Byte columns. */
4995 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4996 line_table_test ltt (case_);
4998 const line_map_ordinary *ord_map
4999 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5000 tmp.get_filename (), 0));
5002 linemap_line_start (line_table, 1, 100);
5004 const location_t final_line_end
5005 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
5007 /* Don't attempt to run the tests if column data might be unavailable. */
5008 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5009 return;
5011 /* A test for converting a C-style cast to a C++-style cast. */
5012 const location_t open_paren
5013 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
5014 const location_t close_paren
5015 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
5016 const location_t expr_start
5017 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5018 const location_t expr_finish
5019 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
5020 const location_t expr = make_location (expr_start, expr_start, expr_finish);
5022 /* Various examples of fix-it hints that aren't themselves consolidated,
5023 but for which the *printing* may need consolidation. */
5025 /* Example where 3 fix-it hints are printed as one. */
5027 test_diagnostic_context dc;
5028 file_cache &fc = dc.get_file_cache ();
5029 rich_location richloc (line_table, expr);
5030 richloc.add_fixit_replace (open_paren, "const_cast<");
5031 richloc.add_fixit_replace (close_paren, "> (");
5032 richloc.add_fixit_insert_after (")");
5034 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5035 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5036 " *f = (f\xf0\x9f\x98\x82"
5037 " *)ptr->field\xcf\x80"
5038 ";\n"
5039 " ^~~~~~~~~~~\n"
5040 " ------------------\n"
5041 " const_cast<f\xf0\x9f\x98\x82"
5042 " *> (ptr->field\xcf\x80"
5043 ")\n",
5044 pp_formatted_text (dc.printer));
5046 /* Unit-test the line_corrections machinery. */
5047 char_display_policy policy (make_policy (dc, richloc));
5048 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5049 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5050 ASSERT_EQ (column_range (14, 14),
5051 get_affected_range (fc, policy, hint_0, CU_BYTES));
5052 ASSERT_EQ (column_range (12, 12),
5053 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
5054 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
5055 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5056 ASSERT_EQ (column_range (22, 22),
5057 get_affected_range (fc, policy, hint_1, CU_BYTES));
5058 ASSERT_EQ (column_range (18, 18),
5059 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
5060 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
5061 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5062 ASSERT_EQ (column_range (35, 34),
5063 get_affected_range (fc, policy, hint_2, CU_BYTES));
5064 ASSERT_EQ (column_range (30, 29),
5065 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
5066 ASSERT_EQ (column_range (30, 30), get_printed_columns (fc, policy, hint_2));
5068 /* Add each hint in turn to a line_corrections instance,
5069 and verify that they are consolidated into one correction instance
5070 as expected. */
5071 line_corrections lc (fc, policy, tmp.get_filename (), 1);
5073 /* The first replace hint by itself. */
5074 lc.add_hint (hint_0);
5075 ASSERT_EQ (1, lc.m_corrections.length ());
5076 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5077 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5078 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5079 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5081 /* After the second replacement hint, they are printed together
5082 as a replacement (along with the text between them). */
5083 lc.add_hint (hint_1);
5084 ASSERT_EQ (1, lc.m_corrections.length ());
5085 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5086 lc.m_corrections[0]->m_text);
5087 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5088 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5089 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5091 /* After the final insertion hint, they are all printed together
5092 as a replacement (along with the text between them). */
5093 lc.add_hint (hint_2);
5094 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5095 lc.m_corrections[0]->m_text);
5096 ASSERT_EQ (1, lc.m_corrections.length ());
5097 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5098 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5099 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5102 /* Example where two are consolidated during printing. */
5104 test_diagnostic_context dc;
5105 rich_location richloc (line_table, expr);
5106 richloc.add_fixit_replace (open_paren, "CAST (");
5107 richloc.add_fixit_replace (close_paren, ") (");
5108 richloc.add_fixit_insert_after (")");
5110 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5111 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5112 " *f = (f\xf0\x9f\x98\x82"
5113 " *)ptr->field\xcf\x80"
5114 ";\n"
5115 " ^~~~~~~~~~~\n"
5116 " -\n"
5117 " CAST (-\n"
5118 " ) ( )\n",
5119 pp_formatted_text (dc.printer));
5122 /* Example where none are consolidated during printing. */
5124 test_diagnostic_context dc;
5125 rich_location richloc (line_table, expr);
5126 richloc.add_fixit_replace (open_paren, "CST (");
5127 richloc.add_fixit_replace (close_paren, ") (");
5128 richloc.add_fixit_insert_after (")");
5130 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5131 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5132 " *f = (f\xf0\x9f\x98\x82"
5133 " *)ptr->field\xcf\x80"
5134 ";\n"
5135 " ^~~~~~~~~~~\n"
5136 " -\n"
5137 " CST ( -\n"
5138 " ) ( )\n",
5139 pp_formatted_text (dc.printer));
5142 /* Example of deletion fix-it hints. */
5144 test_diagnostic_context dc;
5145 rich_location richloc (line_table, expr);
5146 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5147 source_range victim = {open_paren, close_paren};
5148 richloc.add_fixit_remove (victim);
5150 /* This case is actually handled by fixit-consolidation,
5151 rather than by line_corrections. */
5152 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5154 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5155 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5156 " *f = (f\xf0\x9f\x98\x82"
5157 " *)ptr->field\xcf\x80"
5158 ";\n"
5159 " ^~~~~~~~~~~\n"
5160 " -------\n"
5161 " (bar\xf0\x9f\x98\x82"
5162 " *)\n",
5163 pp_formatted_text (dc.printer));
5166 /* Example of deletion fix-it hints that would overlap. */
5168 test_diagnostic_context dc;
5169 rich_location richloc (line_table, expr);
5170 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5171 source_range victim = {expr_start, expr_finish};
5172 richloc.add_fixit_remove (victim);
5174 /* These fixits are not consolidated. */
5175 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5177 /* But the corrections are. */
5178 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5179 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5180 " *f = (f\xf0\x9f\x98\x82"
5181 " *)ptr->field\xcf\x80"
5182 ";\n"
5183 " ^~~~~~~~~~~\n"
5184 " ------------------\n"
5185 " (long\xf0\x9f\x98\x82"
5186 " *)(f\xf0\x9f\x98\x82"
5187 " *)\n",
5188 pp_formatted_text (dc.printer));
5191 /* Example of insertion fix-it hints that would overlap. */
5193 test_diagnostic_context dc;
5194 rich_location richloc (line_table, expr);
5195 richloc.add_fixit_insert_before
5196 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5197 richloc.add_fixit_insert_after (close_paren, "TEST");
5199 /* The first insertion is long enough that if printed naively,
5200 it would overlap with the second.
5201 Verify that they are printed as a single replacement. */
5202 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5203 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5204 " *f = (f\xf0\x9f\x98\x82"
5205 " *)ptr->field\xcf\x80"
5206 ";\n"
5207 " ^~~~~~~~~~~\n"
5208 " -------\n"
5209 " L\xf0\x9f\x98\x82"
5210 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5211 " *)TEST\n",
5212 pp_formatted_text (dc.printer));
5216 /* Verify that the line_corrections machinery correctly prints
5217 overlapping fixit-hints that have been added in the wrong
5218 order.
5219 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5221 static void
5222 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5224 /* Create a tempfile and write some text to it.
5225 ...000000000111111111122222222223333333333.
5226 ...123456789012345678901234567890123456789. */
5227 const char *content
5228 = ("int a5[][0][0] = { 1, 2 };\n");
5229 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5230 line_table_test ltt (case_);
5232 const line_map_ordinary *ord_map
5233 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5234 tmp.get_filename (), 0));
5236 linemap_line_start (line_table, 1, 100);
5238 const location_t final_line_end
5239 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5241 /* Don't attempt to run the tests if column data might be unavailable. */
5242 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5243 return;
5245 const location_t col_1
5246 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5247 const location_t col_20
5248 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5249 const location_t col_21
5250 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5251 const location_t col_23
5252 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5253 const location_t col_25
5254 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5256 /* Two insertions, in the wrong order. */
5258 test_diagnostic_context dc;
5259 file_cache &fc = dc.get_file_cache ();
5261 rich_location richloc (line_table, col_20);
5262 richloc.add_fixit_insert_before (col_23, "{");
5263 richloc.add_fixit_insert_before (col_21, "}");
5265 /* These fixits should be accepted; they can't be consolidated. */
5266 char_display_policy policy (make_policy (dc, richloc));
5267 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5268 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5269 ASSERT_EQ (column_range (23, 22),
5270 get_affected_range (fc, policy, hint_0, CU_BYTES));
5271 ASSERT_EQ (column_range (23, 23), get_printed_columns (fc, policy, hint_0));
5272 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5273 ASSERT_EQ (column_range (21, 20),
5274 get_affected_range (fc, policy, hint_1, CU_BYTES));
5275 ASSERT_EQ (column_range (21, 21), get_printed_columns (fc, policy, hint_1));
5277 /* Verify that they're printed correctly. */
5278 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5279 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5280 " ^\n"
5281 " } {\n",
5282 pp_formatted_text (dc.printer));
5285 /* Various overlapping insertions, some occurring "out of order"
5286 (reproducing the fix-it hints from PR c/81405). */
5288 test_diagnostic_context dc;
5289 rich_location richloc (line_table, col_20);
5291 richloc.add_fixit_insert_before (col_20, "{{");
5292 richloc.add_fixit_insert_before (col_21, "}}");
5293 richloc.add_fixit_insert_before (col_23, "{");
5294 richloc.add_fixit_insert_before (col_21, "}");
5295 richloc.add_fixit_insert_before (col_23, "{{");
5296 richloc.add_fixit_insert_before (col_25, "}");
5297 richloc.add_fixit_insert_before (col_21, "}");
5298 richloc.add_fixit_insert_before (col_1, "{");
5299 richloc.add_fixit_insert_before (col_25, "}");
5300 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5301 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5302 " ^\n"
5303 " { -----\n"
5304 " {{1}}}}, {{{2 }}\n",
5305 pp_formatted_text (dc.printer));
5309 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5311 static void
5312 test_fixit_insert_containing_newline (const line_table_case &case_)
5314 /* Create a tempfile and write some text to it.
5315 .........................0000000001111111.
5316 .........................1234567890123456. */
5317 const char *old_content = (" case 'a':\n" /* line 1. */
5318 " x = a;\n" /* line 2. */
5319 " case 'b':\n" /* line 3. */
5320 " x = b;\n");/* line 4. */
5322 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5323 line_table_test ltt (case_);
5324 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5326 location_t case_start = linemap_position_for_column (line_table, 5);
5327 location_t case_finish = linemap_position_for_column (line_table, 13);
5328 location_t case_loc = make_location (case_start, case_start, case_finish);
5329 location_t line_start = linemap_position_for_column (line_table, 1);
5331 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5332 return;
5334 /* Add a "break;" on a line by itself before line 3 i.e. before
5335 column 1 of line 3. */
5337 rich_location richloc (line_table, case_loc);
5338 richloc.add_fixit_insert_before (line_start, " break;\n");
5340 /* Without line numbers. */
5342 test_diagnostic_context dc;
5343 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5344 ASSERT_STREQ (" x = a;\n"
5345 "+ break;\n"
5346 " case 'b':\n"
5347 " ^~~~~~~~~\n",
5348 pp_formatted_text (dc.printer));
5351 /* With line numbers. */
5353 test_diagnostic_context dc;
5354 dc.m_source_printing.show_line_numbers_p = true;
5355 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5356 ASSERT_STREQ (" 2 | x = a;\n"
5357 " +++ |+ break;\n"
5358 " 3 | case 'b':\n"
5359 " | ^~~~~~~~~\n",
5360 pp_formatted_text (dc.printer));
5364 /* Verify that attempts to add text with a newline fail when the
5365 insertion point is *not* at the start of a line. */
5367 rich_location richloc (line_table, case_loc);
5368 richloc.add_fixit_insert_before (case_start, "break;\n");
5369 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5370 test_diagnostic_context dc;
5371 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5372 ASSERT_STREQ (" case 'b':\n"
5373 " ^~~~~~~~~\n",
5374 pp_formatted_text (dc.printer));
5378 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5379 of the file, where the fix-it is printed in a different line-span
5380 to the primary range of the diagnostic. */
5382 static void
5383 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5385 /* Create a tempfile and write some text to it.
5386 .........................0000000001111111.
5387 .........................1234567890123456. */
5388 const char *old_content = ("test (int ch)\n" /* line 1. */
5389 "{\n" /* line 2. */
5390 " putchar (ch);\n" /* line 3. */
5391 "}\n"); /* line 4. */
5393 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5394 line_table_test ltt (case_);
5396 const line_map_ordinary *ord_map = linemap_check_ordinary
5397 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5398 linemap_line_start (line_table, 1, 100);
5400 /* The primary range is the "putchar" token. */
5401 location_t putchar_start
5402 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5403 location_t putchar_finish
5404 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5405 location_t putchar_loc
5406 = make_location (putchar_start, putchar_start, putchar_finish);
5407 rich_location richloc (line_table, putchar_loc);
5409 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5410 location_t file_start
5411 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5412 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5414 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5415 return;
5418 test_diagnostic_context dc;
5419 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5420 ASSERT_STREQ ("FILENAME:1:1:\n"
5421 "+#include <stdio.h>\n"
5422 " test (int ch)\n"
5423 "FILENAME:3:2:\n"
5424 " putchar (ch);\n"
5425 " ^~~~~~~\n",
5426 pp_formatted_text (dc.printer));
5429 /* With line-numbering, the line spans are close enough to be
5430 consolidated, since it makes little sense to skip line 2. */
5432 test_diagnostic_context dc;
5433 dc.m_source_printing.show_line_numbers_p = true;
5434 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5435 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5436 " 1 | test (int ch)\n"
5437 " 2 | {\n"
5438 " 3 | putchar (ch);\n"
5439 " | ^~~~~~~\n",
5440 pp_formatted_text (dc.printer));
5444 /* Replacement fix-it hint containing a newline.
5445 This will fail, as newlines are only supported when inserting at the
5446 beginning of a line. */
5448 static void
5449 test_fixit_replace_containing_newline (const line_table_case &case_)
5451 /* Create a tempfile and write some text to it.
5452 .........................0000000001111.
5453 .........................1234567890123. */
5454 const char *old_content = "foo = bar ();\n";
5456 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5457 line_table_test ltt (case_);
5458 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5460 /* Replace the " = " with "\n = ", as if we were reformatting an
5461 overly long line. */
5462 location_t start = linemap_position_for_column (line_table, 4);
5463 location_t finish = linemap_position_for_column (line_table, 6);
5464 location_t loc = linemap_position_for_column (line_table, 13);
5465 rich_location richloc (line_table, loc);
5466 source_range range = source_range::from_locations (start, finish);
5467 richloc.add_fixit_replace (range, "\n =");
5469 /* Arbitrary newlines are not yet supported within fix-it hints, so
5470 the fix-it should not be displayed. */
5471 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5473 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5474 return;
5476 test_diagnostic_context dc;
5477 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5478 ASSERT_STREQ (" foo = bar ();\n"
5479 " ^\n",
5480 pp_formatted_text (dc.printer));
5483 /* Fix-it hint, attempting to delete a newline.
5484 This will fail, as we currently only support fix-it hints that
5485 affect one line at a time. */
5487 static void
5488 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5490 /* Create a tempfile and write some text to it.
5491 ..........................0000000001111.
5492 ..........................1234567890123. */
5493 const char *old_content = ("foo = bar (\n"
5494 " );\n");
5496 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5497 line_table_test ltt (case_);
5498 const line_map_ordinary *ord_map = linemap_check_ordinary
5499 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5500 linemap_line_start (line_table, 1, 100);
5502 /* Attempt to delete the " (\n...)". */
5503 location_t start
5504 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5505 location_t caret
5506 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5507 location_t finish
5508 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5509 location_t loc = make_location (caret, start, finish);
5510 rich_location richloc (line_table, loc);
5511 richloc. add_fixit_remove ();
5513 /* Fix-it hints that affect more than one line are not yet supported, so
5514 the fix-it should not be displayed. */
5515 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5517 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5518 return;
5520 test_diagnostic_context dc;
5521 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5522 ASSERT_STREQ (" foo = bar (\n"
5523 " ~^\n"
5524 " );\n"
5525 " ~ \n",
5526 pp_formatted_text (dc.printer));
5529 static void
5530 test_tab_expansion (const line_table_case &case_)
5532 /* Create a tempfile and write some text to it. This example uses a tabstop
5533 of 8, as the column numbers attempt to indicate:
5535 .....................000.01111111111.22222333333 display
5536 .....................123.90123456789.56789012345 columns */
5537 const char *content = " \t This: `\t' is a tab.\n";
5538 /* ....................000 00000011111 11111222222 byte
5539 ....................123 45678901234 56789012345 columns */
5541 const int tabstop = 8;
5542 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5543 const int first_non_ws_byte_col = 7;
5544 const int right_quote_byte_col = 15;
5545 const int last_byte_col = 25;
5546 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5548 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5549 line_table_test ltt (case_);
5550 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5552 /* Don't attempt to run the tests if column data might be unavailable. */
5553 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5554 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5555 return;
5557 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5558 into 11 spaces. Recall that print_line() also puts one space before
5559 everything too. */
5561 test_diagnostic_context dc;
5562 dc.m_tabstop = tabstop;
5563 rich_location richloc (line_table,
5564 linemap_position_for_column (line_table,
5565 first_non_ws_byte_col));
5566 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5567 test_layout.print_line (1);
5568 ASSERT_STREQ (" This: ` ' is a tab.\n"
5569 " ^\n",
5570 pp_formatted_text (dc.printer));
5573 /* Confirm the display width was tracked correctly across the internal tab
5574 as well. */
5576 test_diagnostic_context dc;
5577 dc.m_tabstop = tabstop;
5578 rich_location richloc (line_table,
5579 linemap_position_for_column (line_table,
5580 right_quote_byte_col));
5581 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5582 test_layout.print_line (1);
5583 ASSERT_STREQ (" This: ` ' is a tab.\n"
5584 " ^\n",
5585 pp_formatted_text (dc.printer));
5589 /* Verify that the escaping machinery can cope with a variety of different
5590 invalid bytes. */
5592 static void
5593 test_escaping_bytes_1 (const line_table_case &case_)
5595 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5596 const size_t sz = sizeof (content);
5597 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5598 line_table_test ltt (case_);
5599 const line_map_ordinary *ord_map = linemap_check_ordinary
5600 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5601 linemap_line_start (line_table, 1, 100);
5603 location_t finish
5604 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5605 strlen (content));
5607 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5608 return;
5610 /* Locations of the NUL and \v bytes. */
5611 location_t nul_loc
5612 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5613 location_t v_loc
5614 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5615 gcc_rich_location richloc (nul_loc);
5616 richloc.add_range (v_loc);
5619 test_diagnostic_context dc;
5620 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5621 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5622 " ^ ~\n",
5623 pp_formatted_text (dc.printer));
5625 richloc.set_escape_on_output (true);
5627 test_diagnostic_context dc;
5628 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5629 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5630 ASSERT_STREQ
5631 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5632 " ^~~~~~~~ ~~~~~~~~\n",
5633 pp_formatted_text (dc.printer));
5636 test_diagnostic_context dc;
5637 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5638 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5639 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5640 " ^~~~ ~~~~\n",
5641 pp_formatted_text (dc.printer));
5645 /* As above, but verify that we handle the initial byte of a line
5646 correctly. */
5648 static void
5649 test_escaping_bytes_2 (const line_table_case &case_)
5651 const char content[] = "\0after\n";
5652 const size_t sz = sizeof (content);
5653 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5654 line_table_test ltt (case_);
5655 const line_map_ordinary *ord_map = linemap_check_ordinary
5656 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5657 linemap_line_start (line_table, 1, 100);
5659 location_t finish
5660 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5661 strlen (content));
5663 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5664 return;
5666 /* Location of the NUL byte. */
5667 location_t nul_loc
5668 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5669 gcc_rich_location richloc (nul_loc);
5672 test_diagnostic_context dc;
5673 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5674 ASSERT_STREQ (" after\n"
5675 " ^\n",
5676 pp_formatted_text (dc.printer));
5678 richloc.set_escape_on_output (true);
5680 test_diagnostic_context dc;
5681 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5683 ASSERT_STREQ (" <U+0000>after\n"
5684 " ^~~~~~~~\n",
5685 pp_formatted_text (dc.printer));
5688 test_diagnostic_context dc;
5689 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5690 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5691 ASSERT_STREQ (" <00>after\n"
5692 " ^~~~\n",
5693 pp_formatted_text (dc.printer));
5697 /* Verify that line numbers are correctly printed for the case of
5698 a multiline range in which the width of the line numbers changes
5699 (e.g. from "9" to "10"). */
5701 static void
5702 test_line_numbers_multiline_range ()
5704 /* Create a tempfile and write some text to it. */
5705 pretty_printer pp;
5706 for (int i = 0; i < 20; i++)
5707 /* .........0000000001111111.
5708 .............1234567890123456. */
5709 pp_printf (&pp, "this is line %i\n", i + 1);
5710 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5711 line_table_test ltt;
5713 const line_map_ordinary *ord_map = linemap_check_ordinary
5714 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5715 linemap_line_start (line_table, 1, 100);
5717 /* Create a multi-line location, starting at the "line" of line 9, with
5718 a caret on the "is" of line 10, finishing on the "this" line 11. */
5720 location_t start
5721 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5722 location_t caret
5723 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5724 location_t finish
5725 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5726 location_t loc = make_location (caret, start, finish);
5728 test_diagnostic_context dc;
5729 dc.m_source_printing.show_line_numbers_p = true;
5730 dc.m_source_printing.min_margin_width = 0;
5731 gcc_rich_location richloc (loc);
5732 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5733 ASSERT_STREQ (" 9 | this is line 9\n"
5734 " | ~~~~~~\n"
5735 "10 | this is line 10\n"
5736 " | ~~~~~^~~~~~~~~~\n"
5737 "11 | this is line 11\n"
5738 " | ~~~~ \n",
5739 pp_formatted_text (dc.printer));
5742 /* Run all of the selftests within this file. */
5744 void
5745 diagnostic_show_locus_cc_tests ()
5747 test_line_span ();
5749 test_layout_range_for_single_point ();
5750 test_layout_range_for_single_line ();
5751 test_layout_range_for_multiple_lines ();
5753 test_display_widths ();
5755 for_each_line_table_case (test_layout_x_offset_display_utf8);
5756 for_each_line_table_case (test_layout_x_offset_display_tab);
5758 test_get_line_bytes_without_trailing_whitespace ();
5760 test_diagnostic_show_locus_unknown_location ();
5762 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5763 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5764 for_each_line_table_case (test_add_location_if_nearby);
5765 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5766 for_each_line_table_case (test_fixit_consolidation);
5767 for_each_line_table_case (test_overlapped_fixit_printing);
5768 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5769 for_each_line_table_case (test_overlapped_fixit_printing_2);
5770 for_each_line_table_case (test_fixit_insert_containing_newline);
5771 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5772 for_each_line_table_case (test_fixit_replace_containing_newline);
5773 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5774 for_each_line_table_case (test_tab_expansion);
5775 for_each_line_table_case (test_escaping_bytes_1);
5776 for_each_line_table_case (test_escaping_bytes_2);
5778 test_line_numbers_multiline_range ();
5781 } // namespace selftest
5783 #endif /* #if CHECKING_P */
5785 #if __GNUC__ >= 10
5786 # pragma GCC diagnostic pop
5787 #endif