tree-optimization/112450 - avoid AVX512 style masking for BImode masks
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob43523572fe5beb8f85b01f578999d4c097a8e9e2
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 (const expanded_location &exploc,
179 const cpp_char_column_policy &policy,
180 enum location_aspect aspect)
181 : expanded_location (exploc),
182 m_display_col (location_compute_display_column (exploc, policy))
184 if (exploc.column > 0)
186 /* m_display_col is now the final column of the byte.
187 If escaping has happened, we may want the first column instead. */
188 if (aspect != LOCATION_ASPECT_FINISH)
190 expanded_location prev_exploc (exploc);
191 prev_exploc.column--;
192 int prev_display_col
193 = (location_compute_display_column (prev_exploc, policy));
194 m_display_col = prev_display_col + 1;
199 int m_display_col;
203 /* A point within a layout_range; similar to an exploc_with_display_col,
204 but after filtering on file. */
206 class layout_point
208 public:
209 layout_point (const exploc_with_display_col &exploc)
210 : m_line (exploc.line)
212 m_columns[CU_BYTES] = exploc.column;
213 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
216 linenum_type m_line;
217 int m_columns[CU_NUM_UNITS];
220 /* A class for use by "class layout" below: a filtered location_range. */
222 class layout_range
224 public:
225 layout_range (const exploc_with_display_col &start_exploc,
226 const exploc_with_display_col &finish_exploc,
227 enum range_display_kind range_display_kind,
228 const exploc_with_display_col &caret_exploc,
229 unsigned original_idx,
230 const range_label *label);
232 bool contains_point (linenum_type row, int column,
233 enum column_unit col_unit) const;
234 bool intersects_line_p (linenum_type row) const;
236 layout_point m_start;
237 layout_point m_finish;
238 enum range_display_kind m_range_display_kind;
239 layout_point m_caret;
240 unsigned m_original_idx;
241 const range_label *m_label;
244 /* A struct for use by layout::print_source_line for telling
245 layout::print_annotation_line the extents of the source line that
246 it printed, so that underlines can be clipped appropriately. Units
247 are 1-based display columns. */
249 struct line_bounds
251 int m_first_non_ws_disp_col;
252 int m_last_non_ws_disp_col;
254 line_bounds ()
256 m_first_non_ws_disp_col = INT_MAX;
257 m_last_non_ws_disp_col = 0;
261 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
262 or "line 23"). During the layout ctor, layout::calculate_line_spans
263 splits the pertinent source lines into a list of disjoint line_span
264 instances (e.g. lines 5-10, lines 15-20, line 23). */
266 class line_span
268 public:
269 line_span (linenum_type first_line, linenum_type last_line)
270 : m_first_line (first_line), m_last_line (last_line)
272 gcc_assert (first_line <= last_line);
274 linenum_type get_first_line () const { return m_first_line; }
275 linenum_type get_last_line () const { return m_last_line; }
277 bool contains_line_p (linenum_type line) const
279 return line >= m_first_line && line <= m_last_line;
282 static int comparator (const void *p1, const void *p2)
284 const line_span *ls1 = (const line_span *)p1;
285 const line_span *ls2 = (const line_span *)p2;
286 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
287 if (first_line_cmp)
288 return first_line_cmp;
289 return compare (ls1->m_last_line, ls2->m_last_line);
292 linenum_type m_first_line;
293 linenum_type m_last_line;
296 #if CHECKING_P
298 /* Selftests for line_span. */
300 static void
301 test_line_span ()
303 line_span line_one (1, 1);
304 ASSERT_EQ (1, line_one.get_first_line ());
305 ASSERT_EQ (1, line_one.get_last_line ());
306 ASSERT_FALSE (line_one.contains_line_p (0));
307 ASSERT_TRUE (line_one.contains_line_p (1));
308 ASSERT_FALSE (line_one.contains_line_p (2));
310 line_span lines_1_to_3 (1, 3);
311 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
312 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
313 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
316 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
317 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
318 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
320 /* A linenum > 2^31. */
321 const linenum_type LARGEST_LINE = 0xffffffff;
322 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
323 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
326 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
327 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
330 #endif /* #if CHECKING_P */
332 /* A bundle of information containing how to print unicode
333 characters and bytes when quoting source code.
335 Provides a unified place to support escaping some subset
336 of characters to some format.
338 Extends char_column_policy; printing is split out to avoid
339 libcpp having to know about pretty_printer. */
341 struct char_display_policy : public cpp_char_column_policy
343 public:
344 char_display_policy (int tabstop,
345 int (*width_cb) (cppchar_t c),
346 void (*print_cb) (pretty_printer *pp,
347 const cpp_decoded_char &cp))
348 : cpp_char_column_policy (tabstop, width_cb),
349 m_print_cb (print_cb)
353 void (*m_print_cb) (pretty_printer *pp,
354 const cpp_decoded_char &cp);
357 /* A class to control the overall layout when printing a diagnostic.
359 The layout is determined within the constructor.
360 It is then printed by repeatedly calling the "print_source_line",
361 "print_annotation_line" and "print_any_fixits" methods.
363 We assume we have disjoint ranges. */
365 class layout
367 public:
368 layout (const diagnostic_context &context,
369 rich_location *richloc,
370 diagnostic_t diagnostic_kind,
371 pretty_printer *pp);
373 bool maybe_add_location_range (const location_range *loc_range,
374 unsigned original_idx,
375 bool restrict_to_current_line_spans);
377 int get_num_line_spans () const { return m_line_spans.length (); }
378 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
380 int get_linenum_width () const { return m_linenum_width; }
381 int get_x_offset_display () const { return m_x_offset_display; }
383 void print_gap_in_line_numbering ();
384 bool print_heading_for_line_span_index_p (int line_span_idx) const;
386 expanded_location get_expanded_location (const line_span *) const;
388 void print_line (linenum_type row);
390 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
392 private:
393 bool will_show_line_p (linenum_type row) const;
394 void print_leading_fixits (linenum_type row);
395 line_bounds print_source_line (linenum_type row, const char *line,
396 int line_bytes);
397 bool should_print_annotation_line_p (linenum_type row) const;
398 void start_annotation_line (char margin_char = ' ') const;
399 void print_annotation_line (linenum_type row, const line_bounds lbounds);
400 void print_any_labels (linenum_type row);
401 void print_trailing_fixits (linenum_type row);
403 bool annotation_line_showed_range_p (linenum_type line, int start_column,
404 int finish_column) const;
405 void show_ruler (int max_column) const;
407 bool validate_fixit_hint_p (const fixit_hint *hint);
409 void calculate_line_spans ();
410 void calculate_linenum_width ();
411 void calculate_x_offset_display ();
413 void print_newline ();
415 bool
416 get_state_at_point (/* Inputs. */
417 linenum_type row, int column,
418 int first_non_ws, int last_non_ws,
419 enum column_unit col_unit,
420 /* Outputs. */
421 point_state *out_state);
424 get_x_bound_for_row (linenum_type row, int caret_column,
425 int last_non_ws);
427 void
428 move_to_column (int *column, int dest_column, bool add_left_margin);
430 private:
431 const diagnostic_source_printing_options &m_options;
432 pretty_printer *m_pp;
433 char_display_policy m_policy;
434 location_t m_primary_loc;
435 exploc_with_display_col m_exploc;
436 colorizer m_colorizer;
437 bool m_diagnostic_path_p;
438 auto_vec <layout_range> m_layout_ranges;
439 auto_vec <const fixit_hint *> m_fixit_hints;
440 auto_vec <line_span> m_line_spans;
441 int m_linenum_width;
442 int m_x_offset_display;
443 bool m_escape_on_output;
446 /* Implementation of "class colorizer". */
448 /* The constructor for "colorizer". Lookup and store color codes for the
449 different kinds of things we might need to print. */
451 colorizer::colorizer (pretty_printer *pp,
452 diagnostic_t diagnostic_kind) :
453 m_pp (pp),
454 m_diagnostic_kind (diagnostic_kind),
455 m_current_state (STATE_NORMAL_TEXT)
457 m_range1 = get_color_by_name ("range1");
458 m_range2 = get_color_by_name ("range2");
459 m_fixit_insert = get_color_by_name ("fixit-insert");
460 m_fixit_delete = get_color_by_name ("fixit-delete");
461 m_stop_color = colorize_stop (pp_show_color (m_pp));
464 /* The destructor for "colorize". If colorization is on, print a code to
465 turn it off. */
467 colorizer::~colorizer ()
469 finish_state (m_current_state);
472 /* Update state, printing color codes if necessary if there's a state
473 change. */
475 void
476 colorizer::set_state (int new_state)
478 if (m_current_state != new_state)
480 finish_state (m_current_state);
481 m_current_state = new_state;
482 begin_state (new_state);
486 /* Turn on any colorization for STATE. */
488 void
489 colorizer::begin_state (int state)
491 switch (state)
493 case STATE_NORMAL_TEXT:
494 break;
496 case STATE_FIXIT_INSERT:
497 pp_string (m_pp, m_fixit_insert);
498 break;
500 case STATE_FIXIT_DELETE:
501 pp_string (m_pp, m_fixit_delete);
502 break;
504 case 0:
505 /* Make range 0 be the same color as the "kind" text
506 (error vs warning vs note). */
507 pp_string
508 (m_pp,
509 colorize_start (pp_show_color (m_pp),
510 diagnostic_get_color_for_kind (m_diagnostic_kind)));
511 break;
513 case 1:
514 pp_string (m_pp, m_range1);
515 break;
517 case 2:
518 pp_string (m_pp, m_range2);
519 break;
521 default:
522 /* For ranges beyond 2, alternate between color 1 and color 2. */
524 gcc_assert (state > 2);
525 pp_string (m_pp,
526 state % 2 ? m_range1 : m_range2);
528 break;
532 /* Turn off any colorization for STATE. */
534 void
535 colorizer::finish_state (int state)
537 if (state != STATE_NORMAL_TEXT)
538 pp_string (m_pp, m_stop_color);
541 /* Get the color code for NAME (or the empty string if
542 colorization is disabled). */
544 const char *
545 colorizer::get_color_by_name (const char *name)
547 return colorize_start (pp_show_color (m_pp), name);
550 /* Implementation of class layout_range. */
552 /* The constructor for class layout_range.
553 Initialize various layout_point fields from expanded_location
554 equivalents; we've already filtered on file. */
556 layout_range::layout_range (const exploc_with_display_col &start_exploc,
557 const exploc_with_display_col &finish_exploc,
558 enum range_display_kind range_display_kind,
559 const exploc_with_display_col &caret_exploc,
560 unsigned original_idx,
561 const range_label *label)
562 : m_start (start_exploc),
563 m_finish (finish_exploc),
564 m_range_display_kind (range_display_kind),
565 m_caret (caret_exploc),
566 m_original_idx (original_idx),
567 m_label (label)
571 /* Is (column, row) within the given range?
572 We've already filtered on the file.
574 Ranges are closed (both limits are within the range).
576 Example A: a single-line range:
577 start: (col=22, line=2)
578 finish: (col=38, line=2)
580 |00000011111111112222222222333333333344444444444
581 |34567890123456789012345678901234567890123456789
582 --+-----------------------------------------------
583 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
584 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
585 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
587 Example B: a multiline range with
588 start: (col=14, line=3)
589 finish: (col=08, line=5)
591 |00000011111111112222222222333333333344444444444
592 |34567890123456789012345678901234567890123456789
593 --+-----------------------------------------------
594 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
595 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
596 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
597 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
598 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
599 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
600 --+-----------------------------------------------
602 Legend:
603 - 'b' indicates a point *before* the range
604 - 'S' indicates the start of the range
605 - 'w' indicates a point within the range
606 - 'F' indicates the finish of the range (which is
607 within it).
608 - 'a' indicates a subsequent point *after* the range.
610 COL_UNIT controls whether we check the byte column or
611 the display column; one or the other is more convenient
612 depending on the context. */
614 bool
615 layout_range::contains_point (linenum_type row, int column,
616 enum column_unit col_unit) const
618 gcc_assert (m_start.m_line <= m_finish.m_line);
619 /* ...but the equivalent isn't true for the columns;
620 consider example B in the comment above. */
622 if (row < m_start.m_line)
623 /* Points before the first line of the range are
624 outside it (corresponding to line 01 in example A
625 and lines 01 and 02 in example B above). */
626 return false;
628 if (row == m_start.m_line)
629 /* On same line as start of range (corresponding
630 to line 02 in example A and line 03 in example B). */
632 if (column < m_start.m_columns[col_unit])
633 /* Points on the starting line of the range, but
634 before the column in which it begins. */
635 return false;
637 if (row < m_finish.m_line)
638 /* This is a multiline range; the point
639 is within it (corresponds to line 03 in example B
640 from column 14 onwards) */
641 return true;
642 else
644 /* This is a single-line range. */
645 gcc_assert (row == m_finish.m_line);
646 return column <= m_finish.m_columns[col_unit];
650 /* The point is in a line beyond that containing the
651 start of the range: lines 03 onwards in example A,
652 and lines 04 onwards in example B. */
653 gcc_assert (row > m_start.m_line);
655 if (row > m_finish.m_line)
656 /* The point is beyond the final line of the range
657 (lines 03 onwards in example A, and lines 06 onwards
658 in example B). */
659 return false;
661 if (row < m_finish.m_line)
663 /* The point is in a line that's fully within a multiline
664 range (e.g. line 04 in example B). */
665 gcc_assert (m_start.m_line < m_finish.m_line);
666 return true;
669 gcc_assert (row == m_finish.m_line);
671 return column <= m_finish.m_columns[col_unit];
674 /* Does this layout_range contain any part of line ROW? */
676 bool
677 layout_range::intersects_line_p (linenum_type row) const
679 gcc_assert (m_start.m_line <= m_finish.m_line);
680 if (row < m_start.m_line)
681 return false;
682 if (row > m_finish.m_line)
683 return false;
684 return true;
687 #if CHECKING_P
689 /* Default for when we don't care what the tab expansion is set to. */
690 static const int def_tabstop = 8;
692 static cpp_char_column_policy def_policy ()
694 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
697 /* Create some expanded locations for testing layout_range. The filename
698 member of the explocs is set to the empty string. This member will only be
699 inspected by the calls to location_compute_display_column() made from the
700 layout_point constructors. That function will check for an empty filename
701 argument and not attempt to open it, rather treating the non-existent data
702 as if the display width were the same as the byte count. Tests exercising a
703 real difference between byte count and display width are performed later,
704 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
706 static layout_range
707 make_range (int start_line, int start_col, int end_line, int end_col)
709 const expanded_location start_exploc
710 = {"", start_line, start_col, NULL, false};
711 const expanded_location finish_exploc
712 = {"", end_line, end_col, NULL, false};
713 return layout_range (exploc_with_display_col (start_exploc, def_policy (),
714 LOCATION_ASPECT_START),
715 exploc_with_display_col (finish_exploc, def_policy (),
716 LOCATION_ASPECT_FINISH),
717 SHOW_RANGE_WITHOUT_CARET,
718 exploc_with_display_col (start_exploc, def_policy (),
719 LOCATION_ASPECT_CARET),
720 0, NULL);
723 /* Selftests for layout_range::contains_point and
724 layout_range::intersects_line_p. */
726 /* Selftest for layout_range, where the layout_range
727 is a range with start==end i.e. a single point. */
729 static void
730 test_layout_range_for_single_point ()
732 layout_range point = make_range (7, 10, 7, 10);
734 /* Tests for layout_range::contains_point. */
736 for (int i = 0; i != CU_NUM_UNITS; ++i)
738 const enum column_unit col_unit = (enum column_unit) i;
740 /* Before the line. */
741 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
743 /* On the line, but before start. */
744 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
746 /* At the point. */
747 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
749 /* On the line, after the point. */
750 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
752 /* After the line. */
753 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
756 /* Tests for layout_range::intersects_line_p. */
757 ASSERT_FALSE (point.intersects_line_p (6));
758 ASSERT_TRUE (point.intersects_line_p (7));
759 ASSERT_FALSE (point.intersects_line_p (8));
762 /* Selftest for layout_range, where the layout_range
763 is the single-line range shown as "Example A" above. */
765 static void
766 test_layout_range_for_single_line ()
768 layout_range example_a = make_range (2, 22, 2, 38);
770 /* Tests for layout_range::contains_point. */
772 for (int i = 0; i != CU_NUM_UNITS; ++i)
774 const enum column_unit col_unit = (enum column_unit) i;
776 /* Before the line. */
777 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
779 /* On the line, but before start. */
780 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
782 /* On the line, at the start. */
783 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
785 /* On the line, within the range. */
786 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
788 /* On the line, at the end. */
789 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
791 /* On the line, after the end. */
792 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
794 /* After the line. */
795 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
798 /* Tests for layout_range::intersects_line_p. */
799 ASSERT_FALSE (example_a.intersects_line_p (1));
800 ASSERT_TRUE (example_a.intersects_line_p (2));
801 ASSERT_FALSE (example_a.intersects_line_p (3));
804 /* Selftest for layout_range, where the layout_range
805 is the multi-line range shown as "Example B" above. */
807 static void
808 test_layout_range_for_multiple_lines ()
810 layout_range example_b = make_range (3, 14, 5, 8);
812 /* Tests for layout_range::contains_point. */
814 for (int i = 0; i != CU_NUM_UNITS; ++i)
816 const enum column_unit col_unit = (enum column_unit) i;
818 /* Before first line. */
819 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
821 /* On the first line, but before start. */
822 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
824 /* At the start. */
825 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
827 /* On the first line, within the range. */
828 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
830 /* On an interior line.
831 The column number should not matter; try various boundary
832 values. */
833 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
834 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
835 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
836 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
837 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
838 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
839 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
841 /* On the final line, before the end. */
842 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
844 /* On the final line, at the end. */
845 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
847 /* On the final line, after the end. */
848 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
850 /* After the line. */
851 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
854 /* Tests for layout_range::intersects_line_p. */
855 ASSERT_FALSE (example_b.intersects_line_p (2));
856 ASSERT_TRUE (example_b.intersects_line_p (3));
857 ASSERT_TRUE (example_b.intersects_line_p (4));
858 ASSERT_TRUE (example_b.intersects_line_p (5));
859 ASSERT_FALSE (example_b.intersects_line_p (6));
862 #endif /* #if CHECKING_P */
864 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
865 (still in bytes, not display cols) without any trailing whitespace. */
867 static int
868 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
870 int result = line_bytes;
871 while (result > 0)
873 char ch = line[result - 1];
874 if (ch == ' ' || ch == '\t' || ch == '\r')
875 result--;
876 else
877 break;
879 gcc_assert (result >= 0);
880 gcc_assert (result <= line_bytes);
881 gcc_assert (result == 0 ||
882 (line[result - 1] != ' '
883 && line[result -1] != '\t'
884 && line[result -1] != '\r'));
885 return result;
888 #if CHECKING_P
890 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
892 static void
893 assert_eq (const char *line, int expected_bytes)
895 int actual_value
896 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
897 ASSERT_EQ (actual_value, expected_bytes);
900 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
901 various inputs. It is not required to handle newlines. */
903 static void
904 test_get_line_bytes_without_trailing_whitespace ()
906 assert_eq ("", 0);
907 assert_eq (" ", 0);
908 assert_eq ("\t", 0);
909 assert_eq ("\r", 0);
910 assert_eq ("hello world", 11);
911 assert_eq ("hello world ", 11);
912 assert_eq ("hello world \t\t ", 11);
913 assert_eq ("hello world\r", 11);
916 #endif /* #if CHECKING_P */
918 /* Helper function for layout's ctor, for sanitizing locations relative
919 to the primary location within a diagnostic.
921 Compare LOC_A and LOC_B to see if it makes sense to print underlines
922 connecting their expanded locations. Doing so is only guaranteed to
923 make sense if the locations share the same macro expansion "history"
924 i.e. they can be traced through the same macro expansions, eventually
925 reaching an ordinary map.
927 This may be too strong a condition, but it effectively sanitizes
928 PR c++/70105, which has an example of printing an expression where the
929 final location of the expression is in a different macro, which
930 erroneously was leading to hundreds of lines of irrelevant source
931 being printed. */
933 static bool
934 compatible_locations_p (location_t loc_a, location_t loc_b)
936 if (IS_ADHOC_LOC (loc_a))
937 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
938 if (IS_ADHOC_LOC (loc_b))
939 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
941 /* If either location is one of the special locations outside of a
942 linemap, they are only compatible if they are equal. */
943 if (loc_a < RESERVED_LOCATION_COUNT
944 || loc_b < RESERVED_LOCATION_COUNT)
945 return loc_a == loc_b;
947 const line_map *map_a = linemap_lookup (line_table, loc_a);
948 linemap_assert (map_a);
950 const line_map *map_b = linemap_lookup (line_table, loc_b);
951 linemap_assert (map_b);
953 /* Are they within the same map? */
954 if (map_a == map_b)
956 /* Are both within the same macro expansion? */
957 if (linemap_macro_expansion_map_p (map_a))
959 /* If so, then they're only compatible if either both are
960 from the macro definition, or both from the macro arguments. */
961 bool loc_a_from_defn
962 = linemap_location_from_macro_definition_p (line_table, loc_a);
963 bool loc_b_from_defn
964 = linemap_location_from_macro_definition_p (line_table, loc_b);
965 if (loc_a_from_defn != loc_b_from_defn)
966 return false;
968 /* Expand each location towards the spelling location, and
969 recurse. */
970 const line_map_macro *macro_map = linemap_check_macro (map_a);
971 location_t loc_a_toward_spelling
972 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
973 macro_map,
974 loc_a);
975 location_t loc_b_toward_spelling
976 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
977 macro_map,
978 loc_b);
979 return compatible_locations_p (loc_a_toward_spelling,
980 loc_b_toward_spelling);
983 /* Otherwise they are within the same ordinary map. */
984 return true;
986 else
988 /* Within different maps. */
990 /* If either is within a macro expansion, they are incompatible. */
991 if (linemap_macro_expansion_map_p (map_a)
992 || linemap_macro_expansion_map_p (map_b))
993 return false;
995 /* Within two different ordinary maps; they are compatible iff they
996 are in the same file. */
997 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
998 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
999 return ord_map_a->to_file == ord_map_b->to_file;
1003 /* Comparator for sorting fix-it hints. */
1005 static int
1006 fixit_cmp (const void *p_a, const void *p_b)
1008 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1009 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1010 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1013 /* Callbacks for use when not escaping the source. */
1015 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1017 /* Callback for char_display_policy::m_print_cb for printing source chars
1018 when not escaping the source. */
1020 static void
1021 default_print_decoded_ch (pretty_printer *pp,
1022 const cpp_decoded_char &decoded_ch)
1024 for (const char *ptr = decoded_ch.m_start_byte;
1025 ptr != decoded_ch.m_next_byte; ptr++)
1027 if (*ptr == '\0' || *ptr == '\r')
1029 pp_space (pp);
1030 continue;
1033 pp_character (pp, *ptr);
1037 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1039 static const int width_per_escaped_byte = 4;
1041 /* Callback for char_column_policy::m_width_cb for determining the
1042 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1044 static int
1045 escape_as_bytes_width (cppchar_t ch)
1047 if (ch < 0x80 && ISPRINT (ch))
1048 return cpp_wcwidth (ch);
1049 else
1051 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1052 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1053 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1054 return 4 * width_per_escaped_byte;
1058 /* Callback for char_display_policy::m_print_cb for printing source chars
1059 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1061 static void
1062 escape_as_bytes_print (pretty_printer *pp,
1063 const cpp_decoded_char &decoded_ch)
1065 if (!decoded_ch.m_valid_ch)
1067 for (const char *iter = decoded_ch.m_start_byte;
1068 iter != decoded_ch.m_next_byte; ++iter)
1070 char buf[16];
1071 sprintf (buf, "<%02x>", (unsigned char)*iter);
1072 pp_string (pp, buf);
1074 return;
1077 cppchar_t ch = decoded_ch.m_ch;
1078 if (ch < 0x80 && ISPRINT (ch))
1079 pp_character (pp, ch);
1080 else
1082 for (const char *iter = decoded_ch.m_start_byte;
1083 iter < decoded_ch.m_next_byte; ++iter)
1085 char buf[16];
1086 sprintf (buf, "<%02x>", (unsigned char)*iter);
1087 pp_string (pp, buf);
1092 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1094 /* Callback for char_column_policy::m_width_cb for determining the
1095 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1097 static int
1098 escape_as_unicode_width (cppchar_t ch)
1100 if (ch < 0x80 && ISPRINT (ch))
1101 return cpp_wcwidth (ch);
1102 else
1104 // Width of "<U+%04x>"
1105 if (ch > 0xfffff)
1106 return 10;
1107 else if (ch > 0xffff)
1108 return 9;
1109 else
1110 return 8;
1114 /* Callback for char_display_policy::m_print_cb for printing source chars
1115 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1117 static void
1118 escape_as_unicode_print (pretty_printer *pp,
1119 const cpp_decoded_char &decoded_ch)
1121 if (!decoded_ch.m_valid_ch)
1123 escape_as_bytes_print (pp, decoded_ch);
1124 return;
1127 cppchar_t ch = decoded_ch.m_ch;
1128 if (ch < 0x80 && ISPRINT (ch))
1129 pp_character (pp, ch);
1130 else
1132 char buf[16];
1133 sprintf (buf, "<U+%04X>", ch);
1134 pp_string (pp, buf);
1138 /* Populate a char_display_policy based on DC and RICHLOC. */
1140 static char_display_policy
1141 make_policy (const diagnostic_context &dc,
1142 const rich_location &richloc)
1144 /* The default is to not escape non-ASCII bytes. */
1145 char_display_policy result
1146 (dc.m_tabstop, cpp_wcwidth, default_print_decoded_ch);
1148 /* If the diagnostic suggests escaping non-ASCII bytes, then
1149 use policy from user-supplied options. */
1150 if (richloc.escape_on_output_p ())
1152 result.m_undecoded_byte_width = width_per_escaped_byte;
1153 switch (dc.get_escape_format ())
1155 default:
1156 gcc_unreachable ();
1157 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1158 result.m_width_cb = escape_as_unicode_width;
1159 result.m_print_cb = escape_as_unicode_print;
1160 break;
1161 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1162 result.m_width_cb = escape_as_bytes_width;
1163 result.m_print_cb = escape_as_bytes_print;
1164 break;
1168 return result;
1171 /* Implementation of class layout. */
1173 /* Constructor for class layout.
1175 Filter the ranges from the rich_location to those that we can
1176 sanely print, populating m_layout_ranges and m_fixit_hints.
1177 Determine the range of lines that we will print, splitting them
1178 up into an ordered list of disjoint spans of contiguous line numbers.
1179 Determine m_x_offset_display, to ensure that the primary caret
1180 will fit within the max_width provided by the diagnostic_context. */
1182 layout::layout (const diagnostic_context &context,
1183 rich_location *richloc,
1184 diagnostic_t diagnostic_kind,
1185 pretty_printer *pp)
1186 : m_options (context.m_source_printing),
1187 m_pp (pp ? pp : context.printer),
1188 m_policy (make_policy (context, *richloc)),
1189 m_primary_loc (richloc->get_range (0)->m_loc),
1190 m_exploc (richloc->get_expanded_location (0), m_policy,
1191 LOCATION_ASPECT_CARET),
1192 m_colorizer (m_pp, diagnostic_kind),
1193 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1194 m_layout_ranges (richloc->get_num_locations ()),
1195 m_fixit_hints (richloc->get_num_fixit_hints ()),
1196 m_line_spans (1 + richloc->get_num_locations ()),
1197 m_linenum_width (0),
1198 m_x_offset_display (0),
1199 m_escape_on_output (richloc->escape_on_output_p ())
1201 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
1203 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1204 Ignore any ranges that are awkward to handle. */
1205 const location_range *loc_range = richloc->get_range (idx);
1206 maybe_add_location_range (loc_range, idx, false);
1209 /* Populate m_fixit_hints, filtering to only those that are in the
1210 same file. */
1211 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1213 const fixit_hint *hint = richloc->get_fixit_hint (i);
1214 if (validate_fixit_hint_p (hint))
1215 m_fixit_hints.safe_push (hint);
1218 /* Sort m_fixit_hints. */
1219 m_fixit_hints.qsort (fixit_cmp);
1221 /* Populate the indicated members. */
1222 calculate_line_spans ();
1223 calculate_linenum_width ();
1224 calculate_x_offset_display ();
1226 if (m_options.show_ruler_p)
1227 show_ruler (m_x_offset_display + m_options.max_width);
1231 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1232 those that we can sanely print.
1234 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1235 (for use as extrinsic state by label ranges FIXME).
1237 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1238 filtered against this layout instance's current line spans: it
1239 will only be added if the location is fully within the lines
1240 already specified by other locations.
1242 Return true iff LOC_RANGE was added. */
1244 bool
1245 layout::maybe_add_location_range (const location_range *loc_range,
1246 unsigned original_idx,
1247 bool restrict_to_current_line_spans)
1249 gcc_assert (loc_range);
1251 /* Split the "range" into caret and range information. */
1252 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1254 /* Expand the various locations. */
1255 expanded_location start
1256 = linemap_client_expand_location_to_spelling_point
1257 (src_range.m_start, LOCATION_ASPECT_START);
1258 expanded_location finish
1259 = linemap_client_expand_location_to_spelling_point
1260 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1261 expanded_location caret
1262 = linemap_client_expand_location_to_spelling_point
1263 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1265 /* If any part of the range isn't in the same file as the primary
1266 location of this diagnostic, ignore the range. */
1267 if (start.file != m_exploc.file)
1268 return false;
1269 if (finish.file != m_exploc.file)
1270 return false;
1271 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1272 if (caret.file != m_exploc.file)
1273 return false;
1275 /* Sanitize the caret location for non-primary ranges. */
1276 if (m_layout_ranges.length () > 0)
1277 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1278 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1279 /* Discard any non-primary ranges that can't be printed
1280 sanely relative to the primary location. */
1281 return false;
1283 /* Everything is now known to be in the correct source file,
1284 but it may require further sanitization. */
1285 layout_range ri (exploc_with_display_col (start, m_policy,
1286 LOCATION_ASPECT_START),
1287 exploc_with_display_col (finish, m_policy,
1288 LOCATION_ASPECT_FINISH),
1289 loc_range->m_range_display_kind,
1290 exploc_with_display_col (caret, m_policy,
1291 LOCATION_ASPECT_CARET),
1292 original_idx, loc_range->m_label);
1294 /* If we have a range that finishes before it starts (perhaps
1295 from something built via macro expansion), printing the
1296 range is likely to be nonsensical. Also, attempting to do so
1297 breaks assumptions within the printing code (PR c/68473).
1298 Similarly, don't attempt to print ranges if one or both ends
1299 of the range aren't sane to print relative to the
1300 primary location (PR c++/70105). */
1301 if (start.line > finish.line
1302 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1303 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1305 /* Is this the primary location? */
1306 if (m_layout_ranges.length () == 0)
1308 /* We want to print the caret for the primary location, but
1309 we must sanitize away m_start and m_finish. */
1310 ri.m_start = ri.m_caret;
1311 ri.m_finish = ri.m_caret;
1313 else
1314 /* This is a non-primary range; ignore it. */
1315 return false;
1318 /* Potentially filter to just the lines already specified by other
1319 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1320 The layout ctor doesn't use it, and can't because m_line_spans
1321 hasn't been set up at that point. */
1322 if (restrict_to_current_line_spans)
1324 if (!will_show_line_p (start.line))
1325 return false;
1326 if (!will_show_line_p (finish.line))
1327 return false;
1328 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1329 if (!will_show_line_p (caret.line))
1330 return false;
1333 /* Passed all the tests; add the range to m_layout_ranges so that
1334 it will be printed. */
1335 m_layout_ranges.safe_push (ri);
1336 return true;
1339 /* Return true iff ROW is within one of the line spans for this layout. */
1341 bool
1342 layout::will_show_line_p (linenum_type row) const
1344 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1345 line_span_idx++)
1347 const line_span *line_span = get_line_span (line_span_idx);
1348 if (line_span->contains_line_p (row))
1349 return true;
1351 return false;
1354 /* Print a line showing a gap in the line numbers, for showing the boundary
1355 between two line spans. */
1357 void
1358 layout::print_gap_in_line_numbering ()
1360 gcc_assert (m_options.show_line_numbers_p);
1362 pp_emit_prefix (m_pp);
1364 for (int i = 0; i < m_linenum_width + 1; i++)
1365 pp_character (m_pp, '.');
1367 pp_newline (m_pp);
1370 /* Return true iff we should print a heading when starting the
1371 line span with the given index. */
1373 bool
1374 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1376 /* We print a heading for every change of line span, hence for every
1377 line span after the initial one. */
1378 if (line_span_idx > 0)
1379 return true;
1381 /* We also do it for the initial span if the primary location of the
1382 diagnostic is in a different span. */
1383 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1384 return true;
1386 return false;
1389 /* Get an expanded_location for the first location of interest within
1390 the given line_span.
1391 Used when printing a heading to indicate a new line span. */
1393 expanded_location
1394 layout::get_expanded_location (const line_span *line_span) const
1396 /* Whenever possible, use the caret location. */
1397 if (line_span->contains_line_p (m_exploc.line))
1398 return m_exploc;
1400 /* Otherwise, use the start of the first range that's present
1401 within the line_span. */
1402 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1404 const layout_range *lr = &m_layout_ranges[i];
1405 if (line_span->contains_line_p (lr->m_start.m_line))
1407 expanded_location exploc = m_exploc;
1408 exploc.line = lr->m_start.m_line;
1409 exploc.column = lr->m_start.m_columns[CU_BYTES];
1410 return exploc;
1414 /* Otherwise, use the location of the first fixit-hint present within
1415 the line_span. */
1416 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1418 const fixit_hint *hint = m_fixit_hints[i];
1419 location_t loc = hint->get_start_loc ();
1420 expanded_location exploc = expand_location (loc);
1421 if (line_span->contains_line_p (exploc.line))
1422 return exploc;
1425 /* It should not be possible to have a line span that didn't
1426 contain any of the layout_range or fixit_hint instances. */
1427 gcc_unreachable ();
1428 return m_exploc;
1431 /* Determine if HINT is meaningful to print within this layout. */
1433 bool
1434 layout::validate_fixit_hint_p (const fixit_hint *hint)
1436 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1437 return false;
1438 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1439 return false;
1441 return true;
1444 /* Determine the range of lines affected by HINT.
1445 This assumes that HINT has already been filtered by
1446 validate_fixit_hint_p, and so affects the correct source file. */
1448 static line_span
1449 get_line_span_for_fixit_hint (const fixit_hint *hint)
1451 gcc_assert (hint);
1453 int start_line = LOCATION_LINE (hint->get_start_loc ());
1455 /* For line-insertion fix-it hints, add the previous line to the
1456 span, to give the user more context on the proposed change. */
1457 if (hint->ends_with_newline_p ())
1458 if (start_line > 1)
1459 start_line--;
1461 return line_span (start_line,
1462 LOCATION_LINE (hint->get_next_loc ()));
1465 /* We want to print the pertinent source code at a diagnostic. The
1466 rich_location can contain multiple locations. This will have been
1467 filtered into m_exploc (the caret for the primary location) and
1468 m_layout_ranges, for those ranges within the same source file.
1470 We will print a subset of the lines within the source file in question,
1471 as a collection of "spans" of lines.
1473 This function populates m_line_spans with an ordered, disjoint list of
1474 the line spans of interest.
1476 Printing a gap between line spans takes one line, so, when printing
1477 line numbers, we allow a gap of up to one line between spans when
1478 merging, since it makes more sense to print the source line rather than a
1479 "gap-in-line-numbering" line. When not printing line numbers, it's
1480 better to be more explicit about what's going on, so keeping them as
1481 separate spans is preferred.
1483 For example, if the primary range is on lines 8-10, with secondary ranges
1484 covering lines 5-6 and lines 13-15:
1487 005 |RANGE 1
1488 006 |RANGE 1
1490 008 |PRIMARY RANGE
1491 009 |PRIMARY CARET
1492 010 |PRIMARY RANGE
1495 013 |RANGE 2
1496 014 |RANGE 2
1497 015 |RANGE 2
1500 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1502 With line numbering off (with span headers), we want three spans: lines 5-6,
1503 lines 8-10, and lines 13-15. */
1505 void
1506 layout::calculate_line_spans ()
1508 /* This should only be called once, by the ctor. */
1509 gcc_assert (m_line_spans.length () == 0);
1511 /* Populate tmp_spans with individual spans, for each of
1512 m_exploc, and for m_layout_ranges. */
1513 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1514 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1515 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1517 const layout_range *lr = &m_layout_ranges[i];
1518 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1519 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1520 lr->m_finish.m_line));
1523 /* Also add spans for any fix-it hints, in case they cover other lines. */
1524 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1526 const fixit_hint *hint = m_fixit_hints[i];
1527 gcc_assert (hint);
1528 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1531 /* Sort them. */
1532 tmp_spans.qsort(line_span::comparator);
1534 /* Now iterate through tmp_spans, copying into m_line_spans, and
1535 combining where possible. */
1536 gcc_assert (tmp_spans.length () > 0);
1537 m_line_spans.safe_push (tmp_spans[0]);
1538 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1540 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1541 const line_span *next = &tmp_spans[i];
1542 gcc_assert (next->m_first_line >= current->m_first_line);
1543 const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
1544 if ((linenum_arith_t)next->m_first_line
1545 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1547 /* We can merge them. */
1548 if (next->m_last_line > current->m_last_line)
1549 current->m_last_line = next->m_last_line;
1551 else
1553 /* No merger possible. */
1554 m_line_spans.safe_push (*next);
1558 /* Verify the result, in m_line_spans. */
1559 gcc_assert (m_line_spans.length () > 0);
1560 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1562 const line_span *prev = &m_line_spans[i - 1];
1563 const line_span *next = &m_line_spans[i];
1564 /* The individual spans must be sane. */
1565 gcc_assert (prev->m_first_line <= prev->m_last_line);
1566 gcc_assert (next->m_first_line <= next->m_last_line);
1567 /* The spans must be ordered. */
1568 gcc_assert (prev->m_first_line < next->m_first_line);
1569 /* There must be a gap of at least one line between separate spans. */
1570 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1574 /* Determine how many display columns need to be reserved for line numbers,
1575 based on the largest line number that will be needed, and populate
1576 m_linenum_width. */
1578 void
1579 layout::calculate_linenum_width ()
1581 gcc_assert (m_line_spans.length () > 0);
1582 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1583 int highest_line = last_span->m_last_line;
1584 if (highest_line < 0)
1585 highest_line = 0;
1586 m_linenum_width = num_digits (highest_line);
1587 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1588 if (m_line_spans.length () > 1)
1589 m_linenum_width = MAX (m_linenum_width, 3);
1590 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1591 after the line number. */
1592 m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
1595 /* Calculate m_x_offset_display, which improves readability in case the source
1596 line of interest is longer than the user's display. All lines output will be
1597 shifted to the left (so that their beginning is no longer displayed) by
1598 m_x_offset_display display columns, so that the caret is in a reasonable
1599 location. */
1601 void
1602 layout::calculate_x_offset_display ()
1604 m_x_offset_display = 0;
1606 const int max_width = m_options.max_width;
1607 if (!max_width)
1609 /* Nothing to do, the width is not capped. */
1610 return;
1613 const char_span line = location_get_source_line (m_exploc.file,
1614 m_exploc.line);
1615 if (!line)
1617 /* Nothing to do, we couldn't find the source line. */
1618 return;
1620 int caret_display_column = m_exploc.m_display_col;
1621 const int line_bytes
1622 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1623 line.length ());
1624 int eol_display_column
1625 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1626 if (caret_display_column > eol_display_column
1627 || !caret_display_column)
1629 /* This does not make sense, so don't try to do anything in this case. */
1630 return;
1633 /* Adjust caret and eol positions to include the left margin. If we are
1634 outputting line numbers, then the left margin is equal to m_linenum_width
1635 plus three for the " | " which follows it. Otherwise the left margin width
1636 is equal to 1, because layout::print_source_line() will prefix each line
1637 with a space. */
1638 const int source_display_cols = eol_display_column;
1639 int left_margin_size = 1;
1640 if (m_options.show_line_numbers_p)
1641 left_margin_size = m_linenum_width + 3;
1642 caret_display_column += left_margin_size;
1643 eol_display_column += left_margin_size;
1645 if (eol_display_column <= max_width)
1647 /* Nothing to do, everything fits in the display. */
1648 return;
1651 /* The line is too long for the display. Calculate an offset such that the
1652 caret is not too close to the right edge of the screen. It will be
1653 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1654 than that to the end of the source line anyway. */
1655 int right_margin_size = CARET_LINE_MARGIN;
1656 right_margin_size = MIN (eol_display_column - caret_display_column,
1657 right_margin_size);
1658 if (right_margin_size + left_margin_size >= max_width)
1660 /* The max_width is very small, so anything we try to do will not be very
1661 effective; just punt in this case and output with no offset. */
1662 return;
1664 const int max_caret_display_column = max_width - right_margin_size;
1665 if (caret_display_column > max_caret_display_column)
1667 m_x_offset_display = caret_display_column - max_caret_display_column;
1668 /* Make sure we don't offset the line into oblivion. */
1669 static const int min_cols_visible = 2;
1670 if (source_display_cols - m_x_offset_display < min_cols_visible)
1671 m_x_offset_display = 0;
1675 /* Print line ROW of source code, potentially colorized at any ranges, and
1676 return the line bounds. LINE is the source line (not necessarily
1677 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1678 colorization and tab expansion, this function tracks the line position in
1679 both byte and display column units. */
1681 line_bounds
1682 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1684 m_colorizer.set_normal_text ();
1686 pp_emit_prefix (m_pp);
1687 if (m_options.show_line_numbers_p)
1689 int width = num_digits (row);
1690 for (int i = 0; i < m_linenum_width - width; i++)
1691 pp_space (m_pp);
1692 pp_printf (m_pp, "%i | ", row);
1694 else
1695 pp_space (m_pp);
1697 /* We will stop printing the source line at any trailing whitespace. */
1698 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1699 line_bytes);
1701 /* This object helps to keep track of which display column we are at, which is
1702 necessary for computing the line bounds in display units, for doing
1703 tab expansion, and for implementing m_x_offset_display. */
1704 cpp_display_width_computation dw (line, line_bytes, m_policy);
1706 /* Skip the first m_x_offset_display display columns. In case the leading
1707 portion that will be skipped ends with a character with wcwidth > 1, then
1708 it is possible we skipped too much, so account for that by padding with
1709 spaces. Note that this does the right thing too in case a tab was the last
1710 character to be skipped over; the tab is effectively replaced by the
1711 correct number of trailing spaces needed to offset by the desired number of
1712 display columns. */
1713 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1714 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1715 pp_space (m_pp);
1717 /* Print the line and compute the line_bounds. */
1718 line_bounds lbounds;
1719 while (!dw.done ())
1721 /* Assuming colorization is enabled for the caret and underline
1722 characters, we may also colorize the associated characters
1723 within the source line.
1725 For frontends that generate range information, we color the
1726 associated characters in the source line the same as the
1727 carets and underlines in the annotation line, to make it easier
1728 for the reader to see the pertinent code.
1730 For frontends that only generate carets, we don't colorize the
1731 characters above them, since this would look strange (e.g.
1732 colorizing just the first character in a token). */
1733 if (m_options.colorize_source_p)
1735 bool in_range_p;
1736 point_state state;
1737 const int start_byte_col = dw.bytes_processed () + 1;
1738 in_range_p = get_state_at_point (row, start_byte_col,
1739 0, INT_MAX,
1740 CU_BYTES,
1741 &state);
1742 if (in_range_p)
1743 m_colorizer.set_range (state.range_idx);
1744 else
1745 m_colorizer.set_normal_text ();
1748 /* Get the display width of the next character to be output, expanding
1749 tabs and replacing some control bytes with spaces as necessary. */
1750 const char *c = dw.next_byte ();
1751 const int start_disp_col = dw.display_cols_processed () + 1;
1752 cpp_decoded_char cp;
1753 const int this_display_width = dw.process_next_codepoint (&cp);
1754 if (*c == '\t')
1756 /* The returned display width is the number of spaces into which the
1757 tab should be expanded. */
1758 for (int i = 0; i != this_display_width; ++i)
1759 pp_space (m_pp);
1760 continue;
1763 /* We have a (possibly multibyte) character to output; update the line
1764 bounds if it is not whitespace. */
1765 if (*c != ' ')
1767 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1768 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1769 lbounds.m_first_non_ws_disp_col = start_disp_col;
1772 /* Output the character. */
1773 m_policy.m_print_cb (m_pp, cp);
1774 c = dw.next_byte ();
1776 print_newline ();
1777 return lbounds;
1780 /* Determine if we should print an annotation line for ROW.
1781 i.e. if any of m_layout_ranges contains ROW. */
1783 bool
1784 layout::should_print_annotation_line_p (linenum_type row) const
1786 layout_range *range;
1787 int i;
1788 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1790 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1791 return false;
1792 if (range->intersects_line_p (row))
1793 return true;
1795 return false;
1798 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1799 margin, which is empty for annotation lines. Otherwise, do nothing. */
1801 void
1802 layout::start_annotation_line (char margin_char) const
1804 pp_emit_prefix (m_pp);
1805 if (m_options.show_line_numbers_p)
1807 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1808 of it, right-aligned, padded with spaces. */
1809 int i;
1810 for (i = 0; i < m_linenum_width - 3; i++)
1811 pp_space (m_pp);
1812 for (; i < m_linenum_width; i++)
1813 pp_character (m_pp, margin_char);
1814 pp_string (m_pp, " |");
1818 /* Print a line consisting of the caret/underlines for the given
1819 source line. */
1821 void
1822 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1824 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1825 lbounds.m_last_non_ws_disp_col);
1827 start_annotation_line ();
1828 pp_space (m_pp);
1830 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1832 bool in_range_p;
1833 point_state state;
1834 in_range_p = get_state_at_point (row, column,
1835 lbounds.m_first_non_ws_disp_col,
1836 lbounds.m_last_non_ws_disp_col,
1837 CU_DISPLAY_COLS,
1838 &state);
1839 if (in_range_p)
1841 /* Within a range. Draw either the caret or an underline. */
1842 m_colorizer.set_range (state.range_idx);
1843 if (state.draw_caret_p)
1845 /* Draw the caret. */
1846 char caret_char;
1847 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1848 caret_char = m_options.caret_chars[state.range_idx];
1849 else
1850 caret_char = '^';
1851 pp_character (m_pp, caret_char);
1853 else
1854 pp_character (m_pp, '~');
1856 else
1858 /* Not in a range. */
1859 m_colorizer.set_normal_text ();
1860 pp_character (m_pp, ' ');
1863 print_newline ();
1866 /* A version of label_text that can live inside a vec.
1867 Requires manual cleanup via maybe_free. */
1869 struct pod_label_text
1871 pod_label_text ()
1872 : m_buffer (NULL), m_caller_owned (false)
1875 pod_label_text (label_text &&other)
1876 : m_buffer (const_cast<char*> (other.get ())),
1877 m_caller_owned (other.is_owner ())
1879 other.release ();
1882 void maybe_free ()
1884 if (m_caller_owned)
1885 free (m_buffer);
1888 char *m_buffer;
1889 bool m_caller_owned;
1892 /* Implementation detail of layout::print_any_labels.
1894 A label within the given row of source. */
1896 class line_label
1898 public:
1899 line_label (const cpp_char_column_policy &policy,
1900 int state_idx, int column,
1901 label_text text)
1902 : m_state_idx (state_idx), m_column (column),
1903 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1905 const int bytes = strlen (m_text.m_buffer);
1906 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1909 /* Sorting is primarily by column, then by state index. */
1910 static int comparator (const void *p1, const void *p2)
1912 const line_label *ll1 = (const line_label *)p1;
1913 const line_label *ll2 = (const line_label *)p2;
1914 int column_cmp = compare (ll1->m_column, ll2->m_column);
1915 if (column_cmp)
1916 return column_cmp;
1917 /* Order by reverse state index, so that labels are printed
1918 in order of insertion into the rich_location when the
1919 sorted list is walked backwards. */
1920 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1923 int m_state_idx;
1924 int m_column;
1925 pod_label_text m_text;
1926 size_t m_display_width;
1927 int m_label_line;
1928 bool m_has_vbar;
1931 /* Print any labels in this row. */
1932 void
1933 layout::print_any_labels (linenum_type row)
1935 int i;
1936 auto_vec<line_label> labels;
1938 /* Gather the labels that are to be printed into "labels". */
1940 layout_range *range;
1941 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1943 /* Most ranges don't have labels, so reject this first. */
1944 if (range->m_label == NULL)
1945 continue;
1947 /* The range's caret must be on this line. */
1948 if (range->m_caret.m_line != row)
1949 continue;
1951 /* Reject labels that aren't fully visible due to clipping
1952 by m_x_offset_display. */
1953 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1954 if (disp_col <= m_x_offset_display)
1955 continue;
1957 label_text text;
1958 text = range->m_label->get_text (range->m_original_idx);
1960 /* Allow for labels that return NULL from their get_text
1961 implementation (so e.g. such labels can control their own
1962 visibility). */
1963 if (text.get () == NULL)
1964 continue;
1966 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1970 /* Bail out if there are no labels on this row. */
1971 if (labels.length () == 0)
1972 return;
1974 /* Sort them. */
1975 labels.qsort(line_label::comparator);
1977 /* Figure out how many "label lines" we need, and which
1978 one each label is printed in.
1980 For example, if the labels aren't too densely packed,
1981 we can fit them on the same line, giving two "label lines":
1983 foo + bar
1984 ~~~ ~~~
1985 | | : label line 0
1986 l0 l1 : label line 1
1988 If they would touch each other or overlap, then we need
1989 additional "label lines":
1991 foo + bar
1992 ~~~ ~~~
1993 | | : label line 0
1994 | label 1 : label line 1
1995 label 0 : label line 2
1997 Place the final label on label line 1, and work backwards, adding
1998 label lines as needed.
2000 If multiple labels are at the same place, put them on separate
2001 label lines:
2003 foo + bar
2004 ^ : label line 0
2005 | : label line 1
2006 label 0 : label line 2
2007 label 1 : label line 3. */
2009 int max_label_line = 1;
2011 int next_column = INT_MAX;
2012 line_label *label;
2013 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2015 /* Would this label "touch" or overlap the next label? */
2016 if (label->m_column + label->m_display_width >= (size_t)next_column)
2018 max_label_line++;
2020 /* If we've already seen labels with the same column, suppress the
2021 vertical bar for subsequent ones in this backwards iteration;
2022 hence only the one with the highest label_line has m_has_vbar set. */
2023 if (label->m_column == next_column)
2024 label->m_has_vbar = false;
2027 label->m_label_line = max_label_line;
2028 next_column = label->m_column;
2032 /* Print the "label lines". For each label within the line, print
2033 either a vertical bar ('|') for the labels that are lower down, or the
2034 labels themselves once we've reached their line. */
2036 for (int label_line = 0; label_line <= max_label_line; label_line++)
2038 start_annotation_line ();
2039 pp_space (m_pp);
2040 int column = 1 + m_x_offset_display;
2041 line_label *label;
2042 FOR_EACH_VEC_ELT (labels, i, label)
2044 if (label_line > label->m_label_line)
2045 /* We've printed all the labels for this label line. */
2046 break;
2048 if (label_line == label->m_label_line)
2050 gcc_assert (column <= label->m_column);
2051 move_to_column (&column, label->m_column, true);
2052 /* Colorize the text, unless it's for events in a
2053 diagnostic_path. */
2054 if (!m_diagnostic_path_p)
2055 m_colorizer.set_range (label->m_state_idx);
2056 pp_string (m_pp, label->m_text.m_buffer);
2057 m_colorizer.set_normal_text ();
2058 column += label->m_display_width;
2060 else if (label->m_has_vbar)
2062 gcc_assert (column <= label->m_column);
2063 move_to_column (&column, label->m_column, true);
2064 m_colorizer.set_range (label->m_state_idx);
2065 pp_character (m_pp, '|');
2066 m_colorizer.set_normal_text ();
2067 column++;
2070 print_newline ();
2074 /* Clean up. */
2076 line_label *label;
2077 FOR_EACH_VEC_ELT (labels, i, label)
2078 label->m_text.maybe_free ();
2082 /* If there are any fixit hints inserting new lines before source line ROW,
2083 print them.
2085 They are printed on lines of their own, before the source line
2086 itself, with a leading '+'. */
2088 void
2089 layout::print_leading_fixits (linenum_type row)
2091 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2093 const fixit_hint *hint = m_fixit_hints[i];
2095 if (!hint->ends_with_newline_p ())
2096 /* Not a newline fixit; print it in print_trailing_fixits. */
2097 continue;
2099 gcc_assert (hint->insertion_p ());
2101 if (hint->affects_line_p (m_exploc.file, row))
2103 /* Printing the '+' with normal colorization
2104 and the inserted line with "insert" colorization
2105 helps them stand out from each other, and from
2106 the surrounding text. */
2107 m_colorizer.set_normal_text ();
2108 start_annotation_line ('+');
2109 pp_character (m_pp, '+');
2110 m_colorizer.set_fixit_insert ();
2111 /* Print all but the trailing newline of the fix-it hint.
2112 We have to print the newline separately to avoid
2113 getting additional pp prefixes printed. */
2114 for (size_t i = 0; i < hint->get_length () - 1; i++)
2115 pp_character (m_pp, hint->get_string ()[i]);
2116 m_colorizer.set_normal_text ();
2117 pp_newline (m_pp);
2122 /* Subroutine of layout::print_trailing_fixits.
2124 Determine if the annotation line printed for LINE contained
2125 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2127 bool
2128 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2129 int finish_column) const
2131 layout_range *range;
2132 int i;
2133 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2134 if (range->m_start.m_line == line
2135 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2136 && range->m_finish.m_line == line
2137 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2138 return true;
2139 return false;
2142 /* Classes for printing trailing fix-it hints i.e. those that
2143 don't add new lines.
2145 For insertion, these can look like:
2147 new_text
2149 For replacement, these can look like:
2151 ------------- : underline showing affected range
2152 new_text
2154 For deletion, these can look like:
2156 ------------- : underline showing affected range
2158 This can become confusing if they overlap, and so we need
2159 to do some preprocessing to decide what to print.
2160 We use the list of fixit_hint instances affecting the line
2161 to build a list of "correction" instances, and print the
2162 latter.
2164 For example, consider a set of fix-its for converting
2165 a C-style cast to a C++ const_cast.
2167 Given:
2169 ..000000000111111111122222222223333333333.
2170 ..123456789012345678901234567890123456789.
2171 foo *f = (foo *)ptr->field;
2172 ^~~~~
2174 and the fix-it hints:
2175 - replace col 10 (the open paren) with "const_cast<"
2176 - replace col 16 (the close paren) with "> ("
2177 - insert ")" before col 27
2179 then we would get odd-looking output:
2181 foo *f = (foo *)ptr->field;
2182 ^~~~~
2184 const_cast<
2186 > ( )
2188 It would be better to detect when fixit hints are going to
2189 overlap (those that require new lines), and to consolidate
2190 the printing of such fixits, giving something like:
2192 foo *f = (foo *)ptr->field;
2193 ^~~~~
2194 -----------------
2195 const_cast<foo *> (ptr->field)
2197 This works by detecting when the printing would overlap, and
2198 effectively injecting no-op replace hints into the gaps between
2199 such fix-its, so that the printing joins up.
2201 In the above example, the overlap of:
2202 - replace col 10 (the open paren) with "const_cast<"
2203 and:
2204 - replace col 16 (the close paren) with "> ("
2205 is fixed by injecting a no-op:
2206 - replace cols 11-15 with themselves ("foo *")
2207 and consolidating these, making:
2208 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2209 i.e.:
2210 - replace cols 10-16 with "const_cast<foo *> ("
2212 This overlaps with the final fix-it hint:
2213 - insert ")" before col 27
2214 and so we repeat the consolidation process, by injecting
2215 a no-op:
2216 - replace cols 17-26 with themselves ("ptr->field")
2217 giving:
2218 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2219 i.e.:
2220 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2222 and is thus printed as desired. */
2224 /* A range of (byte or display) columns within a line. */
2226 class column_range
2228 public:
2229 column_range (int start_, int finish_) : start (start_), finish (finish_)
2231 gcc_assert (valid_p (start, finish));
2234 bool operator== (const column_range &other) const
2236 return start == other.start && finish == other.finish;
2239 static bool valid_p (int start, int finish)
2241 /* We must have either a range, or an insertion. */
2242 return (start <= finish || finish == start - 1);
2245 int start;
2246 int finish;
2249 /* Get the range of bytes or display columns that HINT would affect. */
2250 static column_range
2251 get_affected_range (const cpp_char_column_policy &policy,
2252 const fixit_hint *hint, enum column_unit col_unit)
2254 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2255 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2256 --exploc_finish.column;
2258 int start_column;
2259 int finish_column;
2260 if (col_unit == CU_DISPLAY_COLS)
2262 start_column = location_compute_display_column (exploc_start, policy);
2263 if (hint->insertion_p ())
2264 finish_column = start_column - 1;
2265 else
2266 finish_column = location_compute_display_column (exploc_finish, policy);
2268 else
2270 start_column = exploc_start.column;
2271 finish_column = exploc_finish.column;
2273 return column_range (start_column, finish_column);
2276 /* Get the range of display columns that would be printed for HINT. */
2278 static column_range
2279 get_printed_columns (const cpp_char_column_policy &policy,
2280 const fixit_hint *hint)
2282 expanded_location exploc = expand_location (hint->get_start_loc ());
2283 int start_column = location_compute_display_column (exploc, policy);
2284 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2285 policy);
2286 int final_hint_column = start_column + hint_width - 1;
2287 if (hint->insertion_p ())
2289 return column_range (start_column, final_hint_column);
2291 else
2293 exploc = expand_location (hint->get_next_loc ());
2294 --exploc.column;
2295 int finish_column = location_compute_display_column (exploc, policy);
2296 return column_range (start_column,
2297 MAX (finish_column, final_hint_column));
2301 /* A correction on a particular line.
2302 This describes a plan for how to print one or more fixit_hint
2303 instances that affected the line, potentially consolidating hints
2304 into corrections to make the result easier for the user to read. */
2306 class correction
2308 public:
2309 correction (column_range affected_bytes,
2310 column_range affected_columns,
2311 column_range printed_columns,
2312 const char *new_text, size_t new_text_len,
2313 const cpp_char_column_policy &policy)
2314 : m_affected_bytes (affected_bytes),
2315 m_affected_columns (affected_columns),
2316 m_printed_columns (printed_columns),
2317 m_text (xstrdup (new_text)),
2318 m_byte_length (new_text_len),
2319 m_policy (policy),
2320 m_alloc_sz (new_text_len + 1)
2322 compute_display_cols ();
2325 ~correction () { free (m_text); }
2327 bool insertion_p () const
2329 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2332 void ensure_capacity (size_t len);
2333 void ensure_terminated ();
2335 void compute_display_cols ()
2337 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2340 void overwrite (int dst_offset, const char_span &src_span)
2342 gcc_assert (dst_offset >= 0);
2343 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2344 memcpy (m_text + dst_offset, src_span.get_buffer (),
2345 src_span.length ());
2348 /* If insert, then start: the column before which the text
2349 is to be inserted, and finish is offset by the length of
2350 the replacement.
2351 If replace, then the range of columns affected. */
2352 column_range m_affected_bytes;
2353 column_range m_affected_columns;
2355 /* If insert, then start: the column before which the text
2356 is to be inserted, and finish is offset by the length of
2357 the replacement.
2358 If replace, then the range of columns affected. */
2359 column_range m_printed_columns;
2361 /* The text to be inserted/used as replacement. */
2362 char *m_text;
2363 size_t m_byte_length; /* Not including null-terminator. */
2364 int m_display_cols;
2365 const cpp_char_column_policy &m_policy;
2366 size_t m_alloc_sz;
2369 /* Ensure that m_text can hold a string of length LEN
2370 (plus 1 for 0-termination). */
2372 void
2373 correction::ensure_capacity (size_t len)
2375 /* Allow 1 extra byte for 0-termination. */
2376 if (m_alloc_sz < (len + 1))
2378 size_t new_alloc_sz = (len + 1) * 2;
2379 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2380 m_alloc_sz = new_alloc_sz;
2384 /* Ensure that m_text is 0-terminated. */
2386 void
2387 correction::ensure_terminated ()
2389 /* 0-terminate the buffer. */
2390 gcc_assert (m_byte_length < m_alloc_sz);
2391 m_text[m_byte_length] = '\0';
2394 /* A list of corrections affecting a particular line.
2395 This is used by layout::print_trailing_fixits for planning
2396 how to print the fix-it hints affecting the line. */
2398 class line_corrections
2400 public:
2401 line_corrections (const char_display_policy &policy,
2402 const char *filename,
2403 linenum_type row)
2404 : m_policy (policy), m_filename (filename), m_row (row)
2406 ~line_corrections ();
2408 void add_hint (const fixit_hint *hint);
2410 const char_display_policy &m_policy;
2411 const char *m_filename;
2412 linenum_type m_row;
2413 auto_vec <correction *> m_corrections;
2416 /* struct line_corrections. */
2418 line_corrections::~line_corrections ()
2420 unsigned i;
2421 correction *c;
2422 FOR_EACH_VEC_ELT (m_corrections, i, c)
2423 delete c;
2426 /* A struct wrapping a particular source line, allowing
2427 run-time bounds-checking of accesses in a checked build. */
2429 class source_line
2431 public:
2432 source_line (const char *filename, int line);
2434 char_span as_span () { return char_span (chars, width); }
2436 const char *chars;
2437 int width;
2440 /* source_line's ctor. */
2442 source_line::source_line (const char *filename, int line)
2444 char_span span = location_get_source_line (filename, line);
2445 chars = span.get_buffer ();
2446 width = span.length ();
2449 /* Add HINT to the corrections for this line.
2450 Attempt to consolidate nearby hints so that they will not
2451 overlap with printed. */
2453 void
2454 line_corrections::add_hint (const fixit_hint *hint)
2456 column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES);
2457 column_range affected_columns = get_affected_range (m_policy, hint,
2458 CU_DISPLAY_COLS);
2459 column_range printed_columns = get_printed_columns (m_policy, hint);
2461 /* Potentially consolidate. */
2462 if (!m_corrections.is_empty ())
2464 correction *last_correction
2465 = m_corrections[m_corrections.length () - 1];
2467 /* The following consolidation code assumes that the fix-it hints
2468 have been sorted by start (done within layout's ctor). */
2469 gcc_assert (affected_bytes.start
2470 >= last_correction->m_affected_bytes.start);
2471 gcc_assert (printed_columns.start
2472 >= last_correction->m_printed_columns.start);
2474 if (printed_columns.start <= last_correction->m_printed_columns.finish
2475 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2476 affected_bytes.start - 1))
2478 /* We have two hints for which the printed forms of the hints
2479 would touch or overlap, so we need to consolidate them to avoid
2480 confusing the user.
2481 Attempt to inject a "replace" correction from immediately
2482 after the end of the last hint to immediately before the start
2483 of the next hint. */
2484 column_range between (last_correction->m_affected_bytes.finish + 1,
2485 affected_bytes.start - 1);
2487 /* Try to read the source. */
2488 source_line line (m_filename, m_row);
2489 if (line.chars && between.finish < line.width)
2491 /* Consolidate into the last correction:
2492 add a no-op "replace" of the "between" text, and
2493 add the text from the new hint. */
2494 int old_byte_len = last_correction->m_byte_length;
2495 gcc_assert (old_byte_len >= 0);
2496 int between_byte_len = between.finish + 1 - between.start;
2497 gcc_assert (between_byte_len >= 0);
2498 int new_byte_len
2499 = old_byte_len + between_byte_len + hint->get_length ();
2500 gcc_assert (new_byte_len >= 0);
2501 last_correction->ensure_capacity (new_byte_len);
2502 last_correction->overwrite
2503 (old_byte_len,
2504 line.as_span ().subspan (between.start - 1,
2505 between.finish + 1 - between.start));
2506 last_correction->overwrite (old_byte_len + between_byte_len,
2507 char_span (hint->get_string (),
2508 hint->get_length ()));
2509 last_correction->m_byte_length = new_byte_len;
2510 last_correction->ensure_terminated ();
2511 last_correction->m_affected_bytes.finish
2512 = affected_bytes.finish;
2513 last_correction->m_affected_columns.finish
2514 = affected_columns.finish;
2515 int prev_display_cols = last_correction->m_display_cols;
2516 last_correction->compute_display_cols ();
2517 last_correction->m_printed_columns.finish
2518 += last_correction->m_display_cols - prev_display_cols;
2519 return;
2524 /* If no consolidation happened, add a new correction instance. */
2525 m_corrections.safe_push (new correction (affected_bytes,
2526 affected_columns,
2527 printed_columns,
2528 hint->get_string (),
2529 hint->get_length (),
2530 m_policy));
2533 /* If there are any fixit hints on source line ROW, print them.
2534 They are printed in order, attempting to combine them onto lines, but
2535 starting new lines if necessary.
2536 Fix-it hints that insert new lines are handled separately,
2537 in layout::print_leading_fixits. */
2539 void
2540 layout::print_trailing_fixits (linenum_type row)
2542 /* Build a list of correction instances for the line,
2543 potentially consolidating hints (for the sake of readability). */
2544 line_corrections corrections (m_policy, m_exploc.file, row);
2545 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2547 const fixit_hint *hint = m_fixit_hints[i];
2549 /* Newline fixits are handled by layout::print_leading_fixits. */
2550 if (hint->ends_with_newline_p ())
2551 continue;
2553 if (hint->affects_line_p (m_exploc.file, row))
2554 corrections.add_hint (hint);
2557 /* Now print the corrections. */
2558 unsigned i;
2559 correction *c;
2560 int column = m_x_offset_display;
2562 if (!corrections.m_corrections.is_empty ())
2563 start_annotation_line ();
2565 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2567 /* For now we assume each fixit hint can only touch one line. */
2568 if (c->insertion_p ())
2570 /* This assumes the insertion just affects one line. */
2571 int start_column = c->m_printed_columns.start;
2572 move_to_column (&column, start_column, true);
2573 m_colorizer.set_fixit_insert ();
2574 pp_string (m_pp, c->m_text);
2575 m_colorizer.set_normal_text ();
2576 column += c->m_display_cols;
2578 else
2580 /* If the range of the replacement wasn't printed in the
2581 annotation line, then print an extra underline to
2582 indicate exactly what is being replaced.
2583 Always show it for removals. */
2584 int start_column = c->m_affected_columns.start;
2585 int finish_column = c->m_affected_columns.finish;
2586 if (!annotation_line_showed_range_p (row, start_column,
2587 finish_column)
2588 || c->m_byte_length == 0)
2590 move_to_column (&column, start_column, true);
2591 m_colorizer.set_fixit_delete ();
2592 for (; column <= finish_column; column++)
2593 pp_character (m_pp, '-');
2594 m_colorizer.set_normal_text ();
2596 /* Print the replacement text. REPLACE also covers
2597 removals, so only do this extra work (potentially starting
2598 a new line) if we have actual replacement text. */
2599 if (c->m_byte_length > 0)
2601 move_to_column (&column, start_column, true);
2602 m_colorizer.set_fixit_insert ();
2603 pp_string (m_pp, c->m_text);
2604 m_colorizer.set_normal_text ();
2605 column += c->m_display_cols;
2610 /* Add a trailing newline, if necessary. */
2611 move_to_column (&column, 0, false);
2614 /* Disable any colorization and emit a newline. */
2616 void
2617 layout::print_newline ()
2619 m_colorizer.set_normal_text ();
2620 pp_newline (m_pp);
2623 /* Return true if (ROW/COLUMN) is within a range of the layout.
2624 If it returns true, OUT_STATE is written to, with the
2625 range index, and whether we should draw the caret at
2626 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2627 whether all inputs and outputs are in bytes or display column units. */
2629 bool
2630 layout::get_state_at_point (/* Inputs. */
2631 linenum_type row, int column,
2632 int first_non_ws, int last_non_ws,
2633 enum column_unit col_unit,
2634 /* Outputs. */
2635 point_state *out_state)
2637 layout_range *range;
2638 int i;
2639 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2641 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2642 /* Bail out early, so that such ranges don't affect underlining or
2643 source colorization. */
2644 continue;
2646 if (range->contains_point (row, column, col_unit))
2648 out_state->range_idx = i;
2650 /* Are we at the range's caret? is it visible? */
2651 out_state->draw_caret_p = false;
2652 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2653 && row == range->m_caret.m_line
2654 && column == range->m_caret.m_columns[col_unit])
2655 out_state->draw_caret_p = true;
2657 /* Within a multiline range, don't display any underline
2658 in any leading or trailing whitespace on a line.
2659 We do display carets, however. */
2660 if (!out_state->draw_caret_p)
2661 if (column < first_non_ws || column > last_non_ws)
2662 return false;
2664 /* We are within a range. */
2665 return true;
2669 return false;
2672 /* Helper function for use by layout::print_line when printing the
2673 annotation line under the source line.
2674 Get the display column beyond the rightmost one that could contain a caret
2675 or range marker, given that we stop rendering at trailing whitespace.
2676 ROW is the source line within the given file.
2677 CARET_COLUMN is the display column of range 0's caret.
2678 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2679 character of source (as determined when printing the source line). */
2682 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2683 int last_non_ws_column)
2685 int result = caret_column + 1;
2687 layout_range *range;
2688 int i;
2689 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2691 if (row >= range->m_start.m_line)
2693 if (range->m_finish.m_line == row)
2695 /* On the final line within a range; ensure that
2696 we render up to the end of the range. */
2697 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2698 if (result <= disp_col)
2699 result = disp_col + 1;
2701 else if (row < range->m_finish.m_line)
2703 /* Within a multiline range; ensure that we render up to the
2704 last non-whitespace column. */
2705 if (result <= last_non_ws_column)
2706 result = last_non_ws_column + 1;
2711 return result;
2714 /* Given *COLUMN as an x-coordinate, print spaces to position
2715 successive output at DEST_COLUMN, printing a newline if necessary,
2716 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2717 left margin after any newline. */
2719 void
2720 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2722 /* Start a new line if we need to. */
2723 if (*column > dest_column)
2725 print_newline ();
2726 if (add_left_margin)
2727 start_annotation_line ();
2728 *column = m_x_offset_display;
2731 while (*column < dest_column)
2733 pp_space (m_pp);
2734 (*column)++;
2738 /* For debugging layout issues, render a ruler giving column numbers
2739 (after the 1-column indent). */
2741 void
2742 layout::show_ruler (int max_column) const
2744 /* Hundreds. */
2745 if (max_column > 99)
2747 start_annotation_line ();
2748 pp_space (m_pp);
2749 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2750 if (column % 10 == 0)
2751 pp_character (m_pp, '0' + (column / 100) % 10);
2752 else
2753 pp_space (m_pp);
2754 pp_newline (m_pp);
2757 /* Tens. */
2758 start_annotation_line ();
2759 pp_space (m_pp);
2760 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2761 if (column % 10 == 0)
2762 pp_character (m_pp, '0' + (column / 10) % 10);
2763 else
2764 pp_space (m_pp);
2765 pp_newline (m_pp);
2767 /* Units. */
2768 start_annotation_line ();
2769 pp_space (m_pp);
2770 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2771 pp_character (m_pp, '0' + (column % 10));
2772 pp_newline (m_pp);
2775 /* Print leading fix-its (for new lines inserted before the source line)
2776 then the source line, followed by an annotation line
2777 consisting of any caret/underlines, then any fixits.
2778 If the source line can't be read, print nothing. */
2779 void
2780 layout::print_line (linenum_type row)
2782 char_span line = location_get_source_line (m_exploc.file, row);
2783 if (!line)
2784 return;
2786 print_leading_fixits (row);
2787 const line_bounds lbounds
2788 = print_source_line (row, line.get_buffer (), line.length ());
2789 if (should_print_annotation_line_p (row))
2790 print_annotation_line (row, lbounds);
2791 if (m_options.show_labels_p)
2792 print_any_labels (row);
2793 print_trailing_fixits (row);
2796 } /* End of anonymous namespace. */
2798 /* If LOC is within the spans of lines that will already be printed for
2799 this gcc_rich_location, then add it as a secondary location and return true.
2801 Otherwise return false. */
2803 bool
2804 gcc_rich_location::add_location_if_nearby (location_t loc,
2805 bool restrict_to_current_line_spans,
2806 const range_label *label)
2808 /* Use the layout location-handling logic to sanitize LOC,
2809 filtering it to the current line spans within a temporary
2810 layout instance. */
2811 layout layout (*global_dc, this, DK_ERROR, nullptr);
2812 location_range loc_range;
2813 loc_range.m_loc = loc;
2814 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2815 if (!layout.maybe_add_location_range (&loc_range, 0,
2816 restrict_to_current_line_spans))
2817 return false;
2819 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2820 return true;
2823 /* Print the physical source code corresponding to the location of
2824 this diagnostic, with additional annotations.
2825 If PP is non-null, then use it rather than CONTEXT's printer. */
2827 void
2828 diagnostic_show_locus (diagnostic_context * context,
2829 rich_location *richloc,
2830 diagnostic_t diagnostic_kind,
2831 pretty_printer *pp)
2833 location_t loc = richloc->get_loc ();
2834 /* Do nothing if source-printing has been disabled. */
2835 if (!context->m_source_printing.enabled)
2836 return;
2838 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2839 if (loc <= BUILTINS_LOCATION)
2840 return;
2842 /* Don't print the same source location twice in a row, unless we have
2843 fix-it hints, or multiple locations, or a label. */
2844 if (loc == context->m_last_location
2845 && richloc->get_num_fixit_hints () == 0
2846 && richloc->get_num_locations () == 1
2847 && richloc->get_range (0)->m_label == NULL)
2848 return;
2850 context->m_last_location = loc;
2852 layout layout (*context, richloc, diagnostic_kind, pp);
2853 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2854 line_span_idx++)
2856 const line_span *line_span = layout.get_line_span (line_span_idx);
2857 if (context->m_source_printing.show_line_numbers_p)
2859 /* With line numbers, we should show whenever the line-numbering
2860 "jumps". */
2861 if (line_span_idx > 0)
2862 layout.print_gap_in_line_numbering ();
2864 else
2866 /* Without line numbers, we print headings for some line spans. */
2867 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2869 expanded_location exploc
2870 = layout.get_expanded_location (line_span);
2871 context->m_text_callbacks.start_span (context, exploc);
2874 /* Iterate over the lines within this span (using linenum_arith_t to
2875 avoid overflow with 0xffffffff causing an infinite loop). */
2876 linenum_arith_t last_line = line_span->get_last_line ();
2877 for (linenum_arith_t row = line_span->get_first_line ();
2878 row <= last_line; row++)
2879 layout.print_line (row);
2883 #if CHECKING_P
2885 namespace selftest {
2887 /* Selftests for diagnostic_show_locus. */
2889 /* Verify that cpp_display_width correctly handles escaping. */
2891 static void
2892 test_display_widths ()
2894 gcc_rich_location richloc (UNKNOWN_LOCATION);
2896 /* U+03C0 "GREEK SMALL LETTER PI". */
2897 const char *pi = "\xCF\x80";
2898 /* U+1F642 "SLIGHTLY SMILING FACE". */
2899 const char *emoji = "\xF0\x9F\x99\x82";
2900 /* Stray trailing byte of a UTF-8 character. */
2901 const char *stray = "\xBF";
2902 /* U+10FFFF. */
2903 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2905 /* No escaping. */
2907 test_diagnostic_context dc;
2908 char_display_policy policy (make_policy (dc, richloc));
2909 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2910 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2911 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2912 /* Don't check width of U+10FFFF; it's in a private use plane. */
2915 richloc.set_escape_on_output (true);
2918 test_diagnostic_context dc;
2919 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
2920 char_display_policy policy (make_policy (dc, richloc));
2921 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2922 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2923 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2924 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2925 policy),
2926 strlen ("<U+10FFFF>"));
2930 test_diagnostic_context dc;
2931 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
2932 char_display_policy policy (make_policy (dc, richloc));
2933 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2934 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2935 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2936 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2937 policy),
2938 16);
2942 /* For precise tests of the layout, make clear where the source line will
2943 start. test_left_margin sets the total byte count from the left side of the
2944 screen to the start of source lines, after the line number and the separator,
2945 which consists of the three characters " | ". */
2946 static const int test_linenum_sep = 3;
2947 static const int test_left_margin = 7;
2949 /* Helper function for test_layout_x_offset_display_utf8(). */
2950 static void
2951 test_offset_impl (int caret_byte_col, int max_width,
2952 int expected_x_offset_display,
2953 int left_margin = test_left_margin)
2955 test_diagnostic_context dc;
2956 dc.m_source_printing.max_width = max_width;
2957 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2958 the line number plus one space after. */
2959 dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
2960 dc.m_source_printing.show_line_numbers_p = true;
2961 rich_location richloc (line_table,
2962 linemap_position_for_column (line_table,
2963 caret_byte_col));
2964 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
2965 ASSERT_EQ (left_margin - test_linenum_sep,
2966 test_layout.get_linenum_width ());
2967 ASSERT_EQ (expected_x_offset_display,
2968 test_layout.get_x_offset_display ());
2971 /* Test that layout::calculate_x_offset_display() works. */
2972 static void
2973 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2976 const char *content
2977 = "This line is very long, so that we can use it to test the logic for "
2978 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2979 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2980 "column #102.\n";
2982 /* Number of bytes in the line, subtracting one to remove the newline. */
2983 const int line_bytes = strlen (content) - 1;
2985 /* Number of display columns occupied by the line; each of the 2 emojis
2986 takes up 2 fewer display columns than it does bytes. */
2987 const int line_display_cols = line_bytes - 2*2;
2989 /* The column of the first emoji. Byte or display is the same as there are
2990 no multibyte characters earlier on the line. */
2991 const int emoji_col = 102;
2993 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2994 line_table_test ltt (case_);
2996 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2998 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3000 /* Don't attempt to run the tests if column data might be unavailable. */
3001 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3002 return;
3004 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3005 ASSERT_EQ (1, LOCATION_LINE (line_end));
3006 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3008 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3009 ASSERT_EQ (line_display_cols,
3010 cpp_display_width (lspan.get_buffer (), lspan.length (),
3011 def_policy ()));
3012 ASSERT_EQ (line_display_cols,
3013 location_compute_display_column (expand_location (line_end),
3014 def_policy ()));
3015 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3016 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3018 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3020 /* No constraint on the width -> no offset. */
3021 test_offset_impl (emoji_col, 0, 0);
3023 /* Caret is before the beginning -> no offset. */
3024 test_offset_impl (0, 100, 0);
3026 /* Caret is past the end of the line -> no offset. */
3027 test_offset_impl (line_bytes+1, 100, 0);
3029 /* Line fits in the display -> no offset. */
3030 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3031 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3033 /* Line is too long for the display but caret location is OK
3034 anyway -> no offset. */
3035 static const int small_width = 24;
3036 test_offset_impl (1, small_width, 0);
3038 /* Width constraint is very small -> no offset. */
3039 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3041 /* Line would be offset, but due to large line numbers, offsetting
3042 would remove the whole line -> no offset. */
3043 static const int huge_left_margin = 100;
3044 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3046 /* Line is the same length as the display, but the line number makes it too
3047 long, so offset is required. Caret is at the end so padding on the right
3048 is not in effect. */
3049 for (int excess = 1; excess <= 3; ++excess)
3050 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3051 excess);
3053 /* Line is much too long for the display, caret is near the end ->
3054 offset should be such that the line fits in the display and caret
3055 remains the same distance from the end that it was. */
3056 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3057 caret_offset <= max_offset; ++caret_offset)
3058 test_offset_impl (line_bytes - caret_offset, small_width,
3059 line_display_cols + test_left_margin - small_width);
3061 /* As previous case but caret is closer to the middle; now we want it to end
3062 up CARET_LINE_MARGIN bytes from the end. */
3063 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3064 test_offset_impl (emoji_col, small_width,
3065 emoji_col + test_left_margin
3066 - (small_width - CARET_LINE_MARGIN));
3068 /* Test that the source line is offset as expected when printed. */
3070 test_diagnostic_context dc;
3071 dc.m_source_printing.max_width = small_width - 6;
3072 dc.m_source_printing.min_margin_width
3073 = test_left_margin - test_linenum_sep + 1;
3074 dc.m_source_printing.show_line_numbers_p = true;
3075 dc.m_source_printing.show_ruler_p = true;
3076 rich_location richloc (line_table,
3077 linemap_position_for_column (line_table,
3078 emoji_col));
3079 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
3080 test_layout.print_line (1);
3081 ASSERT_STREQ (" | 1 \n"
3082 " | 1 \n"
3083 " | 234567890123456789\n"
3084 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3085 "that occupies 8 bytes and 4 display columns, starting at "
3086 "column #102.\n"
3087 " | ^\n\n",
3088 pp_formatted_text (dc.printer));
3091 /* Similar to the previous example, but now the offset called for would split
3092 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3093 it with a padding space in this case. */
3095 test_diagnostic_context dc;
3096 dc.m_source_printing.max_width = small_width - 5;
3097 dc.m_source_printing.min_margin_width
3098 = test_left_margin - test_linenum_sep + 1;
3099 dc.m_source_printing.show_line_numbers_p = true;
3100 dc.m_source_printing.show_ruler_p = true;
3101 rich_location richloc (line_table,
3102 linemap_position_for_column (line_table,
3103 emoji_col + 2));
3104 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
3105 test_layout.print_line (1);
3106 ASSERT_STREQ (" | 1 1 \n"
3107 " | 1 2 \n"
3108 " | 3456789012345678901\n"
3109 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3110 "that occupies 8 bytes and 4 display columns, starting at "
3111 "column #102.\n"
3112 " | ^\n\n",
3113 pp_formatted_text (dc.printer));
3118 static void
3119 test_layout_x_offset_display_tab (const line_table_case &case_)
3121 const char *content
3122 = "This line is very long, so that we can use it to test the logic for "
3123 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3124 "a variable number of display columns, starting at column #103.\n";
3126 /* Number of bytes in the line, subtracting one to remove the newline. */
3127 const int line_bytes = strlen (content) - 1;
3129 /* The column where the tab begins. Byte or display is the same as there are
3130 no multibyte characters earlier on the line. */
3131 const int tab_col = 103;
3133 /* Effective extra size of the tab beyond what a single space would have taken
3134 up, indexed by tabstop. */
3135 static const int num_tabstops = 11;
3136 int extra_width[num_tabstops];
3137 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3139 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3140 extra_width[tabstop] = this_tab_size - 1;
3142 /* Example of this calculation: if tabstop is 10, the tab starting at column
3143 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3144 next character is at column #111. So it takes up 7 more columns than
3145 a space would have taken up. */
3146 ASSERT_EQ (7, extra_width[10]);
3148 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3149 line_table_test ltt (case_);
3151 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3153 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3155 /* Don't attempt to run the tests if column data might be unavailable. */
3156 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3157 return;
3159 /* Check that cpp_display_width handles the tabs as expected. */
3160 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3161 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3162 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3164 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3165 ASSERT_EQ (line_bytes + extra_width[tabstop],
3166 cpp_display_width (lspan.get_buffer (), lspan.length (),
3167 policy));
3168 ASSERT_EQ (line_bytes + extra_width[tabstop],
3169 location_compute_display_column (expand_location (line_end),
3170 policy));
3173 /* Check that the tab is expanded to the expected number of spaces. */
3174 rich_location richloc (line_table,
3175 linemap_position_for_column (line_table,
3176 tab_col + 1));
3177 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3179 test_diagnostic_context dc;
3180 dc.m_tabstop = tabstop;
3181 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
3182 test_layout.print_line (1);
3183 const char *out = pp_formatted_text (dc.printer);
3184 ASSERT_EQ (NULL, strchr (out, '\t'));
3185 const char *left_quote = strchr (out, '`');
3186 const char *right_quote = strchr (out, '\'');
3187 ASSERT_NE (NULL, left_quote);
3188 ASSERT_NE (NULL, right_quote);
3189 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3192 /* Check that the line is offset properly and that the tab is broken up
3193 into the expected number of spaces when it is the last character skipped
3194 over. */
3195 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3197 test_diagnostic_context dc;
3198 dc.m_tabstop = tabstop;
3199 static const int small_width = 24;
3200 dc.m_source_printing.max_width = small_width - 4;
3201 dc.m_source_printing.min_margin_width
3202 = test_left_margin - test_linenum_sep + 1;
3203 dc.m_source_printing.show_line_numbers_p = true;
3204 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
3205 test_layout.print_line (1);
3207 /* We have arranged things so that two columns will be printed before
3208 the caret. If the tab results in more than one space, this should
3209 produce two spaces in the output; otherwise, it will be a single space
3210 preceded by the opening quote before the tab character. */
3211 const char *output1
3212 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3213 "display columns, starting at column #103.\n"
3214 " | ^\n\n";
3215 const char *output2
3216 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3217 "display columns, starting at column #103.\n"
3218 " | ^\n\n";
3219 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3220 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3225 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3227 static void
3228 test_diagnostic_show_locus_unknown_location ()
3230 test_diagnostic_context dc;
3231 rich_location richloc (line_table, UNKNOWN_LOCATION);
3232 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3233 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3236 /* Verify that diagnostic_show_locus works sanely for various
3237 single-line cases.
3239 All of these work on the following 1-line source file:
3240 .0000000001111111
3241 .1234567890123456
3242 "foo = bar.field;\n"
3243 which is set up by test_diagnostic_show_locus_one_liner and calls
3244 them. */
3246 /* Just a caret. */
3248 static void
3249 test_one_liner_simple_caret ()
3251 test_diagnostic_context dc;
3252 location_t caret = linemap_position_for_column (line_table, 10);
3253 rich_location richloc (line_table, caret);
3254 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3255 ASSERT_STREQ (" foo = bar.field;\n"
3256 " ^\n",
3257 pp_formatted_text (dc.printer));
3260 /* Caret and range. */
3262 static void
3263 test_one_liner_caret_and_range ()
3265 test_diagnostic_context dc;
3266 location_t caret = linemap_position_for_column (line_table, 10);
3267 location_t start = linemap_position_for_column (line_table, 7);
3268 location_t finish = linemap_position_for_column (line_table, 15);
3269 location_t loc = make_location (caret, start, finish);
3270 rich_location richloc (line_table, loc);
3271 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3272 ASSERT_STREQ (" foo = bar.field;\n"
3273 " ~~~^~~~~~\n",
3274 pp_formatted_text (dc.printer));
3277 /* Multiple ranges and carets. */
3279 static void
3280 test_one_liner_multiple_carets_and_ranges ()
3282 test_diagnostic_context dc;
3283 location_t foo
3284 = make_location (linemap_position_for_column (line_table, 2),
3285 linemap_position_for_column (line_table, 1),
3286 linemap_position_for_column (line_table, 3));
3287 dc.m_source_printing.caret_chars[0] = 'A';
3289 location_t bar
3290 = make_location (linemap_position_for_column (line_table, 8),
3291 linemap_position_for_column (line_table, 7),
3292 linemap_position_for_column (line_table, 9));
3293 dc.m_source_printing.caret_chars[1] = 'B';
3295 location_t field
3296 = make_location (linemap_position_for_column (line_table, 13),
3297 linemap_position_for_column (line_table, 11),
3298 linemap_position_for_column (line_table, 15));
3299 dc.m_source_printing.caret_chars[2] = 'C';
3301 rich_location richloc (line_table, foo);
3302 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3303 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3304 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3305 ASSERT_STREQ (" foo = bar.field;\n"
3306 " ~A~ ~B~ ~~C~~\n",
3307 pp_formatted_text (dc.printer));
3310 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3312 static void
3313 test_one_liner_fixit_insert_before ()
3315 test_diagnostic_context dc;
3316 location_t caret = linemap_position_for_column (line_table, 7);
3317 rich_location richloc (line_table, caret);
3318 richloc.add_fixit_insert_before ("&");
3319 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3320 ASSERT_STREQ (" foo = bar.field;\n"
3321 " ^\n"
3322 " &\n",
3323 pp_formatted_text (dc.printer));
3326 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3328 static void
3329 test_one_liner_fixit_insert_after ()
3331 test_diagnostic_context dc;
3332 location_t start = linemap_position_for_column (line_table, 1);
3333 location_t finish = linemap_position_for_column (line_table, 3);
3334 location_t foo = make_location (start, start, finish);
3335 rich_location richloc (line_table, foo);
3336 richloc.add_fixit_insert_after ("[0]");
3337 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3338 ASSERT_STREQ (" foo = bar.field;\n"
3339 " ^~~\n"
3340 " [0]\n",
3341 pp_formatted_text (dc.printer));
3344 /* Removal fix-it hint: removal of the ".field".
3345 Also verify the interaction of pp_set_prefix with rulers and
3346 fix-it hints. */
3348 static void
3349 test_one_liner_fixit_remove ()
3351 location_t start = linemap_position_for_column (line_table, 10);
3352 location_t finish = linemap_position_for_column (line_table, 15);
3353 location_t dot = make_location (start, start, finish);
3354 rich_location richloc (line_table, dot);
3355 richloc.add_fixit_remove ();
3357 /* Normal. */
3359 test_diagnostic_context dc;
3360 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3361 ASSERT_STREQ (" foo = bar.field;\n"
3362 " ^~~~~~\n"
3363 " ------\n",
3364 pp_formatted_text (dc.printer));
3367 /* Test of adding a prefix. */
3369 test_diagnostic_context dc;
3370 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3371 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3372 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3373 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3374 "TEST PREFIX: ^~~~~~\n"
3375 "TEST PREFIX: ------\n",
3376 pp_formatted_text (dc.printer));
3379 /* Normal, with ruler. */
3381 test_diagnostic_context dc;
3382 dc.m_source_printing.show_ruler_p = true;
3383 dc.m_source_printing.max_width = 104;
3384 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3385 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3386 " 1 2 3 4 5 6 7 8 9 0 \n"
3387 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3388 " foo = bar.field;\n"
3389 " ^~~~~~\n"
3390 " ------\n",
3391 pp_formatted_text (dc.printer));
3394 /* Test of adding a prefix, with ruler. */
3396 test_diagnostic_context dc;
3397 dc.m_source_printing.show_ruler_p = true;
3398 dc.m_source_printing.max_width = 50;
3399 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3400 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3401 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3402 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3403 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3404 "TEST PREFIX: foo = bar.field;\n"
3405 "TEST PREFIX: ^~~~~~\n"
3406 "TEST PREFIX: ------\n",
3407 pp_formatted_text (dc.printer));
3410 /* Test of adding a prefix, with ruler and line numbers. */
3412 test_diagnostic_context dc;
3413 dc.m_source_printing.show_ruler_p = true;
3414 dc.m_source_printing.max_width = 50;
3415 dc.m_source_printing.show_line_numbers_p = true;
3416 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3417 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3418 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3419 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3420 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3421 "TEST PREFIX: 1 | foo = bar.field;\n"
3422 "TEST PREFIX: | ^~~~~~\n"
3423 "TEST PREFIX: | ------\n",
3424 pp_formatted_text (dc.printer));
3428 /* Replace fix-it hint: replacing "field" with "m_field". */
3430 static void
3431 test_one_liner_fixit_replace ()
3433 test_diagnostic_context dc;
3434 location_t start = linemap_position_for_column (line_table, 11);
3435 location_t finish = linemap_position_for_column (line_table, 15);
3436 location_t field = make_location (start, start, finish);
3437 rich_location richloc (line_table, field);
3438 richloc.add_fixit_replace ("m_field");
3439 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3440 ASSERT_STREQ (" foo = bar.field;\n"
3441 " ^~~~~\n"
3442 " m_field\n",
3443 pp_formatted_text (dc.printer));
3446 /* Replace fix-it hint: replacing "field" with "m_field",
3447 but where the caret was elsewhere. */
3449 static void
3450 test_one_liner_fixit_replace_non_equal_range ()
3452 test_diagnostic_context dc;
3453 location_t equals = linemap_position_for_column (line_table, 5);
3454 location_t start = linemap_position_for_column (line_table, 11);
3455 location_t finish = linemap_position_for_column (line_table, 15);
3456 rich_location richloc (line_table, equals);
3457 source_range range;
3458 range.m_start = start;
3459 range.m_finish = finish;
3460 richloc.add_fixit_replace (range, "m_field");
3461 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3462 /* The replacement range is not indicated in the annotation line, so
3463 it should be indicated via an additional underline. */
3464 ASSERT_STREQ (" foo = bar.field;\n"
3465 " ^\n"
3466 " -----\n"
3467 " m_field\n",
3468 pp_formatted_text (dc.printer));
3471 /* Replace fix-it hint: replacing "field" with "m_field",
3472 where the caret was elsewhere, but where a secondary range
3473 exactly covers "field". */
3475 static void
3476 test_one_liner_fixit_replace_equal_secondary_range ()
3478 test_diagnostic_context dc;
3479 location_t equals = linemap_position_for_column (line_table, 5);
3480 location_t start = linemap_position_for_column (line_table, 11);
3481 location_t finish = linemap_position_for_column (line_table, 15);
3482 rich_location richloc (line_table, equals);
3483 location_t field = make_location (start, start, finish);
3484 richloc.add_range (field);
3485 richloc.add_fixit_replace (field, "m_field");
3486 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3487 /* The replacement range is indicated in the annotation line,
3488 so it shouldn't be indicated via an additional underline. */
3489 ASSERT_STREQ (" foo = bar.field;\n"
3490 " ^ ~~~~~\n"
3491 " m_field\n",
3492 pp_formatted_text (dc.printer));
3495 /* Verify that we can use ad-hoc locations when adding fixits to a
3496 rich_location. */
3498 static void
3499 test_one_liner_fixit_validation_adhoc_locations ()
3501 /* Generate a range that's too long to be packed, so must
3502 be stored as an ad-hoc location (given the defaults
3503 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3504 const location_t c7 = linemap_position_for_column (line_table, 7);
3505 const location_t c47 = linemap_position_for_column (line_table, 47);
3506 const location_t loc = make_location (c7, c7, c47);
3508 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3509 return;
3511 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3513 /* Insert. */
3515 rich_location richloc (line_table, loc);
3516 richloc.add_fixit_insert_before (loc, "test");
3517 /* It should not have been discarded by the validator. */
3518 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3520 test_diagnostic_context dc;
3521 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3522 ASSERT_STREQ (" foo = bar.field;\n"
3523 " ^~~~~~~~~~ \n"
3524 " test\n",
3525 pp_formatted_text (dc.printer));
3528 /* Remove. */
3530 rich_location richloc (line_table, loc);
3531 source_range range = source_range::from_locations (loc, c47);
3532 richloc.add_fixit_remove (range);
3533 /* It should not have been discarded by the validator. */
3534 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3536 test_diagnostic_context dc;
3537 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3538 ASSERT_STREQ (" foo = bar.field;\n"
3539 " ^~~~~~~~~~ \n"
3540 " -----------------------------------------\n",
3541 pp_formatted_text (dc.printer));
3544 /* Replace. */
3546 rich_location richloc (line_table, loc);
3547 source_range range = source_range::from_locations (loc, c47);
3548 richloc.add_fixit_replace (range, "test");
3549 /* It should not have been discarded by the validator. */
3550 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3552 test_diagnostic_context dc;
3553 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3554 ASSERT_STREQ (" foo = bar.field;\n"
3555 " ^~~~~~~~~~ \n"
3556 " test\n",
3557 pp_formatted_text (dc.printer));
3561 /* Test of consolidating insertions at the same location. */
3563 static void
3564 test_one_liner_many_fixits_1 ()
3566 test_diagnostic_context dc;
3567 location_t equals = linemap_position_for_column (line_table, 5);
3568 rich_location richloc (line_table, equals);
3569 for (int i = 0; i < 19; i++)
3570 richloc.add_fixit_insert_before ("a");
3571 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3572 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3573 ASSERT_STREQ (" foo = bar.field;\n"
3574 " ^\n"
3575 " aaaaaaaaaaaaaaaaaaa\n",
3576 pp_formatted_text (dc.printer));
3579 /* Ensure that we can add an arbitrary number of fix-it hints to a
3580 rich_location, even if they are not consolidated. */
3582 static void
3583 test_one_liner_many_fixits_2 ()
3585 test_diagnostic_context dc;
3586 location_t equals = linemap_position_for_column (line_table, 5);
3587 rich_location richloc (line_table, equals);
3588 for (int i = 0; i < 19; i++)
3590 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3591 richloc.add_fixit_insert_before (loc, "a");
3593 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3594 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3595 ASSERT_STREQ (" foo = bar.field;\n"
3596 " ^\n"
3597 " a a a a a a a a a a a a a a a a a a a\n",
3598 pp_formatted_text (dc.printer));
3601 /* Test of labeling the ranges within a rich_location. */
3603 static void
3604 test_one_liner_labels ()
3606 location_t foo
3607 = make_location (linemap_position_for_column (line_table, 1),
3608 linemap_position_for_column (line_table, 1),
3609 linemap_position_for_column (line_table, 3));
3610 location_t bar
3611 = make_location (linemap_position_for_column (line_table, 7),
3612 linemap_position_for_column (line_table, 7),
3613 linemap_position_for_column (line_table, 9));
3614 location_t field
3615 = make_location (linemap_position_for_column (line_table, 11),
3616 linemap_position_for_column (line_table, 11),
3617 linemap_position_for_column (line_table, 15));
3619 /* Example where all the labels fit on one line. */
3621 text_range_label label0 ("0");
3622 text_range_label label1 ("1");
3623 text_range_label label2 ("2");
3624 gcc_rich_location richloc (foo, &label0);
3625 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3626 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3629 test_diagnostic_context dc;
3630 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3631 ASSERT_STREQ (" foo = bar.field;\n"
3632 " ^~~ ~~~ ~~~~~\n"
3633 " | | |\n"
3634 " 0 1 2\n",
3635 pp_formatted_text (dc.printer));
3638 /* Verify that we can disable label-printing. */
3640 test_diagnostic_context dc;
3641 dc.m_source_printing.show_labels_p = false;
3642 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3643 ASSERT_STREQ (" foo = bar.field;\n"
3644 " ^~~ ~~~ ~~~~~\n",
3645 pp_formatted_text (dc.printer));
3649 /* Example where the labels need extra lines. */
3651 text_range_label label0 ("label 0");
3652 text_range_label label1 ("label 1");
3653 text_range_label label2 ("label 2");
3654 gcc_rich_location richloc (foo, &label0);
3655 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3656 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3658 test_diagnostic_context dc;
3659 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3660 ASSERT_STREQ (" foo = bar.field;\n"
3661 " ^~~ ~~~ ~~~~~\n"
3662 " | | |\n"
3663 " | | label 2\n"
3664 " | label 1\n"
3665 " label 0\n",
3666 pp_formatted_text (dc.printer));
3669 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3670 but label 1 just touches label 2. */
3672 text_range_label label0 ("aaaaa");
3673 text_range_label label1 ("bbbb");
3674 text_range_label label2 ("c");
3675 gcc_rich_location richloc (foo, &label0);
3676 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3677 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3679 test_diagnostic_context dc;
3680 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3681 ASSERT_STREQ (" foo = bar.field;\n"
3682 " ^~~ ~~~ ~~~~~\n"
3683 " | | |\n"
3684 " | | c\n"
3685 " aaaaa bbbb\n",
3686 pp_formatted_text (dc.printer));
3689 /* Example of out-of-order ranges (thus requiring a sort). */
3691 text_range_label label0 ("0");
3692 text_range_label label1 ("1");
3693 text_range_label label2 ("2");
3694 gcc_rich_location richloc (field, &label0);
3695 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3696 richloc.add_range (foo, 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 " 2 1 0\n",
3704 pp_formatted_text (dc.printer));
3707 /* Ensure we don't ICE if multiple ranges with labels are on
3708 the same point. */
3710 text_range_label label0 ("label 0");
3711 text_range_label label1 ("label 1");
3712 text_range_label label2 ("label 2");
3713 gcc_rich_location richloc (bar, &label0);
3714 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3715 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3717 test_diagnostic_context dc;
3718 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3719 ASSERT_STREQ (" foo = bar.field;\n"
3720 " ^~~\n"
3721 " |\n"
3722 " label 0\n"
3723 " label 1\n"
3724 " label 2\n",
3725 pp_formatted_text (dc.printer));
3728 /* Example of out-of-order ranges (thus requiring a sort), where
3729 they overlap, and there are multiple ranges on the same point. */
3731 text_range_label label_0a ("label 0a");
3732 text_range_label label_1a ("label 1a");
3733 text_range_label label_2a ("label 2a");
3734 text_range_label label_0b ("label 0b");
3735 text_range_label label_1b ("label 1b");
3736 text_range_label label_2b ("label 2b");
3737 text_range_label label_0c ("label 0c");
3738 text_range_label label_1c ("label 1c");
3739 text_range_label label_2c ("label 2c");
3740 gcc_rich_location richloc (field, &label_0a);
3741 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3742 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3744 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3745 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3746 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3748 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3749 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3750 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3752 test_diagnostic_context dc;
3753 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3754 ASSERT_STREQ (" foo = bar.field;\n"
3755 " ~~~ ~~~ ^~~~~\n"
3756 " | | |\n"
3757 " | | label 0a\n"
3758 " | | label 0b\n"
3759 " | | label 0c\n"
3760 " | label 1a\n"
3761 " | label 1b\n"
3762 " | label 1c\n"
3763 " label 2a\n"
3764 " label 2b\n"
3765 " label 2c\n",
3766 pp_formatted_text (dc.printer));
3769 /* Verify that a NULL result from range_label::get_text is
3770 handled gracefully. */
3772 text_range_label label (NULL);
3773 gcc_rich_location richloc (bar, &label);
3775 test_diagnostic_context dc;
3776 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3777 ASSERT_STREQ (" foo = bar.field;\n"
3778 " ^~~\n",
3779 pp_formatted_text (dc.printer));
3782 /* TODO: example of formatted printing (needs to be in
3783 gcc-rich-location.cc due to Makefile.in issues). */
3786 /* Run the various one-liner tests. */
3788 static void
3789 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3791 /* Create a tempfile and write some text to it.
3792 ....................0000000001111111.
3793 ....................1234567890123456. */
3794 const char *content = "foo = bar.field;\n";
3795 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3796 line_table_test ltt (case_);
3798 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3800 location_t line_end = linemap_position_for_column (line_table, 16);
3802 /* Don't attempt to run the tests if column data might be unavailable. */
3803 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3804 return;
3806 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3807 ASSERT_EQ (1, LOCATION_LINE (line_end));
3808 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3810 test_one_liner_simple_caret ();
3811 test_one_liner_caret_and_range ();
3812 test_one_liner_multiple_carets_and_ranges ();
3813 test_one_liner_fixit_insert_before ();
3814 test_one_liner_fixit_insert_after ();
3815 test_one_liner_fixit_remove ();
3816 test_one_liner_fixit_replace ();
3817 test_one_liner_fixit_replace_non_equal_range ();
3818 test_one_liner_fixit_replace_equal_secondary_range ();
3819 test_one_liner_fixit_validation_adhoc_locations ();
3820 test_one_liner_many_fixits_1 ();
3821 test_one_liner_many_fixits_2 ();
3822 test_one_liner_labels ();
3825 /* Version of all one-liner tests exercising multibyte awareness. For
3826 simplicity we stick to using two multibyte characters in the test, U+1F602
3827 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3828 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3829 below asserts would be easier to read if we used UTF-8 directly in the
3830 string constants, but it seems better not to demand the host compiler
3831 support this, when it isn't otherwise necessary. Instead, whenever an
3832 extended character appears in a string, we put a line break after it so that
3833 all succeeding characters can appear visually at the correct display column.
3835 All of these work on the following 1-line source file:
3837 .0000000001111111111222222 display
3838 .1234567890123456789012345 columns
3839 "SS_foo = P_bar.SS_fieldP;\n"
3840 .0000000111111111222222223 byte
3841 .1356789012456789134567891 columns
3843 which is set up by test_diagnostic_show_locus_one_liner and calls
3844 them. Here SS represents the two display columns for the U+1F602 emoji and
3845 P represents the one display column for the U+03C0 pi symbol. */
3847 /* Just a caret. */
3849 static void
3850 test_one_liner_simple_caret_utf8 ()
3852 test_diagnostic_context dc;
3853 location_t caret = linemap_position_for_column (line_table, 18);
3854 rich_location richloc (line_table, caret);
3855 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3856 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3857 "_foo = \xcf\x80"
3858 "_bar.\xf0\x9f\x98\x82"
3859 "_field\xcf\x80"
3860 ";\n"
3861 " ^\n",
3862 pp_formatted_text (dc.printer));
3865 /* Caret and range. */
3866 static void
3867 test_one_liner_caret_and_range_utf8 ()
3869 test_diagnostic_context dc;
3870 location_t caret = linemap_position_for_column (line_table, 18);
3871 location_t start = linemap_position_for_column (line_table, 12);
3872 location_t finish = linemap_position_for_column (line_table, 30);
3873 location_t loc = make_location (caret, start, finish);
3874 rich_location richloc (line_table, loc);
3875 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3876 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3877 "_foo = \xcf\x80"
3878 "_bar.\xf0\x9f\x98\x82"
3879 "_field\xcf\x80"
3880 ";\n"
3881 " ~~~~~^~~~~~~~~~\n",
3882 pp_formatted_text (dc.printer));
3885 /* Multiple ranges and carets. */
3887 static void
3888 test_one_liner_multiple_carets_and_ranges_utf8 ()
3890 test_diagnostic_context dc;
3891 location_t foo
3892 = make_location (linemap_position_for_column (line_table, 7),
3893 linemap_position_for_column (line_table, 1),
3894 linemap_position_for_column (line_table, 8));
3895 dc.m_source_printing.caret_chars[0] = 'A';
3897 location_t bar
3898 = make_location (linemap_position_for_column (line_table, 16),
3899 linemap_position_for_column (line_table, 12),
3900 linemap_position_for_column (line_table, 17));
3901 dc.m_source_printing.caret_chars[1] = 'B';
3903 location_t field
3904 = make_location (linemap_position_for_column (line_table, 26),
3905 linemap_position_for_column (line_table, 19),
3906 linemap_position_for_column (line_table, 30));
3907 dc.m_source_printing.caret_chars[2] = 'C';
3908 rich_location richloc (line_table, foo);
3909 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3910 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3911 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3912 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3913 "_foo = \xcf\x80"
3914 "_bar.\xf0\x9f\x98\x82"
3915 "_field\xcf\x80"
3916 ";\n"
3917 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3918 pp_formatted_text (dc.printer));
3921 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3923 static void
3924 test_one_liner_fixit_insert_before_utf8 ()
3926 test_diagnostic_context dc;
3927 location_t caret = linemap_position_for_column (line_table, 12);
3928 rich_location richloc (line_table, caret);
3929 richloc.add_fixit_insert_before ("&");
3930 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3931 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3932 "_foo = \xcf\x80"
3933 "_bar.\xf0\x9f\x98\x82"
3934 "_field\xcf\x80"
3935 ";\n"
3936 " ^\n"
3937 " &\n",
3938 pp_formatted_text (dc.printer));
3941 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3943 static void
3944 test_one_liner_fixit_insert_after_utf8 ()
3946 test_diagnostic_context dc;
3947 location_t start = linemap_position_for_column (line_table, 1);
3948 location_t finish = linemap_position_for_column (line_table, 8);
3949 location_t foo = make_location (start, start, finish);
3950 rich_location richloc (line_table, foo);
3951 richloc.add_fixit_insert_after ("[0]");
3952 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3953 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3954 "_foo = \xcf\x80"
3955 "_bar.\xf0\x9f\x98\x82"
3956 "_field\xcf\x80"
3957 ";\n"
3958 " ^~~~~~\n"
3959 " [0]\n",
3960 pp_formatted_text (dc.printer));
3963 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3965 static void
3966 test_one_liner_fixit_remove_utf8 ()
3968 test_diagnostic_context dc;
3969 location_t start = linemap_position_for_column (line_table, 18);
3970 location_t finish = linemap_position_for_column (line_table, 30);
3971 location_t dot = make_location (start, start, finish);
3972 rich_location richloc (line_table, dot);
3973 richloc.add_fixit_remove ();
3974 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3975 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3976 "_foo = \xcf\x80"
3977 "_bar.\xf0\x9f\x98\x82"
3978 "_field\xcf\x80"
3979 ";\n"
3980 " ^~~~~~~~~~\n"
3981 " ----------\n",
3982 pp_formatted_text (dc.printer));
3985 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3987 static void
3988 test_one_liner_fixit_replace_utf8 ()
3990 test_diagnostic_context dc;
3991 location_t start = linemap_position_for_column (line_table, 19);
3992 location_t finish = linemap_position_for_column (line_table, 30);
3993 location_t field = make_location (start, start, finish);
3994 rich_location richloc (line_table, field);
3995 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3996 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3997 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3998 "_foo = \xcf\x80"
3999 "_bar.\xf0\x9f\x98\x82"
4000 "_field\xcf\x80"
4001 ";\n"
4002 " ^~~~~~~~~\n"
4003 " m_\xf0\x9f\x98\x82"
4004 "_field\xcf\x80\n",
4005 pp_formatted_text (dc.printer));
4008 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4009 but where the caret was elsewhere. */
4011 static void
4012 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4014 test_diagnostic_context dc;
4015 location_t equals = linemap_position_for_column (line_table, 10);
4016 location_t start = linemap_position_for_column (line_table, 19);
4017 location_t finish = linemap_position_for_column (line_table, 30);
4018 rich_location richloc (line_table, equals);
4019 source_range range;
4020 range.m_start = start;
4021 range.m_finish = finish;
4022 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4023 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4024 /* The replacement range is not indicated in the annotation line, so
4025 it should be indicated via an additional underline. */
4026 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4027 "_foo = \xcf\x80"
4028 "_bar.\xf0\x9f\x98\x82"
4029 "_field\xcf\x80"
4030 ";\n"
4031 " ^\n"
4032 " ---------\n"
4033 " m_\xf0\x9f\x98\x82"
4034 "_field\xcf\x80\n",
4035 pp_formatted_text (dc.printer));
4038 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4039 where the caret was elsewhere, but where a secondary range
4040 exactly covers "field". */
4042 static void
4043 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4045 test_diagnostic_context dc;
4046 location_t equals = linemap_position_for_column (line_table, 10);
4047 location_t start = linemap_position_for_column (line_table, 19);
4048 location_t finish = linemap_position_for_column (line_table, 30);
4049 rich_location richloc (line_table, equals);
4050 location_t field = make_location (start, start, finish);
4051 richloc.add_range (field);
4052 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4053 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4054 /* The replacement range is indicated in the annotation line,
4055 so it shouldn't be indicated via an additional underline. */
4056 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4057 "_foo = \xcf\x80"
4058 "_bar.\xf0\x9f\x98\x82"
4059 "_field\xcf\x80"
4060 ";\n"
4061 " ^ ~~~~~~~~~\n"
4062 " m_\xf0\x9f\x98\x82"
4063 "_field\xcf\x80\n",
4064 pp_formatted_text (dc.printer));
4067 /* Verify that we can use ad-hoc locations when adding fixits to a
4068 rich_location. */
4070 static void
4071 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4073 /* Generate a range that's too long to be packed, so must
4074 be stored as an ad-hoc location (given the defaults
4075 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4076 const location_t c12 = linemap_position_for_column (line_table, 12);
4077 const location_t c52 = linemap_position_for_column (line_table, 52);
4078 const location_t loc = make_location (c12, c12, c52);
4080 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4081 return;
4083 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4085 /* Insert. */
4087 rich_location richloc (line_table, loc);
4088 richloc.add_fixit_insert_before (loc, "test");
4089 /* It should not have been discarded by the validator. */
4090 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4092 test_diagnostic_context dc;
4093 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4094 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4095 "_foo = \xcf\x80"
4096 "_bar.\xf0\x9f\x98\x82"
4097 "_field\xcf\x80"
4098 ";\n"
4099 " ^~~~~~~~~~~~~~~~ \n"
4100 " test\n",
4101 pp_formatted_text (dc.printer));
4104 /* Remove. */
4106 rich_location richloc (line_table, loc);
4107 source_range range = source_range::from_locations (loc, c52);
4108 richloc.add_fixit_remove (range);
4109 /* It should not have been discarded by the validator. */
4110 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4112 test_diagnostic_context dc;
4113 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4114 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4115 "_foo = \xcf\x80"
4116 "_bar.\xf0\x9f\x98\x82"
4117 "_field\xcf\x80"
4118 ";\n"
4119 " ^~~~~~~~~~~~~~~~ \n"
4120 " -------------------------------------\n",
4121 pp_formatted_text (dc.printer));
4124 /* Replace. */
4126 rich_location richloc (line_table, loc);
4127 source_range range = source_range::from_locations (loc, c52);
4128 richloc.add_fixit_replace (range, "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));
4145 /* Test of consolidating insertions at the same location. */
4147 static void
4148 test_one_liner_many_fixits_1_utf8 ()
4150 test_diagnostic_context dc;
4151 location_t equals = linemap_position_for_column (line_table, 10);
4152 rich_location richloc (line_table, equals);
4153 for (int i = 0; i < 19; i++)
4154 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4155 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4156 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4157 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4158 "_foo = \xcf\x80"
4159 "_bar.\xf0\x9f\x98\x82"
4160 "_field\xcf\x80"
4161 ";\n"
4162 " ^\n"
4163 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4164 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4165 pp_formatted_text (dc.printer));
4168 /* Ensure that we can add an arbitrary number of fix-it hints to a
4169 rich_location, even if they are not consolidated. */
4171 static void
4172 test_one_liner_many_fixits_2_utf8 ()
4174 test_diagnostic_context dc;
4175 location_t equals = linemap_position_for_column (line_table, 10);
4176 rich_location richloc (line_table, equals);
4177 const int nlocs = 19;
4178 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4179 34, 36, 38, 40, 42, 44};
4180 for (int i = 0; i != nlocs; ++i)
4182 location_t loc = linemap_position_for_column (line_table, locs[i]);
4183 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4186 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4187 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4188 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4189 "_foo = \xcf\x80"
4190 "_bar.\xf0\x9f\x98\x82"
4191 "_field\xcf\x80"
4192 ";\n"
4193 " ^\n"
4194 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4195 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4196 pp_formatted_text (dc.printer));
4199 /* Test of labeling the ranges within a rich_location. */
4201 static void
4202 test_one_liner_labels_utf8 ()
4204 location_t foo
4205 = make_location (linemap_position_for_column (line_table, 1),
4206 linemap_position_for_column (line_table, 1),
4207 linemap_position_for_column (line_table, 8));
4208 location_t bar
4209 = make_location (linemap_position_for_column (line_table, 12),
4210 linemap_position_for_column (line_table, 12),
4211 linemap_position_for_column (line_table, 17));
4212 location_t field
4213 = make_location (linemap_position_for_column (line_table, 19),
4214 linemap_position_for_column (line_table, 19),
4215 linemap_position_for_column (line_table, 30));
4217 /* Example where all the labels fit on one line. */
4219 /* These three labels contain multibyte characters such that their byte
4220 lengths are respectively (12, 10, 18), but their display widths are only
4221 (6, 5, 9). All three fit on the line when considering the display
4222 widths, but not when considering the byte widths, so verify that we do
4223 indeed put them all on one line. */
4224 text_range_label label0
4225 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4226 text_range_label label1
4227 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4228 text_range_label label2
4229 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4230 "\xcf\x80");
4231 gcc_rich_location richloc (foo, &label0);
4232 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4233 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4236 test_diagnostic_context dc;
4237 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4238 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4239 "_foo = \xcf\x80"
4240 "_bar.\xf0\x9f\x98\x82"
4241 "_field\xcf\x80"
4242 ";\n"
4243 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4244 " | | |\n"
4245 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4246 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4247 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4248 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4249 pp_formatted_text (dc.printer));
4254 /* Example where the labels need extra lines. */
4256 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4257 text_range_label label1 ("label 1\xcf\x80");
4258 text_range_label label2 ("label 2\xcf\x80");
4259 gcc_rich_location richloc (foo, &label0);
4260 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4261 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4263 test_diagnostic_context dc;
4264 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4266 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4267 "_foo = \xcf\x80"
4268 "_bar.\xf0\x9f\x98\x82"
4269 "_field\xcf\x80"
4270 ";\n"
4271 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4272 " | | |\n"
4273 " | | label 2\xcf\x80\n"
4274 " | label 1\xcf\x80\n"
4275 " label 0\xf0\x9f\x98\x82\n",
4276 pp_formatted_text (dc.printer));
4279 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4280 but label 1 just touches label 2. */
4282 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4283 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4284 text_range_label label2 ("c");
4285 gcc_rich_location richloc (foo, &label0);
4286 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4287 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4289 test_diagnostic_context dc;
4290 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4291 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4292 "_foo = \xcf\x80"
4293 "_bar.\xf0\x9f\x98\x82"
4294 "_field\xcf\x80"
4295 ";\n"
4296 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4297 " | | |\n"
4298 " | | c\n"
4299 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4300 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4301 pp_formatted_text (dc.printer));
4304 /* Example of escaping the source lines. */
4306 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4307 text_range_label label1 ("label 1\xcf\x80");
4308 text_range_label label2 ("label 2\xcf\x80");
4309 gcc_rich_location richloc (foo, &label0);
4310 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4311 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4312 richloc.set_escape_on_output (true);
4315 test_diagnostic_context dc;
4316 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
4317 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4318 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4319 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4320 " | | |\n"
4321 " | | label 2\xcf\x80\n"
4322 " | label 1\xcf\x80\n"
4323 " label 0\xf0\x9f\x98\x82\n",
4324 pp_formatted_text (dc.printer));
4327 test_diagnostic_context dc;
4328 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
4329 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4330 ASSERT_STREQ
4331 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4332 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4333 " | | |\n"
4334 " | | label 2\xcf\x80\n"
4335 " | label 1\xcf\x80\n"
4336 " label 0\xf0\x9f\x98\x82\n",
4337 pp_formatted_text (dc.printer));
4342 /* Make sure that colorization codes don't interrupt a multibyte
4343 sequence, which would corrupt it. */
4344 static void
4345 test_one_liner_colorized_utf8 ()
4347 test_diagnostic_context dc;
4348 dc.m_source_printing.colorize_source_p = true;
4349 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4350 const location_t pi = linemap_position_for_column (line_table, 12);
4351 rich_location richloc (line_table, pi);
4352 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4354 /* In order to avoid having the test depend on exactly how the colorization
4355 was effected, just confirm there are two pi characters in the output. */
4356 const char *result = pp_formatted_text (dc.printer);
4357 const char *null_term = result + strlen (result);
4358 const char *first_pi = strstr (result, "\xcf\x80");
4359 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4360 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4363 /* Run the various one-liner tests. */
4365 static void
4366 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4368 /* Create a tempfile and write some text to it. */
4369 const char *content
4370 /* Display columns.
4371 0000000000000000000000011111111111111111111111111111112222222222222
4372 1111111122222222345678900000000123456666666677777777890123444444445 */
4373 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4374 /* 0000000000000000000001111111111111111111222222222222222222222233333
4375 1111222233334444567890122223333456789999000011112222345678999900001
4376 Byte columns. */
4377 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4378 line_table_test ltt (case_);
4380 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4382 location_t line_end = linemap_position_for_column (line_table, 31);
4384 /* Don't attempt to run the tests if column data might be unavailable. */
4385 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4386 return;
4388 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4389 ASSERT_EQ (1, LOCATION_LINE (line_end));
4390 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4392 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4393 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4394 def_policy ()));
4395 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4396 def_policy ()));
4398 test_one_liner_simple_caret_utf8 ();
4399 test_one_liner_caret_and_range_utf8 ();
4400 test_one_liner_multiple_carets_and_ranges_utf8 ();
4401 test_one_liner_fixit_insert_before_utf8 ();
4402 test_one_liner_fixit_insert_after_utf8 ();
4403 test_one_liner_fixit_remove_utf8 ();
4404 test_one_liner_fixit_replace_utf8 ();
4405 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4406 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4407 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4408 test_one_liner_many_fixits_1_utf8 ();
4409 test_one_liner_many_fixits_2_utf8 ();
4410 test_one_liner_labels_utf8 ();
4411 test_one_liner_colorized_utf8 ();
4414 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4416 static void
4417 test_add_location_if_nearby (const line_table_case &case_)
4419 /* Create a tempfile and write some text to it.
4420 ...000000000111111111122222222223333333333.
4421 ...123456789012345678901234567890123456789. */
4422 const char *content
4423 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4424 "struct different_line\n" /* line 2. */
4425 "{\n" /* line 3. */
4426 " double x;\n" /* line 4. */
4427 " double y;\n" /* line 5. */
4428 ";\n"); /* line 6. */
4429 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4430 line_table_test ltt (case_);
4432 const line_map_ordinary *ord_map
4433 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4434 tmp.get_filename (), 0));
4436 linemap_line_start (line_table, 1, 100);
4438 const location_t final_line_end
4439 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4441 /* Don't attempt to run the tests if column data might be unavailable. */
4442 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4443 return;
4445 /* Test of add_location_if_nearby on the same line as the
4446 primary location. */
4448 const location_t missing_close_brace_1_39
4449 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4450 const location_t matching_open_brace_1_18
4451 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4452 gcc_rich_location richloc (missing_close_brace_1_39);
4453 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4454 ASSERT_TRUE (added);
4455 ASSERT_EQ (2, richloc.get_num_locations ());
4456 test_diagnostic_context dc;
4457 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4458 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4459 " ~ ^\n",
4460 pp_formatted_text (dc.printer));
4463 /* Test of add_location_if_nearby on a different line to the
4464 primary location. */
4466 const location_t missing_close_brace_6_1
4467 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4468 const location_t matching_open_brace_3_1
4469 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4470 gcc_rich_location richloc (missing_close_brace_6_1);
4471 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4472 ASSERT_FALSE (added);
4473 ASSERT_EQ (1, richloc.get_num_locations ());
4477 /* Verify that we print fixits even if they only affect lines
4478 outside those covered by the ranges in the rich_location. */
4480 static void
4481 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4483 /* Create a tempfile and write some text to it.
4484 ...000000000111111111122222222223333333333.
4485 ...123456789012345678901234567890123456789. */
4486 const char *content
4487 = ("struct point { double x; double y; };\n" /* line 1. */
4488 "struct point origin = {x: 0.0,\n" /* line 2. */
4489 " y\n" /* line 3. */
4490 "\n" /* line 4. */
4491 "\n" /* line 5. */
4492 " : 0.0};\n"); /* line 6. */
4493 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4494 line_table_test ltt (case_);
4496 const line_map_ordinary *ord_map
4497 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4498 tmp.get_filename (), 0));
4500 linemap_line_start (line_table, 1, 100);
4502 const location_t final_line_end
4503 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4505 /* Don't attempt to run the tests if column data might be unavailable. */
4506 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4507 return;
4509 /* A pair of tests for modernizing the initializers to C99-style. */
4511 /* The one-liner case (line 2). */
4513 test_diagnostic_context dc;
4514 const location_t x
4515 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4516 const location_t colon
4517 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4518 rich_location richloc (line_table, colon);
4519 richloc.add_fixit_insert_before (x, ".");
4520 richloc.add_fixit_replace (colon, "=");
4521 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4522 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4523 " ^\n"
4524 " .=\n",
4525 pp_formatted_text (dc.printer));
4528 /* The multiline case. The caret for the rich_location is on line 6;
4529 verify that insertion fixit on line 3 is still printed (and that
4530 span starts are printed due to the gap between the span at line 3
4531 and that at line 6). */
4533 test_diagnostic_context dc;
4534 const location_t y
4535 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4536 const location_t colon
4537 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4538 rich_location richloc (line_table, colon);
4539 richloc.add_fixit_insert_before (y, ".");
4540 richloc.add_fixit_replace (colon, "=");
4541 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4542 ASSERT_STREQ ("FILENAME:3:24:\n"
4543 " y\n"
4544 " .\n"
4545 "FILENAME:6:25:\n"
4546 " : 0.0};\n"
4547 " ^\n"
4548 " =\n",
4549 pp_formatted_text (dc.printer));
4552 /* As above, but verify the behavior of multiple line spans
4553 with line-numbering enabled. */
4555 const location_t y
4556 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4557 const location_t colon
4558 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4559 rich_location richloc (line_table, colon);
4560 richloc.add_fixit_insert_before (y, ".");
4561 richloc.add_fixit_replace (colon, "=");
4562 test_diagnostic_context dc;
4563 dc.m_source_printing.show_line_numbers_p = true;
4564 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4565 ASSERT_STREQ (" 3 | y\n"
4566 " | .\n"
4567 "......\n"
4568 " 6 | : 0.0};\n"
4569 " | ^\n"
4570 " | =\n",
4571 pp_formatted_text (dc.printer));
4576 /* Verify that fix-it hints are appropriately consolidated.
4578 If any fix-it hints in a rich_location involve locations beyond
4579 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4580 the fix-it as a whole, so there should be none.
4582 Otherwise, verify that consecutive "replace" and "remove" fix-its
4583 are merged, and that other fix-its remain separate. */
4585 static void
4586 test_fixit_consolidation (const line_table_case &case_)
4588 line_table_test ltt (case_);
4590 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4592 const location_t c10 = linemap_position_for_column (line_table, 10);
4593 const location_t c15 = linemap_position_for_column (line_table, 15);
4594 const location_t c16 = linemap_position_for_column (line_table, 16);
4595 const location_t c17 = linemap_position_for_column (line_table, 17);
4596 const location_t c20 = linemap_position_for_column (line_table, 20);
4597 const location_t c21 = linemap_position_for_column (line_table, 21);
4598 const location_t caret = c10;
4600 /* Insert + insert. */
4602 rich_location richloc (line_table, caret);
4603 richloc.add_fixit_insert_before (c10, "foo");
4604 richloc.add_fixit_insert_before (c15, "bar");
4606 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4607 /* Bogus column info for 2nd fixit, so no fixits. */
4608 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4609 else
4610 /* They should not have been merged. */
4611 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4614 /* Insert + replace. */
4616 rich_location richloc (line_table, caret);
4617 richloc.add_fixit_insert_before (c10, "foo");
4618 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4619 "bar");
4621 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4622 /* Bogus column info for 2nd fixit, so no fixits. */
4623 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4624 else
4625 /* They should not have been merged. */
4626 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4629 /* Replace + non-consecutive insert. */
4631 rich_location richloc (line_table, caret);
4632 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4633 "bar");
4634 richloc.add_fixit_insert_before (c17, "foo");
4636 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4637 /* Bogus column info for 2nd fixit, so no fixits. */
4638 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4639 else
4640 /* They should not have been merged. */
4641 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4644 /* Replace + non-consecutive replace. */
4646 rich_location richloc (line_table, caret);
4647 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4648 "foo");
4649 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4650 "bar");
4652 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4653 /* Bogus column info for 2nd fixit, so no fixits. */
4654 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4655 else
4656 /* They should not have been merged. */
4657 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4660 /* Replace + consecutive replace. */
4662 rich_location richloc (line_table, caret);
4663 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4664 "foo");
4665 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4666 "bar");
4668 if (c20 > 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
4673 /* They should have been merged into a single "replace". */
4674 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4675 const fixit_hint *hint = richloc.get_fixit_hint (0);
4676 ASSERT_STREQ ("foobar", hint->get_string ());
4677 ASSERT_EQ (c10, hint->get_start_loc ());
4678 ASSERT_EQ (c21, hint->get_next_loc ());
4682 /* Replace + consecutive removal. */
4684 rich_location richloc (line_table, caret);
4685 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4686 "foo");
4687 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4689 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4690 /* Bogus column info for 2nd fixit, so no fixits. */
4691 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4692 else
4694 /* They should have been merged into a single replace, with the
4695 range extended to cover that of the removal. */
4696 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4697 const fixit_hint *hint = richloc.get_fixit_hint (0);
4698 ASSERT_STREQ ("foo", hint->get_string ());
4699 ASSERT_EQ (c10, hint->get_start_loc ());
4700 ASSERT_EQ (c21, hint->get_next_loc ());
4704 /* Consecutive removals. */
4706 rich_location richloc (line_table, caret);
4707 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4708 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4710 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4711 /* Bogus column info for 2nd fixit, so no fixits. */
4712 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4713 else
4715 /* They should have been merged into a single "replace-with-empty". */
4716 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4717 const fixit_hint *hint = richloc.get_fixit_hint (0);
4718 ASSERT_STREQ ("", hint->get_string ());
4719 ASSERT_EQ (c10, hint->get_start_loc ());
4720 ASSERT_EQ (c21, hint->get_next_loc ());
4725 /* Verify that the line_corrections machinery correctly prints
4726 overlapping fixit-hints. */
4728 static void
4729 test_overlapped_fixit_printing (const line_table_case &case_)
4731 /* Create a tempfile and write some text to it.
4732 ...000000000111111111122222222223333333333.
4733 ...123456789012345678901234567890123456789. */
4734 const char *content
4735 = (" foo *f = (foo *)ptr->field;\n");
4736 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4737 line_table_test ltt (case_);
4739 const line_map_ordinary *ord_map
4740 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4741 tmp.get_filename (), 0));
4743 linemap_line_start (line_table, 1, 100);
4745 const location_t final_line_end
4746 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4748 /* Don't attempt to run the tests if column data might be unavailable. */
4749 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4750 return;
4752 /* A test for converting a C-style cast to a C++-style cast. */
4753 const location_t open_paren
4754 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4755 const location_t close_paren
4756 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4757 const location_t expr_start
4758 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4759 const location_t expr_finish
4760 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4761 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4763 /* Various examples of fix-it hints that aren't themselves consolidated,
4764 but for which the *printing* may need consolidation. */
4766 /* Example where 3 fix-it hints are printed as one. */
4768 test_diagnostic_context dc;
4769 rich_location richloc (line_table, expr);
4770 richloc.add_fixit_replace (open_paren, "const_cast<");
4771 richloc.add_fixit_replace (close_paren, "> (");
4772 richloc.add_fixit_insert_after (")");
4774 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4775 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4776 " ^~~~~~~~~~\n"
4777 " -----------------\n"
4778 " const_cast<foo *> (ptr->field)\n",
4779 pp_formatted_text (dc.printer));
4781 /* Unit-test the line_corrections machinery. */
4782 char_display_policy policy (make_policy (dc, richloc));
4783 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4784 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4785 ASSERT_EQ (column_range (12, 12),
4786 get_affected_range (policy, hint_0, CU_BYTES));
4787 ASSERT_EQ (column_range (12, 12),
4788 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4789 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4790 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4791 ASSERT_EQ (column_range (18, 18),
4792 get_affected_range (policy, hint_1, CU_BYTES));
4793 ASSERT_EQ (column_range (18, 18),
4794 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4795 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4796 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4797 ASSERT_EQ (column_range (29, 28),
4798 get_affected_range (policy, hint_2, CU_BYTES));
4799 ASSERT_EQ (column_range (29, 28),
4800 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4801 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2));
4803 /* Add each hint in turn to a line_corrections instance,
4804 and verify that they are consolidated into one correction instance
4805 as expected. */
4806 line_corrections lc (policy, tmp.get_filename (), 1);
4808 /* The first replace hint by itself. */
4809 lc.add_hint (hint_0);
4810 ASSERT_EQ (1, lc.m_corrections.length ());
4811 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4812 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4813 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4814 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4816 /* After the second replacement hint, they are printed together
4817 as a replacement (along with the text between them). */
4818 lc.add_hint (hint_1);
4819 ASSERT_EQ (1, lc.m_corrections.length ());
4820 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4821 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4822 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4823 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4825 /* After the final insertion hint, they are all printed together
4826 as a replacement (along with the text between them). */
4827 lc.add_hint (hint_2);
4828 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4829 lc.m_corrections[0]->m_text);
4830 ASSERT_EQ (1, lc.m_corrections.length ());
4831 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4832 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4833 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4836 /* Example where two are consolidated during printing. */
4838 test_diagnostic_context dc;
4839 rich_location richloc (line_table, expr);
4840 richloc.add_fixit_replace (open_paren, "CAST (");
4841 richloc.add_fixit_replace (close_paren, ") (");
4842 richloc.add_fixit_insert_after (")");
4844 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4845 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4846 " ^~~~~~~~~~\n"
4847 " -\n"
4848 " CAST (-\n"
4849 " ) ( )\n",
4850 pp_formatted_text (dc.printer));
4853 /* Example where none are consolidated during printing. */
4855 test_diagnostic_context dc;
4856 rich_location richloc (line_table, expr);
4857 richloc.add_fixit_replace (open_paren, "CST (");
4858 richloc.add_fixit_replace (close_paren, ") (");
4859 richloc.add_fixit_insert_after (")");
4861 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4862 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4863 " ^~~~~~~~~~\n"
4864 " -\n"
4865 " CST ( -\n"
4866 " ) ( )\n",
4867 pp_formatted_text (dc.printer));
4870 /* Example of deletion fix-it hints. */
4872 test_diagnostic_context dc;
4873 rich_location richloc (line_table, expr);
4874 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4875 source_range victim = {open_paren, close_paren};
4876 richloc.add_fixit_remove (victim);
4878 /* This case is actually handled by fixit-consolidation,
4879 rather than by line_corrections. */
4880 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4882 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4883 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4884 " ^~~~~~~~~~\n"
4885 " -------\n"
4886 " (bar *)\n",
4887 pp_formatted_text (dc.printer));
4890 /* Example of deletion fix-it hints that would overlap. */
4892 test_diagnostic_context dc;
4893 rich_location richloc (line_table, expr);
4894 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4895 source_range victim = {expr_start, expr_finish};
4896 richloc.add_fixit_remove (victim);
4898 /* These fixits are not consolidated. */
4899 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4901 /* But the corrections are. */
4902 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4903 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4904 " ^~~~~~~~~~\n"
4905 " -----------------\n"
4906 " (longer *)(foo *)\n",
4907 pp_formatted_text (dc.printer));
4910 /* Example of insertion fix-it hints that would overlap. */
4912 test_diagnostic_context dc;
4913 rich_location richloc (line_table, expr);
4914 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4915 richloc.add_fixit_insert_after (close_paren, "TEST");
4917 /* The first insertion is long enough that if printed naively,
4918 it would overlap with the second.
4919 Verify that they are printed as a single replacement. */
4920 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4921 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4922 " ^~~~~~~~~~\n"
4923 " -------\n"
4924 " LONGER THAN THE CAST(foo *)TEST\n",
4925 pp_formatted_text (dc.printer));
4929 /* Multibyte-aware version of preceding tests. See comments above
4930 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4931 characters here. */
4933 static void
4934 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4936 /* Create a tempfile and write some text to it. */
4938 const char *content
4939 /* Display columns.
4940 00000000000000000000000111111111111111111111111222222222222222223
4941 12344444444555555556789012344444444555555556789012345678999999990 */
4942 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4943 /* 00000000000000000000011111111111111111111112222222222333333333333
4944 12344445555666677778901234566667777888899990123456789012333344445
4945 Byte columns. */
4947 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4948 line_table_test ltt (case_);
4950 const line_map_ordinary *ord_map
4951 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4952 tmp.get_filename (), 0));
4954 linemap_line_start (line_table, 1, 100);
4956 const location_t final_line_end
4957 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4959 /* Don't attempt to run the tests if column data might be unavailable. */
4960 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4961 return;
4963 /* A test for converting a C-style cast to a C++-style cast. */
4964 const location_t open_paren
4965 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4966 const location_t close_paren
4967 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4968 const location_t expr_start
4969 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4970 const location_t expr_finish
4971 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4972 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4974 /* Various examples of fix-it hints that aren't themselves consolidated,
4975 but for which the *printing* may need consolidation. */
4977 /* Example where 3 fix-it hints are printed as one. */
4979 test_diagnostic_context dc;
4980 rich_location richloc (line_table, expr);
4981 richloc.add_fixit_replace (open_paren, "const_cast<");
4982 richloc.add_fixit_replace (close_paren, "> (");
4983 richloc.add_fixit_insert_after (")");
4985 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4986 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4987 " *f = (f\xf0\x9f\x98\x82"
4988 " *)ptr->field\xcf\x80"
4989 ";\n"
4990 " ^~~~~~~~~~~\n"
4991 " ------------------\n"
4992 " const_cast<f\xf0\x9f\x98\x82"
4993 " *> (ptr->field\xcf\x80"
4994 ")\n",
4995 pp_formatted_text (dc.printer));
4997 /* Unit-test the line_corrections machinery. */
4998 char_display_policy policy (make_policy (dc, richloc));
4999 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5000 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5001 ASSERT_EQ (column_range (14, 14),
5002 get_affected_range (policy, hint_0, CU_BYTES));
5003 ASSERT_EQ (column_range (12, 12),
5004 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
5005 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
5006 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5007 ASSERT_EQ (column_range (22, 22),
5008 get_affected_range (policy, hint_1, CU_BYTES));
5009 ASSERT_EQ (column_range (18, 18),
5010 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
5011 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
5012 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5013 ASSERT_EQ (column_range (35, 34),
5014 get_affected_range (policy, hint_2, CU_BYTES));
5015 ASSERT_EQ (column_range (30, 29),
5016 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
5017 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2));
5019 /* Add each hint in turn to a line_corrections instance,
5020 and verify that they are consolidated into one correction instance
5021 as expected. */
5022 line_corrections lc (policy, tmp.get_filename (), 1);
5024 /* The first replace hint by itself. */
5025 lc.add_hint (hint_0);
5026 ASSERT_EQ (1, lc.m_corrections.length ());
5027 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5028 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5029 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5030 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5032 /* After the second replacement hint, they are printed together
5033 as a replacement (along with the text between them). */
5034 lc.add_hint (hint_1);
5035 ASSERT_EQ (1, lc.m_corrections.length ());
5036 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5037 lc.m_corrections[0]->m_text);
5038 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5039 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5040 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5042 /* After the final insertion hint, they are all printed together
5043 as a replacement (along with the text between them). */
5044 lc.add_hint (hint_2);
5045 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5046 lc.m_corrections[0]->m_text);
5047 ASSERT_EQ (1, lc.m_corrections.length ());
5048 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5049 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5050 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5053 /* Example where two are consolidated during printing. */
5055 test_diagnostic_context dc;
5056 rich_location richloc (line_table, expr);
5057 richloc.add_fixit_replace (open_paren, "CAST (");
5058 richloc.add_fixit_replace (close_paren, ") (");
5059 richloc.add_fixit_insert_after (")");
5061 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5062 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5063 " *f = (f\xf0\x9f\x98\x82"
5064 " *)ptr->field\xcf\x80"
5065 ";\n"
5066 " ^~~~~~~~~~~\n"
5067 " -\n"
5068 " CAST (-\n"
5069 " ) ( )\n",
5070 pp_formatted_text (dc.printer));
5073 /* Example where none are consolidated during printing. */
5075 test_diagnostic_context dc;
5076 rich_location richloc (line_table, expr);
5077 richloc.add_fixit_replace (open_paren, "CST (");
5078 richloc.add_fixit_replace (close_paren, ") (");
5079 richloc.add_fixit_insert_after (")");
5081 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5082 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5083 " *f = (f\xf0\x9f\x98\x82"
5084 " *)ptr->field\xcf\x80"
5085 ";\n"
5086 " ^~~~~~~~~~~\n"
5087 " -\n"
5088 " CST ( -\n"
5089 " ) ( )\n",
5090 pp_formatted_text (dc.printer));
5093 /* Example of deletion fix-it hints. */
5095 test_diagnostic_context dc;
5096 rich_location richloc (line_table, expr);
5097 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5098 source_range victim = {open_paren, close_paren};
5099 richloc.add_fixit_remove (victim);
5101 /* This case is actually handled by fixit-consolidation,
5102 rather than by line_corrections. */
5103 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5105 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5106 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5107 " *f = (f\xf0\x9f\x98\x82"
5108 " *)ptr->field\xcf\x80"
5109 ";\n"
5110 " ^~~~~~~~~~~\n"
5111 " -------\n"
5112 " (bar\xf0\x9f\x98\x82"
5113 " *)\n",
5114 pp_formatted_text (dc.printer));
5117 /* Example of deletion fix-it hints that would overlap. */
5119 test_diagnostic_context dc;
5120 rich_location richloc (line_table, expr);
5121 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5122 source_range victim = {expr_start, expr_finish};
5123 richloc.add_fixit_remove (victim);
5125 /* These fixits are not consolidated. */
5126 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5128 /* But the corrections are. */
5129 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5130 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5131 " *f = (f\xf0\x9f\x98\x82"
5132 " *)ptr->field\xcf\x80"
5133 ";\n"
5134 " ^~~~~~~~~~~\n"
5135 " ------------------\n"
5136 " (long\xf0\x9f\x98\x82"
5137 " *)(f\xf0\x9f\x98\x82"
5138 " *)\n",
5139 pp_formatted_text (dc.printer));
5142 /* Example of insertion fix-it hints that would overlap. */
5144 test_diagnostic_context dc;
5145 rich_location richloc (line_table, expr);
5146 richloc.add_fixit_insert_before
5147 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5148 richloc.add_fixit_insert_after (close_paren, "TEST");
5150 /* The first insertion is long enough that if printed naively,
5151 it would overlap with the second.
5152 Verify that they are printed as a single replacement. */
5153 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5154 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5155 " *f = (f\xf0\x9f\x98\x82"
5156 " *)ptr->field\xcf\x80"
5157 ";\n"
5158 " ^~~~~~~~~~~\n"
5159 " -------\n"
5160 " L\xf0\x9f\x98\x82"
5161 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5162 " *)TEST\n",
5163 pp_formatted_text (dc.printer));
5167 /* Verify that the line_corrections machinery correctly prints
5168 overlapping fixit-hints that have been added in the wrong
5169 order.
5170 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5172 static void
5173 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5175 /* Create a tempfile and write some text to it.
5176 ...000000000111111111122222222223333333333.
5177 ...123456789012345678901234567890123456789. */
5178 const char *content
5179 = ("int a5[][0][0] = { 1, 2 };\n");
5180 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5181 line_table_test ltt (case_);
5183 const line_map_ordinary *ord_map
5184 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5185 tmp.get_filename (), 0));
5187 linemap_line_start (line_table, 1, 100);
5189 const location_t final_line_end
5190 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5192 /* Don't attempt to run the tests if column data might be unavailable. */
5193 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5194 return;
5196 const location_t col_1
5197 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5198 const location_t col_20
5199 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5200 const location_t col_21
5201 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5202 const location_t col_23
5203 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5204 const location_t col_25
5205 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5207 /* Two insertions, in the wrong order. */
5209 test_diagnostic_context dc;
5211 rich_location richloc (line_table, col_20);
5212 richloc.add_fixit_insert_before (col_23, "{");
5213 richloc.add_fixit_insert_before (col_21, "}");
5215 /* These fixits should be accepted; they can't be consolidated. */
5216 char_display_policy policy (make_policy (dc, richloc));
5217 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5218 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5219 ASSERT_EQ (column_range (23, 22),
5220 get_affected_range (policy, hint_0, CU_BYTES));
5221 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0));
5222 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5223 ASSERT_EQ (column_range (21, 20),
5224 get_affected_range (policy, hint_1, CU_BYTES));
5225 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1));
5227 /* Verify that they're printed correctly. */
5228 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5229 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5230 " ^\n"
5231 " } {\n",
5232 pp_formatted_text (dc.printer));
5235 /* Various overlapping insertions, some occurring "out of order"
5236 (reproducing the fix-it hints from PR c/81405). */
5238 test_diagnostic_context dc;
5239 rich_location richloc (line_table, col_20);
5241 richloc.add_fixit_insert_before (col_20, "{{");
5242 richloc.add_fixit_insert_before (col_21, "}}");
5243 richloc.add_fixit_insert_before (col_23, "{");
5244 richloc.add_fixit_insert_before (col_21, "}");
5245 richloc.add_fixit_insert_before (col_23, "{{");
5246 richloc.add_fixit_insert_before (col_25, "}");
5247 richloc.add_fixit_insert_before (col_21, "}");
5248 richloc.add_fixit_insert_before (col_1, "{");
5249 richloc.add_fixit_insert_before (col_25, "}");
5250 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5251 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5252 " ^\n"
5253 " { -----\n"
5254 " {{1}}}}, {{{2 }}\n",
5255 pp_formatted_text (dc.printer));
5259 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5261 static void
5262 test_fixit_insert_containing_newline (const line_table_case &case_)
5264 /* Create a tempfile and write some text to it.
5265 .........................0000000001111111.
5266 .........................1234567890123456. */
5267 const char *old_content = (" case 'a':\n" /* line 1. */
5268 " x = a;\n" /* line 2. */
5269 " case 'b':\n" /* line 3. */
5270 " x = b;\n");/* line 4. */
5272 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5273 line_table_test ltt (case_);
5274 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5276 location_t case_start = linemap_position_for_column (line_table, 5);
5277 location_t case_finish = linemap_position_for_column (line_table, 13);
5278 location_t case_loc = make_location (case_start, case_start, case_finish);
5279 location_t line_start = linemap_position_for_column (line_table, 1);
5281 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5282 return;
5284 /* Add a "break;" on a line by itself before line 3 i.e. before
5285 column 1 of line 3. */
5287 rich_location richloc (line_table, case_loc);
5288 richloc.add_fixit_insert_before (line_start, " break;\n");
5290 /* Without line numbers. */
5292 test_diagnostic_context dc;
5293 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5294 ASSERT_STREQ (" x = a;\n"
5295 "+ break;\n"
5296 " case 'b':\n"
5297 " ^~~~~~~~~\n",
5298 pp_formatted_text (dc.printer));
5301 /* With line numbers. */
5303 test_diagnostic_context dc;
5304 dc.m_source_printing.show_line_numbers_p = true;
5305 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5306 ASSERT_STREQ (" 2 | x = a;\n"
5307 " +++ |+ break;\n"
5308 " 3 | case 'b':\n"
5309 " | ^~~~~~~~~\n",
5310 pp_formatted_text (dc.printer));
5314 /* Verify that attempts to add text with a newline fail when the
5315 insertion point is *not* at the start of a line. */
5317 rich_location richloc (line_table, case_loc);
5318 richloc.add_fixit_insert_before (case_start, "break;\n");
5319 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5320 test_diagnostic_context dc;
5321 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5322 ASSERT_STREQ (" case 'b':\n"
5323 " ^~~~~~~~~\n",
5324 pp_formatted_text (dc.printer));
5328 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5329 of the file, where the fix-it is printed in a different line-span
5330 to the primary range of the diagnostic. */
5332 static void
5333 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5335 /* Create a tempfile and write some text to it.
5336 .........................0000000001111111.
5337 .........................1234567890123456. */
5338 const char *old_content = ("test (int ch)\n" /* line 1. */
5339 "{\n" /* line 2. */
5340 " putchar (ch);\n" /* line 3. */
5341 "}\n"); /* line 4. */
5343 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5344 line_table_test ltt (case_);
5346 const line_map_ordinary *ord_map = linemap_check_ordinary
5347 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5348 linemap_line_start (line_table, 1, 100);
5350 /* The primary range is the "putchar" token. */
5351 location_t putchar_start
5352 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5353 location_t putchar_finish
5354 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5355 location_t putchar_loc
5356 = make_location (putchar_start, putchar_start, putchar_finish);
5357 rich_location richloc (line_table, putchar_loc);
5359 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5360 location_t file_start
5361 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5362 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5364 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5365 return;
5368 test_diagnostic_context dc;
5369 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5370 ASSERT_STREQ ("FILENAME:1:1:\n"
5371 "+#include <stdio.h>\n"
5372 " test (int ch)\n"
5373 "FILENAME:3:2:\n"
5374 " putchar (ch);\n"
5375 " ^~~~~~~\n",
5376 pp_formatted_text (dc.printer));
5379 /* With line-numbering, the line spans are close enough to be
5380 consolidated, since it makes little sense to skip line 2. */
5382 test_diagnostic_context dc;
5383 dc.m_source_printing.show_line_numbers_p = true;
5384 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5385 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5386 " 1 | test (int ch)\n"
5387 " 2 | {\n"
5388 " 3 | putchar (ch);\n"
5389 " | ^~~~~~~\n",
5390 pp_formatted_text (dc.printer));
5394 /* Replacement fix-it hint containing a newline.
5395 This will fail, as newlines are only supported when inserting at the
5396 beginning of a line. */
5398 static void
5399 test_fixit_replace_containing_newline (const line_table_case &case_)
5401 /* Create a tempfile and write some text to it.
5402 .........................0000000001111.
5403 .........................1234567890123. */
5404 const char *old_content = "foo = bar ();\n";
5406 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5407 line_table_test ltt (case_);
5408 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5410 /* Replace the " = " with "\n = ", as if we were reformatting an
5411 overly long line. */
5412 location_t start = linemap_position_for_column (line_table, 4);
5413 location_t finish = linemap_position_for_column (line_table, 6);
5414 location_t loc = linemap_position_for_column (line_table, 13);
5415 rich_location richloc (line_table, loc);
5416 source_range range = source_range::from_locations (start, finish);
5417 richloc.add_fixit_replace (range, "\n =");
5419 /* Arbitrary newlines are not yet supported within fix-it hints, so
5420 the fix-it should not be displayed. */
5421 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5423 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5424 return;
5426 test_diagnostic_context dc;
5427 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5428 ASSERT_STREQ (" foo = bar ();\n"
5429 " ^\n",
5430 pp_formatted_text (dc.printer));
5433 /* Fix-it hint, attempting to delete a newline.
5434 This will fail, as we currently only support fix-it hints that
5435 affect one line at a time. */
5437 static void
5438 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5440 /* Create a tempfile and write some text to it.
5441 ..........................0000000001111.
5442 ..........................1234567890123. */
5443 const char *old_content = ("foo = bar (\n"
5444 " );\n");
5446 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5447 line_table_test ltt (case_);
5448 const line_map_ordinary *ord_map = linemap_check_ordinary
5449 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5450 linemap_line_start (line_table, 1, 100);
5452 /* Attempt to delete the " (\n...)". */
5453 location_t start
5454 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5455 location_t caret
5456 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5457 location_t finish
5458 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5459 location_t loc = make_location (caret, start, finish);
5460 rich_location richloc (line_table, loc);
5461 richloc. add_fixit_remove ();
5463 /* Fix-it hints that affect more than one line are not yet supported, so
5464 the fix-it should not be displayed. */
5465 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5467 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5468 return;
5470 test_diagnostic_context dc;
5471 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5472 ASSERT_STREQ (" foo = bar (\n"
5473 " ~^\n"
5474 " );\n"
5475 " ~ \n",
5476 pp_formatted_text (dc.printer));
5479 static void
5480 test_tab_expansion (const line_table_case &case_)
5482 /* Create a tempfile and write some text to it. This example uses a tabstop
5483 of 8, as the column numbers attempt to indicate:
5485 .....................000.01111111111.22222333333 display
5486 .....................123.90123456789.56789012345 columns */
5487 const char *content = " \t This: `\t' is a tab.\n";
5488 /* ....................000 00000011111 11111222222 byte
5489 ....................123 45678901234 56789012345 columns */
5491 const int tabstop = 8;
5492 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5493 const int first_non_ws_byte_col = 7;
5494 const int right_quote_byte_col = 15;
5495 const int last_byte_col = 25;
5496 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5498 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5499 line_table_test ltt (case_);
5500 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5502 /* Don't attempt to run the tests if column data might be unavailable. */
5503 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5504 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5505 return;
5507 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5508 into 11 spaces. Recall that print_line() also puts one space before
5509 everything too. */
5511 test_diagnostic_context dc;
5512 dc.m_tabstop = tabstop;
5513 rich_location richloc (line_table,
5514 linemap_position_for_column (line_table,
5515 first_non_ws_byte_col));
5516 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
5517 test_layout.print_line (1);
5518 ASSERT_STREQ (" This: ` ' is a tab.\n"
5519 " ^\n",
5520 pp_formatted_text (dc.printer));
5523 /* Confirm the display width was tracked correctly across the internal tab
5524 as well. */
5526 test_diagnostic_context dc;
5527 dc.m_tabstop = tabstop;
5528 rich_location richloc (line_table,
5529 linemap_position_for_column (line_table,
5530 right_quote_byte_col));
5531 layout test_layout (dc, &richloc, DK_ERROR, nullptr);
5532 test_layout.print_line (1);
5533 ASSERT_STREQ (" This: ` ' is a tab.\n"
5534 " ^\n",
5535 pp_formatted_text (dc.printer));
5539 /* Verify that the escaping machinery can cope with a variety of different
5540 invalid bytes. */
5542 static void
5543 test_escaping_bytes_1 (const line_table_case &case_)
5545 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5546 const size_t sz = sizeof (content);
5547 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5548 line_table_test ltt (case_);
5549 const line_map_ordinary *ord_map = linemap_check_ordinary
5550 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5551 linemap_line_start (line_table, 1, 100);
5553 location_t finish
5554 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5555 strlen (content));
5557 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5558 return;
5560 /* Locations of the NUL and \v bytes. */
5561 location_t nul_loc
5562 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5563 location_t v_loc
5564 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5565 gcc_rich_location richloc (nul_loc);
5566 richloc.add_range (v_loc);
5569 test_diagnostic_context dc;
5570 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5571 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5572 " ^ ~\n",
5573 pp_formatted_text (dc.printer));
5575 richloc.set_escape_on_output (true);
5577 test_diagnostic_context dc;
5578 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5579 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5580 ASSERT_STREQ
5581 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5582 " ^~~~~~~~ ~~~~~~~~\n",
5583 pp_formatted_text (dc.printer));
5586 test_diagnostic_context dc;
5587 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5588 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5589 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5590 " ^~~~ ~~~~\n",
5591 pp_formatted_text (dc.printer));
5595 /* As above, but verify that we handle the initial byte of a line
5596 correctly. */
5598 static void
5599 test_escaping_bytes_2 (const line_table_case &case_)
5601 const char content[] = "\0after\n";
5602 const size_t sz = sizeof (content);
5603 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5604 line_table_test ltt (case_);
5605 const line_map_ordinary *ord_map = linemap_check_ordinary
5606 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5607 linemap_line_start (line_table, 1, 100);
5609 location_t finish
5610 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5611 strlen (content));
5613 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5614 return;
5616 /* Location of the NUL byte. */
5617 location_t nul_loc
5618 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5619 gcc_rich_location richloc (nul_loc);
5622 test_diagnostic_context dc;
5623 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5624 ASSERT_STREQ (" after\n"
5625 " ^\n",
5626 pp_formatted_text (dc.printer));
5628 richloc.set_escape_on_output (true);
5630 test_diagnostic_context dc;
5631 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5632 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5633 ASSERT_STREQ (" <U+0000>after\n"
5634 " ^~~~~~~~\n",
5635 pp_formatted_text (dc.printer));
5638 test_diagnostic_context dc;
5639 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5640 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5641 ASSERT_STREQ (" <00>after\n"
5642 " ^~~~\n",
5643 pp_formatted_text (dc.printer));
5647 /* Verify that line numbers are correctly printed for the case of
5648 a multiline range in which the width of the line numbers changes
5649 (e.g. from "9" to "10"). */
5651 static void
5652 test_line_numbers_multiline_range ()
5654 /* Create a tempfile and write some text to it. */
5655 pretty_printer pp;
5656 for (int i = 0; i < 20; i++)
5657 /* .........0000000001111111.
5658 .............1234567890123456. */
5659 pp_printf (&pp, "this is line %i\n", i + 1);
5660 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5661 line_table_test ltt;
5663 const line_map_ordinary *ord_map = linemap_check_ordinary
5664 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5665 linemap_line_start (line_table, 1, 100);
5667 /* Create a multi-line location, starting at the "line" of line 9, with
5668 a caret on the "is" of line 10, finishing on the "this" line 11. */
5670 location_t start
5671 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5672 location_t caret
5673 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5674 location_t finish
5675 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5676 location_t loc = make_location (caret, start, finish);
5678 test_diagnostic_context dc;
5679 dc.m_source_printing.show_line_numbers_p = true;
5680 dc.m_source_printing.min_margin_width = 0;
5681 gcc_rich_location richloc (loc);
5682 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5683 ASSERT_STREQ (" 9 | this is line 9\n"
5684 " | ~~~~~~\n"
5685 "10 | this is line 10\n"
5686 " | ~~~~~^~~~~~~~~~\n"
5687 "11 | this is line 11\n"
5688 " | ~~~~ \n",
5689 pp_formatted_text (dc.printer));
5692 /* Run all of the selftests within this file. */
5694 void
5695 diagnostic_show_locus_cc_tests ()
5697 test_line_span ();
5699 test_layout_range_for_single_point ();
5700 test_layout_range_for_single_line ();
5701 test_layout_range_for_multiple_lines ();
5703 test_display_widths ();
5705 for_each_line_table_case (test_layout_x_offset_display_utf8);
5706 for_each_line_table_case (test_layout_x_offset_display_tab);
5708 test_get_line_bytes_without_trailing_whitespace ();
5710 test_diagnostic_show_locus_unknown_location ();
5712 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5713 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5714 for_each_line_table_case (test_add_location_if_nearby);
5715 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5716 for_each_line_table_case (test_fixit_consolidation);
5717 for_each_line_table_case (test_overlapped_fixit_printing);
5718 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5719 for_each_line_table_case (test_overlapped_fixit_printing_2);
5720 for_each_line_table_case (test_fixit_insert_containing_newline);
5721 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5722 for_each_line_table_case (test_fixit_replace_containing_newline);
5723 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5724 for_each_line_table_case (test_tab_expansion);
5725 for_each_line_table_case (test_escaping_bytes_1);
5726 for_each_line_table_case (test_escaping_bytes_2);
5728 test_line_numbers_multiline_range ();
5731 } // namespace selftest
5733 #endif /* #if CHECKING_P */
5735 #if __GNUC__ >= 10
5736 # pragma GCC diagnostic pop
5737 #endif