RISC-V: Make stack_save_restore tests more robust
[official-gcc.git] / gcc / diagnostic-show-locus.cc
blob0514815b51f9271ac1b68b1142d501aa1ea40479
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);
372 bool maybe_add_location_range (const location_range *loc_range,
373 unsigned original_idx,
374 bool restrict_to_current_line_spans);
376 int get_num_line_spans () const { return m_line_spans.length (); }
377 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
379 int get_linenum_width () const { return m_linenum_width; }
380 int get_x_offset_display () const { return m_x_offset_display; }
382 void print_gap_in_line_numbering ();
383 bool print_heading_for_line_span_index_p (int line_span_idx) const;
385 expanded_location get_expanded_location (const line_span *) const;
387 void print_line (linenum_type row);
389 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
391 private:
392 bool will_show_line_p (linenum_type row) const;
393 void print_leading_fixits (linenum_type row);
394 line_bounds print_source_line (linenum_type row, const char *line,
395 int line_bytes);
396 bool should_print_annotation_line_p (linenum_type row) const;
397 void start_annotation_line (char margin_char = ' ') const;
398 void print_annotation_line (linenum_type row, const line_bounds lbounds);
399 void print_any_labels (linenum_type row);
400 void print_trailing_fixits (linenum_type row);
402 bool annotation_line_showed_range_p (linenum_type line, int start_column,
403 int finish_column) const;
404 void show_ruler (int max_column) const;
406 bool validate_fixit_hint_p (const fixit_hint *hint);
408 void calculate_line_spans ();
409 void calculate_linenum_width ();
410 void calculate_x_offset_display ();
412 void print_newline ();
414 bool
415 get_state_at_point (/* Inputs. */
416 linenum_type row, int column,
417 int first_non_ws, int last_non_ws,
418 enum column_unit col_unit,
419 /* Outputs. */
420 point_state *out_state);
423 get_x_bound_for_row (linenum_type row, int caret_column,
424 int last_non_ws);
426 void
427 move_to_column (int *column, int dest_column, bool add_left_margin);
429 private:
430 diagnostic_context *m_context;
431 pretty_printer *m_pp;
432 char_display_policy m_policy;
433 location_t m_primary_loc;
434 exploc_with_display_col m_exploc;
435 colorizer m_colorizer;
436 bool m_colorize_source_p;
437 bool m_show_labels_p;
438 bool m_show_line_numbers_p;
439 bool m_diagnostic_path_p;
440 auto_vec <layout_range> m_layout_ranges;
441 auto_vec <const fixit_hint *> m_fixit_hints;
442 auto_vec <line_span> m_line_spans;
443 int m_linenum_width;
444 int m_x_offset_display;
445 bool m_escape_on_output;
448 /* Implementation of "class colorizer". */
450 /* The constructor for "colorizer". Lookup and store color codes for the
451 different kinds of things we might need to print. */
453 colorizer::colorizer (diagnostic_context *context,
454 diagnostic_t diagnostic_kind) :
455 m_context (context),
456 m_diagnostic_kind (diagnostic_kind),
457 m_current_state (STATE_NORMAL_TEXT)
459 m_range1 = get_color_by_name ("range1");
460 m_range2 = get_color_by_name ("range2");
461 m_fixit_insert = get_color_by_name ("fixit-insert");
462 m_fixit_delete = get_color_by_name ("fixit-delete");
463 m_stop_color = colorize_stop (pp_show_color (context->printer));
466 /* The destructor for "colorize". If colorization is on, print a code to
467 turn it off. */
469 colorizer::~colorizer ()
471 finish_state (m_current_state);
474 /* Update state, printing color codes if necessary if there's a state
475 change. */
477 void
478 colorizer::set_state (int new_state)
480 if (m_current_state != new_state)
482 finish_state (m_current_state);
483 m_current_state = new_state;
484 begin_state (new_state);
488 /* Turn on any colorization for STATE. */
490 void
491 colorizer::begin_state (int state)
493 switch (state)
495 case STATE_NORMAL_TEXT:
496 break;
498 case STATE_FIXIT_INSERT:
499 pp_string (m_context->printer, m_fixit_insert);
500 break;
502 case STATE_FIXIT_DELETE:
503 pp_string (m_context->printer, m_fixit_delete);
504 break;
506 case 0:
507 /* Make range 0 be the same color as the "kind" text
508 (error vs warning vs note). */
509 pp_string
510 (m_context->printer,
511 colorize_start (pp_show_color (m_context->printer),
512 diagnostic_get_color_for_kind (m_diagnostic_kind)));
513 break;
515 case 1:
516 pp_string (m_context->printer, m_range1);
517 break;
519 case 2:
520 pp_string (m_context->printer, m_range2);
521 break;
523 default:
524 /* For ranges beyond 2, alternate between color 1 and color 2. */
526 gcc_assert (state > 2);
527 pp_string (m_context->printer,
528 state % 2 ? m_range1 : m_range2);
530 break;
534 /* Turn off any colorization for STATE. */
536 void
537 colorizer::finish_state (int state)
539 if (state != STATE_NORMAL_TEXT)
540 pp_string (m_context->printer, m_stop_color);
543 /* Get the color code for NAME (or the empty string if
544 colorization is disabled). */
546 const char *
547 colorizer::get_color_by_name (const char *name)
549 return colorize_start (pp_show_color (m_context->printer), name);
552 /* Implementation of class layout_range. */
554 /* The constructor for class layout_range.
555 Initialize various layout_point fields from expanded_location
556 equivalents; we've already filtered on file. */
558 layout_range::layout_range (const exploc_with_display_col &start_exploc,
559 const exploc_with_display_col &finish_exploc,
560 enum range_display_kind range_display_kind,
561 const exploc_with_display_col &caret_exploc,
562 unsigned original_idx,
563 const range_label *label)
564 : m_start (start_exploc),
565 m_finish (finish_exploc),
566 m_range_display_kind (range_display_kind),
567 m_caret (caret_exploc),
568 m_original_idx (original_idx),
569 m_label (label)
573 /* Is (column, row) within the given range?
574 We've already filtered on the file.
576 Ranges are closed (both limits are within the range).
578 Example A: a single-line range:
579 start: (col=22, line=2)
580 finish: (col=38, line=2)
582 |00000011111111112222222222333333333344444444444
583 |34567890123456789012345678901234567890123456789
584 --+-----------------------------------------------
585 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
586 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
587 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
589 Example B: a multiline range with
590 start: (col=14, line=3)
591 finish: (col=08, line=5)
593 |00000011111111112222222222333333333344444444444
594 |34567890123456789012345678901234567890123456789
595 --+-----------------------------------------------
596 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
597 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
598 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
599 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
600 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
601 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
602 --+-----------------------------------------------
604 Legend:
605 - 'b' indicates a point *before* the range
606 - 'S' indicates the start of the range
607 - 'w' indicates a point within the range
608 - 'F' indicates the finish of the range (which is
609 within it).
610 - 'a' indicates a subsequent point *after* the range.
612 COL_UNIT controls whether we check the byte column or
613 the display column; one or the other is more convenient
614 depending on the context. */
616 bool
617 layout_range::contains_point (linenum_type row, int column,
618 enum column_unit col_unit) const
620 gcc_assert (m_start.m_line <= m_finish.m_line);
621 /* ...but the equivalent isn't true for the columns;
622 consider example B in the comment above. */
624 if (row < m_start.m_line)
625 /* Points before the first line of the range are
626 outside it (corresponding to line 01 in example A
627 and lines 01 and 02 in example B above). */
628 return false;
630 if (row == m_start.m_line)
631 /* On same line as start of range (corresponding
632 to line 02 in example A and line 03 in example B). */
634 if (column < m_start.m_columns[col_unit])
635 /* Points on the starting line of the range, but
636 before the column in which it begins. */
637 return false;
639 if (row < m_finish.m_line)
640 /* This is a multiline range; the point
641 is within it (corresponds to line 03 in example B
642 from column 14 onwards) */
643 return true;
644 else
646 /* This is a single-line range. */
647 gcc_assert (row == m_finish.m_line);
648 return column <= m_finish.m_columns[col_unit];
652 /* The point is in a line beyond that containing the
653 start of the range: lines 03 onwards in example A,
654 and lines 04 onwards in example B. */
655 gcc_assert (row > m_start.m_line);
657 if (row > m_finish.m_line)
658 /* The point is beyond the final line of the range
659 (lines 03 onwards in example A, and lines 06 onwards
660 in example B). */
661 return false;
663 if (row < m_finish.m_line)
665 /* The point is in a line that's fully within a multiline
666 range (e.g. line 04 in example B). */
667 gcc_assert (m_start.m_line < m_finish.m_line);
668 return true;
671 gcc_assert (row == m_finish.m_line);
673 return column <= m_finish.m_columns[col_unit];
676 /* Does this layout_range contain any part of line ROW? */
678 bool
679 layout_range::intersects_line_p (linenum_type row) const
681 gcc_assert (m_start.m_line <= m_finish.m_line);
682 if (row < m_start.m_line)
683 return false;
684 if (row > m_finish.m_line)
685 return false;
686 return true;
689 #if CHECKING_P
691 /* Default for when we don't care what the tab expansion is set to. */
692 static const int def_tabstop = 8;
694 static cpp_char_column_policy def_policy ()
696 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
699 /* Create some expanded locations for testing layout_range. The filename
700 member of the explocs is set to the empty string. This member will only be
701 inspected by the calls to location_compute_display_column() made from the
702 layout_point constructors. That function will check for an empty filename
703 argument and not attempt to open it, rather treating the non-existent data
704 as if the display width were the same as the byte count. Tests exercising a
705 real difference between byte count and display width are performed later,
706 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
708 static layout_range
709 make_range (int start_line, int start_col, int end_line, int end_col)
711 const expanded_location start_exploc
712 = {"", start_line, start_col, NULL, false};
713 const expanded_location finish_exploc
714 = {"", end_line, end_col, NULL, false};
715 return layout_range (exploc_with_display_col (start_exploc, def_policy (),
716 LOCATION_ASPECT_START),
717 exploc_with_display_col (finish_exploc, def_policy (),
718 LOCATION_ASPECT_FINISH),
719 SHOW_RANGE_WITHOUT_CARET,
720 exploc_with_display_col (start_exploc, def_policy (),
721 LOCATION_ASPECT_CARET),
722 0, NULL);
725 /* Selftests for layout_range::contains_point and
726 layout_range::intersects_line_p. */
728 /* Selftest for layout_range, where the layout_range
729 is a range with start==end i.e. a single point. */
731 static void
732 test_layout_range_for_single_point ()
734 layout_range point = make_range (7, 10, 7, 10);
736 /* Tests for layout_range::contains_point. */
738 for (int i = 0; i != CU_NUM_UNITS; ++i)
740 const enum column_unit col_unit = (enum column_unit) i;
742 /* Before the line. */
743 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
745 /* On the line, but before start. */
746 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
748 /* At the point. */
749 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
751 /* On the line, after the point. */
752 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
754 /* After the line. */
755 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
758 /* Tests for layout_range::intersects_line_p. */
759 ASSERT_FALSE (point.intersects_line_p (6));
760 ASSERT_TRUE (point.intersects_line_p (7));
761 ASSERT_FALSE (point.intersects_line_p (8));
764 /* Selftest for layout_range, where the layout_range
765 is the single-line range shown as "Example A" above. */
767 static void
768 test_layout_range_for_single_line ()
770 layout_range example_a = make_range (2, 22, 2, 38);
772 /* Tests for layout_range::contains_point. */
774 for (int i = 0; i != CU_NUM_UNITS; ++i)
776 const enum column_unit col_unit = (enum column_unit) i;
778 /* Before the line. */
779 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
781 /* On the line, but before start. */
782 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
784 /* On the line, at the start. */
785 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
787 /* On the line, within the range. */
788 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
790 /* On the line, at the end. */
791 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
793 /* On the line, after the end. */
794 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
796 /* After the line. */
797 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
800 /* Tests for layout_range::intersects_line_p. */
801 ASSERT_FALSE (example_a.intersects_line_p (1));
802 ASSERT_TRUE (example_a.intersects_line_p (2));
803 ASSERT_FALSE (example_a.intersects_line_p (3));
806 /* Selftest for layout_range, where the layout_range
807 is the multi-line range shown as "Example B" above. */
809 static void
810 test_layout_range_for_multiple_lines ()
812 layout_range example_b = make_range (3, 14, 5, 8);
814 /* Tests for layout_range::contains_point. */
816 for (int i = 0; i != CU_NUM_UNITS; ++i)
818 const enum column_unit col_unit = (enum column_unit) i;
820 /* Before first line. */
821 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
823 /* On the first line, but before start. */
824 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
826 /* At the start. */
827 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
829 /* On the first line, within the range. */
830 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
832 /* On an interior line.
833 The column number should not matter; try various boundary
834 values. */
835 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
836 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
837 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
838 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
839 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
840 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
841 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
843 /* On the final line, before the end. */
844 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
846 /* On the final line, at the end. */
847 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
849 /* On the final line, after the end. */
850 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
852 /* After the line. */
853 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
856 /* Tests for layout_range::intersects_line_p. */
857 ASSERT_FALSE (example_b.intersects_line_p (2));
858 ASSERT_TRUE (example_b.intersects_line_p (3));
859 ASSERT_TRUE (example_b.intersects_line_p (4));
860 ASSERT_TRUE (example_b.intersects_line_p (5));
861 ASSERT_FALSE (example_b.intersects_line_p (6));
864 #endif /* #if CHECKING_P */
866 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
867 (still in bytes, not display cols) without any trailing whitespace. */
869 static int
870 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
872 int result = line_bytes;
873 while (result > 0)
875 char ch = line[result - 1];
876 if (ch == ' ' || ch == '\t' || ch == '\r')
877 result--;
878 else
879 break;
881 gcc_assert (result >= 0);
882 gcc_assert (result <= line_bytes);
883 gcc_assert (result == 0 ||
884 (line[result - 1] != ' '
885 && line[result -1] != '\t'
886 && line[result -1] != '\r'));
887 return result;
890 #if CHECKING_P
892 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
894 static void
895 assert_eq (const char *line, int expected_bytes)
897 int actual_value
898 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
899 ASSERT_EQ (actual_value, expected_bytes);
902 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
903 various inputs. It is not required to handle newlines. */
905 static void
906 test_get_line_bytes_without_trailing_whitespace ()
908 assert_eq ("", 0);
909 assert_eq (" ", 0);
910 assert_eq ("\t", 0);
911 assert_eq ("\r", 0);
912 assert_eq ("hello world", 11);
913 assert_eq ("hello world ", 11);
914 assert_eq ("hello world \t\t ", 11);
915 assert_eq ("hello world\r", 11);
918 #endif /* #if CHECKING_P */
920 /* Helper function for layout's ctor, for sanitizing locations relative
921 to the primary location within a diagnostic.
923 Compare LOC_A and LOC_B to see if it makes sense to print underlines
924 connecting their expanded locations. Doing so is only guaranteed to
925 make sense if the locations share the same macro expansion "history"
926 i.e. they can be traced through the same macro expansions, eventually
927 reaching an ordinary map.
929 This may be too strong a condition, but it effectively sanitizes
930 PR c++/70105, which has an example of printing an expression where the
931 final location of the expression is in a different macro, which
932 erroneously was leading to hundreds of lines of irrelevant source
933 being printed. */
935 static bool
936 compatible_locations_p (location_t loc_a, location_t loc_b)
938 if (IS_ADHOC_LOC (loc_a))
939 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
940 if (IS_ADHOC_LOC (loc_b))
941 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
943 /* If either location is one of the special locations outside of a
944 linemap, they are only compatible if they are equal. */
945 if (loc_a < RESERVED_LOCATION_COUNT
946 || loc_b < RESERVED_LOCATION_COUNT)
947 return loc_a == loc_b;
949 const line_map *map_a = linemap_lookup (line_table, loc_a);
950 linemap_assert (map_a);
952 const line_map *map_b = linemap_lookup (line_table, loc_b);
953 linemap_assert (map_b);
955 /* Are they within the same map? */
956 if (map_a == map_b)
958 /* Are both within the same macro expansion? */
959 if (linemap_macro_expansion_map_p (map_a))
961 /* If so, then they're only compatible if either both are
962 from the macro definition, or both from the macro arguments. */
963 bool loc_a_from_defn
964 = linemap_location_from_macro_definition_p (line_table, loc_a);
965 bool loc_b_from_defn
966 = linemap_location_from_macro_definition_p (line_table, loc_b);
967 if (loc_a_from_defn != loc_b_from_defn)
968 return false;
970 /* Expand each location towards the spelling location, and
971 recurse. */
972 const line_map_macro *macro_map = linemap_check_macro (map_a);
973 location_t loc_a_toward_spelling
974 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
975 macro_map,
976 loc_a);
977 location_t loc_b_toward_spelling
978 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
979 macro_map,
980 loc_b);
981 return compatible_locations_p (loc_a_toward_spelling,
982 loc_b_toward_spelling);
985 /* Otherwise they are within the same ordinary map. */
986 return true;
988 else
990 /* Within different maps. */
992 /* If either is within a macro expansion, they are incompatible. */
993 if (linemap_macro_expansion_map_p (map_a)
994 || linemap_macro_expansion_map_p (map_b))
995 return false;
997 /* Within two different ordinary maps; they are compatible iff they
998 are in the same file. */
999 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1000 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1001 return ord_map_a->to_file == ord_map_b->to_file;
1005 /* Comparator for sorting fix-it hints. */
1007 static int
1008 fixit_cmp (const void *p_a, const void *p_b)
1010 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1011 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1012 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1015 /* Callbacks for use when not escaping the source. */
1017 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1019 /* Callback for char_display_policy::m_print_cb for printing source chars
1020 when not escaping the source. */
1022 static void
1023 default_print_decoded_ch (pretty_printer *pp,
1024 const cpp_decoded_char &decoded_ch)
1026 for (const char *ptr = decoded_ch.m_start_byte;
1027 ptr != decoded_ch.m_next_byte; ptr++)
1029 if (*ptr == '\0' || *ptr == '\r')
1031 pp_space (pp);
1032 continue;
1035 pp_character (pp, *ptr);
1039 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1041 static const int width_per_escaped_byte = 4;
1043 /* Callback for char_column_policy::m_width_cb for determining the
1044 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1046 static int
1047 escape_as_bytes_width (cppchar_t ch)
1049 if (ch < 0x80 && ISPRINT (ch))
1050 return cpp_wcwidth (ch);
1051 else
1053 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1054 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1055 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1056 return 4 * width_per_escaped_byte;
1060 /* Callback for char_display_policy::m_print_cb for printing source chars
1061 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1063 static void
1064 escape_as_bytes_print (pretty_printer *pp,
1065 const cpp_decoded_char &decoded_ch)
1067 if (!decoded_ch.m_valid_ch)
1069 for (const char *iter = decoded_ch.m_start_byte;
1070 iter != decoded_ch.m_next_byte; ++iter)
1072 char buf[16];
1073 sprintf (buf, "<%02x>", (unsigned char)*iter);
1074 pp_string (pp, buf);
1076 return;
1079 cppchar_t ch = decoded_ch.m_ch;
1080 if (ch < 0x80 && ISPRINT (ch))
1081 pp_character (pp, ch);
1082 else
1084 for (const char *iter = decoded_ch.m_start_byte;
1085 iter < decoded_ch.m_next_byte; ++iter)
1087 char buf[16];
1088 sprintf (buf, "<%02x>", (unsigned char)*iter);
1089 pp_string (pp, buf);
1094 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1096 /* Callback for char_column_policy::m_width_cb for determining the
1097 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1099 static int
1100 escape_as_unicode_width (cppchar_t ch)
1102 if (ch < 0x80 && ISPRINT (ch))
1103 return cpp_wcwidth (ch);
1104 else
1106 // Width of "<U+%04x>"
1107 if (ch > 0xfffff)
1108 return 10;
1109 else if (ch > 0xffff)
1110 return 9;
1111 else
1112 return 8;
1116 /* Callback for char_display_policy::m_print_cb for printing source chars
1117 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1119 static void
1120 escape_as_unicode_print (pretty_printer *pp,
1121 const cpp_decoded_char &decoded_ch)
1123 if (!decoded_ch.m_valid_ch)
1125 escape_as_bytes_print (pp, decoded_ch);
1126 return;
1129 cppchar_t ch = decoded_ch.m_ch;
1130 if (ch < 0x80 && ISPRINT (ch))
1131 pp_character (pp, ch);
1132 else
1134 char buf[16];
1135 sprintf (buf, "<U+%04X>", ch);
1136 pp_string (pp, buf);
1140 /* Populate a char_display_policy based on DC and RICHLOC. */
1142 static char_display_policy
1143 make_policy (const diagnostic_context &dc,
1144 const rich_location &richloc)
1146 /* The default is to not escape non-ASCII bytes. */
1147 char_display_policy result
1148 (dc.tabstop, cpp_wcwidth, default_print_decoded_ch);
1150 /* If the diagnostic suggests escaping non-ASCII bytes, then
1151 use policy from user-supplied options. */
1152 if (richloc.escape_on_output_p ())
1154 result.m_undecoded_byte_width = width_per_escaped_byte;
1155 switch (dc.escape_format)
1157 default:
1158 gcc_unreachable ();
1159 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1160 result.m_width_cb = escape_as_unicode_width;
1161 result.m_print_cb = escape_as_unicode_print;
1162 break;
1163 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1164 result.m_width_cb = escape_as_bytes_width;
1165 result.m_print_cb = escape_as_bytes_print;
1166 break;
1170 return result;
1173 /* Implementation of class layout. */
1175 /* Constructor for class layout.
1177 Filter the ranges from the rich_location to those that we can
1178 sanely print, populating m_layout_ranges and m_fixit_hints.
1179 Determine the range of lines that we will print, splitting them
1180 up into an ordered list of disjoint spans of contiguous line numbers.
1181 Determine m_x_offset_display, to ensure that the primary caret
1182 will fit within the max_width provided by the diagnostic_context. */
1184 layout::layout (diagnostic_context * context,
1185 rich_location *richloc,
1186 diagnostic_t diagnostic_kind)
1187 : m_context (context),
1188 m_pp (context->printer),
1189 m_policy (make_policy (*context, *richloc)),
1190 m_primary_loc (richloc->get_range (0)->m_loc),
1191 m_exploc (richloc->get_expanded_location (0), m_policy,
1192 LOCATION_ASPECT_CARET),
1193 m_colorizer (context, diagnostic_kind),
1194 m_colorize_source_p (context->colorize_source_p),
1195 m_show_labels_p (context->show_labels_p),
1196 m_show_line_numbers_p (context->show_line_numbers_p),
1197 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1198 m_layout_ranges (richloc->get_num_locations ()),
1199 m_fixit_hints (richloc->get_num_fixit_hints ()),
1200 m_line_spans (1 + richloc->get_num_locations ()),
1201 m_linenum_width (0),
1202 m_x_offset_display (0),
1203 m_escape_on_output (richloc->escape_on_output_p ())
1205 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
1207 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1208 Ignore any ranges that are awkward to handle. */
1209 const location_range *loc_range = richloc->get_range (idx);
1210 maybe_add_location_range (loc_range, idx, false);
1213 /* Populate m_fixit_hints, filtering to only those that are in the
1214 same file. */
1215 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1217 const fixit_hint *hint = richloc->get_fixit_hint (i);
1218 if (validate_fixit_hint_p (hint))
1219 m_fixit_hints.safe_push (hint);
1222 /* Sort m_fixit_hints. */
1223 m_fixit_hints.qsort (fixit_cmp);
1225 /* Populate the indicated members. */
1226 calculate_line_spans ();
1227 calculate_linenum_width ();
1228 calculate_x_offset_display ();
1230 if (context->show_ruler_p)
1231 show_ruler (m_x_offset_display + m_context->caret_max_width);
1235 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1236 those that we can sanely print.
1238 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1239 (for use as extrinsic state by label ranges FIXME).
1241 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1242 filtered against this layout instance's current line spans: it
1243 will only be added if the location is fully within the lines
1244 already specified by other locations.
1246 Return true iff LOC_RANGE was added. */
1248 bool
1249 layout::maybe_add_location_range (const location_range *loc_range,
1250 unsigned original_idx,
1251 bool restrict_to_current_line_spans)
1253 gcc_assert (loc_range);
1255 /* Split the "range" into caret and range information. */
1256 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1258 /* Expand the various locations. */
1259 expanded_location start
1260 = linemap_client_expand_location_to_spelling_point
1261 (src_range.m_start, LOCATION_ASPECT_START);
1262 expanded_location finish
1263 = linemap_client_expand_location_to_spelling_point
1264 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1265 expanded_location caret
1266 = linemap_client_expand_location_to_spelling_point
1267 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1269 /* If any part of the range isn't in the same file as the primary
1270 location of this diagnostic, ignore the range. */
1271 if (start.file != m_exploc.file)
1272 return false;
1273 if (finish.file != m_exploc.file)
1274 return false;
1275 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1276 if (caret.file != m_exploc.file)
1277 return false;
1279 /* Sanitize the caret location for non-primary ranges. */
1280 if (m_layout_ranges.length () > 0)
1281 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1282 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1283 /* Discard any non-primary ranges that can't be printed
1284 sanely relative to the primary location. */
1285 return false;
1287 /* Everything is now known to be in the correct source file,
1288 but it may require further sanitization. */
1289 layout_range ri (exploc_with_display_col (start, m_policy,
1290 LOCATION_ASPECT_START),
1291 exploc_with_display_col (finish, m_policy,
1292 LOCATION_ASPECT_FINISH),
1293 loc_range->m_range_display_kind,
1294 exploc_with_display_col (caret, m_policy,
1295 LOCATION_ASPECT_CARET),
1296 original_idx, loc_range->m_label);
1298 /* If we have a range that finishes before it starts (perhaps
1299 from something built via macro expansion), printing the
1300 range is likely to be nonsensical. Also, attempting to do so
1301 breaks assumptions within the printing code (PR c/68473).
1302 Similarly, don't attempt to print ranges if one or both ends
1303 of the range aren't sane to print relative to the
1304 primary location (PR c++/70105). */
1305 if (start.line > finish.line
1306 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1307 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1309 /* Is this the primary location? */
1310 if (m_layout_ranges.length () == 0)
1312 /* We want to print the caret for the primary location, but
1313 we must sanitize away m_start and m_finish. */
1314 ri.m_start = ri.m_caret;
1315 ri.m_finish = ri.m_caret;
1317 else
1318 /* This is a non-primary range; ignore it. */
1319 return false;
1322 /* Potentially filter to just the lines already specified by other
1323 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1324 The layout ctor doesn't use it, and can't because m_line_spans
1325 hasn't been set up at that point. */
1326 if (restrict_to_current_line_spans)
1328 if (!will_show_line_p (start.line))
1329 return false;
1330 if (!will_show_line_p (finish.line))
1331 return false;
1332 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1333 if (!will_show_line_p (caret.line))
1334 return false;
1337 /* Passed all the tests; add the range to m_layout_ranges so that
1338 it will be printed. */
1339 m_layout_ranges.safe_push (ri);
1340 return true;
1343 /* Return true iff ROW is within one of the line spans for this layout. */
1345 bool
1346 layout::will_show_line_p (linenum_type row) const
1348 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1349 line_span_idx++)
1351 const line_span *line_span = get_line_span (line_span_idx);
1352 if (line_span->contains_line_p (row))
1353 return true;
1355 return false;
1358 /* Print a line showing a gap in the line numbers, for showing the boundary
1359 between two line spans. */
1361 void
1362 layout::print_gap_in_line_numbering ()
1364 gcc_assert (m_show_line_numbers_p);
1366 pp_emit_prefix (m_pp);
1368 for (int i = 0; i < m_linenum_width + 1; i++)
1369 pp_character (m_pp, '.');
1371 pp_newline (m_pp);
1374 /* Return true iff we should print a heading when starting the
1375 line span with the given index. */
1377 bool
1378 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1380 /* We print a heading for every change of line span, hence for every
1381 line span after the initial one. */
1382 if (line_span_idx > 0)
1383 return true;
1385 /* We also do it for the initial span if the primary location of the
1386 diagnostic is in a different span. */
1387 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1388 return true;
1390 return false;
1393 /* Get an expanded_location for the first location of interest within
1394 the given line_span.
1395 Used when printing a heading to indicate a new line span. */
1397 expanded_location
1398 layout::get_expanded_location (const line_span *line_span) const
1400 /* Whenever possible, use the caret location. */
1401 if (line_span->contains_line_p (m_exploc.line))
1402 return m_exploc;
1404 /* Otherwise, use the start of the first range that's present
1405 within the line_span. */
1406 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1408 const layout_range *lr = &m_layout_ranges[i];
1409 if (line_span->contains_line_p (lr->m_start.m_line))
1411 expanded_location exploc = m_exploc;
1412 exploc.line = lr->m_start.m_line;
1413 exploc.column = lr->m_start.m_columns[CU_BYTES];
1414 return exploc;
1418 /* Otherwise, use the location of the first fixit-hint present within
1419 the line_span. */
1420 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1422 const fixit_hint *hint = m_fixit_hints[i];
1423 location_t loc = hint->get_start_loc ();
1424 expanded_location exploc = expand_location (loc);
1425 if (line_span->contains_line_p (exploc.line))
1426 return exploc;
1429 /* It should not be possible to have a line span that didn't
1430 contain any of the layout_range or fixit_hint instances. */
1431 gcc_unreachable ();
1432 return m_exploc;
1435 /* Determine if HINT is meaningful to print within this layout. */
1437 bool
1438 layout::validate_fixit_hint_p (const fixit_hint *hint)
1440 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1441 return false;
1442 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1443 return false;
1445 return true;
1448 /* Determine the range of lines affected by HINT.
1449 This assumes that HINT has already been filtered by
1450 validate_fixit_hint_p, and so affects the correct source file. */
1452 static line_span
1453 get_line_span_for_fixit_hint (const fixit_hint *hint)
1455 gcc_assert (hint);
1457 int start_line = LOCATION_LINE (hint->get_start_loc ());
1459 /* For line-insertion fix-it hints, add the previous line to the
1460 span, to give the user more context on the proposed change. */
1461 if (hint->ends_with_newline_p ())
1462 if (start_line > 1)
1463 start_line--;
1465 return line_span (start_line,
1466 LOCATION_LINE (hint->get_next_loc ()));
1469 /* We want to print the pertinent source code at a diagnostic. The
1470 rich_location can contain multiple locations. This will have been
1471 filtered into m_exploc (the caret for the primary location) and
1472 m_layout_ranges, for those ranges within the same source file.
1474 We will print a subset of the lines within the source file in question,
1475 as a collection of "spans" of lines.
1477 This function populates m_line_spans with an ordered, disjoint list of
1478 the line spans of interest.
1480 Printing a gap between line spans takes one line, so, when printing
1481 line numbers, we allow a gap of up to one line between spans when
1482 merging, since it makes more sense to print the source line rather than a
1483 "gap-in-line-numbering" line. When not printing line numbers, it's
1484 better to be more explicit about what's going on, so keeping them as
1485 separate spans is preferred.
1487 For example, if the primary range is on lines 8-10, with secondary ranges
1488 covering lines 5-6 and lines 13-15:
1491 005 |RANGE 1
1492 006 |RANGE 1
1494 008 |PRIMARY RANGE
1495 009 |PRIMARY CARET
1496 010 |PRIMARY RANGE
1499 013 |RANGE 2
1500 014 |RANGE 2
1501 015 |RANGE 2
1504 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1506 With line numbering off (with span headers), we want three spans: lines 5-6,
1507 lines 8-10, and lines 13-15. */
1509 void
1510 layout::calculate_line_spans ()
1512 /* This should only be called once, by the ctor. */
1513 gcc_assert (m_line_spans.length () == 0);
1515 /* Populate tmp_spans with individual spans, for each of
1516 m_exploc, and for m_layout_ranges. */
1517 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1518 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1519 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1521 const layout_range *lr = &m_layout_ranges[i];
1522 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1523 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1524 lr->m_finish.m_line));
1527 /* Also add spans for any fix-it hints, in case they cover other lines. */
1528 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1530 const fixit_hint *hint = m_fixit_hints[i];
1531 gcc_assert (hint);
1532 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1535 /* Sort them. */
1536 tmp_spans.qsort(line_span::comparator);
1538 /* Now iterate through tmp_spans, copying into m_line_spans, and
1539 combining where possible. */
1540 gcc_assert (tmp_spans.length () > 0);
1541 m_line_spans.safe_push (tmp_spans[0]);
1542 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1544 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1545 const line_span *next = &tmp_spans[i];
1546 gcc_assert (next->m_first_line >= current->m_first_line);
1547 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1548 if ((linenum_arith_t)next->m_first_line
1549 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1551 /* We can merge them. */
1552 if (next->m_last_line > current->m_last_line)
1553 current->m_last_line = next->m_last_line;
1555 else
1557 /* No merger possible. */
1558 m_line_spans.safe_push (*next);
1562 /* Verify the result, in m_line_spans. */
1563 gcc_assert (m_line_spans.length () > 0);
1564 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1566 const line_span *prev = &m_line_spans[i - 1];
1567 const line_span *next = &m_line_spans[i];
1568 /* The individual spans must be sane. */
1569 gcc_assert (prev->m_first_line <= prev->m_last_line);
1570 gcc_assert (next->m_first_line <= next->m_last_line);
1571 /* The spans must be ordered. */
1572 gcc_assert (prev->m_first_line < next->m_first_line);
1573 /* There must be a gap of at least one line between separate spans. */
1574 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1578 /* Determine how many display columns need to be reserved for line numbers,
1579 based on the largest line number that will be needed, and populate
1580 m_linenum_width. */
1582 void
1583 layout::calculate_linenum_width ()
1585 gcc_assert (m_line_spans.length () > 0);
1586 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1587 int highest_line = last_span->m_last_line;
1588 if (highest_line < 0)
1589 highest_line = 0;
1590 m_linenum_width = num_digits (highest_line);
1591 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1592 if (m_line_spans.length () > 1)
1593 m_linenum_width = MAX (m_linenum_width, 3);
1594 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1595 after the line number. */
1596 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1599 /* Calculate m_x_offset_display, which improves readability in case the source
1600 line of interest is longer than the user's display. All lines output will be
1601 shifted to the left (so that their beginning is no longer displayed) by
1602 m_x_offset_display display columns, so that the caret is in a reasonable
1603 location. */
1605 void
1606 layout::calculate_x_offset_display ()
1608 m_x_offset_display = 0;
1610 const int max_width = m_context->caret_max_width;
1611 if (!max_width)
1613 /* Nothing to do, the width is not capped. */
1614 return;
1617 const char_span line = location_get_source_line (m_exploc.file,
1618 m_exploc.line);
1619 if (!line)
1621 /* Nothing to do, we couldn't find the source line. */
1622 return;
1624 int caret_display_column = m_exploc.m_display_col;
1625 const int line_bytes
1626 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1627 line.length ());
1628 int eol_display_column
1629 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1630 if (caret_display_column > eol_display_column
1631 || !caret_display_column)
1633 /* This does not make sense, so don't try to do anything in this case. */
1634 return;
1637 /* Adjust caret and eol positions to include the left margin. If we are
1638 outputting line numbers, then the left margin is equal to m_linenum_width
1639 plus three for the " | " which follows it. Otherwise the left margin width
1640 is equal to 1, because layout::print_source_line() will prefix each line
1641 with a space. */
1642 const int source_display_cols = eol_display_column;
1643 int left_margin_size = 1;
1644 if (m_show_line_numbers_p)
1645 left_margin_size = m_linenum_width + 3;
1646 caret_display_column += left_margin_size;
1647 eol_display_column += left_margin_size;
1649 if (eol_display_column <= max_width)
1651 /* Nothing to do, everything fits in the display. */
1652 return;
1655 /* The line is too long for the display. Calculate an offset such that the
1656 caret is not too close to the right edge of the screen. It will be
1657 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1658 than that to the end of the source line anyway. */
1659 int right_margin_size = CARET_LINE_MARGIN;
1660 right_margin_size = MIN (eol_display_column - caret_display_column,
1661 right_margin_size);
1662 if (right_margin_size + left_margin_size >= max_width)
1664 /* The max_width is very small, so anything we try to do will not be very
1665 effective; just punt in this case and output with no offset. */
1666 return;
1668 const int max_caret_display_column = max_width - right_margin_size;
1669 if (caret_display_column > max_caret_display_column)
1671 m_x_offset_display = caret_display_column - max_caret_display_column;
1672 /* Make sure we don't offset the line into oblivion. */
1673 static const int min_cols_visible = 2;
1674 if (source_display_cols - m_x_offset_display < min_cols_visible)
1675 m_x_offset_display = 0;
1679 /* Print line ROW of source code, potentially colorized at any ranges, and
1680 return the line bounds. LINE is the source line (not necessarily
1681 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1682 colorization and tab expansion, this function tracks the line position in
1683 both byte and display column units. */
1685 line_bounds
1686 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1688 m_colorizer.set_normal_text ();
1690 pp_emit_prefix (m_pp);
1691 if (m_show_line_numbers_p)
1693 int width = num_digits (row);
1694 for (int i = 0; i < m_linenum_width - width; i++)
1695 pp_space (m_pp);
1696 pp_printf (m_pp, "%i | ", row);
1698 else
1699 pp_space (m_pp);
1701 /* We will stop printing the source line at any trailing whitespace. */
1702 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1703 line_bytes);
1705 /* This object helps to keep track of which display column we are at, which is
1706 necessary for computing the line bounds in display units, for doing
1707 tab expansion, and for implementing m_x_offset_display. */
1708 cpp_display_width_computation dw (line, line_bytes, m_policy);
1710 /* Skip the first m_x_offset_display display columns. In case the leading
1711 portion that will be skipped ends with a character with wcwidth > 1, then
1712 it is possible we skipped too much, so account for that by padding with
1713 spaces. Note that this does the right thing too in case a tab was the last
1714 character to be skipped over; the tab is effectively replaced by the
1715 correct number of trailing spaces needed to offset by the desired number of
1716 display columns. */
1717 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1718 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1719 pp_space (m_pp);
1721 /* Print the line and compute the line_bounds. */
1722 line_bounds lbounds;
1723 while (!dw.done ())
1725 /* Assuming colorization is enabled for the caret and underline
1726 characters, we may also colorize the associated characters
1727 within the source line.
1729 For frontends that generate range information, we color the
1730 associated characters in the source line the same as the
1731 carets and underlines in the annotation line, to make it easier
1732 for the reader to see the pertinent code.
1734 For frontends that only generate carets, we don't colorize the
1735 characters above them, since this would look strange (e.g.
1736 colorizing just the first character in a token). */
1737 if (m_colorize_source_p)
1739 bool in_range_p;
1740 point_state state;
1741 const int start_byte_col = dw.bytes_processed () + 1;
1742 in_range_p = get_state_at_point (row, start_byte_col,
1743 0, INT_MAX,
1744 CU_BYTES,
1745 &state);
1746 if (in_range_p)
1747 m_colorizer.set_range (state.range_idx);
1748 else
1749 m_colorizer.set_normal_text ();
1752 /* Get the display width of the next character to be output, expanding
1753 tabs and replacing some control bytes with spaces as necessary. */
1754 const char *c = dw.next_byte ();
1755 const int start_disp_col = dw.display_cols_processed () + 1;
1756 cpp_decoded_char cp;
1757 const int this_display_width = dw.process_next_codepoint (&cp);
1758 if (*c == '\t')
1760 /* The returned display width is the number of spaces into which the
1761 tab should be expanded. */
1762 for (int i = 0; i != this_display_width; ++i)
1763 pp_space (m_pp);
1764 continue;
1767 /* We have a (possibly multibyte) character to output; update the line
1768 bounds if it is not whitespace. */
1769 if (*c != ' ')
1771 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1772 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1773 lbounds.m_first_non_ws_disp_col = start_disp_col;
1776 /* Output the character. */
1777 m_policy.m_print_cb (m_pp, cp);
1778 c = dw.next_byte ();
1780 print_newline ();
1781 return lbounds;
1784 /* Determine if we should print an annotation line for ROW.
1785 i.e. if any of m_layout_ranges contains ROW. */
1787 bool
1788 layout::should_print_annotation_line_p (linenum_type row) const
1790 layout_range *range;
1791 int i;
1792 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1794 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1795 return false;
1796 if (range->intersects_line_p (row))
1797 return true;
1799 return false;
1802 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1803 margin, which is empty for annotation lines. Otherwise, do nothing. */
1805 void
1806 layout::start_annotation_line (char margin_char) const
1808 pp_emit_prefix (m_pp);
1809 if (m_show_line_numbers_p)
1811 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1812 of it, right-aligned, padded with spaces. */
1813 int i;
1814 for (i = 0; i < m_linenum_width - 3; i++)
1815 pp_space (m_pp);
1816 for (; i < m_linenum_width; i++)
1817 pp_character (m_pp, margin_char);
1818 pp_string (m_pp, " |");
1822 /* Print a line consisting of the caret/underlines for the given
1823 source line. */
1825 void
1826 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1828 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1829 lbounds.m_last_non_ws_disp_col);
1831 start_annotation_line ();
1832 pp_space (m_pp);
1834 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1836 bool in_range_p;
1837 point_state state;
1838 in_range_p = get_state_at_point (row, column,
1839 lbounds.m_first_non_ws_disp_col,
1840 lbounds.m_last_non_ws_disp_col,
1841 CU_DISPLAY_COLS,
1842 &state);
1843 if (in_range_p)
1845 /* Within a range. Draw either the caret or an underline. */
1846 m_colorizer.set_range (state.range_idx);
1847 if (state.draw_caret_p)
1849 /* Draw the caret. */
1850 char caret_char;
1851 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1852 caret_char = m_context->caret_chars[state.range_idx];
1853 else
1854 caret_char = '^';
1855 pp_character (m_pp, caret_char);
1857 else
1858 pp_character (m_pp, '~');
1860 else
1862 /* Not in a range. */
1863 m_colorizer.set_normal_text ();
1864 pp_character (m_pp, ' ');
1867 print_newline ();
1870 /* A version of label_text that can live inside a vec.
1871 Requires manual cleanup via maybe_free. */
1873 struct pod_label_text
1875 pod_label_text ()
1876 : m_buffer (NULL), m_caller_owned (false)
1879 pod_label_text (label_text &&other)
1880 : m_buffer (const_cast<char*> (other.get ())),
1881 m_caller_owned (other.is_owner ())
1883 other.release ();
1886 void maybe_free ()
1888 if (m_caller_owned)
1889 free (m_buffer);
1892 char *m_buffer;
1893 bool m_caller_owned;
1896 /* Implementation detail of layout::print_any_labels.
1898 A label within the given row of source. */
1900 class line_label
1902 public:
1903 line_label (const cpp_char_column_policy &policy,
1904 int state_idx, int column,
1905 label_text text)
1906 : m_state_idx (state_idx), m_column (column),
1907 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1909 const int bytes = strlen (m_text.m_buffer);
1910 m_display_width = cpp_display_width (m_text.m_buffer, bytes, policy);
1913 /* Sorting is primarily by column, then by state index. */
1914 static int comparator (const void *p1, const void *p2)
1916 const line_label *ll1 = (const line_label *)p1;
1917 const line_label *ll2 = (const line_label *)p2;
1918 int column_cmp = compare (ll1->m_column, ll2->m_column);
1919 if (column_cmp)
1920 return column_cmp;
1921 /* Order by reverse state index, so that labels are printed
1922 in order of insertion into the rich_location when the
1923 sorted list is walked backwards. */
1924 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1927 int m_state_idx;
1928 int m_column;
1929 pod_label_text m_text;
1930 size_t m_display_width;
1931 int m_label_line;
1932 bool m_has_vbar;
1935 /* Print any labels in this row. */
1936 void
1937 layout::print_any_labels (linenum_type row)
1939 int i;
1940 auto_vec<line_label> labels;
1942 /* Gather the labels that are to be printed into "labels". */
1944 layout_range *range;
1945 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1947 /* Most ranges don't have labels, so reject this first. */
1948 if (range->m_label == NULL)
1949 continue;
1951 /* The range's caret must be on this line. */
1952 if (range->m_caret.m_line != row)
1953 continue;
1955 /* Reject labels that aren't fully visible due to clipping
1956 by m_x_offset_display. */
1957 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1958 if (disp_col <= m_x_offset_display)
1959 continue;
1961 label_text text;
1962 text = range->m_label->get_text (range->m_original_idx);
1964 /* Allow for labels that return NULL from their get_text
1965 implementation (so e.g. such labels can control their own
1966 visibility). */
1967 if (text.get () == NULL)
1968 continue;
1970 labels.safe_push (line_label (m_policy, i, disp_col, std::move (text)));
1974 /* Bail out if there are no labels on this row. */
1975 if (labels.length () == 0)
1976 return;
1978 /* Sort them. */
1979 labels.qsort(line_label::comparator);
1981 /* Figure out how many "label lines" we need, and which
1982 one each label is printed in.
1984 For example, if the labels aren't too densely packed,
1985 we can fit them on the same line, giving two "label lines":
1987 foo + bar
1988 ~~~ ~~~
1989 | | : label line 0
1990 l0 l1 : label line 1
1992 If they would touch each other or overlap, then we need
1993 additional "label lines":
1995 foo + bar
1996 ~~~ ~~~
1997 | | : label line 0
1998 | label 1 : label line 1
1999 label 0 : label line 2
2001 Place the final label on label line 1, and work backwards, adding
2002 label lines as needed.
2004 If multiple labels are at the same place, put them on separate
2005 label lines:
2007 foo + bar
2008 ^ : label line 0
2009 | : label line 1
2010 label 0 : label line 2
2011 label 1 : label line 3. */
2013 int max_label_line = 1;
2015 int next_column = INT_MAX;
2016 line_label *label;
2017 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2019 /* Would this label "touch" or overlap the next label? */
2020 if (label->m_column + label->m_display_width >= (size_t)next_column)
2022 max_label_line++;
2024 /* If we've already seen labels with the same column, suppress the
2025 vertical bar for subsequent ones in this backwards iteration;
2026 hence only the one with the highest label_line has m_has_vbar set. */
2027 if (label->m_column == next_column)
2028 label->m_has_vbar = false;
2031 label->m_label_line = max_label_line;
2032 next_column = label->m_column;
2036 /* Print the "label lines". For each label within the line, print
2037 either a vertical bar ('|') for the labels that are lower down, or the
2038 labels themselves once we've reached their line. */
2040 for (int label_line = 0; label_line <= max_label_line; label_line++)
2042 start_annotation_line ();
2043 pp_space (m_pp);
2044 int column = 1 + m_x_offset_display;
2045 line_label *label;
2046 FOR_EACH_VEC_ELT (labels, i, label)
2048 if (label_line > label->m_label_line)
2049 /* We've printed all the labels for this label line. */
2050 break;
2052 if (label_line == label->m_label_line)
2054 gcc_assert (column <= label->m_column);
2055 move_to_column (&column, label->m_column, true);
2056 /* Colorize the text, unless it's for events in a
2057 diagnostic_path. */
2058 if (!m_diagnostic_path_p)
2059 m_colorizer.set_range (label->m_state_idx);
2060 pp_string (m_pp, label->m_text.m_buffer);
2061 m_colorizer.set_normal_text ();
2062 column += label->m_display_width;
2064 else if (label->m_has_vbar)
2066 gcc_assert (column <= label->m_column);
2067 move_to_column (&column, label->m_column, true);
2068 m_colorizer.set_range (label->m_state_idx);
2069 pp_character (m_pp, '|');
2070 m_colorizer.set_normal_text ();
2071 column++;
2074 print_newline ();
2078 /* Clean up. */
2080 line_label *label;
2081 FOR_EACH_VEC_ELT (labels, i, label)
2082 label->m_text.maybe_free ();
2086 /* If there are any fixit hints inserting new lines before source line ROW,
2087 print them.
2089 They are printed on lines of their own, before the source line
2090 itself, with a leading '+'. */
2092 void
2093 layout::print_leading_fixits (linenum_type row)
2095 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2097 const fixit_hint *hint = m_fixit_hints[i];
2099 if (!hint->ends_with_newline_p ())
2100 /* Not a newline fixit; print it in print_trailing_fixits. */
2101 continue;
2103 gcc_assert (hint->insertion_p ());
2105 if (hint->affects_line_p (m_exploc.file, row))
2107 /* Printing the '+' with normal colorization
2108 and the inserted line with "insert" colorization
2109 helps them stand out from each other, and from
2110 the surrounding text. */
2111 m_colorizer.set_normal_text ();
2112 start_annotation_line ('+');
2113 pp_character (m_pp, '+');
2114 m_colorizer.set_fixit_insert ();
2115 /* Print all but the trailing newline of the fix-it hint.
2116 We have to print the newline separately to avoid
2117 getting additional pp prefixes printed. */
2118 for (size_t i = 0; i < hint->get_length () - 1; i++)
2119 pp_character (m_pp, hint->get_string ()[i]);
2120 m_colorizer.set_normal_text ();
2121 pp_newline (m_pp);
2126 /* Subroutine of layout::print_trailing_fixits.
2128 Determine if the annotation line printed for LINE contained
2129 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2131 bool
2132 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2133 int finish_column) const
2135 layout_range *range;
2136 int i;
2137 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2138 if (range->m_start.m_line == line
2139 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2140 && range->m_finish.m_line == line
2141 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2142 return true;
2143 return false;
2146 /* Classes for printing trailing fix-it hints i.e. those that
2147 don't add new lines.
2149 For insertion, these can look like:
2151 new_text
2153 For replacement, these can look like:
2155 ------------- : underline showing affected range
2156 new_text
2158 For deletion, these can look like:
2160 ------------- : underline showing affected range
2162 This can become confusing if they overlap, and so we need
2163 to do some preprocessing to decide what to print.
2164 We use the list of fixit_hint instances affecting the line
2165 to build a list of "correction" instances, and print the
2166 latter.
2168 For example, consider a set of fix-its for converting
2169 a C-style cast to a C++ const_cast.
2171 Given:
2173 ..000000000111111111122222222223333333333.
2174 ..123456789012345678901234567890123456789.
2175 foo *f = (foo *)ptr->field;
2176 ^~~~~
2178 and the fix-it hints:
2179 - replace col 10 (the open paren) with "const_cast<"
2180 - replace col 16 (the close paren) with "> ("
2181 - insert ")" before col 27
2183 then we would get odd-looking output:
2185 foo *f = (foo *)ptr->field;
2186 ^~~~~
2188 const_cast<
2190 > ( )
2192 It would be better to detect when fixit hints are going to
2193 overlap (those that require new lines), and to consolidate
2194 the printing of such fixits, giving something like:
2196 foo *f = (foo *)ptr->field;
2197 ^~~~~
2198 -----------------
2199 const_cast<foo *> (ptr->field)
2201 This works by detecting when the printing would overlap, and
2202 effectively injecting no-op replace hints into the gaps between
2203 such fix-its, so that the printing joins up.
2205 In the above example, the overlap of:
2206 - replace col 10 (the open paren) with "const_cast<"
2207 and:
2208 - replace col 16 (the close paren) with "> ("
2209 is fixed by injecting a no-op:
2210 - replace cols 11-15 with themselves ("foo *")
2211 and consolidating these, making:
2212 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2213 i.e.:
2214 - replace cols 10-16 with "const_cast<foo *> ("
2216 This overlaps with the final fix-it hint:
2217 - insert ")" before col 27
2218 and so we repeat the consolidation process, by injecting
2219 a no-op:
2220 - replace cols 17-26 with themselves ("ptr->field")
2221 giving:
2222 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2223 i.e.:
2224 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2226 and is thus printed as desired. */
2228 /* A range of (byte or display) columns within a line. */
2230 class column_range
2232 public:
2233 column_range (int start_, int finish_) : start (start_), finish (finish_)
2235 gcc_assert (valid_p (start, finish));
2238 bool operator== (const column_range &other) const
2240 return start == other.start && finish == other.finish;
2243 static bool valid_p (int start, int finish)
2245 /* We must have either a range, or an insertion. */
2246 return (start <= finish || finish == start - 1);
2249 int start;
2250 int finish;
2253 /* Get the range of bytes or display columns that HINT would affect. */
2254 static column_range
2255 get_affected_range (const cpp_char_column_policy &policy,
2256 const fixit_hint *hint, enum column_unit col_unit)
2258 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2259 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2260 --exploc_finish.column;
2262 int start_column;
2263 int finish_column;
2264 if (col_unit == CU_DISPLAY_COLS)
2266 start_column = location_compute_display_column (exploc_start, policy);
2267 if (hint->insertion_p ())
2268 finish_column = start_column - 1;
2269 else
2270 finish_column = location_compute_display_column (exploc_finish, policy);
2272 else
2274 start_column = exploc_start.column;
2275 finish_column = exploc_finish.column;
2277 return column_range (start_column, finish_column);
2280 /* Get the range of display columns that would be printed for HINT. */
2282 static column_range
2283 get_printed_columns (const cpp_char_column_policy &policy,
2284 const fixit_hint *hint)
2286 expanded_location exploc = expand_location (hint->get_start_loc ());
2287 int start_column = location_compute_display_column (exploc, policy);
2288 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2289 policy);
2290 int final_hint_column = start_column + hint_width - 1;
2291 if (hint->insertion_p ())
2293 return column_range (start_column, final_hint_column);
2295 else
2297 exploc = expand_location (hint->get_next_loc ());
2298 --exploc.column;
2299 int finish_column = location_compute_display_column (exploc, policy);
2300 return column_range (start_column,
2301 MAX (finish_column, final_hint_column));
2305 /* A correction on a particular line.
2306 This describes a plan for how to print one or more fixit_hint
2307 instances that affected the line, potentially consolidating hints
2308 into corrections to make the result easier for the user to read. */
2310 class correction
2312 public:
2313 correction (column_range affected_bytes,
2314 column_range affected_columns,
2315 column_range printed_columns,
2316 const char *new_text, size_t new_text_len,
2317 const cpp_char_column_policy &policy)
2318 : m_affected_bytes (affected_bytes),
2319 m_affected_columns (affected_columns),
2320 m_printed_columns (printed_columns),
2321 m_text (xstrdup (new_text)),
2322 m_byte_length (new_text_len),
2323 m_policy (policy),
2324 m_alloc_sz (new_text_len + 1)
2326 compute_display_cols ();
2329 ~correction () { free (m_text); }
2331 bool insertion_p () const
2333 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2336 void ensure_capacity (size_t len);
2337 void ensure_terminated ();
2339 void compute_display_cols ()
2341 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2344 void overwrite (int dst_offset, const char_span &src_span)
2346 gcc_assert (dst_offset >= 0);
2347 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2348 memcpy (m_text + dst_offset, src_span.get_buffer (),
2349 src_span.length ());
2352 /* If insert, then start: the column before which the text
2353 is to be inserted, and finish is offset by the length of
2354 the replacement.
2355 If replace, then the range of columns affected. */
2356 column_range m_affected_bytes;
2357 column_range m_affected_columns;
2359 /* If insert, then start: the column before which the text
2360 is to be inserted, and finish is offset by the length of
2361 the replacement.
2362 If replace, then the range of columns affected. */
2363 column_range m_printed_columns;
2365 /* The text to be inserted/used as replacement. */
2366 char *m_text;
2367 size_t m_byte_length; /* Not including null-terminator. */
2368 int m_display_cols;
2369 const cpp_char_column_policy &m_policy;
2370 size_t m_alloc_sz;
2373 /* Ensure that m_text can hold a string of length LEN
2374 (plus 1 for 0-termination). */
2376 void
2377 correction::ensure_capacity (size_t len)
2379 /* Allow 1 extra byte for 0-termination. */
2380 if (m_alloc_sz < (len + 1))
2382 size_t new_alloc_sz = (len + 1) * 2;
2383 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2384 m_alloc_sz = new_alloc_sz;
2388 /* Ensure that m_text is 0-terminated. */
2390 void
2391 correction::ensure_terminated ()
2393 /* 0-terminate the buffer. */
2394 gcc_assert (m_byte_length < m_alloc_sz);
2395 m_text[m_byte_length] = '\0';
2398 /* A list of corrections affecting a particular line.
2399 This is used by layout::print_trailing_fixits for planning
2400 how to print the fix-it hints affecting the line. */
2402 class line_corrections
2404 public:
2405 line_corrections (const char_display_policy &policy,
2406 const char *filename,
2407 linenum_type row)
2408 : m_policy (policy), m_filename (filename), m_row (row)
2410 ~line_corrections ();
2412 void add_hint (const fixit_hint *hint);
2414 const char_display_policy &m_policy;
2415 const char *m_filename;
2416 linenum_type m_row;
2417 auto_vec <correction *> m_corrections;
2420 /* struct line_corrections. */
2422 line_corrections::~line_corrections ()
2424 unsigned i;
2425 correction *c;
2426 FOR_EACH_VEC_ELT (m_corrections, i, c)
2427 delete c;
2430 /* A struct wrapping a particular source line, allowing
2431 run-time bounds-checking of accesses in a checked build. */
2433 class source_line
2435 public:
2436 source_line (const char *filename, int line);
2438 char_span as_span () { return char_span (chars, width); }
2440 const char *chars;
2441 int width;
2444 /* source_line's ctor. */
2446 source_line::source_line (const char *filename, int line)
2448 char_span span = location_get_source_line (filename, line);
2449 chars = span.get_buffer ();
2450 width = span.length ();
2453 /* Add HINT to the corrections for this line.
2454 Attempt to consolidate nearby hints so that they will not
2455 overlap with printed. */
2457 void
2458 line_corrections::add_hint (const fixit_hint *hint)
2460 column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES);
2461 column_range affected_columns = get_affected_range (m_policy, hint,
2462 CU_DISPLAY_COLS);
2463 column_range printed_columns = get_printed_columns (m_policy, hint);
2465 /* Potentially consolidate. */
2466 if (!m_corrections.is_empty ())
2468 correction *last_correction
2469 = m_corrections[m_corrections.length () - 1];
2471 /* The following consolidation code assumes that the fix-it hints
2472 have been sorted by start (done within layout's ctor). */
2473 gcc_assert (affected_bytes.start
2474 >= last_correction->m_affected_bytes.start);
2475 gcc_assert (printed_columns.start
2476 >= last_correction->m_printed_columns.start);
2478 if (printed_columns.start <= last_correction->m_printed_columns.finish
2479 && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2480 affected_bytes.start - 1))
2482 /* We have two hints for which the printed forms of the hints
2483 would touch or overlap, so we need to consolidate them to avoid
2484 confusing the user.
2485 Attempt to inject a "replace" correction from immediately
2486 after the end of the last hint to immediately before the start
2487 of the next hint. */
2488 column_range between (last_correction->m_affected_bytes.finish + 1,
2489 affected_bytes.start - 1);
2491 /* Try to read the source. */
2492 source_line line (m_filename, m_row);
2493 if (line.chars && between.finish < line.width)
2495 /* Consolidate into the last correction:
2496 add a no-op "replace" of the "between" text, and
2497 add the text from the new hint. */
2498 int old_byte_len = last_correction->m_byte_length;
2499 gcc_assert (old_byte_len >= 0);
2500 int between_byte_len = between.finish + 1 - between.start;
2501 gcc_assert (between_byte_len >= 0);
2502 int new_byte_len
2503 = old_byte_len + between_byte_len + hint->get_length ();
2504 gcc_assert (new_byte_len >= 0);
2505 last_correction->ensure_capacity (new_byte_len);
2506 last_correction->overwrite
2507 (old_byte_len,
2508 line.as_span ().subspan (between.start - 1,
2509 between.finish + 1 - between.start));
2510 last_correction->overwrite (old_byte_len + between_byte_len,
2511 char_span (hint->get_string (),
2512 hint->get_length ()));
2513 last_correction->m_byte_length = new_byte_len;
2514 last_correction->ensure_terminated ();
2515 last_correction->m_affected_bytes.finish
2516 = affected_bytes.finish;
2517 last_correction->m_affected_columns.finish
2518 = affected_columns.finish;
2519 int prev_display_cols = last_correction->m_display_cols;
2520 last_correction->compute_display_cols ();
2521 last_correction->m_printed_columns.finish
2522 += last_correction->m_display_cols - prev_display_cols;
2523 return;
2528 /* If no consolidation happened, add a new correction instance. */
2529 m_corrections.safe_push (new correction (affected_bytes,
2530 affected_columns,
2531 printed_columns,
2532 hint->get_string (),
2533 hint->get_length (),
2534 m_policy));
2537 /* If there are any fixit hints on source line ROW, print them.
2538 They are printed in order, attempting to combine them onto lines, but
2539 starting new lines if necessary.
2540 Fix-it hints that insert new lines are handled separately,
2541 in layout::print_leading_fixits. */
2543 void
2544 layout::print_trailing_fixits (linenum_type row)
2546 /* Build a list of correction instances for the line,
2547 potentially consolidating hints (for the sake of readability). */
2548 line_corrections corrections (m_policy, m_exploc.file, row);
2549 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2551 const fixit_hint *hint = m_fixit_hints[i];
2553 /* Newline fixits are handled by layout::print_leading_fixits. */
2554 if (hint->ends_with_newline_p ())
2555 continue;
2557 if (hint->affects_line_p (m_exploc.file, row))
2558 corrections.add_hint (hint);
2561 /* Now print the corrections. */
2562 unsigned i;
2563 correction *c;
2564 int column = m_x_offset_display;
2566 if (!corrections.m_corrections.is_empty ())
2567 start_annotation_line ();
2569 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2571 /* For now we assume each fixit hint can only touch one line. */
2572 if (c->insertion_p ())
2574 /* This assumes the insertion just affects one line. */
2575 int start_column = c->m_printed_columns.start;
2576 move_to_column (&column, start_column, true);
2577 m_colorizer.set_fixit_insert ();
2578 pp_string (m_pp, c->m_text);
2579 m_colorizer.set_normal_text ();
2580 column += c->m_display_cols;
2582 else
2584 /* If the range of the replacement wasn't printed in the
2585 annotation line, then print an extra underline to
2586 indicate exactly what is being replaced.
2587 Always show it for removals. */
2588 int start_column = c->m_affected_columns.start;
2589 int finish_column = c->m_affected_columns.finish;
2590 if (!annotation_line_showed_range_p (row, start_column,
2591 finish_column)
2592 || c->m_byte_length == 0)
2594 move_to_column (&column, start_column, true);
2595 m_colorizer.set_fixit_delete ();
2596 for (; column <= finish_column; column++)
2597 pp_character (m_pp, '-');
2598 m_colorizer.set_normal_text ();
2600 /* Print the replacement text. REPLACE also covers
2601 removals, so only do this extra work (potentially starting
2602 a new line) if we have actual replacement text. */
2603 if (c->m_byte_length > 0)
2605 move_to_column (&column, start_column, true);
2606 m_colorizer.set_fixit_insert ();
2607 pp_string (m_pp, c->m_text);
2608 m_colorizer.set_normal_text ();
2609 column += c->m_display_cols;
2614 /* Add a trailing newline, if necessary. */
2615 move_to_column (&column, 0, false);
2618 /* Disable any colorization and emit a newline. */
2620 void
2621 layout::print_newline ()
2623 m_colorizer.set_normal_text ();
2624 pp_newline (m_pp);
2627 /* Return true if (ROW/COLUMN) is within a range of the layout.
2628 If it returns true, OUT_STATE is written to, with the
2629 range index, and whether we should draw the caret at
2630 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2631 whether all inputs and outputs are in bytes or display column units. */
2633 bool
2634 layout::get_state_at_point (/* Inputs. */
2635 linenum_type row, int column,
2636 int first_non_ws, int last_non_ws,
2637 enum column_unit col_unit,
2638 /* Outputs. */
2639 point_state *out_state)
2641 layout_range *range;
2642 int i;
2643 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2645 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2646 /* Bail out early, so that such ranges don't affect underlining or
2647 source colorization. */
2648 continue;
2650 if (range->contains_point (row, column, col_unit))
2652 out_state->range_idx = i;
2654 /* Are we at the range's caret? is it visible? */
2655 out_state->draw_caret_p = false;
2656 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2657 && row == range->m_caret.m_line
2658 && column == range->m_caret.m_columns[col_unit])
2659 out_state->draw_caret_p = true;
2661 /* Within a multiline range, don't display any underline
2662 in any leading or trailing whitespace on a line.
2663 We do display carets, however. */
2664 if (!out_state->draw_caret_p)
2665 if (column < first_non_ws || column > last_non_ws)
2666 return false;
2668 /* We are within a range. */
2669 return true;
2673 return false;
2676 /* Helper function for use by layout::print_line when printing the
2677 annotation line under the source line.
2678 Get the display column beyond the rightmost one that could contain a caret
2679 or range marker, given that we stop rendering at trailing whitespace.
2680 ROW is the source line within the given file.
2681 CARET_COLUMN is the display column of range 0's caret.
2682 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2683 character of source (as determined when printing the source line). */
2686 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2687 int last_non_ws_column)
2689 int result = caret_column + 1;
2691 layout_range *range;
2692 int i;
2693 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2695 if (row >= range->m_start.m_line)
2697 if (range->m_finish.m_line == row)
2699 /* On the final line within a range; ensure that
2700 we render up to the end of the range. */
2701 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2702 if (result <= disp_col)
2703 result = disp_col + 1;
2705 else if (row < range->m_finish.m_line)
2707 /* Within a multiline range; ensure that we render up to the
2708 last non-whitespace column. */
2709 if (result <= last_non_ws_column)
2710 result = last_non_ws_column + 1;
2715 return result;
2718 /* Given *COLUMN as an x-coordinate, print spaces to position
2719 successive output at DEST_COLUMN, printing a newline if necessary,
2720 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2721 left margin after any newline. */
2723 void
2724 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2726 /* Start a new line if we need to. */
2727 if (*column > dest_column)
2729 print_newline ();
2730 if (add_left_margin)
2731 start_annotation_line ();
2732 *column = m_x_offset_display;
2735 while (*column < dest_column)
2737 pp_space (m_pp);
2738 (*column)++;
2742 /* For debugging layout issues, render a ruler giving column numbers
2743 (after the 1-column indent). */
2745 void
2746 layout::show_ruler (int max_column) const
2748 /* Hundreds. */
2749 if (max_column > 99)
2751 start_annotation_line ();
2752 pp_space (m_pp);
2753 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2754 if (column % 10 == 0)
2755 pp_character (m_pp, '0' + (column / 100) % 10);
2756 else
2757 pp_space (m_pp);
2758 pp_newline (m_pp);
2761 /* Tens. */
2762 start_annotation_line ();
2763 pp_space (m_pp);
2764 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2765 if (column % 10 == 0)
2766 pp_character (m_pp, '0' + (column / 10) % 10);
2767 else
2768 pp_space (m_pp);
2769 pp_newline (m_pp);
2771 /* Units. */
2772 start_annotation_line ();
2773 pp_space (m_pp);
2774 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2775 pp_character (m_pp, '0' + (column % 10));
2776 pp_newline (m_pp);
2779 /* Print leading fix-its (for new lines inserted before the source line)
2780 then the source line, followed by an annotation line
2781 consisting of any caret/underlines, then any fixits.
2782 If the source line can't be read, print nothing. */
2783 void
2784 layout::print_line (linenum_type row)
2786 char_span line = location_get_source_line (m_exploc.file, row);
2787 if (!line)
2788 return;
2790 print_leading_fixits (row);
2791 const line_bounds lbounds
2792 = print_source_line (row, line.get_buffer (), line.length ());
2793 if (should_print_annotation_line_p (row))
2794 print_annotation_line (row, lbounds);
2795 if (m_show_labels_p)
2796 print_any_labels (row);
2797 print_trailing_fixits (row);
2800 } /* End of anonymous namespace. */
2802 /* If LOC is within the spans of lines that will already be printed for
2803 this gcc_rich_location, then add it as a secondary location and return true.
2805 Otherwise return false. */
2807 bool
2808 gcc_rich_location::add_location_if_nearby (location_t loc,
2809 bool restrict_to_current_line_spans,
2810 const range_label *label)
2812 /* Use the layout location-handling logic to sanitize LOC,
2813 filtering it to the current line spans within a temporary
2814 layout instance. */
2815 layout layout (global_dc, this, DK_ERROR);
2816 location_range loc_range;
2817 loc_range.m_loc = loc;
2818 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2819 if (!layout.maybe_add_location_range (&loc_range, 0,
2820 restrict_to_current_line_spans))
2821 return false;
2823 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2824 return true;
2827 /* Print the physical source code corresponding to the location of
2828 this diagnostic, with additional annotations. */
2830 void
2831 diagnostic_show_locus (diagnostic_context * context,
2832 rich_location *richloc,
2833 diagnostic_t diagnostic_kind)
2835 location_t loc = richloc->get_loc ();
2836 /* Do nothing if source-printing has been disabled. */
2837 if (!context->show_caret)
2838 return;
2840 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2841 if (loc <= BUILTINS_LOCATION)
2842 return;
2844 /* Don't print the same source location twice in a row, unless we have
2845 fix-it hints, or multiple locations, or a label. */
2846 if (loc == context->last_location
2847 && richloc->get_num_fixit_hints () == 0
2848 && richloc->get_num_locations () == 1
2849 && richloc->get_range (0)->m_label == NULL)
2850 return;
2852 context->last_location = loc;
2854 layout layout (context, richloc, diagnostic_kind);
2855 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2856 line_span_idx++)
2858 const line_span *line_span = layout.get_line_span (line_span_idx);
2859 if (context->show_line_numbers_p)
2861 /* With line numbers, we should show whenever the line-numbering
2862 "jumps". */
2863 if (line_span_idx > 0)
2864 layout.print_gap_in_line_numbering ();
2866 else
2868 /* Without line numbers, we print headings for some line spans. */
2869 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2871 expanded_location exploc
2872 = layout.get_expanded_location (line_span);
2873 context->start_span (context, exploc);
2876 /* Iterate over the lines within this span (using linenum_arith_t to
2877 avoid overflow with 0xffffffff causing an infinite loop). */
2878 linenum_arith_t last_line = line_span->get_last_line ();
2879 for (linenum_arith_t row = line_span->get_first_line ();
2880 row <= last_line; row++)
2881 layout.print_line (row);
2885 #if CHECKING_P
2887 namespace selftest {
2889 /* Selftests for diagnostic_show_locus. */
2891 /* Verify that cpp_display_width correctly handles escaping. */
2893 static void
2894 test_display_widths ()
2896 gcc_rich_location richloc (UNKNOWN_LOCATION);
2898 /* U+03C0 "GREEK SMALL LETTER PI". */
2899 const char *pi = "\xCF\x80";
2900 /* U+1F642 "SLIGHTLY SMILING FACE". */
2901 const char *emoji = "\xF0\x9F\x99\x82";
2902 /* Stray trailing byte of a UTF-8 character. */
2903 const char *stray = "\xBF";
2904 /* U+10FFFF. */
2905 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2907 /* No escaping. */
2909 test_diagnostic_context dc;
2910 char_display_policy policy (make_policy (dc, richloc));
2911 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2912 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2913 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2914 /* Don't check width of U+10FFFF; it's in a private use plane. */
2917 richloc.set_escape_on_output (true);
2920 test_diagnostic_context dc;
2921 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
2922 char_display_policy policy (make_policy (dc, richloc));
2923 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2924 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2925 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2926 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2927 policy),
2928 strlen ("<U+10FFFF>"));
2932 test_diagnostic_context dc;
2933 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
2934 char_display_policy policy (make_policy (dc, richloc));
2935 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2936 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2937 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2938 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2939 policy),
2940 16);
2944 /* For precise tests of the layout, make clear where the source line will
2945 start. test_left_margin sets the total byte count from the left side of the
2946 screen to the start of source lines, after the line number and the separator,
2947 which consists of the three characters " | ". */
2948 static const int test_linenum_sep = 3;
2949 static const int test_left_margin = 7;
2951 /* Helper function for test_layout_x_offset_display_utf8(). */
2952 static void
2953 test_offset_impl (int caret_byte_col, int max_width,
2954 int expected_x_offset_display,
2955 int left_margin = test_left_margin)
2957 test_diagnostic_context dc;
2958 dc.caret_max_width = max_width;
2959 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2960 the line number plus one space after. */
2961 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2962 dc.show_line_numbers_p = true;
2963 rich_location richloc (line_table,
2964 linemap_position_for_column (line_table,
2965 caret_byte_col));
2966 layout test_layout (&dc, &richloc, DK_ERROR);
2967 ASSERT_EQ (left_margin - test_linenum_sep,
2968 test_layout.get_linenum_width ());
2969 ASSERT_EQ (expected_x_offset_display,
2970 test_layout.get_x_offset_display ());
2973 /* Test that layout::calculate_x_offset_display() works. */
2974 static void
2975 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2978 const char *content
2979 = "This line is very long, so that we can use it to test the logic for "
2980 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2981 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2982 "column #102.\n";
2984 /* Number of bytes in the line, subtracting one to remove the newline. */
2985 const int line_bytes = strlen (content) - 1;
2987 /* Number of display columns occupied by the line; each of the 2 emojis
2988 takes up 2 fewer display columns than it does bytes. */
2989 const int line_display_cols = line_bytes - 2*2;
2991 /* The column of the first emoji. Byte or display is the same as there are
2992 no multibyte characters earlier on the line. */
2993 const int emoji_col = 102;
2995 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2996 line_table_test ltt (case_);
2998 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3000 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3002 /* Don't attempt to run the tests if column data might be unavailable. */
3003 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3004 return;
3006 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3007 ASSERT_EQ (1, LOCATION_LINE (line_end));
3008 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3010 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3011 ASSERT_EQ (line_display_cols,
3012 cpp_display_width (lspan.get_buffer (), lspan.length (),
3013 def_policy ()));
3014 ASSERT_EQ (line_display_cols,
3015 location_compute_display_column (expand_location (line_end),
3016 def_policy ()));
3017 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3018 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3020 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3022 /* No constraint on the width -> no offset. */
3023 test_offset_impl (emoji_col, 0, 0);
3025 /* Caret is before the beginning -> no offset. */
3026 test_offset_impl (0, 100, 0);
3028 /* Caret is past the end of the line -> no offset. */
3029 test_offset_impl (line_bytes+1, 100, 0);
3031 /* Line fits in the display -> no offset. */
3032 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3033 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3035 /* Line is too long for the display but caret location is OK
3036 anyway -> no offset. */
3037 static const int small_width = 24;
3038 test_offset_impl (1, small_width, 0);
3040 /* Width constraint is very small -> no offset. */
3041 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3043 /* Line would be offset, but due to large line numbers, offsetting
3044 would remove the whole line -> no offset. */
3045 static const int huge_left_margin = 100;
3046 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3048 /* Line is the same length as the display, but the line number makes it too
3049 long, so offset is required. Caret is at the end so padding on the right
3050 is not in effect. */
3051 for (int excess = 1; excess <= 3; ++excess)
3052 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3053 excess);
3055 /* Line is much too long for the display, caret is near the end ->
3056 offset should be such that the line fits in the display and caret
3057 remains the same distance from the end that it was. */
3058 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3059 caret_offset <= max_offset; ++caret_offset)
3060 test_offset_impl (line_bytes - caret_offset, small_width,
3061 line_display_cols + test_left_margin - small_width);
3063 /* As previous case but caret is closer to the middle; now we want it to end
3064 up CARET_LINE_MARGIN bytes from the end. */
3065 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3066 test_offset_impl (emoji_col, small_width,
3067 emoji_col + test_left_margin
3068 - (small_width - CARET_LINE_MARGIN));
3070 /* Test that the source line is offset as expected when printed. */
3072 test_diagnostic_context dc;
3073 dc.caret_max_width = small_width - 6;
3074 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3075 dc.show_line_numbers_p = true;
3076 dc.show_ruler_p = true;
3077 rich_location richloc (line_table,
3078 linemap_position_for_column (line_table,
3079 emoji_col));
3080 layout test_layout (&dc, &richloc, DK_ERROR);
3081 test_layout.print_line (1);
3082 ASSERT_STREQ (" | 1 \n"
3083 " | 1 \n"
3084 " | 234567890123456789\n"
3085 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3086 "that occupies 8 bytes and 4 display columns, starting at "
3087 "column #102.\n"
3088 " | ^\n\n",
3089 pp_formatted_text (dc.printer));
3092 /* Similar to the previous example, but now the offset called for would split
3093 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3094 it with a padding space in this case. */
3096 test_diagnostic_context dc;
3097 dc.caret_max_width = small_width - 5;
3098 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3099 dc.show_line_numbers_p = true;
3100 dc.show_ruler_p = true;
3101 rich_location richloc (line_table,
3102 linemap_position_for_column (line_table,
3103 emoji_col + 2));
3104 layout test_layout (&dc, &richloc, DK_ERROR);
3105 test_layout.print_line (1);
3106 ASSERT_STREQ (" | 1 1 \n"
3107 " | 1 2 \n"
3108 " | 3456789012345678901\n"
3109 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3110 "that occupies 8 bytes and 4 display columns, starting at "
3111 "column #102.\n"
3112 " | ^\n\n",
3113 pp_formatted_text (dc.printer));
3118 static void
3119 test_layout_x_offset_display_tab (const line_table_case &case_)
3121 const char *content
3122 = "This line is very long, so that we can use it to test the logic for "
3123 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3124 "a variable number of display columns, starting at column #103.\n";
3126 /* Number of bytes in the line, subtracting one to remove the newline. */
3127 const int line_bytes = strlen (content) - 1;
3129 /* The column where the tab begins. Byte or display is the same as there are
3130 no multibyte characters earlier on the line. */
3131 const int tab_col = 103;
3133 /* Effective extra size of the tab beyond what a single space would have taken
3134 up, indexed by tabstop. */
3135 static const int num_tabstops = 11;
3136 int extra_width[num_tabstops];
3137 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3139 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3140 extra_width[tabstop] = this_tab_size - 1;
3142 /* Example of this calculation: if tabstop is 10, the tab starting at column
3143 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3144 next character is at column #111. So it takes up 7 more columns than
3145 a space would have taken up. */
3146 ASSERT_EQ (7, extra_width[10]);
3148 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3149 line_table_test ltt (case_);
3151 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3153 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3155 /* Don't attempt to run the tests if column data might be unavailable. */
3156 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3157 return;
3159 /* Check that cpp_display_width handles the tabs as expected. */
3160 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3161 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3162 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3164 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3165 ASSERT_EQ (line_bytes + extra_width[tabstop],
3166 cpp_display_width (lspan.get_buffer (), lspan.length (),
3167 policy));
3168 ASSERT_EQ (line_bytes + extra_width[tabstop],
3169 location_compute_display_column (expand_location (line_end),
3170 policy));
3173 /* Check that the tab is expanded to the expected number of spaces. */
3174 rich_location richloc (line_table,
3175 linemap_position_for_column (line_table,
3176 tab_col + 1));
3177 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3179 test_diagnostic_context dc;
3180 dc.tabstop = tabstop;
3181 layout test_layout (&dc, &richloc, DK_ERROR);
3182 test_layout.print_line (1);
3183 const char *out = pp_formatted_text (dc.printer);
3184 ASSERT_EQ (NULL, strchr (out, '\t'));
3185 const char *left_quote = strchr (out, '`');
3186 const char *right_quote = strchr (out, '\'');
3187 ASSERT_NE (NULL, left_quote);
3188 ASSERT_NE (NULL, right_quote);
3189 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3192 /* Check that the line is offset properly and that the tab is broken up
3193 into the expected number of spaces when it is the last character skipped
3194 over. */
3195 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3197 test_diagnostic_context dc;
3198 dc.tabstop = tabstop;
3199 static const int small_width = 24;
3200 dc.caret_max_width = small_width - 4;
3201 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3202 dc.show_line_numbers_p = true;
3203 layout test_layout (&dc, &richloc, DK_ERROR);
3204 test_layout.print_line (1);
3206 /* We have arranged things so that two columns will be printed before
3207 the caret. If the tab results in more than one space, this should
3208 produce two spaces in the output; otherwise, it will be a single space
3209 preceded by the opening quote before the tab character. */
3210 const char *output1
3211 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3212 "display columns, starting at column #103.\n"
3213 " | ^\n\n";
3214 const char *output2
3215 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3216 "display columns, starting at column #103.\n"
3217 " | ^\n\n";
3218 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3219 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3224 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3226 static void
3227 test_diagnostic_show_locus_unknown_location ()
3229 test_diagnostic_context dc;
3230 rich_location richloc (line_table, UNKNOWN_LOCATION);
3231 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3232 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3235 /* Verify that diagnostic_show_locus works sanely for various
3236 single-line cases.
3238 All of these work on the following 1-line source file:
3239 .0000000001111111
3240 .1234567890123456
3241 "foo = bar.field;\n"
3242 which is set up by test_diagnostic_show_locus_one_liner and calls
3243 them. */
3245 /* Just a caret. */
3247 static void
3248 test_one_liner_simple_caret ()
3250 test_diagnostic_context dc;
3251 location_t caret = linemap_position_for_column (line_table, 10);
3252 rich_location richloc (line_table, caret);
3253 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3254 ASSERT_STREQ (" foo = bar.field;\n"
3255 " ^\n",
3256 pp_formatted_text (dc.printer));
3259 /* Caret and range. */
3261 static void
3262 test_one_liner_caret_and_range ()
3264 test_diagnostic_context dc;
3265 location_t caret = linemap_position_for_column (line_table, 10);
3266 location_t start = linemap_position_for_column (line_table, 7);
3267 location_t finish = linemap_position_for_column (line_table, 15);
3268 location_t loc = make_location (caret, start, finish);
3269 rich_location richloc (line_table, loc);
3270 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3271 ASSERT_STREQ (" foo = bar.field;\n"
3272 " ~~~^~~~~~\n",
3273 pp_formatted_text (dc.printer));
3276 /* Multiple ranges and carets. */
3278 static void
3279 test_one_liner_multiple_carets_and_ranges ()
3281 test_diagnostic_context dc;
3282 location_t foo
3283 = make_location (linemap_position_for_column (line_table, 2),
3284 linemap_position_for_column (line_table, 1),
3285 linemap_position_for_column (line_table, 3));
3286 dc.caret_chars[0] = 'A';
3288 location_t bar
3289 = make_location (linemap_position_for_column (line_table, 8),
3290 linemap_position_for_column (line_table, 7),
3291 linemap_position_for_column (line_table, 9));
3292 dc.caret_chars[1] = 'B';
3294 location_t field
3295 = make_location (linemap_position_for_column (line_table, 13),
3296 linemap_position_for_column (line_table, 11),
3297 linemap_position_for_column (line_table, 15));
3298 dc.caret_chars[2] = 'C';
3300 rich_location richloc (line_table, foo);
3301 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3302 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3303 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3304 ASSERT_STREQ (" foo = bar.field;\n"
3305 " ~A~ ~B~ ~~C~~\n",
3306 pp_formatted_text (dc.printer));
3309 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3311 static void
3312 test_one_liner_fixit_insert_before ()
3314 test_diagnostic_context dc;
3315 location_t caret = linemap_position_for_column (line_table, 7);
3316 rich_location richloc (line_table, caret);
3317 richloc.add_fixit_insert_before ("&");
3318 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3319 ASSERT_STREQ (" foo = bar.field;\n"
3320 " ^\n"
3321 " &\n",
3322 pp_formatted_text (dc.printer));
3325 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3327 static void
3328 test_one_liner_fixit_insert_after ()
3330 test_diagnostic_context dc;
3331 location_t start = linemap_position_for_column (line_table, 1);
3332 location_t finish = linemap_position_for_column (line_table, 3);
3333 location_t foo = make_location (start, start, finish);
3334 rich_location richloc (line_table, foo);
3335 richloc.add_fixit_insert_after ("[0]");
3336 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3337 ASSERT_STREQ (" foo = bar.field;\n"
3338 " ^~~\n"
3339 " [0]\n",
3340 pp_formatted_text (dc.printer));
3343 /* Removal fix-it hint: removal of the ".field".
3344 Also verify the interaction of pp_set_prefix with rulers and
3345 fix-it hints. */
3347 static void
3348 test_one_liner_fixit_remove ()
3350 location_t start = linemap_position_for_column (line_table, 10);
3351 location_t finish = linemap_position_for_column (line_table, 15);
3352 location_t dot = make_location (start, start, finish);
3353 rich_location richloc (line_table, dot);
3354 richloc.add_fixit_remove ();
3356 /* Normal. */
3358 test_diagnostic_context dc;
3359 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3360 ASSERT_STREQ (" foo = bar.field;\n"
3361 " ^~~~~~\n"
3362 " ------\n",
3363 pp_formatted_text (dc.printer));
3366 /* Test of adding a prefix. */
3368 test_diagnostic_context dc;
3369 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3370 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3371 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3372 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3373 "TEST PREFIX: ^~~~~~\n"
3374 "TEST PREFIX: ------\n",
3375 pp_formatted_text (dc.printer));
3378 /* Normal, with ruler. */
3380 test_diagnostic_context dc;
3381 dc.show_ruler_p = true;
3382 dc.caret_max_width = 104;
3383 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3384 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3385 " 1 2 3 4 5 6 7 8 9 0 \n"
3386 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3387 " foo = bar.field;\n"
3388 " ^~~~~~\n"
3389 " ------\n",
3390 pp_formatted_text (dc.printer));
3393 /* Test of adding a prefix, with ruler. */
3395 test_diagnostic_context dc;
3396 dc.show_ruler_p = true;
3397 dc.caret_max_width = 50;
3398 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3399 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3400 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3401 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3402 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3403 "TEST PREFIX: foo = bar.field;\n"
3404 "TEST PREFIX: ^~~~~~\n"
3405 "TEST PREFIX: ------\n",
3406 pp_formatted_text (dc.printer));
3409 /* Test of adding a prefix, with ruler and line numbers. */
3411 test_diagnostic_context dc;
3412 dc.show_ruler_p = true;
3413 dc.caret_max_width = 50;
3414 dc.show_line_numbers_p = true;
3415 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3416 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3417 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3418 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3419 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3420 "TEST PREFIX: 1 | foo = bar.field;\n"
3421 "TEST PREFIX: | ^~~~~~\n"
3422 "TEST PREFIX: | ------\n",
3423 pp_formatted_text (dc.printer));
3427 /* Replace fix-it hint: replacing "field" with "m_field". */
3429 static void
3430 test_one_liner_fixit_replace ()
3432 test_diagnostic_context dc;
3433 location_t start = linemap_position_for_column (line_table, 11);
3434 location_t finish = linemap_position_for_column (line_table, 15);
3435 location_t field = make_location (start, start, finish);
3436 rich_location richloc (line_table, field);
3437 richloc.add_fixit_replace ("m_field");
3438 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3439 ASSERT_STREQ (" foo = bar.field;\n"
3440 " ^~~~~\n"
3441 " m_field\n",
3442 pp_formatted_text (dc.printer));
3445 /* Replace fix-it hint: replacing "field" with "m_field",
3446 but where the caret was elsewhere. */
3448 static void
3449 test_one_liner_fixit_replace_non_equal_range ()
3451 test_diagnostic_context dc;
3452 location_t equals = linemap_position_for_column (line_table, 5);
3453 location_t start = linemap_position_for_column (line_table, 11);
3454 location_t finish = linemap_position_for_column (line_table, 15);
3455 rich_location richloc (line_table, equals);
3456 source_range range;
3457 range.m_start = start;
3458 range.m_finish = finish;
3459 richloc.add_fixit_replace (range, "m_field");
3460 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3461 /* The replacement range is not indicated in the annotation line, so
3462 it should be indicated via an additional underline. */
3463 ASSERT_STREQ (" foo = bar.field;\n"
3464 " ^\n"
3465 " -----\n"
3466 " m_field\n",
3467 pp_formatted_text (dc.printer));
3470 /* Replace fix-it hint: replacing "field" with "m_field",
3471 where the caret was elsewhere, but where a secondary range
3472 exactly covers "field". */
3474 static void
3475 test_one_liner_fixit_replace_equal_secondary_range ()
3477 test_diagnostic_context dc;
3478 location_t equals = linemap_position_for_column (line_table, 5);
3479 location_t start = linemap_position_for_column (line_table, 11);
3480 location_t finish = linemap_position_for_column (line_table, 15);
3481 rich_location richloc (line_table, equals);
3482 location_t field = make_location (start, start, finish);
3483 richloc.add_range (field);
3484 richloc.add_fixit_replace (field, "m_field");
3485 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3486 /* The replacement range is indicated in the annotation line,
3487 so it shouldn't be indicated via an additional underline. */
3488 ASSERT_STREQ (" foo = bar.field;\n"
3489 " ^ ~~~~~\n"
3490 " m_field\n",
3491 pp_formatted_text (dc.printer));
3494 /* Verify that we can use ad-hoc locations when adding fixits to a
3495 rich_location. */
3497 static void
3498 test_one_liner_fixit_validation_adhoc_locations ()
3500 /* Generate a range that's too long to be packed, so must
3501 be stored as an ad-hoc location (given the defaults
3502 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3503 const location_t c7 = linemap_position_for_column (line_table, 7);
3504 const location_t c47 = linemap_position_for_column (line_table, 47);
3505 const location_t loc = make_location (c7, c7, c47);
3507 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3508 return;
3510 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3512 /* Insert. */
3514 rich_location richloc (line_table, loc);
3515 richloc.add_fixit_insert_before (loc, "test");
3516 /* It should not have been discarded by the validator. */
3517 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3519 test_diagnostic_context dc;
3520 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3521 ASSERT_STREQ (" foo = bar.field;\n"
3522 " ^~~~~~~~~~ \n"
3523 " test\n",
3524 pp_formatted_text (dc.printer));
3527 /* Remove. */
3529 rich_location richloc (line_table, loc);
3530 source_range range = source_range::from_locations (loc, c47);
3531 richloc.add_fixit_remove (range);
3532 /* It should not have been discarded by the validator. */
3533 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3535 test_diagnostic_context dc;
3536 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3537 ASSERT_STREQ (" foo = bar.field;\n"
3538 " ^~~~~~~~~~ \n"
3539 " -----------------------------------------\n",
3540 pp_formatted_text (dc.printer));
3543 /* Replace. */
3545 rich_location richloc (line_table, loc);
3546 source_range range = source_range::from_locations (loc, c47);
3547 richloc.add_fixit_replace (range, "test");
3548 /* It should not have been discarded by the validator. */
3549 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3551 test_diagnostic_context dc;
3552 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3553 ASSERT_STREQ (" foo = bar.field;\n"
3554 " ^~~~~~~~~~ \n"
3555 " test\n",
3556 pp_formatted_text (dc.printer));
3560 /* Test of consolidating insertions at the same location. */
3562 static void
3563 test_one_liner_many_fixits_1 ()
3565 test_diagnostic_context dc;
3566 location_t equals = linemap_position_for_column (line_table, 5);
3567 rich_location richloc (line_table, equals);
3568 for (int i = 0; i < 19; i++)
3569 richloc.add_fixit_insert_before ("a");
3570 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3571 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3572 ASSERT_STREQ (" foo = bar.field;\n"
3573 " ^\n"
3574 " aaaaaaaaaaaaaaaaaaa\n",
3575 pp_formatted_text (dc.printer));
3578 /* Ensure that we can add an arbitrary number of fix-it hints to a
3579 rich_location, even if they are not consolidated. */
3581 static void
3582 test_one_liner_many_fixits_2 ()
3584 test_diagnostic_context dc;
3585 location_t equals = linemap_position_for_column (line_table, 5);
3586 rich_location richloc (line_table, equals);
3587 for (int i = 0; i < 19; i++)
3589 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3590 richloc.add_fixit_insert_before (loc, "a");
3592 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3593 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3594 ASSERT_STREQ (" foo = bar.field;\n"
3595 " ^\n"
3596 " a a a a a a a a a a a a a a a a a a a\n",
3597 pp_formatted_text (dc.printer));
3600 /* Test of labeling the ranges within a rich_location. */
3602 static void
3603 test_one_liner_labels ()
3605 location_t foo
3606 = make_location (linemap_position_for_column (line_table, 1),
3607 linemap_position_for_column (line_table, 1),
3608 linemap_position_for_column (line_table, 3));
3609 location_t bar
3610 = make_location (linemap_position_for_column (line_table, 7),
3611 linemap_position_for_column (line_table, 7),
3612 linemap_position_for_column (line_table, 9));
3613 location_t field
3614 = make_location (linemap_position_for_column (line_table, 11),
3615 linemap_position_for_column (line_table, 11),
3616 linemap_position_for_column (line_table, 15));
3618 /* Example where all the labels fit on one line. */
3620 text_range_label label0 ("0");
3621 text_range_label label1 ("1");
3622 text_range_label label2 ("2");
3623 gcc_rich_location richloc (foo, &label0);
3624 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3625 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3628 test_diagnostic_context dc;
3629 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3630 ASSERT_STREQ (" foo = bar.field;\n"
3631 " ^~~ ~~~ ~~~~~\n"
3632 " | | |\n"
3633 " 0 1 2\n",
3634 pp_formatted_text (dc.printer));
3637 /* Verify that we can disable label-printing. */
3639 test_diagnostic_context dc;
3640 dc.show_labels_p = false;
3641 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3642 ASSERT_STREQ (" foo = bar.field;\n"
3643 " ^~~ ~~~ ~~~~~\n",
3644 pp_formatted_text (dc.printer));
3648 /* Example where the labels need extra lines. */
3650 text_range_label label0 ("label 0");
3651 text_range_label label1 ("label 1");
3652 text_range_label label2 ("label 2");
3653 gcc_rich_location richloc (foo, &label0);
3654 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3655 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3657 test_diagnostic_context dc;
3658 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3659 ASSERT_STREQ (" foo = bar.field;\n"
3660 " ^~~ ~~~ ~~~~~\n"
3661 " | | |\n"
3662 " | | label 2\n"
3663 " | label 1\n"
3664 " label 0\n",
3665 pp_formatted_text (dc.printer));
3668 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3669 but label 1 just touches label 2. */
3671 text_range_label label0 ("aaaaa");
3672 text_range_label label1 ("bbbb");
3673 text_range_label label2 ("c");
3674 gcc_rich_location richloc (foo, &label0);
3675 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3676 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3678 test_diagnostic_context dc;
3679 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3680 ASSERT_STREQ (" foo = bar.field;\n"
3681 " ^~~ ~~~ ~~~~~\n"
3682 " | | |\n"
3683 " | | c\n"
3684 " aaaaa bbbb\n",
3685 pp_formatted_text (dc.printer));
3688 /* Example of out-of-order ranges (thus requiring a sort). */
3690 text_range_label label0 ("0");
3691 text_range_label label1 ("1");
3692 text_range_label label2 ("2");
3693 gcc_rich_location richloc (field, &label0);
3694 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3695 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3697 test_diagnostic_context dc;
3698 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3699 ASSERT_STREQ (" foo = bar.field;\n"
3700 " ~~~ ~~~ ^~~~~\n"
3701 " | | |\n"
3702 " 2 1 0\n",
3703 pp_formatted_text (dc.printer));
3706 /* Ensure we don't ICE if multiple ranges with labels are on
3707 the same point. */
3709 text_range_label label0 ("label 0");
3710 text_range_label label1 ("label 1");
3711 text_range_label label2 ("label 2");
3712 gcc_rich_location richloc (bar, &label0);
3713 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3714 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3716 test_diagnostic_context dc;
3717 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3718 ASSERT_STREQ (" foo = bar.field;\n"
3719 " ^~~\n"
3720 " |\n"
3721 " label 0\n"
3722 " label 1\n"
3723 " label 2\n",
3724 pp_formatted_text (dc.printer));
3727 /* Example of out-of-order ranges (thus requiring a sort), where
3728 they overlap, and there are multiple ranges on the same point. */
3730 text_range_label label_0a ("label 0a");
3731 text_range_label label_1a ("label 1a");
3732 text_range_label label_2a ("label 2a");
3733 text_range_label label_0b ("label 0b");
3734 text_range_label label_1b ("label 1b");
3735 text_range_label label_2b ("label 2b");
3736 text_range_label label_0c ("label 0c");
3737 text_range_label label_1c ("label 1c");
3738 text_range_label label_2c ("label 2c");
3739 gcc_rich_location richloc (field, &label_0a);
3740 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3741 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3743 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3744 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3745 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3747 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3748 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3749 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3751 test_diagnostic_context dc;
3752 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3753 ASSERT_STREQ (" foo = bar.field;\n"
3754 " ~~~ ~~~ ^~~~~\n"
3755 " | | |\n"
3756 " | | label 0a\n"
3757 " | | label 0b\n"
3758 " | | label 0c\n"
3759 " | label 1a\n"
3760 " | label 1b\n"
3761 " | label 1c\n"
3762 " label 2a\n"
3763 " label 2b\n"
3764 " label 2c\n",
3765 pp_formatted_text (dc.printer));
3768 /* Verify that a NULL result from range_label::get_text is
3769 handled gracefully. */
3771 text_range_label label (NULL);
3772 gcc_rich_location richloc (bar, &label);
3774 test_diagnostic_context dc;
3775 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3776 ASSERT_STREQ (" foo = bar.field;\n"
3777 " ^~~\n",
3778 pp_formatted_text (dc.printer));
3781 /* TODO: example of formatted printing (needs to be in
3782 gcc-rich-location.cc due to Makefile.in issues). */
3785 /* Run the various one-liner tests. */
3787 static void
3788 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3790 /* Create a tempfile and write some text to it.
3791 ....................0000000001111111.
3792 ....................1234567890123456. */
3793 const char *content = "foo = bar.field;\n";
3794 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3795 line_table_test ltt (case_);
3797 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3799 location_t line_end = linemap_position_for_column (line_table, 16);
3801 /* Don't attempt to run the tests if column data might be unavailable. */
3802 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3803 return;
3805 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3806 ASSERT_EQ (1, LOCATION_LINE (line_end));
3807 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3809 test_one_liner_simple_caret ();
3810 test_one_liner_caret_and_range ();
3811 test_one_liner_multiple_carets_and_ranges ();
3812 test_one_liner_fixit_insert_before ();
3813 test_one_liner_fixit_insert_after ();
3814 test_one_liner_fixit_remove ();
3815 test_one_liner_fixit_replace ();
3816 test_one_liner_fixit_replace_non_equal_range ();
3817 test_one_liner_fixit_replace_equal_secondary_range ();
3818 test_one_liner_fixit_validation_adhoc_locations ();
3819 test_one_liner_many_fixits_1 ();
3820 test_one_liner_many_fixits_2 ();
3821 test_one_liner_labels ();
3824 /* Version of all one-liner tests exercising multibyte awareness. For
3825 simplicity we stick to using two multibyte characters in the test, U+1F602
3826 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3827 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3828 below asserts would be easier to read if we used UTF-8 directly in the
3829 string constants, but it seems better not to demand the host compiler
3830 support this, when it isn't otherwise necessary. Instead, whenever an
3831 extended character appears in a string, we put a line break after it so that
3832 all succeeding characters can appear visually at the correct display column.
3834 All of these work on the following 1-line source file:
3836 .0000000001111111111222222 display
3837 .1234567890123456789012345 columns
3838 "SS_foo = P_bar.SS_fieldP;\n"
3839 .0000000111111111222222223 byte
3840 .1356789012456789134567891 columns
3842 which is set up by test_diagnostic_show_locus_one_liner and calls
3843 them. Here SS represents the two display columns for the U+1F602 emoji and
3844 P represents the one display column for the U+03C0 pi symbol. */
3846 /* Just a caret. */
3848 static void
3849 test_one_liner_simple_caret_utf8 ()
3851 test_diagnostic_context dc;
3852 location_t caret = linemap_position_for_column (line_table, 18);
3853 rich_location richloc (line_table, caret);
3854 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3855 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3856 "_foo = \xcf\x80"
3857 "_bar.\xf0\x9f\x98\x82"
3858 "_field\xcf\x80"
3859 ";\n"
3860 " ^\n",
3861 pp_formatted_text (dc.printer));
3864 /* Caret and range. */
3865 static void
3866 test_one_liner_caret_and_range_utf8 ()
3868 test_diagnostic_context dc;
3869 location_t caret = linemap_position_for_column (line_table, 18);
3870 location_t start = linemap_position_for_column (line_table, 12);
3871 location_t finish = linemap_position_for_column (line_table, 30);
3872 location_t loc = make_location (caret, start, finish);
3873 rich_location richloc (line_table, loc);
3874 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3875 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3876 "_foo = \xcf\x80"
3877 "_bar.\xf0\x9f\x98\x82"
3878 "_field\xcf\x80"
3879 ";\n"
3880 " ~~~~~^~~~~~~~~~\n",
3881 pp_formatted_text (dc.printer));
3884 /* Multiple ranges and carets. */
3886 static void
3887 test_one_liner_multiple_carets_and_ranges_utf8 ()
3889 test_diagnostic_context dc;
3890 location_t foo
3891 = make_location (linemap_position_for_column (line_table, 7),
3892 linemap_position_for_column (line_table, 1),
3893 linemap_position_for_column (line_table, 8));
3894 dc.caret_chars[0] = 'A';
3896 location_t bar
3897 = make_location (linemap_position_for_column (line_table, 16),
3898 linemap_position_for_column (line_table, 12),
3899 linemap_position_for_column (line_table, 17));
3900 dc.caret_chars[1] = 'B';
3902 location_t field
3903 = make_location (linemap_position_for_column (line_table, 26),
3904 linemap_position_for_column (line_table, 19),
3905 linemap_position_for_column (line_table, 30));
3906 dc.caret_chars[2] = 'C';
3907 rich_location richloc (line_table, foo);
3908 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3909 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3910 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3911 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3912 "_foo = \xcf\x80"
3913 "_bar.\xf0\x9f\x98\x82"
3914 "_field\xcf\x80"
3915 ";\n"
3916 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3917 pp_formatted_text (dc.printer));
3920 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3922 static void
3923 test_one_liner_fixit_insert_before_utf8 ()
3925 test_diagnostic_context dc;
3926 location_t caret = linemap_position_for_column (line_table, 12);
3927 rich_location richloc (line_table, caret);
3928 richloc.add_fixit_insert_before ("&");
3929 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3930 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3931 "_foo = \xcf\x80"
3932 "_bar.\xf0\x9f\x98\x82"
3933 "_field\xcf\x80"
3934 ";\n"
3935 " ^\n"
3936 " &\n",
3937 pp_formatted_text (dc.printer));
3940 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3942 static void
3943 test_one_liner_fixit_insert_after_utf8 ()
3945 test_diagnostic_context dc;
3946 location_t start = linemap_position_for_column (line_table, 1);
3947 location_t finish = linemap_position_for_column (line_table, 8);
3948 location_t foo = make_location (start, start, finish);
3949 rich_location richloc (line_table, foo);
3950 richloc.add_fixit_insert_after ("[0]");
3951 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3952 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3953 "_foo = \xcf\x80"
3954 "_bar.\xf0\x9f\x98\x82"
3955 "_field\xcf\x80"
3956 ";\n"
3957 " ^~~~~~\n"
3958 " [0]\n",
3959 pp_formatted_text (dc.printer));
3962 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3964 static void
3965 test_one_liner_fixit_remove_utf8 ()
3967 test_diagnostic_context dc;
3968 location_t start = linemap_position_for_column (line_table, 18);
3969 location_t finish = linemap_position_for_column (line_table, 30);
3970 location_t dot = make_location (start, start, finish);
3971 rich_location richloc (line_table, dot);
3972 richloc.add_fixit_remove ();
3973 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3974 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3975 "_foo = \xcf\x80"
3976 "_bar.\xf0\x9f\x98\x82"
3977 "_field\xcf\x80"
3978 ";\n"
3979 " ^~~~~~~~~~\n"
3980 " ----------\n",
3981 pp_formatted_text (dc.printer));
3984 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3986 static void
3987 test_one_liner_fixit_replace_utf8 ()
3989 test_diagnostic_context dc;
3990 location_t start = linemap_position_for_column (line_table, 19);
3991 location_t finish = linemap_position_for_column (line_table, 30);
3992 location_t field = make_location (start, start, finish);
3993 rich_location richloc (line_table, field);
3994 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3995 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3996 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3997 "_foo = \xcf\x80"
3998 "_bar.\xf0\x9f\x98\x82"
3999 "_field\xcf\x80"
4000 ";\n"
4001 " ^~~~~~~~~\n"
4002 " m_\xf0\x9f\x98\x82"
4003 "_field\xcf\x80\n",
4004 pp_formatted_text (dc.printer));
4007 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4008 but where the caret was elsewhere. */
4010 static void
4011 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4013 test_diagnostic_context dc;
4014 location_t equals = linemap_position_for_column (line_table, 10);
4015 location_t start = linemap_position_for_column (line_table, 19);
4016 location_t finish = linemap_position_for_column (line_table, 30);
4017 rich_location richloc (line_table, equals);
4018 source_range range;
4019 range.m_start = start;
4020 range.m_finish = finish;
4021 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4022 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4023 /* The replacement range is not indicated in the annotation line, so
4024 it should be indicated via an additional underline. */
4025 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4026 "_foo = \xcf\x80"
4027 "_bar.\xf0\x9f\x98\x82"
4028 "_field\xcf\x80"
4029 ";\n"
4030 " ^\n"
4031 " ---------\n"
4032 " m_\xf0\x9f\x98\x82"
4033 "_field\xcf\x80\n",
4034 pp_formatted_text (dc.printer));
4037 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4038 where the caret was elsewhere, but where a secondary range
4039 exactly covers "field". */
4041 static void
4042 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4044 test_diagnostic_context dc;
4045 location_t equals = linemap_position_for_column (line_table, 10);
4046 location_t start = linemap_position_for_column (line_table, 19);
4047 location_t finish = linemap_position_for_column (line_table, 30);
4048 rich_location richloc (line_table, equals);
4049 location_t field = make_location (start, start, finish);
4050 richloc.add_range (field);
4051 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4052 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4053 /* The replacement range is indicated in the annotation line,
4054 so it shouldn't be indicated via an additional underline. */
4055 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4056 "_foo = \xcf\x80"
4057 "_bar.\xf0\x9f\x98\x82"
4058 "_field\xcf\x80"
4059 ";\n"
4060 " ^ ~~~~~~~~~\n"
4061 " m_\xf0\x9f\x98\x82"
4062 "_field\xcf\x80\n",
4063 pp_formatted_text (dc.printer));
4066 /* Verify that we can use ad-hoc locations when adding fixits to a
4067 rich_location. */
4069 static void
4070 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4072 /* Generate a range that's too long to be packed, so must
4073 be stored as an ad-hoc location (given the defaults
4074 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4075 const location_t c12 = linemap_position_for_column (line_table, 12);
4076 const location_t c52 = linemap_position_for_column (line_table, 52);
4077 const location_t loc = make_location (c12, c12, c52);
4079 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4080 return;
4082 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4084 /* Insert. */
4086 rich_location richloc (line_table, loc);
4087 richloc.add_fixit_insert_before (loc, "test");
4088 /* It should not have been discarded by the validator. */
4089 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4091 test_diagnostic_context dc;
4092 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4093 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4094 "_foo = \xcf\x80"
4095 "_bar.\xf0\x9f\x98\x82"
4096 "_field\xcf\x80"
4097 ";\n"
4098 " ^~~~~~~~~~~~~~~~ \n"
4099 " test\n",
4100 pp_formatted_text (dc.printer));
4103 /* Remove. */
4105 rich_location richloc (line_table, loc);
4106 source_range range = source_range::from_locations (loc, c52);
4107 richloc.add_fixit_remove (range);
4108 /* It should not have been discarded by the validator. */
4109 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4111 test_diagnostic_context dc;
4112 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4113 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4114 "_foo = \xcf\x80"
4115 "_bar.\xf0\x9f\x98\x82"
4116 "_field\xcf\x80"
4117 ";\n"
4118 " ^~~~~~~~~~~~~~~~ \n"
4119 " -------------------------------------\n",
4120 pp_formatted_text (dc.printer));
4123 /* Replace. */
4125 rich_location richloc (line_table, loc);
4126 source_range range = source_range::from_locations (loc, c52);
4127 richloc.add_fixit_replace (range, "test");
4128 /* It should not have been discarded by the validator. */
4129 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4131 test_diagnostic_context dc;
4132 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4133 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4134 "_foo = \xcf\x80"
4135 "_bar.\xf0\x9f\x98\x82"
4136 "_field\xcf\x80"
4137 ";\n"
4138 " ^~~~~~~~~~~~~~~~ \n"
4139 " test\n",
4140 pp_formatted_text (dc.printer));
4144 /* Test of consolidating insertions at the same location. */
4146 static void
4147 test_one_liner_many_fixits_1_utf8 ()
4149 test_diagnostic_context dc;
4150 location_t equals = linemap_position_for_column (line_table, 10);
4151 rich_location richloc (line_table, equals);
4152 for (int i = 0; i < 19; i++)
4153 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4154 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4155 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4156 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4157 "_foo = \xcf\x80"
4158 "_bar.\xf0\x9f\x98\x82"
4159 "_field\xcf\x80"
4160 ";\n"
4161 " ^\n"
4162 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4163 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4164 pp_formatted_text (dc.printer));
4167 /* Ensure that we can add an arbitrary number of fix-it hints to a
4168 rich_location, even if they are not consolidated. */
4170 static void
4171 test_one_liner_many_fixits_2_utf8 ()
4173 test_diagnostic_context dc;
4174 location_t equals = linemap_position_for_column (line_table, 10);
4175 rich_location richloc (line_table, equals);
4176 const int nlocs = 19;
4177 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4178 34, 36, 38, 40, 42, 44};
4179 for (int i = 0; i != nlocs; ++i)
4181 location_t loc = linemap_position_for_column (line_table, locs[i]);
4182 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4185 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4186 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4187 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4188 "_foo = \xcf\x80"
4189 "_bar.\xf0\x9f\x98\x82"
4190 "_field\xcf\x80"
4191 ";\n"
4192 " ^\n"
4193 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4194 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4195 pp_formatted_text (dc.printer));
4198 /* Test of labeling the ranges within a rich_location. */
4200 static void
4201 test_one_liner_labels_utf8 ()
4203 location_t foo
4204 = make_location (linemap_position_for_column (line_table, 1),
4205 linemap_position_for_column (line_table, 1),
4206 linemap_position_for_column (line_table, 8));
4207 location_t bar
4208 = make_location (linemap_position_for_column (line_table, 12),
4209 linemap_position_for_column (line_table, 12),
4210 linemap_position_for_column (line_table, 17));
4211 location_t field
4212 = make_location (linemap_position_for_column (line_table, 19),
4213 linemap_position_for_column (line_table, 19),
4214 linemap_position_for_column (line_table, 30));
4216 /* Example where all the labels fit on one line. */
4218 /* These three labels contain multibyte characters such that their byte
4219 lengths are respectively (12, 10, 18), but their display widths are only
4220 (6, 5, 9). All three fit on the line when considering the display
4221 widths, but not when considering the byte widths, so verify that we do
4222 indeed put them all on one line. */
4223 text_range_label label0
4224 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4225 text_range_label label1
4226 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4227 text_range_label label2
4228 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4229 "\xcf\x80");
4230 gcc_rich_location richloc (foo, &label0);
4231 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4232 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4235 test_diagnostic_context dc;
4236 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4237 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4238 "_foo = \xcf\x80"
4239 "_bar.\xf0\x9f\x98\x82"
4240 "_field\xcf\x80"
4241 ";\n"
4242 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4243 " | | |\n"
4244 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4245 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4246 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4247 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4248 pp_formatted_text (dc.printer));
4253 /* Example where the labels need extra lines. */
4255 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4256 text_range_label label1 ("label 1\xcf\x80");
4257 text_range_label label2 ("label 2\xcf\x80");
4258 gcc_rich_location richloc (foo, &label0);
4259 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4260 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4262 test_diagnostic_context dc;
4263 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4265 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4266 "_foo = \xcf\x80"
4267 "_bar.\xf0\x9f\x98\x82"
4268 "_field\xcf\x80"
4269 ";\n"
4270 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4271 " | | |\n"
4272 " | | label 2\xcf\x80\n"
4273 " | label 1\xcf\x80\n"
4274 " label 0\xf0\x9f\x98\x82\n",
4275 pp_formatted_text (dc.printer));
4278 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4279 but label 1 just touches label 2. */
4281 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4282 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4283 text_range_label label2 ("c");
4284 gcc_rich_location richloc (foo, &label0);
4285 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4286 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4288 test_diagnostic_context dc;
4289 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4290 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4291 "_foo = \xcf\x80"
4292 "_bar.\xf0\x9f\x98\x82"
4293 "_field\xcf\x80"
4294 ";\n"
4295 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4296 " | | |\n"
4297 " | | c\n"
4298 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4299 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4300 pp_formatted_text (dc.printer));
4303 /* Example of escaping the source lines. */
4305 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4306 text_range_label label1 ("label 1\xcf\x80");
4307 text_range_label label2 ("label 2\xcf\x80");
4308 gcc_rich_location richloc (foo, &label0);
4309 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4310 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4311 richloc.set_escape_on_output (true);
4314 test_diagnostic_context dc;
4315 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
4316 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4317 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4318 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4319 " | | |\n"
4320 " | | label 2\xcf\x80\n"
4321 " | label 1\xcf\x80\n"
4322 " label 0\xf0\x9f\x98\x82\n",
4323 pp_formatted_text (dc.printer));
4326 test_diagnostic_context dc;
4327 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
4328 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4329 ASSERT_STREQ
4330 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4331 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4332 " | | |\n"
4333 " | | label 2\xcf\x80\n"
4334 " | label 1\xcf\x80\n"
4335 " label 0\xf0\x9f\x98\x82\n",
4336 pp_formatted_text (dc.printer));
4341 /* Make sure that colorization codes don't interrupt a multibyte
4342 sequence, which would corrupt it. */
4343 static void
4344 test_one_liner_colorized_utf8 ()
4346 test_diagnostic_context dc;
4347 dc.colorize_source_p = true;
4348 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4349 const location_t pi = linemap_position_for_column (line_table, 12);
4350 rich_location richloc (line_table, pi);
4351 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4353 /* In order to avoid having the test depend on exactly how the colorization
4354 was effected, just confirm there are two pi characters in the output. */
4355 const char *result = pp_formatted_text (dc.printer);
4356 const char *null_term = result + strlen (result);
4357 const char *first_pi = strstr (result, "\xcf\x80");
4358 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4359 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4362 /* Run the various one-liner tests. */
4364 static void
4365 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4367 /* Create a tempfile and write some text to it. */
4368 const char *content
4369 /* Display columns.
4370 0000000000000000000000011111111111111111111111111111112222222222222
4371 1111111122222222345678900000000123456666666677777777890123444444445 */
4372 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4373 /* 0000000000000000000001111111111111111111222222222222222222222233333
4374 1111222233334444567890122223333456789999000011112222345678999900001
4375 Byte columns. */
4376 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4377 line_table_test ltt (case_);
4379 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4381 location_t line_end = linemap_position_for_column (line_table, 31);
4383 /* Don't attempt to run the tests if column data might be unavailable. */
4384 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4385 return;
4387 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4388 ASSERT_EQ (1, LOCATION_LINE (line_end));
4389 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4391 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4392 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4393 def_policy ()));
4394 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4395 def_policy ()));
4397 test_one_liner_simple_caret_utf8 ();
4398 test_one_liner_caret_and_range_utf8 ();
4399 test_one_liner_multiple_carets_and_ranges_utf8 ();
4400 test_one_liner_fixit_insert_before_utf8 ();
4401 test_one_liner_fixit_insert_after_utf8 ();
4402 test_one_liner_fixit_remove_utf8 ();
4403 test_one_liner_fixit_replace_utf8 ();
4404 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4405 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4406 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4407 test_one_liner_many_fixits_1_utf8 ();
4408 test_one_liner_many_fixits_2_utf8 ();
4409 test_one_liner_labels_utf8 ();
4410 test_one_liner_colorized_utf8 ();
4413 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4415 static void
4416 test_add_location_if_nearby (const line_table_case &case_)
4418 /* Create a tempfile and write some text to it.
4419 ...000000000111111111122222222223333333333.
4420 ...123456789012345678901234567890123456789. */
4421 const char *content
4422 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4423 "struct different_line\n" /* line 2. */
4424 "{\n" /* line 3. */
4425 " double x;\n" /* line 4. */
4426 " double y;\n" /* line 5. */
4427 ";\n"); /* line 6. */
4428 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4429 line_table_test ltt (case_);
4431 const line_map_ordinary *ord_map
4432 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4433 tmp.get_filename (), 0));
4435 linemap_line_start (line_table, 1, 100);
4437 const location_t final_line_end
4438 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4440 /* Don't attempt to run the tests if column data might be unavailable. */
4441 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4442 return;
4444 /* Test of add_location_if_nearby on the same line as the
4445 primary location. */
4447 const location_t missing_close_brace_1_39
4448 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4449 const location_t matching_open_brace_1_18
4450 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4451 gcc_rich_location richloc (missing_close_brace_1_39);
4452 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4453 ASSERT_TRUE (added);
4454 ASSERT_EQ (2, richloc.get_num_locations ());
4455 test_diagnostic_context dc;
4456 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4457 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4458 " ~ ^\n",
4459 pp_formatted_text (dc.printer));
4462 /* Test of add_location_if_nearby on a different line to the
4463 primary location. */
4465 const location_t missing_close_brace_6_1
4466 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4467 const location_t matching_open_brace_3_1
4468 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4469 gcc_rich_location richloc (missing_close_brace_6_1);
4470 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4471 ASSERT_FALSE (added);
4472 ASSERT_EQ (1, richloc.get_num_locations ());
4476 /* Verify that we print fixits even if they only affect lines
4477 outside those covered by the ranges in the rich_location. */
4479 static void
4480 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4482 /* Create a tempfile and write some text to it.
4483 ...000000000111111111122222222223333333333.
4484 ...123456789012345678901234567890123456789. */
4485 const char *content
4486 = ("struct point { double x; double y; };\n" /* line 1. */
4487 "struct point origin = {x: 0.0,\n" /* line 2. */
4488 " y\n" /* line 3. */
4489 "\n" /* line 4. */
4490 "\n" /* line 5. */
4491 " : 0.0};\n"); /* line 6. */
4492 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4493 line_table_test ltt (case_);
4495 const line_map_ordinary *ord_map
4496 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4497 tmp.get_filename (), 0));
4499 linemap_line_start (line_table, 1, 100);
4501 const location_t final_line_end
4502 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4504 /* Don't attempt to run the tests if column data might be unavailable. */
4505 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4506 return;
4508 /* A pair of tests for modernizing the initializers to C99-style. */
4510 /* The one-liner case (line 2). */
4512 test_diagnostic_context dc;
4513 const location_t x
4514 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4515 const location_t colon
4516 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4517 rich_location richloc (line_table, colon);
4518 richloc.add_fixit_insert_before (x, ".");
4519 richloc.add_fixit_replace (colon, "=");
4520 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4521 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4522 " ^\n"
4523 " .=\n",
4524 pp_formatted_text (dc.printer));
4527 /* The multiline case. The caret for the rich_location is on line 6;
4528 verify that insertion fixit on line 3 is still printed (and that
4529 span starts are printed due to the gap between the span at line 3
4530 and that at line 6). */
4532 test_diagnostic_context dc;
4533 const location_t y
4534 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4535 const location_t colon
4536 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4537 rich_location richloc (line_table, colon);
4538 richloc.add_fixit_insert_before (y, ".");
4539 richloc.add_fixit_replace (colon, "=");
4540 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4541 ASSERT_STREQ ("FILENAME:3:24:\n"
4542 " y\n"
4543 " .\n"
4544 "FILENAME:6:25:\n"
4545 " : 0.0};\n"
4546 " ^\n"
4547 " =\n",
4548 pp_formatted_text (dc.printer));
4551 /* As above, but verify the behavior of multiple line spans
4552 with line-numbering enabled. */
4554 const location_t y
4555 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4556 const location_t colon
4557 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4558 rich_location richloc (line_table, colon);
4559 richloc.add_fixit_insert_before (y, ".");
4560 richloc.add_fixit_replace (colon, "=");
4561 test_diagnostic_context dc;
4562 dc.show_line_numbers_p = true;
4563 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4564 ASSERT_STREQ (" 3 | y\n"
4565 " | .\n"
4566 "......\n"
4567 " 6 | : 0.0};\n"
4568 " | ^\n"
4569 " | =\n",
4570 pp_formatted_text (dc.printer));
4575 /* Verify that fix-it hints are appropriately consolidated.
4577 If any fix-it hints in a rich_location involve locations beyond
4578 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4579 the fix-it as a whole, so there should be none.
4581 Otherwise, verify that consecutive "replace" and "remove" fix-its
4582 are merged, and that other fix-its remain separate. */
4584 static void
4585 test_fixit_consolidation (const line_table_case &case_)
4587 line_table_test ltt (case_);
4589 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4591 const location_t c10 = linemap_position_for_column (line_table, 10);
4592 const location_t c15 = linemap_position_for_column (line_table, 15);
4593 const location_t c16 = linemap_position_for_column (line_table, 16);
4594 const location_t c17 = linemap_position_for_column (line_table, 17);
4595 const location_t c20 = linemap_position_for_column (line_table, 20);
4596 const location_t c21 = linemap_position_for_column (line_table, 21);
4597 const location_t caret = c10;
4599 /* Insert + insert. */
4601 rich_location richloc (line_table, caret);
4602 richloc.add_fixit_insert_before (c10, "foo");
4603 richloc.add_fixit_insert_before (c15, "bar");
4605 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4606 /* Bogus column info for 2nd fixit, so no fixits. */
4607 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4608 else
4609 /* They should not have been merged. */
4610 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4613 /* Insert + replace. */
4615 rich_location richloc (line_table, caret);
4616 richloc.add_fixit_insert_before (c10, "foo");
4617 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4618 "bar");
4620 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4621 /* Bogus column info for 2nd fixit, so no fixits. */
4622 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4623 else
4624 /* They should not have been merged. */
4625 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4628 /* Replace + non-consecutive insert. */
4630 rich_location richloc (line_table, caret);
4631 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4632 "bar");
4633 richloc.add_fixit_insert_before (c17, "foo");
4635 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4636 /* Bogus column info for 2nd fixit, so no fixits. */
4637 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4638 else
4639 /* They should not have been merged. */
4640 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4643 /* Replace + non-consecutive replace. */
4645 rich_location richloc (line_table, caret);
4646 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4647 "foo");
4648 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4649 "bar");
4651 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4652 /* Bogus column info for 2nd fixit, so no fixits. */
4653 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4654 else
4655 /* They should not have been merged. */
4656 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4659 /* Replace + consecutive replace. */
4661 rich_location richloc (line_table, caret);
4662 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4663 "foo");
4664 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4665 "bar");
4667 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4668 /* Bogus column info for 2nd fixit, so no fixits. */
4669 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4670 else
4672 /* They should have been merged into a single "replace". */
4673 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4674 const fixit_hint *hint = richloc.get_fixit_hint (0);
4675 ASSERT_STREQ ("foobar", hint->get_string ());
4676 ASSERT_EQ (c10, hint->get_start_loc ());
4677 ASSERT_EQ (c21, hint->get_next_loc ());
4681 /* Replace + consecutive removal. */
4683 rich_location richloc (line_table, caret);
4684 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4685 "foo");
4686 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4688 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4689 /* Bogus column info for 2nd fixit, so no fixits. */
4690 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4691 else
4693 /* They should have been merged into a single replace, with the
4694 range extended to cover that of the removal. */
4695 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4696 const fixit_hint *hint = richloc.get_fixit_hint (0);
4697 ASSERT_STREQ ("foo", hint->get_string ());
4698 ASSERT_EQ (c10, hint->get_start_loc ());
4699 ASSERT_EQ (c21, hint->get_next_loc ());
4703 /* Consecutive removals. */
4705 rich_location richloc (line_table, caret);
4706 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4707 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4709 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4710 /* Bogus column info for 2nd fixit, so no fixits. */
4711 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4712 else
4714 /* They should have been merged into a single "replace-with-empty". */
4715 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4716 const fixit_hint *hint = richloc.get_fixit_hint (0);
4717 ASSERT_STREQ ("", hint->get_string ());
4718 ASSERT_EQ (c10, hint->get_start_loc ());
4719 ASSERT_EQ (c21, hint->get_next_loc ());
4724 /* Verify that the line_corrections machinery correctly prints
4725 overlapping fixit-hints. */
4727 static void
4728 test_overlapped_fixit_printing (const line_table_case &case_)
4730 /* Create a tempfile and write some text to it.
4731 ...000000000111111111122222222223333333333.
4732 ...123456789012345678901234567890123456789. */
4733 const char *content
4734 = (" foo *f = (foo *)ptr->field;\n");
4735 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4736 line_table_test ltt (case_);
4738 const line_map_ordinary *ord_map
4739 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4740 tmp.get_filename (), 0));
4742 linemap_line_start (line_table, 1, 100);
4744 const location_t final_line_end
4745 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4747 /* Don't attempt to run the tests if column data might be unavailable. */
4748 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4749 return;
4751 /* A test for converting a C-style cast to a C++-style cast. */
4752 const location_t open_paren
4753 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4754 const location_t close_paren
4755 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4756 const location_t expr_start
4757 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4758 const location_t expr_finish
4759 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4760 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4762 /* Various examples of fix-it hints that aren't themselves consolidated,
4763 but for which the *printing* may need consolidation. */
4765 /* Example where 3 fix-it hints are printed as one. */
4767 test_diagnostic_context dc;
4768 rich_location richloc (line_table, expr);
4769 richloc.add_fixit_replace (open_paren, "const_cast<");
4770 richloc.add_fixit_replace (close_paren, "> (");
4771 richloc.add_fixit_insert_after (")");
4773 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4774 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4775 " ^~~~~~~~~~\n"
4776 " -----------------\n"
4777 " const_cast<foo *> (ptr->field)\n",
4778 pp_formatted_text (dc.printer));
4780 /* Unit-test the line_corrections machinery. */
4781 char_display_policy policy (make_policy (dc, richloc));
4782 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4783 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4784 ASSERT_EQ (column_range (12, 12),
4785 get_affected_range (policy, hint_0, CU_BYTES));
4786 ASSERT_EQ (column_range (12, 12),
4787 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4788 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4789 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4790 ASSERT_EQ (column_range (18, 18),
4791 get_affected_range (policy, hint_1, CU_BYTES));
4792 ASSERT_EQ (column_range (18, 18),
4793 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4794 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4795 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4796 ASSERT_EQ (column_range (29, 28),
4797 get_affected_range (policy, hint_2, CU_BYTES));
4798 ASSERT_EQ (column_range (29, 28),
4799 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4800 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2));
4802 /* Add each hint in turn to a line_corrections instance,
4803 and verify that they are consolidated into one correction instance
4804 as expected. */
4805 line_corrections lc (policy, tmp.get_filename (), 1);
4807 /* The first replace hint by itself. */
4808 lc.add_hint (hint_0);
4809 ASSERT_EQ (1, lc.m_corrections.length ());
4810 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4811 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4812 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4813 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4815 /* After the second replacement hint, they are printed together
4816 as a replacement (along with the text between them). */
4817 lc.add_hint (hint_1);
4818 ASSERT_EQ (1, lc.m_corrections.length ());
4819 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4820 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4821 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4822 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4824 /* After the final insertion hint, they are all printed together
4825 as a replacement (along with the text between them). */
4826 lc.add_hint (hint_2);
4827 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4828 lc.m_corrections[0]->m_text);
4829 ASSERT_EQ (1, lc.m_corrections.length ());
4830 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4831 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4832 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4835 /* Example where two are consolidated during printing. */
4837 test_diagnostic_context dc;
4838 rich_location richloc (line_table, expr);
4839 richloc.add_fixit_replace (open_paren, "CAST (");
4840 richloc.add_fixit_replace (close_paren, ") (");
4841 richloc.add_fixit_insert_after (")");
4843 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4844 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4845 " ^~~~~~~~~~\n"
4846 " -\n"
4847 " CAST (-\n"
4848 " ) ( )\n",
4849 pp_formatted_text (dc.printer));
4852 /* Example where none are consolidated during printing. */
4854 test_diagnostic_context dc;
4855 rich_location richloc (line_table, expr);
4856 richloc.add_fixit_replace (open_paren, "CST (");
4857 richloc.add_fixit_replace (close_paren, ") (");
4858 richloc.add_fixit_insert_after (")");
4860 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4861 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4862 " ^~~~~~~~~~\n"
4863 " -\n"
4864 " CST ( -\n"
4865 " ) ( )\n",
4866 pp_formatted_text (dc.printer));
4869 /* Example of deletion fix-it hints. */
4871 test_diagnostic_context dc;
4872 rich_location richloc (line_table, expr);
4873 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4874 source_range victim = {open_paren, close_paren};
4875 richloc.add_fixit_remove (victim);
4877 /* This case is actually handled by fixit-consolidation,
4878 rather than by line_corrections. */
4879 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4881 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4882 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4883 " ^~~~~~~~~~\n"
4884 " -------\n"
4885 " (bar *)\n",
4886 pp_formatted_text (dc.printer));
4889 /* Example of deletion fix-it hints that would overlap. */
4891 test_diagnostic_context dc;
4892 rich_location richloc (line_table, expr);
4893 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4894 source_range victim = {expr_start, expr_finish};
4895 richloc.add_fixit_remove (victim);
4897 /* These fixits are not consolidated. */
4898 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4900 /* But the corrections are. */
4901 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4902 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4903 " ^~~~~~~~~~\n"
4904 " -----------------\n"
4905 " (longer *)(foo *)\n",
4906 pp_formatted_text (dc.printer));
4909 /* Example of insertion fix-it hints that would overlap. */
4911 test_diagnostic_context dc;
4912 rich_location richloc (line_table, expr);
4913 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4914 richloc.add_fixit_insert_after (close_paren, "TEST");
4916 /* The first insertion is long enough that if printed naively,
4917 it would overlap with the second.
4918 Verify that they are printed as a single replacement. */
4919 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4920 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4921 " ^~~~~~~~~~\n"
4922 " -------\n"
4923 " LONGER THAN THE CAST(foo *)TEST\n",
4924 pp_formatted_text (dc.printer));
4928 /* Multibyte-aware version of preceding tests. See comments above
4929 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4930 characters here. */
4932 static void
4933 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4935 /* Create a tempfile and write some text to it. */
4937 const char *content
4938 /* Display columns.
4939 00000000000000000000000111111111111111111111111222222222222222223
4940 12344444444555555556789012344444444555555556789012345678999999990 */
4941 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4942 /* 00000000000000000000011111111111111111111112222222222333333333333
4943 12344445555666677778901234566667777888899990123456789012333344445
4944 Byte columns. */
4946 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4947 line_table_test ltt (case_);
4949 const line_map_ordinary *ord_map
4950 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4951 tmp.get_filename (), 0));
4953 linemap_line_start (line_table, 1, 100);
4955 const location_t final_line_end
4956 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4958 /* Don't attempt to run the tests if column data might be unavailable. */
4959 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4960 return;
4962 /* A test for converting a C-style cast to a C++-style cast. */
4963 const location_t open_paren
4964 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4965 const location_t close_paren
4966 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4967 const location_t expr_start
4968 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4969 const location_t expr_finish
4970 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4971 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4973 /* Various examples of fix-it hints that aren't themselves consolidated,
4974 but for which the *printing* may need consolidation. */
4976 /* Example where 3 fix-it hints are printed as one. */
4978 test_diagnostic_context dc;
4979 rich_location richloc (line_table, expr);
4980 richloc.add_fixit_replace (open_paren, "const_cast<");
4981 richloc.add_fixit_replace (close_paren, "> (");
4982 richloc.add_fixit_insert_after (")");
4984 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4985 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4986 " *f = (f\xf0\x9f\x98\x82"
4987 " *)ptr->field\xcf\x80"
4988 ";\n"
4989 " ^~~~~~~~~~~\n"
4990 " ------------------\n"
4991 " const_cast<f\xf0\x9f\x98\x82"
4992 " *> (ptr->field\xcf\x80"
4993 ")\n",
4994 pp_formatted_text (dc.printer));
4996 /* Unit-test the line_corrections machinery. */
4997 char_display_policy policy (make_policy (dc, richloc));
4998 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4999 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5000 ASSERT_EQ (column_range (14, 14),
5001 get_affected_range (policy, hint_0, CU_BYTES));
5002 ASSERT_EQ (column_range (12, 12),
5003 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
5004 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
5005 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5006 ASSERT_EQ (column_range (22, 22),
5007 get_affected_range (policy, hint_1, CU_BYTES));
5008 ASSERT_EQ (column_range (18, 18),
5009 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
5010 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
5011 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5012 ASSERT_EQ (column_range (35, 34),
5013 get_affected_range (policy, hint_2, CU_BYTES));
5014 ASSERT_EQ (column_range (30, 29),
5015 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
5016 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2));
5018 /* Add each hint in turn to a line_corrections instance,
5019 and verify that they are consolidated into one correction instance
5020 as expected. */
5021 line_corrections lc (policy, tmp.get_filename (), 1);
5023 /* The first replace hint by itself. */
5024 lc.add_hint (hint_0);
5025 ASSERT_EQ (1, lc.m_corrections.length ());
5026 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5027 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5028 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5029 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5031 /* After the second replacement hint, they are printed together
5032 as a replacement (along with the text between them). */
5033 lc.add_hint (hint_1);
5034 ASSERT_EQ (1, lc.m_corrections.length ());
5035 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5036 lc.m_corrections[0]->m_text);
5037 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5038 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5039 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5041 /* After the final insertion hint, they are all printed together
5042 as a replacement (along with the text between them). */
5043 lc.add_hint (hint_2);
5044 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5045 lc.m_corrections[0]->m_text);
5046 ASSERT_EQ (1, lc.m_corrections.length ());
5047 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5048 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5049 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5052 /* Example where two are consolidated during printing. */
5054 test_diagnostic_context dc;
5055 rich_location richloc (line_table, expr);
5056 richloc.add_fixit_replace (open_paren, "CAST (");
5057 richloc.add_fixit_replace (close_paren, ") (");
5058 richloc.add_fixit_insert_after (")");
5060 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5061 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5062 " *f = (f\xf0\x9f\x98\x82"
5063 " *)ptr->field\xcf\x80"
5064 ";\n"
5065 " ^~~~~~~~~~~\n"
5066 " -\n"
5067 " CAST (-\n"
5068 " ) ( )\n",
5069 pp_formatted_text (dc.printer));
5072 /* Example where none are consolidated during printing. */
5074 test_diagnostic_context dc;
5075 rich_location richloc (line_table, expr);
5076 richloc.add_fixit_replace (open_paren, "CST (");
5077 richloc.add_fixit_replace (close_paren, ") (");
5078 richloc.add_fixit_insert_after (")");
5080 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5081 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5082 " *f = (f\xf0\x9f\x98\x82"
5083 " *)ptr->field\xcf\x80"
5084 ";\n"
5085 " ^~~~~~~~~~~\n"
5086 " -\n"
5087 " CST ( -\n"
5088 " ) ( )\n",
5089 pp_formatted_text (dc.printer));
5092 /* Example of deletion fix-it hints. */
5094 test_diagnostic_context dc;
5095 rich_location richloc (line_table, expr);
5096 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5097 source_range victim = {open_paren, close_paren};
5098 richloc.add_fixit_remove (victim);
5100 /* This case is actually handled by fixit-consolidation,
5101 rather than by line_corrections. */
5102 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5104 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5105 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5106 " *f = (f\xf0\x9f\x98\x82"
5107 " *)ptr->field\xcf\x80"
5108 ";\n"
5109 " ^~~~~~~~~~~\n"
5110 " -------\n"
5111 " (bar\xf0\x9f\x98\x82"
5112 " *)\n",
5113 pp_formatted_text (dc.printer));
5116 /* Example of deletion fix-it hints that would overlap. */
5118 test_diagnostic_context dc;
5119 rich_location richloc (line_table, expr);
5120 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5121 source_range victim = {expr_start, expr_finish};
5122 richloc.add_fixit_remove (victim);
5124 /* These fixits are not consolidated. */
5125 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5127 /* But the corrections are. */
5128 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5129 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5130 " *f = (f\xf0\x9f\x98\x82"
5131 " *)ptr->field\xcf\x80"
5132 ";\n"
5133 " ^~~~~~~~~~~\n"
5134 " ------------------\n"
5135 " (long\xf0\x9f\x98\x82"
5136 " *)(f\xf0\x9f\x98\x82"
5137 " *)\n",
5138 pp_formatted_text (dc.printer));
5141 /* Example of insertion fix-it hints that would overlap. */
5143 test_diagnostic_context dc;
5144 rich_location richloc (line_table, expr);
5145 richloc.add_fixit_insert_before
5146 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5147 richloc.add_fixit_insert_after (close_paren, "TEST");
5149 /* The first insertion is long enough that if printed naively,
5150 it would overlap with the second.
5151 Verify that they are printed as a single replacement. */
5152 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5153 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5154 " *f = (f\xf0\x9f\x98\x82"
5155 " *)ptr->field\xcf\x80"
5156 ";\n"
5157 " ^~~~~~~~~~~\n"
5158 " -------\n"
5159 " L\xf0\x9f\x98\x82"
5160 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5161 " *)TEST\n",
5162 pp_formatted_text (dc.printer));
5166 /* Verify that the line_corrections machinery correctly prints
5167 overlapping fixit-hints that have been added in the wrong
5168 order.
5169 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5171 static void
5172 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5174 /* Create a tempfile and write some text to it.
5175 ...000000000111111111122222222223333333333.
5176 ...123456789012345678901234567890123456789. */
5177 const char *content
5178 = ("int a5[][0][0] = { 1, 2 };\n");
5179 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5180 line_table_test ltt (case_);
5182 const line_map_ordinary *ord_map
5183 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5184 tmp.get_filename (), 0));
5186 linemap_line_start (line_table, 1, 100);
5188 const location_t final_line_end
5189 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5191 /* Don't attempt to run the tests if column data might be unavailable. */
5192 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5193 return;
5195 const location_t col_1
5196 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5197 const location_t col_20
5198 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5199 const location_t col_21
5200 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5201 const location_t col_23
5202 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5203 const location_t col_25
5204 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5206 /* Two insertions, in the wrong order. */
5208 test_diagnostic_context dc;
5210 rich_location richloc (line_table, col_20);
5211 richloc.add_fixit_insert_before (col_23, "{");
5212 richloc.add_fixit_insert_before (col_21, "}");
5214 /* These fixits should be accepted; they can't be consolidated. */
5215 char_display_policy policy (make_policy (dc, richloc));
5216 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5217 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5218 ASSERT_EQ (column_range (23, 22),
5219 get_affected_range (policy, hint_0, CU_BYTES));
5220 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0));
5221 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5222 ASSERT_EQ (column_range (21, 20),
5223 get_affected_range (policy, hint_1, CU_BYTES));
5224 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1));
5226 /* Verify that they're printed correctly. */
5227 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5228 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5229 " ^\n"
5230 " } {\n",
5231 pp_formatted_text (dc.printer));
5234 /* Various overlapping insertions, some occurring "out of order"
5235 (reproducing the fix-it hints from PR c/81405). */
5237 test_diagnostic_context dc;
5238 rich_location richloc (line_table, col_20);
5240 richloc.add_fixit_insert_before (col_20, "{{");
5241 richloc.add_fixit_insert_before (col_21, "}}");
5242 richloc.add_fixit_insert_before (col_23, "{");
5243 richloc.add_fixit_insert_before (col_21, "}");
5244 richloc.add_fixit_insert_before (col_23, "{{");
5245 richloc.add_fixit_insert_before (col_25, "}");
5246 richloc.add_fixit_insert_before (col_21, "}");
5247 richloc.add_fixit_insert_before (col_1, "{");
5248 richloc.add_fixit_insert_before (col_25, "}");
5249 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5250 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5251 " ^\n"
5252 " { -----\n"
5253 " {{1}}}}, {{{2 }}\n",
5254 pp_formatted_text (dc.printer));
5258 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5260 static void
5261 test_fixit_insert_containing_newline (const line_table_case &case_)
5263 /* Create a tempfile and write some text to it.
5264 .........................0000000001111111.
5265 .........................1234567890123456. */
5266 const char *old_content = (" case 'a':\n" /* line 1. */
5267 " x = a;\n" /* line 2. */
5268 " case 'b':\n" /* line 3. */
5269 " x = b;\n");/* line 4. */
5271 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5272 line_table_test ltt (case_);
5273 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5275 location_t case_start = linemap_position_for_column (line_table, 5);
5276 location_t case_finish = linemap_position_for_column (line_table, 13);
5277 location_t case_loc = make_location (case_start, case_start, case_finish);
5278 location_t line_start = linemap_position_for_column (line_table, 1);
5280 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5281 return;
5283 /* Add a "break;" on a line by itself before line 3 i.e. before
5284 column 1 of line 3. */
5286 rich_location richloc (line_table, case_loc);
5287 richloc.add_fixit_insert_before (line_start, " break;\n");
5289 /* Without line numbers. */
5291 test_diagnostic_context dc;
5292 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5293 ASSERT_STREQ (" x = a;\n"
5294 "+ break;\n"
5295 " case 'b':\n"
5296 " ^~~~~~~~~\n",
5297 pp_formatted_text (dc.printer));
5300 /* With line numbers. */
5302 test_diagnostic_context dc;
5303 dc.show_line_numbers_p = true;
5304 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5305 ASSERT_STREQ (" 2 | x = a;\n"
5306 " +++ |+ break;\n"
5307 " 3 | case 'b':\n"
5308 " | ^~~~~~~~~\n",
5309 pp_formatted_text (dc.printer));
5313 /* Verify that attempts to add text with a newline fail when the
5314 insertion point is *not* at the start of a line. */
5316 rich_location richloc (line_table, case_loc);
5317 richloc.add_fixit_insert_before (case_start, "break;\n");
5318 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5319 test_diagnostic_context dc;
5320 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5321 ASSERT_STREQ (" case 'b':\n"
5322 " ^~~~~~~~~\n",
5323 pp_formatted_text (dc.printer));
5327 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5328 of the file, where the fix-it is printed in a different line-span
5329 to the primary range of the diagnostic. */
5331 static void
5332 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5334 /* Create a tempfile and write some text to it.
5335 .........................0000000001111111.
5336 .........................1234567890123456. */
5337 const char *old_content = ("test (int ch)\n" /* line 1. */
5338 "{\n" /* line 2. */
5339 " putchar (ch);\n" /* line 3. */
5340 "}\n"); /* line 4. */
5342 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5343 line_table_test ltt (case_);
5345 const line_map_ordinary *ord_map = linemap_check_ordinary
5346 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5347 linemap_line_start (line_table, 1, 100);
5349 /* The primary range is the "putchar" token. */
5350 location_t putchar_start
5351 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5352 location_t putchar_finish
5353 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5354 location_t putchar_loc
5355 = make_location (putchar_start, putchar_start, putchar_finish);
5356 rich_location richloc (line_table, putchar_loc);
5358 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5359 location_t file_start
5360 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5361 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5363 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5364 return;
5367 test_diagnostic_context dc;
5368 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5369 ASSERT_STREQ ("FILENAME:1:1:\n"
5370 "+#include <stdio.h>\n"
5371 " test (int ch)\n"
5372 "FILENAME:3:2:\n"
5373 " putchar (ch);\n"
5374 " ^~~~~~~\n",
5375 pp_formatted_text (dc.printer));
5378 /* With line-numbering, the line spans are close enough to be
5379 consolidated, since it makes little sense to skip line 2. */
5381 test_diagnostic_context dc;
5382 dc.show_line_numbers_p = true;
5383 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5384 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5385 " 1 | test (int ch)\n"
5386 " 2 | {\n"
5387 " 3 | putchar (ch);\n"
5388 " | ^~~~~~~\n",
5389 pp_formatted_text (dc.printer));
5393 /* Replacement fix-it hint containing a newline.
5394 This will fail, as newlines are only supported when inserting at the
5395 beginning of a line. */
5397 static void
5398 test_fixit_replace_containing_newline (const line_table_case &case_)
5400 /* Create a tempfile and write some text to it.
5401 .........................0000000001111.
5402 .........................1234567890123. */
5403 const char *old_content = "foo = bar ();\n";
5405 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5406 line_table_test ltt (case_);
5407 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5409 /* Replace the " = " with "\n = ", as if we were reformatting an
5410 overly long line. */
5411 location_t start = linemap_position_for_column (line_table, 4);
5412 location_t finish = linemap_position_for_column (line_table, 6);
5413 location_t loc = linemap_position_for_column (line_table, 13);
5414 rich_location richloc (line_table, loc);
5415 source_range range = source_range::from_locations (start, finish);
5416 richloc.add_fixit_replace (range, "\n =");
5418 /* Arbitrary newlines are not yet supported within fix-it hints, so
5419 the fix-it should not be displayed. */
5420 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5422 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5423 return;
5425 test_diagnostic_context dc;
5426 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5427 ASSERT_STREQ (" foo = bar ();\n"
5428 " ^\n",
5429 pp_formatted_text (dc.printer));
5432 /* Fix-it hint, attempting to delete a newline.
5433 This will fail, as we currently only support fix-it hints that
5434 affect one line at a time. */
5436 static void
5437 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5439 /* Create a tempfile and write some text to it.
5440 ..........................0000000001111.
5441 ..........................1234567890123. */
5442 const char *old_content = ("foo = bar (\n"
5443 " );\n");
5445 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5446 line_table_test ltt (case_);
5447 const line_map_ordinary *ord_map = linemap_check_ordinary
5448 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5449 linemap_line_start (line_table, 1, 100);
5451 /* Attempt to delete the " (\n...)". */
5452 location_t start
5453 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5454 location_t caret
5455 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5456 location_t finish
5457 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5458 location_t loc = make_location (caret, start, finish);
5459 rich_location richloc (line_table, loc);
5460 richloc. add_fixit_remove ();
5462 /* Fix-it hints that affect more than one line are not yet supported, so
5463 the fix-it should not be displayed. */
5464 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5466 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5467 return;
5469 test_diagnostic_context dc;
5470 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5471 ASSERT_STREQ (" foo = bar (\n"
5472 " ~^\n"
5473 " );\n"
5474 " ~ \n",
5475 pp_formatted_text (dc.printer));
5478 static void
5479 test_tab_expansion (const line_table_case &case_)
5481 /* Create a tempfile and write some text to it. This example uses a tabstop
5482 of 8, as the column numbers attempt to indicate:
5484 .....................000.01111111111.22222333333 display
5485 .....................123.90123456789.56789012345 columns */
5486 const char *content = " \t This: `\t' is a tab.\n";
5487 /* ....................000 00000011111 11111222222 byte
5488 ....................123 45678901234 56789012345 columns */
5490 const int tabstop = 8;
5491 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5492 const int first_non_ws_byte_col = 7;
5493 const int right_quote_byte_col = 15;
5494 const int last_byte_col = 25;
5495 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5497 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5498 line_table_test ltt (case_);
5499 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5501 /* Don't attempt to run the tests if column data might be unavailable. */
5502 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5503 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5504 return;
5506 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5507 into 11 spaces. Recall that print_line() also puts one space before
5508 everything too. */
5510 test_diagnostic_context dc;
5511 dc.tabstop = tabstop;
5512 rich_location richloc (line_table,
5513 linemap_position_for_column (line_table,
5514 first_non_ws_byte_col));
5515 layout test_layout (&dc, &richloc, DK_ERROR);
5516 test_layout.print_line (1);
5517 ASSERT_STREQ (" This: ` ' is a tab.\n"
5518 " ^\n",
5519 pp_formatted_text (dc.printer));
5522 /* Confirm the display width was tracked correctly across the internal tab
5523 as well. */
5525 test_diagnostic_context dc;
5526 dc.tabstop = tabstop;
5527 rich_location richloc (line_table,
5528 linemap_position_for_column (line_table,
5529 right_quote_byte_col));
5530 layout test_layout (&dc, &richloc, DK_ERROR);
5531 test_layout.print_line (1);
5532 ASSERT_STREQ (" This: ` ' is a tab.\n"
5533 " ^\n",
5534 pp_formatted_text (dc.printer));
5538 /* Verify that the escaping machinery can cope with a variety of different
5539 invalid bytes. */
5541 static void
5542 test_escaping_bytes_1 (const line_table_case &case_)
5544 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5545 const size_t sz = sizeof (content);
5546 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5547 line_table_test ltt (case_);
5548 const line_map_ordinary *ord_map = linemap_check_ordinary
5549 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5550 linemap_line_start (line_table, 1, 100);
5552 location_t finish
5553 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5554 strlen (content));
5556 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5557 return;
5559 /* Locations of the NUL and \v bytes. */
5560 location_t nul_loc
5561 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5562 location_t v_loc
5563 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5564 gcc_rich_location richloc (nul_loc);
5565 richloc.add_range (v_loc);
5568 test_diagnostic_context dc;
5569 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5570 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5571 " ^ ~\n",
5572 pp_formatted_text (dc.printer));
5574 richloc.set_escape_on_output (true);
5576 test_diagnostic_context dc;
5577 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5578 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5579 ASSERT_STREQ
5580 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5581 " ^~~~~~~~ ~~~~~~~~\n",
5582 pp_formatted_text (dc.printer));
5585 test_diagnostic_context dc;
5586 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5587 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5588 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5589 " ^~~~ ~~~~\n",
5590 pp_formatted_text (dc.printer));
5594 /* As above, but verify that we handle the initial byte of a line
5595 correctly. */
5597 static void
5598 test_escaping_bytes_2 (const line_table_case &case_)
5600 const char content[] = "\0after\n";
5601 const size_t sz = sizeof (content);
5602 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5603 line_table_test ltt (case_);
5604 const line_map_ordinary *ord_map = linemap_check_ordinary
5605 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5606 linemap_line_start (line_table, 1, 100);
5608 location_t finish
5609 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5610 strlen (content));
5612 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5613 return;
5615 /* Location of the NUL byte. */
5616 location_t nul_loc
5617 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5618 gcc_rich_location richloc (nul_loc);
5621 test_diagnostic_context dc;
5622 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5623 ASSERT_STREQ (" after\n"
5624 " ^\n",
5625 pp_formatted_text (dc.printer));
5627 richloc.set_escape_on_output (true);
5629 test_diagnostic_context dc;
5630 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5631 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5632 ASSERT_STREQ (" <U+0000>after\n"
5633 " ^~~~~~~~\n",
5634 pp_formatted_text (dc.printer));
5637 test_diagnostic_context dc;
5638 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5639 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5640 ASSERT_STREQ (" <00>after\n"
5641 " ^~~~\n",
5642 pp_formatted_text (dc.printer));
5646 /* Verify that line numbers are correctly printed for the case of
5647 a multiline range in which the width of the line numbers changes
5648 (e.g. from "9" to "10"). */
5650 static void
5651 test_line_numbers_multiline_range ()
5653 /* Create a tempfile and write some text to it. */
5654 pretty_printer pp;
5655 for (int i = 0; i < 20; i++)
5656 /* .........0000000001111111.
5657 .............1234567890123456. */
5658 pp_printf (&pp, "this is line %i\n", i + 1);
5659 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5660 line_table_test ltt;
5662 const line_map_ordinary *ord_map = linemap_check_ordinary
5663 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5664 linemap_line_start (line_table, 1, 100);
5666 /* Create a multi-line location, starting at the "line" of line 9, with
5667 a caret on the "is" of line 10, finishing on the "this" line 11. */
5669 location_t start
5670 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5671 location_t caret
5672 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5673 location_t finish
5674 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5675 location_t loc = make_location (caret, start, finish);
5677 test_diagnostic_context dc;
5678 dc.show_line_numbers_p = true;
5679 dc.min_margin_width = 0;
5680 gcc_rich_location richloc (loc);
5681 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5682 ASSERT_STREQ (" 9 | this is line 9\n"
5683 " | ~~~~~~\n"
5684 "10 | this is line 10\n"
5685 " | ~~~~~^~~~~~~~~~\n"
5686 "11 | this is line 11\n"
5687 " | ~~~~ \n",
5688 pp_formatted_text (dc.printer));
5691 /* Run all of the selftests within this file. */
5693 void
5694 diagnostic_show_locus_cc_tests ()
5696 test_line_span ();
5698 test_layout_range_for_single_point ();
5699 test_layout_range_for_single_line ();
5700 test_layout_range_for_multiple_lines ();
5702 test_display_widths ();
5704 for_each_line_table_case (test_layout_x_offset_display_utf8);
5705 for_each_line_table_case (test_layout_x_offset_display_tab);
5707 test_get_line_bytes_without_trailing_whitespace ();
5709 test_diagnostic_show_locus_unknown_location ();
5711 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5712 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5713 for_each_line_table_case (test_add_location_if_nearby);
5714 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5715 for_each_line_table_case (test_fixit_consolidation);
5716 for_each_line_table_case (test_overlapped_fixit_printing);
5717 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5718 for_each_line_table_case (test_overlapped_fixit_printing_2);
5719 for_each_line_table_case (test_fixit_insert_containing_newline);
5720 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5721 for_each_line_table_case (test_fixit_replace_containing_newline);
5722 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5723 for_each_line_table_case (test_tab_expansion);
5724 for_each_line_table_case (test_escaping_bytes_1);
5725 for_each_line_table_case (test_escaping_bytes_2);
5727 test_line_numbers_multiline_range ();
5730 } // namespace selftest
5732 #endif /* #if CHECKING_P */
5734 #if __GNUC__ >= 10
5735 # pragma GCC diagnostic pop
5736 #endif