C99 testsuite readiness: Compile more tests with -std=gnu89
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob4439d9a48046d1926948da0fb8d229258053bb95
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2023 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 #include "cpplib.h"
35 #ifdef HAVE_TERMIOS_H
36 # include <termios.h>
37 #endif
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
41 #endif
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
45 #if __GNUC__ >= 10
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
48 #endif
50 /* Classes for rendering source code and diagnostics, within an
51 anonymous namespace.
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
55 namespace {
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
59 this point. */
61 struct point_state
63 int range_idx;
64 bool draw_caret_p;
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
70 - normal text
71 - range 0 (the "primary location")
72 - range 1
73 - range 2
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
83 class colorizer
85 public:
86 colorizer (diagnostic_context *context,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
90 void set_range (int range_idx)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
97 set_state (0);
98 else
99 set_state (range_idx);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
105 private:
106 void set_state (int state);
107 void begin_state (int state);
108 void finish_state (int state);
109 const char *get_color_by_name (const char *);
111 private:
112 static const int STATE_NORMAL_TEXT = -1;
113 static const int STATE_FIXIT_INSERT = -2;
114 static const int STATE_FIXIT_DELETE = -3;
116 diagnostic_context *m_context;
117 diagnostic_t m_diagnostic_kind;
118 int m_current_state;
119 const char *m_range1;
120 const char *m_range2;
121 const char *m_fixit_insert;
122 const char *m_fixit_delete;
123 const char *m_stop_color;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
162 enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col : public expanded_location
177 public:
178 exploc_with_display_col (const expanded_location &exploc,
179 const cpp_char_column_policy &policy,
180 enum location_aspect aspect)
181 : expanded_location (exploc),
182 m_display_col (location_compute_display_column (exploc, policy))
184 if (exploc.column > 0)
186 /* m_display_col is now the final column of the byte.
187 If escaping has happened, we may want the first column instead. */
188 if (aspect != LOCATION_ASPECT_FINISH)
190 expanded_location prev_exploc (exploc);
191 prev_exploc.column--;
192 int prev_display_col
193 = (location_compute_display_column (prev_exploc, policy));
194 m_display_col = prev_display_col + 1;
199 int m_display_col;
203 /* A point within a layout_range; similar to an exploc_with_display_col,
204 but after filtering on file. */
206 class layout_point
208 public:
209 layout_point (const exploc_with_display_col &exploc)
210 : m_line (exploc.line)
212 m_columns[CU_BYTES] = exploc.column;
213 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
216 linenum_type m_line;
217 int m_columns[CU_NUM_UNITS];
220 /* A class for use by "class layout" below: a filtered location_range. */
222 class layout_range
224 public:
225 layout_range (const exploc_with_display_col &start_exploc,
226 const exploc_with_display_col &finish_exploc,
227 enum range_display_kind range_display_kind,
228 const exploc_with_display_col &caret_exploc,
229 unsigned original_idx,
230 const range_label *label);
232 bool contains_point (linenum_type row, int column,
233 enum column_unit col_unit) const;
234 bool intersects_line_p (linenum_type row) const;
236 layout_point m_start;
237 layout_point m_finish;
238 enum range_display_kind m_range_display_kind;
239 layout_point m_caret;
240 unsigned m_original_idx;
241 const range_label *m_label;
244 /* A struct for use by layout::print_source_line for telling
245 layout::print_annotation_line the extents of the source line that
246 it printed, so that underlines can be clipped appropriately. Units
247 are 1-based display columns. */
249 struct line_bounds
251 int m_first_non_ws_disp_col;
252 int m_last_non_ws_disp_col;
254 line_bounds ()
256 m_first_non_ws_disp_col = INT_MAX;
257 m_last_non_ws_disp_col = 0;
261 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
262 or "line 23"). During the layout ctor, layout::calculate_line_spans
263 splits the pertinent source lines into a list of disjoint line_span
264 instances (e.g. lines 5-10, lines 15-20, line 23). */
266 class line_span
268 public:
269 line_span (linenum_type first_line, linenum_type last_line)
270 : m_first_line (first_line), m_last_line (last_line)
272 gcc_assert (first_line <= last_line);
274 linenum_type get_first_line () const { return m_first_line; }
275 linenum_type get_last_line () const { return m_last_line; }
277 bool contains_line_p (linenum_type line) const
279 return line >= m_first_line && line <= m_last_line;
282 static int comparator (const void *p1, const void *p2)
284 const line_span *ls1 = (const line_span *)p1;
285 const line_span *ls2 = (const line_span *)p2;
286 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
287 if (first_line_cmp)
288 return first_line_cmp;
289 return compare (ls1->m_last_line, ls2->m_last_line);
292 linenum_type m_first_line;
293 linenum_type m_last_line;
296 #if CHECKING_P
298 /* Selftests for line_span. */
300 static void
301 test_line_span ()
303 line_span line_one (1, 1);
304 ASSERT_EQ (1, line_one.get_first_line ());
305 ASSERT_EQ (1, line_one.get_last_line ());
306 ASSERT_FALSE (line_one.contains_line_p (0));
307 ASSERT_TRUE (line_one.contains_line_p (1));
308 ASSERT_FALSE (line_one.contains_line_p (2));
310 line_span lines_1_to_3 (1, 3);
311 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
312 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
313 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
316 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
317 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
318 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
320 /* A linenum > 2^31. */
321 const linenum_type LARGEST_LINE = 0xffffffff;
322 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
323 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
326 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
327 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
330 #endif /* #if CHECKING_P */
332 /* A bundle of information containing how to print unicode
333 characters and bytes when quoting source code.
335 Provides a unified place to support escaping some subset
336 of characters to some format.
338 Extends char_column_policy; printing is split out to avoid
339 libcpp having to know about pretty_printer. */
341 struct char_display_policy : public cpp_char_column_policy
343 public:
344 char_display_policy (int tabstop,
345 int (*width_cb) (cppchar_t c),
346 void (*print_cb) (pretty_printer *pp,
347 const cpp_decoded_char &cp))
348 : cpp_char_column_policy (tabstop, width_cb),
349 m_print_cb (print_cb)
353 void (*m_print_cb) (pretty_printer *pp,
354 const cpp_decoded_char &cp);
357 /* A class to control the overall layout when printing a diagnostic.
359 The layout is determined within the constructor.
360 It is then printed by repeatedly calling the "print_source_line",
361 "print_annotation_line" and "print_any_fixits" methods.
363 We assume we have disjoint ranges. */
365 class layout
367 public:
368 layout (diagnostic_context *context,
369 rich_location *richloc,
370 diagnostic_t diagnostic_kind,
371 pretty_printer *pp = nullptr);
373 bool maybe_add_location_range (const location_range *loc_range,
374 unsigned original_idx,
375 bool restrict_to_current_line_spans);
377 int get_num_line_spans () const { return m_line_spans.length (); }
378 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
380 int get_linenum_width () const { return m_linenum_width; }
381 int get_x_offset_display () const { return m_x_offset_display; }
383 void print_gap_in_line_numbering ();
384 bool print_heading_for_line_span_index_p (int line_span_idx) const;
386 expanded_location get_expanded_location (const line_span *) const;
388 void print_line (linenum_type row);
390 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
392 private:
393 bool will_show_line_p (linenum_type row) const;
394 void print_leading_fixits (linenum_type row);
395 line_bounds print_source_line (linenum_type row, const char *line,
396 int line_bytes);
397 bool should_print_annotation_line_p (linenum_type row) const;
398 void start_annotation_line (char margin_char = ' ') const;
399 void print_annotation_line (linenum_type row, const line_bounds lbounds);
400 void print_any_labels (linenum_type row);
401 void print_trailing_fixits (linenum_type row);
403 bool annotation_line_showed_range_p (linenum_type line, int start_column,
404 int finish_column) const;
405 void show_ruler (int max_column) const;
407 bool validate_fixit_hint_p (const fixit_hint *hint);
409 void calculate_line_spans ();
410 void calculate_linenum_width ();
411 void calculate_x_offset_display ();
413 void print_newline ();
415 bool
416 get_state_at_point (/* Inputs. */
417 linenum_type row, int column,
418 int first_non_ws, int last_non_ws,
419 enum column_unit col_unit,
420 /* Outputs. */
421 point_state *out_state);
424 get_x_bound_for_row (linenum_type row, int caret_column,
425 int last_non_ws);
427 void
428 move_to_column (int *column, int dest_column, bool add_left_margin);
430 private:
431 diagnostic_context *m_context;
432 pretty_printer *m_pp;
433 char_display_policy m_policy;
434 location_t m_primary_loc;
435 exploc_with_display_col m_exploc;
436 colorizer m_colorizer;
437 bool m_colorize_source_p;
438 bool m_show_labels_p;
439 bool m_show_line_numbers_p;
440 bool m_diagnostic_path_p;
441 auto_vec <layout_range> m_layout_ranges;
442 auto_vec <const fixit_hint *> m_fixit_hints;
443 auto_vec <line_span> m_line_spans;
444 int m_linenum_width;
445 int m_x_offset_display;
446 bool m_escape_on_output;
449 /* Implementation of "class colorizer". */
451 /* The constructor for "colorizer". Lookup and store color codes for the
452 different kinds of things we might need to print. */
454 colorizer::colorizer (diagnostic_context *context,
455 diagnostic_t diagnostic_kind) :
456 m_context (context),
457 m_diagnostic_kind (diagnostic_kind),
458 m_current_state (STATE_NORMAL_TEXT)
460 m_range1 = get_color_by_name ("range1");
461 m_range2 = get_color_by_name ("range2");
462 m_fixit_insert = get_color_by_name ("fixit-insert");
463 m_fixit_delete = get_color_by_name ("fixit-delete");
464 m_stop_color = colorize_stop (pp_show_color (context->printer));
467 /* The destructor for "colorize". If colorization is on, print a code to
468 turn it off. */
470 colorizer::~colorizer ()
472 finish_state (m_current_state);
475 /* Update state, printing color codes if necessary if there's a state
476 change. */
478 void
479 colorizer::set_state (int new_state)
481 if (m_current_state != new_state)
483 finish_state (m_current_state);
484 m_current_state = new_state;
485 begin_state (new_state);
489 /* Turn on any colorization for STATE. */
491 void
492 colorizer::begin_state (int state)
494 switch (state)
496 case STATE_NORMAL_TEXT:
497 break;
499 case STATE_FIXIT_INSERT:
500 pp_string (m_context->printer, m_fixit_insert);
501 break;
503 case STATE_FIXIT_DELETE:
504 pp_string (m_context->printer, m_fixit_delete);
505 break;
507 case 0:
508 /* Make range 0 be the same color as the "kind" text
509 (error vs warning vs note). */
510 pp_string
511 (m_context->printer,
512 colorize_start (pp_show_color (m_context->printer),
513 diagnostic_get_color_for_kind (m_diagnostic_kind)));
514 break;
516 case 1:
517 pp_string (m_context->printer, m_range1);
518 break;
520 case 2:
521 pp_string (m_context->printer, m_range2);
522 break;
524 default:
525 /* For ranges beyond 2, alternate between color 1 and color 2. */
527 gcc_assert (state > 2);
528 pp_string (m_context->printer,
529 state % 2 ? m_range1 : m_range2);
531 break;
535 /* Turn off any colorization for STATE. */
537 void
538 colorizer::finish_state (int state)
540 if (state != STATE_NORMAL_TEXT)
541 pp_string (m_context->printer, m_stop_color);
544 /* Get the color code for NAME (or the empty string if
545 colorization is disabled). */
547 const char *
548 colorizer::get_color_by_name (const char *name)
550 return colorize_start (pp_show_color (m_context->printer), name);
553 /* Implementation of class layout_range. */
555 /* The constructor for class layout_range.
556 Initialize various layout_point fields from expanded_location
557 equivalents; we've already filtered on file. */
559 layout_range::layout_range (const exploc_with_display_col &start_exploc,
560 const exploc_with_display_col &finish_exploc,
561 enum range_display_kind range_display_kind,
562 const exploc_with_display_col &caret_exploc,
563 unsigned original_idx,
564 const range_label *label)
565 : m_start (start_exploc),
566 m_finish (finish_exploc),
567 m_range_display_kind (range_display_kind),
568 m_caret (caret_exploc),
569 m_original_idx (original_idx),
570 m_label (label)
574 /* Is (column, row) within the given range?
575 We've already filtered on the file.
577 Ranges are closed (both limits are within the range).
579 Example A: a single-line range:
580 start: (col=22, line=2)
581 finish: (col=38, line=2)
583 |00000011111111112222222222333333333344444444444
584 |34567890123456789012345678901234567890123456789
585 --+-----------------------------------------------
586 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
587 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
588 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
590 Example B: a multiline range with
591 start: (col=14, line=3)
592 finish: (col=08, line=5)
594 |00000011111111112222222222333333333344444444444
595 |34567890123456789012345678901234567890123456789
596 --+-----------------------------------------------
597 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
598 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
599 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
600 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
601 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
602 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
603 --+-----------------------------------------------
605 Legend:
606 - 'b' indicates a point *before* the range
607 - 'S' indicates the start of the range
608 - 'w' indicates a point within the range
609 - 'F' indicates the finish of the range (which is
610 within it).
611 - 'a' indicates a subsequent point *after* the range.
613 COL_UNIT controls whether we check the byte column or
614 the display column; one or the other is more convenient
615 depending on the context. */
617 bool
618 layout_range::contains_point (linenum_type row, int column,
619 enum column_unit col_unit) const
621 gcc_assert (m_start.m_line <= m_finish.m_line);
622 /* ...but the equivalent isn't true for the columns;
623 consider example B in the comment above. */
625 if (row < m_start.m_line)
626 /* Points before the first line of the range are
627 outside it (corresponding to line 01 in example A
628 and lines 01 and 02 in example B above). */
629 return false;
631 if (row == m_start.m_line)
632 /* On same line as start of range (corresponding
633 to line 02 in example A and line 03 in example B). */
635 if (column < m_start.m_columns[col_unit])
636 /* Points on the starting line of the range, but
637 before the column in which it begins. */
638 return false;
640 if (row < m_finish.m_line)
641 /* This is a multiline range; the point
642 is within it (corresponds to line 03 in example B
643 from column 14 onwards) */
644 return true;
645 else
647 /* This is a single-line range. */
648 gcc_assert (row == m_finish.m_line);
649 return column <= m_finish.m_columns[col_unit];
653 /* The point is in a line beyond that containing the
654 start of the range: lines 03 onwards in example A,
655 and lines 04 onwards in example B. */
656 gcc_assert (row > m_start.m_line);
658 if (row > m_finish.m_line)
659 /* The point is beyond the final line of the range
660 (lines 03 onwards in example A, and lines 06 onwards
661 in example B). */
662 return false;
664 if (row < m_finish.m_line)
666 /* The point is in a line that's fully within a multiline
667 range (e.g. line 04 in example B). */
668 gcc_assert (m_start.m_line < m_finish.m_line);
669 return true;
672 gcc_assert (row == m_finish.m_line);
674 return column <= m_finish.m_columns[col_unit];
677 /* Does this layout_range contain any part of line ROW? */
679 bool
680 layout_range::intersects_line_p (linenum_type row) const
682 gcc_assert (m_start.m_line <= m_finish.m_line);
683 if (row < m_start.m_line)
684 return false;
685 if (row > m_finish.m_line)
686 return false;
687 return true;
690 #if CHECKING_P
692 /* Default for when we don't care what the tab expansion is set to. */
693 static const int def_tabstop = 8;
695 static cpp_char_column_policy def_policy ()
697 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
700 /* Create some expanded locations for testing layout_range. The filename
701 member of the explocs is set to the empty string. This member will only be
702 inspected by the calls to location_compute_display_column() made from the
703 layout_point constructors. That function will check for an empty filename
704 argument and not attempt to open it, rather treating the non-existent data
705 as if the display width were the same as the byte count. Tests exercising a
706 real difference between byte count and display width are performed later,
707 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
709 static layout_range
710 make_range (int start_line, int start_col, int end_line, int end_col)
712 const expanded_location start_exploc
713 = {"", start_line, start_col, NULL, false};
714 const expanded_location finish_exploc
715 = {"", end_line, end_col, NULL, false};
716 return layout_range (exploc_with_display_col (start_exploc, def_policy (),
717 LOCATION_ASPECT_START),
718 exploc_with_display_col (finish_exploc, def_policy (),
719 LOCATION_ASPECT_FINISH),
720 SHOW_RANGE_WITHOUT_CARET,
721 exploc_with_display_col (start_exploc, def_policy (),
722 LOCATION_ASPECT_CARET),
723 0, NULL);
726 /* Selftests for layout_range::contains_point and
727 layout_range::intersects_line_p. */
729 /* Selftest for layout_range, where the layout_range
730 is a range with start==end i.e. a single point. */
732 static void
733 test_layout_range_for_single_point ()
735 layout_range point = make_range (7, 10, 7, 10);
737 /* Tests for layout_range::contains_point. */
739 for (int i = 0; i != CU_NUM_UNITS; ++i)
741 const enum column_unit col_unit = (enum column_unit) i;
743 /* Before the line. */
744 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
746 /* On the line, but before start. */
747 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
749 /* At the point. */
750 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
752 /* On the line, after the point. */
753 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
755 /* After the line. */
756 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
759 /* Tests for layout_range::intersects_line_p. */
760 ASSERT_FALSE (point.intersects_line_p (6));
761 ASSERT_TRUE (point.intersects_line_p (7));
762 ASSERT_FALSE (point.intersects_line_p (8));
765 /* Selftest for layout_range, where the layout_range
766 is the single-line range shown as "Example A" above. */
768 static void
769 test_layout_range_for_single_line ()
771 layout_range example_a = make_range (2, 22, 2, 38);
773 /* Tests for layout_range::contains_point. */
775 for (int i = 0; i != CU_NUM_UNITS; ++i)
777 const enum column_unit col_unit = (enum column_unit) i;
779 /* Before the line. */
780 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
782 /* On the line, but before start. */
783 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
785 /* On the line, at the start. */
786 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
788 /* On the line, within the range. */
789 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
791 /* On the line, at the end. */
792 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
794 /* On the line, after the end. */
795 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
797 /* After the line. */
798 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
801 /* Tests for layout_range::intersects_line_p. */
802 ASSERT_FALSE (example_a.intersects_line_p (1));
803 ASSERT_TRUE (example_a.intersects_line_p (2));
804 ASSERT_FALSE (example_a.intersects_line_p (3));
807 /* Selftest for layout_range, where the layout_range
808 is the multi-line range shown as "Example B" above. */
810 static void
811 test_layout_range_for_multiple_lines ()
813 layout_range example_b = make_range (3, 14, 5, 8);
815 /* Tests for layout_range::contains_point. */
817 for (int i = 0; i != CU_NUM_UNITS; ++i)
819 const enum column_unit col_unit = (enum column_unit) i;
821 /* Before first line. */
822 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
824 /* On the first line, but before start. */
825 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
827 /* At the start. */
828 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
830 /* On the first line, within the range. */
831 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
833 /* On an interior line.
834 The column number should not matter; try various boundary
835 values. */
836 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
837 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
838 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
839 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
840 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
841 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
842 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
844 /* On the final line, before the end. */
845 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
847 /* On the final line, at the end. */
848 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
850 /* On the final line, after the end. */
851 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
853 /* After the line. */
854 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
857 /* Tests for layout_range::intersects_line_p. */
858 ASSERT_FALSE (example_b.intersects_line_p (2));
859 ASSERT_TRUE (example_b.intersects_line_p (3));
860 ASSERT_TRUE (example_b.intersects_line_p (4));
861 ASSERT_TRUE (example_b.intersects_line_p (5));
862 ASSERT_FALSE (example_b.intersects_line_p (6));
865 #endif /* #if CHECKING_P */
867 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
868 (still in bytes, not display cols) without any trailing whitespace. */
870 static int
871 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
873 int result = line_bytes;
874 while (result > 0)
876 char ch = line[result - 1];
877 if (ch == ' ' || ch == '\t' || ch == '\r')
878 result--;
879 else
880 break;
882 gcc_assert (result >= 0);
883 gcc_assert (result <= line_bytes);
884 gcc_assert (result == 0 ||
885 (line[result - 1] != ' '
886 && line[result -1] != '\t'
887 && line[result -1] != '\r'));
888 return result;
891 #if CHECKING_P
893 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
895 static void
896 assert_eq (const char *line, int expected_bytes)
898 int actual_value
899 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
900 ASSERT_EQ (actual_value, expected_bytes);
903 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
904 various inputs. It is not required to handle newlines. */
906 static void
907 test_get_line_bytes_without_trailing_whitespace ()
909 assert_eq ("", 0);
910 assert_eq (" ", 0);
911 assert_eq ("\t", 0);
912 assert_eq ("\r", 0);
913 assert_eq ("hello world", 11);
914 assert_eq ("hello world ", 11);
915 assert_eq ("hello world \t\t ", 11);
916 assert_eq ("hello world\r", 11);
919 #endif /* #if CHECKING_P */
921 /* Helper function for layout's ctor, for sanitizing locations relative
922 to the primary location within a diagnostic.
924 Compare LOC_A and LOC_B to see if it makes sense to print underlines
925 connecting their expanded locations. Doing so is only guaranteed to
926 make sense if the locations share the same macro expansion "history"
927 i.e. they can be traced through the same macro expansions, eventually
928 reaching an ordinary map.
930 This may be too strong a condition, but it effectively sanitizes
931 PR c++/70105, which has an example of printing an expression where the
932 final location of the expression is in a different macro, which
933 erroneously was leading to hundreds of lines of irrelevant source
934 being printed. */
936 static bool
937 compatible_locations_p (location_t loc_a, location_t loc_b)
939 if (IS_ADHOC_LOC (loc_a))
940 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
941 if (IS_ADHOC_LOC (loc_b))
942 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
944 /* If either location is one of the special locations outside of a
945 linemap, they are only compatible if they are equal. */
946 if (loc_a < RESERVED_LOCATION_COUNT
947 || loc_b < RESERVED_LOCATION_COUNT)
948 return loc_a == loc_b;
950 const line_map *map_a = linemap_lookup (line_table, loc_a);
951 linemap_assert (map_a);
953 const line_map *map_b = linemap_lookup (line_table, loc_b);
954 linemap_assert (map_b);
956 /* Are they within the same map? */
957 if (map_a == map_b)
959 /* Are both within the same macro expansion? */
960 if (linemap_macro_expansion_map_p (map_a))
962 /* If so, then they're only compatible if either both are
963 from the macro definition, or both from the macro arguments. */
964 bool loc_a_from_defn
965 = linemap_location_from_macro_definition_p (line_table, loc_a);
966 bool loc_b_from_defn
967 = linemap_location_from_macro_definition_p (line_table, loc_b);
968 if (loc_a_from_defn != loc_b_from_defn)
969 return false;
971 /* Expand each location towards the spelling location, and
972 recurse. */
973 const line_map_macro *macro_map = linemap_check_macro (map_a);
974 location_t loc_a_toward_spelling
975 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
976 macro_map,
977 loc_a);
978 location_t loc_b_toward_spelling
979 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
980 macro_map,
981 loc_b);
982 return compatible_locations_p (loc_a_toward_spelling,
983 loc_b_toward_spelling);
986 /* Otherwise they are within the same ordinary map. */
987 return true;
989 else
991 /* Within different maps. */
993 /* If either is within a macro expansion, they are incompatible. */
994 if (linemap_macro_expansion_map_p (map_a)
995 || linemap_macro_expansion_map_p (map_b))
996 return false;
998 /* Within two different ordinary maps; they are compatible iff they
999 are in the same file. */
1000 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1001 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1002 return ord_map_a->to_file == ord_map_b->to_file;
1006 /* Comparator for sorting fix-it hints. */
1008 static int
1009 fixit_cmp (const void *p_a, const void *p_b)
1011 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1012 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1013 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1016 /* Callbacks for use when not escaping the source. */
1018 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1020 /* Callback for char_display_policy::m_print_cb for printing source chars
1021 when not escaping the source. */
1023 static void
1024 default_print_decoded_ch (pretty_printer *pp,
1025 const cpp_decoded_char &decoded_ch)
1027 for (const char *ptr = decoded_ch.m_start_byte;
1028 ptr != decoded_ch.m_next_byte; ptr++)
1030 if (*ptr == '\0' || *ptr == '\r')
1032 pp_space (pp);
1033 continue;
1036 pp_character (pp, *ptr);
1040 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1042 static const int width_per_escaped_byte = 4;
1044 /* Callback for char_column_policy::m_width_cb for determining the
1045 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1047 static int
1048 escape_as_bytes_width (cppchar_t ch)
1050 if (ch < 0x80 && ISPRINT (ch))
1051 return cpp_wcwidth (ch);
1052 else
1054 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1055 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1056 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1057 return 4 * width_per_escaped_byte;
1061 /* Callback for char_display_policy::m_print_cb for printing source chars
1062 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1064 static void
1065 escape_as_bytes_print (pretty_printer *pp,
1066 const cpp_decoded_char &decoded_ch)
1068 if (!decoded_ch.m_valid_ch)
1070 for (const char *iter = decoded_ch.m_start_byte;
1071 iter != decoded_ch.m_next_byte; ++iter)
1073 char buf[16];
1074 sprintf (buf, "<%02x>", (unsigned char)*iter);
1075 pp_string (pp, buf);
1077 return;
1080 cppchar_t ch = decoded_ch.m_ch;
1081 if (ch < 0x80 && ISPRINT (ch))
1082 pp_character (pp, ch);
1083 else
1085 for (const char *iter = decoded_ch.m_start_byte;
1086 iter < decoded_ch.m_next_byte; ++iter)
1088 char buf[16];
1089 sprintf (buf, "<%02x>", (unsigned char)*iter);
1090 pp_string (pp, buf);
1095 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1097 /* Callback for char_column_policy::m_width_cb for determining the
1098 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1100 static int
1101 escape_as_unicode_width (cppchar_t ch)
1103 if (ch < 0x80 && ISPRINT (ch))
1104 return cpp_wcwidth (ch);
1105 else
1107 // Width of "<U+%04x>"
1108 if (ch > 0xfffff)
1109 return 10;
1110 else if (ch > 0xffff)
1111 return 9;
1112 else
1113 return 8;
1117 /* Callback for char_display_policy::m_print_cb for printing source chars
1118 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1120 static void
1121 escape_as_unicode_print (pretty_printer *pp,
1122 const cpp_decoded_char &decoded_ch)
1124 if (!decoded_ch.m_valid_ch)
1126 escape_as_bytes_print (pp, decoded_ch);
1127 return;
1130 cppchar_t ch = decoded_ch.m_ch;
1131 if (ch < 0x80 && ISPRINT (ch))
1132 pp_character (pp, ch);
1133 else
1135 char buf[16];
1136 sprintf (buf, "<U+%04X>", ch);
1137 pp_string (pp, buf);
1141 /* Populate a char_display_policy based on DC and RICHLOC. */
1143 static char_display_policy
1144 make_policy (const diagnostic_context &dc,
1145 const rich_location &richloc)
1147 /* The default is to not escape non-ASCII bytes. */
1148 char_display_policy result
1149 (dc.tabstop, cpp_wcwidth, default_print_decoded_ch);
1151 /* If the diagnostic suggests escaping non-ASCII bytes, then
1152 use policy from user-supplied options. */
1153 if (richloc.escape_on_output_p ())
1155 result.m_undecoded_byte_width = width_per_escaped_byte;
1156 switch (dc.escape_format)
1158 default:
1159 gcc_unreachable ();
1160 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1161 result.m_width_cb = escape_as_unicode_width;
1162 result.m_print_cb = escape_as_unicode_print;
1163 break;
1164 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1165 result.m_width_cb = escape_as_bytes_width;
1166 result.m_print_cb = escape_as_bytes_print;
1167 break;
1171 return result;
1174 /* Implementation of class layout. */
1176 /* Constructor for class layout.
1178 Filter the ranges from the rich_location to those that we can
1179 sanely print, populating m_layout_ranges and m_fixit_hints.
1180 Determine the range of lines that we will print, splitting them
1181 up into an ordered list of disjoint spans of contiguous line numbers.
1182 Determine m_x_offset_display, to ensure that the primary caret
1183 will fit within the max_width provided by the diagnostic_context. */
1185 layout::layout (diagnostic_context * context,
1186 rich_location *richloc,
1187 diagnostic_t diagnostic_kind,
1188 pretty_printer *pp)
1189 : m_context (context),
1190 m_pp (pp ? pp : context->printer),
1191 m_policy (make_policy (*context, *richloc)),
1192 m_primary_loc (richloc->get_range (0)->m_loc),
1193 m_exploc (richloc->get_expanded_location (0), m_policy,
1194 LOCATION_ASPECT_CARET),
1195 m_colorizer (context, diagnostic_kind),
1196 m_colorize_source_p (context->m_source_printing.colorize_source_p),
1197 m_show_labels_p (context->m_source_printing.show_labels_p),
1198 m_show_line_numbers_p (context->m_source_printing.show_line_numbers_p),
1199 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1200 m_layout_ranges (richloc->get_num_locations ()),
1201 m_fixit_hints (richloc->get_num_fixit_hints ()),
1202 m_line_spans (1 + richloc->get_num_locations ()),
1203 m_linenum_width (0),
1204 m_x_offset_display (0),
1205 m_escape_on_output (richloc->escape_on_output_p ())
1207 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
1209 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1210 Ignore any ranges that are awkward to handle. */
1211 const location_range *loc_range = richloc->get_range (idx);
1212 maybe_add_location_range (loc_range, idx, false);
1215 /* Populate m_fixit_hints, filtering to only those that are in the
1216 same file. */
1217 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1219 const fixit_hint *hint = richloc->get_fixit_hint (i);
1220 if (validate_fixit_hint_p (hint))
1221 m_fixit_hints.safe_push (hint);
1224 /* Sort m_fixit_hints. */
1225 m_fixit_hints.qsort (fixit_cmp);
1227 /* Populate the indicated members. */
1228 calculate_line_spans ();
1229 calculate_linenum_width ();
1230 calculate_x_offset_display ();
1232 if (context->m_source_printing.show_ruler_p)
1233 show_ruler (m_x_offset_display + m_context->m_source_printing.max_width);
1237 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1238 those that we can sanely print.
1240 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1241 (for use as extrinsic state by label ranges FIXME).
1243 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1244 filtered against this layout instance's current line spans: it
1245 will only be added if the location is fully within the lines
1246 already specified by other locations.
1248 Return true iff LOC_RANGE was added. */
1250 bool
1251 layout::maybe_add_location_range (const location_range *loc_range,
1252 unsigned original_idx,
1253 bool restrict_to_current_line_spans)
1255 gcc_assert (loc_range);
1257 /* Split the "range" into caret and range information. */
1258 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1260 /* Expand the various locations. */
1261 expanded_location start
1262 = linemap_client_expand_location_to_spelling_point
1263 (src_range.m_start, LOCATION_ASPECT_START);
1264 expanded_location finish
1265 = linemap_client_expand_location_to_spelling_point
1266 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1267 expanded_location caret
1268 = linemap_client_expand_location_to_spelling_point
1269 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1271 /* If any part of the range isn't in the same file as the primary
1272 location of this diagnostic, ignore the range. */
1273 if (start.file != m_exploc.file)
1274 return false;
1275 if (finish.file != m_exploc.file)
1276 return false;
1277 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1278 if (caret.file != m_exploc.file)
1279 return false;
1281 /* Sanitize the caret location for non-primary ranges. */
1282 if (m_layout_ranges.length () > 0)
1283 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1284 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1285 /* Discard any non-primary ranges that can't be printed
1286 sanely relative to the primary location. */
1287 return false;
1289 /* Everything is now known to be in the correct source file,
1290 but it may require further sanitization. */
1291 layout_range ri (exploc_with_display_col (start, m_policy,
1292 LOCATION_ASPECT_START),
1293 exploc_with_display_col (finish, m_policy,
1294 LOCATION_ASPECT_FINISH),
1295 loc_range->m_range_display_kind,
1296 exploc_with_display_col (caret, m_policy,
1297 LOCATION_ASPECT_CARET),
1298 original_idx, loc_range->m_label);
1300 /* If we have a range that finishes before it starts (perhaps
1301 from something built via macro expansion), printing the
1302 range is likely to be nonsensical. Also, attempting to do so
1303 breaks assumptions within the printing code (PR c/68473).
1304 Similarly, don't attempt to print ranges if one or both ends
1305 of the range aren't sane to print relative to the
1306 primary location (PR c++/70105). */
1307 if (start.line > finish.line
1308 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1309 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1311 /* Is this the primary location? */
1312 if (m_layout_ranges.length () == 0)
1314 /* We want to print the caret for the primary location, but
1315 we must sanitize away m_start and m_finish. */
1316 ri.m_start = ri.m_caret;
1317 ri.m_finish = ri.m_caret;
1319 else
1320 /* This is a non-primary range; ignore it. */
1321 return false;
1324 /* Potentially filter to just the lines already specified by other
1325 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1326 The layout ctor doesn't use it, and can't because m_line_spans
1327 hasn't been set up at that point. */
1328 if (restrict_to_current_line_spans)
1330 if (!will_show_line_p (start.line))
1331 return false;
1332 if (!will_show_line_p (finish.line))
1333 return false;
1334 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1335 if (!will_show_line_p (caret.line))
1336 return false;
1339 /* Passed all the tests; add the range to m_layout_ranges so that
1340 it will be printed. */
1341 m_layout_ranges.safe_push (ri);
1342 return true;
1345 /* Return true iff ROW is within one of the line spans for this layout. */
1347 bool
1348 layout::will_show_line_p (linenum_type row) const
1350 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1351 line_span_idx++)
1353 const line_span *line_span = get_line_span (line_span_idx);
1354 if (line_span->contains_line_p (row))
1355 return true;
1357 return false;
1360 /* Print a line showing a gap in the line numbers, for showing the boundary
1361 between two line spans. */
1363 void
1364 layout::print_gap_in_line_numbering ()
1366 gcc_assert (m_show_line_numbers_p);
1368 pp_emit_prefix (m_pp);
1370 for (int i = 0; i < m_linenum_width + 1; i++)
1371 pp_character (m_pp, '.');
1373 pp_newline (m_pp);
1376 /* Return true iff we should print a heading when starting the
1377 line span with the given index. */
1379 bool
1380 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1382 /* We print a heading for every change of line span, hence for every
1383 line span after the initial one. */
1384 if (line_span_idx > 0)
1385 return true;
1387 /* We also do it for the initial span if the primary location of the
1388 diagnostic is in a different span. */
1389 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1390 return true;
1392 return false;
1395 /* Get an expanded_location for the first location of interest within
1396 the given line_span.
1397 Used when printing a heading to indicate a new line span. */
1399 expanded_location
1400 layout::get_expanded_location (const line_span *line_span) const
1402 /* Whenever possible, use the caret location. */
1403 if (line_span->contains_line_p (m_exploc.line))
1404 return m_exploc;
1406 /* Otherwise, use the start of the first range that's present
1407 within the line_span. */
1408 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1410 const layout_range *lr = &m_layout_ranges[i];
1411 if (line_span->contains_line_p (lr->m_start.m_line))
1413 expanded_location exploc = m_exploc;
1414 exploc.line = lr->m_start.m_line;
1415 exploc.column = lr->m_start.m_columns[CU_BYTES];
1416 return exploc;
1420 /* Otherwise, use the location of the first fixit-hint present within
1421 the line_span. */
1422 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1424 const fixit_hint *hint = m_fixit_hints[i];
1425 location_t loc = hint->get_start_loc ();
1426 expanded_location exploc = expand_location (loc);
1427 if (line_span->contains_line_p (exploc.line))
1428 return exploc;
1431 /* It should not be possible to have a line span that didn't
1432 contain any of the layout_range or fixit_hint instances. */
1433 gcc_unreachable ();
1434 return m_exploc;
1437 /* Determine if HINT is meaningful to print within this layout. */
1439 bool
1440 layout::validate_fixit_hint_p (const fixit_hint *hint)
1442 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1443 return false;
1444 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1445 return false;
1447 return true;
1450 /* Determine the range of lines affected by HINT.
1451 This assumes that HINT has already been filtered by
1452 validate_fixit_hint_p, and so affects the correct source file. */
1454 static line_span
1455 get_line_span_for_fixit_hint (const fixit_hint *hint)
1457 gcc_assert (hint);
1459 int start_line = LOCATION_LINE (hint->get_start_loc ());
1461 /* For line-insertion fix-it hints, add the previous line to the
1462 span, to give the user more context on the proposed change. */
1463 if (hint->ends_with_newline_p ())
1464 if (start_line > 1)
1465 start_line--;
1467 return line_span (start_line,
1468 LOCATION_LINE (hint->get_next_loc ()));
1471 /* We want to print the pertinent source code at a diagnostic. The
1472 rich_location can contain multiple locations. This will have been
1473 filtered into m_exploc (the caret for the primary location) and
1474 m_layout_ranges, for those ranges within the same source file.
1476 We will print a subset of the lines within the source file in question,
1477 as a collection of "spans" of lines.
1479 This function populates m_line_spans with an ordered, disjoint list of
1480 the line spans of interest.
1482 Printing a gap between line spans takes one line, so, when printing
1483 line numbers, we allow a gap of up to one line between spans when
1484 merging, since it makes more sense to print the source line rather than a
1485 "gap-in-line-numbering" line. When not printing line numbers, it's
1486 better to be more explicit about what's going on, so keeping them as
1487 separate spans is preferred.
1489 For example, if the primary range is on lines 8-10, with secondary ranges
1490 covering lines 5-6 and lines 13-15:
1493 005 |RANGE 1
1494 006 |RANGE 1
1496 008 |PRIMARY RANGE
1497 009 |PRIMARY CARET
1498 010 |PRIMARY RANGE
1501 013 |RANGE 2
1502 014 |RANGE 2
1503 015 |RANGE 2
1506 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1508 With line numbering off (with span headers), we want three spans: lines 5-6,
1509 lines 8-10, and lines 13-15. */
1511 void
1512 layout::calculate_line_spans ()
1514 /* This should only be called once, by the ctor. */
1515 gcc_assert (m_line_spans.length () == 0);
1517 /* Populate tmp_spans with individual spans, for each of
1518 m_exploc, and for m_layout_ranges. */
1519 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1520 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1521 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1523 const layout_range *lr = &m_layout_ranges[i];
1524 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1525 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1526 lr->m_finish.m_line));
1529 /* Also add spans for any fix-it hints, in case they cover other lines. */
1530 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1532 const fixit_hint *hint = m_fixit_hints[i];
1533 gcc_assert (hint);
1534 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1537 /* Sort them. */
1538 tmp_spans.qsort(line_span::comparator);
1540 /* Now iterate through tmp_spans, copying into m_line_spans, and
1541 combining where possible. */
1542 gcc_assert (tmp_spans.length () > 0);
1543 m_line_spans.safe_push (tmp_spans[0]);
1544 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1546 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1547 const line_span *next = &tmp_spans[i];
1548 gcc_assert (next->m_first_line >= current->m_first_line);
1549 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1550 if ((linenum_arith_t)next->m_first_line
1551 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1553 /* We can merge them. */
1554 if (next->m_last_line > current->m_last_line)
1555 current->m_last_line = next->m_last_line;
1557 else
1559 /* No merger possible. */
1560 m_line_spans.safe_push (*next);
1564 /* Verify the result, in m_line_spans. */
1565 gcc_assert (m_line_spans.length () > 0);
1566 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1568 const line_span *prev = &m_line_spans[i - 1];
1569 const line_span *next = &m_line_spans[i];
1570 /* The individual spans must be sane. */
1571 gcc_assert (prev->m_first_line <= prev->m_last_line);
1572 gcc_assert (next->m_first_line <= next->m_last_line);
1573 /* The spans must be ordered. */
1574 gcc_assert (prev->m_first_line < next->m_first_line);
1575 /* There must be a gap of at least one line between separate spans. */
1576 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1580 /* Determine how many display columns need to be reserved for line numbers,
1581 based on the largest line number that will be needed, and populate
1582 m_linenum_width. */
1584 void
1585 layout::calculate_linenum_width ()
1587 gcc_assert (m_line_spans.length () > 0);
1588 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1589 int highest_line = last_span->m_last_line;
1590 if (highest_line < 0)
1591 highest_line = 0;
1592 m_linenum_width = num_digits (highest_line);
1593 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1594 if (m_line_spans.length () > 1)
1595 m_linenum_width = MAX (m_linenum_width, 3);
1596 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1597 after the line number. */
1598 m_linenum_width = MAX (m_linenum_width,
1599 m_context->m_source_printing.min_margin_width - 1);
1602 /* Calculate m_x_offset_display, which improves readability in case the source
1603 line of interest is longer than the user's display. All lines output will be
1604 shifted to the left (so that their beginning is no longer displayed) by
1605 m_x_offset_display display columns, so that the caret is in a reasonable
1606 location. */
1608 void
1609 layout::calculate_x_offset_display ()
1611 m_x_offset_display = 0;
1613 const int max_width = m_context->m_source_printing.max_width;
1614 if (!max_width)
1616 /* Nothing to do, the width is not capped. */
1617 return;
1620 const char_span line = location_get_source_line (m_exploc.file,
1621 m_exploc.line);
1622 if (!line)
1624 /* Nothing to do, we couldn't find the source line. */
1625 return;
1627 int caret_display_column = m_exploc.m_display_col;
1628 const int line_bytes
1629 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1630 line.length ());
1631 int eol_display_column
1632 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1633 if (caret_display_column > eol_display_column
1634 || !caret_display_column)
1636 /* This does not make sense, so don't try to do anything in this case. */
1637 return;
1640 /* Adjust caret and eol positions to include the left margin. If we are
1641 outputting line numbers, then the left margin is equal to m_linenum_width
1642 plus three for the " | " which follows it. Otherwise the left margin width
1643 is equal to 1, because layout::print_source_line() will prefix each line
1644 with a space. */
1645 const int source_display_cols = eol_display_column;
1646 int left_margin_size = 1;
1647 if (m_show_line_numbers_p)
1648 left_margin_size = m_linenum_width + 3;
1649 caret_display_column += left_margin_size;
1650 eol_display_column += left_margin_size;
1652 if (eol_display_column <= max_width)
1654 /* Nothing to do, everything fits in the display. */
1655 return;
1658 /* The line is too long for the display. Calculate an offset such that the
1659 caret is not too close to the right edge of the screen. It will be
1660 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1661 than that to the end of the source line anyway. */
1662 int right_margin_size = CARET_LINE_MARGIN;
1663 right_margin_size = MIN (eol_display_column - caret_display_column,
1664 right_margin_size);
1665 if (right_margin_size + left_margin_size >= max_width)
1667 /* The max_width is very small, so anything we try to do will not be very
1668 effective; just punt in this case and output with no offset. */
1669 return;
1671 const int max_caret_display_column = max_width - right_margin_size;
1672 if (caret_display_column > max_caret_display_column)
1674 m_x_offset_display = caret_display_column - max_caret_display_column;
1675 /* Make sure we don't offset the line into oblivion. */
1676 static const int min_cols_visible = 2;
1677 if (source_display_cols - m_x_offset_display < min_cols_visible)
1678 m_x_offset_display = 0;
1682 /* Print line ROW of source code, potentially colorized at any ranges, and
1683 return the line bounds. LINE is the source line (not necessarily
1684 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1685 colorization and tab expansion, this function tracks the line position in
1686 both byte and display column units. */
1688 line_bounds
1689 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1691 m_colorizer.set_normal_text ();
1693 pp_emit_prefix (m_pp);
1694 if (m_show_line_numbers_p)
1696 int width = num_digits (row);
1697 for (int i = 0; i < m_linenum_width - width; i++)
1698 pp_space (m_pp);
1699 pp_printf (m_pp, "%i | ", row);
1701 else
1702 pp_space (m_pp);
1704 /* We will stop printing the source line at any trailing whitespace. */
1705 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1706 line_bytes);
1708 /* This object helps to keep track of which display column we are at, which is
1709 necessary for computing the line bounds in display units, for doing
1710 tab expansion, and for implementing m_x_offset_display. */
1711 cpp_display_width_computation dw (line, line_bytes, m_policy);
1713 /* Skip the first m_x_offset_display display columns. In case the leading
1714 portion that will be skipped ends with a character with wcwidth > 1, then
1715 it is possible we skipped too much, so account for that by padding with
1716 spaces. Note that this does the right thing too in case a tab was the last
1717 character to be skipped over; the tab is effectively replaced by the
1718 correct number of trailing spaces needed to offset by the desired number of
1719 display columns. */
1720 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1721 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1722 pp_space (m_pp);
1724 /* Print the line and compute the line_bounds. */
1725 line_bounds lbounds;
1726 while (!dw.done ())
1728 /* Assuming colorization is enabled for the caret and underline
1729 characters, we may also colorize the associated characters
1730 within the source line.
1732 For frontends that generate range information, we color the
1733 associated characters in the source line the same as the
1734 carets and underlines in the annotation line, to make it easier
1735 for the reader to see the pertinent code.
1737 For frontends that only generate carets, we don't colorize the
1738 characters above them, since this would look strange (e.g.
1739 colorizing just the first character in a token). */
1740 if (m_colorize_source_p)
1742 bool in_range_p;
1743 point_state state;
1744 const int start_byte_col = dw.bytes_processed () + 1;
1745 in_range_p = get_state_at_point (row, start_byte_col,
1746 0, INT_MAX,
1747 CU_BYTES,
1748 &state);
1749 if (in_range_p)
1750 m_colorizer.set_range (state.range_idx);
1751 else
1752 m_colorizer.set_normal_text ();
1755 /* Get the display width of the next character to be output, expanding
1756 tabs and replacing some control bytes with spaces as necessary. */
1757 const char *c = dw.next_byte ();
1758 const int start_disp_col = dw.display_cols_processed () + 1;
1759 cpp_decoded_char cp;
1760 const int this_display_width = dw.process_next_codepoint (&cp);
1761 if (*c == '\t')
1763 /* The returned display width is the number of spaces into which the
1764 tab should be expanded. */
1765 for (int i = 0; i != this_display_width; ++i)
1766 pp_space (m_pp);
1767 continue;
1770 /* We have a (possibly multibyte) character to output; update the line
1771 bounds if it is not whitespace. */
1772 if (*c != ' ')
1774 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1775 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1776 lbounds.m_first_non_ws_disp_col = start_disp_col;
1779 /* Output the character. */
1780 m_policy.m_print_cb (m_pp, cp);
1781 c = dw.next_byte ();
1783 print_newline ();
1784 return lbounds;
1787 /* Determine if we should print an annotation line for ROW.
1788 i.e. if any of m_layout_ranges contains ROW. */
1790 bool
1791 layout::should_print_annotation_line_p (linenum_type row) const
1793 layout_range *range;
1794 int i;
1795 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1797 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1798 return false;
1799 if (range->intersects_line_p (row))
1800 return true;
1802 return false;
1805 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1806 margin, which is empty for annotation lines. Otherwise, do nothing. */
1808 void
1809 layout::start_annotation_line (char margin_char) const
1811 pp_emit_prefix (m_pp);
1812 if (m_show_line_numbers_p)
1814 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1815 of it, right-aligned, padded with spaces. */
1816 int i;
1817 for (i = 0; i < m_linenum_width - 3; i++)
1818 pp_space (m_pp);
1819 for (; i < m_linenum_width; i++)
1820 pp_character (m_pp, margin_char);
1821 pp_string (m_pp, " |");
1825 /* Print a line consisting of the caret/underlines for the given
1826 source line. */
1828 void
1829 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1831 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1832 lbounds.m_last_non_ws_disp_col);
1834 start_annotation_line ();
1835 pp_space (m_pp);
1837 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1839 bool in_range_p;
1840 point_state state;
1841 in_range_p = get_state_at_point (row, column,
1842 lbounds.m_first_non_ws_disp_col,
1843 lbounds.m_last_non_ws_disp_col,
1844 CU_DISPLAY_COLS,
1845 &state);
1846 if (in_range_p)
1848 /* Within a range. Draw either the caret or an underline. */
1849 m_colorizer.set_range (state.range_idx);
1850 if (state.draw_caret_p)
1852 /* Draw the caret. */
1853 char caret_char;
1854 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1855 caret_char
1856 = m_context->m_source_printing.caret_chars[state.range_idx];
1857 else
1858 caret_char = '^';
1859 pp_character (m_pp, caret_char);
1861 else
1862 pp_character (m_pp, '~');
1864 else
1866 /* Not in a range. */
1867 m_colorizer.set_normal_text ();
1868 pp_character (m_pp, ' ');
1871 print_newline ();
1874 /* A version of label_text that can live inside a vec.
1875 Requires manual cleanup via maybe_free. */
1877 struct pod_label_text
1879 pod_label_text ()
1880 : m_buffer (NULL), m_caller_owned (false)
1883 pod_label_text (label_text &&other)
1884 : m_buffer (const_cast<char*> (other.get ())),
1885 m_caller_owned (other.is_owner ())
1887 other.release ();
1890 void maybe_free ()
1892 if (m_caller_owned)
1893 free (m_buffer);
1896 char *m_buffer;
1897 bool m_caller_owned;
1900 /* Implementation detail of layout::print_any_labels.
1902 A label within the given row of source. */
1904 class line_label
1906 public:
1907 line_label (const cpp_char_column_policy &policy,
1908 int state_idx, int column,
1909 label_text text)
1910 : m_state_idx (state_idx), m_column (column),
1911 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1913 const int bytes = strlen (m_text.m_buffer);
1914 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1917 /* Sorting is primarily by column, then by state index. */
1918 static int comparator (const void *p1, const void *p2)
1920 const line_label *ll1 = (const line_label *)p1;
1921 const line_label *ll2 = (const line_label *)p2;
1922 int column_cmp = compare (ll1->m_column, ll2->m_column);
1923 if (column_cmp)
1924 return column_cmp;
1925 /* Order by reverse state index, so that labels are printed
1926 in order of insertion into the rich_location when the
1927 sorted list is walked backwards. */
1928 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1931 int m_state_idx;
1932 int m_column;
1933 pod_label_text m_text;
1934 size_t m_display_width;
1935 int m_label_line;
1936 bool m_has_vbar;
1939 /* Print any labels in this row. */
1940 void
1941 layout::print_any_labels (linenum_type row)
1943 int i;
1944 auto_vec<line_label> labels;
1946 /* Gather the labels that are to be printed into "labels". */
1948 layout_range *range;
1949 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1951 /* Most ranges don't have labels, so reject this first. */
1952 if (range->m_label == NULL)
1953 continue;
1955 /* The range's caret must be on this line. */
1956 if (range->m_caret.m_line != row)
1957 continue;
1959 /* Reject labels that aren't fully visible due to clipping
1960 by m_x_offset_display. */
1961 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1962 if (disp_col <= m_x_offset_display)
1963 continue;
1965 label_text text;
1966 text = range->m_label->get_text (range->m_original_idx);
1968 /* Allow for labels that return NULL from their get_text
1969 implementation (so e.g. such labels can control their own
1970 visibility). */
1971 if (text.get () == NULL)
1972 continue;
1974 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1978 /* Bail out if there are no labels on this row. */
1979 if (labels.length () == 0)
1980 return;
1982 /* Sort them. */
1983 labels.qsort(line_label::comparator);
1985 /* Figure out how many "label lines" we need, and which
1986 one each label is printed in.
1988 For example, if the labels aren't too densely packed,
1989 we can fit them on the same line, giving two "label lines":
1991 foo + bar
1992 ~~~ ~~~
1993 | | : label line 0
1994 l0 l1 : label line 1
1996 If they would touch each other or overlap, then we need
1997 additional "label lines":
1999 foo + bar
2000 ~~~ ~~~
2001 | | : label line 0
2002 | label 1 : label line 1
2003 label 0 : label line 2
2005 Place the final label on label line 1, and work backwards, adding
2006 label lines as needed.
2008 If multiple labels are at the same place, put them on separate
2009 label lines:
2011 foo + bar
2012 ^ : label line 0
2013 | : label line 1
2014 label 0 : label line 2
2015 label 1 : label line 3. */
2017 int max_label_line = 1;
2019 int next_column = INT_MAX;
2020 line_label *label;
2021 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2023 /* Would this label "touch" or overlap the next label? */
2024 if (label->m_column + label->m_display_width >= (size_t)next_column)
2026 max_label_line++;
2028 /* If we've already seen labels with the same column, suppress the
2029 vertical bar for subsequent ones in this backwards iteration;
2030 hence only the one with the highest label_line has m_has_vbar set. */
2031 if (label->m_column == next_column)
2032 label->m_has_vbar = false;
2035 label->m_label_line = max_label_line;
2036 next_column = label->m_column;
2040 /* Print the "label lines". For each label within the line, print
2041 either a vertical bar ('|') for the labels that are lower down, or the
2042 labels themselves once we've reached their line. */
2044 for (int label_line = 0; label_line <= max_label_line; label_line++)
2046 start_annotation_line ();
2047 pp_space (m_pp);
2048 int column = 1 + m_x_offset_display;
2049 line_label *label;
2050 FOR_EACH_VEC_ELT (labels, i, label)
2052 if (label_line > label->m_label_line)
2053 /* We've printed all the labels for this label line. */
2054 break;
2056 if (label_line == label->m_label_line)
2058 gcc_assert (column <= label->m_column);
2059 move_to_column (&column, label->m_column, true);
2060 /* Colorize the text, unless it's for events in a
2061 diagnostic_path. */
2062 if (!m_diagnostic_path_p)
2063 m_colorizer.set_range (label->m_state_idx);
2064 pp_string (m_pp, label->m_text.m_buffer);
2065 m_colorizer.set_normal_text ();
2066 column += label->m_display_width;
2068 else if (label->m_has_vbar)
2070 gcc_assert (column <= label->m_column);
2071 move_to_column (&column, label->m_column, true);
2072 m_colorizer.set_range (label->m_state_idx);
2073 pp_character (m_pp, '|');
2074 m_colorizer.set_normal_text ();
2075 column++;
2078 print_newline ();
2082 /* Clean up. */
2084 line_label *label;
2085 FOR_EACH_VEC_ELT (labels, i, label)
2086 label->m_text.maybe_free ();
2090 /* If there are any fixit hints inserting new lines before source line ROW,
2091 print them.
2093 They are printed on lines of their own, before the source line
2094 itself, with a leading '+'. */
2096 void
2097 layout::print_leading_fixits (linenum_type row)
2099 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2101 const fixit_hint *hint = m_fixit_hints[i];
2103 if (!hint->ends_with_newline_p ())
2104 /* Not a newline fixit; print it in print_trailing_fixits. */
2105 continue;
2107 gcc_assert (hint->insertion_p ());
2109 if (hint->affects_line_p (m_exploc.file, row))
2111 /* Printing the '+' with normal colorization
2112 and the inserted line with "insert" colorization
2113 helps them stand out from each other, and from
2114 the surrounding text. */
2115 m_colorizer.set_normal_text ();
2116 start_annotation_line ('+');
2117 pp_character (m_pp, '+');
2118 m_colorizer.set_fixit_insert ();
2119 /* Print all but the trailing newline of the fix-it hint.
2120 We have to print the newline separately to avoid
2121 getting additional pp prefixes printed. */
2122 for (size_t i = 0; i < hint->get_length () - 1; i++)
2123 pp_character (m_pp, hint->get_string ()[i]);
2124 m_colorizer.set_normal_text ();
2125 pp_newline (m_pp);
2130 /* Subroutine of layout::print_trailing_fixits.
2132 Determine if the annotation line printed for LINE contained
2133 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2135 bool
2136 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2137 int finish_column) const
2139 layout_range *range;
2140 int i;
2141 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2142 if (range->m_start.m_line == line
2143 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2144 && range->m_finish.m_line == line
2145 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2146 return true;
2147 return false;
2150 /* Classes for printing trailing fix-it hints i.e. those that
2151 don't add new lines.
2153 For insertion, these can look like:
2155 new_text
2157 For replacement, these can look like:
2159 ------------- : underline showing affected range
2160 new_text
2162 For deletion, these can look like:
2164 ------------- : underline showing affected range
2166 This can become confusing if they overlap, and so we need
2167 to do some preprocessing to decide what to print.
2168 We use the list of fixit_hint instances affecting the line
2169 to build a list of "correction" instances, and print the
2170 latter.
2172 For example, consider a set of fix-its for converting
2173 a C-style cast to a C++ const_cast.
2175 Given:
2177 ..000000000111111111122222222223333333333.
2178 ..123456789012345678901234567890123456789.
2179 foo *f = (foo *)ptr->field;
2180 ^~~~~
2182 and the fix-it hints:
2183 - replace col 10 (the open paren) with "const_cast<"
2184 - replace col 16 (the close paren) with "> ("
2185 - insert ")" before col 27
2187 then we would get odd-looking output:
2189 foo *f = (foo *)ptr->field;
2190 ^~~~~
2192 const_cast<
2194 > ( )
2196 It would be better to detect when fixit hints are going to
2197 overlap (those that require new lines), and to consolidate
2198 the printing of such fixits, giving something like:
2200 foo *f = (foo *)ptr->field;
2201 ^~~~~
2202 -----------------
2203 const_cast<foo *> (ptr->field)
2205 This works by detecting when the printing would overlap, and
2206 effectively injecting no-op replace hints into the gaps between
2207 such fix-its, so that the printing joins up.
2209 In the above example, the overlap of:
2210 - replace col 10 (the open paren) with "const_cast<"
2211 and:
2212 - replace col 16 (the close paren) with "> ("
2213 is fixed by injecting a no-op:
2214 - replace cols 11-15 with themselves ("foo *")
2215 and consolidating these, making:
2216 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2217 i.e.:
2218 - replace cols 10-16 with "const_cast<foo *> ("
2220 This overlaps with the final fix-it hint:
2221 - insert ")" before col 27
2222 and so we repeat the consolidation process, by injecting
2223 a no-op:
2224 - replace cols 17-26 with themselves ("ptr->field")
2225 giving:
2226 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2227 i.e.:
2228 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2230 and is thus printed as desired. */
2232 /* A range of (byte or display) columns within a line. */
2234 class column_range
2236 public:
2237 column_range (int start_, int finish_) : start (start_), finish (finish_)
2239 gcc_assert (valid_p (start, finish));
2242 bool operator== (const column_range &other) const
2244 return start == other.start && finish == other.finish;
2247 static bool valid_p (int start, int finish)
2249 /* We must have either a range, or an insertion. */
2250 return (start <= finish || finish == start - 1);
2253 int start;
2254 int finish;
2257 /* Get the range of bytes or display columns that HINT would affect. */
2258 static column_range
2259 get_affected_range (const cpp_char_column_policy &policy,
2260 const fixit_hint *hint, enum column_unit col_unit)
2262 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2263 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2264 --exploc_finish.column;
2266 int start_column;
2267 int finish_column;
2268 if (col_unit == CU_DISPLAY_COLS)
2270 start_column = location_compute_display_column (exploc_start, policy);
2271 if (hint->insertion_p ())
2272 finish_column = start_column - 1;
2273 else
2274 finish_column = location_compute_display_column (exploc_finish, policy);
2276 else
2278 start_column = exploc_start.column;
2279 finish_column = exploc_finish.column;
2281 return column_range (start_column, finish_column);
2284 /* Get the range of display columns that would be printed for HINT. */
2286 static column_range
2287 get_printed_columns (const cpp_char_column_policy &policy,
2288 const fixit_hint *hint)
2290 expanded_location exploc = expand_location (hint->get_start_loc ());
2291 int start_column = location_compute_display_column (exploc, policy);
2292 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2293 policy);
2294 int final_hint_column = start_column + hint_width - 1;
2295 if (hint->insertion_p ())
2297 return column_range (start_column, final_hint_column);
2299 else
2301 exploc = expand_location (hint->get_next_loc ());
2302 --exploc.column;
2303 int finish_column = location_compute_display_column (exploc, policy);
2304 return column_range (start_column,
2305 MAX (finish_column, final_hint_column));
2309 /* A correction on a particular line.
2310 This describes a plan for how to print one or more fixit_hint
2311 instances that affected the line, potentially consolidating hints
2312 into corrections to make the result easier for the user to read. */
2314 class correction
2316 public:
2317 correction (column_range affected_bytes,
2318 column_range affected_columns,
2319 column_range printed_columns,
2320 const char *new_text, size_t new_text_len,
2321 const cpp_char_column_policy &policy)
2322 : m_affected_bytes (affected_bytes),
2323 m_affected_columns (affected_columns),
2324 m_printed_columns (printed_columns),
2325 m_text (xstrdup (new_text)),
2326 m_byte_length (new_text_len),
2327 m_policy (policy),
2328 m_alloc_sz (new_text_len + 1)
2330 compute_display_cols ();
2333 ~correction () { free (m_text); }
2335 bool insertion_p () const
2337 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2340 void ensure_capacity (size_t len);
2341 void ensure_terminated ();
2343 void compute_display_cols ()
2345 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2348 void overwrite (int dst_offset, const char_span &src_span)
2350 gcc_assert (dst_offset >= 0);
2351 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2352 memcpy (m_text + dst_offset, src_span.get_buffer (),
2353 src_span.length ());
2356 /* If insert, then start: the column before which the text
2357 is to be inserted, and finish is offset by the length of
2358 the replacement.
2359 If replace, then the range of columns affected. */
2360 column_range m_affected_bytes;
2361 column_range m_affected_columns;
2363 /* If insert, then start: the column before which the text
2364 is to be inserted, and finish is offset by the length of
2365 the replacement.
2366 If replace, then the range of columns affected. */
2367 column_range m_printed_columns;
2369 /* The text to be inserted/used as replacement. */
2370 char *m_text;
2371 size_t m_byte_length; /* Not including null-terminator. */
2372 int m_display_cols;
2373 const cpp_char_column_policy &m_policy;
2374 size_t m_alloc_sz;
2377 /* Ensure that m_text can hold a string of length LEN
2378 (plus 1 for 0-termination). */
2380 void
2381 correction::ensure_capacity (size_t len)
2383 /* Allow 1 extra byte for 0-termination. */
2384 if (m_alloc_sz < (len + 1))
2386 size_t new_alloc_sz = (len + 1) * 2;
2387 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2388 m_alloc_sz = new_alloc_sz;
2392 /* Ensure that m_text is 0-terminated. */
2394 void
2395 correction::ensure_terminated ()
2397 /* 0-terminate the buffer. */
2398 gcc_assert (m_byte_length < m_alloc_sz);
2399 m_text[m_byte_length] = '\0';
2402 /* A list of corrections affecting a particular line.
2403 This is used by layout::print_trailing_fixits for planning
2404 how to print the fix-it hints affecting the line. */
2406 class line_corrections
2408 public:
2409 line_corrections (const char_display_policy &policy,
2410 const char *filename,
2411 linenum_type row)
2412 : m_policy (policy), m_filename (filename), m_row (row)
2414 ~line_corrections ();
2416 void add_hint (const fixit_hint *hint);
2418 const char_display_policy &m_policy;
2419 const char *m_filename;
2420 linenum_type m_row;
2421 auto_vec <correction *> m_corrections;
2424 /* struct line_corrections. */
2426 line_corrections::~line_corrections ()
2428 unsigned i;
2429 correction *c;
2430 FOR_EACH_VEC_ELT (m_corrections, i, c)
2431 delete c;
2434 /* A struct wrapping a particular source line, allowing
2435 run-time bounds-checking of accesses in a checked build. */
2437 class source_line
2439 public:
2440 source_line (const char *filename, int line);
2442 char_span as_span () { return char_span (chars, width); }
2444 const char *chars;
2445 int width;
2448 /* source_line's ctor. */
2450 source_line::source_line (const char *filename, int line)
2452 char_span span = location_get_source_line (filename, line);
2453 chars = span.get_buffer ();
2454 width = span.length ();
2457 /* Add HINT to the corrections for this line.
2458 Attempt to consolidate nearby hints so that they will not
2459 overlap with printed. */
2461 void
2462 line_corrections::add_hint (const fixit_hint *hint)
2464 column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES);
2465 column_range affected_columns = get_affected_range (m_policy, hint,
2466 CU_DISPLAY_COLS);
2467 column_range printed_columns = get_printed_columns (m_policy, hint);
2469 /* Potentially consolidate. */
2470 if (!m_corrections.is_empty ())
2472 correction *last_correction
2473 = m_corrections[m_corrections.length () - 1];
2475 /* The following consolidation code assumes that the fix-it hints
2476 have been sorted by start (done within layout's ctor). */
2477 gcc_assert (affected_bytes.start
2478 >= last_correction->m_affected_bytes.start);
2479 gcc_assert (printed_columns.start
2480 >= last_correction->m_printed_columns.start);
2482 if (printed_columns.start <= last_correction->m_printed_columns.finish
2483 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2484 affected_bytes.start - 1))
2486 /* We have two hints for which the printed forms of the hints
2487 would touch or overlap, so we need to consolidate them to avoid
2488 confusing the user.
2489 Attempt to inject a "replace" correction from immediately
2490 after the end of the last hint to immediately before the start
2491 of the next hint. */
2492 column_range between (last_correction->m_affected_bytes.finish + 1,
2493 affected_bytes.start - 1);
2495 /* Try to read the source. */
2496 source_line line (m_filename, m_row);
2497 if (line.chars && between.finish < line.width)
2499 /* Consolidate into the last correction:
2500 add a no-op "replace" of the "between" text, and
2501 add the text from the new hint. */
2502 int old_byte_len = last_correction->m_byte_length;
2503 gcc_assert (old_byte_len >= 0);
2504 int between_byte_len = between.finish + 1 - between.start;
2505 gcc_assert (between_byte_len >= 0);
2506 int new_byte_len
2507 = old_byte_len + between_byte_len + hint->get_length ();
2508 gcc_assert (new_byte_len >= 0);
2509 last_correction->ensure_capacity (new_byte_len);
2510 last_correction->overwrite
2511 (old_byte_len,
2512 line.as_span ().subspan (between.start - 1,
2513 between.finish + 1 - between.start));
2514 last_correction->overwrite (old_byte_len + between_byte_len,
2515 char_span (hint->get_string (),
2516 hint->get_length ()));
2517 last_correction->m_byte_length = new_byte_len;
2518 last_correction->ensure_terminated ();
2519 last_correction->m_affected_bytes.finish
2520 = affected_bytes.finish;
2521 last_correction->m_affected_columns.finish
2522 = affected_columns.finish;
2523 int prev_display_cols = last_correction->m_display_cols;
2524 last_correction->compute_display_cols ();
2525 last_correction->m_printed_columns.finish
2526 += last_correction->m_display_cols - prev_display_cols;
2527 return;
2532 /* If no consolidation happened, add a new correction instance. */
2533 m_corrections.safe_push (new correction (affected_bytes,
2534 affected_columns,
2535 printed_columns,
2536 hint->get_string (),
2537 hint->get_length (),
2538 m_policy));
2541 /* If there are any fixit hints on source line ROW, print them.
2542 They are printed in order, attempting to combine them onto lines, but
2543 starting new lines if necessary.
2544 Fix-it hints that insert new lines are handled separately,
2545 in layout::print_leading_fixits. */
2547 void
2548 layout::print_trailing_fixits (linenum_type row)
2550 /* Build a list of correction instances for the line,
2551 potentially consolidating hints (for the sake of readability). */
2552 line_corrections corrections (m_policy, m_exploc.file, row);
2553 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2555 const fixit_hint *hint = m_fixit_hints[i];
2557 /* Newline fixits are handled by layout::print_leading_fixits. */
2558 if (hint->ends_with_newline_p ())
2559 continue;
2561 if (hint->affects_line_p (m_exploc.file, row))
2562 corrections.add_hint (hint);
2565 /* Now print the corrections. */
2566 unsigned i;
2567 correction *c;
2568 int column = m_x_offset_display;
2570 if (!corrections.m_corrections.is_empty ())
2571 start_annotation_line ();
2573 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2575 /* For now we assume each fixit hint can only touch one line. */
2576 if (c->insertion_p ())
2578 /* This assumes the insertion just affects one line. */
2579 int start_column = c->m_printed_columns.start;
2580 move_to_column (&column, start_column, true);
2581 m_colorizer.set_fixit_insert ();
2582 pp_string (m_pp, c->m_text);
2583 m_colorizer.set_normal_text ();
2584 column += c->m_display_cols;
2586 else
2588 /* If the range of the replacement wasn't printed in the
2589 annotation line, then print an extra underline to
2590 indicate exactly what is being replaced.
2591 Always show it for removals. */
2592 int start_column = c->m_affected_columns.start;
2593 int finish_column = c->m_affected_columns.finish;
2594 if (!annotation_line_showed_range_p (row, start_column,
2595 finish_column)
2596 || c->m_byte_length == 0)
2598 move_to_column (&column, start_column, true);
2599 m_colorizer.set_fixit_delete ();
2600 for (; column <= finish_column; column++)
2601 pp_character (m_pp, '-');
2602 m_colorizer.set_normal_text ();
2604 /* Print the replacement text. REPLACE also covers
2605 removals, so only do this extra work (potentially starting
2606 a new line) if we have actual replacement text. */
2607 if (c->m_byte_length > 0)
2609 move_to_column (&column, start_column, true);
2610 m_colorizer.set_fixit_insert ();
2611 pp_string (m_pp, c->m_text);
2612 m_colorizer.set_normal_text ();
2613 column += c->m_display_cols;
2618 /* Add a trailing newline, if necessary. */
2619 move_to_column (&column, 0, false);
2622 /* Disable any colorization and emit a newline. */
2624 void
2625 layout::print_newline ()
2627 m_colorizer.set_normal_text ();
2628 pp_newline (m_pp);
2631 /* Return true if (ROW/COLUMN) is within a range of the layout.
2632 If it returns true, OUT_STATE is written to, with the
2633 range index, and whether we should draw the caret at
2634 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2635 whether all inputs and outputs are in bytes or display column units. */
2637 bool
2638 layout::get_state_at_point (/* Inputs. */
2639 linenum_type row, int column,
2640 int first_non_ws, int last_non_ws,
2641 enum column_unit col_unit,
2642 /* Outputs. */
2643 point_state *out_state)
2645 layout_range *range;
2646 int i;
2647 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2649 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2650 /* Bail out early, so that such ranges don't affect underlining or
2651 source colorization. */
2652 continue;
2654 if (range->contains_point (row, column, col_unit))
2656 out_state->range_idx = i;
2658 /* Are we at the range's caret? is it visible? */
2659 out_state->draw_caret_p = false;
2660 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2661 && row == range->m_caret.m_line
2662 && column == range->m_caret.m_columns[col_unit])
2663 out_state->draw_caret_p = true;
2665 /* Within a multiline range, don't display any underline
2666 in any leading or trailing whitespace on a line.
2667 We do display carets, however. */
2668 if (!out_state->draw_caret_p)
2669 if (column < first_non_ws || column > last_non_ws)
2670 return false;
2672 /* We are within a range. */
2673 return true;
2677 return false;
2680 /* Helper function for use by layout::print_line when printing the
2681 annotation line under the source line.
2682 Get the display column beyond the rightmost one that could contain a caret
2683 or range marker, given that we stop rendering at trailing whitespace.
2684 ROW is the source line within the given file.
2685 CARET_COLUMN is the display column of range 0's caret.
2686 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2687 character of source (as determined when printing the source line). */
2690 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2691 int last_non_ws_column)
2693 int result = caret_column + 1;
2695 layout_range *range;
2696 int i;
2697 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2699 if (row >= range->m_start.m_line)
2701 if (range->m_finish.m_line == row)
2703 /* On the final line within a range; ensure that
2704 we render up to the end of the range. */
2705 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2706 if (result <= disp_col)
2707 result = disp_col + 1;
2709 else if (row < range->m_finish.m_line)
2711 /* Within a multiline range; ensure that we render up to the
2712 last non-whitespace column. */
2713 if (result <= last_non_ws_column)
2714 result = last_non_ws_column + 1;
2719 return result;
2722 /* Given *COLUMN as an x-coordinate, print spaces to position
2723 successive output at DEST_COLUMN, printing a newline if necessary,
2724 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2725 left margin after any newline. */
2727 void
2728 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2730 /* Start a new line if we need to. */
2731 if (*column > dest_column)
2733 print_newline ();
2734 if (add_left_margin)
2735 start_annotation_line ();
2736 *column = m_x_offset_display;
2739 while (*column < dest_column)
2741 pp_space (m_pp);
2742 (*column)++;
2746 /* For debugging layout issues, render a ruler giving column numbers
2747 (after the 1-column indent). */
2749 void
2750 layout::show_ruler (int max_column) const
2752 /* Hundreds. */
2753 if (max_column > 99)
2755 start_annotation_line ();
2756 pp_space (m_pp);
2757 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2758 if (column % 10 == 0)
2759 pp_character (m_pp, '0' + (column / 100) % 10);
2760 else
2761 pp_space (m_pp);
2762 pp_newline (m_pp);
2765 /* Tens. */
2766 start_annotation_line ();
2767 pp_space (m_pp);
2768 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2769 if (column % 10 == 0)
2770 pp_character (m_pp, '0' + (column / 10) % 10);
2771 else
2772 pp_space (m_pp);
2773 pp_newline (m_pp);
2775 /* Units. */
2776 start_annotation_line ();
2777 pp_space (m_pp);
2778 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2779 pp_character (m_pp, '0' + (column % 10));
2780 pp_newline (m_pp);
2783 /* Print leading fix-its (for new lines inserted before the source line)
2784 then the source line, followed by an annotation line
2785 consisting of any caret/underlines, then any fixits.
2786 If the source line can't be read, print nothing. */
2787 void
2788 layout::print_line (linenum_type row)
2790 char_span line = location_get_source_line (m_exploc.file, row);
2791 if (!line)
2792 return;
2794 print_leading_fixits (row);
2795 const line_bounds lbounds
2796 = print_source_line (row, line.get_buffer (), line.length ());
2797 if (should_print_annotation_line_p (row))
2798 print_annotation_line (row, lbounds);
2799 if (m_show_labels_p)
2800 print_any_labels (row);
2801 print_trailing_fixits (row);
2804 } /* End of anonymous namespace. */
2806 /* If LOC is within the spans of lines that will already be printed for
2807 this gcc_rich_location, then add it as a secondary location and return true.
2809 Otherwise return false. */
2811 bool
2812 gcc_rich_location::add_location_if_nearby (location_t loc,
2813 bool restrict_to_current_line_spans,
2814 const range_label *label)
2816 /* Use the layout location-handling logic to sanitize LOC,
2817 filtering it to the current line spans within a temporary
2818 layout instance. */
2819 layout layout (global_dc, this, DK_ERROR);
2820 location_range loc_range;
2821 loc_range.m_loc = loc;
2822 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2823 if (!layout.maybe_add_location_range (&loc_range, 0,
2824 restrict_to_current_line_spans))
2825 return false;
2827 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2828 return true;
2831 /* Print the physical source code corresponding to the location of
2832 this diagnostic, with additional annotations.
2833 If PP is non-null, then use it rather than CONTEXT's printer. */
2835 void
2836 diagnostic_show_locus (diagnostic_context * context,
2837 rich_location *richloc,
2838 diagnostic_t diagnostic_kind,
2839 pretty_printer *pp)
2841 location_t loc = richloc->get_loc ();
2842 /* Do nothing if source-printing has been disabled. */
2843 if (!context->m_source_printing.enabled)
2844 return;
2846 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2847 if (loc <= BUILTINS_LOCATION)
2848 return;
2850 /* Don't print the same source location twice in a row, unless we have
2851 fix-it hints, or multiple locations, or a label. */
2852 if (loc == context->last_location
2853 && richloc->get_num_fixit_hints () == 0
2854 && richloc->get_num_locations () == 1
2855 && richloc->get_range (0)->m_label == NULL)
2856 return;
2858 context->last_location = loc;
2860 layout layout (context, richloc, diagnostic_kind, pp);
2861 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2862 line_span_idx++)
2864 const line_span *line_span = layout.get_line_span (line_span_idx);
2865 if (context->m_source_printing.show_line_numbers_p)
2867 /* With line numbers, we should show whenever the line-numbering
2868 "jumps". */
2869 if (line_span_idx > 0)
2870 layout.print_gap_in_line_numbering ();
2872 else
2874 /* Without line numbers, we print headings for some line spans. */
2875 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2877 expanded_location exploc
2878 = layout.get_expanded_location (line_span);
2879 context->m_text_callbacks.start_span (context, exploc);
2882 /* Iterate over the lines within this span (using linenum_arith_t to
2883 avoid overflow with 0xffffffff causing an infinite loop). */
2884 linenum_arith_t last_line = line_span->get_last_line ();
2885 for (linenum_arith_t row = line_span->get_first_line ();
2886 row <= last_line; row++)
2887 layout.print_line (row);
2891 #if CHECKING_P
2893 namespace selftest {
2895 /* Selftests for diagnostic_show_locus. */
2897 /* Verify that cpp_display_width correctly handles escaping. */
2899 static void
2900 test_display_widths ()
2902 gcc_rich_location richloc (UNKNOWN_LOCATION);
2904 /* U+03C0 "GREEK SMALL LETTER PI". */
2905 const char *pi = "\xCF\x80";
2906 /* U+1F642 "SLIGHTLY SMILING FACE". */
2907 const char *emoji = "\xF0\x9F\x99\x82";
2908 /* Stray trailing byte of a UTF-8 character. */
2909 const char *stray = "\xBF";
2910 /* U+10FFFF. */
2911 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2913 /* No escaping. */
2915 test_diagnostic_context dc;
2916 char_display_policy policy (make_policy (dc, richloc));
2917 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2918 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2919 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2920 /* Don't check width of U+10FFFF; it's in a private use plane. */
2923 richloc.set_escape_on_output (true);
2926 test_diagnostic_context dc;
2927 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
2928 char_display_policy policy (make_policy (dc, richloc));
2929 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2930 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2931 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2932 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2933 policy),
2934 strlen ("<U+10FFFF>"));
2938 test_diagnostic_context dc;
2939 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
2940 char_display_policy policy (make_policy (dc, richloc));
2941 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2942 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2943 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2944 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2945 policy),
2946 16);
2950 /* For precise tests of the layout, make clear where the source line will
2951 start. test_left_margin sets the total byte count from the left side of the
2952 screen to the start of source lines, after the line number and the separator,
2953 which consists of the three characters " | ". */
2954 static const int test_linenum_sep = 3;
2955 static const int test_left_margin = 7;
2957 /* Helper function for test_layout_x_offset_display_utf8(). */
2958 static void
2959 test_offset_impl (int caret_byte_col, int max_width,
2960 int expected_x_offset_display,
2961 int left_margin = test_left_margin)
2963 test_diagnostic_context dc;
2964 dc.m_source_printing.max_width = max_width;
2965 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2966 the line number plus one space after. */
2967 dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
2968 dc.m_source_printing.show_line_numbers_p = true;
2969 rich_location richloc (line_table,
2970 linemap_position_for_column (line_table,
2971 caret_byte_col));
2972 layout test_layout (&dc, &richloc, DK_ERROR);
2973 ASSERT_EQ (left_margin - test_linenum_sep,
2974 test_layout.get_linenum_width ());
2975 ASSERT_EQ (expected_x_offset_display,
2976 test_layout.get_x_offset_display ());
2979 /* Test that layout::calculate_x_offset_display() works. */
2980 static void
2981 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2984 const char *content
2985 = "This line is very long, so that we can use it to test the logic for "
2986 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2987 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2988 "column #102.\n";
2990 /* Number of bytes in the line, subtracting one to remove the newline. */
2991 const int line_bytes = strlen (content) - 1;
2993 /* Number of display columns occupied by the line; each of the 2 emojis
2994 takes up 2 fewer display columns than it does bytes. */
2995 const int line_display_cols = line_bytes - 2*2;
2997 /* The column of the first emoji. Byte or display is the same as there are
2998 no multibyte characters earlier on the line. */
2999 const int emoji_col = 102;
3001 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3002 line_table_test ltt (case_);
3004 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3006 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3008 /* Don't attempt to run the tests if column data might be unavailable. */
3009 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3010 return;
3012 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3013 ASSERT_EQ (1, LOCATION_LINE (line_end));
3014 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3016 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3017 ASSERT_EQ (line_display_cols,
3018 cpp_display_width (lspan.get_buffer (), lspan.length (),
3019 def_policy ()));
3020 ASSERT_EQ (line_display_cols,
3021 location_compute_display_column (expand_location (line_end),
3022 def_policy ()));
3023 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3024 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3026 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3028 /* No constraint on the width -> no offset. */
3029 test_offset_impl (emoji_col, 0, 0);
3031 /* Caret is before the beginning -> no offset. */
3032 test_offset_impl (0, 100, 0);
3034 /* Caret is past the end of the line -> no offset. */
3035 test_offset_impl (line_bytes+1, 100, 0);
3037 /* Line fits in the display -> no offset. */
3038 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3039 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3041 /* Line is too long for the display but caret location is OK
3042 anyway -> no offset. */
3043 static const int small_width = 24;
3044 test_offset_impl (1, small_width, 0);
3046 /* Width constraint is very small -> no offset. */
3047 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3049 /* Line would be offset, but due to large line numbers, offsetting
3050 would remove the whole line -> no offset. */
3051 static const int huge_left_margin = 100;
3052 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3054 /* Line is the same length as the display, but the line number makes it too
3055 long, so offset is required. Caret is at the end so padding on the right
3056 is not in effect. */
3057 for (int excess = 1; excess <= 3; ++excess)
3058 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3059 excess);
3061 /* Line is much too long for the display, caret is near the end ->
3062 offset should be such that the line fits in the display and caret
3063 remains the same distance from the end that it was. */
3064 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3065 caret_offset <= max_offset; ++caret_offset)
3066 test_offset_impl (line_bytes - caret_offset, small_width,
3067 line_display_cols + test_left_margin - small_width);
3069 /* As previous case but caret is closer to the middle; now we want it to end
3070 up CARET_LINE_MARGIN bytes from the end. */
3071 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3072 test_offset_impl (emoji_col, small_width,
3073 emoji_col + test_left_margin
3074 - (small_width - CARET_LINE_MARGIN));
3076 /* Test that the source line is offset as expected when printed. */
3078 test_diagnostic_context dc;
3079 dc.m_source_printing.max_width = small_width - 6;
3080 dc.m_source_printing.min_margin_width
3081 = test_left_margin - test_linenum_sep + 1;
3082 dc.m_source_printing.show_line_numbers_p = true;
3083 dc.m_source_printing.show_ruler_p = true;
3084 rich_location richloc (line_table,
3085 linemap_position_for_column (line_table,
3086 emoji_col));
3087 layout test_layout (&dc, &richloc, DK_ERROR);
3088 test_layout.print_line (1);
3089 ASSERT_STREQ (" | 1 \n"
3090 " | 1 \n"
3091 " | 234567890123456789\n"
3092 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3093 "that occupies 8 bytes and 4 display columns, starting at "
3094 "column #102.\n"
3095 " | ^\n\n",
3096 pp_formatted_text (dc.printer));
3099 /* Similar to the previous example, but now the offset called for would split
3100 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3101 it with a padding space in this case. */
3103 test_diagnostic_context dc;
3104 dc.m_source_printing.max_width = small_width - 5;
3105 dc.m_source_printing.min_margin_width
3106 = test_left_margin - test_linenum_sep + 1;
3107 dc.m_source_printing.show_line_numbers_p = true;
3108 dc.m_source_printing.show_ruler_p = true;
3109 rich_location richloc (line_table,
3110 linemap_position_for_column (line_table,
3111 emoji_col + 2));
3112 layout test_layout (&dc, &richloc, DK_ERROR);
3113 test_layout.print_line (1);
3114 ASSERT_STREQ (" | 1 1 \n"
3115 " | 1 2 \n"
3116 " | 3456789012345678901\n"
3117 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3118 "that occupies 8 bytes and 4 display columns, starting at "
3119 "column #102.\n"
3120 " | ^\n\n",
3121 pp_formatted_text (dc.printer));
3126 static void
3127 test_layout_x_offset_display_tab (const line_table_case &case_)
3129 const char *content
3130 = "This line is very long, so that we can use it to test the logic for "
3131 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3132 "a variable number of display columns, starting at column #103.\n";
3134 /* Number of bytes in the line, subtracting one to remove the newline. */
3135 const int line_bytes = strlen (content) - 1;
3137 /* The column where the tab begins. Byte or display is the same as there are
3138 no multibyte characters earlier on the line. */
3139 const int tab_col = 103;
3141 /* Effective extra size of the tab beyond what a single space would have taken
3142 up, indexed by tabstop. */
3143 static const int num_tabstops = 11;
3144 int extra_width[num_tabstops];
3145 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3147 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3148 extra_width[tabstop] = this_tab_size - 1;
3150 /* Example of this calculation: if tabstop is 10, the tab starting at column
3151 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3152 next character is at column #111. So it takes up 7 more columns than
3153 a space would have taken up. */
3154 ASSERT_EQ (7, extra_width[10]);
3156 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3157 line_table_test ltt (case_);
3159 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3161 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3163 /* Don't attempt to run the tests if column data might be unavailable. */
3164 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3165 return;
3167 /* Check that cpp_display_width handles the tabs as expected. */
3168 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3169 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3170 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3172 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3173 ASSERT_EQ (line_bytes + extra_width[tabstop],
3174 cpp_display_width (lspan.get_buffer (), lspan.length (),
3175 policy));
3176 ASSERT_EQ (line_bytes + extra_width[tabstop],
3177 location_compute_display_column (expand_location (line_end),
3178 policy));
3181 /* Check that the tab is expanded to the expected number of spaces. */
3182 rich_location richloc (line_table,
3183 linemap_position_for_column (line_table,
3184 tab_col + 1));
3185 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3187 test_diagnostic_context dc;
3188 dc.tabstop = tabstop;
3189 layout test_layout (&dc, &richloc, DK_ERROR);
3190 test_layout.print_line (1);
3191 const char *out = pp_formatted_text (dc.printer);
3192 ASSERT_EQ (NULL, strchr (out, '\t'));
3193 const char *left_quote = strchr (out, '`');
3194 const char *right_quote = strchr (out, '\'');
3195 ASSERT_NE (NULL, left_quote);
3196 ASSERT_NE (NULL, right_quote);
3197 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3200 /* Check that the line is offset properly and that the tab is broken up
3201 into the expected number of spaces when it is the last character skipped
3202 over. */
3203 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3205 test_diagnostic_context dc;
3206 dc.tabstop = tabstop;
3207 static const int small_width = 24;
3208 dc.m_source_printing.max_width = small_width - 4;
3209 dc.m_source_printing.min_margin_width
3210 = test_left_margin - test_linenum_sep + 1;
3211 dc.m_source_printing.show_line_numbers_p = true;
3212 layout test_layout (&dc, &richloc, DK_ERROR);
3213 test_layout.print_line (1);
3215 /* We have arranged things so that two columns will be printed before
3216 the caret. If the tab results in more than one space, this should
3217 produce two spaces in the output; otherwise, it will be a single space
3218 preceded by the opening quote before the tab character. */
3219 const char *output1
3220 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3221 "display columns, starting at column #103.\n"
3222 " | ^\n\n";
3223 const char *output2
3224 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3225 "display columns, starting at column #103.\n"
3226 " | ^\n\n";
3227 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3228 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3233 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3235 static void
3236 test_diagnostic_show_locus_unknown_location ()
3238 test_diagnostic_context dc;
3239 rich_location richloc (line_table, UNKNOWN_LOCATION);
3240 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3241 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3244 /* Verify that diagnostic_show_locus works sanely for various
3245 single-line cases.
3247 All of these work on the following 1-line source file:
3248 .0000000001111111
3249 .1234567890123456
3250 "foo = bar.field;\n"
3251 which is set up by test_diagnostic_show_locus_one_liner and calls
3252 them. */
3254 /* Just a caret. */
3256 static void
3257 test_one_liner_simple_caret ()
3259 test_diagnostic_context dc;
3260 location_t caret = linemap_position_for_column (line_table, 10);
3261 rich_location richloc (line_table, caret);
3262 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3263 ASSERT_STREQ (" foo = bar.field;\n"
3264 " ^\n",
3265 pp_formatted_text (dc.printer));
3268 /* Caret and range. */
3270 static void
3271 test_one_liner_caret_and_range ()
3273 test_diagnostic_context dc;
3274 location_t caret = linemap_position_for_column (line_table, 10);
3275 location_t start = linemap_position_for_column (line_table, 7);
3276 location_t finish = linemap_position_for_column (line_table, 15);
3277 location_t loc = make_location (caret, start, finish);
3278 rich_location richloc (line_table, loc);
3279 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3280 ASSERT_STREQ (" foo = bar.field;\n"
3281 " ~~~^~~~~~\n",
3282 pp_formatted_text (dc.printer));
3285 /* Multiple ranges and carets. */
3287 static void
3288 test_one_liner_multiple_carets_and_ranges ()
3290 test_diagnostic_context dc;
3291 location_t foo
3292 = make_location (linemap_position_for_column (line_table, 2),
3293 linemap_position_for_column (line_table, 1),
3294 linemap_position_for_column (line_table, 3));
3295 dc.m_source_printing.caret_chars[0] = 'A';
3297 location_t bar
3298 = make_location (linemap_position_for_column (line_table, 8),
3299 linemap_position_for_column (line_table, 7),
3300 linemap_position_for_column (line_table, 9));
3301 dc.m_source_printing.caret_chars[1] = 'B';
3303 location_t field
3304 = make_location (linemap_position_for_column (line_table, 13),
3305 linemap_position_for_column (line_table, 11),
3306 linemap_position_for_column (line_table, 15));
3307 dc.m_source_printing.caret_chars[2] = 'C';
3309 rich_location richloc (line_table, foo);
3310 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3311 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3312 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3313 ASSERT_STREQ (" foo = bar.field;\n"
3314 " ~A~ ~B~ ~~C~~\n",
3315 pp_formatted_text (dc.printer));
3318 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3320 static void
3321 test_one_liner_fixit_insert_before ()
3323 test_diagnostic_context dc;
3324 location_t caret = linemap_position_for_column (line_table, 7);
3325 rich_location richloc (line_table, caret);
3326 richloc.add_fixit_insert_before ("&");
3327 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3328 ASSERT_STREQ (" foo = bar.field;\n"
3329 " ^\n"
3330 " &\n",
3331 pp_formatted_text (dc.printer));
3334 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3336 static void
3337 test_one_liner_fixit_insert_after ()
3339 test_diagnostic_context dc;
3340 location_t start = linemap_position_for_column (line_table, 1);
3341 location_t finish = linemap_position_for_column (line_table, 3);
3342 location_t foo = make_location (start, start, finish);
3343 rich_location richloc (line_table, foo);
3344 richloc.add_fixit_insert_after ("[0]");
3345 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3346 ASSERT_STREQ (" foo = bar.field;\n"
3347 " ^~~\n"
3348 " [0]\n",
3349 pp_formatted_text (dc.printer));
3352 /* Removal fix-it hint: removal of the ".field".
3353 Also verify the interaction of pp_set_prefix with rulers and
3354 fix-it hints. */
3356 static void
3357 test_one_liner_fixit_remove ()
3359 location_t start = linemap_position_for_column (line_table, 10);
3360 location_t finish = linemap_position_for_column (line_table, 15);
3361 location_t dot = make_location (start, start, finish);
3362 rich_location richloc (line_table, dot);
3363 richloc.add_fixit_remove ();
3365 /* Normal. */
3367 test_diagnostic_context dc;
3368 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3369 ASSERT_STREQ (" foo = bar.field;\n"
3370 " ^~~~~~\n"
3371 " ------\n",
3372 pp_formatted_text (dc.printer));
3375 /* Test of adding a prefix. */
3377 test_diagnostic_context dc;
3378 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3379 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3380 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3381 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3382 "TEST PREFIX: ^~~~~~\n"
3383 "TEST PREFIX: ------\n",
3384 pp_formatted_text (dc.printer));
3387 /* Normal, with ruler. */
3389 test_diagnostic_context dc;
3390 dc.m_source_printing.show_ruler_p = true;
3391 dc.m_source_printing.max_width = 104;
3392 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3393 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3394 " 1 2 3 4 5 6 7 8 9 0 \n"
3395 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3396 " foo = bar.field;\n"
3397 " ^~~~~~\n"
3398 " ------\n",
3399 pp_formatted_text (dc.printer));
3402 /* Test of adding a prefix, with ruler. */
3404 test_diagnostic_context dc;
3405 dc.m_source_printing.show_ruler_p = true;
3406 dc.m_source_printing.max_width = 50;
3407 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3408 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3409 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3410 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3411 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3412 "TEST PREFIX: foo = bar.field;\n"
3413 "TEST PREFIX: ^~~~~~\n"
3414 "TEST PREFIX: ------\n",
3415 pp_formatted_text (dc.printer));
3418 /* Test of adding a prefix, with ruler and line numbers. */
3420 test_diagnostic_context dc;
3421 dc.m_source_printing.show_ruler_p = true;
3422 dc.m_source_printing.max_width = 50;
3423 dc.m_source_printing.show_line_numbers_p = true;
3424 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3425 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3426 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3427 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3428 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3429 "TEST PREFIX: 1 | foo = bar.field;\n"
3430 "TEST PREFIX: | ^~~~~~\n"
3431 "TEST PREFIX: | ------\n",
3432 pp_formatted_text (dc.printer));
3436 /* Replace fix-it hint: replacing "field" with "m_field". */
3438 static void
3439 test_one_liner_fixit_replace ()
3441 test_diagnostic_context dc;
3442 location_t start = linemap_position_for_column (line_table, 11);
3443 location_t finish = linemap_position_for_column (line_table, 15);
3444 location_t field = make_location (start, start, finish);
3445 rich_location richloc (line_table, field);
3446 richloc.add_fixit_replace ("m_field");
3447 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3448 ASSERT_STREQ (" foo = bar.field;\n"
3449 " ^~~~~\n"
3450 " m_field\n",
3451 pp_formatted_text (dc.printer));
3454 /* Replace fix-it hint: replacing "field" with "m_field",
3455 but where the caret was elsewhere. */
3457 static void
3458 test_one_liner_fixit_replace_non_equal_range ()
3460 test_diagnostic_context dc;
3461 location_t equals = linemap_position_for_column (line_table, 5);
3462 location_t start = linemap_position_for_column (line_table, 11);
3463 location_t finish = linemap_position_for_column (line_table, 15);
3464 rich_location richloc (line_table, equals);
3465 source_range range;
3466 range.m_start = start;
3467 range.m_finish = finish;
3468 richloc.add_fixit_replace (range, "m_field");
3469 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3470 /* The replacement range is not indicated in the annotation line, so
3471 it should be indicated via an additional underline. */
3472 ASSERT_STREQ (" foo = bar.field;\n"
3473 " ^\n"
3474 " -----\n"
3475 " m_field\n",
3476 pp_formatted_text (dc.printer));
3479 /* Replace fix-it hint: replacing "field" with "m_field",
3480 where the caret was elsewhere, but where a secondary range
3481 exactly covers "field". */
3483 static void
3484 test_one_liner_fixit_replace_equal_secondary_range ()
3486 test_diagnostic_context dc;
3487 location_t equals = linemap_position_for_column (line_table, 5);
3488 location_t start = linemap_position_for_column (line_table, 11);
3489 location_t finish = linemap_position_for_column (line_table, 15);
3490 rich_location richloc (line_table, equals);
3491 location_t field = make_location (start, start, finish);
3492 richloc.add_range (field);
3493 richloc.add_fixit_replace (field, "m_field");
3494 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3495 /* The replacement range is indicated in the annotation line,
3496 so it shouldn't be indicated via an additional underline. */
3497 ASSERT_STREQ (" foo = bar.field;\n"
3498 " ^ ~~~~~\n"
3499 " m_field\n",
3500 pp_formatted_text (dc.printer));
3503 /* Verify that we can use ad-hoc locations when adding fixits to a
3504 rich_location. */
3506 static void
3507 test_one_liner_fixit_validation_adhoc_locations ()
3509 /* Generate a range that's too long to be packed, so must
3510 be stored as an ad-hoc location (given the defaults
3511 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3512 const location_t c7 = linemap_position_for_column (line_table, 7);
3513 const location_t c47 = linemap_position_for_column (line_table, 47);
3514 const location_t loc = make_location (c7, c7, c47);
3516 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3517 return;
3519 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3521 /* Insert. */
3523 rich_location richloc (line_table, loc);
3524 richloc.add_fixit_insert_before (loc, "test");
3525 /* It should not have been discarded by the validator. */
3526 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3528 test_diagnostic_context dc;
3529 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3530 ASSERT_STREQ (" foo = bar.field;\n"
3531 " ^~~~~~~~~~ \n"
3532 " test\n",
3533 pp_formatted_text (dc.printer));
3536 /* Remove. */
3538 rich_location richloc (line_table, loc);
3539 source_range range = source_range::from_locations (loc, c47);
3540 richloc.add_fixit_remove (range);
3541 /* It should not have been discarded by the validator. */
3542 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3544 test_diagnostic_context dc;
3545 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3546 ASSERT_STREQ (" foo = bar.field;\n"
3547 " ^~~~~~~~~~ \n"
3548 " -----------------------------------------\n",
3549 pp_formatted_text (dc.printer));
3552 /* Replace. */
3554 rich_location richloc (line_table, loc);
3555 source_range range = source_range::from_locations (loc, c47);
3556 richloc.add_fixit_replace (range, "test");
3557 /* It should not have been discarded by the validator. */
3558 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3560 test_diagnostic_context dc;
3561 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3562 ASSERT_STREQ (" foo = bar.field;\n"
3563 " ^~~~~~~~~~ \n"
3564 " test\n",
3565 pp_formatted_text (dc.printer));
3569 /* Test of consolidating insertions at the same location. */
3571 static void
3572 test_one_liner_many_fixits_1 ()
3574 test_diagnostic_context dc;
3575 location_t equals = linemap_position_for_column (line_table, 5);
3576 rich_location richloc (line_table, equals);
3577 for (int i = 0; i < 19; i++)
3578 richloc.add_fixit_insert_before ("a");
3579 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3580 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3581 ASSERT_STREQ (" foo = bar.field;\n"
3582 " ^\n"
3583 " aaaaaaaaaaaaaaaaaaa\n",
3584 pp_formatted_text (dc.printer));
3587 /* Ensure that we can add an arbitrary number of fix-it hints to a
3588 rich_location, even if they are not consolidated. */
3590 static void
3591 test_one_liner_many_fixits_2 ()
3593 test_diagnostic_context dc;
3594 location_t equals = linemap_position_for_column (line_table, 5);
3595 rich_location richloc (line_table, equals);
3596 for (int i = 0; i < 19; i++)
3598 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3599 richloc.add_fixit_insert_before (loc, "a");
3601 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3602 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3603 ASSERT_STREQ (" foo = bar.field;\n"
3604 " ^\n"
3605 " a a a a a a a a a a a a a a a a a a a\n",
3606 pp_formatted_text (dc.printer));
3609 /* Test of labeling the ranges within a rich_location. */
3611 static void
3612 test_one_liner_labels ()
3614 location_t foo
3615 = make_location (linemap_position_for_column (line_table, 1),
3616 linemap_position_for_column (line_table, 1),
3617 linemap_position_for_column (line_table, 3));
3618 location_t bar
3619 = make_location (linemap_position_for_column (line_table, 7),
3620 linemap_position_for_column (line_table, 7),
3621 linemap_position_for_column (line_table, 9));
3622 location_t field
3623 = make_location (linemap_position_for_column (line_table, 11),
3624 linemap_position_for_column (line_table, 11),
3625 linemap_position_for_column (line_table, 15));
3627 /* Example where all the labels fit on one line. */
3629 text_range_label label0 ("0");
3630 text_range_label label1 ("1");
3631 text_range_label label2 ("2");
3632 gcc_rich_location richloc (foo, &label0);
3633 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3634 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3637 test_diagnostic_context dc;
3638 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3639 ASSERT_STREQ (" foo = bar.field;\n"
3640 " ^~~ ~~~ ~~~~~\n"
3641 " | | |\n"
3642 " 0 1 2\n",
3643 pp_formatted_text (dc.printer));
3646 /* Verify that we can disable label-printing. */
3648 test_diagnostic_context dc;
3649 dc.m_source_printing.show_labels_p = false;
3650 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3651 ASSERT_STREQ (" foo = bar.field;\n"
3652 " ^~~ ~~~ ~~~~~\n",
3653 pp_formatted_text (dc.printer));
3657 /* Example where the labels need extra lines. */
3659 text_range_label label0 ("label 0");
3660 text_range_label label1 ("label 1");
3661 text_range_label label2 ("label 2");
3662 gcc_rich_location richloc (foo, &label0);
3663 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3664 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3666 test_diagnostic_context dc;
3667 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3668 ASSERT_STREQ (" foo = bar.field;\n"
3669 " ^~~ ~~~ ~~~~~\n"
3670 " | | |\n"
3671 " | | label 2\n"
3672 " | label 1\n"
3673 " label 0\n",
3674 pp_formatted_text (dc.printer));
3677 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3678 but label 1 just touches label 2. */
3680 text_range_label label0 ("aaaaa");
3681 text_range_label label1 ("bbbb");
3682 text_range_label label2 ("c");
3683 gcc_rich_location richloc (foo, &label0);
3684 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3685 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3687 test_diagnostic_context dc;
3688 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3689 ASSERT_STREQ (" foo = bar.field;\n"
3690 " ^~~ ~~~ ~~~~~\n"
3691 " | | |\n"
3692 " | | c\n"
3693 " aaaaa bbbb\n",
3694 pp_formatted_text (dc.printer));
3697 /* Example of out-of-order ranges (thus requiring a sort). */
3699 text_range_label label0 ("0");
3700 text_range_label label1 ("1");
3701 text_range_label label2 ("2");
3702 gcc_rich_location richloc (field, &label0);
3703 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3704 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3706 test_diagnostic_context dc;
3707 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3708 ASSERT_STREQ (" foo = bar.field;\n"
3709 " ~~~ ~~~ ^~~~~\n"
3710 " | | |\n"
3711 " 2 1 0\n",
3712 pp_formatted_text (dc.printer));
3715 /* Ensure we don't ICE if multiple ranges with labels are on
3716 the same point. */
3718 text_range_label label0 ("label 0");
3719 text_range_label label1 ("label 1");
3720 text_range_label label2 ("label 2");
3721 gcc_rich_location richloc (bar, &label0);
3722 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3723 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3725 test_diagnostic_context dc;
3726 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3727 ASSERT_STREQ (" foo = bar.field;\n"
3728 " ^~~\n"
3729 " |\n"
3730 " label 0\n"
3731 " label 1\n"
3732 " label 2\n",
3733 pp_formatted_text (dc.printer));
3736 /* Example of out-of-order ranges (thus requiring a sort), where
3737 they overlap, and there are multiple ranges on the same point. */
3739 text_range_label label_0a ("label 0a");
3740 text_range_label label_1a ("label 1a");
3741 text_range_label label_2a ("label 2a");
3742 text_range_label label_0b ("label 0b");
3743 text_range_label label_1b ("label 1b");
3744 text_range_label label_2b ("label 2b");
3745 text_range_label label_0c ("label 0c");
3746 text_range_label label_1c ("label 1c");
3747 text_range_label label_2c ("label 2c");
3748 gcc_rich_location richloc (field, &label_0a);
3749 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3750 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3752 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3753 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3754 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3756 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3757 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3758 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3760 test_diagnostic_context dc;
3761 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3762 ASSERT_STREQ (" foo = bar.field;\n"
3763 " ~~~ ~~~ ^~~~~\n"
3764 " | | |\n"
3765 " | | label 0a\n"
3766 " | | label 0b\n"
3767 " | | label 0c\n"
3768 " | label 1a\n"
3769 " | label 1b\n"
3770 " | label 1c\n"
3771 " label 2a\n"
3772 " label 2b\n"
3773 " label 2c\n",
3774 pp_formatted_text (dc.printer));
3777 /* Verify that a NULL result from range_label::get_text is
3778 handled gracefully. */
3780 text_range_label label (NULL);
3781 gcc_rich_location richloc (bar, &label);
3783 test_diagnostic_context dc;
3784 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3785 ASSERT_STREQ (" foo = bar.field;\n"
3786 " ^~~\n",
3787 pp_formatted_text (dc.printer));
3790 /* TODO: example of formatted printing (needs to be in
3791 gcc-rich-location.cc due to Makefile.in issues). */
3794 /* Run the various one-liner tests. */
3796 static void
3797 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3799 /* Create a tempfile and write some text to it.
3800 ....................0000000001111111.
3801 ....................1234567890123456. */
3802 const char *content = "foo = bar.field;\n";
3803 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3804 line_table_test ltt (case_);
3806 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3808 location_t line_end = linemap_position_for_column (line_table, 16);
3810 /* Don't attempt to run the tests if column data might be unavailable. */
3811 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3812 return;
3814 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3815 ASSERT_EQ (1, LOCATION_LINE (line_end));
3816 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3818 test_one_liner_simple_caret ();
3819 test_one_liner_caret_and_range ();
3820 test_one_liner_multiple_carets_and_ranges ();
3821 test_one_liner_fixit_insert_before ();
3822 test_one_liner_fixit_insert_after ();
3823 test_one_liner_fixit_remove ();
3824 test_one_liner_fixit_replace ();
3825 test_one_liner_fixit_replace_non_equal_range ();
3826 test_one_liner_fixit_replace_equal_secondary_range ();
3827 test_one_liner_fixit_validation_adhoc_locations ();
3828 test_one_liner_many_fixits_1 ();
3829 test_one_liner_many_fixits_2 ();
3830 test_one_liner_labels ();
3833 /* Version of all one-liner tests exercising multibyte awareness. For
3834 simplicity we stick to using two multibyte characters in the test, U+1F602
3835 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3836 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3837 below asserts would be easier to read if we used UTF-8 directly in the
3838 string constants, but it seems better not to demand the host compiler
3839 support this, when it isn't otherwise necessary. Instead, whenever an
3840 extended character appears in a string, we put a line break after it so that
3841 all succeeding characters can appear visually at the correct display column.
3843 All of these work on the following 1-line source file:
3845 .0000000001111111111222222 display
3846 .1234567890123456789012345 columns
3847 "SS_foo = P_bar.SS_fieldP;\n"
3848 .0000000111111111222222223 byte
3849 .1356789012456789134567891 columns
3851 which is set up by test_diagnostic_show_locus_one_liner and calls
3852 them. Here SS represents the two display columns for the U+1F602 emoji and
3853 P represents the one display column for the U+03C0 pi symbol. */
3855 /* Just a caret. */
3857 static void
3858 test_one_liner_simple_caret_utf8 ()
3860 test_diagnostic_context dc;
3861 location_t caret = linemap_position_for_column (line_table, 18);
3862 rich_location richloc (line_table, caret);
3863 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3864 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3865 "_foo = \xcf\x80"
3866 "_bar.\xf0\x9f\x98\x82"
3867 "_field\xcf\x80"
3868 ";\n"
3869 " ^\n",
3870 pp_formatted_text (dc.printer));
3873 /* Caret and range. */
3874 static void
3875 test_one_liner_caret_and_range_utf8 ()
3877 test_diagnostic_context dc;
3878 location_t caret = linemap_position_for_column (line_table, 18);
3879 location_t start = linemap_position_for_column (line_table, 12);
3880 location_t finish = linemap_position_for_column (line_table, 30);
3881 location_t loc = make_location (caret, start, finish);
3882 rich_location richloc (line_table, loc);
3883 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3884 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3885 "_foo = \xcf\x80"
3886 "_bar.\xf0\x9f\x98\x82"
3887 "_field\xcf\x80"
3888 ";\n"
3889 " ~~~~~^~~~~~~~~~\n",
3890 pp_formatted_text (dc.printer));
3893 /* Multiple ranges and carets. */
3895 static void
3896 test_one_liner_multiple_carets_and_ranges_utf8 ()
3898 test_diagnostic_context dc;
3899 location_t foo
3900 = make_location (linemap_position_for_column (line_table, 7),
3901 linemap_position_for_column (line_table, 1),
3902 linemap_position_for_column (line_table, 8));
3903 dc.m_source_printing.caret_chars[0] = 'A';
3905 location_t bar
3906 = make_location (linemap_position_for_column (line_table, 16),
3907 linemap_position_for_column (line_table, 12),
3908 linemap_position_for_column (line_table, 17));
3909 dc.m_source_printing.caret_chars[1] = 'B';
3911 location_t field
3912 = make_location (linemap_position_for_column (line_table, 26),
3913 linemap_position_for_column (line_table, 19),
3914 linemap_position_for_column (line_table, 30));
3915 dc.m_source_printing.caret_chars[2] = 'C';
3916 rich_location richloc (line_table, foo);
3917 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3918 richloc.add_range (field, SHOW_RANGE_WITH_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 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3926 pp_formatted_text (dc.printer));
3929 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3931 static void
3932 test_one_liner_fixit_insert_before_utf8 ()
3934 test_diagnostic_context dc;
3935 location_t caret = linemap_position_for_column (line_table, 12);
3936 rich_location richloc (line_table, caret);
3937 richloc.add_fixit_insert_before ("&");
3938 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3939 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3940 "_foo = \xcf\x80"
3941 "_bar.\xf0\x9f\x98\x82"
3942 "_field\xcf\x80"
3943 ";\n"
3944 " ^\n"
3945 " &\n",
3946 pp_formatted_text (dc.printer));
3949 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3951 static void
3952 test_one_liner_fixit_insert_after_utf8 ()
3954 test_diagnostic_context dc;
3955 location_t start = linemap_position_for_column (line_table, 1);
3956 location_t finish = linemap_position_for_column (line_table, 8);
3957 location_t foo = make_location (start, start, finish);
3958 rich_location richloc (line_table, foo);
3959 richloc.add_fixit_insert_after ("[0]");
3960 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3961 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3962 "_foo = \xcf\x80"
3963 "_bar.\xf0\x9f\x98\x82"
3964 "_field\xcf\x80"
3965 ";\n"
3966 " ^~~~~~\n"
3967 " [0]\n",
3968 pp_formatted_text (dc.printer));
3971 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3973 static void
3974 test_one_liner_fixit_remove_utf8 ()
3976 test_diagnostic_context dc;
3977 location_t start = linemap_position_for_column (line_table, 18);
3978 location_t finish = linemap_position_for_column (line_table, 30);
3979 location_t dot = make_location (start, start, finish);
3980 rich_location richloc (line_table, dot);
3981 richloc.add_fixit_remove ();
3982 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3983 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3984 "_foo = \xcf\x80"
3985 "_bar.\xf0\x9f\x98\x82"
3986 "_field\xcf\x80"
3987 ";\n"
3988 " ^~~~~~~~~~\n"
3989 " ----------\n",
3990 pp_formatted_text (dc.printer));
3993 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3995 static void
3996 test_one_liner_fixit_replace_utf8 ()
3998 test_diagnostic_context dc;
3999 location_t start = linemap_position_for_column (line_table, 19);
4000 location_t finish = linemap_position_for_column (line_table, 30);
4001 location_t field = make_location (start, start, finish);
4002 rich_location richloc (line_table, field);
4003 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4004 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4005 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4006 "_foo = \xcf\x80"
4007 "_bar.\xf0\x9f\x98\x82"
4008 "_field\xcf\x80"
4009 ";\n"
4010 " ^~~~~~~~~\n"
4011 " m_\xf0\x9f\x98\x82"
4012 "_field\xcf\x80\n",
4013 pp_formatted_text (dc.printer));
4016 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4017 but where the caret was elsewhere. */
4019 static void
4020 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4022 test_diagnostic_context dc;
4023 location_t equals = linemap_position_for_column (line_table, 10);
4024 location_t start = linemap_position_for_column (line_table, 19);
4025 location_t finish = linemap_position_for_column (line_table, 30);
4026 rich_location richloc (line_table, equals);
4027 source_range range;
4028 range.m_start = start;
4029 range.m_finish = finish;
4030 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4031 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4032 /* The replacement range is not indicated in the annotation line, so
4033 it should be indicated via an additional underline. */
4034 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4035 "_foo = \xcf\x80"
4036 "_bar.\xf0\x9f\x98\x82"
4037 "_field\xcf\x80"
4038 ";\n"
4039 " ^\n"
4040 " ---------\n"
4041 " m_\xf0\x9f\x98\x82"
4042 "_field\xcf\x80\n",
4043 pp_formatted_text (dc.printer));
4046 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4047 where the caret was elsewhere, but where a secondary range
4048 exactly covers "field". */
4050 static void
4051 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4053 test_diagnostic_context dc;
4054 location_t equals = linemap_position_for_column (line_table, 10);
4055 location_t start = linemap_position_for_column (line_table, 19);
4056 location_t finish = linemap_position_for_column (line_table, 30);
4057 rich_location richloc (line_table, equals);
4058 location_t field = make_location (start, start, finish);
4059 richloc.add_range (field);
4060 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4061 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4062 /* The replacement range is indicated in the annotation line,
4063 so it shouldn't be indicated via an additional underline. */
4064 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4065 "_foo = \xcf\x80"
4066 "_bar.\xf0\x9f\x98\x82"
4067 "_field\xcf\x80"
4068 ";\n"
4069 " ^ ~~~~~~~~~\n"
4070 " m_\xf0\x9f\x98\x82"
4071 "_field\xcf\x80\n",
4072 pp_formatted_text (dc.printer));
4075 /* Verify that we can use ad-hoc locations when adding fixits to a
4076 rich_location. */
4078 static void
4079 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4081 /* Generate a range that's too long to be packed, so must
4082 be stored as an ad-hoc location (given the defaults
4083 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4084 const location_t c12 = linemap_position_for_column (line_table, 12);
4085 const location_t c52 = linemap_position_for_column (line_table, 52);
4086 const location_t loc = make_location (c12, c12, c52);
4088 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4089 return;
4091 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4093 /* Insert. */
4095 rich_location richloc (line_table, loc);
4096 richloc.add_fixit_insert_before (loc, "test");
4097 /* It should not have been discarded by the validator. */
4098 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4100 test_diagnostic_context dc;
4101 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4102 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4103 "_foo = \xcf\x80"
4104 "_bar.\xf0\x9f\x98\x82"
4105 "_field\xcf\x80"
4106 ";\n"
4107 " ^~~~~~~~~~~~~~~~ \n"
4108 " test\n",
4109 pp_formatted_text (dc.printer));
4112 /* Remove. */
4114 rich_location richloc (line_table, loc);
4115 source_range range = source_range::from_locations (loc, c52);
4116 richloc.add_fixit_remove (range);
4117 /* It should not have been discarded by the validator. */
4118 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4120 test_diagnostic_context dc;
4121 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4122 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4123 "_foo = \xcf\x80"
4124 "_bar.\xf0\x9f\x98\x82"
4125 "_field\xcf\x80"
4126 ";\n"
4127 " ^~~~~~~~~~~~~~~~ \n"
4128 " -------------------------------------\n",
4129 pp_formatted_text (dc.printer));
4132 /* Replace. */
4134 rich_location richloc (line_table, loc);
4135 source_range range = source_range::from_locations (loc, c52);
4136 richloc.add_fixit_replace (range, "test");
4137 /* It should not have been discarded by the validator. */
4138 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4140 test_diagnostic_context dc;
4141 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4142 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4143 "_foo = \xcf\x80"
4144 "_bar.\xf0\x9f\x98\x82"
4145 "_field\xcf\x80"
4146 ";\n"
4147 " ^~~~~~~~~~~~~~~~ \n"
4148 " test\n",
4149 pp_formatted_text (dc.printer));
4153 /* Test of consolidating insertions at the same location. */
4155 static void
4156 test_one_liner_many_fixits_1_utf8 ()
4158 test_diagnostic_context dc;
4159 location_t equals = linemap_position_for_column (line_table, 10);
4160 rich_location richloc (line_table, equals);
4161 for (int i = 0; i < 19; i++)
4162 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4163 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4164 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4165 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4166 "_foo = \xcf\x80"
4167 "_bar.\xf0\x9f\x98\x82"
4168 "_field\xcf\x80"
4169 ";\n"
4170 " ^\n"
4171 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4172 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4173 pp_formatted_text (dc.printer));
4176 /* Ensure that we can add an arbitrary number of fix-it hints to a
4177 rich_location, even if they are not consolidated. */
4179 static void
4180 test_one_liner_many_fixits_2_utf8 ()
4182 test_diagnostic_context dc;
4183 location_t equals = linemap_position_for_column (line_table, 10);
4184 rich_location richloc (line_table, equals);
4185 const int nlocs = 19;
4186 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4187 34, 36, 38, 40, 42, 44};
4188 for (int i = 0; i != nlocs; ++i)
4190 location_t loc = linemap_position_for_column (line_table, locs[i]);
4191 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4194 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4195 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4196 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4197 "_foo = \xcf\x80"
4198 "_bar.\xf0\x9f\x98\x82"
4199 "_field\xcf\x80"
4200 ";\n"
4201 " ^\n"
4202 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4203 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4204 pp_formatted_text (dc.printer));
4207 /* Test of labeling the ranges within a rich_location. */
4209 static void
4210 test_one_liner_labels_utf8 ()
4212 location_t foo
4213 = make_location (linemap_position_for_column (line_table, 1),
4214 linemap_position_for_column (line_table, 1),
4215 linemap_position_for_column (line_table, 8));
4216 location_t bar
4217 = make_location (linemap_position_for_column (line_table, 12),
4218 linemap_position_for_column (line_table, 12),
4219 linemap_position_for_column (line_table, 17));
4220 location_t field
4221 = make_location (linemap_position_for_column (line_table, 19),
4222 linemap_position_for_column (line_table, 19),
4223 linemap_position_for_column (line_table, 30));
4225 /* Example where all the labels fit on one line. */
4227 /* These three labels contain multibyte characters such that their byte
4228 lengths are respectively (12, 10, 18), but their display widths are only
4229 (6, 5, 9). All three fit on the line when considering the display
4230 widths, but not when considering the byte widths, so verify that we do
4231 indeed put them all on one line. */
4232 text_range_label label0
4233 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4234 text_range_label label1
4235 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4236 text_range_label label2
4237 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4238 "\xcf\x80");
4239 gcc_rich_location richloc (foo, &label0);
4240 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4241 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4244 test_diagnostic_context dc;
4245 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4246 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4247 "_foo = \xcf\x80"
4248 "_bar.\xf0\x9f\x98\x82"
4249 "_field\xcf\x80"
4250 ";\n"
4251 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4252 " | | |\n"
4253 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4254 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4255 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4256 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4257 pp_formatted_text (dc.printer));
4262 /* Example where the labels need extra lines. */
4264 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4265 text_range_label label1 ("label 1\xcf\x80");
4266 text_range_label label2 ("label 2\xcf\x80");
4267 gcc_rich_location richloc (foo, &label0);
4268 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4269 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4271 test_diagnostic_context dc;
4272 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4274 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4275 "_foo = \xcf\x80"
4276 "_bar.\xf0\x9f\x98\x82"
4277 "_field\xcf\x80"
4278 ";\n"
4279 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4280 " | | |\n"
4281 " | | label 2\xcf\x80\n"
4282 " | label 1\xcf\x80\n"
4283 " label 0\xf0\x9f\x98\x82\n",
4284 pp_formatted_text (dc.printer));
4287 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4288 but label 1 just touches label 2. */
4290 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4291 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4292 text_range_label label2 ("c");
4293 gcc_rich_location richloc (foo, &label0);
4294 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4295 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4297 test_diagnostic_context dc;
4298 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4299 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4300 "_foo = \xcf\x80"
4301 "_bar.\xf0\x9f\x98\x82"
4302 "_field\xcf\x80"
4303 ";\n"
4304 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4305 " | | |\n"
4306 " | | c\n"
4307 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4308 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4309 pp_formatted_text (dc.printer));
4312 /* Example of escaping the source lines. */
4314 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4315 text_range_label label1 ("label 1\xcf\x80");
4316 text_range_label label2 ("label 2\xcf\x80");
4317 gcc_rich_location richloc (foo, &label0);
4318 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4319 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4320 richloc.set_escape_on_output (true);
4323 test_diagnostic_context dc;
4324 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
4325 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4326 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4327 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4328 " | | |\n"
4329 " | | label 2\xcf\x80\n"
4330 " | label 1\xcf\x80\n"
4331 " label 0\xf0\x9f\x98\x82\n",
4332 pp_formatted_text (dc.printer));
4335 test_diagnostic_context dc;
4336 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
4337 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4338 ASSERT_STREQ
4339 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4340 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4341 " | | |\n"
4342 " | | label 2\xcf\x80\n"
4343 " | label 1\xcf\x80\n"
4344 " label 0\xf0\x9f\x98\x82\n",
4345 pp_formatted_text (dc.printer));
4350 /* Make sure that colorization codes don't interrupt a multibyte
4351 sequence, which would corrupt it. */
4352 static void
4353 test_one_liner_colorized_utf8 ()
4355 test_diagnostic_context dc;
4356 dc.m_source_printing.colorize_source_p = true;
4357 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4358 const location_t pi = linemap_position_for_column (line_table, 12);
4359 rich_location richloc (line_table, pi);
4360 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4362 /* In order to avoid having the test depend on exactly how the colorization
4363 was effected, just confirm there are two pi characters in the output. */
4364 const char *result = pp_formatted_text (dc.printer);
4365 const char *null_term = result + strlen (result);
4366 const char *first_pi = strstr (result, "\xcf\x80");
4367 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4368 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4371 /* Run the various one-liner tests. */
4373 static void
4374 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4376 /* Create a tempfile and write some text to it. */
4377 const char *content
4378 /* Display columns.
4379 0000000000000000000000011111111111111111111111111111112222222222222
4380 1111111122222222345678900000000123456666666677777777890123444444445 */
4381 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4382 /* 0000000000000000000001111111111111111111222222222222222222222233333
4383 1111222233334444567890122223333456789999000011112222345678999900001
4384 Byte columns. */
4385 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4386 line_table_test ltt (case_);
4388 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4390 location_t line_end = linemap_position_for_column (line_table, 31);
4392 /* Don't attempt to run the tests if column data might be unavailable. */
4393 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4394 return;
4396 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4397 ASSERT_EQ (1, LOCATION_LINE (line_end));
4398 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4400 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4401 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4402 def_policy ()));
4403 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4404 def_policy ()));
4406 test_one_liner_simple_caret_utf8 ();
4407 test_one_liner_caret_and_range_utf8 ();
4408 test_one_liner_multiple_carets_and_ranges_utf8 ();
4409 test_one_liner_fixit_insert_before_utf8 ();
4410 test_one_liner_fixit_insert_after_utf8 ();
4411 test_one_liner_fixit_remove_utf8 ();
4412 test_one_liner_fixit_replace_utf8 ();
4413 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4414 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4415 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4416 test_one_liner_many_fixits_1_utf8 ();
4417 test_one_liner_many_fixits_2_utf8 ();
4418 test_one_liner_labels_utf8 ();
4419 test_one_liner_colorized_utf8 ();
4422 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4424 static void
4425 test_add_location_if_nearby (const line_table_case &case_)
4427 /* Create a tempfile and write some text to it.
4428 ...000000000111111111122222222223333333333.
4429 ...123456789012345678901234567890123456789. */
4430 const char *content
4431 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4432 "struct different_line\n" /* line 2. */
4433 "{\n" /* line 3. */
4434 " double x;\n" /* line 4. */
4435 " double y;\n" /* line 5. */
4436 ";\n"); /* line 6. */
4437 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4438 line_table_test ltt (case_);
4440 const line_map_ordinary *ord_map
4441 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4442 tmp.get_filename (), 0));
4444 linemap_line_start (line_table, 1, 100);
4446 const location_t final_line_end
4447 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4449 /* Don't attempt to run the tests if column data might be unavailable. */
4450 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4451 return;
4453 /* Test of add_location_if_nearby on the same line as the
4454 primary location. */
4456 const location_t missing_close_brace_1_39
4457 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4458 const location_t matching_open_brace_1_18
4459 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4460 gcc_rich_location richloc (missing_close_brace_1_39);
4461 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4462 ASSERT_TRUE (added);
4463 ASSERT_EQ (2, richloc.get_num_locations ());
4464 test_diagnostic_context dc;
4465 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4466 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4467 " ~ ^\n",
4468 pp_formatted_text (dc.printer));
4471 /* Test of add_location_if_nearby on a different line to the
4472 primary location. */
4474 const location_t missing_close_brace_6_1
4475 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4476 const location_t matching_open_brace_3_1
4477 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4478 gcc_rich_location richloc (missing_close_brace_6_1);
4479 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4480 ASSERT_FALSE (added);
4481 ASSERT_EQ (1, richloc.get_num_locations ());
4485 /* Verify that we print fixits even if they only affect lines
4486 outside those covered by the ranges in the rich_location. */
4488 static void
4489 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4491 /* Create a tempfile and write some text to it.
4492 ...000000000111111111122222222223333333333.
4493 ...123456789012345678901234567890123456789. */
4494 const char *content
4495 = ("struct point { double x; double y; };\n" /* line 1. */
4496 "struct point origin = {x: 0.0,\n" /* line 2. */
4497 " y\n" /* line 3. */
4498 "\n" /* line 4. */
4499 "\n" /* line 5. */
4500 " : 0.0};\n"); /* line 6. */
4501 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4502 line_table_test ltt (case_);
4504 const line_map_ordinary *ord_map
4505 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4506 tmp.get_filename (), 0));
4508 linemap_line_start (line_table, 1, 100);
4510 const location_t final_line_end
4511 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4513 /* Don't attempt to run the tests if column data might be unavailable. */
4514 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4515 return;
4517 /* A pair of tests for modernizing the initializers to C99-style. */
4519 /* The one-liner case (line 2). */
4521 test_diagnostic_context dc;
4522 const location_t x
4523 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4524 const location_t colon
4525 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4526 rich_location richloc (line_table, colon);
4527 richloc.add_fixit_insert_before (x, ".");
4528 richloc.add_fixit_replace (colon, "=");
4529 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4530 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4531 " ^\n"
4532 " .=\n",
4533 pp_formatted_text (dc.printer));
4536 /* The multiline case. The caret for the rich_location is on line 6;
4537 verify that insertion fixit on line 3 is still printed (and that
4538 span starts are printed due to the gap between the span at line 3
4539 and that at line 6). */
4541 test_diagnostic_context dc;
4542 const location_t y
4543 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4544 const location_t colon
4545 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4546 rich_location richloc (line_table, colon);
4547 richloc.add_fixit_insert_before (y, ".");
4548 richloc.add_fixit_replace (colon, "=");
4549 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4550 ASSERT_STREQ ("FILENAME:3:24:\n"
4551 " y\n"
4552 " .\n"
4553 "FILENAME:6:25:\n"
4554 " : 0.0};\n"
4555 " ^\n"
4556 " =\n",
4557 pp_formatted_text (dc.printer));
4560 /* As above, but verify the behavior of multiple line spans
4561 with line-numbering enabled. */
4563 const location_t y
4564 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4565 const location_t colon
4566 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4567 rich_location richloc (line_table, colon);
4568 richloc.add_fixit_insert_before (y, ".");
4569 richloc.add_fixit_replace (colon, "=");
4570 test_diagnostic_context dc;
4571 dc.m_source_printing.show_line_numbers_p = true;
4572 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4573 ASSERT_STREQ (" 3 | y\n"
4574 " | .\n"
4575 "......\n"
4576 " 6 | : 0.0};\n"
4577 " | ^\n"
4578 " | =\n",
4579 pp_formatted_text (dc.printer));
4584 /* Verify that fix-it hints are appropriately consolidated.
4586 If any fix-it hints in a rich_location involve locations beyond
4587 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4588 the fix-it as a whole, so there should be none.
4590 Otherwise, verify that consecutive "replace" and "remove" fix-its
4591 are merged, and that other fix-its remain separate. */
4593 static void
4594 test_fixit_consolidation (const line_table_case &case_)
4596 line_table_test ltt (case_);
4598 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4600 const location_t c10 = linemap_position_for_column (line_table, 10);
4601 const location_t c15 = linemap_position_for_column (line_table, 15);
4602 const location_t c16 = linemap_position_for_column (line_table, 16);
4603 const location_t c17 = linemap_position_for_column (line_table, 17);
4604 const location_t c20 = linemap_position_for_column (line_table, 20);
4605 const location_t c21 = linemap_position_for_column (line_table, 21);
4606 const location_t caret = c10;
4608 /* Insert + insert. */
4610 rich_location richloc (line_table, caret);
4611 richloc.add_fixit_insert_before (c10, "foo");
4612 richloc.add_fixit_insert_before (c15, "bar");
4614 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4615 /* Bogus column info for 2nd fixit, so no fixits. */
4616 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4617 else
4618 /* They should not have been merged. */
4619 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4622 /* Insert + replace. */
4624 rich_location richloc (line_table, caret);
4625 richloc.add_fixit_insert_before (c10, "foo");
4626 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4627 "bar");
4629 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4630 /* Bogus column info for 2nd fixit, so no fixits. */
4631 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4632 else
4633 /* They should not have been merged. */
4634 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4637 /* Replace + non-consecutive insert. */
4639 rich_location richloc (line_table, caret);
4640 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4641 "bar");
4642 richloc.add_fixit_insert_before (c17, "foo");
4644 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4645 /* Bogus column info for 2nd fixit, so no fixits. */
4646 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4647 else
4648 /* They should not have been merged. */
4649 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4652 /* Replace + non-consecutive replace. */
4654 rich_location richloc (line_table, caret);
4655 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4656 "foo");
4657 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4658 "bar");
4660 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4661 /* Bogus column info for 2nd fixit, so no fixits. */
4662 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4663 else
4664 /* They should not have been merged. */
4665 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4668 /* Replace + consecutive replace. */
4670 rich_location richloc (line_table, caret);
4671 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4672 "foo");
4673 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4674 "bar");
4676 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4677 /* Bogus column info for 2nd fixit, so no fixits. */
4678 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4679 else
4681 /* They should have been merged into a single "replace". */
4682 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4683 const fixit_hint *hint = richloc.get_fixit_hint (0);
4684 ASSERT_STREQ ("foobar", hint->get_string ());
4685 ASSERT_EQ (c10, hint->get_start_loc ());
4686 ASSERT_EQ (c21, hint->get_next_loc ());
4690 /* Replace + consecutive removal. */
4692 rich_location richloc (line_table, caret);
4693 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4694 "foo");
4695 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4697 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4698 /* Bogus column info for 2nd fixit, so no fixits. */
4699 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4700 else
4702 /* They should have been merged into a single replace, with the
4703 range extended to cover that of the removal. */
4704 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4705 const fixit_hint *hint = richloc.get_fixit_hint (0);
4706 ASSERT_STREQ ("foo", hint->get_string ());
4707 ASSERT_EQ (c10, hint->get_start_loc ());
4708 ASSERT_EQ (c21, hint->get_next_loc ());
4712 /* Consecutive removals. */
4714 rich_location richloc (line_table, caret);
4715 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4716 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4718 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4719 /* Bogus column info for 2nd fixit, so no fixits. */
4720 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4721 else
4723 /* They should have been merged into a single "replace-with-empty". */
4724 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4725 const fixit_hint *hint = richloc.get_fixit_hint (0);
4726 ASSERT_STREQ ("", hint->get_string ());
4727 ASSERT_EQ (c10, hint->get_start_loc ());
4728 ASSERT_EQ (c21, hint->get_next_loc ());
4733 /* Verify that the line_corrections machinery correctly prints
4734 overlapping fixit-hints. */
4736 static void
4737 test_overlapped_fixit_printing (const line_table_case &case_)
4739 /* Create a tempfile and write some text to it.
4740 ...000000000111111111122222222223333333333.
4741 ...123456789012345678901234567890123456789. */
4742 const char *content
4743 = (" foo *f = (foo *)ptr->field;\n");
4744 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4745 line_table_test ltt (case_);
4747 const line_map_ordinary *ord_map
4748 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4749 tmp.get_filename (), 0));
4751 linemap_line_start (line_table, 1, 100);
4753 const location_t final_line_end
4754 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4756 /* Don't attempt to run the tests if column data might be unavailable. */
4757 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4758 return;
4760 /* A test for converting a C-style cast to a C++-style cast. */
4761 const location_t open_paren
4762 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4763 const location_t close_paren
4764 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4765 const location_t expr_start
4766 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4767 const location_t expr_finish
4768 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4769 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4771 /* Various examples of fix-it hints that aren't themselves consolidated,
4772 but for which the *printing* may need consolidation. */
4774 /* Example where 3 fix-it hints are printed as one. */
4776 test_diagnostic_context dc;
4777 rich_location richloc (line_table, expr);
4778 richloc.add_fixit_replace (open_paren, "const_cast<");
4779 richloc.add_fixit_replace (close_paren, "> (");
4780 richloc.add_fixit_insert_after (")");
4782 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4783 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4784 " ^~~~~~~~~~\n"
4785 " -----------------\n"
4786 " const_cast<foo *> (ptr->field)\n",
4787 pp_formatted_text (dc.printer));
4789 /* Unit-test the line_corrections machinery. */
4790 char_display_policy policy (make_policy (dc, richloc));
4791 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4792 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4793 ASSERT_EQ (column_range (12, 12),
4794 get_affected_range (policy, hint_0, CU_BYTES));
4795 ASSERT_EQ (column_range (12, 12),
4796 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4797 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4798 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4799 ASSERT_EQ (column_range (18, 18),
4800 get_affected_range (policy, hint_1, CU_BYTES));
4801 ASSERT_EQ (column_range (18, 18),
4802 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4803 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4804 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4805 ASSERT_EQ (column_range (29, 28),
4806 get_affected_range (policy, hint_2, CU_BYTES));
4807 ASSERT_EQ (column_range (29, 28),
4808 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4809 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2));
4811 /* Add each hint in turn to a line_corrections instance,
4812 and verify that they are consolidated into one correction instance
4813 as expected. */
4814 line_corrections lc (policy, tmp.get_filename (), 1);
4816 /* The first replace hint by itself. */
4817 lc.add_hint (hint_0);
4818 ASSERT_EQ (1, lc.m_corrections.length ());
4819 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4820 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4821 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4822 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4824 /* After the second replacement hint, they are printed together
4825 as a replacement (along with the text between them). */
4826 lc.add_hint (hint_1);
4827 ASSERT_EQ (1, lc.m_corrections.length ());
4828 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4829 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4830 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4831 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4833 /* After the final insertion hint, they are all printed together
4834 as a replacement (along with the text between them). */
4835 lc.add_hint (hint_2);
4836 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4837 lc.m_corrections[0]->m_text);
4838 ASSERT_EQ (1, lc.m_corrections.length ());
4839 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4840 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4841 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4844 /* Example where two are consolidated during printing. */
4846 test_diagnostic_context dc;
4847 rich_location richloc (line_table, expr);
4848 richloc.add_fixit_replace (open_paren, "CAST (");
4849 richloc.add_fixit_replace (close_paren, ") (");
4850 richloc.add_fixit_insert_after (")");
4852 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4853 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4854 " ^~~~~~~~~~\n"
4855 " -\n"
4856 " CAST (-\n"
4857 " ) ( )\n",
4858 pp_formatted_text (dc.printer));
4861 /* Example where none are consolidated during printing. */
4863 test_diagnostic_context dc;
4864 rich_location richloc (line_table, expr);
4865 richloc.add_fixit_replace (open_paren, "CST (");
4866 richloc.add_fixit_replace (close_paren, ") (");
4867 richloc.add_fixit_insert_after (")");
4869 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4870 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4871 " ^~~~~~~~~~\n"
4872 " -\n"
4873 " CST ( -\n"
4874 " ) ( )\n",
4875 pp_formatted_text (dc.printer));
4878 /* Example of deletion fix-it hints. */
4880 test_diagnostic_context dc;
4881 rich_location richloc (line_table, expr);
4882 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4883 source_range victim = {open_paren, close_paren};
4884 richloc.add_fixit_remove (victim);
4886 /* This case is actually handled by fixit-consolidation,
4887 rather than by line_corrections. */
4888 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4890 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4891 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4892 " ^~~~~~~~~~\n"
4893 " -------\n"
4894 " (bar *)\n",
4895 pp_formatted_text (dc.printer));
4898 /* Example of deletion fix-it hints that would overlap. */
4900 test_diagnostic_context dc;
4901 rich_location richloc (line_table, expr);
4902 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4903 source_range victim = {expr_start, expr_finish};
4904 richloc.add_fixit_remove (victim);
4906 /* These fixits are not consolidated. */
4907 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4909 /* But the corrections are. */
4910 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4911 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4912 " ^~~~~~~~~~\n"
4913 " -----------------\n"
4914 " (longer *)(foo *)\n",
4915 pp_formatted_text (dc.printer));
4918 /* Example of insertion fix-it hints that would overlap. */
4920 test_diagnostic_context dc;
4921 rich_location richloc (line_table, expr);
4922 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4923 richloc.add_fixit_insert_after (close_paren, "TEST");
4925 /* The first insertion is long enough that if printed naively,
4926 it would overlap with the second.
4927 Verify that they are printed as a single replacement. */
4928 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4929 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4930 " ^~~~~~~~~~\n"
4931 " -------\n"
4932 " LONGER THAN THE CAST(foo *)TEST\n",
4933 pp_formatted_text (dc.printer));
4937 /* Multibyte-aware version of preceding tests. See comments above
4938 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4939 characters here. */
4941 static void
4942 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4944 /* Create a tempfile and write some text to it. */
4946 const char *content
4947 /* Display columns.
4948 00000000000000000000000111111111111111111111111222222222222222223
4949 12344444444555555556789012344444444555555556789012345678999999990 */
4950 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4951 /* 00000000000000000000011111111111111111111112222222222333333333333
4952 12344445555666677778901234566667777888899990123456789012333344445
4953 Byte columns. */
4955 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4956 line_table_test ltt (case_);
4958 const line_map_ordinary *ord_map
4959 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4960 tmp.get_filename (), 0));
4962 linemap_line_start (line_table, 1, 100);
4964 const location_t final_line_end
4965 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4967 /* Don't attempt to run the tests if column data might be unavailable. */
4968 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4969 return;
4971 /* A test for converting a C-style cast to a C++-style cast. */
4972 const location_t open_paren
4973 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4974 const location_t close_paren
4975 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4976 const location_t expr_start
4977 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4978 const location_t expr_finish
4979 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4980 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4982 /* Various examples of fix-it hints that aren't themselves consolidated,
4983 but for which the *printing* may need consolidation. */
4985 /* Example where 3 fix-it hints are printed as one. */
4987 test_diagnostic_context dc;
4988 rich_location richloc (line_table, expr);
4989 richloc.add_fixit_replace (open_paren, "const_cast<");
4990 richloc.add_fixit_replace (close_paren, "> (");
4991 richloc.add_fixit_insert_after (")");
4993 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4994 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4995 " *f = (f\xf0\x9f\x98\x82"
4996 " *)ptr->field\xcf\x80"
4997 ";\n"
4998 " ^~~~~~~~~~~\n"
4999 " ------------------\n"
5000 " const_cast<f\xf0\x9f\x98\x82"
5001 " *> (ptr->field\xcf\x80"
5002 ")\n",
5003 pp_formatted_text (dc.printer));
5005 /* Unit-test the line_corrections machinery. */
5006 char_display_policy policy (make_policy (dc, richloc));
5007 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5008 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5009 ASSERT_EQ (column_range (14, 14),
5010 get_affected_range (policy, hint_0, CU_BYTES));
5011 ASSERT_EQ (column_range (12, 12),
5012 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
5013 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
5014 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5015 ASSERT_EQ (column_range (22, 22),
5016 get_affected_range (policy, hint_1, CU_BYTES));
5017 ASSERT_EQ (column_range (18, 18),
5018 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
5019 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
5020 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5021 ASSERT_EQ (column_range (35, 34),
5022 get_affected_range (policy, hint_2, CU_BYTES));
5023 ASSERT_EQ (column_range (30, 29),
5024 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
5025 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2));
5027 /* Add each hint in turn to a line_corrections instance,
5028 and verify that they are consolidated into one correction instance
5029 as expected. */
5030 line_corrections lc (policy, tmp.get_filename (), 1);
5032 /* The first replace hint by itself. */
5033 lc.add_hint (hint_0);
5034 ASSERT_EQ (1, lc.m_corrections.length ());
5035 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5036 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5037 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5038 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5040 /* After the second replacement hint, they are printed together
5041 as a replacement (along with the text between them). */
5042 lc.add_hint (hint_1);
5043 ASSERT_EQ (1, lc.m_corrections.length ());
5044 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5045 lc.m_corrections[0]->m_text);
5046 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5047 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5048 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5050 /* After the final insertion hint, they are all printed together
5051 as a replacement (along with the text between them). */
5052 lc.add_hint (hint_2);
5053 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5054 lc.m_corrections[0]->m_text);
5055 ASSERT_EQ (1, lc.m_corrections.length ());
5056 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5057 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5058 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5061 /* Example where two are consolidated during printing. */
5063 test_diagnostic_context dc;
5064 rich_location richloc (line_table, expr);
5065 richloc.add_fixit_replace (open_paren, "CAST (");
5066 richloc.add_fixit_replace (close_paren, ") (");
5067 richloc.add_fixit_insert_after (")");
5069 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5070 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5071 " *f = (f\xf0\x9f\x98\x82"
5072 " *)ptr->field\xcf\x80"
5073 ";\n"
5074 " ^~~~~~~~~~~\n"
5075 " -\n"
5076 " CAST (-\n"
5077 " ) ( )\n",
5078 pp_formatted_text (dc.printer));
5081 /* Example where none are consolidated during printing. */
5083 test_diagnostic_context dc;
5084 rich_location richloc (line_table, expr);
5085 richloc.add_fixit_replace (open_paren, "CST (");
5086 richloc.add_fixit_replace (close_paren, ") (");
5087 richloc.add_fixit_insert_after (")");
5089 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5090 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5091 " *f = (f\xf0\x9f\x98\x82"
5092 " *)ptr->field\xcf\x80"
5093 ";\n"
5094 " ^~~~~~~~~~~\n"
5095 " -\n"
5096 " CST ( -\n"
5097 " ) ( )\n",
5098 pp_formatted_text (dc.printer));
5101 /* Example of deletion fix-it hints. */
5103 test_diagnostic_context dc;
5104 rich_location richloc (line_table, expr);
5105 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5106 source_range victim = {open_paren, close_paren};
5107 richloc.add_fixit_remove (victim);
5109 /* This case is actually handled by fixit-consolidation,
5110 rather than by line_corrections. */
5111 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5113 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5114 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5115 " *f = (f\xf0\x9f\x98\x82"
5116 " *)ptr->field\xcf\x80"
5117 ";\n"
5118 " ^~~~~~~~~~~\n"
5119 " -------\n"
5120 " (bar\xf0\x9f\x98\x82"
5121 " *)\n",
5122 pp_formatted_text (dc.printer));
5125 /* Example of deletion fix-it hints that would overlap. */
5127 test_diagnostic_context dc;
5128 rich_location richloc (line_table, expr);
5129 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5130 source_range victim = {expr_start, expr_finish};
5131 richloc.add_fixit_remove (victim);
5133 /* These fixits are not consolidated. */
5134 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5136 /* But the corrections are. */
5137 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5138 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5139 " *f = (f\xf0\x9f\x98\x82"
5140 " *)ptr->field\xcf\x80"
5141 ";\n"
5142 " ^~~~~~~~~~~\n"
5143 " ------------------\n"
5144 " (long\xf0\x9f\x98\x82"
5145 " *)(f\xf0\x9f\x98\x82"
5146 " *)\n",
5147 pp_formatted_text (dc.printer));
5150 /* Example of insertion fix-it hints that would overlap. */
5152 test_diagnostic_context dc;
5153 rich_location richloc (line_table, expr);
5154 richloc.add_fixit_insert_before
5155 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5156 richloc.add_fixit_insert_after (close_paren, "TEST");
5158 /* The first insertion is long enough that if printed naively,
5159 it would overlap with the second.
5160 Verify that they are printed as a single replacement. */
5161 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5162 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5163 " *f = (f\xf0\x9f\x98\x82"
5164 " *)ptr->field\xcf\x80"
5165 ";\n"
5166 " ^~~~~~~~~~~\n"
5167 " -------\n"
5168 " L\xf0\x9f\x98\x82"
5169 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5170 " *)TEST\n",
5171 pp_formatted_text (dc.printer));
5175 /* Verify that the line_corrections machinery correctly prints
5176 overlapping fixit-hints that have been added in the wrong
5177 order.
5178 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5180 static void
5181 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5183 /* Create a tempfile and write some text to it.
5184 ...000000000111111111122222222223333333333.
5185 ...123456789012345678901234567890123456789. */
5186 const char *content
5187 = ("int a5[][0][0] = { 1, 2 };\n");
5188 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5189 line_table_test ltt (case_);
5191 const line_map_ordinary *ord_map
5192 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5193 tmp.get_filename (), 0));
5195 linemap_line_start (line_table, 1, 100);
5197 const location_t final_line_end
5198 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5200 /* Don't attempt to run the tests if column data might be unavailable. */
5201 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5202 return;
5204 const location_t col_1
5205 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5206 const location_t col_20
5207 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5208 const location_t col_21
5209 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5210 const location_t col_23
5211 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5212 const location_t col_25
5213 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5215 /* Two insertions, in the wrong order. */
5217 test_diagnostic_context dc;
5219 rich_location richloc (line_table, col_20);
5220 richloc.add_fixit_insert_before (col_23, "{");
5221 richloc.add_fixit_insert_before (col_21, "}");
5223 /* These fixits should be accepted; they can't be consolidated. */
5224 char_display_policy policy (make_policy (dc, richloc));
5225 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5226 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5227 ASSERT_EQ (column_range (23, 22),
5228 get_affected_range (policy, hint_0, CU_BYTES));
5229 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0));
5230 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5231 ASSERT_EQ (column_range (21, 20),
5232 get_affected_range (policy, hint_1, CU_BYTES));
5233 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1));
5235 /* Verify that they're printed correctly. */
5236 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5237 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5238 " ^\n"
5239 " } {\n",
5240 pp_formatted_text (dc.printer));
5243 /* Various overlapping insertions, some occurring "out of order"
5244 (reproducing the fix-it hints from PR c/81405). */
5246 test_diagnostic_context dc;
5247 rich_location richloc (line_table, col_20);
5249 richloc.add_fixit_insert_before (col_20, "{{");
5250 richloc.add_fixit_insert_before (col_21, "}}");
5251 richloc.add_fixit_insert_before (col_23, "{");
5252 richloc.add_fixit_insert_before (col_21, "}");
5253 richloc.add_fixit_insert_before (col_23, "{{");
5254 richloc.add_fixit_insert_before (col_25, "}");
5255 richloc.add_fixit_insert_before (col_21, "}");
5256 richloc.add_fixit_insert_before (col_1, "{");
5257 richloc.add_fixit_insert_before (col_25, "}");
5258 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5259 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5260 " ^\n"
5261 " { -----\n"
5262 " {{1}}}}, {{{2 }}\n",
5263 pp_formatted_text (dc.printer));
5267 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5269 static void
5270 test_fixit_insert_containing_newline (const line_table_case &case_)
5272 /* Create a tempfile and write some text to it.
5273 .........................0000000001111111.
5274 .........................1234567890123456. */
5275 const char *old_content = (" case 'a':\n" /* line 1. */
5276 " x = a;\n" /* line 2. */
5277 " case 'b':\n" /* line 3. */
5278 " x = b;\n");/* line 4. */
5280 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5281 line_table_test ltt (case_);
5282 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5284 location_t case_start = linemap_position_for_column (line_table, 5);
5285 location_t case_finish = linemap_position_for_column (line_table, 13);
5286 location_t case_loc = make_location (case_start, case_start, case_finish);
5287 location_t line_start = linemap_position_for_column (line_table, 1);
5289 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5290 return;
5292 /* Add a "break;" on a line by itself before line 3 i.e. before
5293 column 1 of line 3. */
5295 rich_location richloc (line_table, case_loc);
5296 richloc.add_fixit_insert_before (line_start, " break;\n");
5298 /* Without line numbers. */
5300 test_diagnostic_context dc;
5301 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5302 ASSERT_STREQ (" x = a;\n"
5303 "+ break;\n"
5304 " case 'b':\n"
5305 " ^~~~~~~~~\n",
5306 pp_formatted_text (dc.printer));
5309 /* With line numbers. */
5311 test_diagnostic_context dc;
5312 dc.m_source_printing.show_line_numbers_p = true;
5313 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5314 ASSERT_STREQ (" 2 | x = a;\n"
5315 " +++ |+ break;\n"
5316 " 3 | case 'b':\n"
5317 " | ^~~~~~~~~\n",
5318 pp_formatted_text (dc.printer));
5322 /* Verify that attempts to add text with a newline fail when the
5323 insertion point is *not* at the start of a line. */
5325 rich_location richloc (line_table, case_loc);
5326 richloc.add_fixit_insert_before (case_start, "break;\n");
5327 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5328 test_diagnostic_context dc;
5329 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5330 ASSERT_STREQ (" case 'b':\n"
5331 " ^~~~~~~~~\n",
5332 pp_formatted_text (dc.printer));
5336 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5337 of the file, where the fix-it is printed in a different line-span
5338 to the primary range of the diagnostic. */
5340 static void
5341 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5343 /* Create a tempfile and write some text to it.
5344 .........................0000000001111111.
5345 .........................1234567890123456. */
5346 const char *old_content = ("test (int ch)\n" /* line 1. */
5347 "{\n" /* line 2. */
5348 " putchar (ch);\n" /* line 3. */
5349 "}\n"); /* line 4. */
5351 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5352 line_table_test ltt (case_);
5354 const line_map_ordinary *ord_map = linemap_check_ordinary
5355 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5356 linemap_line_start (line_table, 1, 100);
5358 /* The primary range is the "putchar" token. */
5359 location_t putchar_start
5360 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5361 location_t putchar_finish
5362 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5363 location_t putchar_loc
5364 = make_location (putchar_start, putchar_start, putchar_finish);
5365 rich_location richloc (line_table, putchar_loc);
5367 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5368 location_t file_start
5369 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5370 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5372 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5373 return;
5376 test_diagnostic_context dc;
5377 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5378 ASSERT_STREQ ("FILENAME:1:1:\n"
5379 "+#include <stdio.h>\n"
5380 " test (int ch)\n"
5381 "FILENAME:3:2:\n"
5382 " putchar (ch);\n"
5383 " ^~~~~~~\n",
5384 pp_formatted_text (dc.printer));
5387 /* With line-numbering, the line spans are close enough to be
5388 consolidated, since it makes little sense to skip line 2. */
5390 test_diagnostic_context dc;
5391 dc.m_source_printing.show_line_numbers_p = true;
5392 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5393 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5394 " 1 | test (int ch)\n"
5395 " 2 | {\n"
5396 " 3 | putchar (ch);\n"
5397 " | ^~~~~~~\n",
5398 pp_formatted_text (dc.printer));
5402 /* Replacement fix-it hint containing a newline.
5403 This will fail, as newlines are only supported when inserting at the
5404 beginning of a line. */
5406 static void
5407 test_fixit_replace_containing_newline (const line_table_case &case_)
5409 /* Create a tempfile and write some text to it.
5410 .........................0000000001111.
5411 .........................1234567890123. */
5412 const char *old_content = "foo = bar ();\n";
5414 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5415 line_table_test ltt (case_);
5416 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5418 /* Replace the " = " with "\n = ", as if we were reformatting an
5419 overly long line. */
5420 location_t start = linemap_position_for_column (line_table, 4);
5421 location_t finish = linemap_position_for_column (line_table, 6);
5422 location_t loc = linemap_position_for_column (line_table, 13);
5423 rich_location richloc (line_table, loc);
5424 source_range range = source_range::from_locations (start, finish);
5425 richloc.add_fixit_replace (range, "\n =");
5427 /* Arbitrary newlines are not yet supported within fix-it hints, so
5428 the fix-it should not be displayed. */
5429 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5431 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5432 return;
5434 test_diagnostic_context dc;
5435 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5436 ASSERT_STREQ (" foo = bar ();\n"
5437 " ^\n",
5438 pp_formatted_text (dc.printer));
5441 /* Fix-it hint, attempting to delete a newline.
5442 This will fail, as we currently only support fix-it hints that
5443 affect one line at a time. */
5445 static void
5446 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5448 /* Create a tempfile and write some text to it.
5449 ..........................0000000001111.
5450 ..........................1234567890123. */
5451 const char *old_content = ("foo = bar (\n"
5452 " );\n");
5454 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5455 line_table_test ltt (case_);
5456 const line_map_ordinary *ord_map = linemap_check_ordinary
5457 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5458 linemap_line_start (line_table, 1, 100);
5460 /* Attempt to delete the " (\n...)". */
5461 location_t start
5462 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5463 location_t caret
5464 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5465 location_t finish
5466 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5467 location_t loc = make_location (caret, start, finish);
5468 rich_location richloc (line_table, loc);
5469 richloc. add_fixit_remove ();
5471 /* Fix-it hints that affect more than one line are not yet supported, so
5472 the fix-it should not be displayed. */
5473 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5475 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5476 return;
5478 test_diagnostic_context dc;
5479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5480 ASSERT_STREQ (" foo = bar (\n"
5481 " ~^\n"
5482 " );\n"
5483 " ~ \n",
5484 pp_formatted_text (dc.printer));
5487 static void
5488 test_tab_expansion (const line_table_case &case_)
5490 /* Create a tempfile and write some text to it. This example uses a tabstop
5491 of 8, as the column numbers attempt to indicate:
5493 .....................000.01111111111.22222333333 display
5494 .....................123.90123456789.56789012345 columns */
5495 const char *content = " \t This: `\t' is a tab.\n";
5496 /* ....................000 00000011111 11111222222 byte
5497 ....................123 45678901234 56789012345 columns */
5499 const int tabstop = 8;
5500 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5501 const int first_non_ws_byte_col = 7;
5502 const int right_quote_byte_col = 15;
5503 const int last_byte_col = 25;
5504 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5506 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5507 line_table_test ltt (case_);
5508 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5510 /* Don't attempt to run the tests if column data might be unavailable. */
5511 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5512 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5513 return;
5515 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5516 into 11 spaces. Recall that print_line() also puts one space before
5517 everything too. */
5519 test_diagnostic_context dc;
5520 dc.tabstop = tabstop;
5521 rich_location richloc (line_table,
5522 linemap_position_for_column (line_table,
5523 first_non_ws_byte_col));
5524 layout test_layout (&dc, &richloc, DK_ERROR);
5525 test_layout.print_line (1);
5526 ASSERT_STREQ (" This: ` ' is a tab.\n"
5527 " ^\n",
5528 pp_formatted_text (dc.printer));
5531 /* Confirm the display width was tracked correctly across the internal tab
5532 as well. */
5534 test_diagnostic_context dc;
5535 dc.tabstop = tabstop;
5536 rich_location richloc (line_table,
5537 linemap_position_for_column (line_table,
5538 right_quote_byte_col));
5539 layout test_layout (&dc, &richloc, DK_ERROR);
5540 test_layout.print_line (1);
5541 ASSERT_STREQ (" This: ` ' is a tab.\n"
5542 " ^\n",
5543 pp_formatted_text (dc.printer));
5547 /* Verify that the escaping machinery can cope with a variety of different
5548 invalid bytes. */
5550 static void
5551 test_escaping_bytes_1 (const line_table_case &case_)
5553 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5554 const size_t sz = sizeof (content);
5555 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5556 line_table_test ltt (case_);
5557 const line_map_ordinary *ord_map = linemap_check_ordinary
5558 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5559 linemap_line_start (line_table, 1, 100);
5561 location_t finish
5562 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5563 strlen (content));
5565 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5566 return;
5568 /* Locations of the NUL and \v bytes. */
5569 location_t nul_loc
5570 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5571 location_t v_loc
5572 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5573 gcc_rich_location richloc (nul_loc);
5574 richloc.add_range (v_loc);
5577 test_diagnostic_context dc;
5578 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5579 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5580 " ^ ~\n",
5581 pp_formatted_text (dc.printer));
5583 richloc.set_escape_on_output (true);
5585 test_diagnostic_context dc;
5586 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5587 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5588 ASSERT_STREQ
5589 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5590 " ^~~~~~~~ ~~~~~~~~\n",
5591 pp_formatted_text (dc.printer));
5594 test_diagnostic_context dc;
5595 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5596 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5597 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5598 " ^~~~ ~~~~\n",
5599 pp_formatted_text (dc.printer));
5603 /* As above, but verify that we handle the initial byte of a line
5604 correctly. */
5606 static void
5607 test_escaping_bytes_2 (const line_table_case &case_)
5609 const char content[] = "\0after\n";
5610 const size_t sz = sizeof (content);
5611 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5612 line_table_test ltt (case_);
5613 const line_map_ordinary *ord_map = linemap_check_ordinary
5614 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5615 linemap_line_start (line_table, 1, 100);
5617 location_t finish
5618 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5619 strlen (content));
5621 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5622 return;
5624 /* Location of the NUL byte. */
5625 location_t nul_loc
5626 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5627 gcc_rich_location richloc (nul_loc);
5630 test_diagnostic_context dc;
5631 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5632 ASSERT_STREQ (" after\n"
5633 " ^\n",
5634 pp_formatted_text (dc.printer));
5636 richloc.set_escape_on_output (true);
5638 test_diagnostic_context dc;
5639 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5640 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5641 ASSERT_STREQ (" <U+0000>after\n"
5642 " ^~~~~~~~\n",
5643 pp_formatted_text (dc.printer));
5646 test_diagnostic_context dc;
5647 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5648 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5649 ASSERT_STREQ (" <00>after\n"
5650 " ^~~~\n",
5651 pp_formatted_text (dc.printer));
5655 /* Verify that line numbers are correctly printed for the case of
5656 a multiline range in which the width of the line numbers changes
5657 (e.g. from "9" to "10"). */
5659 static void
5660 test_line_numbers_multiline_range ()
5662 /* Create a tempfile and write some text to it. */
5663 pretty_printer pp;
5664 for (int i = 0; i < 20; i++)
5665 /* .........0000000001111111.
5666 .............1234567890123456. */
5667 pp_printf (&pp, "this is line %i\n", i + 1);
5668 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5669 line_table_test ltt;
5671 const line_map_ordinary *ord_map = linemap_check_ordinary
5672 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5673 linemap_line_start (line_table, 1, 100);
5675 /* Create a multi-line location, starting at the "line" of line 9, with
5676 a caret on the "is" of line 10, finishing on the "this" line 11. */
5678 location_t start
5679 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5680 location_t caret
5681 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5682 location_t finish
5683 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5684 location_t loc = make_location (caret, start, finish);
5686 test_diagnostic_context dc;
5687 dc.m_source_printing.show_line_numbers_p = true;
5688 dc.m_source_printing.min_margin_width = 0;
5689 gcc_rich_location richloc (loc);
5690 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5691 ASSERT_STREQ (" 9 | this is line 9\n"
5692 " | ~~~~~~\n"
5693 "10 | this is line 10\n"
5694 " | ~~~~~^~~~~~~~~~\n"
5695 "11 | this is line 11\n"
5696 " | ~~~~ \n",
5697 pp_formatted_text (dc.printer));
5700 /* Run all of the selftests within this file. */
5702 void
5703 diagnostic_show_locus_cc_tests ()
5705 test_line_span ();
5707 test_layout_range_for_single_point ();
5708 test_layout_range_for_single_line ();
5709 test_layout_range_for_multiple_lines ();
5711 test_display_widths ();
5713 for_each_line_table_case (test_layout_x_offset_display_utf8);
5714 for_each_line_table_case (test_layout_x_offset_display_tab);
5716 test_get_line_bytes_without_trailing_whitespace ();
5718 test_diagnostic_show_locus_unknown_location ();
5720 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5721 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5722 for_each_line_table_case (test_add_location_if_nearby);
5723 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5724 for_each_line_table_case (test_fixit_consolidation);
5725 for_each_line_table_case (test_overlapped_fixit_printing);
5726 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5727 for_each_line_table_case (test_overlapped_fixit_printing_2);
5728 for_each_line_table_case (test_fixit_insert_containing_newline);
5729 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5730 for_each_line_table_case (test_fixit_replace_containing_newline);
5731 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5732 for_each_line_table_case (test_tab_expansion);
5733 for_each_line_table_case (test_escaping_bytes_1);
5734 for_each_line_table_case (test_escaping_bytes_2);
5736 test_line_numbers_multiline_range ();
5739 } // namespace selftest
5741 #endif /* #if CHECKING_P */
5743 #if __GNUC__ >= 10
5744 # pragma GCC diagnostic pop
5745 #endif