hppa: Fix pr110279-1.c on hppa
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob55e7166b9448c7c8c93cfd4204e75cd2460ee056
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2023 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 #include "cpplib.h"
35 #ifdef HAVE_TERMIOS_H
36 # include <termios.h>
37 #endif
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
41 #endif
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
45 #if __GNUC__ >= 10
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
48 #endif
50 /* Classes for rendering source code and diagnostics, within an
51 anonymous namespace.
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
55 namespace {
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
59 this point. */
61 struct point_state
63 int range_idx;
64 bool draw_caret_p;
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
70 - normal text
71 - range 0 (the "primary location")
72 - range 1
73 - range 2
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
83 class colorizer
85 public:
86 colorizer (pretty_printer *pp,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
90 void set_range (int range_idx)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
97 set_state (0);
98 else
99 set_state (range_idx);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
105 private:
106 void set_state (int state);
107 void begin_state (int state);
108 void finish_state (int state);
109 const char *get_color_by_name (const char *);
111 private:
112 static const int STATE_NORMAL_TEXT = -1;
113 static const int STATE_FIXIT_INSERT = -2;
114 static const int STATE_FIXIT_DELETE = -3;
116 pretty_printer *m_pp;
117 diagnostic_t m_diagnostic_kind;
118 int m_current_state;
119 const char *m_range1;
120 const char *m_range2;
121 const char *m_fixit_insert;
122 const char *m_fixit_delete;
123 const char *m_stop_color;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
162 enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col : public expanded_location
177 public:
178 exploc_with_display_col (file_cache &fc,
179 const expanded_location &exploc,
180 const cpp_char_column_policy &policy,
181 enum location_aspect aspect)
182 : expanded_location (exploc),
183 m_display_col (location_compute_display_column (fc, exploc, policy))
185 if (exploc.column > 0)
187 /* m_display_col is now the final column of the byte.
188 If escaping has happened, we may want the first column instead. */
189 if (aspect != LOCATION_ASPECT_FINISH)
191 expanded_location prev_exploc (exploc);
192 prev_exploc.column--;
193 int prev_display_col
194 = (location_compute_display_column (fc, prev_exploc, policy));
195 m_display_col = prev_display_col + 1;
200 int m_display_col;
204 /* A point within a layout_range; similar to an exploc_with_display_col,
205 but after filtering on file. */
207 class layout_point
209 public:
210 layout_point (const exploc_with_display_col &exploc)
211 : m_line (exploc.line)
213 m_columns[CU_BYTES] = exploc.column;
214 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
217 linenum_type m_line;
218 int m_columns[CU_NUM_UNITS];
221 /* A class for use by "class layout" below: a filtered location_range. */
223 class layout_range
225 public:
226 layout_range (const exploc_with_display_col &start_exploc,
227 const exploc_with_display_col &finish_exploc,
228 enum range_display_kind range_display_kind,
229 const exploc_with_display_col &caret_exploc,
230 unsigned original_idx,
231 const range_label *label);
233 bool contains_point (linenum_type row, int column,
234 enum column_unit col_unit) const;
235 bool intersects_line_p (linenum_type row) const;
237 layout_point m_start;
238 layout_point m_finish;
239 enum range_display_kind m_range_display_kind;
240 layout_point m_caret;
241 unsigned m_original_idx;
242 const range_label *m_label;
245 /* A struct for use by layout::print_source_line for telling
246 layout::print_annotation_line the extents of the source line that
247 it printed, so that underlines can be clipped appropriately. Units
248 are 1-based display columns. */
250 struct line_bounds
252 int m_first_non_ws_disp_col;
253 int m_last_non_ws_disp_col;
255 line_bounds ()
257 m_first_non_ws_disp_col = INT_MAX;
258 m_last_non_ws_disp_col = 0;
262 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
263 or "line 23"). During the layout ctor, layout::calculate_line_spans
264 splits the pertinent source lines into a list of disjoint line_span
265 instances (e.g. lines 5-10, lines 15-20, line 23). */
267 class line_span
269 public:
270 line_span (linenum_type first_line, linenum_type last_line)
271 : m_first_line (first_line), m_last_line (last_line)
273 gcc_assert (first_line <= last_line);
275 linenum_type get_first_line () const { return m_first_line; }
276 linenum_type get_last_line () const { return m_last_line; }
278 bool contains_line_p (linenum_type line) const
280 return line >= m_first_line && line <= m_last_line;
283 static int comparator (const void *p1, const void *p2)
285 const line_span *ls1 = (const line_span *)p1;
286 const line_span *ls2 = (const line_span *)p2;
287 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
288 if (first_line_cmp)
289 return first_line_cmp;
290 return compare (ls1->m_last_line, ls2->m_last_line);
293 linenum_type m_first_line;
294 linenum_type m_last_line;
297 #if CHECKING_P
299 /* Selftests for line_span. */
301 static void
302 test_line_span ()
304 line_span line_one (1, 1);
305 ASSERT_EQ (1, line_one.get_first_line ());
306 ASSERT_EQ (1, line_one.get_last_line ());
307 ASSERT_FALSE (line_one.contains_line_p (0));
308 ASSERT_TRUE (line_one.contains_line_p (1));
309 ASSERT_FALSE (line_one.contains_line_p (2));
311 line_span lines_1_to_3 (1, 3);
312 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
313 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
315 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
317 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
318 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
319 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
321 /* A linenum > 2^31. */
322 const linenum_type LARGEST_LINE = 0xffffffff;
323 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
325 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
327 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
328 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
331 #endif /* #if CHECKING_P */
333 /* A bundle of information containing how to print unicode
334 characters and bytes when quoting source code.
336 Provides a unified place to support escaping some subset
337 of characters to some format.
339 Extends char_column_policy; printing is split out to avoid
340 libcpp having to know about pretty_printer. */
342 struct char_display_policy : public cpp_char_column_policy
344 public:
345 char_display_policy (int tabstop,
346 int (*width_cb) (cppchar_t c),
347 void (*print_cb) (pretty_printer *pp,
348 const cpp_decoded_char &cp))
349 : cpp_char_column_policy (tabstop, width_cb),
350 m_print_cb (print_cb)
354 void (*m_print_cb) (pretty_printer *pp,
355 const cpp_decoded_char &cp);
358 /* A class to control the overall layout when printing a diagnostic.
360 The layout is determined within the constructor.
361 It is then printed by repeatedly calling the "print_source_line",
362 "print_annotation_line" and "print_any_fixits" methods.
364 We assume we have disjoint ranges. */
366 class layout
368 public:
369 layout (const diagnostic_context &context,
370 const rich_location &richloc,
371 diagnostic_t diagnostic_kind,
372 pretty_printer *pp);
374 bool maybe_add_location_range (const location_range *loc_range,
375 unsigned original_idx,
376 bool restrict_to_current_line_spans);
378 int get_num_line_spans () const { return m_line_spans.length (); }
379 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
381 int get_linenum_width () const { return m_linenum_width; }
382 int get_x_offset_display () const { return m_x_offset_display; }
384 void print_gap_in_line_numbering ();
385 bool print_heading_for_line_span_index_p (int line_span_idx) const;
387 expanded_location get_expanded_location (const line_span *) const;
389 void print_line (linenum_type row);
391 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
393 private:
394 bool will_show_line_p (linenum_type row) const;
395 void print_leading_fixits (linenum_type row);
396 line_bounds print_source_line (linenum_type row, const char *line,
397 int line_bytes);
398 bool should_print_annotation_line_p (linenum_type row) const;
399 void start_annotation_line (char margin_char = ' ') const;
400 void print_annotation_line (linenum_type row, const line_bounds lbounds);
401 void print_any_labels (linenum_type row);
402 void print_trailing_fixits (linenum_type row);
404 bool annotation_line_showed_range_p (linenum_type line, int start_column,
405 int finish_column) const;
406 void show_ruler (int max_column) const;
408 bool validate_fixit_hint_p (const fixit_hint *hint);
410 void calculate_line_spans ();
411 void calculate_linenum_width ();
412 void calculate_x_offset_display ();
414 void print_newline ();
416 bool
417 get_state_at_point (/* Inputs. */
418 linenum_type row, int column,
419 int first_non_ws, int last_non_ws,
420 enum column_unit col_unit,
421 /* Outputs. */
422 point_state *out_state);
425 get_x_bound_for_row (linenum_type row, int caret_column,
426 int last_non_ws);
428 void
429 move_to_column (int *column, int dest_column, bool add_left_margin);
431 private:
432 bool compatible_locations_p (location_t loc_a, location_t loc_b) const;
434 const diagnostic_source_printing_options &m_options;
435 const line_maps *m_line_table;
436 file_cache &m_file_cache;
437 pretty_printer *m_pp;
438 char_display_policy m_policy;
439 location_t m_primary_loc;
440 exploc_with_display_col m_exploc;
441 colorizer m_colorizer;
442 bool m_diagnostic_path_p;
443 auto_vec <layout_range> m_layout_ranges;
444 auto_vec <const fixit_hint *> m_fixit_hints;
445 auto_vec <line_span> m_line_spans;
446 int m_linenum_width;
447 int m_x_offset_display;
448 bool m_escape_on_output;
451 /* Implementation of "class colorizer". */
453 /* The constructor for "colorizer". Lookup and store color codes for the
454 different kinds of things we might need to print. */
456 colorizer::colorizer (pretty_printer *pp,
457 diagnostic_t diagnostic_kind) :
458 m_pp (pp),
459 m_diagnostic_kind (diagnostic_kind),
460 m_current_state (STATE_NORMAL_TEXT)
462 m_range1 = get_color_by_name ("range1");
463 m_range2 = get_color_by_name ("range2");
464 m_fixit_insert = get_color_by_name ("fixit-insert");
465 m_fixit_delete = get_color_by_name ("fixit-delete");
466 m_stop_color = colorize_stop (pp_show_color (m_pp));
469 /* The destructor for "colorize". If colorization is on, print a code to
470 turn it off. */
472 colorizer::~colorizer ()
474 finish_state (m_current_state);
477 /* Update state, printing color codes if necessary if there's a state
478 change. */
480 void
481 colorizer::set_state (int new_state)
483 if (m_current_state != new_state)
485 finish_state (m_current_state);
486 m_current_state = new_state;
487 begin_state (new_state);
491 /* Turn on any colorization for STATE. */
493 void
494 colorizer::begin_state (int state)
496 switch (state)
498 case STATE_NORMAL_TEXT:
499 break;
501 case STATE_FIXIT_INSERT:
502 pp_string (m_pp, m_fixit_insert);
503 break;
505 case STATE_FIXIT_DELETE:
506 pp_string (m_pp, m_fixit_delete);
507 break;
509 case 0:
510 /* Make range 0 be the same color as the "kind" text
511 (error vs warning vs note). */
512 pp_string
513 (m_pp,
514 colorize_start (pp_show_color (m_pp),
515 diagnostic_get_color_for_kind (m_diagnostic_kind)));
516 break;
518 case 1:
519 pp_string (m_pp, m_range1);
520 break;
522 case 2:
523 pp_string (m_pp, m_range2);
524 break;
526 default:
527 /* For ranges beyond 2, alternate between color 1 and color 2. */
529 gcc_assert (state > 2);
530 pp_string (m_pp,
531 state % 2 ? m_range1 : m_range2);
533 break;
537 /* Turn off any colorization for STATE. */
539 void
540 colorizer::finish_state (int state)
542 if (state != STATE_NORMAL_TEXT)
543 pp_string (m_pp, m_stop_color);
546 /* Get the color code for NAME (or the empty string if
547 colorization is disabled). */
549 const char *
550 colorizer::get_color_by_name (const char *name)
552 return colorize_start (pp_show_color (m_pp), name);
555 /* Implementation of class layout_range. */
557 /* The constructor for class layout_range.
558 Initialize various layout_point fields from expanded_location
559 equivalents; we've already filtered on file. */
561 layout_range::layout_range (const exploc_with_display_col &start_exploc,
562 const exploc_with_display_col &finish_exploc,
563 enum range_display_kind range_display_kind,
564 const exploc_with_display_col &caret_exploc,
565 unsigned original_idx,
566 const range_label *label)
567 : m_start (start_exploc),
568 m_finish (finish_exploc),
569 m_range_display_kind (range_display_kind),
570 m_caret (caret_exploc),
571 m_original_idx (original_idx),
572 m_label (label)
576 /* Is (column, row) within the given range?
577 We've already filtered on the file.
579 Ranges are closed (both limits are within the range).
581 Example A: a single-line range:
582 start: (col=22, line=2)
583 finish: (col=38, line=2)
585 |00000011111111112222222222333333333344444444444
586 |34567890123456789012345678901234567890123456789
587 --+-----------------------------------------------
588 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
589 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
590 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
592 Example B: a multiline range with
593 start: (col=14, line=3)
594 finish: (col=08, line=5)
596 |00000011111111112222222222333333333344444444444
597 |34567890123456789012345678901234567890123456789
598 --+-----------------------------------------------
599 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
600 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
601 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
602 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
603 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
604 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
605 --+-----------------------------------------------
607 Legend:
608 - 'b' indicates a point *before* the range
609 - 'S' indicates the start of the range
610 - 'w' indicates a point within the range
611 - 'F' indicates the finish of the range (which is
612 within it).
613 - 'a' indicates a subsequent point *after* the range.
615 COL_UNIT controls whether we check the byte column or
616 the display column; one or the other is more convenient
617 depending on the context. */
619 bool
620 layout_range::contains_point (linenum_type row, int column,
621 enum column_unit col_unit) const
623 gcc_assert (m_start.m_line <= m_finish.m_line);
624 /* ...but the equivalent isn't true for the columns;
625 consider example B in the comment above. */
627 if (row < m_start.m_line)
628 /* Points before the first line of the range are
629 outside it (corresponding to line 01 in example A
630 and lines 01 and 02 in example B above). */
631 return false;
633 if (row == m_start.m_line)
634 /* On same line as start of range (corresponding
635 to line 02 in example A and line 03 in example B). */
637 if (column < m_start.m_columns[col_unit])
638 /* Points on the starting line of the range, but
639 before the column in which it begins. */
640 return false;
642 if (row < m_finish.m_line)
643 /* This is a multiline range; the point
644 is within it (corresponds to line 03 in example B
645 from column 14 onwards) */
646 return true;
647 else
649 /* This is a single-line range. */
650 gcc_assert (row == m_finish.m_line);
651 return column <= m_finish.m_columns[col_unit];
655 /* The point is in a line beyond that containing the
656 start of the range: lines 03 onwards in example A,
657 and lines 04 onwards in example B. */
658 gcc_assert (row > m_start.m_line);
660 if (row > m_finish.m_line)
661 /* The point is beyond the final line of the range
662 (lines 03 onwards in example A, and lines 06 onwards
663 in example B). */
664 return false;
666 if (row < m_finish.m_line)
668 /* The point is in a line that's fully within a multiline
669 range (e.g. line 04 in example B). */
670 gcc_assert (m_start.m_line < m_finish.m_line);
671 return true;
674 gcc_assert (row == m_finish.m_line);
676 return column <= m_finish.m_columns[col_unit];
679 /* Does this layout_range contain any part of line ROW? */
681 bool
682 layout_range::intersects_line_p (linenum_type row) const
684 gcc_assert (m_start.m_line <= m_finish.m_line);
685 if (row < m_start.m_line)
686 return false;
687 if (row > m_finish.m_line)
688 return false;
689 return true;
692 #if CHECKING_P
694 /* Default for when we don't care what the tab expansion is set to. */
695 static const int def_tabstop = 8;
697 static cpp_char_column_policy def_policy ()
699 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
702 /* Create some expanded locations for testing layout_range. The filename
703 member of the explocs is set to the empty string. This member will only be
704 inspected by the calls to location_compute_display_column() made from the
705 layout_point constructors. That function will check for an empty filename
706 argument and not attempt to open it, rather treating the non-existent data
707 as if the display width were the same as the byte count. Tests exercising a
708 real difference between byte count and display width are performed later,
709 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
711 static layout_range
712 make_range (file_cache &fc,
713 int start_line, int start_col, int end_line, int end_col)
715 const expanded_location start_exploc
716 = {"", start_line, start_col, NULL, false};
717 const expanded_location finish_exploc
718 = {"", end_line, end_col, NULL, false};
719 return layout_range (exploc_with_display_col (fc,
720 start_exploc, def_policy (),
721 LOCATION_ASPECT_START),
722 exploc_with_display_col (fc,
723 finish_exploc, def_policy (),
724 LOCATION_ASPECT_FINISH),
725 SHOW_RANGE_WITHOUT_CARET,
726 exploc_with_display_col (fc,
727 start_exploc, def_policy (),
728 LOCATION_ASPECT_CARET),
729 0, NULL);
732 /* Selftests for layout_range::contains_point and
733 layout_range::intersects_line_p. */
735 /* Selftest for layout_range, where the layout_range
736 is a range with start==end i.e. a single point. */
738 static void
739 test_layout_range_for_single_point ()
741 file_cache fc;
742 layout_range point = make_range (fc, 7, 10, 7, 10);
744 /* Tests for layout_range::contains_point. */
746 for (int i = 0; i != CU_NUM_UNITS; ++i)
748 const enum column_unit col_unit = (enum column_unit) i;
750 /* Before the line. */
751 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
753 /* On the line, but before start. */
754 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
756 /* At the point. */
757 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
759 /* On the line, after the point. */
760 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
762 /* After the line. */
763 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
766 /* Tests for layout_range::intersects_line_p. */
767 ASSERT_FALSE (point.intersects_line_p (6));
768 ASSERT_TRUE (point.intersects_line_p (7));
769 ASSERT_FALSE (point.intersects_line_p (8));
772 /* Selftest for layout_range, where the layout_range
773 is the single-line range shown as "Example A" above. */
775 static void
776 test_layout_range_for_single_line ()
778 file_cache fc;
779 layout_range example_a = make_range (fc, 2, 22, 2, 38);
781 /* Tests for layout_range::contains_point. */
783 for (int i = 0; i != CU_NUM_UNITS; ++i)
785 const enum column_unit col_unit = (enum column_unit) i;
787 /* Before the line. */
788 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
790 /* On the line, but before start. */
791 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
793 /* On the line, at the start. */
794 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
796 /* On the line, within the range. */
797 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
799 /* On the line, at the end. */
800 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
802 /* On the line, after the end. */
803 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
805 /* After the line. */
806 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
809 /* Tests for layout_range::intersects_line_p. */
810 ASSERT_FALSE (example_a.intersects_line_p (1));
811 ASSERT_TRUE (example_a.intersects_line_p (2));
812 ASSERT_FALSE (example_a.intersects_line_p (3));
815 /* Selftest for layout_range, where the layout_range
816 is the multi-line range shown as "Example B" above. */
818 static void
819 test_layout_range_for_multiple_lines ()
821 file_cache fc;
822 layout_range example_b = make_range (fc, 3, 14, 5, 8);
824 /* Tests for layout_range::contains_point. */
826 for (int i = 0; i != CU_NUM_UNITS; ++i)
828 const enum column_unit col_unit = (enum column_unit) i;
830 /* Before first line. */
831 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
833 /* On the first line, but before start. */
834 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
836 /* At the start. */
837 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
839 /* On the first line, within the range. */
840 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
842 /* On an interior line.
843 The column number should not matter; try various boundary
844 values. */
845 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
846 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
847 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
848 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
849 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
850 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
851 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
853 /* On the final line, before the end. */
854 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
856 /* On the final line, at the end. */
857 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
859 /* On the final line, after the end. */
860 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
862 /* After the line. */
863 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
866 /* Tests for layout_range::intersects_line_p. */
867 ASSERT_FALSE (example_b.intersects_line_p (2));
868 ASSERT_TRUE (example_b.intersects_line_p (3));
869 ASSERT_TRUE (example_b.intersects_line_p (4));
870 ASSERT_TRUE (example_b.intersects_line_p (5));
871 ASSERT_FALSE (example_b.intersects_line_p (6));
874 #endif /* #if CHECKING_P */
876 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
877 (still in bytes, not display cols) without any trailing whitespace. */
879 static int
880 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
882 int result = line_bytes;
883 while (result > 0)
885 char ch = line[result - 1];
886 if (ch == ' ' || ch == '\t' || ch == '\r')
887 result--;
888 else
889 break;
891 gcc_assert (result >= 0);
892 gcc_assert (result <= line_bytes);
893 gcc_assert (result == 0 ||
894 (line[result - 1] != ' '
895 && line[result -1] != '\t'
896 && line[result -1] != '\r'));
897 return result;
900 #if CHECKING_P
902 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
904 static void
905 assert_eq (const char *line, int expected_bytes)
907 int actual_value
908 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
909 ASSERT_EQ (actual_value, expected_bytes);
912 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
913 various inputs. It is not required to handle newlines. */
915 static void
916 test_get_line_bytes_without_trailing_whitespace ()
918 assert_eq ("", 0);
919 assert_eq (" ", 0);
920 assert_eq ("\t", 0);
921 assert_eq ("\r", 0);
922 assert_eq ("hello world", 11);
923 assert_eq ("hello world ", 11);
924 assert_eq ("hello world \t\t ", 11);
925 assert_eq ("hello world\r", 11);
928 #endif /* #if CHECKING_P */
930 /* Helper function for layout's ctor, for sanitizing locations relative
931 to the primary location within a diagnostic.
933 Compare LOC_A and LOC_B to see if it makes sense to print underlines
934 connecting their expanded locations. Doing so is only guaranteed to
935 make sense if the locations share the same macro expansion "history"
936 i.e. they can be traced through the same macro expansions, eventually
937 reaching an ordinary map.
939 This may be too strong a condition, but it effectively sanitizes
940 PR c++/70105, which has an example of printing an expression where the
941 final location of the expression is in a different macro, which
942 erroneously was leading to hundreds of lines of irrelevant source
943 being printed. */
945 bool
946 layout::compatible_locations_p (location_t loc_a, location_t loc_b) const
948 if (IS_ADHOC_LOC (loc_a))
949 loc_a = get_location_from_adhoc_loc (m_line_table, loc_a);
950 if (IS_ADHOC_LOC (loc_b))
951 loc_b = get_location_from_adhoc_loc (m_line_table, loc_b);
953 /* If either location is one of the special locations outside of a
954 linemap, they are only compatible if they are equal. */
955 if (loc_a < RESERVED_LOCATION_COUNT
956 || loc_b < RESERVED_LOCATION_COUNT)
957 return loc_a == loc_b;
959 const line_map *map_a = linemap_lookup (m_line_table, loc_a);
960 linemap_assert (map_a);
962 const line_map *map_b = linemap_lookup (m_line_table, loc_b);
963 linemap_assert (map_b);
965 /* Are they within the same map? */
966 if (map_a == map_b)
968 /* Are both within the same macro expansion? */
969 if (linemap_macro_expansion_map_p (map_a))
971 /* If so, then they're only compatible if either both are
972 from the macro definition, or both from the macro arguments. */
973 bool loc_a_from_defn
974 = linemap_location_from_macro_definition_p (m_line_table, loc_a);
975 bool loc_b_from_defn
976 = linemap_location_from_macro_definition_p (m_line_table, loc_b);
977 if (loc_a_from_defn != loc_b_from_defn)
978 return false;
980 /* Expand each location towards the spelling location, and
981 recurse. */
982 const line_map_macro *macro_map = linemap_check_macro (map_a);
983 location_t loc_a_toward_spelling
984 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
985 macro_map,
986 loc_a);
987 location_t loc_b_toward_spelling
988 = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
989 macro_map,
990 loc_b);
991 return compatible_locations_p (loc_a_toward_spelling,
992 loc_b_toward_spelling);
995 /* Otherwise they are within the same ordinary map. */
996 return true;
998 else
1000 /* Within different maps. */
1002 /* If either is within a macro expansion, they are incompatible. */
1003 if (linemap_macro_expansion_map_p (map_a)
1004 || linemap_macro_expansion_map_p (map_b))
1005 return false;
1007 /* Within two different ordinary maps; they are compatible iff they
1008 are in the same file. */
1009 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1010 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1011 return ord_map_a->to_file == ord_map_b->to_file;
1015 /* Comparator for sorting fix-it hints. */
1017 static int
1018 fixit_cmp (const void *p_a, const void *p_b)
1020 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1021 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1022 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1025 /* Callbacks for use when not escaping the source. */
1027 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1029 /* Callback for char_display_policy::m_print_cb for printing source chars
1030 when not escaping the source. */
1032 static void
1033 default_print_decoded_ch (pretty_printer *pp,
1034 const cpp_decoded_char &decoded_ch)
1036 for (const char *ptr = decoded_ch.m_start_byte;
1037 ptr != decoded_ch.m_next_byte; ptr++)
1039 if (*ptr == '\0' || *ptr == '\r')
1041 pp_space (pp);
1042 continue;
1045 pp_character (pp, *ptr);
1049 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1051 static const int width_per_escaped_byte = 4;
1053 /* Callback for char_column_policy::m_width_cb for determining the
1054 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1056 static int
1057 escape_as_bytes_width (cppchar_t ch)
1059 if (ch < 0x80 && ISPRINT (ch))
1060 return cpp_wcwidth (ch);
1061 else
1063 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1064 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1065 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1066 return 4 * width_per_escaped_byte;
1070 /* Callback for char_display_policy::m_print_cb for printing source chars
1071 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1073 static void
1074 escape_as_bytes_print (pretty_printer *pp,
1075 const cpp_decoded_char &decoded_ch)
1077 if (!decoded_ch.m_valid_ch)
1079 for (const char *iter = decoded_ch.m_start_byte;
1080 iter != decoded_ch.m_next_byte; ++iter)
1082 char buf[16];
1083 sprintf (buf, "<%02x>", (unsigned char)*iter);
1084 pp_string (pp, buf);
1086 return;
1089 cppchar_t ch = decoded_ch.m_ch;
1090 if (ch < 0x80 && ISPRINT (ch))
1091 pp_character (pp, ch);
1092 else
1094 for (const char *iter = decoded_ch.m_start_byte;
1095 iter < decoded_ch.m_next_byte; ++iter)
1097 char buf[16];
1098 sprintf (buf, "<%02x>", (unsigned char)*iter);
1099 pp_string (pp, buf);
1104 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1106 /* Callback for char_column_policy::m_width_cb for determining the
1107 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1109 static int
1110 escape_as_unicode_width (cppchar_t ch)
1112 if (ch < 0x80 && ISPRINT (ch))
1113 return cpp_wcwidth (ch);
1114 else
1116 // Width of "<U+%04x>"
1117 if (ch > 0xfffff)
1118 return 10;
1119 else if (ch > 0xffff)
1120 return 9;
1121 else
1122 return 8;
1126 /* Callback for char_display_policy::m_print_cb for printing source chars
1127 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1129 static void
1130 escape_as_unicode_print (pretty_printer *pp,
1131 const cpp_decoded_char &decoded_ch)
1133 if (!decoded_ch.m_valid_ch)
1135 escape_as_bytes_print (pp, decoded_ch);
1136 return;
1139 cppchar_t ch = decoded_ch.m_ch;
1140 if (ch < 0x80 && ISPRINT (ch))
1141 pp_character (pp, ch);
1142 else
1144 char buf[16];
1145 sprintf (buf, "<U+%04X>", ch);
1146 pp_string (pp, buf);
1150 /* Populate a char_display_policy based on DC and RICHLOC. */
1152 static char_display_policy
1153 make_policy (const diagnostic_context &dc,
1154 const rich_location &richloc)
1156 /* The default is to not escape non-ASCII bytes. */
1157 char_display_policy result
1158 (dc.m_tabstop, cpp_wcwidth, default_print_decoded_ch);
1160 /* If the diagnostic suggests escaping non-ASCII bytes, then
1161 use policy from user-supplied options. */
1162 if (richloc.escape_on_output_p ())
1164 result.m_undecoded_byte_width = width_per_escaped_byte;
1165 switch (dc.get_escape_format ())
1167 default:
1168 gcc_unreachable ();
1169 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1170 result.m_width_cb = escape_as_unicode_width;
1171 result.m_print_cb = escape_as_unicode_print;
1172 break;
1173 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1174 result.m_width_cb = escape_as_bytes_width;
1175 result.m_print_cb = escape_as_bytes_print;
1176 break;
1180 return result;
1183 /* Implementation of class layout. */
1185 /* Constructor for class layout.
1187 Filter the ranges from the rich_location to those that we can
1188 sanely print, populating m_layout_ranges and m_fixit_hints.
1189 Determine the range of lines that we will print, splitting them
1190 up into an ordered list of disjoint spans of contiguous line numbers.
1191 Determine m_x_offset_display, to ensure that the primary caret
1192 will fit within the max_width provided by the diagnostic_context. */
1194 layout::layout (const diagnostic_context &context,
1195 const rich_location &richloc,
1196 diagnostic_t diagnostic_kind,
1197 pretty_printer *pp)
1198 : m_options (context.m_source_printing),
1199 m_line_table (richloc.get_line_table ()),
1200 m_file_cache (context.get_file_cache ()),
1201 m_pp (pp ? pp : context.printer),
1202 m_policy (make_policy (context, richloc)),
1203 m_primary_loc (richloc.get_range (0)->m_loc),
1204 m_exploc (m_file_cache,
1205 richloc.get_expanded_location (0), m_policy,
1206 LOCATION_ASPECT_CARET),
1207 m_colorizer (m_pp, diagnostic_kind),
1208 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1209 m_layout_ranges (richloc.get_num_locations ()),
1210 m_fixit_hints (richloc.get_num_fixit_hints ()),
1211 m_line_spans (1 + richloc.get_num_locations ()),
1212 m_linenum_width (0),
1213 m_x_offset_display (0),
1214 m_escape_on_output (richloc.escape_on_output_p ())
1216 for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
1218 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1219 Ignore any ranges that are awkward to handle. */
1220 const location_range *loc_range = richloc.get_range (idx);
1221 maybe_add_location_range (loc_range, idx, false);
1224 /* Populate m_fixit_hints, filtering to only those that are in the
1225 same file. */
1226 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1228 const fixit_hint *hint = richloc.get_fixit_hint (i);
1229 if (validate_fixit_hint_p (hint))
1230 m_fixit_hints.safe_push (hint);
1233 /* Sort m_fixit_hints. */
1234 m_fixit_hints.qsort (fixit_cmp);
1236 /* Populate the indicated members. */
1237 calculate_line_spans ();
1238 calculate_linenum_width ();
1239 calculate_x_offset_display ();
1241 if (m_options.show_ruler_p)
1242 show_ruler (m_x_offset_display + m_options.max_width);
1246 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1247 those that we can sanely print.
1249 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1250 (for use as extrinsic state by label ranges FIXME).
1252 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1253 filtered against this layout instance's current line spans: it
1254 will only be added if the location is fully within the lines
1255 already specified by other locations.
1257 Return true iff LOC_RANGE was added. */
1259 bool
1260 layout::maybe_add_location_range (const location_range *loc_range,
1261 unsigned original_idx,
1262 bool restrict_to_current_line_spans)
1264 gcc_assert (loc_range);
1266 /* Split the "range" into caret and range information. */
1267 source_range src_range = get_range_from_loc (m_line_table, loc_range->m_loc);
1269 /* Expand the various locations. */
1270 expanded_location start
1271 = linemap_client_expand_location_to_spelling_point
1272 (m_line_table, src_range.m_start, LOCATION_ASPECT_START);
1273 expanded_location finish
1274 = linemap_client_expand_location_to_spelling_point
1275 (m_line_table, src_range.m_finish, LOCATION_ASPECT_FINISH);
1276 expanded_location caret
1277 = linemap_client_expand_location_to_spelling_point
1278 (m_line_table, loc_range->m_loc, LOCATION_ASPECT_CARET);
1280 /* If any part of the range isn't in the same file as the primary
1281 location of this diagnostic, ignore the range. */
1282 if (start.file != m_exploc.file)
1283 return false;
1284 if (finish.file != m_exploc.file)
1285 return false;
1286 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1287 if (caret.file != m_exploc.file)
1288 return false;
1290 /* Sanitize the caret location for non-primary ranges. */
1291 if (m_layout_ranges.length () > 0)
1292 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1293 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1294 /* Discard any non-primary ranges that can't be printed
1295 sanely relative to the primary location. */
1296 return false;
1298 /* If there's no column information, then don't try to print
1299 annotation lines for this range. */
1300 enum range_display_kind range_display_kind
1301 = loc_range->m_range_display_kind;
1302 if (start.column == 0
1303 || finish.column == 0
1304 || caret.column == 0)
1305 range_display_kind = SHOW_LINES_WITHOUT_RANGE;
1307 /* Everything is now known to be in the correct source file,
1308 but it may require further sanitization. */
1309 layout_range ri (exploc_with_display_col (m_file_cache,
1310 start, m_policy,
1311 LOCATION_ASPECT_START),
1312 exploc_with_display_col (m_file_cache,
1313 finish, m_policy,
1314 LOCATION_ASPECT_FINISH),
1315 range_display_kind,
1316 exploc_with_display_col (m_file_cache,
1317 caret, m_policy,
1318 LOCATION_ASPECT_CARET),
1319 original_idx, loc_range->m_label);
1321 /* If we have a range that finishes before it starts (perhaps
1322 from something built via macro expansion), printing the
1323 range is likely to be nonsensical. Also, attempting to do so
1324 breaks assumptions within the printing code (PR c/68473).
1325 Similarly, don't attempt to print ranges if one or both ends
1326 of the range aren't sane to print relative to the
1327 primary location (PR c++/70105). */
1328 if (start.line > finish.line
1329 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1330 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1332 /* Is this the primary location? */
1333 if (m_layout_ranges.length () == 0)
1335 /* We want to print the caret for the primary location, but
1336 we must sanitize away m_start and m_finish. */
1337 ri.m_start = ri.m_caret;
1338 ri.m_finish = ri.m_caret;
1340 else
1341 /* This is a non-primary range; ignore it. */
1342 return false;
1345 /* Potentially filter to just the lines already specified by other
1346 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1347 The layout ctor doesn't use it, and can't because m_line_spans
1348 hasn't been set up at that point. */
1349 if (restrict_to_current_line_spans)
1351 if (!will_show_line_p (start.line))
1352 return false;
1353 if (!will_show_line_p (finish.line))
1354 return false;
1355 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1356 if (!will_show_line_p (caret.line))
1357 return false;
1360 /* Passed all the tests; add the range to m_layout_ranges so that
1361 it will be printed. */
1362 m_layout_ranges.safe_push (ri);
1363 return true;
1366 /* Return true iff ROW is within one of the line spans for this layout. */
1368 bool
1369 layout::will_show_line_p (linenum_type row) const
1371 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1372 line_span_idx++)
1374 const line_span *line_span = get_line_span (line_span_idx);
1375 if (line_span->contains_line_p (row))
1376 return true;
1378 return false;
1381 /* Print a line showing a gap in the line numbers, for showing the boundary
1382 between two line spans. */
1384 void
1385 layout::print_gap_in_line_numbering ()
1387 gcc_assert (m_options.show_line_numbers_p);
1389 pp_emit_prefix (m_pp);
1391 for (int i = 0; i < m_linenum_width + 1; i++)
1392 pp_character (m_pp, '.');
1394 pp_newline (m_pp);
1397 /* Return true iff we should print a heading when starting the
1398 line span with the given index. */
1400 bool
1401 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1403 /* We print a heading for every change of line span, hence for every
1404 line span after the initial one. */
1405 if (line_span_idx > 0)
1406 return true;
1408 /* We also do it for the initial span if the primary location of the
1409 diagnostic is in a different span. */
1410 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1411 return true;
1413 return false;
1416 /* Get an expanded_location for the first location of interest within
1417 the given line_span.
1418 Used when printing a heading to indicate a new line span. */
1420 expanded_location
1421 layout::get_expanded_location (const line_span *line_span) const
1423 /* Whenever possible, use the caret location. */
1424 if (line_span->contains_line_p (m_exploc.line))
1425 return m_exploc;
1427 /* Otherwise, use the start of the first range that's present
1428 within the line_span. */
1429 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1431 const layout_range *lr = &m_layout_ranges[i];
1432 if (line_span->contains_line_p (lr->m_start.m_line))
1434 expanded_location exploc = m_exploc;
1435 exploc.line = lr->m_start.m_line;
1436 exploc.column = lr->m_start.m_columns[CU_BYTES];
1437 return exploc;
1441 /* Otherwise, use the location of the first fixit-hint present within
1442 the line_span. */
1443 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1445 const fixit_hint *hint = m_fixit_hints[i];
1446 location_t loc = hint->get_start_loc ();
1447 expanded_location exploc = expand_location (loc);
1448 if (line_span->contains_line_p (exploc.line))
1449 return exploc;
1452 /* It should not be possible to have a line span that didn't
1453 contain any of the layout_range or fixit_hint instances. */
1454 gcc_unreachable ();
1455 return m_exploc;
1458 /* Determine if HINT is meaningful to print within this layout. */
1460 bool
1461 layout::validate_fixit_hint_p (const fixit_hint *hint)
1463 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1464 return false;
1465 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1466 return false;
1468 return true;
1471 /* Determine the range of lines affected by HINT.
1472 This assumes that HINT has already been filtered by
1473 validate_fixit_hint_p, and so affects the correct source file. */
1475 static line_span
1476 get_line_span_for_fixit_hint (const fixit_hint *hint)
1478 gcc_assert (hint);
1480 int start_line = LOCATION_LINE (hint->get_start_loc ());
1482 /* For line-insertion fix-it hints, add the previous line to the
1483 span, to give the user more context on the proposed change. */
1484 if (hint->ends_with_newline_p ())
1485 if (start_line > 1)
1486 start_line--;
1488 return line_span (start_line,
1489 LOCATION_LINE (hint->get_next_loc ()));
1492 /* We want to print the pertinent source code at a diagnostic. The
1493 rich_location can contain multiple locations. This will have been
1494 filtered into m_exploc (the caret for the primary location) and
1495 m_layout_ranges, for those ranges within the same source file.
1497 We will print a subset of the lines within the source file in question,
1498 as a collection of "spans" of lines.
1500 This function populates m_line_spans with an ordered, disjoint list of
1501 the line spans of interest.
1503 Printing a gap between line spans takes one line, so, when printing
1504 line numbers, we allow a gap of up to one line between spans when
1505 merging, since it makes more sense to print the source line rather than a
1506 "gap-in-line-numbering" line. When not printing line numbers, it's
1507 better to be more explicit about what's going on, so keeping them as
1508 separate spans is preferred.
1510 For example, if the primary range is on lines 8-10, with secondary ranges
1511 covering lines 5-6 and lines 13-15:
1514 005 |RANGE 1
1515 006 |RANGE 1
1517 008 |PRIMARY RANGE
1518 009 |PRIMARY CARET
1519 010 |PRIMARY RANGE
1522 013 |RANGE 2
1523 014 |RANGE 2
1524 015 |RANGE 2
1527 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1529 With line numbering off (with span headers), we want three spans: lines 5-6,
1530 lines 8-10, and lines 13-15. */
1532 void
1533 layout::calculate_line_spans ()
1535 /* This should only be called once, by the ctor. */
1536 gcc_assert (m_line_spans.length () == 0);
1538 /* Populate tmp_spans with individual spans, for each of
1539 m_exploc, and for m_layout_ranges. */
1540 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1541 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1542 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1544 const layout_range *lr = &m_layout_ranges[i];
1545 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1546 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1547 lr->m_finish.m_line));
1550 /* Also add spans for any fix-it hints, in case they cover other lines. */
1551 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1553 const fixit_hint *hint = m_fixit_hints[i];
1554 gcc_assert (hint);
1555 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1558 /* Sort them. */
1559 tmp_spans.qsort(line_span::comparator);
1561 /* Now iterate through tmp_spans, copying into m_line_spans, and
1562 combining where possible. */
1563 gcc_assert (tmp_spans.length () > 0);
1564 m_line_spans.safe_push (tmp_spans[0]);
1565 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1567 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1568 const line_span *next = &tmp_spans[i];
1569 gcc_assert (next->m_first_line >= current->m_first_line);
1570 const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
1571 if ((linenum_arith_t)next->m_first_line
1572 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1574 /* We can merge them. */
1575 if (next->m_last_line > current->m_last_line)
1576 current->m_last_line = next->m_last_line;
1578 else
1580 /* No merger possible. */
1581 m_line_spans.safe_push (*next);
1585 /* Verify the result, in m_line_spans. */
1586 gcc_assert (m_line_spans.length () > 0);
1587 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1589 const line_span *prev = &m_line_spans[i - 1];
1590 const line_span *next = &m_line_spans[i];
1591 /* The individual spans must be sane. */
1592 gcc_assert (prev->m_first_line <= prev->m_last_line);
1593 gcc_assert (next->m_first_line <= next->m_last_line);
1594 /* The spans must be ordered. */
1595 gcc_assert (prev->m_first_line < next->m_first_line);
1596 /* There must be a gap of at least one line between separate spans. */
1597 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1601 /* Determine how many display columns need to be reserved for line numbers,
1602 based on the largest line number that will be needed, and populate
1603 m_linenum_width. */
1605 void
1606 layout::calculate_linenum_width ()
1608 gcc_assert (m_line_spans.length () > 0);
1609 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1610 int highest_line = last_span->m_last_line;
1611 if (highest_line < 0)
1612 highest_line = 0;
1613 m_linenum_width = num_digits (highest_line);
1614 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1615 if (m_line_spans.length () > 1)
1616 m_linenum_width = MAX (m_linenum_width, 3);
1617 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1618 after the line number. */
1619 m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
1622 /* Calculate m_x_offset_display, which improves readability in case the source
1623 line of interest is longer than the user's display. All lines output will be
1624 shifted to the left (so that their beginning is no longer displayed) by
1625 m_x_offset_display display columns, so that the caret is in a reasonable
1626 location. */
1628 void
1629 layout::calculate_x_offset_display ()
1631 m_x_offset_display = 0;
1633 const int max_width = m_options.max_width;
1634 if (!max_width)
1636 /* Nothing to do, the width is not capped. */
1637 return;
1640 const char_span line = m_file_cache.get_source_line (m_exploc.file,
1641 m_exploc.line);
1642 if (!line)
1644 /* Nothing to do, we couldn't find the source line. */
1645 return;
1647 int caret_display_column = m_exploc.m_display_col;
1648 const int line_bytes
1649 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1650 line.length ());
1651 int eol_display_column
1652 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1653 if (caret_display_column > eol_display_column
1654 || !caret_display_column)
1656 /* This does not make sense, so don't try to do anything in this case. */
1657 return;
1660 /* Adjust caret and eol positions to include the left margin. If we are
1661 outputting line numbers, then the left margin is equal to m_linenum_width
1662 plus three for the " | " which follows it. Otherwise the left margin width
1663 is equal to 1, because layout::print_source_line() will prefix each line
1664 with a space. */
1665 const int source_display_cols = eol_display_column;
1666 int left_margin_size = 1;
1667 if (m_options.show_line_numbers_p)
1668 left_margin_size = m_linenum_width + 3;
1669 caret_display_column += left_margin_size;
1670 eol_display_column += left_margin_size;
1672 if (eol_display_column <= max_width)
1674 /* Nothing to do, everything fits in the display. */
1675 return;
1678 /* The line is too long for the display. Calculate an offset such that the
1679 caret is not too close to the right edge of the screen. It will be
1680 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1681 than that to the end of the source line anyway. */
1682 int right_margin_size = CARET_LINE_MARGIN;
1683 right_margin_size = MIN (eol_display_column - caret_display_column,
1684 right_margin_size);
1685 if (right_margin_size + left_margin_size >= max_width)
1687 /* The max_width is very small, so anything we try to do will not be very
1688 effective; just punt in this case and output with no offset. */
1689 return;
1691 const int max_caret_display_column = max_width - right_margin_size;
1692 if (caret_display_column > max_caret_display_column)
1694 m_x_offset_display = caret_display_column - max_caret_display_column;
1695 /* Make sure we don't offset the line into oblivion. */
1696 static const int min_cols_visible = 2;
1697 if (source_display_cols - m_x_offset_display < min_cols_visible)
1698 m_x_offset_display = 0;
1702 /* Print line ROW of source code, potentially colorized at any ranges, and
1703 return the line bounds. LINE is the source line (not necessarily
1704 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1705 colorization and tab expansion, this function tracks the line position in
1706 both byte and display column units. */
1708 line_bounds
1709 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1711 m_colorizer.set_normal_text ();
1713 pp_emit_prefix (m_pp);
1714 if (m_options.show_line_numbers_p)
1716 int width = num_digits (row);
1717 for (int i = 0; i < m_linenum_width - width; i++)
1718 pp_space (m_pp);
1719 pp_printf (m_pp, "%i | ", row);
1721 else
1722 pp_space (m_pp);
1724 /* We will stop printing the source line at any trailing whitespace. */
1725 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1726 line_bytes);
1728 /* This object helps to keep track of which display column we are at, which is
1729 necessary for computing the line bounds in display units, for doing
1730 tab expansion, and for implementing m_x_offset_display. */
1731 cpp_display_width_computation dw (line, line_bytes, m_policy);
1733 /* Skip the first m_x_offset_display display columns. In case the leading
1734 portion that will be skipped ends with a character with wcwidth > 1, then
1735 it is possible we skipped too much, so account for that by padding with
1736 spaces. Note that this does the right thing too in case a tab was the last
1737 character to be skipped over; the tab is effectively replaced by the
1738 correct number of trailing spaces needed to offset by the desired number of
1739 display columns. */
1740 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1741 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1742 pp_space (m_pp);
1744 /* Print the line and compute the line_bounds. */
1745 line_bounds lbounds;
1746 while (!dw.done ())
1748 /* Assuming colorization is enabled for the caret and underline
1749 characters, we may also colorize the associated characters
1750 within the source line.
1752 For frontends that generate range information, we color the
1753 associated characters in the source line the same as the
1754 carets and underlines in the annotation line, to make it easier
1755 for the reader to see the pertinent code.
1757 For frontends that only generate carets, we don't colorize the
1758 characters above them, since this would look strange (e.g.
1759 colorizing just the first character in a token). */
1760 if (m_options.colorize_source_p)
1762 bool in_range_p;
1763 point_state state;
1764 const int start_byte_col = dw.bytes_processed () + 1;
1765 in_range_p = get_state_at_point (row, start_byte_col,
1766 0, INT_MAX,
1767 CU_BYTES,
1768 &state);
1769 if (in_range_p)
1770 m_colorizer.set_range (state.range_idx);
1771 else
1772 m_colorizer.set_normal_text ();
1775 /* Get the display width of the next character to be output, expanding
1776 tabs and replacing some control bytes with spaces as necessary. */
1777 const char *c = dw.next_byte ();
1778 const int start_disp_col = dw.display_cols_processed () + 1;
1779 cpp_decoded_char cp;
1780 const int this_display_width = dw.process_next_codepoint (&cp);
1781 if (*c == '\t')
1783 /* The returned display width is the number of spaces into which the
1784 tab should be expanded. */
1785 for (int i = 0; i != this_display_width; ++i)
1786 pp_space (m_pp);
1787 continue;
1790 /* We have a (possibly multibyte) character to output; update the line
1791 bounds if it is not whitespace. */
1792 if (*c != ' ')
1794 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1795 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1796 lbounds.m_first_non_ws_disp_col = start_disp_col;
1799 /* Output the character. */
1800 m_policy.m_print_cb (m_pp, cp);
1801 c = dw.next_byte ();
1803 print_newline ();
1804 return lbounds;
1807 /* Determine if we should print an annotation line for ROW.
1808 i.e. if any of m_layout_ranges contains ROW. */
1810 bool
1811 layout::should_print_annotation_line_p (linenum_type row) const
1813 layout_range *range;
1814 int i;
1815 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1817 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1818 return false;
1819 if (range->intersects_line_p (row))
1820 return true;
1822 return false;
1825 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1826 margin, which is empty for annotation lines. Otherwise, do nothing. */
1828 void
1829 layout::start_annotation_line (char margin_char) const
1831 pp_emit_prefix (m_pp);
1832 if (m_options.show_line_numbers_p)
1834 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1835 of it, right-aligned, padded with spaces. */
1836 int i;
1837 for (i = 0; i < m_linenum_width - 3; i++)
1838 pp_space (m_pp);
1839 for (; i < m_linenum_width; i++)
1840 pp_character (m_pp, margin_char);
1841 pp_string (m_pp, " |");
1845 /* Print a line consisting of the caret/underlines for the given
1846 source line. */
1848 void
1849 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1851 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1852 lbounds.m_last_non_ws_disp_col);
1854 start_annotation_line ();
1855 pp_space (m_pp);
1857 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1859 bool in_range_p;
1860 point_state state;
1861 in_range_p = get_state_at_point (row, column,
1862 lbounds.m_first_non_ws_disp_col,
1863 lbounds.m_last_non_ws_disp_col,
1864 CU_DISPLAY_COLS,
1865 &state);
1866 if (in_range_p)
1868 /* Within a range. Draw either the caret or an underline. */
1869 m_colorizer.set_range (state.range_idx);
1870 if (state.draw_caret_p)
1872 /* Draw the caret. */
1873 char caret_char;
1874 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1875 caret_char = m_options.caret_chars[state.range_idx];
1876 else
1877 caret_char = '^';
1878 pp_character (m_pp, caret_char);
1880 else
1881 pp_character (m_pp, '~');
1883 else
1885 /* Not in a range. */
1886 m_colorizer.set_normal_text ();
1887 pp_character (m_pp, ' ');
1890 print_newline ();
1893 /* A version of label_text that can live inside a vec.
1894 Requires manual cleanup via maybe_free. */
1896 struct pod_label_text
1898 pod_label_text ()
1899 : m_buffer (NULL), m_caller_owned (false)
1902 pod_label_text (label_text &&other)
1903 : m_buffer (const_cast<char*> (other.get ())),
1904 m_caller_owned (other.is_owner ())
1906 other.release ();
1909 void maybe_free ()
1911 if (m_caller_owned)
1912 free (m_buffer);
1915 char *m_buffer;
1916 bool m_caller_owned;
1919 /* Implementation detail of layout::print_any_labels.
1921 A label within the given row of source. */
1923 class line_label
1925 public:
1926 line_label (const cpp_char_column_policy &policy,
1927 int state_idx, int column,
1928 label_text text)
1929 : m_state_idx (state_idx), m_column (column),
1930 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1932 const int bytes = strlen (m_text.m_buffer);
1933 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1936 /* Sorting is primarily by column, then by state index. */
1937 static int comparator (const void *p1, const void *p2)
1939 const line_label *ll1 = (const line_label *)p1;
1940 const line_label *ll2 = (const line_label *)p2;
1941 int column_cmp = compare (ll1->m_column, ll2->m_column);
1942 if (column_cmp)
1943 return column_cmp;
1944 /* Order by reverse state index, so that labels are printed
1945 in order of insertion into the rich_location when the
1946 sorted list is walked backwards. */
1947 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1950 int m_state_idx;
1951 int m_column;
1952 pod_label_text m_text;
1953 size_t m_display_width;
1954 int m_label_line;
1955 bool m_has_vbar;
1958 /* Print any labels in this row. */
1959 void
1960 layout::print_any_labels (linenum_type row)
1962 int i;
1963 auto_vec<line_label> labels;
1965 /* Gather the labels that are to be printed into "labels". */
1967 layout_range *range;
1968 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1970 /* Most ranges don't have labels, so reject this first. */
1971 if (range->m_label == NULL)
1972 continue;
1974 /* The range's caret must be on this line. */
1975 if (range->m_caret.m_line != row)
1976 continue;
1978 /* Reject labels that aren't fully visible due to clipping
1979 by m_x_offset_display. */
1980 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1981 if (disp_col <= m_x_offset_display)
1982 continue;
1984 label_text text;
1985 text = range->m_label->get_text (range->m_original_idx);
1987 /* Allow for labels that return NULL from their get_text
1988 implementation (so e.g. such labels can control their own
1989 visibility). */
1990 if (text.get () == NULL)
1991 continue;
1993 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1997 /* Bail out if there are no labels on this row. */
1998 if (labels.length () == 0)
1999 return;
2001 /* Sort them. */
2002 labels.qsort(line_label::comparator);
2004 /* Figure out how many "label lines" we need, and which
2005 one each label is printed in.
2007 For example, if the labels aren't too densely packed,
2008 we can fit them on the same line, giving two "label lines":
2010 foo + bar
2011 ~~~ ~~~
2012 | | : label line 0
2013 l0 l1 : label line 1
2015 If they would touch each other or overlap, then we need
2016 additional "label lines":
2018 foo + bar
2019 ~~~ ~~~
2020 | | : label line 0
2021 | label 1 : label line 1
2022 label 0 : label line 2
2024 Place the final label on label line 1, and work backwards, adding
2025 label lines as needed.
2027 If multiple labels are at the same place, put them on separate
2028 label lines:
2030 foo + bar
2031 ^ : label line 0
2032 | : label line 1
2033 label 0 : label line 2
2034 label 1 : label line 3. */
2036 int max_label_line = 1;
2038 int next_column = INT_MAX;
2039 line_label *label;
2040 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2042 /* Would this label "touch" or overlap the next label? */
2043 if (label->m_column + label->m_display_width >= (size_t)next_column)
2045 max_label_line++;
2047 /* If we've already seen labels with the same column, suppress the
2048 vertical bar for subsequent ones in this backwards iteration;
2049 hence only the one with the highest label_line has m_has_vbar set. */
2050 if (label->m_column == next_column)
2051 label->m_has_vbar = false;
2054 label->m_label_line = max_label_line;
2055 next_column = label->m_column;
2059 /* Print the "label lines". For each label within the line, print
2060 either a vertical bar ('|') for the labels that are lower down, or the
2061 labels themselves once we've reached their line. */
2063 for (int label_line = 0; label_line <= max_label_line; label_line++)
2065 start_annotation_line ();
2066 pp_space (m_pp);
2067 int column = 1 + m_x_offset_display;
2068 line_label *label;
2069 FOR_EACH_VEC_ELT (labels, i, label)
2071 if (label_line > label->m_label_line)
2072 /* We've printed all the labels for this label line. */
2073 break;
2075 if (label_line == label->m_label_line)
2077 gcc_assert (column <= label->m_column);
2078 move_to_column (&column, label->m_column, true);
2079 /* Colorize the text, unless it's for events in a
2080 diagnostic_path. */
2081 if (!m_diagnostic_path_p)
2082 m_colorizer.set_range (label->m_state_idx);
2083 pp_string (m_pp, label->m_text.m_buffer);
2084 m_colorizer.set_normal_text ();
2085 column += label->m_display_width;
2087 else if (label->m_has_vbar)
2089 gcc_assert (column <= label->m_column);
2090 move_to_column (&column, label->m_column, true);
2091 m_colorizer.set_range (label->m_state_idx);
2092 pp_character (m_pp, '|');
2093 m_colorizer.set_normal_text ();
2094 column++;
2097 print_newline ();
2101 /* Clean up. */
2103 line_label *label;
2104 FOR_EACH_VEC_ELT (labels, i, label)
2105 label->m_text.maybe_free ();
2109 /* If there are any fixit hints inserting new lines before source line ROW,
2110 print them.
2112 They are printed on lines of their own, before the source line
2113 itself, with a leading '+'. */
2115 void
2116 layout::print_leading_fixits (linenum_type row)
2118 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2120 const fixit_hint *hint = m_fixit_hints[i];
2122 if (!hint->ends_with_newline_p ())
2123 /* Not a newline fixit; print it in print_trailing_fixits. */
2124 continue;
2126 gcc_assert (hint->insertion_p ());
2128 if (hint->affects_line_p (m_line_table, m_exploc.file, row))
2130 /* Printing the '+' with normal colorization
2131 and the inserted line with "insert" colorization
2132 helps them stand out from each other, and from
2133 the surrounding text. */
2134 m_colorizer.set_normal_text ();
2135 start_annotation_line ('+');
2136 pp_character (m_pp, '+');
2137 m_colorizer.set_fixit_insert ();
2138 /* Print all but the trailing newline of the fix-it hint.
2139 We have to print the newline separately to avoid
2140 getting additional pp prefixes printed. */
2141 for (size_t i = 0; i < hint->get_length () - 1; i++)
2142 pp_character (m_pp, hint->get_string ()[i]);
2143 m_colorizer.set_normal_text ();
2144 pp_newline (m_pp);
2149 /* Subroutine of layout::print_trailing_fixits.
2151 Determine if the annotation line printed for LINE contained
2152 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2154 bool
2155 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2156 int finish_column) const
2158 layout_range *range;
2159 int i;
2160 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2161 if (range->m_start.m_line == line
2162 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2163 && range->m_finish.m_line == line
2164 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2165 return true;
2166 return false;
2169 /* Classes for printing trailing fix-it hints i.e. those that
2170 don't add new lines.
2172 For insertion, these can look like:
2174 new_text
2176 For replacement, these can look like:
2178 ------------- : underline showing affected range
2179 new_text
2181 For deletion, these can look like:
2183 ------------- : underline showing affected range
2185 This can become confusing if they overlap, and so we need
2186 to do some preprocessing to decide what to print.
2187 We use the list of fixit_hint instances affecting the line
2188 to build a list of "correction" instances, and print the
2189 latter.
2191 For example, consider a set of fix-its for converting
2192 a C-style cast to a C++ const_cast.
2194 Given:
2196 ..000000000111111111122222222223333333333.
2197 ..123456789012345678901234567890123456789.
2198 foo *f = (foo *)ptr->field;
2199 ^~~~~
2201 and the fix-it hints:
2202 - replace col 10 (the open paren) with "const_cast<"
2203 - replace col 16 (the close paren) with "> ("
2204 - insert ")" before col 27
2206 then we would get odd-looking output:
2208 foo *f = (foo *)ptr->field;
2209 ^~~~~
2211 const_cast<
2213 > ( )
2215 It would be better to detect when fixit hints are going to
2216 overlap (those that require new lines), and to consolidate
2217 the printing of such fixits, giving something like:
2219 foo *f = (foo *)ptr->field;
2220 ^~~~~
2221 -----------------
2222 const_cast<foo *> (ptr->field)
2224 This works by detecting when the printing would overlap, and
2225 effectively injecting no-op replace hints into the gaps between
2226 such fix-its, so that the printing joins up.
2228 In the above example, the overlap of:
2229 - replace col 10 (the open paren) with "const_cast<"
2230 and:
2231 - replace col 16 (the close paren) with "> ("
2232 is fixed by injecting a no-op:
2233 - replace cols 11-15 with themselves ("foo *")
2234 and consolidating these, making:
2235 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2236 i.e.:
2237 - replace cols 10-16 with "const_cast<foo *> ("
2239 This overlaps with the final fix-it hint:
2240 - insert ")" before col 27
2241 and so we repeat the consolidation process, by injecting
2242 a no-op:
2243 - replace cols 17-26 with themselves ("ptr->field")
2244 giving:
2245 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2246 i.e.:
2247 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2249 and is thus printed as desired. */
2251 /* A range of (byte or display) columns within a line. */
2253 class column_range
2255 public:
2256 column_range (int start_, int finish_) : start (start_), finish (finish_)
2258 gcc_assert (valid_p (start, finish));
2261 bool operator== (const column_range &other) const
2263 return start == other.start && finish == other.finish;
2266 static bool valid_p (int start, int finish)
2268 /* We must have either a range, or an insertion. */
2269 return (start <= finish || finish == start - 1);
2272 int start;
2273 int finish;
2276 /* Get the range of bytes or display columns that HINT would affect. */
2277 static column_range
2278 get_affected_range (file_cache &fc,
2279 const cpp_char_column_policy &policy,
2280 const fixit_hint *hint, enum column_unit col_unit)
2282 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2283 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2284 --exploc_finish.column;
2286 int start_column;
2287 int finish_column;
2288 if (col_unit == CU_DISPLAY_COLS)
2290 start_column = location_compute_display_column (fc, exploc_start, policy);
2291 if (hint->insertion_p ())
2292 finish_column = start_column - 1;
2293 else
2294 finish_column
2295 = location_compute_display_column (fc, exploc_finish, policy);
2297 else
2299 start_column = exploc_start.column;
2300 finish_column = exploc_finish.column;
2302 return column_range (start_column, finish_column);
2305 /* Get the range of display columns that would be printed for HINT. */
2307 static column_range
2308 get_printed_columns (file_cache &fc,
2309 const cpp_char_column_policy &policy,
2310 const fixit_hint *hint)
2312 expanded_location exploc = expand_location (hint->get_start_loc ());
2313 int start_column = location_compute_display_column (fc, exploc, policy);
2314 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2315 policy);
2316 int final_hint_column = start_column + hint_width - 1;
2317 if (hint->insertion_p ())
2319 return column_range (start_column, final_hint_column);
2321 else
2323 exploc = expand_location (hint->get_next_loc ());
2324 --exploc.column;
2325 int finish_column = location_compute_display_column (fc, exploc, policy);
2326 return column_range (start_column,
2327 MAX (finish_column, final_hint_column));
2331 /* A correction on a particular line.
2332 This describes a plan for how to print one or more fixit_hint
2333 instances that affected the line, potentially consolidating hints
2334 into corrections to make the result easier for the user to read. */
2336 class correction
2338 public:
2339 correction (column_range affected_bytes,
2340 column_range affected_columns,
2341 column_range printed_columns,
2342 const char *new_text, size_t new_text_len,
2343 const cpp_char_column_policy &policy)
2344 : m_affected_bytes (affected_bytes),
2345 m_affected_columns (affected_columns),
2346 m_printed_columns (printed_columns),
2347 m_text (xstrdup (new_text)),
2348 m_byte_length (new_text_len),
2349 m_policy (policy),
2350 m_alloc_sz (new_text_len + 1)
2352 compute_display_cols ();
2355 ~correction () { free (m_text); }
2357 bool insertion_p () const
2359 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2362 void ensure_capacity (size_t len);
2363 void ensure_terminated ();
2365 void compute_display_cols ()
2367 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2370 void overwrite (int dst_offset, const char_span &src_span)
2372 gcc_assert (dst_offset >= 0);
2373 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2374 memcpy (m_text + dst_offset, src_span.get_buffer (),
2375 src_span.length ());
2378 /* If insert, then start: the column before which the text
2379 is to be inserted, and finish is offset by the length of
2380 the replacement.
2381 If replace, then the range of columns affected. */
2382 column_range m_affected_bytes;
2383 column_range m_affected_columns;
2385 /* If insert, then start: the column before which the text
2386 is to be inserted, and finish is offset by the length of
2387 the replacement.
2388 If replace, then the range of columns affected. */
2389 column_range m_printed_columns;
2391 /* The text to be inserted/used as replacement. */
2392 char *m_text;
2393 size_t m_byte_length; /* Not including null-terminator. */
2394 int m_display_cols;
2395 const cpp_char_column_policy &m_policy;
2396 size_t m_alloc_sz;
2399 /* Ensure that m_text can hold a string of length LEN
2400 (plus 1 for 0-termination). */
2402 void
2403 correction::ensure_capacity (size_t len)
2405 /* Allow 1 extra byte for 0-termination. */
2406 if (m_alloc_sz < (len + 1))
2408 size_t new_alloc_sz = (len + 1) * 2;
2409 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2410 m_alloc_sz = new_alloc_sz;
2414 /* Ensure that m_text is 0-terminated. */
2416 void
2417 correction::ensure_terminated ()
2419 /* 0-terminate the buffer. */
2420 gcc_assert (m_byte_length < m_alloc_sz);
2421 m_text[m_byte_length] = '\0';
2424 /* A list of corrections affecting a particular line.
2425 This is used by layout::print_trailing_fixits for planning
2426 how to print the fix-it hints affecting the line. */
2428 class line_corrections
2430 public:
2431 line_corrections (file_cache &fc,
2432 const char_display_policy &policy,
2433 const char *filename,
2434 linenum_type row)
2435 : m_file_cache (fc),
2436 m_policy (policy), m_filename (filename), m_row (row)
2438 ~line_corrections ();
2440 void add_hint (const fixit_hint *hint);
2442 file_cache &m_file_cache;
2443 const char_display_policy &m_policy;
2444 const char *m_filename;
2445 linenum_type m_row;
2446 auto_vec <correction *> m_corrections;
2449 /* struct line_corrections. */
2451 line_corrections::~line_corrections ()
2453 unsigned i;
2454 correction *c;
2455 FOR_EACH_VEC_ELT (m_corrections, i, c)
2456 delete c;
2459 /* A struct wrapping a particular source line, allowing
2460 run-time bounds-checking of accesses in a checked build. */
2462 class source_line
2464 public:
2465 source_line (file_cache &fc, const char *filename, int line);
2467 char_span as_span () { return char_span (chars, width); }
2469 const char *chars;
2470 int width;
2473 /* source_line's ctor. */
2475 source_line::source_line (file_cache &fc, const char *filename, int line)
2477 char_span span = fc.get_source_line (filename, line);
2478 chars = span.get_buffer ();
2479 width = span.length ();
2482 /* Add HINT to the corrections for this line.
2483 Attempt to consolidate nearby hints so that they will not
2484 overlap with printed. */
2486 void
2487 line_corrections::add_hint (const fixit_hint *hint)
2489 column_range affected_bytes
2490 = get_affected_range (m_file_cache, m_policy, hint, CU_BYTES);
2491 column_range affected_columns
2492 = get_affected_range (m_file_cache, m_policy, hint, CU_DISPLAY_COLS);
2493 column_range printed_columns
2494 = get_printed_columns (m_file_cache, m_policy, hint);
2496 /* Potentially consolidate. */
2497 if (!m_corrections.is_empty ())
2499 correction *last_correction
2500 = m_corrections[m_corrections.length () - 1];
2502 /* The following consolidation code assumes that the fix-it hints
2503 have been sorted by start (done within layout's ctor). */
2504 gcc_assert (affected_bytes.start
2505 >= last_correction->m_affected_bytes.start);
2506 gcc_assert (printed_columns.start
2507 >= last_correction->m_printed_columns.start);
2509 if (printed_columns.start <= last_correction->m_printed_columns.finish
2510 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2511 affected_bytes.start - 1))
2513 /* We have two hints for which the printed forms of the hints
2514 would touch or overlap, so we need to consolidate them to avoid
2515 confusing the user.
2516 Attempt to inject a "replace" correction from immediately
2517 after the end of the last hint to immediately before the start
2518 of the next hint. */
2519 column_range between (last_correction->m_affected_bytes.finish + 1,
2520 affected_bytes.start - 1);
2522 /* Try to read the source. */
2523 source_line line (m_file_cache, m_filename, m_row);
2524 if (line.chars && between.finish < line.width)
2526 /* Consolidate into the last correction:
2527 add a no-op "replace" of the "between" text, and
2528 add the text from the new hint. */
2529 int old_byte_len = last_correction->m_byte_length;
2530 gcc_assert (old_byte_len >= 0);
2531 int between_byte_len = between.finish + 1 - between.start;
2532 gcc_assert (between_byte_len >= 0);
2533 int new_byte_len
2534 = old_byte_len + between_byte_len + hint->get_length ();
2535 gcc_assert (new_byte_len >= 0);
2536 last_correction->ensure_capacity (new_byte_len);
2537 last_correction->overwrite
2538 (old_byte_len,
2539 line.as_span ().subspan (between.start - 1,
2540 between.finish + 1 - between.start));
2541 last_correction->overwrite (old_byte_len + between_byte_len,
2542 char_span (hint->get_string (),
2543 hint->get_length ()));
2544 last_correction->m_byte_length = new_byte_len;
2545 last_correction->ensure_terminated ();
2546 last_correction->m_affected_bytes.finish
2547 = affected_bytes.finish;
2548 last_correction->m_affected_columns.finish
2549 = affected_columns.finish;
2550 int prev_display_cols = last_correction->m_display_cols;
2551 last_correction->compute_display_cols ();
2552 last_correction->m_printed_columns.finish
2553 += last_correction->m_display_cols - prev_display_cols;
2554 return;
2559 /* If no consolidation happened, add a new correction instance. */
2560 m_corrections.safe_push (new correction (affected_bytes,
2561 affected_columns,
2562 printed_columns,
2563 hint->get_string (),
2564 hint->get_length (),
2565 m_policy));
2568 /* If there are any fixit hints on source line ROW, print them.
2569 They are printed in order, attempting to combine them onto lines, but
2570 starting new lines if necessary.
2571 Fix-it hints that insert new lines are handled separately,
2572 in layout::print_leading_fixits. */
2574 void
2575 layout::print_trailing_fixits (linenum_type row)
2577 /* Build a list of correction instances for the line,
2578 potentially consolidating hints (for the sake of readability). */
2579 line_corrections corrections (m_file_cache, m_policy, m_exploc.file, row);
2580 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2582 const fixit_hint *hint = m_fixit_hints[i];
2584 /* Newline fixits are handled by layout::print_leading_fixits. */
2585 if (hint->ends_with_newline_p ())
2586 continue;
2588 if (hint->affects_line_p (m_line_table, m_exploc.file, row))
2589 corrections.add_hint (hint);
2592 /* Now print the corrections. */
2593 unsigned i;
2594 correction *c;
2595 int column = m_x_offset_display;
2597 if (!corrections.m_corrections.is_empty ())
2598 start_annotation_line ();
2600 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2602 /* For now we assume each fixit hint can only touch one line. */
2603 if (c->insertion_p ())
2605 /* This assumes the insertion just affects one line. */
2606 int start_column = c->m_printed_columns.start;
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;
2613 else
2615 /* If the range of the replacement wasn't printed in the
2616 annotation line, then print an extra underline to
2617 indicate exactly what is being replaced.
2618 Always show it for removals. */
2619 int start_column = c->m_affected_columns.start;
2620 int finish_column = c->m_affected_columns.finish;
2621 if (!annotation_line_showed_range_p (row, start_column,
2622 finish_column)
2623 || c->m_byte_length == 0)
2625 move_to_column (&column, start_column, true);
2626 m_colorizer.set_fixit_delete ();
2627 for (; column <= finish_column; column++)
2628 pp_character (m_pp, '-');
2629 m_colorizer.set_normal_text ();
2631 /* Print the replacement text. REPLACE also covers
2632 removals, so only do this extra work (potentially starting
2633 a new line) if we have actual replacement text. */
2634 if (c->m_byte_length > 0)
2636 move_to_column (&column, start_column, true);
2637 m_colorizer.set_fixit_insert ();
2638 pp_string (m_pp, c->m_text);
2639 m_colorizer.set_normal_text ();
2640 column += c->m_display_cols;
2645 /* Add a trailing newline, if necessary. */
2646 move_to_column (&column, 0, false);
2649 /* Disable any colorization and emit a newline. */
2651 void
2652 layout::print_newline ()
2654 m_colorizer.set_normal_text ();
2655 pp_newline (m_pp);
2658 /* Return true if (ROW/COLUMN) is within a range of the layout.
2659 If it returns true, OUT_STATE is written to, with the
2660 range index, and whether we should draw the caret at
2661 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2662 whether all inputs and outputs are in bytes or display column units. */
2664 bool
2665 layout::get_state_at_point (/* Inputs. */
2666 linenum_type row, int column,
2667 int first_non_ws, int last_non_ws,
2668 enum column_unit col_unit,
2669 /* Outputs. */
2670 point_state *out_state)
2672 layout_range *range;
2673 int i;
2674 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2676 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2677 /* Bail out early, so that such ranges don't affect underlining or
2678 source colorization. */
2679 continue;
2681 if (range->contains_point (row, column, col_unit))
2683 out_state->range_idx = i;
2685 /* Are we at the range's caret? is it visible? */
2686 out_state->draw_caret_p = false;
2687 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2688 && row == range->m_caret.m_line
2689 && column == range->m_caret.m_columns[col_unit])
2690 out_state->draw_caret_p = true;
2692 /* Within a multiline range, don't display any underline
2693 in any leading or trailing whitespace on a line.
2694 We do display carets, however. */
2695 if (!out_state->draw_caret_p)
2696 if (column < first_non_ws || column > last_non_ws)
2697 return false;
2699 /* We are within a range. */
2700 return true;
2704 return false;
2707 /* Helper function for use by layout::print_line when printing the
2708 annotation line under the source line.
2709 Get the display column beyond the rightmost one that could contain a caret
2710 or range marker, given that we stop rendering at trailing whitespace.
2711 ROW is the source line within the given file.
2712 CARET_COLUMN is the display column of range 0's caret.
2713 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2714 character of source (as determined when printing the source line). */
2717 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2718 int last_non_ws_column)
2720 int result = caret_column + 1;
2722 layout_range *range;
2723 int i;
2724 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2726 if (row >= range->m_start.m_line)
2728 if (range->m_finish.m_line == row)
2730 /* On the final line within a range; ensure that
2731 we render up to the end of the range. */
2732 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2733 if (result <= disp_col)
2734 result = disp_col + 1;
2736 else if (row < range->m_finish.m_line)
2738 /* Within a multiline range; ensure that we render up to the
2739 last non-whitespace column. */
2740 if (result <= last_non_ws_column)
2741 result = last_non_ws_column + 1;
2746 return result;
2749 /* Given *COLUMN as an x-coordinate, print spaces to position
2750 successive output at DEST_COLUMN, printing a newline if necessary,
2751 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2752 left margin after any newline. */
2754 void
2755 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2757 /* Start a new line if we need to. */
2758 if (*column > dest_column)
2760 print_newline ();
2761 if (add_left_margin)
2762 start_annotation_line ();
2763 *column = m_x_offset_display;
2766 while (*column < dest_column)
2768 pp_space (m_pp);
2769 (*column)++;
2773 /* For debugging layout issues, render a ruler giving column numbers
2774 (after the 1-column indent). */
2776 void
2777 layout::show_ruler (int max_column) const
2779 /* Hundreds. */
2780 if (max_column > 99)
2782 start_annotation_line ();
2783 pp_space (m_pp);
2784 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2785 if (column % 10 == 0)
2786 pp_character (m_pp, '0' + (column / 100) % 10);
2787 else
2788 pp_space (m_pp);
2789 pp_newline (m_pp);
2792 /* Tens. */
2793 start_annotation_line ();
2794 pp_space (m_pp);
2795 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2796 if (column % 10 == 0)
2797 pp_character (m_pp, '0' + (column / 10) % 10);
2798 else
2799 pp_space (m_pp);
2800 pp_newline (m_pp);
2802 /* Units. */
2803 start_annotation_line ();
2804 pp_space (m_pp);
2805 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2806 pp_character (m_pp, '0' + (column % 10));
2807 pp_newline (m_pp);
2810 /* Print leading fix-its (for new lines inserted before the source line)
2811 then the source line, followed by an annotation line
2812 consisting of any caret/underlines, then any fixits.
2813 If the source line can't be read, print nothing. */
2814 void
2815 layout::print_line (linenum_type row)
2817 char_span line = m_file_cache.get_source_line (m_exploc.file, row);
2818 if (!line)
2819 return;
2821 print_leading_fixits (row);
2822 const line_bounds lbounds
2823 = print_source_line (row, line.get_buffer (), line.length ());
2824 if (should_print_annotation_line_p (row))
2825 print_annotation_line (row, lbounds);
2826 if (m_options.show_labels_p)
2827 print_any_labels (row);
2828 print_trailing_fixits (row);
2831 } /* End of anonymous namespace. */
2833 /* If LOC is within the spans of lines that will already be printed for
2834 this gcc_rich_location, then add it as a secondary location and return true.
2836 Otherwise return false. */
2838 bool
2839 gcc_rich_location::add_location_if_nearby (location_t loc,
2840 bool restrict_to_current_line_spans,
2841 const range_label *label)
2843 /* Use the layout location-handling logic to sanitize LOC,
2844 filtering it to the current line spans within a temporary
2845 layout instance. */
2846 layout layout (*global_dc, *this, DK_ERROR, nullptr);
2847 location_range loc_range;
2848 loc_range.m_loc = loc;
2849 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2850 if (!layout.maybe_add_location_range (&loc_range, 0,
2851 restrict_to_current_line_spans))
2852 return false;
2854 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2855 return true;
2858 /* As per diagnostic_context::show_locus, but don't print anything
2859 if source printing is disabled, or if the location hasn't changed. */
2861 void
2862 diagnostic_context::maybe_show_locus (const rich_location &richloc,
2863 diagnostic_t diagnostic_kind,
2864 pretty_printer *pp)
2866 const location_t loc = richloc.get_loc ();
2867 /* Do nothing if source-printing has been disabled. */
2868 if (!m_source_printing.enabled)
2869 return;
2871 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2872 if (loc <= BUILTINS_LOCATION)
2873 return;
2875 /* Don't print the same source location twice in a row, unless we have
2876 fix-it hints, or multiple locations, or a label. */
2877 if (loc == m_last_location
2878 && richloc.get_num_fixit_hints () == 0
2879 && richloc.get_num_locations () == 1
2880 && richloc.get_range (0)->m_label == NULL)
2881 return;
2883 m_last_location = loc;
2885 show_locus (richloc, diagnostic_kind, pp);
2888 /* Print the physical source code corresponding to the location of
2889 this diagnostic, with additional annotations.
2890 If PP is non-null, then use it rather than this context's printer. */
2892 void
2893 diagnostic_context::show_locus (const rich_location &richloc,
2894 diagnostic_t diagnostic_kind,
2895 pretty_printer *pp)
2897 layout layout (*this, richloc, diagnostic_kind, pp);
2898 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2899 line_span_idx++)
2901 const line_span *line_span = layout.get_line_span (line_span_idx);
2902 if (m_source_printing.show_line_numbers_p)
2904 /* With line numbers, we should show whenever the line-numbering
2905 "jumps". */
2906 if (line_span_idx > 0)
2907 layout.print_gap_in_line_numbering ();
2909 else
2911 /* Without line numbers, we print headings for some line spans. */
2912 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2914 expanded_location exploc
2915 = layout.get_expanded_location (line_span);
2916 m_text_callbacks.m_start_span (this, exploc);
2919 /* Iterate over the lines within this span (using linenum_arith_t to
2920 avoid overflow with 0xffffffff causing an infinite loop). */
2921 linenum_arith_t last_line = line_span->get_last_line ();
2922 for (linenum_arith_t row = line_span->get_first_line ();
2923 row <= last_line; row++)
2924 layout.print_line (row);
2928 #if CHECKING_P
2930 namespace selftest {
2932 /* Selftests for diagnostic_show_locus. */
2934 /* Verify that cpp_display_width correctly handles escaping. */
2936 static void
2937 test_display_widths ()
2939 gcc_rich_location richloc (UNKNOWN_LOCATION);
2941 /* U+03C0 "GREEK SMALL LETTER PI". */
2942 const char *pi = "\xCF\x80";
2943 /* U+1F642 "SLIGHTLY SMILING FACE". */
2944 const char *emoji = "\xF0\x9F\x99\x82";
2945 /* Stray trailing byte of a UTF-8 character. */
2946 const char *stray = "\xBF";
2947 /* U+10FFFF. */
2948 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2950 /* No escaping. */
2952 test_diagnostic_context dc;
2953 char_display_policy policy (make_policy (dc, richloc));
2954 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2955 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2956 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2957 /* Don't check width of U+10FFFF; it's in a private use plane. */
2960 richloc.set_escape_on_output (true);
2963 test_diagnostic_context dc;
2964 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
2965 char_display_policy policy (make_policy (dc, richloc));
2966 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2967 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2968 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2969 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2970 policy),
2971 strlen ("<U+10FFFF>"));
2975 test_diagnostic_context dc;
2976 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
2977 char_display_policy policy (make_policy (dc, richloc));
2978 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2979 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2980 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2981 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2982 policy),
2983 16);
2987 /* For precise tests of the layout, make clear where the source line will
2988 start. test_left_margin sets the total byte count from the left side of the
2989 screen to the start of source lines, after the line number and the separator,
2990 which consists of the three characters " | ". */
2991 static const int test_linenum_sep = 3;
2992 static const int test_left_margin = 7;
2994 /* Helper function for test_layout_x_offset_display_utf8(). */
2995 static void
2996 test_offset_impl (int caret_byte_col, int max_width,
2997 int expected_x_offset_display,
2998 int left_margin = test_left_margin)
3000 test_diagnostic_context dc;
3001 dc.m_source_printing.max_width = max_width;
3002 /* diagnostic_context::min_margin_width sets the minimum space reserved for
3003 the line number plus one space after. */
3004 dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
3005 dc.m_source_printing.show_line_numbers_p = true;
3006 rich_location richloc (line_table,
3007 linemap_position_for_column (line_table,
3008 caret_byte_col));
3009 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3010 ASSERT_EQ (left_margin - test_linenum_sep,
3011 test_layout.get_linenum_width ());
3012 ASSERT_EQ (expected_x_offset_display,
3013 test_layout.get_x_offset_display ());
3016 /* Test that layout::calculate_x_offset_display() works. */
3017 static void
3018 test_layout_x_offset_display_utf8 (const line_table_case &case_)
3021 const char *content
3022 = "This line is very long, so that we can use it to test the logic for "
3023 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
3024 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
3025 "column #102.\n";
3027 /* Number of bytes in the line, subtracting one to remove the newline. */
3028 const int line_bytes = strlen (content) - 1;
3030 /* Number of display columns occupied by the line; each of the 2 emojis
3031 takes up 2 fewer display columns than it does bytes. */
3032 const int line_display_cols = line_bytes - 2*2;
3034 /* The column of the first emoji. Byte or display is the same as there are
3035 no multibyte characters earlier on the line. */
3036 const int emoji_col = 102;
3038 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3039 file_cache fc;
3040 line_table_test ltt (case_);
3042 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3044 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3046 /* Don't attempt to run the tests if column data might be unavailable. */
3047 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3048 return;
3050 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3051 ASSERT_EQ (1, LOCATION_LINE (line_end));
3052 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3054 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
3055 ASSERT_EQ (line_display_cols,
3056 cpp_display_width (lspan.get_buffer (), lspan.length (),
3057 def_policy ()));
3058 ASSERT_EQ (line_display_cols,
3059 location_compute_display_column (fc,
3060 expand_location (line_end),
3061 def_policy ()));
3062 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3063 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3065 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3067 /* No constraint on the width -> no offset. */
3068 test_offset_impl (emoji_col, 0, 0);
3070 /* Caret is before the beginning -> no offset. */
3071 test_offset_impl (0, 100, 0);
3073 /* Caret is past the end of the line -> no offset. */
3074 test_offset_impl (line_bytes+1, 100, 0);
3076 /* Line fits in the display -> no offset. */
3077 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3078 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3080 /* Line is too long for the display but caret location is OK
3081 anyway -> no offset. */
3082 static const int small_width = 24;
3083 test_offset_impl (1, small_width, 0);
3085 /* Width constraint is very small -> no offset. */
3086 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3088 /* Line would be offset, but due to large line numbers, offsetting
3089 would remove the whole line -> no offset. */
3090 static const int huge_left_margin = 100;
3091 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3093 /* Line is the same length as the display, but the line number makes it too
3094 long, so offset is required. Caret is at the end so padding on the right
3095 is not in effect. */
3096 for (int excess = 1; excess <= 3; ++excess)
3097 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3098 excess);
3100 /* Line is much too long for the display, caret is near the end ->
3101 offset should be such that the line fits in the display and caret
3102 remains the same distance from the end that it was. */
3103 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3104 caret_offset <= max_offset; ++caret_offset)
3105 test_offset_impl (line_bytes - caret_offset, small_width,
3106 line_display_cols + test_left_margin - small_width);
3108 /* As previous case but caret is closer to the middle; now we want it to end
3109 up CARET_LINE_MARGIN bytes from the end. */
3110 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3111 test_offset_impl (emoji_col, small_width,
3112 emoji_col + test_left_margin
3113 - (small_width - CARET_LINE_MARGIN));
3115 /* Test that the source line is offset as expected when printed. */
3117 test_diagnostic_context dc;
3118 dc.m_source_printing.max_width = small_width - 6;
3119 dc.m_source_printing.min_margin_width
3120 = test_left_margin - test_linenum_sep + 1;
3121 dc.m_source_printing.show_line_numbers_p = true;
3122 dc.m_source_printing.show_ruler_p = true;
3123 rich_location richloc (line_table,
3124 linemap_position_for_column (line_table,
3125 emoji_col));
3126 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3127 test_layout.print_line (1);
3128 ASSERT_STREQ (" | 1 \n"
3129 " | 1 \n"
3130 " | 234567890123456789\n"
3131 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3132 "that occupies 8 bytes and 4 display columns, starting at "
3133 "column #102.\n"
3134 " | ^\n\n",
3135 pp_formatted_text (dc.printer));
3138 /* Similar to the previous example, but now the offset called for would split
3139 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3140 it with a padding space in this case. */
3142 test_diagnostic_context dc;
3143 dc.m_source_printing.max_width = small_width - 5;
3144 dc.m_source_printing.min_margin_width
3145 = test_left_margin - test_linenum_sep + 1;
3146 dc.m_source_printing.show_line_numbers_p = true;
3147 dc.m_source_printing.show_ruler_p = true;
3148 rich_location richloc (line_table,
3149 linemap_position_for_column (line_table,
3150 emoji_col + 2));
3151 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3152 test_layout.print_line (1);
3153 ASSERT_STREQ (" | 1 1 \n"
3154 " | 1 2 \n"
3155 " | 3456789012345678901\n"
3156 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3157 "that occupies 8 bytes and 4 display columns, starting at "
3158 "column #102.\n"
3159 " | ^\n\n",
3160 pp_formatted_text (dc.printer));
3165 static void
3166 test_layout_x_offset_display_tab (const line_table_case &case_)
3168 const char *content
3169 = "This line is very long, so that we can use it to test the logic for "
3170 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3171 "a variable number of display columns, starting at column #103.\n";
3173 /* Number of bytes in the line, subtracting one to remove the newline. */
3174 const int line_bytes = strlen (content) - 1;
3176 /* The column where the tab begins. Byte or display is the same as there are
3177 no multibyte characters earlier on the line. */
3178 const int tab_col = 103;
3180 /* Effective extra size of the tab beyond what a single space would have taken
3181 up, indexed by tabstop. */
3182 static const int num_tabstops = 11;
3183 int extra_width[num_tabstops];
3184 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3186 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3187 extra_width[tabstop] = this_tab_size - 1;
3189 /* Example of this calculation: if tabstop is 10, the tab starting at column
3190 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3191 next character is at column #111. So it takes up 7 more columns than
3192 a space would have taken up. */
3193 ASSERT_EQ (7, extra_width[10]);
3195 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3196 file_cache fc;
3197 line_table_test ltt (case_);
3199 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3201 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3203 /* Don't attempt to run the tests if column data might be unavailable. */
3204 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3205 return;
3207 /* Check that cpp_display_width handles the tabs as expected. */
3208 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
3209 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3210 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3212 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3213 ASSERT_EQ (line_bytes + extra_width[tabstop],
3214 cpp_display_width (lspan.get_buffer (), lspan.length (),
3215 policy));
3216 ASSERT_EQ (line_bytes + extra_width[tabstop],
3217 location_compute_display_column (fc,
3218 expand_location (line_end),
3219 policy));
3222 /* Check that the tab is expanded to the expected number of spaces. */
3223 rich_location richloc (line_table,
3224 linemap_position_for_column (line_table,
3225 tab_col + 1));
3226 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3228 test_diagnostic_context dc;
3229 dc.m_tabstop = tabstop;
3230 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3231 test_layout.print_line (1);
3232 const char *out = pp_formatted_text (dc.printer);
3233 ASSERT_EQ (NULL, strchr (out, '\t'));
3234 const char *left_quote = strchr (out, '`');
3235 const char *right_quote = strchr (out, '\'');
3236 ASSERT_NE (NULL, left_quote);
3237 ASSERT_NE (NULL, right_quote);
3238 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3241 /* Check that the line is offset properly and that the tab is broken up
3242 into the expected number of spaces when it is the last character skipped
3243 over. */
3244 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3246 test_diagnostic_context dc;
3247 dc.m_tabstop = tabstop;
3248 static const int small_width = 24;
3249 dc.m_source_printing.max_width = small_width - 4;
3250 dc.m_source_printing.min_margin_width
3251 = test_left_margin - test_linenum_sep + 1;
3252 dc.m_source_printing.show_line_numbers_p = true;
3253 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3254 test_layout.print_line (1);
3256 /* We have arranged things so that two columns will be printed before
3257 the caret. If the tab results in more than one space, this should
3258 produce two spaces in the output; otherwise, it will be a single space
3259 preceded by the opening quote before the tab character. */
3260 const char *output1
3261 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3262 "display columns, starting at column #103.\n"
3263 " | ^\n\n";
3264 const char *output2
3265 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3266 "display columns, starting at column #103.\n"
3267 " | ^\n\n";
3268 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3269 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3274 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3276 static void
3277 test_diagnostic_show_locus_unknown_location ()
3279 test_diagnostic_context dc;
3280 rich_location richloc (line_table, UNKNOWN_LOCATION);
3281 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3282 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3285 /* Verify that diagnostic_show_locus works sanely for various
3286 single-line cases.
3288 All of these work on the following 1-line source file:
3289 .0000000001111111
3290 .1234567890123456
3291 "foo = bar.field;\n"
3292 which is set up by test_diagnostic_show_locus_one_liner and calls
3293 them. */
3295 /* Just a caret. */
3297 static void
3298 test_one_liner_simple_caret ()
3300 test_diagnostic_context dc;
3301 location_t caret = linemap_position_for_column (line_table, 10);
3302 rich_location richloc (line_table, caret);
3303 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3304 ASSERT_STREQ (" foo = bar.field;\n"
3305 " ^\n",
3306 pp_formatted_text (dc.printer));
3309 /* No column information (column == 0).
3310 No annotation line should be printed. */
3312 static void
3313 test_one_liner_no_column ()
3315 test_diagnostic_context dc;
3316 location_t caret = linemap_position_for_column (line_table, 0);
3317 rich_location richloc (line_table, caret);
3318 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3319 ASSERT_STREQ (" foo = bar.field;\n",
3320 pp_formatted_text (dc.printer));
3323 /* Caret and range. */
3325 static void
3326 test_one_liner_caret_and_range ()
3328 test_diagnostic_context dc;
3329 location_t caret = linemap_position_for_column (line_table, 10);
3330 location_t start = linemap_position_for_column (line_table, 7);
3331 location_t finish = linemap_position_for_column (line_table, 15);
3332 location_t loc = make_location (caret, start, finish);
3333 rich_location richloc (line_table, loc);
3334 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3335 ASSERT_STREQ (" foo = bar.field;\n"
3336 " ~~~^~~~~~\n",
3337 pp_formatted_text (dc.printer));
3340 /* Multiple ranges and carets. */
3342 static void
3343 test_one_liner_multiple_carets_and_ranges ()
3345 test_diagnostic_context dc;
3346 location_t foo
3347 = make_location (linemap_position_for_column (line_table, 2),
3348 linemap_position_for_column (line_table, 1),
3349 linemap_position_for_column (line_table, 3));
3350 dc.m_source_printing.caret_chars[0] = 'A';
3352 location_t bar
3353 = make_location (linemap_position_for_column (line_table, 8),
3354 linemap_position_for_column (line_table, 7),
3355 linemap_position_for_column (line_table, 9));
3356 dc.m_source_printing.caret_chars[1] = 'B';
3358 location_t field
3359 = make_location (linemap_position_for_column (line_table, 13),
3360 linemap_position_for_column (line_table, 11),
3361 linemap_position_for_column (line_table, 15));
3362 dc.m_source_printing.caret_chars[2] = 'C';
3364 rich_location richloc (line_table, foo);
3365 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3366 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3367 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3368 ASSERT_STREQ (" foo = bar.field;\n"
3369 " ~A~ ~B~ ~~C~~\n",
3370 pp_formatted_text (dc.printer));
3373 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3375 static void
3376 test_one_liner_fixit_insert_before ()
3378 test_diagnostic_context dc;
3379 location_t caret = linemap_position_for_column (line_table, 7);
3380 rich_location richloc (line_table, caret);
3381 richloc.add_fixit_insert_before ("&");
3382 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3383 ASSERT_STREQ (" foo = bar.field;\n"
3384 " ^\n"
3385 " &\n",
3386 pp_formatted_text (dc.printer));
3389 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3391 static void
3392 test_one_liner_fixit_insert_after ()
3394 test_diagnostic_context dc;
3395 location_t start = linemap_position_for_column (line_table, 1);
3396 location_t finish = linemap_position_for_column (line_table, 3);
3397 location_t foo = make_location (start, start, finish);
3398 rich_location richloc (line_table, foo);
3399 richloc.add_fixit_insert_after ("[0]");
3400 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3401 ASSERT_STREQ (" foo = bar.field;\n"
3402 " ^~~\n"
3403 " [0]\n",
3404 pp_formatted_text (dc.printer));
3407 /* Removal fix-it hint: removal of the ".field".
3408 Also verify the interaction of pp_set_prefix with rulers and
3409 fix-it hints. */
3411 static void
3412 test_one_liner_fixit_remove ()
3414 location_t start = linemap_position_for_column (line_table, 10);
3415 location_t finish = linemap_position_for_column (line_table, 15);
3416 location_t dot = make_location (start, start, finish);
3417 rich_location richloc (line_table, dot);
3418 richloc.add_fixit_remove ();
3420 /* Normal. */
3422 test_diagnostic_context dc;
3423 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3424 ASSERT_STREQ (" foo = bar.field;\n"
3425 " ^~~~~~\n"
3426 " ------\n",
3427 pp_formatted_text (dc.printer));
3430 /* Test of adding a prefix. */
3432 test_diagnostic_context dc;
3433 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3434 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3435 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3436 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3437 "TEST PREFIX: ^~~~~~\n"
3438 "TEST PREFIX: ------\n",
3439 pp_formatted_text (dc.printer));
3442 /* Normal, with ruler. */
3444 test_diagnostic_context dc;
3445 dc.m_source_printing.show_ruler_p = true;
3446 dc.m_source_printing.max_width = 104;
3447 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3448 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3449 " 1 2 3 4 5 6 7 8 9 0 \n"
3450 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3451 " foo = bar.field;\n"
3452 " ^~~~~~\n"
3453 " ------\n",
3454 pp_formatted_text (dc.printer));
3457 /* Test of adding a prefix, with ruler. */
3459 test_diagnostic_context dc;
3460 dc.m_source_printing.show_ruler_p = true;
3461 dc.m_source_printing.max_width = 50;
3462 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3463 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3464 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3465 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3466 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3467 "TEST PREFIX: foo = bar.field;\n"
3468 "TEST PREFIX: ^~~~~~\n"
3469 "TEST PREFIX: ------\n",
3470 pp_formatted_text (dc.printer));
3473 /* Test of adding a prefix, with ruler and line numbers. */
3475 test_diagnostic_context dc;
3476 dc.m_source_printing.show_ruler_p = true;
3477 dc.m_source_printing.max_width = 50;
3478 dc.m_source_printing.show_line_numbers_p = true;
3479 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3480 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3481 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3482 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3483 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3484 "TEST PREFIX: 1 | foo = bar.field;\n"
3485 "TEST PREFIX: | ^~~~~~\n"
3486 "TEST PREFIX: | ------\n",
3487 pp_formatted_text (dc.printer));
3491 /* Replace fix-it hint: replacing "field" with "m_field". */
3493 static void
3494 test_one_liner_fixit_replace ()
3496 test_diagnostic_context dc;
3497 location_t start = linemap_position_for_column (line_table, 11);
3498 location_t finish = linemap_position_for_column (line_table, 15);
3499 location_t field = make_location (start, start, finish);
3500 rich_location richloc (line_table, field);
3501 richloc.add_fixit_replace ("m_field");
3502 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3503 ASSERT_STREQ (" foo = bar.field;\n"
3504 " ^~~~~\n"
3505 " m_field\n",
3506 pp_formatted_text (dc.printer));
3509 /* Replace fix-it hint: replacing "field" with "m_field",
3510 but where the caret was elsewhere. */
3512 static void
3513 test_one_liner_fixit_replace_non_equal_range ()
3515 test_diagnostic_context dc;
3516 location_t equals = linemap_position_for_column (line_table, 5);
3517 location_t start = linemap_position_for_column (line_table, 11);
3518 location_t finish = linemap_position_for_column (line_table, 15);
3519 rich_location richloc (line_table, equals);
3520 source_range range;
3521 range.m_start = start;
3522 range.m_finish = finish;
3523 richloc.add_fixit_replace (range, "m_field");
3524 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3525 /* The replacement range is not indicated in the annotation line, so
3526 it should be indicated via an additional underline. */
3527 ASSERT_STREQ (" foo = bar.field;\n"
3528 " ^\n"
3529 " -----\n"
3530 " m_field\n",
3531 pp_formatted_text (dc.printer));
3534 /* Replace fix-it hint: replacing "field" with "m_field",
3535 where the caret was elsewhere, but where a secondary range
3536 exactly covers "field". */
3538 static void
3539 test_one_liner_fixit_replace_equal_secondary_range ()
3541 test_diagnostic_context dc;
3542 location_t equals = linemap_position_for_column (line_table, 5);
3543 location_t start = linemap_position_for_column (line_table, 11);
3544 location_t finish = linemap_position_for_column (line_table, 15);
3545 rich_location richloc (line_table, equals);
3546 location_t field = make_location (start, start, finish);
3547 richloc.add_range (field);
3548 richloc.add_fixit_replace (field, "m_field");
3549 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3550 /* The replacement range is indicated in the annotation line,
3551 so it shouldn't be indicated via an additional underline. */
3552 ASSERT_STREQ (" foo = bar.field;\n"
3553 " ^ ~~~~~\n"
3554 " m_field\n",
3555 pp_formatted_text (dc.printer));
3558 /* Verify that we can use ad-hoc locations when adding fixits to a
3559 rich_location. */
3561 static void
3562 test_one_liner_fixit_validation_adhoc_locations ()
3564 /* Generate a range that's too long to be packed, so must
3565 be stored as an ad-hoc location (given the defaults
3566 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3567 const location_t c7 = linemap_position_for_column (line_table, 7);
3568 const location_t c47 = linemap_position_for_column (line_table, 47);
3569 const location_t loc = make_location (c7, c7, c47);
3571 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3572 return;
3574 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3576 /* Insert. */
3578 rich_location richloc (line_table, loc);
3579 richloc.add_fixit_insert_before (loc, "test");
3580 /* It should not have been discarded by the validator. */
3581 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3583 test_diagnostic_context dc;
3584 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3585 ASSERT_STREQ (" foo = bar.field;\n"
3586 " ^~~~~~~~~~ \n"
3587 " test\n",
3588 pp_formatted_text (dc.printer));
3591 /* Remove. */
3593 rich_location richloc (line_table, loc);
3594 source_range range = source_range::from_locations (loc, c47);
3595 richloc.add_fixit_remove (range);
3596 /* It should not have been discarded by the validator. */
3597 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3599 test_diagnostic_context dc;
3600 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3601 ASSERT_STREQ (" foo = bar.field;\n"
3602 " ^~~~~~~~~~ \n"
3603 " -----------------------------------------\n",
3604 pp_formatted_text (dc.printer));
3607 /* Replace. */
3609 rich_location richloc (line_table, loc);
3610 source_range range = source_range::from_locations (loc, c47);
3611 richloc.add_fixit_replace (range, "test");
3612 /* It should not have been discarded by the validator. */
3613 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3615 test_diagnostic_context dc;
3616 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3617 ASSERT_STREQ (" foo = bar.field;\n"
3618 " ^~~~~~~~~~ \n"
3619 " test\n",
3620 pp_formatted_text (dc.printer));
3624 /* Test of consolidating insertions at the same location. */
3626 static void
3627 test_one_liner_many_fixits_1 ()
3629 test_diagnostic_context dc;
3630 location_t equals = linemap_position_for_column (line_table, 5);
3631 rich_location richloc (line_table, equals);
3632 for (int i = 0; i < 19; i++)
3633 richloc.add_fixit_insert_before ("a");
3634 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3635 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3636 ASSERT_STREQ (" foo = bar.field;\n"
3637 " ^\n"
3638 " aaaaaaaaaaaaaaaaaaa\n",
3639 pp_formatted_text (dc.printer));
3642 /* Ensure that we can add an arbitrary number of fix-it hints to a
3643 rich_location, even if they are not consolidated. */
3645 static void
3646 test_one_liner_many_fixits_2 ()
3648 test_diagnostic_context dc;
3649 location_t equals = linemap_position_for_column (line_table, 5);
3650 rich_location richloc (line_table, equals);
3651 for (int i = 0; i < 19; i++)
3653 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3654 richloc.add_fixit_insert_before (loc, "a");
3656 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3657 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3658 ASSERT_STREQ (" foo = bar.field;\n"
3659 " ^\n"
3660 " a a a a a a a a a a a a a a a a a a a\n",
3661 pp_formatted_text (dc.printer));
3664 /* Test of labeling the ranges within a rich_location. */
3666 static void
3667 test_one_liner_labels ()
3669 location_t foo
3670 = make_location (linemap_position_for_column (line_table, 1),
3671 linemap_position_for_column (line_table, 1),
3672 linemap_position_for_column (line_table, 3));
3673 location_t bar
3674 = make_location (linemap_position_for_column (line_table, 7),
3675 linemap_position_for_column (line_table, 7),
3676 linemap_position_for_column (line_table, 9));
3677 location_t field
3678 = make_location (linemap_position_for_column (line_table, 11),
3679 linemap_position_for_column (line_table, 11),
3680 linemap_position_for_column (line_table, 15));
3682 /* Example where all the labels fit on one line. */
3684 text_range_label label0 ("0");
3685 text_range_label label1 ("1");
3686 text_range_label label2 ("2");
3687 gcc_rich_location richloc (foo, &label0);
3688 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3689 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3692 test_diagnostic_context dc;
3693 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3694 ASSERT_STREQ (" foo = bar.field;\n"
3695 " ^~~ ~~~ ~~~~~\n"
3696 " | | |\n"
3697 " 0 1 2\n",
3698 pp_formatted_text (dc.printer));
3701 /* Verify that we can disable label-printing. */
3703 test_diagnostic_context dc;
3704 dc.m_source_printing.show_labels_p = false;
3705 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3706 ASSERT_STREQ (" foo = bar.field;\n"
3707 " ^~~ ~~~ ~~~~~\n",
3708 pp_formatted_text (dc.printer));
3712 /* Example where the labels need extra lines. */
3714 text_range_label label0 ("label 0");
3715 text_range_label label1 ("label 1");
3716 text_range_label label2 ("label 2");
3717 gcc_rich_location richloc (foo, &label0);
3718 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3719 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3721 test_diagnostic_context dc;
3722 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3723 ASSERT_STREQ (" foo = bar.field;\n"
3724 " ^~~ ~~~ ~~~~~\n"
3725 " | | |\n"
3726 " | | label 2\n"
3727 " | label 1\n"
3728 " label 0\n",
3729 pp_formatted_text (dc.printer));
3732 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3733 but label 1 just touches label 2. */
3735 text_range_label label0 ("aaaaa");
3736 text_range_label label1 ("bbbb");
3737 text_range_label label2 ("c");
3738 gcc_rich_location richloc (foo, &label0);
3739 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3740 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3742 test_diagnostic_context dc;
3743 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3744 ASSERT_STREQ (" foo = bar.field;\n"
3745 " ^~~ ~~~ ~~~~~\n"
3746 " | | |\n"
3747 " | | c\n"
3748 " aaaaa bbbb\n",
3749 pp_formatted_text (dc.printer));
3752 /* Example of out-of-order ranges (thus requiring a sort). */
3754 text_range_label label0 ("0");
3755 text_range_label label1 ("1");
3756 text_range_label label2 ("2");
3757 gcc_rich_location richloc (field, &label0);
3758 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3759 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3761 test_diagnostic_context dc;
3762 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3763 ASSERT_STREQ (" foo = bar.field;\n"
3764 " ~~~ ~~~ ^~~~~\n"
3765 " | | |\n"
3766 " 2 1 0\n",
3767 pp_formatted_text (dc.printer));
3770 /* Ensure we don't ICE if multiple ranges with labels are on
3771 the same point. */
3773 text_range_label label0 ("label 0");
3774 text_range_label label1 ("label 1");
3775 text_range_label label2 ("label 2");
3776 gcc_rich_location richloc (bar, &label0);
3777 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3778 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3780 test_diagnostic_context dc;
3781 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3782 ASSERT_STREQ (" foo = bar.field;\n"
3783 " ^~~\n"
3784 " |\n"
3785 " label 0\n"
3786 " label 1\n"
3787 " label 2\n",
3788 pp_formatted_text (dc.printer));
3791 /* Example of out-of-order ranges (thus requiring a sort), where
3792 they overlap, and there are multiple ranges on the same point. */
3794 text_range_label label_0a ("label 0a");
3795 text_range_label label_1a ("label 1a");
3796 text_range_label label_2a ("label 2a");
3797 text_range_label label_0b ("label 0b");
3798 text_range_label label_1b ("label 1b");
3799 text_range_label label_2b ("label 2b");
3800 text_range_label label_0c ("label 0c");
3801 text_range_label label_1c ("label 1c");
3802 text_range_label label_2c ("label 2c");
3803 gcc_rich_location richloc (field, &label_0a);
3804 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3805 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3807 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3808 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3809 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3811 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3812 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3813 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3815 test_diagnostic_context dc;
3816 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3817 ASSERT_STREQ (" foo = bar.field;\n"
3818 " ~~~ ~~~ ^~~~~\n"
3819 " | | |\n"
3820 " | | label 0a\n"
3821 " | | label 0b\n"
3822 " | | label 0c\n"
3823 " | label 1a\n"
3824 " | label 1b\n"
3825 " | label 1c\n"
3826 " label 2a\n"
3827 " label 2b\n"
3828 " label 2c\n",
3829 pp_formatted_text (dc.printer));
3832 /* Verify that a NULL result from range_label::get_text is
3833 handled gracefully. */
3835 text_range_label label (NULL);
3836 gcc_rich_location richloc (bar, &label);
3838 test_diagnostic_context dc;
3839 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3840 ASSERT_STREQ (" foo = bar.field;\n"
3841 " ^~~\n",
3842 pp_formatted_text (dc.printer));
3845 /* TODO: example of formatted printing (needs to be in
3846 gcc-rich-location.cc due to Makefile.in issues). */
3849 /* Run the various one-liner tests. */
3851 static void
3852 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3854 /* Create a tempfile and write some text to it.
3855 ....................0000000001111111.
3856 ....................1234567890123456. */
3857 const char *content = "foo = bar.field;\n";
3858 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3859 line_table_test ltt (case_);
3861 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3863 location_t line_end = linemap_position_for_column (line_table, 16);
3865 /* Don't attempt to run the tests if column data might be unavailable. */
3866 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3867 return;
3869 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3870 ASSERT_EQ (1, LOCATION_LINE (line_end));
3871 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3873 test_one_liner_simple_caret ();
3874 test_one_liner_no_column ();
3875 test_one_liner_caret_and_range ();
3876 test_one_liner_multiple_carets_and_ranges ();
3877 test_one_liner_fixit_insert_before ();
3878 test_one_liner_fixit_insert_after ();
3879 test_one_liner_fixit_remove ();
3880 test_one_liner_fixit_replace ();
3881 test_one_liner_fixit_replace_non_equal_range ();
3882 test_one_liner_fixit_replace_equal_secondary_range ();
3883 test_one_liner_fixit_validation_adhoc_locations ();
3884 test_one_liner_many_fixits_1 ();
3885 test_one_liner_many_fixits_2 ();
3886 test_one_liner_labels ();
3889 /* Version of all one-liner tests exercising multibyte awareness. For
3890 simplicity we stick to using two multibyte characters in the test, U+1F602
3891 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3892 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3893 below asserts would be easier to read if we used UTF-8 directly in the
3894 string constants, but it seems better not to demand the host compiler
3895 support this, when it isn't otherwise necessary. Instead, whenever an
3896 extended character appears in a string, we put a line break after it so that
3897 all succeeding characters can appear visually at the correct display column.
3899 All of these work on the following 1-line source file:
3901 .0000000001111111111222222 display
3902 .1234567890123456789012345 columns
3903 "SS_foo = P_bar.SS_fieldP;\n"
3904 .0000000111111111222222223 byte
3905 .1356789012456789134567891 columns
3907 which is set up by test_diagnostic_show_locus_one_liner and calls
3908 them. Here SS represents the two display columns for the U+1F602 emoji and
3909 P represents the one display column for the U+03C0 pi symbol. */
3911 /* Just a caret. */
3913 static void
3914 test_one_liner_simple_caret_utf8 ()
3916 test_diagnostic_context dc;
3917 location_t caret = linemap_position_for_column (line_table, 18);
3918 rich_location richloc (line_table, caret);
3919 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3920 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3921 "_foo = \xcf\x80"
3922 "_bar.\xf0\x9f\x98\x82"
3923 "_field\xcf\x80"
3924 ";\n"
3925 " ^\n",
3926 pp_formatted_text (dc.printer));
3929 /* Caret and range. */
3930 static void
3931 test_one_liner_caret_and_range_utf8 ()
3933 test_diagnostic_context dc;
3934 location_t caret = linemap_position_for_column (line_table, 18);
3935 location_t start = linemap_position_for_column (line_table, 12);
3936 location_t finish = linemap_position_for_column (line_table, 30);
3937 location_t loc = make_location (caret, start, finish);
3938 rich_location richloc (line_table, loc);
3939 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3940 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3941 "_foo = \xcf\x80"
3942 "_bar.\xf0\x9f\x98\x82"
3943 "_field\xcf\x80"
3944 ";\n"
3945 " ~~~~~^~~~~~~~~~\n",
3946 pp_formatted_text (dc.printer));
3949 /* Multiple ranges and carets. */
3951 static void
3952 test_one_liner_multiple_carets_and_ranges_utf8 ()
3954 test_diagnostic_context dc;
3955 location_t foo
3956 = make_location (linemap_position_for_column (line_table, 7),
3957 linemap_position_for_column (line_table, 1),
3958 linemap_position_for_column (line_table, 8));
3959 dc.m_source_printing.caret_chars[0] = 'A';
3961 location_t bar
3962 = make_location (linemap_position_for_column (line_table, 16),
3963 linemap_position_for_column (line_table, 12),
3964 linemap_position_for_column (line_table, 17));
3965 dc.m_source_printing.caret_chars[1] = 'B';
3967 location_t field
3968 = make_location (linemap_position_for_column (line_table, 26),
3969 linemap_position_for_column (line_table, 19),
3970 linemap_position_for_column (line_table, 30));
3971 dc.m_source_printing.caret_chars[2] = 'C';
3972 rich_location richloc (line_table, foo);
3973 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3974 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3975 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3976 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3977 "_foo = \xcf\x80"
3978 "_bar.\xf0\x9f\x98\x82"
3979 "_field\xcf\x80"
3980 ";\n"
3981 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3982 pp_formatted_text (dc.printer));
3985 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3987 static void
3988 test_one_liner_fixit_insert_before_utf8 ()
3990 test_diagnostic_context dc;
3991 location_t caret = linemap_position_for_column (line_table, 12);
3992 rich_location richloc (line_table, caret);
3993 richloc.add_fixit_insert_before ("&");
3994 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3995 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3996 "_foo = \xcf\x80"
3997 "_bar.\xf0\x9f\x98\x82"
3998 "_field\xcf\x80"
3999 ";\n"
4000 " ^\n"
4001 " &\n",
4002 pp_formatted_text (dc.printer));
4005 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
4007 static void
4008 test_one_liner_fixit_insert_after_utf8 ()
4010 test_diagnostic_context dc;
4011 location_t start = linemap_position_for_column (line_table, 1);
4012 location_t finish = linemap_position_for_column (line_table, 8);
4013 location_t foo = make_location (start, start, finish);
4014 rich_location richloc (line_table, foo);
4015 richloc.add_fixit_insert_after ("[0]");
4016 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4017 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4018 "_foo = \xcf\x80"
4019 "_bar.\xf0\x9f\x98\x82"
4020 "_field\xcf\x80"
4021 ";\n"
4022 " ^~~~~~\n"
4023 " [0]\n",
4024 pp_formatted_text (dc.printer));
4027 /* Removal fix-it hint: removal of the ".SS_fieldP". */
4029 static void
4030 test_one_liner_fixit_remove_utf8 ()
4032 test_diagnostic_context dc;
4033 location_t start = linemap_position_for_column (line_table, 18);
4034 location_t finish = linemap_position_for_column (line_table, 30);
4035 location_t dot = make_location (start, start, finish);
4036 rich_location richloc (line_table, dot);
4037 richloc.add_fixit_remove ();
4038 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4039 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4040 "_foo = \xcf\x80"
4041 "_bar.\xf0\x9f\x98\x82"
4042 "_field\xcf\x80"
4043 ";\n"
4044 " ^~~~~~~~~~\n"
4045 " ----------\n",
4046 pp_formatted_text (dc.printer));
4049 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4051 static void
4052 test_one_liner_fixit_replace_utf8 ()
4054 test_diagnostic_context dc;
4055 location_t start = linemap_position_for_column (line_table, 19);
4056 location_t finish = linemap_position_for_column (line_table, 30);
4057 location_t field = make_location (start, start, finish);
4058 rich_location richloc (line_table, field);
4059 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4060 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4061 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4062 "_foo = \xcf\x80"
4063 "_bar.\xf0\x9f\x98\x82"
4064 "_field\xcf\x80"
4065 ";\n"
4066 " ^~~~~~~~~\n"
4067 " m_\xf0\x9f\x98\x82"
4068 "_field\xcf\x80\n",
4069 pp_formatted_text (dc.printer));
4072 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4073 but where the caret was elsewhere. */
4075 static void
4076 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4078 test_diagnostic_context dc;
4079 location_t equals = linemap_position_for_column (line_table, 10);
4080 location_t start = linemap_position_for_column (line_table, 19);
4081 location_t finish = linemap_position_for_column (line_table, 30);
4082 rich_location richloc (line_table, equals);
4083 source_range range;
4084 range.m_start = start;
4085 range.m_finish = finish;
4086 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4087 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4088 /* The replacement range is not indicated in the annotation line, so
4089 it should be indicated via an additional underline. */
4090 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4091 "_foo = \xcf\x80"
4092 "_bar.\xf0\x9f\x98\x82"
4093 "_field\xcf\x80"
4094 ";\n"
4095 " ^\n"
4096 " ---------\n"
4097 " m_\xf0\x9f\x98\x82"
4098 "_field\xcf\x80\n",
4099 pp_formatted_text (dc.printer));
4102 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4103 where the caret was elsewhere, but where a secondary range
4104 exactly covers "field". */
4106 static void
4107 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4109 test_diagnostic_context dc;
4110 location_t equals = linemap_position_for_column (line_table, 10);
4111 location_t start = linemap_position_for_column (line_table, 19);
4112 location_t finish = linemap_position_for_column (line_table, 30);
4113 rich_location richloc (line_table, equals);
4114 location_t field = make_location (start, start, finish);
4115 richloc.add_range (field);
4116 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4117 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4118 /* The replacement range is indicated in the annotation line,
4119 so it shouldn't be indicated via an additional underline. */
4120 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4121 "_foo = \xcf\x80"
4122 "_bar.\xf0\x9f\x98\x82"
4123 "_field\xcf\x80"
4124 ";\n"
4125 " ^ ~~~~~~~~~\n"
4126 " m_\xf0\x9f\x98\x82"
4127 "_field\xcf\x80\n",
4128 pp_formatted_text (dc.printer));
4131 /* Verify that we can use ad-hoc locations when adding fixits to a
4132 rich_location. */
4134 static void
4135 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4137 /* Generate a range that's too long to be packed, so must
4138 be stored as an ad-hoc location (given the defaults
4139 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4140 const location_t c12 = linemap_position_for_column (line_table, 12);
4141 const location_t c52 = linemap_position_for_column (line_table, 52);
4142 const location_t loc = make_location (c12, c12, c52);
4144 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4145 return;
4147 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4149 /* Insert. */
4151 rich_location richloc (line_table, loc);
4152 richloc.add_fixit_insert_before (loc, "test");
4153 /* It should not have been discarded by the validator. */
4154 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4156 test_diagnostic_context dc;
4157 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4158 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4159 "_foo = \xcf\x80"
4160 "_bar.\xf0\x9f\x98\x82"
4161 "_field\xcf\x80"
4162 ";\n"
4163 " ^~~~~~~~~~~~~~~~ \n"
4164 " test\n",
4165 pp_formatted_text (dc.printer));
4168 /* Remove. */
4170 rich_location richloc (line_table, loc);
4171 source_range range = source_range::from_locations (loc, c52);
4172 richloc.add_fixit_remove (range);
4173 /* It should not have been discarded by the validator. */
4174 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4176 test_diagnostic_context dc;
4177 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4178 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4179 "_foo = \xcf\x80"
4180 "_bar.\xf0\x9f\x98\x82"
4181 "_field\xcf\x80"
4182 ";\n"
4183 " ^~~~~~~~~~~~~~~~ \n"
4184 " -------------------------------------\n",
4185 pp_formatted_text (dc.printer));
4188 /* Replace. */
4190 rich_location richloc (line_table, loc);
4191 source_range range = source_range::from_locations (loc, c52);
4192 richloc.add_fixit_replace (range, "test");
4193 /* It should not have been discarded by the validator. */
4194 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4196 test_diagnostic_context dc;
4197 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4198 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4199 "_foo = \xcf\x80"
4200 "_bar.\xf0\x9f\x98\x82"
4201 "_field\xcf\x80"
4202 ";\n"
4203 " ^~~~~~~~~~~~~~~~ \n"
4204 " test\n",
4205 pp_formatted_text (dc.printer));
4209 /* Test of consolidating insertions at the same location. */
4211 static void
4212 test_one_liner_many_fixits_1_utf8 ()
4214 test_diagnostic_context dc;
4215 location_t equals = linemap_position_for_column (line_table, 10);
4216 rich_location richloc (line_table, equals);
4217 for (int i = 0; i < 19; i++)
4218 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4219 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4220 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4221 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4222 "_foo = \xcf\x80"
4223 "_bar.\xf0\x9f\x98\x82"
4224 "_field\xcf\x80"
4225 ";\n"
4226 " ^\n"
4227 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4228 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4229 pp_formatted_text (dc.printer));
4232 /* Ensure that we can add an arbitrary number of fix-it hints to a
4233 rich_location, even if they are not consolidated. */
4235 static void
4236 test_one_liner_many_fixits_2_utf8 ()
4238 test_diagnostic_context dc;
4239 location_t equals = linemap_position_for_column (line_table, 10);
4240 rich_location richloc (line_table, equals);
4241 const int nlocs = 19;
4242 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4243 34, 36, 38, 40, 42, 44};
4244 for (int i = 0; i != nlocs; ++i)
4246 location_t loc = linemap_position_for_column (line_table, locs[i]);
4247 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4250 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4251 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4252 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4253 "_foo = \xcf\x80"
4254 "_bar.\xf0\x9f\x98\x82"
4255 "_field\xcf\x80"
4256 ";\n"
4257 " ^\n"
4258 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4259 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4260 pp_formatted_text (dc.printer));
4263 /* Test of labeling the ranges within a rich_location. */
4265 static void
4266 test_one_liner_labels_utf8 ()
4268 location_t foo
4269 = make_location (linemap_position_for_column (line_table, 1),
4270 linemap_position_for_column (line_table, 1),
4271 linemap_position_for_column (line_table, 8));
4272 location_t bar
4273 = make_location (linemap_position_for_column (line_table, 12),
4274 linemap_position_for_column (line_table, 12),
4275 linemap_position_for_column (line_table, 17));
4276 location_t field
4277 = make_location (linemap_position_for_column (line_table, 19),
4278 linemap_position_for_column (line_table, 19),
4279 linemap_position_for_column (line_table, 30));
4281 /* Example where all the labels fit on one line. */
4283 /* These three labels contain multibyte characters such that their byte
4284 lengths are respectively (12, 10, 18), but their display widths are only
4285 (6, 5, 9). All three fit on the line when considering the display
4286 widths, but not when considering the byte widths, so verify that we do
4287 indeed put them all on one line. */
4288 text_range_label label0
4289 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4290 text_range_label label1
4291 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4292 text_range_label label2
4293 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4294 "\xcf\x80");
4295 gcc_rich_location richloc (foo, &label0);
4296 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4297 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4300 test_diagnostic_context dc;
4301 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4302 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4303 "_foo = \xcf\x80"
4304 "_bar.\xf0\x9f\x98\x82"
4305 "_field\xcf\x80"
4306 ";\n"
4307 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4308 " | | |\n"
4309 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4310 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4311 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4312 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4313 pp_formatted_text (dc.printer));
4318 /* Example where the labels need extra lines. */
4320 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4321 text_range_label label1 ("label 1\xcf\x80");
4322 text_range_label label2 ("label 2\xcf\x80");
4323 gcc_rich_location richloc (foo, &label0);
4324 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4325 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4327 test_diagnostic_context dc;
4328 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4330 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4331 "_foo = \xcf\x80"
4332 "_bar.\xf0\x9f\x98\x82"
4333 "_field\xcf\x80"
4334 ";\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));
4343 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4344 but label 1 just touches label 2. */
4346 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4347 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4348 text_range_label label2 ("c");
4349 gcc_rich_location richloc (foo, &label0);
4350 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4351 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4353 test_diagnostic_context dc;
4354 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4355 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4356 "_foo = \xcf\x80"
4357 "_bar.\xf0\x9f\x98\x82"
4358 "_field\xcf\x80"
4359 ";\n"
4360 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4361 " | | |\n"
4362 " | | c\n"
4363 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4364 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4365 pp_formatted_text (dc.printer));
4368 /* Example of escaping the source lines. */
4370 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4371 text_range_label label1 ("label 1\xcf\x80");
4372 text_range_label label2 ("label 2\xcf\x80");
4373 gcc_rich_location richloc (foo, &label0);
4374 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4375 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4376 richloc.set_escape_on_output (true);
4379 test_diagnostic_context dc;
4380 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
4381 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4382 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4383 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4384 " | | |\n"
4385 " | | label 2\xcf\x80\n"
4386 " | label 1\xcf\x80\n"
4387 " label 0\xf0\x9f\x98\x82\n",
4388 pp_formatted_text (dc.printer));
4391 test_diagnostic_context dc;
4392 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
4393 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4394 ASSERT_STREQ
4395 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4396 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4397 " | | |\n"
4398 " | | label 2\xcf\x80\n"
4399 " | label 1\xcf\x80\n"
4400 " label 0\xf0\x9f\x98\x82\n",
4401 pp_formatted_text (dc.printer));
4406 /* Make sure that colorization codes don't interrupt a multibyte
4407 sequence, which would corrupt it. */
4408 static void
4409 test_one_liner_colorized_utf8 ()
4411 test_diagnostic_context dc;
4412 dc.m_source_printing.colorize_source_p = true;
4413 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4414 const location_t pi = linemap_position_for_column (line_table, 12);
4415 rich_location richloc (line_table, pi);
4416 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4418 /* In order to avoid having the test depend on exactly how the colorization
4419 was effected, just confirm there are two pi characters in the output. */
4420 const char *result = pp_formatted_text (dc.printer);
4421 const char *null_term = result + strlen (result);
4422 const char *first_pi = strstr (result, "\xcf\x80");
4423 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4424 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4427 /* Run the various one-liner tests. */
4429 static void
4430 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4432 /* Create a tempfile and write some text to it. */
4433 const char *content
4434 /* Display columns.
4435 0000000000000000000000011111111111111111111111111111112222222222222
4436 1111111122222222345678900000000123456666666677777777890123444444445 */
4437 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4438 /* 0000000000000000000001111111111111111111222222222222222222222233333
4439 1111222233334444567890122223333456789999000011112222345678999900001
4440 Byte columns. */
4441 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4442 file_cache fc;
4443 line_table_test ltt (case_);
4445 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4447 location_t line_end = linemap_position_for_column (line_table, 31);
4449 /* Don't attempt to run the tests if column data might be unavailable. */
4450 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4451 return;
4453 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4454 ASSERT_EQ (1, LOCATION_LINE (line_end));
4455 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4457 char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
4458 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4459 def_policy ()));
4460 ASSERT_EQ (25, location_compute_display_column (fc,
4461 expand_location (line_end),
4462 def_policy ()));
4464 test_one_liner_simple_caret_utf8 ();
4465 test_one_liner_caret_and_range_utf8 ();
4466 test_one_liner_multiple_carets_and_ranges_utf8 ();
4467 test_one_liner_fixit_insert_before_utf8 ();
4468 test_one_liner_fixit_insert_after_utf8 ();
4469 test_one_liner_fixit_remove_utf8 ();
4470 test_one_liner_fixit_replace_utf8 ();
4471 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4472 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4473 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4474 test_one_liner_many_fixits_1_utf8 ();
4475 test_one_liner_many_fixits_2_utf8 ();
4476 test_one_liner_labels_utf8 ();
4477 test_one_liner_colorized_utf8 ();
4480 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4482 static void
4483 test_add_location_if_nearby (const line_table_case &case_)
4485 /* Create a tempfile and write some text to it.
4486 ...000000000111111111122222222223333333333.
4487 ...123456789012345678901234567890123456789. */
4488 const char *content
4489 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4490 "struct different_line\n" /* line 2. */
4491 "{\n" /* line 3. */
4492 " double x;\n" /* line 4. */
4493 " double y;\n" /* line 5. */
4494 ";\n"); /* line 6. */
4495 temp_source_file tmp (SELFTEST_LOCATION, ".c", content,
4497 /* gcc_rich_location::add_location_if_nearby implicitly
4498 uses global_dc's file_cache, so we need to evict
4499 tmp when we're done. */
4500 &global_dc->get_file_cache ());
4501 line_table_test ltt (case_);
4503 const line_map_ordinary *ord_map
4504 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4505 tmp.get_filename (), 0));
4507 linemap_line_start (line_table, 1, 100);
4509 const location_t final_line_end
4510 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4512 /* Don't attempt to run the tests if column data might be unavailable. */
4513 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4514 return;
4516 /* Test of add_location_if_nearby on the same line as the
4517 primary location. */
4519 const location_t missing_close_brace_1_39
4520 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4521 const location_t matching_open_brace_1_18
4522 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4523 gcc_rich_location richloc (missing_close_brace_1_39);
4524 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4525 ASSERT_TRUE (added);
4526 ASSERT_EQ (2, richloc.get_num_locations ());
4527 test_diagnostic_context dc;
4528 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4529 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4530 " ~ ^\n",
4531 pp_formatted_text (dc.printer));
4534 /* Test of add_location_if_nearby on a different line to the
4535 primary location. */
4537 const location_t missing_close_brace_6_1
4538 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4539 const location_t matching_open_brace_3_1
4540 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4541 gcc_rich_location richloc (missing_close_brace_6_1);
4542 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4543 ASSERT_FALSE (added);
4544 ASSERT_EQ (1, richloc.get_num_locations ());
4548 /* Verify that we print fixits even if they only affect lines
4549 outside those covered by the ranges in the rich_location. */
4551 static void
4552 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4554 /* Create a tempfile and write some text to it.
4555 ...000000000111111111122222222223333333333.
4556 ...123456789012345678901234567890123456789. */
4557 const char *content
4558 = ("struct point { double x; double y; };\n" /* line 1. */
4559 "struct point origin = {x: 0.0,\n" /* line 2. */
4560 " y\n" /* line 3. */
4561 "\n" /* line 4. */
4562 "\n" /* line 5. */
4563 " : 0.0};\n"); /* line 6. */
4564 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4565 line_table_test ltt (case_);
4567 const line_map_ordinary *ord_map
4568 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4569 tmp.get_filename (), 0));
4571 linemap_line_start (line_table, 1, 100);
4573 const location_t final_line_end
4574 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4576 /* Don't attempt to run the tests if column data might be unavailable. */
4577 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4578 return;
4580 /* A pair of tests for modernizing the initializers to C99-style. */
4582 /* The one-liner case (line 2). */
4584 test_diagnostic_context dc;
4585 const location_t x
4586 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4587 const location_t colon
4588 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4589 rich_location richloc (line_table, colon);
4590 richloc.add_fixit_insert_before (x, ".");
4591 richloc.add_fixit_replace (colon, "=");
4592 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4593 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4594 " ^\n"
4595 " .=\n",
4596 pp_formatted_text (dc.printer));
4599 /* The multiline case. The caret for the rich_location is on line 6;
4600 verify that insertion fixit on line 3 is still printed (and that
4601 span starts are printed due to the gap between the span at line 3
4602 and that at line 6). */
4604 test_diagnostic_context dc;
4605 const location_t y
4606 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4607 const location_t colon
4608 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4609 rich_location richloc (line_table, colon);
4610 richloc.add_fixit_insert_before (y, ".");
4611 richloc.add_fixit_replace (colon, "=");
4612 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4613 ASSERT_STREQ ("FILENAME:3:24:\n"
4614 " y\n"
4615 " .\n"
4616 "FILENAME:6:25:\n"
4617 " : 0.0};\n"
4618 " ^\n"
4619 " =\n",
4620 pp_formatted_text (dc.printer));
4623 /* As above, but verify the behavior of multiple line spans
4624 with line-numbering enabled. */
4626 const location_t y
4627 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4628 const location_t colon
4629 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4630 rich_location richloc (line_table, colon);
4631 richloc.add_fixit_insert_before (y, ".");
4632 richloc.add_fixit_replace (colon, "=");
4633 test_diagnostic_context dc;
4634 dc.m_source_printing.show_line_numbers_p = true;
4635 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4636 ASSERT_STREQ (" 3 | y\n"
4637 " | .\n"
4638 "......\n"
4639 " 6 | : 0.0};\n"
4640 " | ^\n"
4641 " | =\n",
4642 pp_formatted_text (dc.printer));
4647 /* Verify that fix-it hints are appropriately consolidated.
4649 If any fix-it hints in a rich_location involve locations beyond
4650 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4651 the fix-it as a whole, so there should be none.
4653 Otherwise, verify that consecutive "replace" and "remove" fix-its
4654 are merged, and that other fix-its remain separate. */
4656 static void
4657 test_fixit_consolidation (const line_table_case &case_)
4659 line_table_test ltt (case_);
4661 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4663 const location_t c10 = linemap_position_for_column (line_table, 10);
4664 const location_t c15 = linemap_position_for_column (line_table, 15);
4665 const location_t c16 = linemap_position_for_column (line_table, 16);
4666 const location_t c17 = linemap_position_for_column (line_table, 17);
4667 const location_t c20 = linemap_position_for_column (line_table, 20);
4668 const location_t c21 = linemap_position_for_column (line_table, 21);
4669 const location_t caret = c10;
4671 /* Insert + insert. */
4673 rich_location richloc (line_table, caret);
4674 richloc.add_fixit_insert_before (c10, "foo");
4675 richloc.add_fixit_insert_before (c15, "bar");
4677 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4678 /* Bogus column info for 2nd fixit, so no fixits. */
4679 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4680 else
4681 /* They should not have been merged. */
4682 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4685 /* Insert + replace. */
4687 rich_location richloc (line_table, caret);
4688 richloc.add_fixit_insert_before (c10, "foo");
4689 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4690 "bar");
4692 if (c17 > 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
4696 /* They should not have been merged. */
4697 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4700 /* Replace + non-consecutive insert. */
4702 rich_location richloc (line_table, caret);
4703 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4704 "bar");
4705 richloc.add_fixit_insert_before (c17, "foo");
4707 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4708 /* Bogus column info for 2nd fixit, so no fixits. */
4709 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4710 else
4711 /* They should not have been merged. */
4712 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4715 /* Replace + non-consecutive replace. */
4717 rich_location richloc (line_table, caret);
4718 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4719 "foo");
4720 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4721 "bar");
4723 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4724 /* Bogus column info for 2nd fixit, so no fixits. */
4725 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4726 else
4727 /* They should not have been merged. */
4728 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4731 /* Replace + consecutive replace. */
4733 rich_location richloc (line_table, caret);
4734 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4735 "foo");
4736 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4737 "bar");
4739 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4740 /* Bogus column info for 2nd fixit, so no fixits. */
4741 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4742 else
4744 /* They should have been merged into a single "replace". */
4745 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4746 const fixit_hint *hint = richloc.get_fixit_hint (0);
4747 ASSERT_STREQ ("foobar", hint->get_string ());
4748 ASSERT_EQ (c10, hint->get_start_loc ());
4749 ASSERT_EQ (c21, hint->get_next_loc ());
4753 /* Replace + consecutive removal. */
4755 rich_location richloc (line_table, caret);
4756 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4757 "foo");
4758 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4760 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4761 /* Bogus column info for 2nd fixit, so no fixits. */
4762 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4763 else
4765 /* They should have been merged into a single replace, with the
4766 range extended to cover that of the removal. */
4767 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4768 const fixit_hint *hint = richloc.get_fixit_hint (0);
4769 ASSERT_STREQ ("foo", hint->get_string ());
4770 ASSERT_EQ (c10, hint->get_start_loc ());
4771 ASSERT_EQ (c21, hint->get_next_loc ());
4775 /* Consecutive removals. */
4777 rich_location richloc (line_table, caret);
4778 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4779 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4781 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4782 /* Bogus column info for 2nd fixit, so no fixits. */
4783 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4784 else
4786 /* They should have been merged into a single "replace-with-empty". */
4787 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4788 const fixit_hint *hint = richloc.get_fixit_hint (0);
4789 ASSERT_STREQ ("", hint->get_string ());
4790 ASSERT_EQ (c10, hint->get_start_loc ());
4791 ASSERT_EQ (c21, hint->get_next_loc ());
4796 /* Verify that the line_corrections machinery correctly prints
4797 overlapping fixit-hints. */
4799 static void
4800 test_overlapped_fixit_printing (const line_table_case &case_)
4802 /* Create a tempfile and write some text to it.
4803 ...000000000111111111122222222223333333333.
4804 ...123456789012345678901234567890123456789. */
4805 const char *content
4806 = (" foo *f = (foo *)ptr->field;\n");
4807 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4808 file_cache fc;
4809 line_table_test ltt (case_);
4811 const line_map_ordinary *ord_map
4812 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4813 tmp.get_filename (), 0));
4815 linemap_line_start (line_table, 1, 100);
4817 const location_t final_line_end
4818 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4820 /* Don't attempt to run the tests if column data might be unavailable. */
4821 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4822 return;
4824 /* A test for converting a C-style cast to a C++-style cast. */
4825 const location_t open_paren
4826 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4827 const location_t close_paren
4828 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4829 const location_t expr_start
4830 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4831 const location_t expr_finish
4832 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4833 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4835 /* Various examples of fix-it hints that aren't themselves consolidated,
4836 but for which the *printing* may need consolidation. */
4838 /* Example where 3 fix-it hints are printed as one. */
4840 test_diagnostic_context dc;
4841 rich_location richloc (line_table, expr);
4842 richloc.add_fixit_replace (open_paren, "const_cast<");
4843 richloc.add_fixit_replace (close_paren, "> (");
4844 richloc.add_fixit_insert_after (")");
4846 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4847 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4848 " ^~~~~~~~~~\n"
4849 " -----------------\n"
4850 " const_cast<foo *> (ptr->field)\n",
4851 pp_formatted_text (dc.printer));
4853 /* Unit-test the line_corrections machinery. */
4854 char_display_policy policy (make_policy (dc, richloc));
4855 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4856 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4857 ASSERT_EQ (column_range (12, 12),
4858 get_affected_range (fc, policy, hint_0, CU_BYTES));
4859 ASSERT_EQ (column_range (12, 12),
4860 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
4861 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
4862 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4863 ASSERT_EQ (column_range (18, 18),
4864 get_affected_range (fc, policy, hint_1, CU_BYTES));
4865 ASSERT_EQ (column_range (18, 18),
4866 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
4867 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
4868 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4869 ASSERT_EQ (column_range (29, 28),
4870 get_affected_range (fc, policy, hint_2, CU_BYTES));
4871 ASSERT_EQ (column_range (29, 28),
4872 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
4873 ASSERT_EQ (column_range (29, 29), get_printed_columns (fc, policy, hint_2));
4875 /* Add each hint in turn to a line_corrections instance,
4876 and verify that they are consolidated into one correction instance
4877 as expected. */
4878 line_corrections lc (fc, policy, tmp.get_filename (), 1);
4880 /* The first replace hint by itself. */
4881 lc.add_hint (hint_0);
4882 ASSERT_EQ (1, lc.m_corrections.length ());
4883 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4884 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4885 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4886 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4888 /* After the second replacement hint, they are printed together
4889 as a replacement (along with the text between them). */
4890 lc.add_hint (hint_1);
4891 ASSERT_EQ (1, lc.m_corrections.length ());
4892 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4893 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4894 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4895 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4897 /* After the final insertion hint, they are all printed together
4898 as a replacement (along with the text between them). */
4899 lc.add_hint (hint_2);
4900 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4901 lc.m_corrections[0]->m_text);
4902 ASSERT_EQ (1, lc.m_corrections.length ());
4903 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4904 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4905 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4908 /* Example where two are consolidated during printing. */
4910 test_diagnostic_context dc;
4911 rich_location richloc (line_table, expr);
4912 richloc.add_fixit_replace (open_paren, "CAST (");
4913 richloc.add_fixit_replace (close_paren, ") (");
4914 richloc.add_fixit_insert_after (")");
4916 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4917 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4918 " ^~~~~~~~~~\n"
4919 " -\n"
4920 " CAST (-\n"
4921 " ) ( )\n",
4922 pp_formatted_text (dc.printer));
4925 /* Example where none are consolidated during printing. */
4927 test_diagnostic_context dc;
4928 rich_location richloc (line_table, expr);
4929 richloc.add_fixit_replace (open_paren, "CST (");
4930 richloc.add_fixit_replace (close_paren, ") (");
4931 richloc.add_fixit_insert_after (")");
4933 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4934 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4935 " ^~~~~~~~~~\n"
4936 " -\n"
4937 " CST ( -\n"
4938 " ) ( )\n",
4939 pp_formatted_text (dc.printer));
4942 /* Example of deletion fix-it hints. */
4944 test_diagnostic_context dc;
4945 rich_location richloc (line_table, expr);
4946 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4947 source_range victim = {open_paren, close_paren};
4948 richloc.add_fixit_remove (victim);
4950 /* This case is actually handled by fixit-consolidation,
4951 rather than by line_corrections. */
4952 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4954 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4955 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4956 " ^~~~~~~~~~\n"
4957 " -------\n"
4958 " (bar *)\n",
4959 pp_formatted_text (dc.printer));
4962 /* Example of deletion fix-it hints that would overlap. */
4964 test_diagnostic_context dc;
4965 rich_location richloc (line_table, expr);
4966 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4967 source_range victim = {expr_start, expr_finish};
4968 richloc.add_fixit_remove (victim);
4970 /* These fixits are not consolidated. */
4971 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4973 /* But the corrections are. */
4974 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4975 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4976 " ^~~~~~~~~~\n"
4977 " -----------------\n"
4978 " (longer *)(foo *)\n",
4979 pp_formatted_text (dc.printer));
4982 /* Example of insertion fix-it hints that would overlap. */
4984 test_diagnostic_context dc;
4985 rich_location richloc (line_table, expr);
4986 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4987 richloc.add_fixit_insert_after (close_paren, "TEST");
4989 /* The first insertion is long enough that if printed naively,
4990 it would overlap with the second.
4991 Verify that they are printed as a single replacement. */
4992 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4993 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4994 " ^~~~~~~~~~\n"
4995 " -------\n"
4996 " LONGER THAN THE CAST(foo *)TEST\n",
4997 pp_formatted_text (dc.printer));
5001 /* Multibyte-aware version of preceding tests. See comments above
5002 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
5003 characters here. */
5005 static void
5006 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
5008 /* Create a tempfile and write some text to it. */
5010 const char *content
5011 /* Display columns.
5012 00000000000000000000000111111111111111111111111222222222222222223
5013 12344444444555555556789012344444444555555556789012345678999999990 */
5014 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
5015 /* 00000000000000000000011111111111111111111112222222222333333333333
5016 12344445555666677778901234566667777888899990123456789012333344445
5017 Byte columns. */
5019 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
5020 line_table_test ltt (case_);
5022 const line_map_ordinary *ord_map
5023 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5024 tmp.get_filename (), 0));
5026 linemap_line_start (line_table, 1, 100);
5028 const location_t final_line_end
5029 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
5031 /* Don't attempt to run the tests if column data might be unavailable. */
5032 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5033 return;
5035 /* A test for converting a C-style cast to a C++-style cast. */
5036 const location_t open_paren
5037 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
5038 const location_t close_paren
5039 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
5040 const location_t expr_start
5041 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5042 const location_t expr_finish
5043 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
5044 const location_t expr = make_location (expr_start, expr_start, expr_finish);
5046 /* Various examples of fix-it hints that aren't themselves consolidated,
5047 but for which the *printing* may need consolidation. */
5049 /* Example where 3 fix-it hints are printed as one. */
5051 test_diagnostic_context dc;
5052 file_cache &fc = dc.get_file_cache ();
5053 rich_location richloc (line_table, expr);
5054 richloc.add_fixit_replace (open_paren, "const_cast<");
5055 richloc.add_fixit_replace (close_paren, "> (");
5056 richloc.add_fixit_insert_after (")");
5058 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5059 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5060 " *f = (f\xf0\x9f\x98\x82"
5061 " *)ptr->field\xcf\x80"
5062 ";\n"
5063 " ^~~~~~~~~~~\n"
5064 " ------------------\n"
5065 " const_cast<f\xf0\x9f\x98\x82"
5066 " *> (ptr->field\xcf\x80"
5067 ")\n",
5068 pp_formatted_text (dc.printer));
5070 /* Unit-test the line_corrections machinery. */
5071 char_display_policy policy (make_policy (dc, richloc));
5072 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5073 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5074 ASSERT_EQ (column_range (14, 14),
5075 get_affected_range (fc, policy, hint_0, CU_BYTES));
5076 ASSERT_EQ (column_range (12, 12),
5077 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
5078 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
5079 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5080 ASSERT_EQ (column_range (22, 22),
5081 get_affected_range (fc, policy, hint_1, CU_BYTES));
5082 ASSERT_EQ (column_range (18, 18),
5083 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
5084 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
5085 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5086 ASSERT_EQ (column_range (35, 34),
5087 get_affected_range (fc, policy, hint_2, CU_BYTES));
5088 ASSERT_EQ (column_range (30, 29),
5089 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
5090 ASSERT_EQ (column_range (30, 30), get_printed_columns (fc, policy, hint_2));
5092 /* Add each hint in turn to a line_corrections instance,
5093 and verify that they are consolidated into one correction instance
5094 as expected. */
5095 line_corrections lc (fc, policy, tmp.get_filename (), 1);
5097 /* The first replace hint by itself. */
5098 lc.add_hint (hint_0);
5099 ASSERT_EQ (1, lc.m_corrections.length ());
5100 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5101 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5102 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5103 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5105 /* After the second replacement hint, they are printed together
5106 as a replacement (along with the text between them). */
5107 lc.add_hint (hint_1);
5108 ASSERT_EQ (1, lc.m_corrections.length ());
5109 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5110 lc.m_corrections[0]->m_text);
5111 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5112 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5113 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5115 /* After the final insertion hint, they are all printed together
5116 as a replacement (along with the text between them). */
5117 lc.add_hint (hint_2);
5118 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5119 lc.m_corrections[0]->m_text);
5120 ASSERT_EQ (1, lc.m_corrections.length ());
5121 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5122 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5123 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5126 /* Example where two are consolidated during printing. */
5128 test_diagnostic_context dc;
5129 rich_location richloc (line_table, expr);
5130 richloc.add_fixit_replace (open_paren, "CAST (");
5131 richloc.add_fixit_replace (close_paren, ") (");
5132 richloc.add_fixit_insert_after (")");
5134 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5135 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5136 " *f = (f\xf0\x9f\x98\x82"
5137 " *)ptr->field\xcf\x80"
5138 ";\n"
5139 " ^~~~~~~~~~~\n"
5140 " -\n"
5141 " CAST (-\n"
5142 " ) ( )\n",
5143 pp_formatted_text (dc.printer));
5146 /* Example where none are consolidated during printing. */
5148 test_diagnostic_context dc;
5149 rich_location richloc (line_table, expr);
5150 richloc.add_fixit_replace (open_paren, "CST (");
5151 richloc.add_fixit_replace (close_paren, ") (");
5152 richloc.add_fixit_insert_after (")");
5154 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5155 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5156 " *f = (f\xf0\x9f\x98\x82"
5157 " *)ptr->field\xcf\x80"
5158 ";\n"
5159 " ^~~~~~~~~~~\n"
5160 " -\n"
5161 " CST ( -\n"
5162 " ) ( )\n",
5163 pp_formatted_text (dc.printer));
5166 /* Example of deletion fix-it hints. */
5168 test_diagnostic_context dc;
5169 rich_location richloc (line_table, expr);
5170 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5171 source_range victim = {open_paren, close_paren};
5172 richloc.add_fixit_remove (victim);
5174 /* This case is actually handled by fixit-consolidation,
5175 rather than by line_corrections. */
5176 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5178 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5179 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5180 " *f = (f\xf0\x9f\x98\x82"
5181 " *)ptr->field\xcf\x80"
5182 ";\n"
5183 " ^~~~~~~~~~~\n"
5184 " -------\n"
5185 " (bar\xf0\x9f\x98\x82"
5186 " *)\n",
5187 pp_formatted_text (dc.printer));
5190 /* Example of deletion fix-it hints that would overlap. */
5192 test_diagnostic_context dc;
5193 rich_location richloc (line_table, expr);
5194 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5195 source_range victim = {expr_start, expr_finish};
5196 richloc.add_fixit_remove (victim);
5198 /* These fixits are not consolidated. */
5199 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5201 /* But the corrections are. */
5202 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5203 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5204 " *f = (f\xf0\x9f\x98\x82"
5205 " *)ptr->field\xcf\x80"
5206 ";\n"
5207 " ^~~~~~~~~~~\n"
5208 " ------------------\n"
5209 " (long\xf0\x9f\x98\x82"
5210 " *)(f\xf0\x9f\x98\x82"
5211 " *)\n",
5212 pp_formatted_text (dc.printer));
5215 /* Example of insertion fix-it hints that would overlap. */
5217 test_diagnostic_context dc;
5218 rich_location richloc (line_table, expr);
5219 richloc.add_fixit_insert_before
5220 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5221 richloc.add_fixit_insert_after (close_paren, "TEST");
5223 /* The first insertion is long enough that if printed naively,
5224 it would overlap with the second.
5225 Verify that they are printed as a single replacement. */
5226 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5227 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5228 " *f = (f\xf0\x9f\x98\x82"
5229 " *)ptr->field\xcf\x80"
5230 ";\n"
5231 " ^~~~~~~~~~~\n"
5232 " -------\n"
5233 " L\xf0\x9f\x98\x82"
5234 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5235 " *)TEST\n",
5236 pp_formatted_text (dc.printer));
5240 /* Verify that the line_corrections machinery correctly prints
5241 overlapping fixit-hints that have been added in the wrong
5242 order.
5243 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5245 static void
5246 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5248 /* Create a tempfile and write some text to it.
5249 ...000000000111111111122222222223333333333.
5250 ...123456789012345678901234567890123456789. */
5251 const char *content
5252 = ("int a5[][0][0] = { 1, 2 };\n");
5253 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5254 line_table_test ltt (case_);
5256 const line_map_ordinary *ord_map
5257 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5258 tmp.get_filename (), 0));
5260 linemap_line_start (line_table, 1, 100);
5262 const location_t final_line_end
5263 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5265 /* Don't attempt to run the tests if column data might be unavailable. */
5266 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5267 return;
5269 const location_t col_1
5270 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5271 const location_t col_20
5272 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5273 const location_t col_21
5274 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5275 const location_t col_23
5276 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5277 const location_t col_25
5278 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5280 /* Two insertions, in the wrong order. */
5282 test_diagnostic_context dc;
5283 file_cache &fc = dc.get_file_cache ();
5285 rich_location richloc (line_table, col_20);
5286 richloc.add_fixit_insert_before (col_23, "{");
5287 richloc.add_fixit_insert_before (col_21, "}");
5289 /* These fixits should be accepted; they can't be consolidated. */
5290 char_display_policy policy (make_policy (dc, richloc));
5291 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5292 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5293 ASSERT_EQ (column_range (23, 22),
5294 get_affected_range (fc, policy, hint_0, CU_BYTES));
5295 ASSERT_EQ (column_range (23, 23), get_printed_columns (fc, policy, hint_0));
5296 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5297 ASSERT_EQ (column_range (21, 20),
5298 get_affected_range (fc, policy, hint_1, CU_BYTES));
5299 ASSERT_EQ (column_range (21, 21), get_printed_columns (fc, policy, hint_1));
5301 /* Verify that they're printed correctly. */
5302 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5303 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5304 " ^\n"
5305 " } {\n",
5306 pp_formatted_text (dc.printer));
5309 /* Various overlapping insertions, some occurring "out of order"
5310 (reproducing the fix-it hints from PR c/81405). */
5312 test_diagnostic_context dc;
5313 rich_location richloc (line_table, col_20);
5315 richloc.add_fixit_insert_before (col_20, "{{");
5316 richloc.add_fixit_insert_before (col_21, "}}");
5317 richloc.add_fixit_insert_before (col_23, "{");
5318 richloc.add_fixit_insert_before (col_21, "}");
5319 richloc.add_fixit_insert_before (col_23, "{{");
5320 richloc.add_fixit_insert_before (col_25, "}");
5321 richloc.add_fixit_insert_before (col_21, "}");
5322 richloc.add_fixit_insert_before (col_1, "{");
5323 richloc.add_fixit_insert_before (col_25, "}");
5324 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5325 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5326 " ^\n"
5327 " { -----\n"
5328 " {{1}}}}, {{{2 }}\n",
5329 pp_formatted_text (dc.printer));
5333 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5335 static void
5336 test_fixit_insert_containing_newline (const line_table_case &case_)
5338 /* Create a tempfile and write some text to it.
5339 .........................0000000001111111.
5340 .........................1234567890123456. */
5341 const char *old_content = (" case 'a':\n" /* line 1. */
5342 " x = a;\n" /* line 2. */
5343 " case 'b':\n" /* line 3. */
5344 " x = b;\n");/* line 4. */
5346 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5347 line_table_test ltt (case_);
5348 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5350 location_t case_start = linemap_position_for_column (line_table, 5);
5351 location_t case_finish = linemap_position_for_column (line_table, 13);
5352 location_t case_loc = make_location (case_start, case_start, case_finish);
5353 location_t line_start = linemap_position_for_column (line_table, 1);
5355 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5356 return;
5358 /* Add a "break;" on a line by itself before line 3 i.e. before
5359 column 1 of line 3. */
5361 rich_location richloc (line_table, case_loc);
5362 richloc.add_fixit_insert_before (line_start, " break;\n");
5364 /* Without line numbers. */
5366 test_diagnostic_context dc;
5367 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5368 ASSERT_STREQ (" x = a;\n"
5369 "+ break;\n"
5370 " case 'b':\n"
5371 " ^~~~~~~~~\n",
5372 pp_formatted_text (dc.printer));
5375 /* With line numbers. */
5377 test_diagnostic_context dc;
5378 dc.m_source_printing.show_line_numbers_p = true;
5379 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5380 ASSERT_STREQ (" 2 | x = a;\n"
5381 " +++ |+ break;\n"
5382 " 3 | case 'b':\n"
5383 " | ^~~~~~~~~\n",
5384 pp_formatted_text (dc.printer));
5388 /* Verify that attempts to add text with a newline fail when the
5389 insertion point is *not* at the start of a line. */
5391 rich_location richloc (line_table, case_loc);
5392 richloc.add_fixit_insert_before (case_start, "break;\n");
5393 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5394 test_diagnostic_context dc;
5395 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5396 ASSERT_STREQ (" case 'b':\n"
5397 " ^~~~~~~~~\n",
5398 pp_formatted_text (dc.printer));
5402 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5403 of the file, where the fix-it is printed in a different line-span
5404 to the primary range of the diagnostic. */
5406 static void
5407 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5409 /* Create a tempfile and write some text to it.
5410 .........................0000000001111111.
5411 .........................1234567890123456. */
5412 const char *old_content = ("test (int ch)\n" /* line 1. */
5413 "{\n" /* line 2. */
5414 " putchar (ch);\n" /* line 3. */
5415 "}\n"); /* line 4. */
5417 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5418 line_table_test ltt (case_);
5420 const line_map_ordinary *ord_map = linemap_check_ordinary
5421 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5422 linemap_line_start (line_table, 1, 100);
5424 /* The primary range is the "putchar" token. */
5425 location_t putchar_start
5426 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5427 location_t putchar_finish
5428 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5429 location_t putchar_loc
5430 = make_location (putchar_start, putchar_start, putchar_finish);
5431 rich_location richloc (line_table, putchar_loc);
5433 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5434 location_t file_start
5435 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5436 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5438 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5439 return;
5442 test_diagnostic_context dc;
5443 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5444 ASSERT_STREQ ("FILENAME:1:1:\n"
5445 "+#include <stdio.h>\n"
5446 " test (int ch)\n"
5447 "FILENAME:3:2:\n"
5448 " putchar (ch);\n"
5449 " ^~~~~~~\n",
5450 pp_formatted_text (dc.printer));
5453 /* With line-numbering, the line spans are close enough to be
5454 consolidated, since it makes little sense to skip line 2. */
5456 test_diagnostic_context dc;
5457 dc.m_source_printing.show_line_numbers_p = true;
5458 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5459 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5460 " 1 | test (int ch)\n"
5461 " 2 | {\n"
5462 " 3 | putchar (ch);\n"
5463 " | ^~~~~~~\n",
5464 pp_formatted_text (dc.printer));
5468 /* Replacement fix-it hint containing a newline.
5469 This will fail, as newlines are only supported when inserting at the
5470 beginning of a line. */
5472 static void
5473 test_fixit_replace_containing_newline (const line_table_case &case_)
5475 /* Create a tempfile and write some text to it.
5476 .........................0000000001111.
5477 .........................1234567890123. */
5478 const char *old_content = "foo = bar ();\n";
5480 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5481 line_table_test ltt (case_);
5482 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5484 /* Replace the " = " with "\n = ", as if we were reformatting an
5485 overly long line. */
5486 location_t start = linemap_position_for_column (line_table, 4);
5487 location_t finish = linemap_position_for_column (line_table, 6);
5488 location_t loc = linemap_position_for_column (line_table, 13);
5489 rich_location richloc (line_table, loc);
5490 source_range range = source_range::from_locations (start, finish);
5491 richloc.add_fixit_replace (range, "\n =");
5493 /* Arbitrary newlines are not yet supported within fix-it hints, so
5494 the fix-it should not be displayed. */
5495 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5497 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5498 return;
5500 test_diagnostic_context dc;
5501 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5502 ASSERT_STREQ (" foo = bar ();\n"
5503 " ^\n",
5504 pp_formatted_text (dc.printer));
5507 /* Fix-it hint, attempting to delete a newline.
5508 This will fail, as we currently only support fix-it hints that
5509 affect one line at a time. */
5511 static void
5512 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5514 /* Create a tempfile and write some text to it.
5515 ..........................0000000001111.
5516 ..........................1234567890123. */
5517 const char *old_content = ("foo = bar (\n"
5518 " );\n");
5520 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5521 line_table_test ltt (case_);
5522 const line_map_ordinary *ord_map = linemap_check_ordinary
5523 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5524 linemap_line_start (line_table, 1, 100);
5526 /* Attempt to delete the " (\n...)". */
5527 location_t start
5528 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5529 location_t caret
5530 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5531 location_t finish
5532 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5533 location_t loc = make_location (caret, start, finish);
5534 rich_location richloc (line_table, loc);
5535 richloc. add_fixit_remove ();
5537 /* Fix-it hints that affect more than one line are not yet supported, so
5538 the fix-it should not be displayed. */
5539 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5541 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5542 return;
5544 test_diagnostic_context dc;
5545 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5546 ASSERT_STREQ (" foo = bar (\n"
5547 " ~^\n"
5548 " );\n"
5549 " ~ \n",
5550 pp_formatted_text (dc.printer));
5553 static void
5554 test_tab_expansion (const line_table_case &case_)
5556 /* Create a tempfile and write some text to it. This example uses a tabstop
5557 of 8, as the column numbers attempt to indicate:
5559 .....................000.01111111111.22222333333 display
5560 .....................123.90123456789.56789012345 columns */
5561 const char *content = " \t This: `\t' is a tab.\n";
5562 /* ....................000 00000011111 11111222222 byte
5563 ....................123 45678901234 56789012345 columns */
5565 const int tabstop = 8;
5566 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5567 const int first_non_ws_byte_col = 7;
5568 const int right_quote_byte_col = 15;
5569 const int last_byte_col = 25;
5570 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5572 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5573 line_table_test ltt (case_);
5574 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5576 /* Don't attempt to run the tests if column data might be unavailable. */
5577 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5578 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5579 return;
5581 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5582 into 11 spaces. Recall that print_line() also puts one space before
5583 everything too. */
5585 test_diagnostic_context dc;
5586 dc.m_tabstop = tabstop;
5587 rich_location richloc (line_table,
5588 linemap_position_for_column (line_table,
5589 first_non_ws_byte_col));
5590 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5591 test_layout.print_line (1);
5592 ASSERT_STREQ (" This: ` ' is a tab.\n"
5593 " ^\n",
5594 pp_formatted_text (dc.printer));
5597 /* Confirm the display width was tracked correctly across the internal tab
5598 as well. */
5600 test_diagnostic_context dc;
5601 dc.m_tabstop = tabstop;
5602 rich_location richloc (line_table,
5603 linemap_position_for_column (line_table,
5604 right_quote_byte_col));
5605 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5606 test_layout.print_line (1);
5607 ASSERT_STREQ (" This: ` ' is a tab.\n"
5608 " ^\n",
5609 pp_formatted_text (dc.printer));
5613 /* Verify that the escaping machinery can cope with a variety of different
5614 invalid bytes. */
5616 static void
5617 test_escaping_bytes_1 (const line_table_case &case_)
5619 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5620 const size_t sz = sizeof (content);
5621 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5622 line_table_test ltt (case_);
5623 const line_map_ordinary *ord_map = linemap_check_ordinary
5624 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5625 linemap_line_start (line_table, 1, 100);
5627 location_t finish
5628 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5629 strlen (content));
5631 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5632 return;
5634 /* Locations of the NUL and \v bytes. */
5635 location_t nul_loc
5636 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5637 location_t v_loc
5638 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5639 gcc_rich_location richloc (nul_loc);
5640 richloc.add_range (v_loc);
5643 test_diagnostic_context dc;
5644 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5645 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5646 " ^ ~\n",
5647 pp_formatted_text (dc.printer));
5649 richloc.set_escape_on_output (true);
5651 test_diagnostic_context dc;
5652 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5653 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5654 ASSERT_STREQ
5655 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5656 " ^~~~~~~~ ~~~~~~~~\n",
5657 pp_formatted_text (dc.printer));
5660 test_diagnostic_context dc;
5661 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5662 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5663 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5664 " ^~~~ ~~~~\n",
5665 pp_formatted_text (dc.printer));
5669 /* As above, but verify that we handle the initial byte of a line
5670 correctly. */
5672 static void
5673 test_escaping_bytes_2 (const line_table_case &case_)
5675 const char content[] = "\0after\n";
5676 const size_t sz = sizeof (content);
5677 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5678 line_table_test ltt (case_);
5679 const line_map_ordinary *ord_map = linemap_check_ordinary
5680 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5681 linemap_line_start (line_table, 1, 100);
5683 location_t finish
5684 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5685 strlen (content));
5687 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5688 return;
5690 /* Location of the NUL byte. */
5691 location_t nul_loc
5692 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5693 gcc_rich_location richloc (nul_loc);
5696 test_diagnostic_context dc;
5697 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5698 ASSERT_STREQ (" after\n"
5699 " ^\n",
5700 pp_formatted_text (dc.printer));
5702 richloc.set_escape_on_output (true);
5704 test_diagnostic_context dc;
5705 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5706 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5707 ASSERT_STREQ (" <U+0000>after\n"
5708 " ^~~~~~~~\n",
5709 pp_formatted_text (dc.printer));
5712 test_diagnostic_context dc;
5713 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5714 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5715 ASSERT_STREQ (" <00>after\n"
5716 " ^~~~\n",
5717 pp_formatted_text (dc.printer));
5721 /* Verify that line numbers are correctly printed for the case of
5722 a multiline range in which the width of the line numbers changes
5723 (e.g. from "9" to "10"). */
5725 static void
5726 test_line_numbers_multiline_range ()
5728 /* Create a tempfile and write some text to it. */
5729 pretty_printer pp;
5730 for (int i = 0; i < 20; i++)
5731 /* .........0000000001111111.
5732 .............1234567890123456. */
5733 pp_printf (&pp, "this is line %i\n", i + 1);
5734 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5735 line_table_test ltt;
5737 const line_map_ordinary *ord_map = linemap_check_ordinary
5738 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5739 linemap_line_start (line_table, 1, 100);
5741 /* Create a multi-line location, starting at the "line" of line 9, with
5742 a caret on the "is" of line 10, finishing on the "this" line 11. */
5744 location_t start
5745 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5746 location_t caret
5747 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5748 location_t finish
5749 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5750 location_t loc = make_location (caret, start, finish);
5752 test_diagnostic_context dc;
5753 dc.m_source_printing.show_line_numbers_p = true;
5754 dc.m_source_printing.min_margin_width = 0;
5755 gcc_rich_location richloc (loc);
5756 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5757 ASSERT_STREQ (" 9 | this is line 9\n"
5758 " | ~~~~~~\n"
5759 "10 | this is line 10\n"
5760 " | ~~~~~^~~~~~~~~~\n"
5761 "11 | this is line 11\n"
5762 " | ~~~~ \n",
5763 pp_formatted_text (dc.printer));
5766 /* Run all of the selftests within this file. */
5768 void
5769 diagnostic_show_locus_cc_tests ()
5771 test_line_span ();
5773 test_layout_range_for_single_point ();
5774 test_layout_range_for_single_line ();
5775 test_layout_range_for_multiple_lines ();
5777 test_display_widths ();
5779 for_each_line_table_case (test_layout_x_offset_display_utf8);
5780 for_each_line_table_case (test_layout_x_offset_display_tab);
5782 test_get_line_bytes_without_trailing_whitespace ();
5784 test_diagnostic_show_locus_unknown_location ();
5786 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5787 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5788 for_each_line_table_case (test_add_location_if_nearby);
5789 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5790 for_each_line_table_case (test_fixit_consolidation);
5791 for_each_line_table_case (test_overlapped_fixit_printing);
5792 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5793 for_each_line_table_case (test_overlapped_fixit_printing_2);
5794 for_each_line_table_case (test_fixit_insert_containing_newline);
5795 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5796 for_each_line_table_case (test_fixit_replace_containing_newline);
5797 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5798 for_each_line_table_case (test_tab_expansion);
5799 for_each_line_table_case (test_escaping_bytes_1);
5800 for_each_line_table_case (test_escaping_bytes_2);
5802 test_line_numbers_multiline_range ();
5805 } // namespace selftest
5807 #endif /* #if CHECKING_P */
5809 #if __GNUC__ >= 10
5810 # pragma GCC diagnostic pop
5811 #endif