remove workaround for GCC 4.1-4.3 [PR105606]
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob31ef85151fd09559b10ad7544fdf9c1844abb889
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 (diagnostic_context *context,
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 diagnostic_context *m_context;
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 (diagnostic_context *context,
369 rich_location *richloc,
370 diagnostic_t diagnostic_kind,
371 pretty_printer *pp = nullptr);
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 diagnostic_context *m_context;
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_colorize_source_p;
438 bool m_show_labels_p;
439 bool m_show_line_numbers_p;
440 bool m_diagnostic_path_p;
441 auto_vec <layout_range> m_layout_ranges;
442 auto_vec <const fixit_hint *> m_fixit_hints;
443 auto_vec <line_span> m_line_spans;
444 int m_linenum_width;
445 int m_x_offset_display;
446 bool m_escape_on_output;
449 /* Implementation of "class colorizer". */
451 /* The constructor for "colorizer". Lookup and store color codes for the
452 different kinds of things we might need to print. */
454 colorizer::colorizer (diagnostic_context *context,
455 diagnostic_t diagnostic_kind) :
456 m_context (context),
457 m_diagnostic_kind (diagnostic_kind),
458 m_current_state (STATE_NORMAL_TEXT)
460 m_range1 = get_color_by_name ("range1");
461 m_range2 = get_color_by_name ("range2");
462 m_fixit_insert = get_color_by_name ("fixit-insert");
463 m_fixit_delete = get_color_by_name ("fixit-delete");
464 m_stop_color = colorize_stop (pp_show_color (context->printer));
467 /* The destructor for "colorize". If colorization is on, print a code to
468 turn it off. */
470 colorizer::~colorizer ()
472 finish_state (m_current_state);
475 /* Update state, printing color codes if necessary if there's a state
476 change. */
478 void
479 colorizer::set_state (int new_state)
481 if (m_current_state != new_state)
483 finish_state (m_current_state);
484 m_current_state = new_state;
485 begin_state (new_state);
489 /* Turn on any colorization for STATE. */
491 void
492 colorizer::begin_state (int state)
494 switch (state)
496 case STATE_NORMAL_TEXT:
497 break;
499 case STATE_FIXIT_INSERT:
500 pp_string (m_context->printer, m_fixit_insert);
501 break;
503 case STATE_FIXIT_DELETE:
504 pp_string (m_context->printer, m_fixit_delete);
505 break;
507 case 0:
508 /* Make range 0 be the same color as the "kind" text
509 (error vs warning vs note). */
510 pp_string
511 (m_context->printer,
512 colorize_start (pp_show_color (m_context->printer),
513 diagnostic_get_color_for_kind (m_diagnostic_kind)));
514 break;
516 case 1:
517 pp_string (m_context->printer, m_range1);
518 break;
520 case 2:
521 pp_string (m_context->printer, m_range2);
522 break;
524 default:
525 /* For ranges beyond 2, alternate between color 1 and color 2. */
527 gcc_assert (state > 2);
528 pp_string (m_context->printer,
529 state % 2 ? m_range1 : m_range2);
531 break;
535 /* Turn off any colorization for STATE. */
537 void
538 colorizer::finish_state (int state)
540 if (state != STATE_NORMAL_TEXT)
541 pp_string (m_context->printer, m_stop_color);
544 /* Get the color code for NAME (or the empty string if
545 colorization is disabled). */
547 const char *
548 colorizer::get_color_by_name (const char *name)
550 return colorize_start (pp_show_color (m_context->printer), name);
553 /* Implementation of class layout_range. */
555 /* The constructor for class layout_range.
556 Initialize various layout_point fields from expanded_location
557 equivalents; we've already filtered on file. */
559 layout_range::layout_range (const exploc_with_display_col &start_exploc,
560 const exploc_with_display_col &finish_exploc,
561 enum range_display_kind range_display_kind,
562 const exploc_with_display_col &caret_exploc,
563 unsigned original_idx,
564 const range_label *label)
565 : m_start (start_exploc),
566 m_finish (finish_exploc),
567 m_range_display_kind (range_display_kind),
568 m_caret (caret_exploc),
569 m_original_idx (original_idx),
570 m_label (label)
574 /* Is (column, row) within the given range?
575 We've already filtered on the file.
577 Ranges are closed (both limits are within the range).
579 Example A: a single-line range:
580 start: (col=22, line=2)
581 finish: (col=38, line=2)
583 |00000011111111112222222222333333333344444444444
584 |34567890123456789012345678901234567890123456789
585 --+-----------------------------------------------
586 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
587 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
588 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
590 Example B: a multiline range with
591 start: (col=14, line=3)
592 finish: (col=08, line=5)
594 |00000011111111112222222222333333333344444444444
595 |34567890123456789012345678901234567890123456789
596 --+-----------------------------------------------
597 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
598 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
599 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
600 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
601 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
602 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
603 --+-----------------------------------------------
605 Legend:
606 - 'b' indicates a point *before* the range
607 - 'S' indicates the start of the range
608 - 'w' indicates a point within the range
609 - 'F' indicates the finish of the range (which is
610 within it).
611 - 'a' indicates a subsequent point *after* the range.
613 COL_UNIT controls whether we check the byte column or
614 the display column; one or the other is more convenient
615 depending on the context. */
617 bool
618 layout_range::contains_point (linenum_type row, int column,
619 enum column_unit col_unit) const
621 gcc_assert (m_start.m_line <= m_finish.m_line);
622 /* ...but the equivalent isn't true for the columns;
623 consider example B in the comment above. */
625 if (row < m_start.m_line)
626 /* Points before the first line of the range are
627 outside it (corresponding to line 01 in example A
628 and lines 01 and 02 in example B above). */
629 return false;
631 if (row == m_start.m_line)
632 /* On same line as start of range (corresponding
633 to line 02 in example A and line 03 in example B). */
635 if (column < m_start.m_columns[col_unit])
636 /* Points on the starting line of the range, but
637 before the column in which it begins. */
638 return false;
640 if (row < m_finish.m_line)
641 /* This is a multiline range; the point
642 is within it (corresponds to line 03 in example B
643 from column 14 onwards) */
644 return true;
645 else
647 /* This is a single-line range. */
648 gcc_assert (row == m_finish.m_line);
649 return column <= m_finish.m_columns[col_unit];
653 /* The point is in a line beyond that containing the
654 start of the range: lines 03 onwards in example A,
655 and lines 04 onwards in example B. */
656 gcc_assert (row > m_start.m_line);
658 if (row > m_finish.m_line)
659 /* The point is beyond the final line of the range
660 (lines 03 onwards in example A, and lines 06 onwards
661 in example B). */
662 return false;
664 if (row < m_finish.m_line)
666 /* The point is in a line that's fully within a multiline
667 range (e.g. line 04 in example B). */
668 gcc_assert (m_start.m_line < m_finish.m_line);
669 return true;
672 gcc_assert (row == m_finish.m_line);
674 return column <= m_finish.m_columns[col_unit];
677 /* Does this layout_range contain any part of line ROW? */
679 bool
680 layout_range::intersects_line_p (linenum_type row) const
682 gcc_assert (m_start.m_line <= m_finish.m_line);
683 if (row < m_start.m_line)
684 return false;
685 if (row > m_finish.m_line)
686 return false;
687 return true;
690 #if CHECKING_P
692 /* Default for when we don't care what the tab expansion is set to. */
693 static const int def_tabstop = 8;
695 static cpp_char_column_policy def_policy ()
697 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
700 /* Create some expanded locations for testing layout_range. The filename
701 member of the explocs is set to the empty string. This member will only be
702 inspected by the calls to location_compute_display_column() made from the
703 layout_point constructors. That function will check for an empty filename
704 argument and not attempt to open it, rather treating the non-existent data
705 as if the display width were the same as the byte count. Tests exercising a
706 real difference between byte count and display width are performed later,
707 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
709 static layout_range
710 make_range (int start_line, int start_col, int end_line, int end_col)
712 const expanded_location start_exploc
713 = {"", start_line, start_col, NULL, false};
714 const expanded_location finish_exploc
715 = {"", end_line, end_col, NULL, false};
716 return layout_range (exploc_with_display_col (start_exploc, def_policy (),
717 LOCATION_ASPECT_START),
718 exploc_with_display_col (finish_exploc, def_policy (),
719 LOCATION_ASPECT_FINISH),
720 SHOW_RANGE_WITHOUT_CARET,
721 exploc_with_display_col (start_exploc, def_policy (),
722 LOCATION_ASPECT_CARET),
723 0, NULL);
726 /* Selftests for layout_range::contains_point and
727 layout_range::intersects_line_p. */
729 /* Selftest for layout_range, where the layout_range
730 is a range with start==end i.e. a single point. */
732 static void
733 test_layout_range_for_single_point ()
735 layout_range point = make_range (7, 10, 7, 10);
737 /* Tests for layout_range::contains_point. */
739 for (int i = 0; i != CU_NUM_UNITS; ++i)
741 const enum column_unit col_unit = (enum column_unit) i;
743 /* Before the line. */
744 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
746 /* On the line, but before start. */
747 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
749 /* At the point. */
750 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
752 /* On the line, after the point. */
753 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
755 /* After the line. */
756 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
759 /* Tests for layout_range::intersects_line_p. */
760 ASSERT_FALSE (point.intersects_line_p (6));
761 ASSERT_TRUE (point.intersects_line_p (7));
762 ASSERT_FALSE (point.intersects_line_p (8));
765 /* Selftest for layout_range, where the layout_range
766 is the single-line range shown as "Example A" above. */
768 static void
769 test_layout_range_for_single_line ()
771 layout_range example_a = make_range (2, 22, 2, 38);
773 /* Tests for layout_range::contains_point. */
775 for (int i = 0; i != CU_NUM_UNITS; ++i)
777 const enum column_unit col_unit = (enum column_unit) i;
779 /* Before the line. */
780 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
782 /* On the line, but before start. */
783 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
785 /* On the line, at the start. */
786 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
788 /* On the line, within the range. */
789 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
791 /* On the line, at the end. */
792 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
794 /* On the line, after the end. */
795 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
797 /* After the line. */
798 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
801 /* Tests for layout_range::intersects_line_p. */
802 ASSERT_FALSE (example_a.intersects_line_p (1));
803 ASSERT_TRUE (example_a.intersects_line_p (2));
804 ASSERT_FALSE (example_a.intersects_line_p (3));
807 /* Selftest for layout_range, where the layout_range
808 is the multi-line range shown as "Example B" above. */
810 static void
811 test_layout_range_for_multiple_lines ()
813 layout_range example_b = make_range (3, 14, 5, 8);
815 /* Tests for layout_range::contains_point. */
817 for (int i = 0; i != CU_NUM_UNITS; ++i)
819 const enum column_unit col_unit = (enum column_unit) i;
821 /* Before first line. */
822 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
824 /* On the first line, but before start. */
825 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
827 /* At the start. */
828 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
830 /* On the first line, within the range. */
831 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
833 /* On an interior line.
834 The column number should not matter; try various boundary
835 values. */
836 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
837 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
838 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
839 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
840 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
841 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
842 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
844 /* On the final line, before the end. */
845 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
847 /* On the final line, at the end. */
848 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
850 /* On the final line, after the end. */
851 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
853 /* After the line. */
854 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
857 /* Tests for layout_range::intersects_line_p. */
858 ASSERT_FALSE (example_b.intersects_line_p (2));
859 ASSERT_TRUE (example_b.intersects_line_p (3));
860 ASSERT_TRUE (example_b.intersects_line_p (4));
861 ASSERT_TRUE (example_b.intersects_line_p (5));
862 ASSERT_FALSE (example_b.intersects_line_p (6));
865 #endif /* #if CHECKING_P */
867 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
868 (still in bytes, not display cols) without any trailing whitespace. */
870 static int
871 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
873 int result = line_bytes;
874 while (result > 0)
876 char ch = line[result - 1];
877 if (ch == ' ' || ch == '\t' || ch == '\r')
878 result--;
879 else
880 break;
882 gcc_assert (result >= 0);
883 gcc_assert (result <= line_bytes);
884 gcc_assert (result == 0 ||
885 (line[result - 1] != ' '
886 && line[result -1] != '\t'
887 && line[result -1] != '\r'));
888 return result;
891 #if CHECKING_P
893 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
895 static void
896 assert_eq (const char *line, int expected_bytes)
898 int actual_value
899 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
900 ASSERT_EQ (actual_value, expected_bytes);
903 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
904 various inputs. It is not required to handle newlines. */
906 static void
907 test_get_line_bytes_without_trailing_whitespace ()
909 assert_eq ("", 0);
910 assert_eq (" ", 0);
911 assert_eq ("\t", 0);
912 assert_eq ("\r", 0);
913 assert_eq ("hello world", 11);
914 assert_eq ("hello world ", 11);
915 assert_eq ("hello world \t\t ", 11);
916 assert_eq ("hello world\r", 11);
919 #endif /* #if CHECKING_P */
921 /* Helper function for layout's ctor, for sanitizing locations relative
922 to the primary location within a diagnostic.
924 Compare LOC_A and LOC_B to see if it makes sense to print underlines
925 connecting their expanded locations. Doing so is only guaranteed to
926 make sense if the locations share the same macro expansion "history"
927 i.e. they can be traced through the same macro expansions, eventually
928 reaching an ordinary map.
930 This may be too strong a condition, but it effectively sanitizes
931 PR c++/70105, which has an example of printing an expression where the
932 final location of the expression is in a different macro, which
933 erroneously was leading to hundreds of lines of irrelevant source
934 being printed. */
936 static bool
937 compatible_locations_p (location_t loc_a, location_t loc_b)
939 if (IS_ADHOC_LOC (loc_a))
940 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
941 if (IS_ADHOC_LOC (loc_b))
942 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
944 /* If either location is one of the special locations outside of a
945 linemap, they are only compatible if they are equal. */
946 if (loc_a < RESERVED_LOCATION_COUNT
947 || loc_b < RESERVED_LOCATION_COUNT)
948 return loc_a == loc_b;
950 const line_map *map_a = linemap_lookup (line_table, loc_a);
951 linemap_assert (map_a);
953 const line_map *map_b = linemap_lookup (line_table, loc_b);
954 linemap_assert (map_b);
956 /* Are they within the same map? */
957 if (map_a == map_b)
959 /* Are both within the same macro expansion? */
960 if (linemap_macro_expansion_map_p (map_a))
962 /* If so, then they're only compatible if either both are
963 from the macro definition, or both from the macro arguments. */
964 bool loc_a_from_defn
965 = linemap_location_from_macro_definition_p (line_table, loc_a);
966 bool loc_b_from_defn
967 = linemap_location_from_macro_definition_p (line_table, loc_b);
968 if (loc_a_from_defn != loc_b_from_defn)
969 return false;
971 /* Expand each location towards the spelling location, and
972 recurse. */
973 const line_map_macro *macro_map = linemap_check_macro (map_a);
974 location_t loc_a_toward_spelling
975 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
976 macro_map,
977 loc_a);
978 location_t loc_b_toward_spelling
979 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
980 macro_map,
981 loc_b);
982 return compatible_locations_p (loc_a_toward_spelling,
983 loc_b_toward_spelling);
986 /* Otherwise they are within the same ordinary map. */
987 return true;
989 else
991 /* Within different maps. */
993 /* If either is within a macro expansion, they are incompatible. */
994 if (linemap_macro_expansion_map_p (map_a)
995 || linemap_macro_expansion_map_p (map_b))
996 return false;
998 /* Within two different ordinary maps; they are compatible iff they
999 are in the same file. */
1000 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1001 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1002 return ord_map_a->to_file == ord_map_b->to_file;
1006 /* Comparator for sorting fix-it hints. */
1008 static int
1009 fixit_cmp (const void *p_a, const void *p_b)
1011 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1012 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1013 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1016 /* Callbacks for use when not escaping the source. */
1018 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1020 /* Callback for char_display_policy::m_print_cb for printing source chars
1021 when not escaping the source. */
1023 static void
1024 default_print_decoded_ch (pretty_printer *pp,
1025 const cpp_decoded_char &decoded_ch)
1027 for (const char *ptr = decoded_ch.m_start_byte;
1028 ptr != decoded_ch.m_next_byte; ptr++)
1030 if (*ptr == '\0' || *ptr == '\r')
1032 pp_space (pp);
1033 continue;
1036 pp_character (pp, *ptr);
1040 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1042 static const int width_per_escaped_byte = 4;
1044 /* Callback for char_column_policy::m_width_cb for determining the
1045 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1047 static int
1048 escape_as_bytes_width (cppchar_t ch)
1050 if (ch < 0x80 && ISPRINT (ch))
1051 return cpp_wcwidth (ch);
1052 else
1054 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1055 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1056 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1057 return 4 * width_per_escaped_byte;
1061 /* Callback for char_display_policy::m_print_cb for printing source chars
1062 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1064 static void
1065 escape_as_bytes_print (pretty_printer *pp,
1066 const cpp_decoded_char &decoded_ch)
1068 if (!decoded_ch.m_valid_ch)
1070 for (const char *iter = decoded_ch.m_start_byte;
1071 iter != decoded_ch.m_next_byte; ++iter)
1073 char buf[16];
1074 sprintf (buf, "<%02x>", (unsigned char)*iter);
1075 pp_string (pp, buf);
1077 return;
1080 cppchar_t ch = decoded_ch.m_ch;
1081 if (ch < 0x80 && ISPRINT (ch))
1082 pp_character (pp, ch);
1083 else
1085 for (const char *iter = decoded_ch.m_start_byte;
1086 iter < decoded_ch.m_next_byte; ++iter)
1088 char buf[16];
1089 sprintf (buf, "<%02x>", (unsigned char)*iter);
1090 pp_string (pp, buf);
1095 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1097 /* Callback for char_column_policy::m_width_cb for determining the
1098 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1100 static int
1101 escape_as_unicode_width (cppchar_t ch)
1103 if (ch < 0x80 && ISPRINT (ch))
1104 return cpp_wcwidth (ch);
1105 else
1107 // Width of "<U+%04x>"
1108 if (ch > 0xfffff)
1109 return 10;
1110 else if (ch > 0xffff)
1111 return 9;
1112 else
1113 return 8;
1117 /* Callback for char_display_policy::m_print_cb for printing source chars
1118 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1120 static void
1121 escape_as_unicode_print (pretty_printer *pp,
1122 const cpp_decoded_char &decoded_ch)
1124 if (!decoded_ch.m_valid_ch)
1126 escape_as_bytes_print (pp, decoded_ch);
1127 return;
1130 cppchar_t ch = decoded_ch.m_ch;
1131 if (ch < 0x80 && ISPRINT (ch))
1132 pp_character (pp, ch);
1133 else
1135 char buf[16];
1136 sprintf (buf, "<U+%04X>", ch);
1137 pp_string (pp, buf);
1141 /* Populate a char_display_policy based on DC and RICHLOC. */
1143 static char_display_policy
1144 make_policy (const diagnostic_context &dc,
1145 const rich_location &richloc)
1147 /* The default is to not escape non-ASCII bytes. */
1148 char_display_policy result
1149 (dc.tabstop, cpp_wcwidth, default_print_decoded_ch);
1151 /* If the diagnostic suggests escaping non-ASCII bytes, then
1152 use policy from user-supplied options. */
1153 if (richloc.escape_on_output_p ())
1155 result.m_undecoded_byte_width = width_per_escaped_byte;
1156 switch (dc.escape_format)
1158 default:
1159 gcc_unreachable ();
1160 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1161 result.m_width_cb = escape_as_unicode_width;
1162 result.m_print_cb = escape_as_unicode_print;
1163 break;
1164 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1165 result.m_width_cb = escape_as_bytes_width;
1166 result.m_print_cb = escape_as_bytes_print;
1167 break;
1171 return result;
1174 /* Implementation of class layout. */
1176 /* Constructor for class layout.
1178 Filter the ranges from the rich_location to those that we can
1179 sanely print, populating m_layout_ranges and m_fixit_hints.
1180 Determine the range of lines that we will print, splitting them
1181 up into an ordered list of disjoint spans of contiguous line numbers.
1182 Determine m_x_offset_display, to ensure that the primary caret
1183 will fit within the max_width provided by the diagnostic_context. */
1185 layout::layout (diagnostic_context * context,
1186 rich_location *richloc,
1187 diagnostic_t diagnostic_kind,
1188 pretty_printer *pp)
1189 : m_context (context),
1190 m_pp (pp ? pp : context->printer),
1191 m_policy (make_policy (*context, *richloc)),
1192 m_primary_loc (richloc->get_range (0)->m_loc),
1193 m_exploc (richloc->get_expanded_location (0), m_policy,
1194 LOCATION_ASPECT_CARET),
1195 m_colorizer (context, diagnostic_kind),
1196 m_colorize_source_p (context->colorize_source_p),
1197 m_show_labels_p (context->show_labels_p),
1198 m_show_line_numbers_p (context->show_line_numbers_p),
1199 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1200 m_layout_ranges (richloc->get_num_locations ()),
1201 m_fixit_hints (richloc->get_num_fixit_hints ()),
1202 m_line_spans (1 + richloc->get_num_locations ()),
1203 m_linenum_width (0),
1204 m_x_offset_display (0),
1205 m_escape_on_output (richloc->escape_on_output_p ())
1207 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
1209 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1210 Ignore any ranges that are awkward to handle. */
1211 const location_range *loc_range = richloc->get_range (idx);
1212 maybe_add_location_range (loc_range, idx, false);
1215 /* Populate m_fixit_hints, filtering to only those that are in the
1216 same file. */
1217 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1219 const fixit_hint *hint = richloc->get_fixit_hint (i);
1220 if (validate_fixit_hint_p (hint))
1221 m_fixit_hints.safe_push (hint);
1224 /* Sort m_fixit_hints. */
1225 m_fixit_hints.qsort (fixit_cmp);
1227 /* Populate the indicated members. */
1228 calculate_line_spans ();
1229 calculate_linenum_width ();
1230 calculate_x_offset_display ();
1232 if (context->show_ruler_p)
1233 show_ruler (m_x_offset_display + m_context->caret_max_width);
1237 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1238 those that we can sanely print.
1240 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1241 (for use as extrinsic state by label ranges FIXME).
1243 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1244 filtered against this layout instance's current line spans: it
1245 will only be added if the location is fully within the lines
1246 already specified by other locations.
1248 Return true iff LOC_RANGE was added. */
1250 bool
1251 layout::maybe_add_location_range (const location_range *loc_range,
1252 unsigned original_idx,
1253 bool restrict_to_current_line_spans)
1255 gcc_assert (loc_range);
1257 /* Split the "range" into caret and range information. */
1258 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1260 /* Expand the various locations. */
1261 expanded_location start
1262 = linemap_client_expand_location_to_spelling_point
1263 (src_range.m_start, LOCATION_ASPECT_START);
1264 expanded_location finish
1265 = linemap_client_expand_location_to_spelling_point
1266 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1267 expanded_location caret
1268 = linemap_client_expand_location_to_spelling_point
1269 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1271 /* If any part of the range isn't in the same file as the primary
1272 location of this diagnostic, ignore the range. */
1273 if (start.file != m_exploc.file)
1274 return false;
1275 if (finish.file != m_exploc.file)
1276 return false;
1277 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1278 if (caret.file != m_exploc.file)
1279 return false;
1281 /* Sanitize the caret location for non-primary ranges. */
1282 if (m_layout_ranges.length () > 0)
1283 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1284 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1285 /* Discard any non-primary ranges that can't be printed
1286 sanely relative to the primary location. */
1287 return false;
1289 /* Everything is now known to be in the correct source file,
1290 but it may require further sanitization. */
1291 layout_range ri (exploc_with_display_col (start, m_policy,
1292 LOCATION_ASPECT_START),
1293 exploc_with_display_col (finish, m_policy,
1294 LOCATION_ASPECT_FINISH),
1295 loc_range->m_range_display_kind,
1296 exploc_with_display_col (caret, m_policy,
1297 LOCATION_ASPECT_CARET),
1298 original_idx, loc_range->m_label);
1300 /* If we have a range that finishes before it starts (perhaps
1301 from something built via macro expansion), printing the
1302 range is likely to be nonsensical. Also, attempting to do so
1303 breaks assumptions within the printing code (PR c/68473).
1304 Similarly, don't attempt to print ranges if one or both ends
1305 of the range aren't sane to print relative to the
1306 primary location (PR c++/70105). */
1307 if (start.line > finish.line
1308 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1309 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1311 /* Is this the primary location? */
1312 if (m_layout_ranges.length () == 0)
1314 /* We want to print the caret for the primary location, but
1315 we must sanitize away m_start and m_finish. */
1316 ri.m_start = ri.m_caret;
1317 ri.m_finish = ri.m_caret;
1319 else
1320 /* This is a non-primary range; ignore it. */
1321 return false;
1324 /* Potentially filter to just the lines already specified by other
1325 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1326 The layout ctor doesn't use it, and can't because m_line_spans
1327 hasn't been set up at that point. */
1328 if (restrict_to_current_line_spans)
1330 if (!will_show_line_p (start.line))
1331 return false;
1332 if (!will_show_line_p (finish.line))
1333 return false;
1334 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1335 if (!will_show_line_p (caret.line))
1336 return false;
1339 /* Passed all the tests; add the range to m_layout_ranges so that
1340 it will be printed. */
1341 m_layout_ranges.safe_push (ri);
1342 return true;
1345 /* Return true iff ROW is within one of the line spans for this layout. */
1347 bool
1348 layout::will_show_line_p (linenum_type row) const
1350 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1351 line_span_idx++)
1353 const line_span *line_span = get_line_span (line_span_idx);
1354 if (line_span->contains_line_p (row))
1355 return true;
1357 return false;
1360 /* Print a line showing a gap in the line numbers, for showing the boundary
1361 between two line spans. */
1363 void
1364 layout::print_gap_in_line_numbering ()
1366 gcc_assert (m_show_line_numbers_p);
1368 pp_emit_prefix (m_pp);
1370 for (int i = 0; i < m_linenum_width + 1; i++)
1371 pp_character (m_pp, '.');
1373 pp_newline (m_pp);
1376 /* Return true iff we should print a heading when starting the
1377 line span with the given index. */
1379 bool
1380 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1382 /* We print a heading for every change of line span, hence for every
1383 line span after the initial one. */
1384 if (line_span_idx > 0)
1385 return true;
1387 /* We also do it for the initial span if the primary location of the
1388 diagnostic is in a different span. */
1389 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1390 return true;
1392 return false;
1395 /* Get an expanded_location for the first location of interest within
1396 the given line_span.
1397 Used when printing a heading to indicate a new line span. */
1399 expanded_location
1400 layout::get_expanded_location (const line_span *line_span) const
1402 /* Whenever possible, use the caret location. */
1403 if (line_span->contains_line_p (m_exploc.line))
1404 return m_exploc;
1406 /* Otherwise, use the start of the first range that's present
1407 within the line_span. */
1408 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1410 const layout_range *lr = &m_layout_ranges[i];
1411 if (line_span->contains_line_p (lr->m_start.m_line))
1413 expanded_location exploc = m_exploc;
1414 exploc.line = lr->m_start.m_line;
1415 exploc.column = lr->m_start.m_columns[CU_BYTES];
1416 return exploc;
1420 /* Otherwise, use the location of the first fixit-hint present within
1421 the line_span. */
1422 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1424 const fixit_hint *hint = m_fixit_hints[i];
1425 location_t loc = hint->get_start_loc ();
1426 expanded_location exploc = expand_location (loc);
1427 if (line_span->contains_line_p (exploc.line))
1428 return exploc;
1431 /* It should not be possible to have a line span that didn't
1432 contain any of the layout_range or fixit_hint instances. */
1433 gcc_unreachable ();
1434 return m_exploc;
1437 /* Determine if HINT is meaningful to print within this layout. */
1439 bool
1440 layout::validate_fixit_hint_p (const fixit_hint *hint)
1442 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1443 return false;
1444 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1445 return false;
1447 return true;
1450 /* Determine the range of lines affected by HINT.
1451 This assumes that HINT has already been filtered by
1452 validate_fixit_hint_p, and so affects the correct source file. */
1454 static line_span
1455 get_line_span_for_fixit_hint (const fixit_hint *hint)
1457 gcc_assert (hint);
1459 int start_line = LOCATION_LINE (hint->get_start_loc ());
1461 /* For line-insertion fix-it hints, add the previous line to the
1462 span, to give the user more context on the proposed change. */
1463 if (hint->ends_with_newline_p ())
1464 if (start_line > 1)
1465 start_line--;
1467 return line_span (start_line,
1468 LOCATION_LINE (hint->get_next_loc ()));
1471 /* We want to print the pertinent source code at a diagnostic. The
1472 rich_location can contain multiple locations. This will have been
1473 filtered into m_exploc (the caret for the primary location) and
1474 m_layout_ranges, for those ranges within the same source file.
1476 We will print a subset of the lines within the source file in question,
1477 as a collection of "spans" of lines.
1479 This function populates m_line_spans with an ordered, disjoint list of
1480 the line spans of interest.
1482 Printing a gap between line spans takes one line, so, when printing
1483 line numbers, we allow a gap of up to one line between spans when
1484 merging, since it makes more sense to print the source line rather than a
1485 "gap-in-line-numbering" line. When not printing line numbers, it's
1486 better to be more explicit about what's going on, so keeping them as
1487 separate spans is preferred.
1489 For example, if the primary range is on lines 8-10, with secondary ranges
1490 covering lines 5-6 and lines 13-15:
1493 005 |RANGE 1
1494 006 |RANGE 1
1496 008 |PRIMARY RANGE
1497 009 |PRIMARY CARET
1498 010 |PRIMARY RANGE
1501 013 |RANGE 2
1502 014 |RANGE 2
1503 015 |RANGE 2
1506 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1508 With line numbering off (with span headers), we want three spans: lines 5-6,
1509 lines 8-10, and lines 13-15. */
1511 void
1512 layout::calculate_line_spans ()
1514 /* This should only be called once, by the ctor. */
1515 gcc_assert (m_line_spans.length () == 0);
1517 /* Populate tmp_spans with individual spans, for each of
1518 m_exploc, and for m_layout_ranges. */
1519 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1520 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1521 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1523 const layout_range *lr = &m_layout_ranges[i];
1524 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1525 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1526 lr->m_finish.m_line));
1529 /* Also add spans for any fix-it hints, in case they cover other lines. */
1530 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1532 const fixit_hint *hint = m_fixit_hints[i];
1533 gcc_assert (hint);
1534 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1537 /* Sort them. */
1538 tmp_spans.qsort(line_span::comparator);
1540 /* Now iterate through tmp_spans, copying into m_line_spans, and
1541 combining where possible. */
1542 gcc_assert (tmp_spans.length () > 0);
1543 m_line_spans.safe_push (tmp_spans[0]);
1544 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1546 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1547 const line_span *next = &tmp_spans[i];
1548 gcc_assert (next->m_first_line >= current->m_first_line);
1549 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1550 if ((linenum_arith_t)next->m_first_line
1551 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1553 /* We can merge them. */
1554 if (next->m_last_line > current->m_last_line)
1555 current->m_last_line = next->m_last_line;
1557 else
1559 /* No merger possible. */
1560 m_line_spans.safe_push (*next);
1564 /* Verify the result, in m_line_spans. */
1565 gcc_assert (m_line_spans.length () > 0);
1566 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1568 const line_span *prev = &m_line_spans[i - 1];
1569 const line_span *next = &m_line_spans[i];
1570 /* The individual spans must be sane. */
1571 gcc_assert (prev->m_first_line <= prev->m_last_line);
1572 gcc_assert (next->m_first_line <= next->m_last_line);
1573 /* The spans must be ordered. */
1574 gcc_assert (prev->m_first_line < next->m_first_line);
1575 /* There must be a gap of at least one line between separate spans. */
1576 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1580 /* Determine how many display columns need to be reserved for line numbers,
1581 based on the largest line number that will be needed, and populate
1582 m_linenum_width. */
1584 void
1585 layout::calculate_linenum_width ()
1587 gcc_assert (m_line_spans.length () > 0);
1588 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1589 int highest_line = last_span->m_last_line;
1590 if (highest_line < 0)
1591 highest_line = 0;
1592 m_linenum_width = num_digits (highest_line);
1593 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1594 if (m_line_spans.length () > 1)
1595 m_linenum_width = MAX (m_linenum_width, 3);
1596 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1597 after the line number. */
1598 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1601 /* Calculate m_x_offset_display, which improves readability in case the source
1602 line of interest is longer than the user's display. All lines output will be
1603 shifted to the left (so that their beginning is no longer displayed) by
1604 m_x_offset_display display columns, so that the caret is in a reasonable
1605 location. */
1607 void
1608 layout::calculate_x_offset_display ()
1610 m_x_offset_display = 0;
1612 const int max_width = m_context->caret_max_width;
1613 if (!max_width)
1615 /* Nothing to do, the width is not capped. */
1616 return;
1619 const char_span line = location_get_source_line (m_exploc.file,
1620 m_exploc.line);
1621 if (!line)
1623 /* Nothing to do, we couldn't find the source line. */
1624 return;
1626 int caret_display_column = m_exploc.m_display_col;
1627 const int line_bytes
1628 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1629 line.length ());
1630 int eol_display_column
1631 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1632 if (caret_display_column > eol_display_column
1633 || !caret_display_column)
1635 /* This does not make sense, so don't try to do anything in this case. */
1636 return;
1639 /* Adjust caret and eol positions to include the left margin. If we are
1640 outputting line numbers, then the left margin is equal to m_linenum_width
1641 plus three for the " | " which follows it. Otherwise the left margin width
1642 is equal to 1, because layout::print_source_line() will prefix each line
1643 with a space. */
1644 const int source_display_cols = eol_display_column;
1645 int left_margin_size = 1;
1646 if (m_show_line_numbers_p)
1647 left_margin_size = m_linenum_width + 3;
1648 caret_display_column += left_margin_size;
1649 eol_display_column += left_margin_size;
1651 if (eol_display_column <= max_width)
1653 /* Nothing to do, everything fits in the display. */
1654 return;
1657 /* The line is too long for the display. Calculate an offset such that the
1658 caret is not too close to the right edge of the screen. It will be
1659 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1660 than that to the end of the source line anyway. */
1661 int right_margin_size = CARET_LINE_MARGIN;
1662 right_margin_size = MIN (eol_display_column - caret_display_column,
1663 right_margin_size);
1664 if (right_margin_size + left_margin_size >= max_width)
1666 /* The max_width is very small, so anything we try to do will not be very
1667 effective; just punt in this case and output with no offset. */
1668 return;
1670 const int max_caret_display_column = max_width - right_margin_size;
1671 if (caret_display_column > max_caret_display_column)
1673 m_x_offset_display = caret_display_column - max_caret_display_column;
1674 /* Make sure we don't offset the line into oblivion. */
1675 static const int min_cols_visible = 2;
1676 if (source_display_cols - m_x_offset_display < min_cols_visible)
1677 m_x_offset_display = 0;
1681 /* Print line ROW of source code, potentially colorized at any ranges, and
1682 return the line bounds. LINE is the source line (not necessarily
1683 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1684 colorization and tab expansion, this function tracks the line position in
1685 both byte and display column units. */
1687 line_bounds
1688 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1690 m_colorizer.set_normal_text ();
1692 pp_emit_prefix (m_pp);
1693 if (m_show_line_numbers_p)
1695 int width = num_digits (row);
1696 for (int i = 0; i < m_linenum_width - width; i++)
1697 pp_space (m_pp);
1698 pp_printf (m_pp, "%i | ", row);
1700 else
1701 pp_space (m_pp);
1703 /* We will stop printing the source line at any trailing whitespace. */
1704 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1705 line_bytes);
1707 /* This object helps to keep track of which display column we are at, which is
1708 necessary for computing the line bounds in display units, for doing
1709 tab expansion, and for implementing m_x_offset_display. */
1710 cpp_display_width_computation dw (line, line_bytes, m_policy);
1712 /* Skip the first m_x_offset_display display columns. In case the leading
1713 portion that will be skipped ends with a character with wcwidth > 1, then
1714 it is possible we skipped too much, so account for that by padding with
1715 spaces. Note that this does the right thing too in case a tab was the last
1716 character to be skipped over; the tab is effectively replaced by the
1717 correct number of trailing spaces needed to offset by the desired number of
1718 display columns. */
1719 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1720 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1721 pp_space (m_pp);
1723 /* Print the line and compute the line_bounds. */
1724 line_bounds lbounds;
1725 while (!dw.done ())
1727 /* Assuming colorization is enabled for the caret and underline
1728 characters, we may also colorize the associated characters
1729 within the source line.
1731 For frontends that generate range information, we color the
1732 associated characters in the source line the same as the
1733 carets and underlines in the annotation line, to make it easier
1734 for the reader to see the pertinent code.
1736 For frontends that only generate carets, we don't colorize the
1737 characters above them, since this would look strange (e.g.
1738 colorizing just the first character in a token). */
1739 if (m_colorize_source_p)
1741 bool in_range_p;
1742 point_state state;
1743 const int start_byte_col = dw.bytes_processed () + 1;
1744 in_range_p = get_state_at_point (row, start_byte_col,
1745 0, INT_MAX,
1746 CU_BYTES,
1747 &state);
1748 if (in_range_p)
1749 m_colorizer.set_range (state.range_idx);
1750 else
1751 m_colorizer.set_normal_text ();
1754 /* Get the display width of the next character to be output, expanding
1755 tabs and replacing some control bytes with spaces as necessary. */
1756 const char *c = dw.next_byte ();
1757 const int start_disp_col = dw.display_cols_processed () + 1;
1758 cpp_decoded_char cp;
1759 const int this_display_width = dw.process_next_codepoint (&cp);
1760 if (*c == '\t')
1762 /* The returned display width is the number of spaces into which the
1763 tab should be expanded. */
1764 for (int i = 0; i != this_display_width; ++i)
1765 pp_space (m_pp);
1766 continue;
1769 /* We have a (possibly multibyte) character to output; update the line
1770 bounds if it is not whitespace. */
1771 if (*c != ' ')
1773 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1774 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1775 lbounds.m_first_non_ws_disp_col = start_disp_col;
1778 /* Output the character. */
1779 m_policy.m_print_cb (m_pp, cp);
1780 c = dw.next_byte ();
1782 print_newline ();
1783 return lbounds;
1786 /* Determine if we should print an annotation line for ROW.
1787 i.e. if any of m_layout_ranges contains ROW. */
1789 bool
1790 layout::should_print_annotation_line_p (linenum_type row) const
1792 layout_range *range;
1793 int i;
1794 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1796 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1797 return false;
1798 if (range->intersects_line_p (row))
1799 return true;
1801 return false;
1804 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1805 margin, which is empty for annotation lines. Otherwise, do nothing. */
1807 void
1808 layout::start_annotation_line (char margin_char) const
1810 pp_emit_prefix (m_pp);
1811 if (m_show_line_numbers_p)
1813 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1814 of it, right-aligned, padded with spaces. */
1815 int i;
1816 for (i = 0; i < m_linenum_width - 3; i++)
1817 pp_space (m_pp);
1818 for (; i < m_linenum_width; i++)
1819 pp_character (m_pp, margin_char);
1820 pp_string (m_pp, " |");
1824 /* Print a line consisting of the caret/underlines for the given
1825 source line. */
1827 void
1828 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1830 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1831 lbounds.m_last_non_ws_disp_col);
1833 start_annotation_line ();
1834 pp_space (m_pp);
1836 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1838 bool in_range_p;
1839 point_state state;
1840 in_range_p = get_state_at_point (row, column,
1841 lbounds.m_first_non_ws_disp_col,
1842 lbounds.m_last_non_ws_disp_col,
1843 CU_DISPLAY_COLS,
1844 &state);
1845 if (in_range_p)
1847 /* Within a range. Draw either the caret or an underline. */
1848 m_colorizer.set_range (state.range_idx);
1849 if (state.draw_caret_p)
1851 /* Draw the caret. */
1852 char caret_char;
1853 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1854 caret_char = m_context->caret_chars[state.range_idx];
1855 else
1856 caret_char = '^';
1857 pp_character (m_pp, caret_char);
1859 else
1860 pp_character (m_pp, '~');
1862 else
1864 /* Not in a range. */
1865 m_colorizer.set_normal_text ();
1866 pp_character (m_pp, ' ');
1869 print_newline ();
1872 /* A version of label_text that can live inside a vec.
1873 Requires manual cleanup via maybe_free. */
1875 struct pod_label_text
1877 pod_label_text ()
1878 : m_buffer (NULL), m_caller_owned (false)
1881 pod_label_text (label_text &&other)
1882 : m_buffer (const_cast<char*> (other.get ())),
1883 m_caller_owned (other.is_owner ())
1885 other.release ();
1888 void maybe_free ()
1890 if (m_caller_owned)
1891 free (m_buffer);
1894 char *m_buffer;
1895 bool m_caller_owned;
1898 /* Implementation detail of layout::print_any_labels.
1900 A label within the given row of source. */
1902 class line_label
1904 public:
1905 line_label (const cpp_char_column_policy &policy,
1906 int state_idx, int column,
1907 label_text text)
1908 : m_state_idx (state_idx), m_column (column),
1909 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1911 const int bytes = strlen (m_text.m_buffer);
1912 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1915 /* Sorting is primarily by column, then by state index. */
1916 static int comparator (const void *p1, const void *p2)
1918 const line_label *ll1 = (const line_label *)p1;
1919 const line_label *ll2 = (const line_label *)p2;
1920 int column_cmp = compare (ll1->m_column, ll2->m_column);
1921 if (column_cmp)
1922 return column_cmp;
1923 /* Order by reverse state index, so that labels are printed
1924 in order of insertion into the rich_location when the
1925 sorted list is walked backwards. */
1926 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1929 int m_state_idx;
1930 int m_column;
1931 pod_label_text m_text;
1932 size_t m_display_width;
1933 int m_label_line;
1934 bool m_has_vbar;
1937 /* Print any labels in this row. */
1938 void
1939 layout::print_any_labels (linenum_type row)
1941 int i;
1942 auto_vec<line_label> labels;
1944 /* Gather the labels that are to be printed into "labels". */
1946 layout_range *range;
1947 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1949 /* Most ranges don't have labels, so reject this first. */
1950 if (range->m_label == NULL)
1951 continue;
1953 /* The range's caret must be on this line. */
1954 if (range->m_caret.m_line != row)
1955 continue;
1957 /* Reject labels that aren't fully visible due to clipping
1958 by m_x_offset_display. */
1959 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1960 if (disp_col <= m_x_offset_display)
1961 continue;
1963 label_text text;
1964 text = range->m_label->get_text (range->m_original_idx);
1966 /* Allow for labels that return NULL from their get_text
1967 implementation (so e.g. such labels can control their own
1968 visibility). */
1969 if (text.get () == NULL)
1970 continue;
1972 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1976 /* Bail out if there are no labels on this row. */
1977 if (labels.length () == 0)
1978 return;
1980 /* Sort them. */
1981 labels.qsort(line_label::comparator);
1983 /* Figure out how many "label lines" we need, and which
1984 one each label is printed in.
1986 For example, if the labels aren't too densely packed,
1987 we can fit them on the same line, giving two "label lines":
1989 foo + bar
1990 ~~~ ~~~
1991 | | : label line 0
1992 l0 l1 : label line 1
1994 If they would touch each other or overlap, then we need
1995 additional "label lines":
1997 foo + bar
1998 ~~~ ~~~
1999 | | : label line 0
2000 | label 1 : label line 1
2001 label 0 : label line 2
2003 Place the final label on label line 1, and work backwards, adding
2004 label lines as needed.
2006 If multiple labels are at the same place, put them on separate
2007 label lines:
2009 foo + bar
2010 ^ : label line 0
2011 | : label line 1
2012 label 0 : label line 2
2013 label 1 : label line 3. */
2015 int max_label_line = 1;
2017 int next_column = INT_MAX;
2018 line_label *label;
2019 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2021 /* Would this label "touch" or overlap the next label? */
2022 if (label->m_column + label->m_display_width >= (size_t)next_column)
2024 max_label_line++;
2026 /* If we've already seen labels with the same column, suppress the
2027 vertical bar for subsequent ones in this backwards iteration;
2028 hence only the one with the highest label_line has m_has_vbar set. */
2029 if (label->m_column == next_column)
2030 label->m_has_vbar = false;
2033 label->m_label_line = max_label_line;
2034 next_column = label->m_column;
2038 /* Print the "label lines". For each label within the line, print
2039 either a vertical bar ('|') for the labels that are lower down, or the
2040 labels themselves once we've reached their line. */
2042 for (int label_line = 0; label_line <= max_label_line; label_line++)
2044 start_annotation_line ();
2045 pp_space (m_pp);
2046 int column = 1 + m_x_offset_display;
2047 line_label *label;
2048 FOR_EACH_VEC_ELT (labels, i, label)
2050 if (label_line > label->m_label_line)
2051 /* We've printed all the labels for this label line. */
2052 break;
2054 if (label_line == label->m_label_line)
2056 gcc_assert (column <= label->m_column);
2057 move_to_column (&column, label->m_column, true);
2058 /* Colorize the text, unless it's for events in a
2059 diagnostic_path. */
2060 if (!m_diagnostic_path_p)
2061 m_colorizer.set_range (label->m_state_idx);
2062 pp_string (m_pp, label->m_text.m_buffer);
2063 m_colorizer.set_normal_text ();
2064 column += label->m_display_width;
2066 else if (label->m_has_vbar)
2068 gcc_assert (column <= label->m_column);
2069 move_to_column (&column, label->m_column, true);
2070 m_colorizer.set_range (label->m_state_idx);
2071 pp_character (m_pp, '|');
2072 m_colorizer.set_normal_text ();
2073 column++;
2076 print_newline ();
2080 /* Clean up. */
2082 line_label *label;
2083 FOR_EACH_VEC_ELT (labels, i, label)
2084 label->m_text.maybe_free ();
2088 /* If there are any fixit hints inserting new lines before source line ROW,
2089 print them.
2091 They are printed on lines of their own, before the source line
2092 itself, with a leading '+'. */
2094 void
2095 layout::print_leading_fixits (linenum_type row)
2097 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2099 const fixit_hint *hint = m_fixit_hints[i];
2101 if (!hint->ends_with_newline_p ())
2102 /* Not a newline fixit; print it in print_trailing_fixits. */
2103 continue;
2105 gcc_assert (hint->insertion_p ());
2107 if (hint->affects_line_p (m_exploc.file, row))
2109 /* Printing the '+' with normal colorization
2110 and the inserted line with "insert" colorization
2111 helps them stand out from each other, and from
2112 the surrounding text. */
2113 m_colorizer.set_normal_text ();
2114 start_annotation_line ('+');
2115 pp_character (m_pp, '+');
2116 m_colorizer.set_fixit_insert ();
2117 /* Print all but the trailing newline of the fix-it hint.
2118 We have to print the newline separately to avoid
2119 getting additional pp prefixes printed. */
2120 for (size_t i = 0; i < hint->get_length () - 1; i++)
2121 pp_character (m_pp, hint->get_string ()[i]);
2122 m_colorizer.set_normal_text ();
2123 pp_newline (m_pp);
2128 /* Subroutine of layout::print_trailing_fixits.
2130 Determine if the annotation line printed for LINE contained
2131 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2133 bool
2134 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2135 int finish_column) const
2137 layout_range *range;
2138 int i;
2139 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2140 if (range->m_start.m_line == line
2141 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2142 && range->m_finish.m_line == line
2143 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2144 return true;
2145 return false;
2148 /* Classes for printing trailing fix-it hints i.e. those that
2149 don't add new lines.
2151 For insertion, these can look like:
2153 new_text
2155 For replacement, these can look like:
2157 ------------- : underline showing affected range
2158 new_text
2160 For deletion, these can look like:
2162 ------------- : underline showing affected range
2164 This can become confusing if they overlap, and so we need
2165 to do some preprocessing to decide what to print.
2166 We use the list of fixit_hint instances affecting the line
2167 to build a list of "correction" instances, and print the
2168 latter.
2170 For example, consider a set of fix-its for converting
2171 a C-style cast to a C++ const_cast.
2173 Given:
2175 ..000000000111111111122222222223333333333.
2176 ..123456789012345678901234567890123456789.
2177 foo *f = (foo *)ptr->field;
2178 ^~~~~
2180 and the fix-it hints:
2181 - replace col 10 (the open paren) with "const_cast<"
2182 - replace col 16 (the close paren) with "> ("
2183 - insert ")" before col 27
2185 then we would get odd-looking output:
2187 foo *f = (foo *)ptr->field;
2188 ^~~~~
2190 const_cast<
2192 > ( )
2194 It would be better to detect when fixit hints are going to
2195 overlap (those that require new lines), and to consolidate
2196 the printing of such fixits, giving something like:
2198 foo *f = (foo *)ptr->field;
2199 ^~~~~
2200 -----------------
2201 const_cast<foo *> (ptr->field)
2203 This works by detecting when the printing would overlap, and
2204 effectively injecting no-op replace hints into the gaps between
2205 such fix-its, so that the printing joins up.
2207 In the above example, the overlap of:
2208 - replace col 10 (the open paren) with "const_cast<"
2209 and:
2210 - replace col 16 (the close paren) with "> ("
2211 is fixed by injecting a no-op:
2212 - replace cols 11-15 with themselves ("foo *")
2213 and consolidating these, making:
2214 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2215 i.e.:
2216 - replace cols 10-16 with "const_cast<foo *> ("
2218 This overlaps with the final fix-it hint:
2219 - insert ")" before col 27
2220 and so we repeat the consolidation process, by injecting
2221 a no-op:
2222 - replace cols 17-26 with themselves ("ptr->field")
2223 giving:
2224 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2225 i.e.:
2226 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2228 and is thus printed as desired. */
2230 /* A range of (byte or display) columns within a line. */
2232 class column_range
2234 public:
2235 column_range (int start_, int finish_) : start (start_), finish (finish_)
2237 gcc_assert (valid_p (start, finish));
2240 bool operator== (const column_range &other) const
2242 return start == other.start && finish == other.finish;
2245 static bool valid_p (int start, int finish)
2247 /* We must have either a range, or an insertion. */
2248 return (start <= finish || finish == start - 1);
2251 int start;
2252 int finish;
2255 /* Get the range of bytes or display columns that HINT would affect. */
2256 static column_range
2257 get_affected_range (const cpp_char_column_policy &policy,
2258 const fixit_hint *hint, enum column_unit col_unit)
2260 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2261 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2262 --exploc_finish.column;
2264 int start_column;
2265 int finish_column;
2266 if (col_unit == CU_DISPLAY_COLS)
2268 start_column = location_compute_display_column (exploc_start, policy);
2269 if (hint->insertion_p ())
2270 finish_column = start_column - 1;
2271 else
2272 finish_column = location_compute_display_column (exploc_finish, policy);
2274 else
2276 start_column = exploc_start.column;
2277 finish_column = exploc_finish.column;
2279 return column_range (start_column, finish_column);
2282 /* Get the range of display columns that would be printed for HINT. */
2284 static column_range
2285 get_printed_columns (const cpp_char_column_policy &policy,
2286 const fixit_hint *hint)
2288 expanded_location exploc = expand_location (hint->get_start_loc ());
2289 int start_column = location_compute_display_column (exploc, policy);
2290 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2291 policy);
2292 int final_hint_column = start_column + hint_width - 1;
2293 if (hint->insertion_p ())
2295 return column_range (start_column, final_hint_column);
2297 else
2299 exploc = expand_location (hint->get_next_loc ());
2300 --exploc.column;
2301 int finish_column = location_compute_display_column (exploc, policy);
2302 return column_range (start_column,
2303 MAX (finish_column, final_hint_column));
2307 /* A correction on a particular line.
2308 This describes a plan for how to print one or more fixit_hint
2309 instances that affected the line, potentially consolidating hints
2310 into corrections to make the result easier for the user to read. */
2312 class correction
2314 public:
2315 correction (column_range affected_bytes,
2316 column_range affected_columns,
2317 column_range printed_columns,
2318 const char *new_text, size_t new_text_len,
2319 const cpp_char_column_policy &policy)
2320 : m_affected_bytes (affected_bytes),
2321 m_affected_columns (affected_columns),
2322 m_printed_columns (printed_columns),
2323 m_text (xstrdup (new_text)),
2324 m_byte_length (new_text_len),
2325 m_policy (policy),
2326 m_alloc_sz (new_text_len + 1)
2328 compute_display_cols ();
2331 ~correction () { free (m_text); }
2333 bool insertion_p () const
2335 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2338 void ensure_capacity (size_t len);
2339 void ensure_terminated ();
2341 void compute_display_cols ()
2343 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2346 void overwrite (int dst_offset, const char_span &src_span)
2348 gcc_assert (dst_offset >= 0);
2349 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2350 memcpy (m_text + dst_offset, src_span.get_buffer (),
2351 src_span.length ());
2354 /* If insert, then start: the column before which the text
2355 is to be inserted, and finish is offset by the length of
2356 the replacement.
2357 If replace, then the range of columns affected. */
2358 column_range m_affected_bytes;
2359 column_range m_affected_columns;
2361 /* If insert, then start: the column before which the text
2362 is to be inserted, and finish is offset by the length of
2363 the replacement.
2364 If replace, then the range of columns affected. */
2365 column_range m_printed_columns;
2367 /* The text to be inserted/used as replacement. */
2368 char *m_text;
2369 size_t m_byte_length; /* Not including null-terminator. */
2370 int m_display_cols;
2371 const cpp_char_column_policy &m_policy;
2372 size_t m_alloc_sz;
2375 /* Ensure that m_text can hold a string of length LEN
2376 (plus 1 for 0-termination). */
2378 void
2379 correction::ensure_capacity (size_t len)
2381 /* Allow 1 extra byte for 0-termination. */
2382 if (m_alloc_sz < (len + 1))
2384 size_t new_alloc_sz = (len + 1) * 2;
2385 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2386 m_alloc_sz = new_alloc_sz;
2390 /* Ensure that m_text is 0-terminated. */
2392 void
2393 correction::ensure_terminated ()
2395 /* 0-terminate the buffer. */
2396 gcc_assert (m_byte_length < m_alloc_sz);
2397 m_text[m_byte_length] = '\0';
2400 /* A list of corrections affecting a particular line.
2401 This is used by layout::print_trailing_fixits for planning
2402 how to print the fix-it hints affecting the line. */
2404 class line_corrections
2406 public:
2407 line_corrections (const char_display_policy &policy,
2408 const char *filename,
2409 linenum_type row)
2410 : m_policy (policy), m_filename (filename), m_row (row)
2412 ~line_corrections ();
2414 void add_hint (const fixit_hint *hint);
2416 const char_display_policy &m_policy;
2417 const char *m_filename;
2418 linenum_type m_row;
2419 auto_vec <correction *> m_corrections;
2422 /* struct line_corrections. */
2424 line_corrections::~line_corrections ()
2426 unsigned i;
2427 correction *c;
2428 FOR_EACH_VEC_ELT (m_corrections, i, c)
2429 delete c;
2432 /* A struct wrapping a particular source line, allowing
2433 run-time bounds-checking of accesses in a checked build. */
2435 class source_line
2437 public:
2438 source_line (const char *filename, int line);
2440 char_span as_span () { return char_span (chars, width); }
2442 const char *chars;
2443 int width;
2446 /* source_line's ctor. */
2448 source_line::source_line (const char *filename, int line)
2450 char_span span = location_get_source_line (filename, line);
2451 chars = span.get_buffer ();
2452 width = span.length ();
2455 /* Add HINT to the corrections for this line.
2456 Attempt to consolidate nearby hints so that they will not
2457 overlap with printed. */
2459 void
2460 line_corrections::add_hint (const fixit_hint *hint)
2462 column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES);
2463 column_range affected_columns = get_affected_range (m_policy, hint,
2464 CU_DISPLAY_COLS);
2465 column_range printed_columns = get_printed_columns (m_policy, hint);
2467 /* Potentially consolidate. */
2468 if (!m_corrections.is_empty ())
2470 correction *last_correction
2471 = m_corrections[m_corrections.length () - 1];
2473 /* The following consolidation code assumes that the fix-it hints
2474 have been sorted by start (done within layout's ctor). */
2475 gcc_assert (affected_bytes.start
2476 >= last_correction->m_affected_bytes.start);
2477 gcc_assert (printed_columns.start
2478 >= last_correction->m_printed_columns.start);
2480 if (printed_columns.start <= last_correction->m_printed_columns.finish
2481 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2482 affected_bytes.start - 1))
2484 /* We have two hints for which the printed forms of the hints
2485 would touch or overlap, so we need to consolidate them to avoid
2486 confusing the user.
2487 Attempt to inject a "replace" correction from immediately
2488 after the end of the last hint to immediately before the start
2489 of the next hint. */
2490 column_range between (last_correction->m_affected_bytes.finish + 1,
2491 affected_bytes.start - 1);
2493 /* Try to read the source. */
2494 source_line line (m_filename, m_row);
2495 if (line.chars && between.finish < line.width)
2497 /* Consolidate into the last correction:
2498 add a no-op "replace" of the "between" text, and
2499 add the text from the new hint. */
2500 int old_byte_len = last_correction->m_byte_length;
2501 gcc_assert (old_byte_len >= 0);
2502 int between_byte_len = between.finish + 1 - between.start;
2503 gcc_assert (between_byte_len >= 0);
2504 int new_byte_len
2505 = old_byte_len + between_byte_len + hint->get_length ();
2506 gcc_assert (new_byte_len >= 0);
2507 last_correction->ensure_capacity (new_byte_len);
2508 last_correction->overwrite
2509 (old_byte_len,
2510 line.as_span ().subspan (between.start - 1,
2511 between.finish + 1 - between.start));
2512 last_correction->overwrite (old_byte_len + between_byte_len,
2513 char_span (hint->get_string (),
2514 hint->get_length ()));
2515 last_correction->m_byte_length = new_byte_len;
2516 last_correction->ensure_terminated ();
2517 last_correction->m_affected_bytes.finish
2518 = affected_bytes.finish;
2519 last_correction->m_affected_columns.finish
2520 = affected_columns.finish;
2521 int prev_display_cols = last_correction->m_display_cols;
2522 last_correction->compute_display_cols ();
2523 last_correction->m_printed_columns.finish
2524 += last_correction->m_display_cols - prev_display_cols;
2525 return;
2530 /* If no consolidation happened, add a new correction instance. */
2531 m_corrections.safe_push (new correction (affected_bytes,
2532 affected_columns,
2533 printed_columns,
2534 hint->get_string (),
2535 hint->get_length (),
2536 m_policy));
2539 /* If there are any fixit hints on source line ROW, print them.
2540 They are printed in order, attempting to combine them onto lines, but
2541 starting new lines if necessary.
2542 Fix-it hints that insert new lines are handled separately,
2543 in layout::print_leading_fixits. */
2545 void
2546 layout::print_trailing_fixits (linenum_type row)
2548 /* Build a list of correction instances for the line,
2549 potentially consolidating hints (for the sake of readability). */
2550 line_corrections corrections (m_policy, m_exploc.file, row);
2551 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2553 const fixit_hint *hint = m_fixit_hints[i];
2555 /* Newline fixits are handled by layout::print_leading_fixits. */
2556 if (hint->ends_with_newline_p ())
2557 continue;
2559 if (hint->affects_line_p (m_exploc.file, row))
2560 corrections.add_hint (hint);
2563 /* Now print the corrections. */
2564 unsigned i;
2565 correction *c;
2566 int column = m_x_offset_display;
2568 if (!corrections.m_corrections.is_empty ())
2569 start_annotation_line ();
2571 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2573 /* For now we assume each fixit hint can only touch one line. */
2574 if (c->insertion_p ())
2576 /* This assumes the insertion just affects one line. */
2577 int start_column = c->m_printed_columns.start;
2578 move_to_column (&column, start_column, true);
2579 m_colorizer.set_fixit_insert ();
2580 pp_string (m_pp, c->m_text);
2581 m_colorizer.set_normal_text ();
2582 column += c->m_display_cols;
2584 else
2586 /* If the range of the replacement wasn't printed in the
2587 annotation line, then print an extra underline to
2588 indicate exactly what is being replaced.
2589 Always show it for removals. */
2590 int start_column = c->m_affected_columns.start;
2591 int finish_column = c->m_affected_columns.finish;
2592 if (!annotation_line_showed_range_p (row, start_column,
2593 finish_column)
2594 || c->m_byte_length == 0)
2596 move_to_column (&column, start_column, true);
2597 m_colorizer.set_fixit_delete ();
2598 for (; column <= finish_column; column++)
2599 pp_character (m_pp, '-');
2600 m_colorizer.set_normal_text ();
2602 /* Print the replacement text. REPLACE also covers
2603 removals, so only do this extra work (potentially starting
2604 a new line) if we have actual replacement text. */
2605 if (c->m_byte_length > 0)
2607 move_to_column (&column, start_column, true);
2608 m_colorizer.set_fixit_insert ();
2609 pp_string (m_pp, c->m_text);
2610 m_colorizer.set_normal_text ();
2611 column += c->m_display_cols;
2616 /* Add a trailing newline, if necessary. */
2617 move_to_column (&column, 0, false);
2620 /* Disable any colorization and emit a newline. */
2622 void
2623 layout::print_newline ()
2625 m_colorizer.set_normal_text ();
2626 pp_newline (m_pp);
2629 /* Return true if (ROW/COLUMN) is within a range of the layout.
2630 If it returns true, OUT_STATE is written to, with the
2631 range index, and whether we should draw the caret at
2632 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2633 whether all inputs and outputs are in bytes or display column units. */
2635 bool
2636 layout::get_state_at_point (/* Inputs. */
2637 linenum_type row, int column,
2638 int first_non_ws, int last_non_ws,
2639 enum column_unit col_unit,
2640 /* Outputs. */
2641 point_state *out_state)
2643 layout_range *range;
2644 int i;
2645 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2647 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2648 /* Bail out early, so that such ranges don't affect underlining or
2649 source colorization. */
2650 continue;
2652 if (range->contains_point (row, column, col_unit))
2654 out_state->range_idx = i;
2656 /* Are we at the range's caret? is it visible? */
2657 out_state->draw_caret_p = false;
2658 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2659 && row == range->m_caret.m_line
2660 && column == range->m_caret.m_columns[col_unit])
2661 out_state->draw_caret_p = true;
2663 /* Within a multiline range, don't display any underline
2664 in any leading or trailing whitespace on a line.
2665 We do display carets, however. */
2666 if (!out_state->draw_caret_p)
2667 if (column < first_non_ws || column > last_non_ws)
2668 return false;
2670 /* We are within a range. */
2671 return true;
2675 return false;
2678 /* Helper function for use by layout::print_line when printing the
2679 annotation line under the source line.
2680 Get the display column beyond the rightmost one that could contain a caret
2681 or range marker, given that we stop rendering at trailing whitespace.
2682 ROW is the source line within the given file.
2683 CARET_COLUMN is the display column of range 0's caret.
2684 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2685 character of source (as determined when printing the source line). */
2688 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2689 int last_non_ws_column)
2691 int result = caret_column + 1;
2693 layout_range *range;
2694 int i;
2695 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2697 if (row >= range->m_start.m_line)
2699 if (range->m_finish.m_line == row)
2701 /* On the final line within a range; ensure that
2702 we render up to the end of the range. */
2703 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2704 if (result <= disp_col)
2705 result = disp_col + 1;
2707 else if (row < range->m_finish.m_line)
2709 /* Within a multiline range; ensure that we render up to the
2710 last non-whitespace column. */
2711 if (result <= last_non_ws_column)
2712 result = last_non_ws_column + 1;
2717 return result;
2720 /* Given *COLUMN as an x-coordinate, print spaces to position
2721 successive output at DEST_COLUMN, printing a newline if necessary,
2722 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2723 left margin after any newline. */
2725 void
2726 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2728 /* Start a new line if we need to. */
2729 if (*column > dest_column)
2731 print_newline ();
2732 if (add_left_margin)
2733 start_annotation_line ();
2734 *column = m_x_offset_display;
2737 while (*column < dest_column)
2739 pp_space (m_pp);
2740 (*column)++;
2744 /* For debugging layout issues, render a ruler giving column numbers
2745 (after the 1-column indent). */
2747 void
2748 layout::show_ruler (int max_column) const
2750 /* Hundreds. */
2751 if (max_column > 99)
2753 start_annotation_line ();
2754 pp_space (m_pp);
2755 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2756 if (column % 10 == 0)
2757 pp_character (m_pp, '0' + (column / 100) % 10);
2758 else
2759 pp_space (m_pp);
2760 pp_newline (m_pp);
2763 /* Tens. */
2764 start_annotation_line ();
2765 pp_space (m_pp);
2766 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2767 if (column % 10 == 0)
2768 pp_character (m_pp, '0' + (column / 10) % 10);
2769 else
2770 pp_space (m_pp);
2771 pp_newline (m_pp);
2773 /* Units. */
2774 start_annotation_line ();
2775 pp_space (m_pp);
2776 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2777 pp_character (m_pp, '0' + (column % 10));
2778 pp_newline (m_pp);
2781 /* Print leading fix-its (for new lines inserted before the source line)
2782 then the source line, followed by an annotation line
2783 consisting of any caret/underlines, then any fixits.
2784 If the source line can't be read, print nothing. */
2785 void
2786 layout::print_line (linenum_type row)
2788 char_span line = location_get_source_line (m_exploc.file, row);
2789 if (!line)
2790 return;
2792 print_leading_fixits (row);
2793 const line_bounds lbounds
2794 = print_source_line (row, line.get_buffer (), line.length ());
2795 if (should_print_annotation_line_p (row))
2796 print_annotation_line (row, lbounds);
2797 if (m_show_labels_p)
2798 print_any_labels (row);
2799 print_trailing_fixits (row);
2802 } /* End of anonymous namespace. */
2804 /* If LOC is within the spans of lines that will already be printed for
2805 this gcc_rich_location, then add it as a secondary location and return true.
2807 Otherwise return false. */
2809 bool
2810 gcc_rich_location::add_location_if_nearby (location_t loc,
2811 bool restrict_to_current_line_spans,
2812 const range_label *label)
2814 /* Use the layout location-handling logic to sanitize LOC,
2815 filtering it to the current line spans within a temporary
2816 layout instance. */
2817 layout layout (global_dc, this, DK_ERROR);
2818 location_range loc_range;
2819 loc_range.m_loc = loc;
2820 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2821 if (!layout.maybe_add_location_range (&loc_range, 0,
2822 restrict_to_current_line_spans))
2823 return false;
2825 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2826 return true;
2829 /* Print the physical source code corresponding to the location of
2830 this diagnostic, with additional annotations.
2831 If PP is non-null, then use it rather than CONTEXT's printer. */
2833 void
2834 diagnostic_show_locus (diagnostic_context * context,
2835 rich_location *richloc,
2836 diagnostic_t diagnostic_kind,
2837 pretty_printer *pp)
2839 location_t loc = richloc->get_loc ();
2840 /* Do nothing if source-printing has been disabled. */
2841 if (!context->show_caret)
2842 return;
2844 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2845 if (loc <= BUILTINS_LOCATION)
2846 return;
2848 /* Don't print the same source location twice in a row, unless we have
2849 fix-it hints, or multiple locations, or a label. */
2850 if (loc == context->last_location
2851 && richloc->get_num_fixit_hints () == 0
2852 && richloc->get_num_locations () == 1
2853 && richloc->get_range (0)->m_label == NULL)
2854 return;
2856 context->last_location = loc;
2858 layout layout (context, richloc, diagnostic_kind, pp);
2859 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2860 line_span_idx++)
2862 const line_span *line_span = layout.get_line_span (line_span_idx);
2863 if (context->show_line_numbers_p)
2865 /* With line numbers, we should show whenever the line-numbering
2866 "jumps". */
2867 if (line_span_idx > 0)
2868 layout.print_gap_in_line_numbering ();
2870 else
2872 /* Without line numbers, we print headings for some line spans. */
2873 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2875 expanded_location exploc
2876 = layout.get_expanded_location (line_span);
2877 context->start_span (context, exploc);
2880 /* Iterate over the lines within this span (using linenum_arith_t to
2881 avoid overflow with 0xffffffff causing an infinite loop). */
2882 linenum_arith_t last_line = line_span->get_last_line ();
2883 for (linenum_arith_t row = line_span->get_first_line ();
2884 row <= last_line; row++)
2885 layout.print_line (row);
2889 #if CHECKING_P
2891 namespace selftest {
2893 /* Selftests for diagnostic_show_locus. */
2895 /* Verify that cpp_display_width correctly handles escaping. */
2897 static void
2898 test_display_widths ()
2900 gcc_rich_location richloc (UNKNOWN_LOCATION);
2902 /* U+03C0 "GREEK SMALL LETTER PI". */
2903 const char *pi = "\xCF\x80";
2904 /* U+1F642 "SLIGHTLY SMILING FACE". */
2905 const char *emoji = "\xF0\x9F\x99\x82";
2906 /* Stray trailing byte of a UTF-8 character. */
2907 const char *stray = "\xBF";
2908 /* U+10FFFF. */
2909 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2911 /* No escaping. */
2913 test_diagnostic_context dc;
2914 char_display_policy policy (make_policy (dc, richloc));
2915 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2916 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2917 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2918 /* Don't check width of U+10FFFF; it's in a private use plane. */
2921 richloc.set_escape_on_output (true);
2924 test_diagnostic_context dc;
2925 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
2926 char_display_policy policy (make_policy (dc, richloc));
2927 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2928 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2929 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2930 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2931 policy),
2932 strlen ("<U+10FFFF>"));
2936 test_diagnostic_context dc;
2937 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
2938 char_display_policy policy (make_policy (dc, richloc));
2939 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2940 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2941 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2942 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2943 policy),
2944 16);
2948 /* For precise tests of the layout, make clear where the source line will
2949 start. test_left_margin sets the total byte count from the left side of the
2950 screen to the start of source lines, after the line number and the separator,
2951 which consists of the three characters " | ". */
2952 static const int test_linenum_sep = 3;
2953 static const int test_left_margin = 7;
2955 /* Helper function for test_layout_x_offset_display_utf8(). */
2956 static void
2957 test_offset_impl (int caret_byte_col, int max_width,
2958 int expected_x_offset_display,
2959 int left_margin = test_left_margin)
2961 test_diagnostic_context dc;
2962 dc.caret_max_width = max_width;
2963 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2964 the line number plus one space after. */
2965 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2966 dc.show_line_numbers_p = true;
2967 rich_location richloc (line_table,
2968 linemap_position_for_column (line_table,
2969 caret_byte_col));
2970 layout test_layout (&dc, &richloc, DK_ERROR);
2971 ASSERT_EQ (left_margin - test_linenum_sep,
2972 test_layout.get_linenum_width ());
2973 ASSERT_EQ (expected_x_offset_display,
2974 test_layout.get_x_offset_display ());
2977 /* Test that layout::calculate_x_offset_display() works. */
2978 static void
2979 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2982 const char *content
2983 = "This line is very long, so that we can use it to test the logic for "
2984 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2985 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2986 "column #102.\n";
2988 /* Number of bytes in the line, subtracting one to remove the newline. */
2989 const int line_bytes = strlen (content) - 1;
2991 /* Number of display columns occupied by the line; each of the 2 emojis
2992 takes up 2 fewer display columns than it does bytes. */
2993 const int line_display_cols = line_bytes - 2*2;
2995 /* The column of the first emoji. Byte or display is the same as there are
2996 no multibyte characters earlier on the line. */
2997 const int emoji_col = 102;
2999 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3000 line_table_test ltt (case_);
3002 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3004 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3006 /* Don't attempt to run the tests if column data might be unavailable. */
3007 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3008 return;
3010 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3011 ASSERT_EQ (1, LOCATION_LINE (line_end));
3012 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3014 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3015 ASSERT_EQ (line_display_cols,
3016 cpp_display_width (lspan.get_buffer (), lspan.length (),
3017 def_policy ()));
3018 ASSERT_EQ (line_display_cols,
3019 location_compute_display_column (expand_location (line_end),
3020 def_policy ()));
3021 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3022 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3024 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3026 /* No constraint on the width -> no offset. */
3027 test_offset_impl (emoji_col, 0, 0);
3029 /* Caret is before the beginning -> no offset. */
3030 test_offset_impl (0, 100, 0);
3032 /* Caret is past the end of the line -> no offset. */
3033 test_offset_impl (line_bytes+1, 100, 0);
3035 /* Line fits in the display -> no offset. */
3036 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3037 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3039 /* Line is too long for the display but caret location is OK
3040 anyway -> no offset. */
3041 static const int small_width = 24;
3042 test_offset_impl (1, small_width, 0);
3044 /* Width constraint is very small -> no offset. */
3045 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3047 /* Line would be offset, but due to large line numbers, offsetting
3048 would remove the whole line -> no offset. */
3049 static const int huge_left_margin = 100;
3050 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3052 /* Line is the same length as the display, but the line number makes it too
3053 long, so offset is required. Caret is at the end so padding on the right
3054 is not in effect. */
3055 for (int excess = 1; excess <= 3; ++excess)
3056 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3057 excess);
3059 /* Line is much too long for the display, caret is near the end ->
3060 offset should be such that the line fits in the display and caret
3061 remains the same distance from the end that it was. */
3062 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3063 caret_offset <= max_offset; ++caret_offset)
3064 test_offset_impl (line_bytes - caret_offset, small_width,
3065 line_display_cols + test_left_margin - small_width);
3067 /* As previous case but caret is closer to the middle; now we want it to end
3068 up CARET_LINE_MARGIN bytes from the end. */
3069 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3070 test_offset_impl (emoji_col, small_width,
3071 emoji_col + test_left_margin
3072 - (small_width - CARET_LINE_MARGIN));
3074 /* Test that the source line is offset as expected when printed. */
3076 test_diagnostic_context dc;
3077 dc.caret_max_width = small_width - 6;
3078 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3079 dc.show_line_numbers_p = true;
3080 dc.show_ruler_p = true;
3081 rich_location richloc (line_table,
3082 linemap_position_for_column (line_table,
3083 emoji_col));
3084 layout test_layout (&dc, &richloc, DK_ERROR);
3085 test_layout.print_line (1);
3086 ASSERT_STREQ (" | 1 \n"
3087 " | 1 \n"
3088 " | 234567890123456789\n"
3089 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3090 "that occupies 8 bytes and 4 display columns, starting at "
3091 "column #102.\n"
3092 " | ^\n\n",
3093 pp_formatted_text (dc.printer));
3096 /* Similar to the previous example, but now the offset called for would split
3097 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3098 it with a padding space in this case. */
3100 test_diagnostic_context dc;
3101 dc.caret_max_width = small_width - 5;
3102 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3103 dc.show_line_numbers_p = true;
3104 dc.show_ruler_p = true;
3105 rich_location richloc (line_table,
3106 linemap_position_for_column (line_table,
3107 emoji_col + 2));
3108 layout test_layout (&dc, &richloc, DK_ERROR);
3109 test_layout.print_line (1);
3110 ASSERT_STREQ (" | 1 1 \n"
3111 " | 1 2 \n"
3112 " | 3456789012345678901\n"
3113 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3114 "that occupies 8 bytes and 4 display columns, starting at "
3115 "column #102.\n"
3116 " | ^\n\n",
3117 pp_formatted_text (dc.printer));
3122 static void
3123 test_layout_x_offset_display_tab (const line_table_case &case_)
3125 const char *content
3126 = "This line is very long, so that we can use it to test the logic for "
3127 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3128 "a variable number of display columns, starting at column #103.\n";
3130 /* Number of bytes in the line, subtracting one to remove the newline. */
3131 const int line_bytes = strlen (content) - 1;
3133 /* The column where the tab begins. Byte or display is the same as there are
3134 no multibyte characters earlier on the line. */
3135 const int tab_col = 103;
3137 /* Effective extra size of the tab beyond what a single space would have taken
3138 up, indexed by tabstop. */
3139 static const int num_tabstops = 11;
3140 int extra_width[num_tabstops];
3141 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3143 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3144 extra_width[tabstop] = this_tab_size - 1;
3146 /* Example of this calculation: if tabstop is 10, the tab starting at column
3147 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3148 next character is at column #111. So it takes up 7 more columns than
3149 a space would have taken up. */
3150 ASSERT_EQ (7, extra_width[10]);
3152 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3153 line_table_test ltt (case_);
3155 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3157 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3159 /* Don't attempt to run the tests if column data might be unavailable. */
3160 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3161 return;
3163 /* Check that cpp_display_width handles the tabs as expected. */
3164 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3165 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3166 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3168 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3169 ASSERT_EQ (line_bytes + extra_width[tabstop],
3170 cpp_display_width (lspan.get_buffer (), lspan.length (),
3171 policy));
3172 ASSERT_EQ (line_bytes + extra_width[tabstop],
3173 location_compute_display_column (expand_location (line_end),
3174 policy));
3177 /* Check that the tab is expanded to the expected number of spaces. */
3178 rich_location richloc (line_table,
3179 linemap_position_for_column (line_table,
3180 tab_col + 1));
3181 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3183 test_diagnostic_context dc;
3184 dc.tabstop = tabstop;
3185 layout test_layout (&dc, &richloc, DK_ERROR);
3186 test_layout.print_line (1);
3187 const char *out = pp_formatted_text (dc.printer);
3188 ASSERT_EQ (NULL, strchr (out, '\t'));
3189 const char *left_quote = strchr (out, '`');
3190 const char *right_quote = strchr (out, '\'');
3191 ASSERT_NE (NULL, left_quote);
3192 ASSERT_NE (NULL, right_quote);
3193 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3196 /* Check that the line is offset properly and that the tab is broken up
3197 into the expected number of spaces when it is the last character skipped
3198 over. */
3199 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3201 test_diagnostic_context dc;
3202 dc.tabstop = tabstop;
3203 static const int small_width = 24;
3204 dc.caret_max_width = small_width - 4;
3205 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3206 dc.show_line_numbers_p = true;
3207 layout test_layout (&dc, &richloc, DK_ERROR);
3208 test_layout.print_line (1);
3210 /* We have arranged things so that two columns will be printed before
3211 the caret. If the tab results in more than one space, this should
3212 produce two spaces in the output; otherwise, it will be a single space
3213 preceded by the opening quote before the tab character. */
3214 const char *output1
3215 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3216 "display columns, starting at column #103.\n"
3217 " | ^\n\n";
3218 const char *output2
3219 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3220 "display columns, starting at column #103.\n"
3221 " | ^\n\n";
3222 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3223 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3228 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3230 static void
3231 test_diagnostic_show_locus_unknown_location ()
3233 test_diagnostic_context dc;
3234 rich_location richloc (line_table, UNKNOWN_LOCATION);
3235 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3236 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3239 /* Verify that diagnostic_show_locus works sanely for various
3240 single-line cases.
3242 All of these work on the following 1-line source file:
3243 .0000000001111111
3244 .1234567890123456
3245 "foo = bar.field;\n"
3246 which is set up by test_diagnostic_show_locus_one_liner and calls
3247 them. */
3249 /* Just a caret. */
3251 static void
3252 test_one_liner_simple_caret ()
3254 test_diagnostic_context dc;
3255 location_t caret = linemap_position_for_column (line_table, 10);
3256 rich_location richloc (line_table, caret);
3257 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3258 ASSERT_STREQ (" foo = bar.field;\n"
3259 " ^\n",
3260 pp_formatted_text (dc.printer));
3263 /* Caret and range. */
3265 static void
3266 test_one_liner_caret_and_range ()
3268 test_diagnostic_context dc;
3269 location_t caret = linemap_position_for_column (line_table, 10);
3270 location_t start = linemap_position_for_column (line_table, 7);
3271 location_t finish = linemap_position_for_column (line_table, 15);
3272 location_t loc = make_location (caret, start, finish);
3273 rich_location richloc (line_table, loc);
3274 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3275 ASSERT_STREQ (" foo = bar.field;\n"
3276 " ~~~^~~~~~\n",
3277 pp_formatted_text (dc.printer));
3280 /* Multiple ranges and carets. */
3282 static void
3283 test_one_liner_multiple_carets_and_ranges ()
3285 test_diagnostic_context dc;
3286 location_t foo
3287 = make_location (linemap_position_for_column (line_table, 2),
3288 linemap_position_for_column (line_table, 1),
3289 linemap_position_for_column (line_table, 3));
3290 dc.caret_chars[0] = 'A';
3292 location_t bar
3293 = make_location (linemap_position_for_column (line_table, 8),
3294 linemap_position_for_column (line_table, 7),
3295 linemap_position_for_column (line_table, 9));
3296 dc.caret_chars[1] = 'B';
3298 location_t field
3299 = make_location (linemap_position_for_column (line_table, 13),
3300 linemap_position_for_column (line_table, 11),
3301 linemap_position_for_column (line_table, 15));
3302 dc.caret_chars[2] = 'C';
3304 rich_location richloc (line_table, foo);
3305 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3306 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3307 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3308 ASSERT_STREQ (" foo = bar.field;\n"
3309 " ~A~ ~B~ ~~C~~\n",
3310 pp_formatted_text (dc.printer));
3313 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3315 static void
3316 test_one_liner_fixit_insert_before ()
3318 test_diagnostic_context dc;
3319 location_t caret = linemap_position_for_column (line_table, 7);
3320 rich_location richloc (line_table, caret);
3321 richloc.add_fixit_insert_before ("&");
3322 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3323 ASSERT_STREQ (" foo = bar.field;\n"
3324 " ^\n"
3325 " &\n",
3326 pp_formatted_text (dc.printer));
3329 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3331 static void
3332 test_one_liner_fixit_insert_after ()
3334 test_diagnostic_context dc;
3335 location_t start = linemap_position_for_column (line_table, 1);
3336 location_t finish = linemap_position_for_column (line_table, 3);
3337 location_t foo = make_location (start, start, finish);
3338 rich_location richloc (line_table, foo);
3339 richloc.add_fixit_insert_after ("[0]");
3340 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3341 ASSERT_STREQ (" foo = bar.field;\n"
3342 " ^~~\n"
3343 " [0]\n",
3344 pp_formatted_text (dc.printer));
3347 /* Removal fix-it hint: removal of the ".field".
3348 Also verify the interaction of pp_set_prefix with rulers and
3349 fix-it hints. */
3351 static void
3352 test_one_liner_fixit_remove ()
3354 location_t start = linemap_position_for_column (line_table, 10);
3355 location_t finish = linemap_position_for_column (line_table, 15);
3356 location_t dot = make_location (start, start, finish);
3357 rich_location richloc (line_table, dot);
3358 richloc.add_fixit_remove ();
3360 /* Normal. */
3362 test_diagnostic_context dc;
3363 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3364 ASSERT_STREQ (" foo = bar.field;\n"
3365 " ^~~~~~\n"
3366 " ------\n",
3367 pp_formatted_text (dc.printer));
3370 /* Test of adding a prefix. */
3372 test_diagnostic_context dc;
3373 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3374 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3375 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3376 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3377 "TEST PREFIX: ^~~~~~\n"
3378 "TEST PREFIX: ------\n",
3379 pp_formatted_text (dc.printer));
3382 /* Normal, with ruler. */
3384 test_diagnostic_context dc;
3385 dc.show_ruler_p = true;
3386 dc.caret_max_width = 104;
3387 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3388 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3389 " 1 2 3 4 5 6 7 8 9 0 \n"
3390 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3391 " foo = bar.field;\n"
3392 " ^~~~~~\n"
3393 " ------\n",
3394 pp_formatted_text (dc.printer));
3397 /* Test of adding a prefix, with ruler. */
3399 test_diagnostic_context dc;
3400 dc.show_ruler_p = true;
3401 dc.caret_max_width = 50;
3402 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3403 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3404 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3405 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3406 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3407 "TEST PREFIX: foo = bar.field;\n"
3408 "TEST PREFIX: ^~~~~~\n"
3409 "TEST PREFIX: ------\n",
3410 pp_formatted_text (dc.printer));
3413 /* Test of adding a prefix, with ruler and line numbers. */
3415 test_diagnostic_context dc;
3416 dc.show_ruler_p = true;
3417 dc.caret_max_width = 50;
3418 dc.show_line_numbers_p = true;
3419 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3420 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3421 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3422 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3423 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3424 "TEST PREFIX: 1 | foo = bar.field;\n"
3425 "TEST PREFIX: | ^~~~~~\n"
3426 "TEST PREFIX: | ------\n",
3427 pp_formatted_text (dc.printer));
3431 /* Replace fix-it hint: replacing "field" with "m_field". */
3433 static void
3434 test_one_liner_fixit_replace ()
3436 test_diagnostic_context dc;
3437 location_t start = linemap_position_for_column (line_table, 11);
3438 location_t finish = linemap_position_for_column (line_table, 15);
3439 location_t field = make_location (start, start, finish);
3440 rich_location richloc (line_table, field);
3441 richloc.add_fixit_replace ("m_field");
3442 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3443 ASSERT_STREQ (" foo = bar.field;\n"
3444 " ^~~~~\n"
3445 " m_field\n",
3446 pp_formatted_text (dc.printer));
3449 /* Replace fix-it hint: replacing "field" with "m_field",
3450 but where the caret was elsewhere. */
3452 static void
3453 test_one_liner_fixit_replace_non_equal_range ()
3455 test_diagnostic_context dc;
3456 location_t equals = linemap_position_for_column (line_table, 5);
3457 location_t start = linemap_position_for_column (line_table, 11);
3458 location_t finish = linemap_position_for_column (line_table, 15);
3459 rich_location richloc (line_table, equals);
3460 source_range range;
3461 range.m_start = start;
3462 range.m_finish = finish;
3463 richloc.add_fixit_replace (range, "m_field");
3464 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3465 /* The replacement range is not indicated in the annotation line, so
3466 it should be indicated via an additional underline. */
3467 ASSERT_STREQ (" foo = bar.field;\n"
3468 " ^\n"
3469 " -----\n"
3470 " m_field\n",
3471 pp_formatted_text (dc.printer));
3474 /* Replace fix-it hint: replacing "field" with "m_field",
3475 where the caret was elsewhere, but where a secondary range
3476 exactly covers "field". */
3478 static void
3479 test_one_liner_fixit_replace_equal_secondary_range ()
3481 test_diagnostic_context dc;
3482 location_t equals = linemap_position_for_column (line_table, 5);
3483 location_t start = linemap_position_for_column (line_table, 11);
3484 location_t finish = linemap_position_for_column (line_table, 15);
3485 rich_location richloc (line_table, equals);
3486 location_t field = make_location (start, start, finish);
3487 richloc.add_range (field);
3488 richloc.add_fixit_replace (field, "m_field");
3489 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3490 /* The replacement range is indicated in the annotation line,
3491 so it shouldn't be indicated via an additional underline. */
3492 ASSERT_STREQ (" foo = bar.field;\n"
3493 " ^ ~~~~~\n"
3494 " m_field\n",
3495 pp_formatted_text (dc.printer));
3498 /* Verify that we can use ad-hoc locations when adding fixits to a
3499 rich_location. */
3501 static void
3502 test_one_liner_fixit_validation_adhoc_locations ()
3504 /* Generate a range that's too long to be packed, so must
3505 be stored as an ad-hoc location (given the defaults
3506 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3507 const location_t c7 = linemap_position_for_column (line_table, 7);
3508 const location_t c47 = linemap_position_for_column (line_table, 47);
3509 const location_t loc = make_location (c7, c7, c47);
3511 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3512 return;
3514 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3516 /* Insert. */
3518 rich_location richloc (line_table, loc);
3519 richloc.add_fixit_insert_before (loc, "test");
3520 /* It should not have been discarded by the validator. */
3521 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3523 test_diagnostic_context dc;
3524 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3525 ASSERT_STREQ (" foo = bar.field;\n"
3526 " ^~~~~~~~~~ \n"
3527 " test\n",
3528 pp_formatted_text (dc.printer));
3531 /* Remove. */
3533 rich_location richloc (line_table, loc);
3534 source_range range = source_range::from_locations (loc, c47);
3535 richloc.add_fixit_remove (range);
3536 /* It should not have been discarded by the validator. */
3537 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3539 test_diagnostic_context dc;
3540 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3541 ASSERT_STREQ (" foo = bar.field;\n"
3542 " ^~~~~~~~~~ \n"
3543 " -----------------------------------------\n",
3544 pp_formatted_text (dc.printer));
3547 /* Replace. */
3549 rich_location richloc (line_table, loc);
3550 source_range range = source_range::from_locations (loc, c47);
3551 richloc.add_fixit_replace (range, "test");
3552 /* It should not have been discarded by the validator. */
3553 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3555 test_diagnostic_context dc;
3556 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3557 ASSERT_STREQ (" foo = bar.field;\n"
3558 " ^~~~~~~~~~ \n"
3559 " test\n",
3560 pp_formatted_text (dc.printer));
3564 /* Test of consolidating insertions at the same location. */
3566 static void
3567 test_one_liner_many_fixits_1 ()
3569 test_diagnostic_context dc;
3570 location_t equals = linemap_position_for_column (line_table, 5);
3571 rich_location richloc (line_table, equals);
3572 for (int i = 0; i < 19; i++)
3573 richloc.add_fixit_insert_before ("a");
3574 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3575 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3576 ASSERT_STREQ (" foo = bar.field;\n"
3577 " ^\n"
3578 " aaaaaaaaaaaaaaaaaaa\n",
3579 pp_formatted_text (dc.printer));
3582 /* Ensure that we can add an arbitrary number of fix-it hints to a
3583 rich_location, even if they are not consolidated. */
3585 static void
3586 test_one_liner_many_fixits_2 ()
3588 test_diagnostic_context dc;
3589 location_t equals = linemap_position_for_column (line_table, 5);
3590 rich_location richloc (line_table, equals);
3591 for (int i = 0; i < 19; i++)
3593 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3594 richloc.add_fixit_insert_before (loc, "a");
3596 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3597 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3598 ASSERT_STREQ (" foo = bar.field;\n"
3599 " ^\n"
3600 " a a a a a a a a a a a a a a a a a a a\n",
3601 pp_formatted_text (dc.printer));
3604 /* Test of labeling the ranges within a rich_location. */
3606 static void
3607 test_one_liner_labels ()
3609 location_t foo
3610 = make_location (linemap_position_for_column (line_table, 1),
3611 linemap_position_for_column (line_table, 1),
3612 linemap_position_for_column (line_table, 3));
3613 location_t bar
3614 = make_location (linemap_position_for_column (line_table, 7),
3615 linemap_position_for_column (line_table, 7),
3616 linemap_position_for_column (line_table, 9));
3617 location_t field
3618 = make_location (linemap_position_for_column (line_table, 11),
3619 linemap_position_for_column (line_table, 11),
3620 linemap_position_for_column (line_table, 15));
3622 /* Example where all the labels fit on one line. */
3624 text_range_label label0 ("0");
3625 text_range_label label1 ("1");
3626 text_range_label label2 ("2");
3627 gcc_rich_location richloc (foo, &label0);
3628 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3629 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3632 test_diagnostic_context dc;
3633 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3634 ASSERT_STREQ (" foo = bar.field;\n"
3635 " ^~~ ~~~ ~~~~~\n"
3636 " | | |\n"
3637 " 0 1 2\n",
3638 pp_formatted_text (dc.printer));
3641 /* Verify that we can disable label-printing. */
3643 test_diagnostic_context dc;
3644 dc.show_labels_p = false;
3645 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3646 ASSERT_STREQ (" foo = bar.field;\n"
3647 " ^~~ ~~~ ~~~~~\n",
3648 pp_formatted_text (dc.printer));
3652 /* Example where the labels need extra lines. */
3654 text_range_label label0 ("label 0");
3655 text_range_label label1 ("label 1");
3656 text_range_label label2 ("label 2");
3657 gcc_rich_location richloc (foo, &label0);
3658 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3659 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3661 test_diagnostic_context dc;
3662 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3663 ASSERT_STREQ (" foo = bar.field;\n"
3664 " ^~~ ~~~ ~~~~~\n"
3665 " | | |\n"
3666 " | | label 2\n"
3667 " | label 1\n"
3668 " label 0\n",
3669 pp_formatted_text (dc.printer));
3672 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3673 but label 1 just touches label 2. */
3675 text_range_label label0 ("aaaaa");
3676 text_range_label label1 ("bbbb");
3677 text_range_label label2 ("c");
3678 gcc_rich_location richloc (foo, &label0);
3679 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3680 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3682 test_diagnostic_context dc;
3683 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3684 ASSERT_STREQ (" foo = bar.field;\n"
3685 " ^~~ ~~~ ~~~~~\n"
3686 " | | |\n"
3687 " | | c\n"
3688 " aaaaa bbbb\n",
3689 pp_formatted_text (dc.printer));
3692 /* Example of out-of-order ranges (thus requiring a sort). */
3694 text_range_label label0 ("0");
3695 text_range_label label1 ("1");
3696 text_range_label label2 ("2");
3697 gcc_rich_location richloc (field, &label0);
3698 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3699 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3701 test_diagnostic_context dc;
3702 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3703 ASSERT_STREQ (" foo = bar.field;\n"
3704 " ~~~ ~~~ ^~~~~\n"
3705 " | | |\n"
3706 " 2 1 0\n",
3707 pp_formatted_text (dc.printer));
3710 /* Ensure we don't ICE if multiple ranges with labels are on
3711 the same point. */
3713 text_range_label label0 ("label 0");
3714 text_range_label label1 ("label 1");
3715 text_range_label label2 ("label 2");
3716 gcc_rich_location richloc (bar, &label0);
3717 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3718 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3720 test_diagnostic_context dc;
3721 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3722 ASSERT_STREQ (" foo = bar.field;\n"
3723 " ^~~\n"
3724 " |\n"
3725 " label 0\n"
3726 " label 1\n"
3727 " label 2\n",
3728 pp_formatted_text (dc.printer));
3731 /* Example of out-of-order ranges (thus requiring a sort), where
3732 they overlap, and there are multiple ranges on the same point. */
3734 text_range_label label_0a ("label 0a");
3735 text_range_label label_1a ("label 1a");
3736 text_range_label label_2a ("label 2a");
3737 text_range_label label_0b ("label 0b");
3738 text_range_label label_1b ("label 1b");
3739 text_range_label label_2b ("label 2b");
3740 text_range_label label_0c ("label 0c");
3741 text_range_label label_1c ("label 1c");
3742 text_range_label label_2c ("label 2c");
3743 gcc_rich_location richloc (field, &label_0a);
3744 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3745 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3747 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3748 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3749 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3751 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3752 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3753 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3755 test_diagnostic_context dc;
3756 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3757 ASSERT_STREQ (" foo = bar.field;\n"
3758 " ~~~ ~~~ ^~~~~\n"
3759 " | | |\n"
3760 " | | label 0a\n"
3761 " | | label 0b\n"
3762 " | | label 0c\n"
3763 " | label 1a\n"
3764 " | label 1b\n"
3765 " | label 1c\n"
3766 " label 2a\n"
3767 " label 2b\n"
3768 " label 2c\n",
3769 pp_formatted_text (dc.printer));
3772 /* Verify that a NULL result from range_label::get_text is
3773 handled gracefully. */
3775 text_range_label label (NULL);
3776 gcc_rich_location richloc (bar, &label);
3778 test_diagnostic_context dc;
3779 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3780 ASSERT_STREQ (" foo = bar.field;\n"
3781 " ^~~\n",
3782 pp_formatted_text (dc.printer));
3785 /* TODO: example of formatted printing (needs to be in
3786 gcc-rich-location.cc due to Makefile.in issues). */
3789 /* Run the various one-liner tests. */
3791 static void
3792 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3794 /* Create a tempfile and write some text to it.
3795 ....................0000000001111111.
3796 ....................1234567890123456. */
3797 const char *content = "foo = bar.field;\n";
3798 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3799 line_table_test ltt (case_);
3801 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3803 location_t line_end = linemap_position_for_column (line_table, 16);
3805 /* Don't attempt to run the tests if column data might be unavailable. */
3806 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3807 return;
3809 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3810 ASSERT_EQ (1, LOCATION_LINE (line_end));
3811 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3813 test_one_liner_simple_caret ();
3814 test_one_liner_caret_and_range ();
3815 test_one_liner_multiple_carets_and_ranges ();
3816 test_one_liner_fixit_insert_before ();
3817 test_one_liner_fixit_insert_after ();
3818 test_one_liner_fixit_remove ();
3819 test_one_liner_fixit_replace ();
3820 test_one_liner_fixit_replace_non_equal_range ();
3821 test_one_liner_fixit_replace_equal_secondary_range ();
3822 test_one_liner_fixit_validation_adhoc_locations ();
3823 test_one_liner_many_fixits_1 ();
3824 test_one_liner_many_fixits_2 ();
3825 test_one_liner_labels ();
3828 /* Version of all one-liner tests exercising multibyte awareness. For
3829 simplicity we stick to using two multibyte characters in the test, U+1F602
3830 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3831 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3832 below asserts would be easier to read if we used UTF-8 directly in the
3833 string constants, but it seems better not to demand the host compiler
3834 support this, when it isn't otherwise necessary. Instead, whenever an
3835 extended character appears in a string, we put a line break after it so that
3836 all succeeding characters can appear visually at the correct display column.
3838 All of these work on the following 1-line source file:
3840 .0000000001111111111222222 display
3841 .1234567890123456789012345 columns
3842 "SS_foo = P_bar.SS_fieldP;\n"
3843 .0000000111111111222222223 byte
3844 .1356789012456789134567891 columns
3846 which is set up by test_diagnostic_show_locus_one_liner and calls
3847 them. Here SS represents the two display columns for the U+1F602 emoji and
3848 P represents the one display column for the U+03C0 pi symbol. */
3850 /* Just a caret. */
3852 static void
3853 test_one_liner_simple_caret_utf8 ()
3855 test_diagnostic_context dc;
3856 location_t caret = linemap_position_for_column (line_table, 18);
3857 rich_location richloc (line_table, caret);
3858 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3859 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3860 "_foo = \xcf\x80"
3861 "_bar.\xf0\x9f\x98\x82"
3862 "_field\xcf\x80"
3863 ";\n"
3864 " ^\n",
3865 pp_formatted_text (dc.printer));
3868 /* Caret and range. */
3869 static void
3870 test_one_liner_caret_and_range_utf8 ()
3872 test_diagnostic_context dc;
3873 location_t caret = linemap_position_for_column (line_table, 18);
3874 location_t start = linemap_position_for_column (line_table, 12);
3875 location_t finish = linemap_position_for_column (line_table, 30);
3876 location_t loc = make_location (caret, start, finish);
3877 rich_location richloc (line_table, loc);
3878 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3879 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3880 "_foo = \xcf\x80"
3881 "_bar.\xf0\x9f\x98\x82"
3882 "_field\xcf\x80"
3883 ";\n"
3884 " ~~~~~^~~~~~~~~~\n",
3885 pp_formatted_text (dc.printer));
3888 /* Multiple ranges and carets. */
3890 static void
3891 test_one_liner_multiple_carets_and_ranges_utf8 ()
3893 test_diagnostic_context dc;
3894 location_t foo
3895 = make_location (linemap_position_for_column (line_table, 7),
3896 linemap_position_for_column (line_table, 1),
3897 linemap_position_for_column (line_table, 8));
3898 dc.caret_chars[0] = 'A';
3900 location_t bar
3901 = make_location (linemap_position_for_column (line_table, 16),
3902 linemap_position_for_column (line_table, 12),
3903 linemap_position_for_column (line_table, 17));
3904 dc.caret_chars[1] = 'B';
3906 location_t field
3907 = make_location (linemap_position_for_column (line_table, 26),
3908 linemap_position_for_column (line_table, 19),
3909 linemap_position_for_column (line_table, 30));
3910 dc.caret_chars[2] = 'C';
3911 rich_location richloc (line_table, foo);
3912 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3913 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3914 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3915 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3916 "_foo = \xcf\x80"
3917 "_bar.\xf0\x9f\x98\x82"
3918 "_field\xcf\x80"
3919 ";\n"
3920 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3921 pp_formatted_text (dc.printer));
3924 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3926 static void
3927 test_one_liner_fixit_insert_before_utf8 ()
3929 test_diagnostic_context dc;
3930 location_t caret = linemap_position_for_column (line_table, 12);
3931 rich_location richloc (line_table, caret);
3932 richloc.add_fixit_insert_before ("&");
3933 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3934 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3935 "_foo = \xcf\x80"
3936 "_bar.\xf0\x9f\x98\x82"
3937 "_field\xcf\x80"
3938 ";\n"
3939 " ^\n"
3940 " &\n",
3941 pp_formatted_text (dc.printer));
3944 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3946 static void
3947 test_one_liner_fixit_insert_after_utf8 ()
3949 test_diagnostic_context dc;
3950 location_t start = linemap_position_for_column (line_table, 1);
3951 location_t finish = linemap_position_for_column (line_table, 8);
3952 location_t foo = make_location (start, start, finish);
3953 rich_location richloc (line_table, foo);
3954 richloc.add_fixit_insert_after ("[0]");
3955 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3956 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3957 "_foo = \xcf\x80"
3958 "_bar.\xf0\x9f\x98\x82"
3959 "_field\xcf\x80"
3960 ";\n"
3961 " ^~~~~~\n"
3962 " [0]\n",
3963 pp_formatted_text (dc.printer));
3966 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3968 static void
3969 test_one_liner_fixit_remove_utf8 ()
3971 test_diagnostic_context dc;
3972 location_t start = linemap_position_for_column (line_table, 18);
3973 location_t finish = linemap_position_for_column (line_table, 30);
3974 location_t dot = make_location (start, start, finish);
3975 rich_location richloc (line_table, dot);
3976 richloc.add_fixit_remove ();
3977 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3978 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3979 "_foo = \xcf\x80"
3980 "_bar.\xf0\x9f\x98\x82"
3981 "_field\xcf\x80"
3982 ";\n"
3983 " ^~~~~~~~~~\n"
3984 " ----------\n",
3985 pp_formatted_text (dc.printer));
3988 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3990 static void
3991 test_one_liner_fixit_replace_utf8 ()
3993 test_diagnostic_context dc;
3994 location_t start = linemap_position_for_column (line_table, 19);
3995 location_t finish = linemap_position_for_column (line_table, 30);
3996 location_t field = make_location (start, start, finish);
3997 rich_location richloc (line_table, field);
3998 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3999 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4000 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4001 "_foo = \xcf\x80"
4002 "_bar.\xf0\x9f\x98\x82"
4003 "_field\xcf\x80"
4004 ";\n"
4005 " ^~~~~~~~~\n"
4006 " m_\xf0\x9f\x98\x82"
4007 "_field\xcf\x80\n",
4008 pp_formatted_text (dc.printer));
4011 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4012 but where the caret was elsewhere. */
4014 static void
4015 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4017 test_diagnostic_context dc;
4018 location_t equals = linemap_position_for_column (line_table, 10);
4019 location_t start = linemap_position_for_column (line_table, 19);
4020 location_t finish = linemap_position_for_column (line_table, 30);
4021 rich_location richloc (line_table, equals);
4022 source_range range;
4023 range.m_start = start;
4024 range.m_finish = finish;
4025 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4026 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4027 /* The replacement range is not indicated in the annotation line, so
4028 it should be indicated via an additional underline. */
4029 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4030 "_foo = \xcf\x80"
4031 "_bar.\xf0\x9f\x98\x82"
4032 "_field\xcf\x80"
4033 ";\n"
4034 " ^\n"
4035 " ---------\n"
4036 " m_\xf0\x9f\x98\x82"
4037 "_field\xcf\x80\n",
4038 pp_formatted_text (dc.printer));
4041 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4042 where the caret was elsewhere, but where a secondary range
4043 exactly covers "field". */
4045 static void
4046 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4048 test_diagnostic_context dc;
4049 location_t equals = linemap_position_for_column (line_table, 10);
4050 location_t start = linemap_position_for_column (line_table, 19);
4051 location_t finish = linemap_position_for_column (line_table, 30);
4052 rich_location richloc (line_table, equals);
4053 location_t field = make_location (start, start, finish);
4054 richloc.add_range (field);
4055 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4056 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4057 /* The replacement range is indicated in the annotation line,
4058 so it shouldn't be indicated via an additional underline. */
4059 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4060 "_foo = \xcf\x80"
4061 "_bar.\xf0\x9f\x98\x82"
4062 "_field\xcf\x80"
4063 ";\n"
4064 " ^ ~~~~~~~~~\n"
4065 " m_\xf0\x9f\x98\x82"
4066 "_field\xcf\x80\n",
4067 pp_formatted_text (dc.printer));
4070 /* Verify that we can use ad-hoc locations when adding fixits to a
4071 rich_location. */
4073 static void
4074 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4076 /* Generate a range that's too long to be packed, so must
4077 be stored as an ad-hoc location (given the defaults
4078 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4079 const location_t c12 = linemap_position_for_column (line_table, 12);
4080 const location_t c52 = linemap_position_for_column (line_table, 52);
4081 const location_t loc = make_location (c12, c12, c52);
4083 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4084 return;
4086 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4088 /* Insert. */
4090 rich_location richloc (line_table, loc);
4091 richloc.add_fixit_insert_before (loc, "test");
4092 /* It should not have been discarded by the validator. */
4093 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4095 test_diagnostic_context dc;
4096 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4097 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4098 "_foo = \xcf\x80"
4099 "_bar.\xf0\x9f\x98\x82"
4100 "_field\xcf\x80"
4101 ";\n"
4102 " ^~~~~~~~~~~~~~~~ \n"
4103 " test\n",
4104 pp_formatted_text (dc.printer));
4107 /* Remove. */
4109 rich_location richloc (line_table, loc);
4110 source_range range = source_range::from_locations (loc, c52);
4111 richloc.add_fixit_remove (range);
4112 /* It should not have been discarded by the validator. */
4113 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4115 test_diagnostic_context dc;
4116 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4117 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4118 "_foo = \xcf\x80"
4119 "_bar.\xf0\x9f\x98\x82"
4120 "_field\xcf\x80"
4121 ";\n"
4122 " ^~~~~~~~~~~~~~~~ \n"
4123 " -------------------------------------\n",
4124 pp_formatted_text (dc.printer));
4127 /* Replace. */
4129 rich_location richloc (line_table, loc);
4130 source_range range = source_range::from_locations (loc, c52);
4131 richloc.add_fixit_replace (range, "test");
4132 /* It should not have been discarded by the validator. */
4133 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4135 test_diagnostic_context dc;
4136 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4137 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4138 "_foo = \xcf\x80"
4139 "_bar.\xf0\x9f\x98\x82"
4140 "_field\xcf\x80"
4141 ";\n"
4142 " ^~~~~~~~~~~~~~~~ \n"
4143 " test\n",
4144 pp_formatted_text (dc.printer));
4148 /* Test of consolidating insertions at the same location. */
4150 static void
4151 test_one_liner_many_fixits_1_utf8 ()
4153 test_diagnostic_context dc;
4154 location_t equals = linemap_position_for_column (line_table, 10);
4155 rich_location richloc (line_table, equals);
4156 for (int i = 0; i < 19; i++)
4157 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4158 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4159 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4160 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4161 "_foo = \xcf\x80"
4162 "_bar.\xf0\x9f\x98\x82"
4163 "_field\xcf\x80"
4164 ";\n"
4165 " ^\n"
4166 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4167 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4168 pp_formatted_text (dc.printer));
4171 /* Ensure that we can add an arbitrary number of fix-it hints to a
4172 rich_location, even if they are not consolidated. */
4174 static void
4175 test_one_liner_many_fixits_2_utf8 ()
4177 test_diagnostic_context dc;
4178 location_t equals = linemap_position_for_column (line_table, 10);
4179 rich_location richloc (line_table, equals);
4180 const int nlocs = 19;
4181 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4182 34, 36, 38, 40, 42, 44};
4183 for (int i = 0; i != nlocs; ++i)
4185 location_t loc = linemap_position_for_column (line_table, locs[i]);
4186 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4189 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4190 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4191 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4192 "_foo = \xcf\x80"
4193 "_bar.\xf0\x9f\x98\x82"
4194 "_field\xcf\x80"
4195 ";\n"
4196 " ^\n"
4197 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4198 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4199 pp_formatted_text (dc.printer));
4202 /* Test of labeling the ranges within a rich_location. */
4204 static void
4205 test_one_liner_labels_utf8 ()
4207 location_t foo
4208 = make_location (linemap_position_for_column (line_table, 1),
4209 linemap_position_for_column (line_table, 1),
4210 linemap_position_for_column (line_table, 8));
4211 location_t bar
4212 = make_location (linemap_position_for_column (line_table, 12),
4213 linemap_position_for_column (line_table, 12),
4214 linemap_position_for_column (line_table, 17));
4215 location_t field
4216 = make_location (linemap_position_for_column (line_table, 19),
4217 linemap_position_for_column (line_table, 19),
4218 linemap_position_for_column (line_table, 30));
4220 /* Example where all the labels fit on one line. */
4222 /* These three labels contain multibyte characters such that their byte
4223 lengths are respectively (12, 10, 18), but their display widths are only
4224 (6, 5, 9). All three fit on the line when considering the display
4225 widths, but not when considering the byte widths, so verify that we do
4226 indeed put them all on one line. */
4227 text_range_label label0
4228 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4229 text_range_label label1
4230 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4231 text_range_label label2
4232 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4233 "\xcf\x80");
4234 gcc_rich_location richloc (foo, &label0);
4235 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4236 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4239 test_diagnostic_context dc;
4240 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4241 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4242 "_foo = \xcf\x80"
4243 "_bar.\xf0\x9f\x98\x82"
4244 "_field\xcf\x80"
4245 ";\n"
4246 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4247 " | | |\n"
4248 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4249 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4250 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4251 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4252 pp_formatted_text (dc.printer));
4257 /* Example where the labels need extra lines. */
4259 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4260 text_range_label label1 ("label 1\xcf\x80");
4261 text_range_label label2 ("label 2\xcf\x80");
4262 gcc_rich_location richloc (foo, &label0);
4263 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4264 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4266 test_diagnostic_context dc;
4267 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4269 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4270 "_foo = \xcf\x80"
4271 "_bar.\xf0\x9f\x98\x82"
4272 "_field\xcf\x80"
4273 ";\n"
4274 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4275 " | | |\n"
4276 " | | label 2\xcf\x80\n"
4277 " | label 1\xcf\x80\n"
4278 " label 0\xf0\x9f\x98\x82\n",
4279 pp_formatted_text (dc.printer));
4282 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4283 but label 1 just touches label 2. */
4285 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4286 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4287 text_range_label label2 ("c");
4288 gcc_rich_location richloc (foo, &label0);
4289 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4290 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4292 test_diagnostic_context dc;
4293 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4294 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4295 "_foo = \xcf\x80"
4296 "_bar.\xf0\x9f\x98\x82"
4297 "_field\xcf\x80"
4298 ";\n"
4299 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4300 " | | |\n"
4301 " | | c\n"
4302 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4303 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4304 pp_formatted_text (dc.printer));
4307 /* Example of escaping the source lines. */
4309 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4310 text_range_label label1 ("label 1\xcf\x80");
4311 text_range_label label2 ("label 2\xcf\x80");
4312 gcc_rich_location richloc (foo, &label0);
4313 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4314 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4315 richloc.set_escape_on_output (true);
4318 test_diagnostic_context dc;
4319 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
4320 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4321 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4322 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4323 " | | |\n"
4324 " | | label 2\xcf\x80\n"
4325 " | label 1\xcf\x80\n"
4326 " label 0\xf0\x9f\x98\x82\n",
4327 pp_formatted_text (dc.printer));
4330 test_diagnostic_context dc;
4331 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
4332 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4333 ASSERT_STREQ
4334 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4335 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4336 " | | |\n"
4337 " | | label 2\xcf\x80\n"
4338 " | label 1\xcf\x80\n"
4339 " label 0\xf0\x9f\x98\x82\n",
4340 pp_formatted_text (dc.printer));
4345 /* Make sure that colorization codes don't interrupt a multibyte
4346 sequence, which would corrupt it. */
4347 static void
4348 test_one_liner_colorized_utf8 ()
4350 test_diagnostic_context dc;
4351 dc.colorize_source_p = true;
4352 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4353 const location_t pi = linemap_position_for_column (line_table, 12);
4354 rich_location richloc (line_table, pi);
4355 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4357 /* In order to avoid having the test depend on exactly how the colorization
4358 was effected, just confirm there are two pi characters in the output. */
4359 const char *result = pp_formatted_text (dc.printer);
4360 const char *null_term = result + strlen (result);
4361 const char *first_pi = strstr (result, "\xcf\x80");
4362 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4363 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4366 /* Run the various one-liner tests. */
4368 static void
4369 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4371 /* Create a tempfile and write some text to it. */
4372 const char *content
4373 /* Display columns.
4374 0000000000000000000000011111111111111111111111111111112222222222222
4375 1111111122222222345678900000000123456666666677777777890123444444445 */
4376 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4377 /* 0000000000000000000001111111111111111111222222222222222222222233333
4378 1111222233334444567890122223333456789999000011112222345678999900001
4379 Byte columns. */
4380 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4381 line_table_test ltt (case_);
4383 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4385 location_t line_end = linemap_position_for_column (line_table, 31);
4387 /* Don't attempt to run the tests if column data might be unavailable. */
4388 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4389 return;
4391 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4392 ASSERT_EQ (1, LOCATION_LINE (line_end));
4393 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4395 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4396 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4397 def_policy ()));
4398 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4399 def_policy ()));
4401 test_one_liner_simple_caret_utf8 ();
4402 test_one_liner_caret_and_range_utf8 ();
4403 test_one_liner_multiple_carets_and_ranges_utf8 ();
4404 test_one_liner_fixit_insert_before_utf8 ();
4405 test_one_liner_fixit_insert_after_utf8 ();
4406 test_one_liner_fixit_remove_utf8 ();
4407 test_one_liner_fixit_replace_utf8 ();
4408 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4409 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4410 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4411 test_one_liner_many_fixits_1_utf8 ();
4412 test_one_liner_many_fixits_2_utf8 ();
4413 test_one_liner_labels_utf8 ();
4414 test_one_liner_colorized_utf8 ();
4417 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4419 static void
4420 test_add_location_if_nearby (const line_table_case &case_)
4422 /* Create a tempfile and write some text to it.
4423 ...000000000111111111122222222223333333333.
4424 ...123456789012345678901234567890123456789. */
4425 const char *content
4426 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4427 "struct different_line\n" /* line 2. */
4428 "{\n" /* line 3. */
4429 " double x;\n" /* line 4. */
4430 " double y;\n" /* line 5. */
4431 ";\n"); /* line 6. */
4432 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4433 line_table_test ltt (case_);
4435 const line_map_ordinary *ord_map
4436 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4437 tmp.get_filename (), 0));
4439 linemap_line_start (line_table, 1, 100);
4441 const location_t final_line_end
4442 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4444 /* Don't attempt to run the tests if column data might be unavailable. */
4445 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4446 return;
4448 /* Test of add_location_if_nearby on the same line as the
4449 primary location. */
4451 const location_t missing_close_brace_1_39
4452 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4453 const location_t matching_open_brace_1_18
4454 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4455 gcc_rich_location richloc (missing_close_brace_1_39);
4456 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4457 ASSERT_TRUE (added);
4458 ASSERT_EQ (2, richloc.get_num_locations ());
4459 test_diagnostic_context dc;
4460 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4461 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4462 " ~ ^\n",
4463 pp_formatted_text (dc.printer));
4466 /* Test of add_location_if_nearby on a different line to the
4467 primary location. */
4469 const location_t missing_close_brace_6_1
4470 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4471 const location_t matching_open_brace_3_1
4472 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4473 gcc_rich_location richloc (missing_close_brace_6_1);
4474 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4475 ASSERT_FALSE (added);
4476 ASSERT_EQ (1, richloc.get_num_locations ());
4480 /* Verify that we print fixits even if they only affect lines
4481 outside those covered by the ranges in the rich_location. */
4483 static void
4484 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4486 /* Create a tempfile and write some text to it.
4487 ...000000000111111111122222222223333333333.
4488 ...123456789012345678901234567890123456789. */
4489 const char *content
4490 = ("struct point { double x; double y; };\n" /* line 1. */
4491 "struct point origin = {x: 0.0,\n" /* line 2. */
4492 " y\n" /* line 3. */
4493 "\n" /* line 4. */
4494 "\n" /* line 5. */
4495 " : 0.0};\n"); /* line 6. */
4496 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4497 line_table_test ltt (case_);
4499 const line_map_ordinary *ord_map
4500 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4501 tmp.get_filename (), 0));
4503 linemap_line_start (line_table, 1, 100);
4505 const location_t final_line_end
4506 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4508 /* Don't attempt to run the tests if column data might be unavailable. */
4509 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4510 return;
4512 /* A pair of tests for modernizing the initializers to C99-style. */
4514 /* The one-liner case (line 2). */
4516 test_diagnostic_context dc;
4517 const location_t x
4518 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4519 const location_t colon
4520 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4521 rich_location richloc (line_table, colon);
4522 richloc.add_fixit_insert_before (x, ".");
4523 richloc.add_fixit_replace (colon, "=");
4524 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4525 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4526 " ^\n"
4527 " .=\n",
4528 pp_formatted_text (dc.printer));
4531 /* The multiline case. The caret for the rich_location is on line 6;
4532 verify that insertion fixit on line 3 is still printed (and that
4533 span starts are printed due to the gap between the span at line 3
4534 and that at line 6). */
4536 test_diagnostic_context dc;
4537 const location_t y
4538 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4539 const location_t colon
4540 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4541 rich_location richloc (line_table, colon);
4542 richloc.add_fixit_insert_before (y, ".");
4543 richloc.add_fixit_replace (colon, "=");
4544 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4545 ASSERT_STREQ ("FILENAME:3:24:\n"
4546 " y\n"
4547 " .\n"
4548 "FILENAME:6:25:\n"
4549 " : 0.0};\n"
4550 " ^\n"
4551 " =\n",
4552 pp_formatted_text (dc.printer));
4555 /* As above, but verify the behavior of multiple line spans
4556 with line-numbering enabled. */
4558 const location_t y
4559 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4560 const location_t colon
4561 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4562 rich_location richloc (line_table, colon);
4563 richloc.add_fixit_insert_before (y, ".");
4564 richloc.add_fixit_replace (colon, "=");
4565 test_diagnostic_context dc;
4566 dc.show_line_numbers_p = true;
4567 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4568 ASSERT_STREQ (" 3 | y\n"
4569 " | .\n"
4570 "......\n"
4571 " 6 | : 0.0};\n"
4572 " | ^\n"
4573 " | =\n",
4574 pp_formatted_text (dc.printer));
4579 /* Verify that fix-it hints are appropriately consolidated.
4581 If any fix-it hints in a rich_location involve locations beyond
4582 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4583 the fix-it as a whole, so there should be none.
4585 Otherwise, verify that consecutive "replace" and "remove" fix-its
4586 are merged, and that other fix-its remain separate. */
4588 static void
4589 test_fixit_consolidation (const line_table_case &case_)
4591 line_table_test ltt (case_);
4593 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4595 const location_t c10 = linemap_position_for_column (line_table, 10);
4596 const location_t c15 = linemap_position_for_column (line_table, 15);
4597 const location_t c16 = linemap_position_for_column (line_table, 16);
4598 const location_t c17 = linemap_position_for_column (line_table, 17);
4599 const location_t c20 = linemap_position_for_column (line_table, 20);
4600 const location_t c21 = linemap_position_for_column (line_table, 21);
4601 const location_t caret = c10;
4603 /* Insert + insert. */
4605 rich_location richloc (line_table, caret);
4606 richloc.add_fixit_insert_before (c10, "foo");
4607 richloc.add_fixit_insert_before (c15, "bar");
4609 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4610 /* Bogus column info for 2nd fixit, so no fixits. */
4611 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4612 else
4613 /* They should not have been merged. */
4614 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4617 /* Insert + replace. */
4619 rich_location richloc (line_table, caret);
4620 richloc.add_fixit_insert_before (c10, "foo");
4621 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4622 "bar");
4624 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4625 /* Bogus column info for 2nd fixit, so no fixits. */
4626 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4627 else
4628 /* They should not have been merged. */
4629 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4632 /* Replace + non-consecutive insert. */
4634 rich_location richloc (line_table, caret);
4635 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4636 "bar");
4637 richloc.add_fixit_insert_before (c17, "foo");
4639 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4640 /* Bogus column info for 2nd fixit, so no fixits. */
4641 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4642 else
4643 /* They should not have been merged. */
4644 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4647 /* Replace + non-consecutive replace. */
4649 rich_location richloc (line_table, caret);
4650 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4651 "foo");
4652 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4653 "bar");
4655 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4656 /* Bogus column info for 2nd fixit, so no fixits. */
4657 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4658 else
4659 /* They should not have been merged. */
4660 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4663 /* Replace + consecutive replace. */
4665 rich_location richloc (line_table, caret);
4666 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4667 "foo");
4668 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4669 "bar");
4671 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4672 /* Bogus column info for 2nd fixit, so no fixits. */
4673 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4674 else
4676 /* They should have been merged into a single "replace". */
4677 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4678 const fixit_hint *hint = richloc.get_fixit_hint (0);
4679 ASSERT_STREQ ("foobar", hint->get_string ());
4680 ASSERT_EQ (c10, hint->get_start_loc ());
4681 ASSERT_EQ (c21, hint->get_next_loc ());
4685 /* Replace + consecutive removal. */
4687 rich_location richloc (line_table, caret);
4688 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4689 "foo");
4690 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4692 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4693 /* Bogus column info for 2nd fixit, so no fixits. */
4694 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4695 else
4697 /* They should have been merged into a single replace, with the
4698 range extended to cover that of the removal. */
4699 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4700 const fixit_hint *hint = richloc.get_fixit_hint (0);
4701 ASSERT_STREQ ("foo", hint->get_string ());
4702 ASSERT_EQ (c10, hint->get_start_loc ());
4703 ASSERT_EQ (c21, hint->get_next_loc ());
4707 /* Consecutive removals. */
4709 rich_location richloc (line_table, caret);
4710 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4711 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4713 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4714 /* Bogus column info for 2nd fixit, so no fixits. */
4715 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4716 else
4718 /* They should have been merged into a single "replace-with-empty". */
4719 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4720 const fixit_hint *hint = richloc.get_fixit_hint (0);
4721 ASSERT_STREQ ("", hint->get_string ());
4722 ASSERT_EQ (c10, hint->get_start_loc ());
4723 ASSERT_EQ (c21, hint->get_next_loc ());
4728 /* Verify that the line_corrections machinery correctly prints
4729 overlapping fixit-hints. */
4731 static void
4732 test_overlapped_fixit_printing (const line_table_case &case_)
4734 /* Create a tempfile and write some text to it.
4735 ...000000000111111111122222222223333333333.
4736 ...123456789012345678901234567890123456789. */
4737 const char *content
4738 = (" foo *f = (foo *)ptr->field;\n");
4739 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4740 line_table_test ltt (case_);
4742 const line_map_ordinary *ord_map
4743 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4744 tmp.get_filename (), 0));
4746 linemap_line_start (line_table, 1, 100);
4748 const location_t final_line_end
4749 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4751 /* Don't attempt to run the tests if column data might be unavailable. */
4752 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4753 return;
4755 /* A test for converting a C-style cast to a C++-style cast. */
4756 const location_t open_paren
4757 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4758 const location_t close_paren
4759 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4760 const location_t expr_start
4761 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4762 const location_t expr_finish
4763 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4764 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4766 /* Various examples of fix-it hints that aren't themselves consolidated,
4767 but for which the *printing* may need consolidation. */
4769 /* Example where 3 fix-it hints are printed as one. */
4771 test_diagnostic_context dc;
4772 rich_location richloc (line_table, expr);
4773 richloc.add_fixit_replace (open_paren, "const_cast<");
4774 richloc.add_fixit_replace (close_paren, "> (");
4775 richloc.add_fixit_insert_after (")");
4777 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4778 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4779 " ^~~~~~~~~~\n"
4780 " -----------------\n"
4781 " const_cast<foo *> (ptr->field)\n",
4782 pp_formatted_text (dc.printer));
4784 /* Unit-test the line_corrections machinery. */
4785 char_display_policy policy (make_policy (dc, richloc));
4786 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4787 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4788 ASSERT_EQ (column_range (12, 12),
4789 get_affected_range (policy, hint_0, CU_BYTES));
4790 ASSERT_EQ (column_range (12, 12),
4791 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4792 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4793 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4794 ASSERT_EQ (column_range (18, 18),
4795 get_affected_range (policy, hint_1, CU_BYTES));
4796 ASSERT_EQ (column_range (18, 18),
4797 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4798 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4799 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4800 ASSERT_EQ (column_range (29, 28),
4801 get_affected_range (policy, hint_2, CU_BYTES));
4802 ASSERT_EQ (column_range (29, 28),
4803 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4804 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2));
4806 /* Add each hint in turn to a line_corrections instance,
4807 and verify that they are consolidated into one correction instance
4808 as expected. */
4809 line_corrections lc (policy, tmp.get_filename (), 1);
4811 /* The first replace hint by itself. */
4812 lc.add_hint (hint_0);
4813 ASSERT_EQ (1, lc.m_corrections.length ());
4814 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4815 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4816 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4817 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4819 /* After the second replacement hint, they are printed together
4820 as a replacement (along with the text between them). */
4821 lc.add_hint (hint_1);
4822 ASSERT_EQ (1, lc.m_corrections.length ());
4823 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4824 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4825 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4826 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4828 /* After the final insertion hint, they are all printed together
4829 as a replacement (along with the text between them). */
4830 lc.add_hint (hint_2);
4831 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4832 lc.m_corrections[0]->m_text);
4833 ASSERT_EQ (1, lc.m_corrections.length ());
4834 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4835 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4836 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4839 /* Example where two are consolidated during printing. */
4841 test_diagnostic_context dc;
4842 rich_location richloc (line_table, expr);
4843 richloc.add_fixit_replace (open_paren, "CAST (");
4844 richloc.add_fixit_replace (close_paren, ") (");
4845 richloc.add_fixit_insert_after (")");
4847 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4848 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4849 " ^~~~~~~~~~\n"
4850 " -\n"
4851 " CAST (-\n"
4852 " ) ( )\n",
4853 pp_formatted_text (dc.printer));
4856 /* Example where none are consolidated during printing. */
4858 test_diagnostic_context dc;
4859 rich_location richloc (line_table, expr);
4860 richloc.add_fixit_replace (open_paren, "CST (");
4861 richloc.add_fixit_replace (close_paren, ") (");
4862 richloc.add_fixit_insert_after (")");
4864 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4865 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4866 " ^~~~~~~~~~\n"
4867 " -\n"
4868 " CST ( -\n"
4869 " ) ( )\n",
4870 pp_formatted_text (dc.printer));
4873 /* Example of deletion fix-it hints. */
4875 test_diagnostic_context dc;
4876 rich_location richloc (line_table, expr);
4877 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4878 source_range victim = {open_paren, close_paren};
4879 richloc.add_fixit_remove (victim);
4881 /* This case is actually handled by fixit-consolidation,
4882 rather than by line_corrections. */
4883 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4885 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4886 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4887 " ^~~~~~~~~~\n"
4888 " -------\n"
4889 " (bar *)\n",
4890 pp_formatted_text (dc.printer));
4893 /* Example of deletion fix-it hints that would overlap. */
4895 test_diagnostic_context dc;
4896 rich_location richloc (line_table, expr);
4897 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4898 source_range victim = {expr_start, expr_finish};
4899 richloc.add_fixit_remove (victim);
4901 /* These fixits are not consolidated. */
4902 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4904 /* But the corrections are. */
4905 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4906 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4907 " ^~~~~~~~~~\n"
4908 " -----------------\n"
4909 " (longer *)(foo *)\n",
4910 pp_formatted_text (dc.printer));
4913 /* Example of insertion fix-it hints that would overlap. */
4915 test_diagnostic_context dc;
4916 rich_location richloc (line_table, expr);
4917 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4918 richloc.add_fixit_insert_after (close_paren, "TEST");
4920 /* The first insertion is long enough that if printed naively,
4921 it would overlap with the second.
4922 Verify that they are printed as a single replacement. */
4923 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4924 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4925 " ^~~~~~~~~~\n"
4926 " -------\n"
4927 " LONGER THAN THE CAST(foo *)TEST\n",
4928 pp_formatted_text (dc.printer));
4932 /* Multibyte-aware version of preceding tests. See comments above
4933 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4934 characters here. */
4936 static void
4937 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4939 /* Create a tempfile and write some text to it. */
4941 const char *content
4942 /* Display columns.
4943 00000000000000000000000111111111111111111111111222222222222222223
4944 12344444444555555556789012344444444555555556789012345678999999990 */
4945 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4946 /* 00000000000000000000011111111111111111111112222222222333333333333
4947 12344445555666677778901234566667777888899990123456789012333344445
4948 Byte columns. */
4950 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4951 line_table_test ltt (case_);
4953 const line_map_ordinary *ord_map
4954 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4955 tmp.get_filename (), 0));
4957 linemap_line_start (line_table, 1, 100);
4959 const location_t final_line_end
4960 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4962 /* Don't attempt to run the tests if column data might be unavailable. */
4963 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4964 return;
4966 /* A test for converting a C-style cast to a C++-style cast. */
4967 const location_t open_paren
4968 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4969 const location_t close_paren
4970 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4971 const location_t expr_start
4972 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4973 const location_t expr_finish
4974 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4975 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4977 /* Various examples of fix-it hints that aren't themselves consolidated,
4978 but for which the *printing* may need consolidation. */
4980 /* Example where 3 fix-it hints are printed as one. */
4982 test_diagnostic_context dc;
4983 rich_location richloc (line_table, expr);
4984 richloc.add_fixit_replace (open_paren, "const_cast<");
4985 richloc.add_fixit_replace (close_paren, "> (");
4986 richloc.add_fixit_insert_after (")");
4988 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4989 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4990 " *f = (f\xf0\x9f\x98\x82"
4991 " *)ptr->field\xcf\x80"
4992 ";\n"
4993 " ^~~~~~~~~~~\n"
4994 " ------------------\n"
4995 " const_cast<f\xf0\x9f\x98\x82"
4996 " *> (ptr->field\xcf\x80"
4997 ")\n",
4998 pp_formatted_text (dc.printer));
5000 /* Unit-test the line_corrections machinery. */
5001 char_display_policy policy (make_policy (dc, richloc));
5002 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5003 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5004 ASSERT_EQ (column_range (14, 14),
5005 get_affected_range (policy, hint_0, CU_BYTES));
5006 ASSERT_EQ (column_range (12, 12),
5007 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
5008 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
5009 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5010 ASSERT_EQ (column_range (22, 22),
5011 get_affected_range (policy, hint_1, CU_BYTES));
5012 ASSERT_EQ (column_range (18, 18),
5013 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
5014 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
5015 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5016 ASSERT_EQ (column_range (35, 34),
5017 get_affected_range (policy, hint_2, CU_BYTES));
5018 ASSERT_EQ (column_range (30, 29),
5019 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
5020 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2));
5022 /* Add each hint in turn to a line_corrections instance,
5023 and verify that they are consolidated into one correction instance
5024 as expected. */
5025 line_corrections lc (policy, tmp.get_filename (), 1);
5027 /* The first replace hint by itself. */
5028 lc.add_hint (hint_0);
5029 ASSERT_EQ (1, lc.m_corrections.length ());
5030 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5031 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5032 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5033 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5035 /* After the second replacement hint, they are printed together
5036 as a replacement (along with the text between them). */
5037 lc.add_hint (hint_1);
5038 ASSERT_EQ (1, lc.m_corrections.length ());
5039 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5040 lc.m_corrections[0]->m_text);
5041 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5042 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5043 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5045 /* After the final insertion hint, they are all printed together
5046 as a replacement (along with the text between them). */
5047 lc.add_hint (hint_2);
5048 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5049 lc.m_corrections[0]->m_text);
5050 ASSERT_EQ (1, lc.m_corrections.length ());
5051 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5052 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5053 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5056 /* Example where two are consolidated during printing. */
5058 test_diagnostic_context dc;
5059 rich_location richloc (line_table, expr);
5060 richloc.add_fixit_replace (open_paren, "CAST (");
5061 richloc.add_fixit_replace (close_paren, ") (");
5062 richloc.add_fixit_insert_after (")");
5064 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5065 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5066 " *f = (f\xf0\x9f\x98\x82"
5067 " *)ptr->field\xcf\x80"
5068 ";\n"
5069 " ^~~~~~~~~~~\n"
5070 " -\n"
5071 " CAST (-\n"
5072 " ) ( )\n",
5073 pp_formatted_text (dc.printer));
5076 /* Example where none are consolidated during printing. */
5078 test_diagnostic_context dc;
5079 rich_location richloc (line_table, expr);
5080 richloc.add_fixit_replace (open_paren, "CST (");
5081 richloc.add_fixit_replace (close_paren, ") (");
5082 richloc.add_fixit_insert_after (")");
5084 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5085 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5086 " *f = (f\xf0\x9f\x98\x82"
5087 " *)ptr->field\xcf\x80"
5088 ";\n"
5089 " ^~~~~~~~~~~\n"
5090 " -\n"
5091 " CST ( -\n"
5092 " ) ( )\n",
5093 pp_formatted_text (dc.printer));
5096 /* Example of deletion fix-it hints. */
5098 test_diagnostic_context dc;
5099 rich_location richloc (line_table, expr);
5100 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5101 source_range victim = {open_paren, close_paren};
5102 richloc.add_fixit_remove (victim);
5104 /* This case is actually handled by fixit-consolidation,
5105 rather than by line_corrections. */
5106 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5108 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5109 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5110 " *f = (f\xf0\x9f\x98\x82"
5111 " *)ptr->field\xcf\x80"
5112 ";\n"
5113 " ^~~~~~~~~~~\n"
5114 " -------\n"
5115 " (bar\xf0\x9f\x98\x82"
5116 " *)\n",
5117 pp_formatted_text (dc.printer));
5120 /* Example of deletion fix-it hints that would overlap. */
5122 test_diagnostic_context dc;
5123 rich_location richloc (line_table, expr);
5124 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5125 source_range victim = {expr_start, expr_finish};
5126 richloc.add_fixit_remove (victim);
5128 /* These fixits are not consolidated. */
5129 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5131 /* But the corrections are. */
5132 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5133 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5134 " *f = (f\xf0\x9f\x98\x82"
5135 " *)ptr->field\xcf\x80"
5136 ";\n"
5137 " ^~~~~~~~~~~\n"
5138 " ------------------\n"
5139 " (long\xf0\x9f\x98\x82"
5140 " *)(f\xf0\x9f\x98\x82"
5141 " *)\n",
5142 pp_formatted_text (dc.printer));
5145 /* Example of insertion fix-it hints that would overlap. */
5147 test_diagnostic_context dc;
5148 rich_location richloc (line_table, expr);
5149 richloc.add_fixit_insert_before
5150 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5151 richloc.add_fixit_insert_after (close_paren, "TEST");
5153 /* The first insertion is long enough that if printed naively,
5154 it would overlap with the second.
5155 Verify that they are printed as a single replacement. */
5156 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5157 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5158 " *f = (f\xf0\x9f\x98\x82"
5159 " *)ptr->field\xcf\x80"
5160 ";\n"
5161 " ^~~~~~~~~~~\n"
5162 " -------\n"
5163 " L\xf0\x9f\x98\x82"
5164 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5165 " *)TEST\n",
5166 pp_formatted_text (dc.printer));
5170 /* Verify that the line_corrections machinery correctly prints
5171 overlapping fixit-hints that have been added in the wrong
5172 order.
5173 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5175 static void
5176 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5178 /* Create a tempfile and write some text to it.
5179 ...000000000111111111122222222223333333333.
5180 ...123456789012345678901234567890123456789. */
5181 const char *content
5182 = ("int a5[][0][0] = { 1, 2 };\n");
5183 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5184 line_table_test ltt (case_);
5186 const line_map_ordinary *ord_map
5187 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5188 tmp.get_filename (), 0));
5190 linemap_line_start (line_table, 1, 100);
5192 const location_t final_line_end
5193 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5195 /* Don't attempt to run the tests if column data might be unavailable. */
5196 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5197 return;
5199 const location_t col_1
5200 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5201 const location_t col_20
5202 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5203 const location_t col_21
5204 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5205 const location_t col_23
5206 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5207 const location_t col_25
5208 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5210 /* Two insertions, in the wrong order. */
5212 test_diagnostic_context dc;
5214 rich_location richloc (line_table, col_20);
5215 richloc.add_fixit_insert_before (col_23, "{");
5216 richloc.add_fixit_insert_before (col_21, "}");
5218 /* These fixits should be accepted; they can't be consolidated. */
5219 char_display_policy policy (make_policy (dc, richloc));
5220 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5221 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5222 ASSERT_EQ (column_range (23, 22),
5223 get_affected_range (policy, hint_0, CU_BYTES));
5224 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0));
5225 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5226 ASSERT_EQ (column_range (21, 20),
5227 get_affected_range (policy, hint_1, CU_BYTES));
5228 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1));
5230 /* Verify that they're printed correctly. */
5231 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5232 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5233 " ^\n"
5234 " } {\n",
5235 pp_formatted_text (dc.printer));
5238 /* Various overlapping insertions, some occurring "out of order"
5239 (reproducing the fix-it hints from PR c/81405). */
5241 test_diagnostic_context dc;
5242 rich_location richloc (line_table, col_20);
5244 richloc.add_fixit_insert_before (col_20, "{{");
5245 richloc.add_fixit_insert_before (col_21, "}}");
5246 richloc.add_fixit_insert_before (col_23, "{");
5247 richloc.add_fixit_insert_before (col_21, "}");
5248 richloc.add_fixit_insert_before (col_23, "{{");
5249 richloc.add_fixit_insert_before (col_25, "}");
5250 richloc.add_fixit_insert_before (col_21, "}");
5251 richloc.add_fixit_insert_before (col_1, "{");
5252 richloc.add_fixit_insert_before (col_25, "}");
5253 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5254 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5255 " ^\n"
5256 " { -----\n"
5257 " {{1}}}}, {{{2 }}\n",
5258 pp_formatted_text (dc.printer));
5262 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5264 static void
5265 test_fixit_insert_containing_newline (const line_table_case &case_)
5267 /* Create a tempfile and write some text to it.
5268 .........................0000000001111111.
5269 .........................1234567890123456. */
5270 const char *old_content = (" case 'a':\n" /* line 1. */
5271 " x = a;\n" /* line 2. */
5272 " case 'b':\n" /* line 3. */
5273 " x = b;\n");/* line 4. */
5275 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5276 line_table_test ltt (case_);
5277 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5279 location_t case_start = linemap_position_for_column (line_table, 5);
5280 location_t case_finish = linemap_position_for_column (line_table, 13);
5281 location_t case_loc = make_location (case_start, case_start, case_finish);
5282 location_t line_start = linemap_position_for_column (line_table, 1);
5284 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5285 return;
5287 /* Add a "break;" on a line by itself before line 3 i.e. before
5288 column 1 of line 3. */
5290 rich_location richloc (line_table, case_loc);
5291 richloc.add_fixit_insert_before (line_start, " break;\n");
5293 /* Without line numbers. */
5295 test_diagnostic_context dc;
5296 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5297 ASSERT_STREQ (" x = a;\n"
5298 "+ break;\n"
5299 " case 'b':\n"
5300 " ^~~~~~~~~\n",
5301 pp_formatted_text (dc.printer));
5304 /* With line numbers. */
5306 test_diagnostic_context dc;
5307 dc.show_line_numbers_p = true;
5308 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5309 ASSERT_STREQ (" 2 | x = a;\n"
5310 " +++ |+ break;\n"
5311 " 3 | case 'b':\n"
5312 " | ^~~~~~~~~\n",
5313 pp_formatted_text (dc.printer));
5317 /* Verify that attempts to add text with a newline fail when the
5318 insertion point is *not* at the start of a line. */
5320 rich_location richloc (line_table, case_loc);
5321 richloc.add_fixit_insert_before (case_start, "break;\n");
5322 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5323 test_diagnostic_context dc;
5324 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5325 ASSERT_STREQ (" case 'b':\n"
5326 " ^~~~~~~~~\n",
5327 pp_formatted_text (dc.printer));
5331 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5332 of the file, where the fix-it is printed in a different line-span
5333 to the primary range of the diagnostic. */
5335 static void
5336 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5338 /* Create a tempfile and write some text to it.
5339 .........................0000000001111111.
5340 .........................1234567890123456. */
5341 const char *old_content = ("test (int ch)\n" /* line 1. */
5342 "{\n" /* line 2. */
5343 " putchar (ch);\n" /* line 3. */
5344 "}\n"); /* line 4. */
5346 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5347 line_table_test ltt (case_);
5349 const line_map_ordinary *ord_map = linemap_check_ordinary
5350 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5351 linemap_line_start (line_table, 1, 100);
5353 /* The primary range is the "putchar" token. */
5354 location_t putchar_start
5355 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5356 location_t putchar_finish
5357 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5358 location_t putchar_loc
5359 = make_location (putchar_start, putchar_start, putchar_finish);
5360 rich_location richloc (line_table, putchar_loc);
5362 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5363 location_t file_start
5364 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5365 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5367 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5368 return;
5371 test_diagnostic_context dc;
5372 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5373 ASSERT_STREQ ("FILENAME:1:1:\n"
5374 "+#include <stdio.h>\n"
5375 " test (int ch)\n"
5376 "FILENAME:3:2:\n"
5377 " putchar (ch);\n"
5378 " ^~~~~~~\n",
5379 pp_formatted_text (dc.printer));
5382 /* With line-numbering, the line spans are close enough to be
5383 consolidated, since it makes little sense to skip line 2. */
5385 test_diagnostic_context dc;
5386 dc.show_line_numbers_p = true;
5387 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5388 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5389 " 1 | test (int ch)\n"
5390 " 2 | {\n"
5391 " 3 | putchar (ch);\n"
5392 " | ^~~~~~~\n",
5393 pp_formatted_text (dc.printer));
5397 /* Replacement fix-it hint containing a newline.
5398 This will fail, as newlines are only supported when inserting at the
5399 beginning of a line. */
5401 static void
5402 test_fixit_replace_containing_newline (const line_table_case &case_)
5404 /* Create a tempfile and write some text to it.
5405 .........................0000000001111.
5406 .........................1234567890123. */
5407 const char *old_content = "foo = bar ();\n";
5409 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5410 line_table_test ltt (case_);
5411 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5413 /* Replace the " = " with "\n = ", as if we were reformatting an
5414 overly long line. */
5415 location_t start = linemap_position_for_column (line_table, 4);
5416 location_t finish = linemap_position_for_column (line_table, 6);
5417 location_t loc = linemap_position_for_column (line_table, 13);
5418 rich_location richloc (line_table, loc);
5419 source_range range = source_range::from_locations (start, finish);
5420 richloc.add_fixit_replace (range, "\n =");
5422 /* Arbitrary newlines are not yet supported within fix-it hints, so
5423 the fix-it should not be displayed. */
5424 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5426 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5427 return;
5429 test_diagnostic_context dc;
5430 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5431 ASSERT_STREQ (" foo = bar ();\n"
5432 " ^\n",
5433 pp_formatted_text (dc.printer));
5436 /* Fix-it hint, attempting to delete a newline.
5437 This will fail, as we currently only support fix-it hints that
5438 affect one line at a time. */
5440 static void
5441 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5443 /* Create a tempfile and write some text to it.
5444 ..........................0000000001111.
5445 ..........................1234567890123. */
5446 const char *old_content = ("foo = bar (\n"
5447 " );\n");
5449 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5450 line_table_test ltt (case_);
5451 const line_map_ordinary *ord_map = linemap_check_ordinary
5452 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5453 linemap_line_start (line_table, 1, 100);
5455 /* Attempt to delete the " (\n...)". */
5456 location_t start
5457 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5458 location_t caret
5459 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5460 location_t finish
5461 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5462 location_t loc = make_location (caret, start, finish);
5463 rich_location richloc (line_table, loc);
5464 richloc. add_fixit_remove ();
5466 /* Fix-it hints that affect more than one line are not yet supported, so
5467 the fix-it should not be displayed. */
5468 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5470 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5471 return;
5473 test_diagnostic_context dc;
5474 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5475 ASSERT_STREQ (" foo = bar (\n"
5476 " ~^\n"
5477 " );\n"
5478 " ~ \n",
5479 pp_formatted_text (dc.printer));
5482 static void
5483 test_tab_expansion (const line_table_case &case_)
5485 /* Create a tempfile and write some text to it. This example uses a tabstop
5486 of 8, as the column numbers attempt to indicate:
5488 .....................000.01111111111.22222333333 display
5489 .....................123.90123456789.56789012345 columns */
5490 const char *content = " \t This: `\t' is a tab.\n";
5491 /* ....................000 00000011111 11111222222 byte
5492 ....................123 45678901234 56789012345 columns */
5494 const int tabstop = 8;
5495 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5496 const int first_non_ws_byte_col = 7;
5497 const int right_quote_byte_col = 15;
5498 const int last_byte_col = 25;
5499 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5501 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5502 line_table_test ltt (case_);
5503 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5505 /* Don't attempt to run the tests if column data might be unavailable. */
5506 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5507 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5508 return;
5510 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5511 into 11 spaces. Recall that print_line() also puts one space before
5512 everything too. */
5514 test_diagnostic_context dc;
5515 dc.tabstop = tabstop;
5516 rich_location richloc (line_table,
5517 linemap_position_for_column (line_table,
5518 first_non_ws_byte_col));
5519 layout test_layout (&dc, &richloc, DK_ERROR);
5520 test_layout.print_line (1);
5521 ASSERT_STREQ (" This: ` ' is a tab.\n"
5522 " ^\n",
5523 pp_formatted_text (dc.printer));
5526 /* Confirm the display width was tracked correctly across the internal tab
5527 as well. */
5529 test_diagnostic_context dc;
5530 dc.tabstop = tabstop;
5531 rich_location richloc (line_table,
5532 linemap_position_for_column (line_table,
5533 right_quote_byte_col));
5534 layout test_layout (&dc, &richloc, DK_ERROR);
5535 test_layout.print_line (1);
5536 ASSERT_STREQ (" This: ` ' is a tab.\n"
5537 " ^\n",
5538 pp_formatted_text (dc.printer));
5542 /* Verify that the escaping machinery can cope with a variety of different
5543 invalid bytes. */
5545 static void
5546 test_escaping_bytes_1 (const line_table_case &case_)
5548 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5549 const size_t sz = sizeof (content);
5550 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5551 line_table_test ltt (case_);
5552 const line_map_ordinary *ord_map = linemap_check_ordinary
5553 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5554 linemap_line_start (line_table, 1, 100);
5556 location_t finish
5557 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5558 strlen (content));
5560 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5561 return;
5563 /* Locations of the NUL and \v bytes. */
5564 location_t nul_loc
5565 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5566 location_t v_loc
5567 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5568 gcc_rich_location richloc (nul_loc);
5569 richloc.add_range (v_loc);
5572 test_diagnostic_context dc;
5573 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5574 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5575 " ^ ~\n",
5576 pp_formatted_text (dc.printer));
5578 richloc.set_escape_on_output (true);
5580 test_diagnostic_context dc;
5581 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5582 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5583 ASSERT_STREQ
5584 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5585 " ^~~~~~~~ ~~~~~~~~\n",
5586 pp_formatted_text (dc.printer));
5589 test_diagnostic_context dc;
5590 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5591 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5592 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5593 " ^~~~ ~~~~\n",
5594 pp_formatted_text (dc.printer));
5598 /* As above, but verify that we handle the initial byte of a line
5599 correctly. */
5601 static void
5602 test_escaping_bytes_2 (const line_table_case &case_)
5604 const char content[] = "\0after\n";
5605 const size_t sz = sizeof (content);
5606 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5607 line_table_test ltt (case_);
5608 const line_map_ordinary *ord_map = linemap_check_ordinary
5609 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5610 linemap_line_start (line_table, 1, 100);
5612 location_t finish
5613 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5614 strlen (content));
5616 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5617 return;
5619 /* Location of the NUL byte. */
5620 location_t nul_loc
5621 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5622 gcc_rich_location richloc (nul_loc);
5625 test_diagnostic_context dc;
5626 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5627 ASSERT_STREQ (" after\n"
5628 " ^\n",
5629 pp_formatted_text (dc.printer));
5631 richloc.set_escape_on_output (true);
5633 test_diagnostic_context dc;
5634 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5635 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5636 ASSERT_STREQ (" <U+0000>after\n"
5637 " ^~~~~~~~\n",
5638 pp_formatted_text (dc.printer));
5641 test_diagnostic_context dc;
5642 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5643 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5644 ASSERT_STREQ (" <00>after\n"
5645 " ^~~~\n",
5646 pp_formatted_text (dc.printer));
5650 /* Verify that line numbers are correctly printed for the case of
5651 a multiline range in which the width of the line numbers changes
5652 (e.g. from "9" to "10"). */
5654 static void
5655 test_line_numbers_multiline_range ()
5657 /* Create a tempfile and write some text to it. */
5658 pretty_printer pp;
5659 for (int i = 0; i < 20; i++)
5660 /* .........0000000001111111.
5661 .............1234567890123456. */
5662 pp_printf (&pp, "this is line %i\n", i + 1);
5663 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5664 line_table_test ltt;
5666 const line_map_ordinary *ord_map = linemap_check_ordinary
5667 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5668 linemap_line_start (line_table, 1, 100);
5670 /* Create a multi-line location, starting at the "line" of line 9, with
5671 a caret on the "is" of line 10, finishing on the "this" line 11. */
5673 location_t start
5674 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5675 location_t caret
5676 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5677 location_t finish
5678 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5679 location_t loc = make_location (caret, start, finish);
5681 test_diagnostic_context dc;
5682 dc.show_line_numbers_p = true;
5683 dc.min_margin_width = 0;
5684 gcc_rich_location richloc (loc);
5685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5686 ASSERT_STREQ (" 9 | this is line 9\n"
5687 " | ~~~~~~\n"
5688 "10 | this is line 10\n"
5689 " | ~~~~~^~~~~~~~~~\n"
5690 "11 | this is line 11\n"
5691 " | ~~~~ \n",
5692 pp_formatted_text (dc.printer));
5695 /* Run all of the selftests within this file. */
5697 void
5698 diagnostic_show_locus_cc_tests ()
5700 test_line_span ();
5702 test_layout_range_for_single_point ();
5703 test_layout_range_for_single_line ();
5704 test_layout_range_for_multiple_lines ();
5706 test_display_widths ();
5708 for_each_line_table_case (test_layout_x_offset_display_utf8);
5709 for_each_line_table_case (test_layout_x_offset_display_tab);
5711 test_get_line_bytes_without_trailing_whitespace ();
5713 test_diagnostic_show_locus_unknown_location ();
5715 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5716 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5717 for_each_line_table_case (test_add_location_if_nearby);
5718 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5719 for_each_line_table_case (test_fixit_consolidation);
5720 for_each_line_table_case (test_overlapped_fixit_printing);
5721 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5722 for_each_line_table_case (test_overlapped_fixit_printing_2);
5723 for_each_line_table_case (test_fixit_insert_containing_newline);
5724 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5725 for_each_line_table_case (test_fixit_replace_containing_newline);
5726 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5727 for_each_line_table_case (test_tab_expansion);
5728 for_each_line_table_case (test_escaping_bytes_1);
5729 for_each_line_table_case (test_escaping_bytes_2);
5731 test_line_numbers_multiline_range ();
5734 } // namespace selftest
5736 #endif /* #if CHECKING_P */
5738 #if __GNUC__ >= 10
5739 # pragma GCC diagnostic pop
5740 #endif