Daily bump.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob24bd0313126fd6ede858c4f7c9702a1264391708
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2021 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, int tabstop)
179 : expanded_location (exploc),
180 m_display_col (location_compute_display_column (exploc, tabstop))
183 int m_display_col;
187 /* A point within a layout_range; similar to an exploc_with_display_col,
188 but after filtering on file. */
190 class layout_point
192 public:
193 layout_point (const exploc_with_display_col &exploc)
194 : m_line (exploc.line)
196 m_columns[CU_BYTES] = exploc.column;
197 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
200 linenum_type m_line;
201 int m_columns[CU_NUM_UNITS];
204 /* A class for use by "class layout" below: a filtered location_range. */
206 class layout_range
208 public:
209 layout_range (const exploc_with_display_col &start_exploc,
210 const exploc_with_display_col &finish_exploc,
211 enum range_display_kind range_display_kind,
212 const exploc_with_display_col &caret_exploc,
213 unsigned original_idx,
214 const range_label *label);
216 bool contains_point (linenum_type row, int column,
217 enum column_unit col_unit) const;
218 bool intersects_line_p (linenum_type row) const;
220 layout_point m_start;
221 layout_point m_finish;
222 enum range_display_kind m_range_display_kind;
223 layout_point m_caret;
224 unsigned m_original_idx;
225 const range_label *m_label;
228 /* A struct for use by layout::print_source_line for telling
229 layout::print_annotation_line the extents of the source line that
230 it printed, so that underlines can be clipped appropriately. Units
231 are 1-based display columns. */
233 struct line_bounds
235 int m_first_non_ws_disp_col;
236 int m_last_non_ws_disp_col;
238 line_bounds ()
240 m_first_non_ws_disp_col = INT_MAX;
241 m_last_non_ws_disp_col = 0;
245 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
246 or "line 23"). During the layout ctor, layout::calculate_line_spans
247 splits the pertinent source lines into a list of disjoint line_span
248 instances (e.g. lines 5-10, lines 15-20, line 23). */
250 class line_span
252 public:
253 line_span (linenum_type first_line, linenum_type last_line)
254 : m_first_line (first_line), m_last_line (last_line)
256 gcc_assert (first_line <= last_line);
258 linenum_type get_first_line () const { return m_first_line; }
259 linenum_type get_last_line () const { return m_last_line; }
261 bool contains_line_p (linenum_type line) const
263 return line >= m_first_line && line <= m_last_line;
266 static int comparator (const void *p1, const void *p2)
268 const line_span *ls1 = (const line_span *)p1;
269 const line_span *ls2 = (const line_span *)p2;
270 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
271 if (first_line_cmp)
272 return first_line_cmp;
273 return compare (ls1->m_last_line, ls2->m_last_line);
276 linenum_type m_first_line;
277 linenum_type m_last_line;
280 #if CHECKING_P
282 /* Selftests for line_span. */
284 static void
285 test_line_span ()
287 line_span line_one (1, 1);
288 ASSERT_EQ (1, line_one.get_first_line ());
289 ASSERT_EQ (1, line_one.get_last_line ());
290 ASSERT_FALSE (line_one.contains_line_p (0));
291 ASSERT_TRUE (line_one.contains_line_p (1));
292 ASSERT_FALSE (line_one.contains_line_p (2));
294 line_span lines_1_to_3 (1, 3);
295 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
296 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
297 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
298 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
300 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
301 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
302 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
304 /* A linenum > 2^31. */
305 const linenum_type LARGEST_LINE = 0xffffffff;
306 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
307 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
308 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
310 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
311 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
314 #endif /* #if CHECKING_P */
316 /* A class to control the overall layout when printing a diagnostic.
318 The layout is determined within the constructor.
319 It is then printed by repeatedly calling the "print_source_line",
320 "print_annotation_line" and "print_any_fixits" methods.
322 We assume we have disjoint ranges. */
324 class layout
326 public:
327 layout (diagnostic_context *context,
328 rich_location *richloc,
329 diagnostic_t diagnostic_kind);
331 bool maybe_add_location_range (const location_range *loc_range,
332 unsigned original_idx,
333 bool restrict_to_current_line_spans);
335 int get_num_line_spans () const { return m_line_spans.length (); }
336 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
338 int get_linenum_width () const { return m_linenum_width; }
339 int get_x_offset_display () const { return m_x_offset_display; }
341 void print_gap_in_line_numbering ();
342 bool print_heading_for_line_span_index_p (int line_span_idx) const;
344 expanded_location get_expanded_location (const line_span *) const;
346 void print_line (linenum_type row);
348 private:
349 bool will_show_line_p (linenum_type row) const;
350 void print_leading_fixits (linenum_type row);
351 line_bounds print_source_line (linenum_type row, const char *line,
352 int line_bytes);
353 bool should_print_annotation_line_p (linenum_type row) const;
354 void start_annotation_line (char margin_char = ' ') const;
355 void print_annotation_line (linenum_type row, const line_bounds lbounds);
356 void print_any_labels (linenum_type row);
357 void print_trailing_fixits (linenum_type row);
359 bool annotation_line_showed_range_p (linenum_type line, int start_column,
360 int finish_column) const;
361 void show_ruler (int max_column) const;
363 bool validate_fixit_hint_p (const fixit_hint *hint);
365 void calculate_line_spans ();
366 void calculate_linenum_width ();
367 void calculate_x_offset_display ();
369 void print_newline ();
371 bool
372 get_state_at_point (/* Inputs. */
373 linenum_type row, int column,
374 int first_non_ws, int last_non_ws,
375 enum column_unit col_unit,
376 /* Outputs. */
377 point_state *out_state);
380 get_x_bound_for_row (linenum_type row, int caret_column,
381 int last_non_ws);
383 void
384 move_to_column (int *column, int dest_column, bool add_left_margin);
386 private:
387 diagnostic_context *m_context;
388 pretty_printer *m_pp;
389 location_t m_primary_loc;
390 exploc_with_display_col m_exploc;
391 colorizer m_colorizer;
392 bool m_colorize_source_p;
393 bool m_show_labels_p;
394 bool m_show_line_numbers_p;
395 bool m_diagnostic_path_p;
396 auto_vec <layout_range> m_layout_ranges;
397 auto_vec <const fixit_hint *> m_fixit_hints;
398 auto_vec <line_span> m_line_spans;
399 int m_linenum_width;
400 int m_x_offset_display;
403 /* Implementation of "class colorizer". */
405 /* The constructor for "colorizer". Lookup and store color codes for the
406 different kinds of things we might need to print. */
408 colorizer::colorizer (diagnostic_context *context,
409 diagnostic_t diagnostic_kind) :
410 m_context (context),
411 m_diagnostic_kind (diagnostic_kind),
412 m_current_state (STATE_NORMAL_TEXT)
414 m_range1 = get_color_by_name ("range1");
415 m_range2 = get_color_by_name ("range2");
416 m_fixit_insert = get_color_by_name ("fixit-insert");
417 m_fixit_delete = get_color_by_name ("fixit-delete");
418 m_stop_color = colorize_stop (pp_show_color (context->printer));
421 /* The destructor for "colorize". If colorization is on, print a code to
422 turn it off. */
424 colorizer::~colorizer ()
426 finish_state (m_current_state);
429 /* Update state, printing color codes if necessary if there's a state
430 change. */
432 void
433 colorizer::set_state (int new_state)
435 if (m_current_state != new_state)
437 finish_state (m_current_state);
438 m_current_state = new_state;
439 begin_state (new_state);
443 /* Turn on any colorization for STATE. */
445 void
446 colorizer::begin_state (int state)
448 switch (state)
450 case STATE_NORMAL_TEXT:
451 break;
453 case STATE_FIXIT_INSERT:
454 pp_string (m_context->printer, m_fixit_insert);
455 break;
457 case STATE_FIXIT_DELETE:
458 pp_string (m_context->printer, m_fixit_delete);
459 break;
461 case 0:
462 /* Make range 0 be the same color as the "kind" text
463 (error vs warning vs note). */
464 pp_string
465 (m_context->printer,
466 colorize_start (pp_show_color (m_context->printer),
467 diagnostic_get_color_for_kind (m_diagnostic_kind)));
468 break;
470 case 1:
471 pp_string (m_context->printer, m_range1);
472 break;
474 case 2:
475 pp_string (m_context->printer, m_range2);
476 break;
478 default:
479 /* For ranges beyond 2, alternate between color 1 and color 2. */
481 gcc_assert (state > 2);
482 pp_string (m_context->printer,
483 state % 2 ? m_range1 : m_range2);
485 break;
489 /* Turn off any colorization for STATE. */
491 void
492 colorizer::finish_state (int state)
494 if (state != STATE_NORMAL_TEXT)
495 pp_string (m_context->printer, m_stop_color);
498 /* Get the color code for NAME (or the empty string if
499 colorization is disabled). */
501 const char *
502 colorizer::get_color_by_name (const char *name)
504 return colorize_start (pp_show_color (m_context->printer), name);
507 /* Implementation of class layout_range. */
509 /* The constructor for class layout_range.
510 Initialize various layout_point fields from expanded_location
511 equivalents; we've already filtered on file. */
513 layout_range::layout_range (const exploc_with_display_col &start_exploc,
514 const exploc_with_display_col &finish_exploc,
515 enum range_display_kind range_display_kind,
516 const exploc_with_display_col &caret_exploc,
517 unsigned original_idx,
518 const range_label *label)
519 : m_start (start_exploc),
520 m_finish (finish_exploc),
521 m_range_display_kind (range_display_kind),
522 m_caret (caret_exploc),
523 m_original_idx (original_idx),
524 m_label (label)
528 /* Is (column, row) within the given range?
529 We've already filtered on the file.
531 Ranges are closed (both limits are within the range).
533 Example A: a single-line range:
534 start: (col=22, line=2)
535 finish: (col=38, line=2)
537 |00000011111111112222222222333333333344444444444
538 |34567890123456789012345678901234567890123456789
539 --+-----------------------------------------------
540 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
541 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
542 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
544 Example B: a multiline range with
545 start: (col=14, line=3)
546 finish: (col=08, line=5)
548 |00000011111111112222222222333333333344444444444
549 |34567890123456789012345678901234567890123456789
550 --+-----------------------------------------------
551 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
552 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
553 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
554 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
555 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
556 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
557 --+-----------------------------------------------
559 Legend:
560 - 'b' indicates a point *before* the range
561 - 'S' indicates the start of the range
562 - 'w' indicates a point within the range
563 - 'F' indicates the finish of the range (which is
564 within it).
565 - 'a' indicates a subsequent point *after* the range.
567 COL_UNIT controls whether we check the byte column or
568 the display column; one or the other is more convenient
569 depending on the context. */
571 bool
572 layout_range::contains_point (linenum_type row, int column,
573 enum column_unit col_unit) const
575 gcc_assert (m_start.m_line <= m_finish.m_line);
576 /* ...but the equivalent isn't true for the columns;
577 consider example B in the comment above. */
579 if (row < m_start.m_line)
580 /* Points before the first line of the range are
581 outside it (corresponding to line 01 in example A
582 and lines 01 and 02 in example B above). */
583 return false;
585 if (row == m_start.m_line)
586 /* On same line as start of range (corresponding
587 to line 02 in example A and line 03 in example B). */
589 if (column < m_start.m_columns[col_unit])
590 /* Points on the starting line of the range, but
591 before the column in which it begins. */
592 return false;
594 if (row < m_finish.m_line)
595 /* This is a multiline range; the point
596 is within it (corresponds to line 03 in example B
597 from column 14 onwards) */
598 return true;
599 else
601 /* This is a single-line range. */
602 gcc_assert (row == m_finish.m_line);
603 return column <= m_finish.m_columns[col_unit];
607 /* The point is in a line beyond that containing the
608 start of the range: lines 03 onwards in example A,
609 and lines 04 onwards in example B. */
610 gcc_assert (row > m_start.m_line);
612 if (row > m_finish.m_line)
613 /* The point is beyond the final line of the range
614 (lines 03 onwards in example A, and lines 06 onwards
615 in example B). */
616 return false;
618 if (row < m_finish.m_line)
620 /* The point is in a line that's fully within a multiline
621 range (e.g. line 04 in example B). */
622 gcc_assert (m_start.m_line < m_finish.m_line);
623 return true;
626 gcc_assert (row == m_finish.m_line);
628 return column <= m_finish.m_columns[col_unit];
631 /* Does this layout_range contain any part of line ROW? */
633 bool
634 layout_range::intersects_line_p (linenum_type row) const
636 gcc_assert (m_start.m_line <= m_finish.m_line);
637 if (row < m_start.m_line)
638 return false;
639 if (row > m_finish.m_line)
640 return false;
641 return true;
644 #if CHECKING_P
646 /* Default for when we don't care what the tab expansion is set to. */
647 static const int def_tabstop = 8;
649 /* Create some expanded locations for testing layout_range. The filename
650 member of the explocs is set to the empty string. This member will only be
651 inspected by the calls to location_compute_display_column() made from the
652 layout_point constructors. That function will check for an empty filename
653 argument and not attempt to open it, rather treating the non-existent data
654 as if the display width were the same as the byte count. Tests exercising a
655 real difference between byte count and display width are performed later,
656 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
658 static layout_range
659 make_range (int start_line, int start_col, int end_line, int end_col)
661 const expanded_location start_exploc
662 = {"", start_line, start_col, NULL, false};
663 const expanded_location finish_exploc
664 = {"", end_line, end_col, NULL, false};
665 return layout_range (exploc_with_display_col (start_exploc, def_tabstop),
666 exploc_with_display_col (finish_exploc, def_tabstop),
667 SHOW_RANGE_WITHOUT_CARET,
668 exploc_with_display_col (start_exploc, def_tabstop),
669 0, NULL);
672 /* Selftests for layout_range::contains_point and
673 layout_range::intersects_line_p. */
675 /* Selftest for layout_range, where the layout_range
676 is a range with start==end i.e. a single point. */
678 static void
679 test_layout_range_for_single_point ()
681 layout_range point = make_range (7, 10, 7, 10);
683 /* Tests for layout_range::contains_point. */
685 for (int i = 0; i != CU_NUM_UNITS; ++i)
687 const enum column_unit col_unit = (enum column_unit) i;
689 /* Before the line. */
690 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
692 /* On the line, but before start. */
693 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
695 /* At the point. */
696 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
698 /* On the line, after the point. */
699 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
701 /* After the line. */
702 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
705 /* Tests for layout_range::intersects_line_p. */
706 ASSERT_FALSE (point.intersects_line_p (6));
707 ASSERT_TRUE (point.intersects_line_p (7));
708 ASSERT_FALSE (point.intersects_line_p (8));
711 /* Selftest for layout_range, where the layout_range
712 is the single-line range shown as "Example A" above. */
714 static void
715 test_layout_range_for_single_line ()
717 layout_range example_a = make_range (2, 22, 2, 38);
719 /* Tests for layout_range::contains_point. */
721 for (int i = 0; i != CU_NUM_UNITS; ++i)
723 const enum column_unit col_unit = (enum column_unit) i;
725 /* Before the line. */
726 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
728 /* On the line, but before start. */
729 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
731 /* On the line, at the start. */
732 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
734 /* On the line, within the range. */
735 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
737 /* On the line, at the end. */
738 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
740 /* On the line, after the end. */
741 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
743 /* After the line. */
744 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
747 /* Tests for layout_range::intersects_line_p. */
748 ASSERT_FALSE (example_a.intersects_line_p (1));
749 ASSERT_TRUE (example_a.intersects_line_p (2));
750 ASSERT_FALSE (example_a.intersects_line_p (3));
753 /* Selftest for layout_range, where the layout_range
754 is the multi-line range shown as "Example B" above. */
756 static void
757 test_layout_range_for_multiple_lines ()
759 layout_range example_b = make_range (3, 14, 5, 8);
761 /* Tests for layout_range::contains_point. */
763 for (int i = 0; i != CU_NUM_UNITS; ++i)
765 const enum column_unit col_unit = (enum column_unit) i;
767 /* Before first line. */
768 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
770 /* On the first line, but before start. */
771 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
773 /* At the start. */
774 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
776 /* On the first line, within the range. */
777 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
779 /* On an interior line.
780 The column number should not matter; try various boundary
781 values. */
782 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
783 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
784 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
785 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
786 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
787 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
788 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
790 /* On the final line, before the end. */
791 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
793 /* On the final line, at the end. */
794 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
796 /* On the final line, after the end. */
797 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
799 /* After the line. */
800 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
803 /* Tests for layout_range::intersects_line_p. */
804 ASSERT_FALSE (example_b.intersects_line_p (2));
805 ASSERT_TRUE (example_b.intersects_line_p (3));
806 ASSERT_TRUE (example_b.intersects_line_p (4));
807 ASSERT_TRUE (example_b.intersects_line_p (5));
808 ASSERT_FALSE (example_b.intersects_line_p (6));
811 #endif /* #if CHECKING_P */
813 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
814 (still in bytes, not display cols) without any trailing whitespace. */
816 static int
817 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
819 int result = line_bytes;
820 while (result > 0)
822 char ch = line[result - 1];
823 if (ch == ' ' || ch == '\t' || ch == '\r')
824 result--;
825 else
826 break;
828 gcc_assert (result >= 0);
829 gcc_assert (result <= line_bytes);
830 gcc_assert (result == 0 ||
831 (line[result - 1] != ' '
832 && line[result -1] != '\t'
833 && line[result -1] != '\r'));
834 return result;
837 #if CHECKING_P
839 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
841 static void
842 assert_eq (const char *line, int expected_bytes)
844 int actual_value
845 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
846 ASSERT_EQ (actual_value, expected_bytes);
849 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
850 various inputs. It is not required to handle newlines. */
852 static void
853 test_get_line_bytes_without_trailing_whitespace ()
855 assert_eq ("", 0);
856 assert_eq (" ", 0);
857 assert_eq ("\t", 0);
858 assert_eq ("\r", 0);
859 assert_eq ("hello world", 11);
860 assert_eq ("hello world ", 11);
861 assert_eq ("hello world \t\t ", 11);
862 assert_eq ("hello world\r", 11);
865 #endif /* #if CHECKING_P */
867 /* Helper function for layout's ctor, for sanitizing locations relative
868 to the primary location within a diagnostic.
870 Compare LOC_A and LOC_B to see if it makes sense to print underlines
871 connecting their expanded locations. Doing so is only guaranteed to
872 make sense if the locations share the same macro expansion "history"
873 i.e. they can be traced through the same macro expansions, eventually
874 reaching an ordinary map.
876 This may be too strong a condition, but it effectively sanitizes
877 PR c++/70105, which has an example of printing an expression where the
878 final location of the expression is in a different macro, which
879 erroneously was leading to hundreds of lines of irrelevant source
880 being printed. */
882 static bool
883 compatible_locations_p (location_t loc_a, location_t loc_b)
885 if (IS_ADHOC_LOC (loc_a))
886 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
887 if (IS_ADHOC_LOC (loc_b))
888 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
890 /* If either location is one of the special locations outside of a
891 linemap, they are only compatible if they are equal. */
892 if (loc_a < RESERVED_LOCATION_COUNT
893 || loc_b < RESERVED_LOCATION_COUNT)
894 return loc_a == loc_b;
896 const line_map *map_a = linemap_lookup (line_table, loc_a);
897 linemap_assert (map_a);
899 const line_map *map_b = linemap_lookup (line_table, loc_b);
900 linemap_assert (map_b);
902 /* Are they within the same map? */
903 if (map_a == map_b)
905 /* Are both within the same macro expansion? */
906 if (linemap_macro_expansion_map_p (map_a))
908 /* If so, then they're only compatible if either both are
909 from the macro definition, or both from the macro arguments. */
910 bool loc_a_from_defn
911 = linemap_location_from_macro_definition_p (line_table, loc_a);
912 bool loc_b_from_defn
913 = linemap_location_from_macro_definition_p (line_table, loc_b);
914 if (loc_a_from_defn != loc_b_from_defn)
915 return false;
917 /* Expand each location towards the spelling location, and
918 recurse. */
919 const line_map_macro *macro_map = linemap_check_macro (map_a);
920 location_t loc_a_toward_spelling
921 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
922 macro_map,
923 loc_a);
924 location_t loc_b_toward_spelling
925 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
926 macro_map,
927 loc_b);
928 return compatible_locations_p (loc_a_toward_spelling,
929 loc_b_toward_spelling);
932 /* Otherwise they are within the same ordinary map. */
933 return true;
935 else
937 /* Within different maps. */
939 /* If either is within a macro expansion, they are incompatible. */
940 if (linemap_macro_expansion_map_p (map_a)
941 || linemap_macro_expansion_map_p (map_b))
942 return false;
944 /* Within two different ordinary maps; they are compatible iff they
945 are in the same file. */
946 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
947 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
948 return ord_map_a->to_file == ord_map_b->to_file;
952 /* Comparator for sorting fix-it hints. */
954 static int
955 fixit_cmp (const void *p_a, const void *p_b)
957 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
958 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
959 return hint_a->get_start_loc () - hint_b->get_start_loc ();
962 /* Implementation of class layout. */
964 /* Constructor for class layout.
966 Filter the ranges from the rich_location to those that we can
967 sanely print, populating m_layout_ranges and m_fixit_hints.
968 Determine the range of lines that we will print, splitting them
969 up into an ordered list of disjoint spans of contiguous line numbers.
970 Determine m_x_offset_display, to ensure that the primary caret
971 will fit within the max_width provided by the diagnostic_context. */
973 layout::layout (diagnostic_context * context,
974 rich_location *richloc,
975 diagnostic_t diagnostic_kind)
976 : m_context (context),
977 m_pp (context->printer),
978 m_primary_loc (richloc->get_range (0)->m_loc),
979 m_exploc (richloc->get_expanded_location (0), context->tabstop),
980 m_colorizer (context, diagnostic_kind),
981 m_colorize_source_p (context->colorize_source_p),
982 m_show_labels_p (context->show_labels_p),
983 m_show_line_numbers_p (context->show_line_numbers_p),
984 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
985 m_layout_ranges (richloc->get_num_locations ()),
986 m_fixit_hints (richloc->get_num_fixit_hints ()),
987 m_line_spans (1 + richloc->get_num_locations ()),
988 m_linenum_width (0),
989 m_x_offset_display (0)
991 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
993 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
994 Ignore any ranges that are awkward to handle. */
995 const location_range *loc_range = richloc->get_range (idx);
996 maybe_add_location_range (loc_range, idx, false);
999 /* Populate m_fixit_hints, filtering to only those that are in the
1000 same file. */
1001 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1003 const fixit_hint *hint = richloc->get_fixit_hint (i);
1004 if (validate_fixit_hint_p (hint))
1005 m_fixit_hints.safe_push (hint);
1008 /* Sort m_fixit_hints. */
1009 m_fixit_hints.qsort (fixit_cmp);
1011 /* Populate the indicated members. */
1012 calculate_line_spans ();
1013 calculate_linenum_width ();
1014 calculate_x_offset_display ();
1016 if (context->show_ruler_p)
1017 show_ruler (m_x_offset_display + m_context->caret_max_width);
1021 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1022 those that we can sanely print.
1024 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1025 (for use as extrinsic state by label ranges FIXME).
1027 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1028 filtered against this layout instance's current line spans: it
1029 will only be added if the location is fully within the lines
1030 already specified by other locations.
1032 Return true iff LOC_RANGE was added. */
1034 bool
1035 layout::maybe_add_location_range (const location_range *loc_range,
1036 unsigned original_idx,
1037 bool restrict_to_current_line_spans)
1039 gcc_assert (loc_range);
1041 /* Split the "range" into caret and range information. */
1042 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1044 /* Expand the various locations. */
1045 expanded_location start
1046 = linemap_client_expand_location_to_spelling_point
1047 (src_range.m_start, LOCATION_ASPECT_START);
1048 expanded_location finish
1049 = linemap_client_expand_location_to_spelling_point
1050 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1051 expanded_location caret
1052 = linemap_client_expand_location_to_spelling_point
1053 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1055 /* If any part of the range isn't in the same file as the primary
1056 location of this diagnostic, ignore the range. */
1057 if (start.file != m_exploc.file)
1058 return false;
1059 if (finish.file != m_exploc.file)
1060 return false;
1061 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1062 if (caret.file != m_exploc.file)
1063 return false;
1065 /* Sanitize the caret location for non-primary ranges. */
1066 if (m_layout_ranges.length () > 0)
1067 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1068 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1069 /* Discard any non-primary ranges that can't be printed
1070 sanely relative to the primary location. */
1071 return false;
1073 /* Everything is now known to be in the correct source file,
1074 but it may require further sanitization. */
1075 layout_range ri (exploc_with_display_col (start, m_context->tabstop),
1076 exploc_with_display_col (finish, m_context->tabstop),
1077 loc_range->m_range_display_kind,
1078 exploc_with_display_col (caret, m_context->tabstop),
1079 original_idx, loc_range->m_label);
1081 /* If we have a range that finishes before it starts (perhaps
1082 from something built via macro expansion), printing the
1083 range is likely to be nonsensical. Also, attempting to do so
1084 breaks assumptions within the printing code (PR c/68473).
1085 Similarly, don't attempt to print ranges if one or both ends
1086 of the range aren't sane to print relative to the
1087 primary location (PR c++/70105). */
1088 if (start.line > finish.line
1089 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1090 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1092 /* Is this the primary location? */
1093 if (m_layout_ranges.length () == 0)
1095 /* We want to print the caret for the primary location, but
1096 we must sanitize away m_start and m_finish. */
1097 ri.m_start = ri.m_caret;
1098 ri.m_finish = ri.m_caret;
1100 else
1101 /* This is a non-primary range; ignore it. */
1102 return false;
1105 /* Potentially filter to just the lines already specified by other
1106 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1107 The layout ctor doesn't use it, and can't because m_line_spans
1108 hasn't been set up at that point. */
1109 if (restrict_to_current_line_spans)
1111 if (!will_show_line_p (start.line))
1112 return false;
1113 if (!will_show_line_p (finish.line))
1114 return false;
1115 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1116 if (!will_show_line_p (caret.line))
1117 return false;
1120 /* Passed all the tests; add the range to m_layout_ranges so that
1121 it will be printed. */
1122 m_layout_ranges.safe_push (ri);
1123 return true;
1126 /* Return true iff ROW is within one of the line spans for this layout. */
1128 bool
1129 layout::will_show_line_p (linenum_type row) const
1131 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1132 line_span_idx++)
1134 const line_span *line_span = get_line_span (line_span_idx);
1135 if (line_span->contains_line_p (row))
1136 return true;
1138 return false;
1141 /* Print a line showing a gap in the line numbers, for showing the boundary
1142 between two line spans. */
1144 void
1145 layout::print_gap_in_line_numbering ()
1147 gcc_assert (m_show_line_numbers_p);
1149 pp_emit_prefix (m_pp);
1151 for (int i = 0; i < m_linenum_width + 1; i++)
1152 pp_character (m_pp, '.');
1154 pp_newline (m_pp);
1157 /* Return true iff we should print a heading when starting the
1158 line span with the given index. */
1160 bool
1161 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1163 /* We print a heading for every change of line span, hence for every
1164 line span after the initial one. */
1165 if (line_span_idx > 0)
1166 return true;
1168 /* We also do it for the initial span if the primary location of the
1169 diagnostic is in a different span. */
1170 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1171 return true;
1173 return false;
1176 /* Get an expanded_location for the first location of interest within
1177 the given line_span.
1178 Used when printing a heading to indicate a new line span. */
1180 expanded_location
1181 layout::get_expanded_location (const line_span *line_span) const
1183 /* Whenever possible, use the caret location. */
1184 if (line_span->contains_line_p (m_exploc.line))
1185 return m_exploc;
1187 /* Otherwise, use the start of the first range that's present
1188 within the line_span. */
1189 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1191 const layout_range *lr = &m_layout_ranges[i];
1192 if (line_span->contains_line_p (lr->m_start.m_line))
1194 expanded_location exploc = m_exploc;
1195 exploc.line = lr->m_start.m_line;
1196 exploc.column = lr->m_start.m_columns[CU_BYTES];
1197 return exploc;
1201 /* Otherwise, use the location of the first fixit-hint present within
1202 the line_span. */
1203 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1205 const fixit_hint *hint = m_fixit_hints[i];
1206 location_t loc = hint->get_start_loc ();
1207 expanded_location exploc = expand_location (loc);
1208 if (line_span->contains_line_p (exploc.line))
1209 return exploc;
1212 /* It should not be possible to have a line span that didn't
1213 contain any of the layout_range or fixit_hint instances. */
1214 gcc_unreachable ();
1215 return m_exploc;
1218 /* Determine if HINT is meaningful to print within this layout. */
1220 bool
1221 layout::validate_fixit_hint_p (const fixit_hint *hint)
1223 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1224 return false;
1225 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1226 return false;
1228 return true;
1231 /* Determine the range of lines affected by HINT.
1232 This assumes that HINT has already been filtered by
1233 validate_fixit_hint_p, and so affects the correct source file. */
1235 static line_span
1236 get_line_span_for_fixit_hint (const fixit_hint *hint)
1238 gcc_assert (hint);
1240 int start_line = LOCATION_LINE (hint->get_start_loc ());
1242 /* For line-insertion fix-it hints, add the previous line to the
1243 span, to give the user more context on the proposed change. */
1244 if (hint->ends_with_newline_p ())
1245 if (start_line > 1)
1246 start_line--;
1248 return line_span (start_line,
1249 LOCATION_LINE (hint->get_next_loc ()));
1252 /* We want to print the pertinent source code at a diagnostic. The
1253 rich_location can contain multiple locations. This will have been
1254 filtered into m_exploc (the caret for the primary location) and
1255 m_layout_ranges, for those ranges within the same source file.
1257 We will print a subset of the lines within the source file in question,
1258 as a collection of "spans" of lines.
1260 This function populates m_line_spans with an ordered, disjoint list of
1261 the line spans of interest.
1263 Printing a gap between line spans takes one line, so, when printing
1264 line numbers, we allow a gap of up to one line between spans when
1265 merging, since it makes more sense to print the source line rather than a
1266 "gap-in-line-numbering" line. When not printing line numbers, it's
1267 better to be more explicit about what's going on, so keeping them as
1268 separate spans is preferred.
1270 For example, if the primary range is on lines 8-10, with secondary ranges
1271 covering lines 5-6 and lines 13-15:
1274 005 |RANGE 1
1275 006 |RANGE 1
1277 008 |PRIMARY RANGE
1278 009 |PRIMARY CARET
1279 010 |PRIMARY RANGE
1282 013 |RANGE 2
1283 014 |RANGE 2
1284 015 |RANGE 2
1287 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1289 With line numbering off (with span headers), we want three spans: lines 5-6,
1290 lines 8-10, and lines 13-15. */
1292 void
1293 layout::calculate_line_spans ()
1295 /* This should only be called once, by the ctor. */
1296 gcc_assert (m_line_spans.length () == 0);
1298 /* Populate tmp_spans with individual spans, for each of
1299 m_exploc, and for m_layout_ranges. */
1300 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1301 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1302 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1304 const layout_range *lr = &m_layout_ranges[i];
1305 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1306 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1307 lr->m_finish.m_line));
1310 /* Also add spans for any fix-it hints, in case they cover other lines. */
1311 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1313 const fixit_hint *hint = m_fixit_hints[i];
1314 gcc_assert (hint);
1315 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1318 /* Sort them. */
1319 tmp_spans.qsort(line_span::comparator);
1321 /* Now iterate through tmp_spans, copying into m_line_spans, and
1322 combining where possible. */
1323 gcc_assert (tmp_spans.length () > 0);
1324 m_line_spans.safe_push (tmp_spans[0]);
1325 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1327 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1328 const line_span *next = &tmp_spans[i];
1329 gcc_assert (next->m_first_line >= current->m_first_line);
1330 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1331 if ((linenum_arith_t)next->m_first_line
1332 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1334 /* We can merge them. */
1335 if (next->m_last_line > current->m_last_line)
1336 current->m_last_line = next->m_last_line;
1338 else
1340 /* No merger possible. */
1341 m_line_spans.safe_push (*next);
1345 /* Verify the result, in m_line_spans. */
1346 gcc_assert (m_line_spans.length () > 0);
1347 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1349 const line_span *prev = &m_line_spans[i - 1];
1350 const line_span *next = &m_line_spans[i];
1351 /* The individual spans must be sane. */
1352 gcc_assert (prev->m_first_line <= prev->m_last_line);
1353 gcc_assert (next->m_first_line <= next->m_last_line);
1354 /* The spans must be ordered. */
1355 gcc_assert (prev->m_first_line < next->m_first_line);
1356 /* There must be a gap of at least one line between separate spans. */
1357 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1361 /* Determine how many display columns need to be reserved for line numbers,
1362 based on the largest line number that will be needed, and populate
1363 m_linenum_width. */
1365 void
1366 layout::calculate_linenum_width ()
1368 gcc_assert (m_line_spans.length () > 0);
1369 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1370 int highest_line = last_span->m_last_line;
1371 if (highest_line < 0)
1372 highest_line = 0;
1373 m_linenum_width = num_digits (highest_line);
1374 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1375 if (m_line_spans.length () > 1)
1376 m_linenum_width = MAX (m_linenum_width, 3);
1377 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1378 after the line number. */
1379 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1382 /* Calculate m_x_offset_display, which improves readability in case the source
1383 line of interest is longer than the user's display. All lines output will be
1384 shifted to the left (so that their beginning is no longer displayed) by
1385 m_x_offset_display display columns, so that the caret is in a reasonable
1386 location. */
1388 void
1389 layout::calculate_x_offset_display ()
1391 m_x_offset_display = 0;
1393 const int max_width = m_context->caret_max_width;
1394 if (!max_width)
1396 /* Nothing to do, the width is not capped. */
1397 return;
1400 const char_span line = location_get_source_line (m_exploc.file,
1401 m_exploc.line);
1402 if (!line)
1404 /* Nothing to do, we couldn't find the source line. */
1405 return;
1407 int caret_display_column = m_exploc.m_display_col;
1408 const int line_bytes
1409 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1410 line.length ());
1411 int eol_display_column
1412 = cpp_display_width (line.get_buffer (), line_bytes, m_context->tabstop);
1413 if (caret_display_column > eol_display_column
1414 || !caret_display_column)
1416 /* This does not make sense, so don't try to do anything in this case. */
1417 return;
1420 /* Adjust caret and eol positions to include the left margin. If we are
1421 outputting line numbers, then the left margin is equal to m_linenum_width
1422 plus three for the " | " which follows it. Otherwise the left margin width
1423 is equal to 1, because layout::print_source_line() will prefix each line
1424 with a space. */
1425 const int source_display_cols = eol_display_column;
1426 int left_margin_size = 1;
1427 if (m_show_line_numbers_p)
1428 left_margin_size = m_linenum_width + 3;
1429 caret_display_column += left_margin_size;
1430 eol_display_column += left_margin_size;
1432 if (eol_display_column <= max_width)
1434 /* Nothing to do, everything fits in the display. */
1435 return;
1438 /* The line is too long for the display. Calculate an offset such that the
1439 caret is not too close to the right edge of the screen. It will be
1440 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1441 than that to the end of the source line anyway. */
1442 int right_margin_size = CARET_LINE_MARGIN;
1443 right_margin_size = MIN (eol_display_column - caret_display_column,
1444 right_margin_size);
1445 if (right_margin_size + left_margin_size >= max_width)
1447 /* The max_width is very small, so anything we try to do will not be very
1448 effective; just punt in this case and output with no offset. */
1449 return;
1451 const int max_caret_display_column = max_width - right_margin_size;
1452 if (caret_display_column > max_caret_display_column)
1454 m_x_offset_display = caret_display_column - max_caret_display_column;
1455 /* Make sure we don't offset the line into oblivion. */
1456 static const int min_cols_visible = 2;
1457 if (source_display_cols - m_x_offset_display < min_cols_visible)
1458 m_x_offset_display = 0;
1462 /* Print line ROW of source code, potentially colorized at any ranges, and
1463 return the line bounds. LINE is the source line (not necessarily
1464 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1465 colorization and tab expansion, this function tracks the line position in
1466 both byte and display column units. */
1468 line_bounds
1469 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1471 m_colorizer.set_normal_text ();
1473 pp_emit_prefix (m_pp);
1474 if (m_show_line_numbers_p)
1476 int width = num_digits (row);
1477 for (int i = 0; i < m_linenum_width - width; i++)
1478 pp_space (m_pp);
1479 pp_printf (m_pp, "%i | ", row);
1481 else
1482 pp_space (m_pp);
1484 /* We will stop printing the source line at any trailing whitespace. */
1485 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1486 line_bytes);
1488 /* This object helps to keep track of which display column we are at, which is
1489 necessary for computing the line bounds in display units, for doing
1490 tab expansion, and for implementing m_x_offset_display. */
1491 cpp_display_width_computation dw (line, line_bytes, m_context->tabstop);
1493 /* Skip the first m_x_offset_display display columns. In case the leading
1494 portion that will be skipped ends with a character with wcwidth > 1, then
1495 it is possible we skipped too much, so account for that by padding with
1496 spaces. Note that this does the right thing too in case a tab was the last
1497 character to be skipped over; the tab is effectively replaced by the
1498 correct number of trailing spaces needed to offset by the desired number of
1499 display columns. */
1500 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1501 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1502 pp_space (m_pp);
1504 /* Print the line and compute the line_bounds. */
1505 line_bounds lbounds;
1506 while (!dw.done ())
1508 /* Assuming colorization is enabled for the caret and underline
1509 characters, we may also colorize the associated characters
1510 within the source line.
1512 For frontends that generate range information, we color the
1513 associated characters in the source line the same as the
1514 carets and underlines in the annotation line, to make it easier
1515 for the reader to see the pertinent code.
1517 For frontends that only generate carets, we don't colorize the
1518 characters above them, since this would look strange (e.g.
1519 colorizing just the first character in a token). */
1520 if (m_colorize_source_p)
1522 bool in_range_p;
1523 point_state state;
1524 const int start_byte_col = dw.bytes_processed () + 1;
1525 in_range_p = get_state_at_point (row, start_byte_col,
1526 0, INT_MAX,
1527 CU_BYTES,
1528 &state);
1529 if (in_range_p)
1530 m_colorizer.set_range (state.range_idx);
1531 else
1532 m_colorizer.set_normal_text ();
1535 /* Get the display width of the next character to be output, expanding
1536 tabs and replacing some control bytes with spaces as necessary. */
1537 const char *c = dw.next_byte ();
1538 const int start_disp_col = dw.display_cols_processed () + 1;
1539 const int this_display_width = dw.process_next_codepoint ();
1540 if (*c == '\t')
1542 /* The returned display width is the number of spaces into which the
1543 tab should be expanded. */
1544 for (int i = 0; i != this_display_width; ++i)
1545 pp_space (m_pp);
1546 continue;
1548 if (*c == '\0' || *c == '\r')
1550 /* cpp_wcwidth() promises to return 1 for all control bytes, and we
1551 want to output these as a single space too, so this case is
1552 actually the same as the '\t' case. */
1553 gcc_assert (this_display_width == 1);
1554 pp_space (m_pp);
1555 continue;
1558 /* We have a (possibly multibyte) character to output; update the line
1559 bounds if it is not whitespace. */
1560 if (*c != ' ')
1562 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1563 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1564 lbounds.m_first_non_ws_disp_col = start_disp_col;
1567 /* Output the character. */
1568 while (c != dw.next_byte ()) pp_character (m_pp, *c++);
1570 print_newline ();
1571 return lbounds;
1574 /* Determine if we should print an annotation line for ROW.
1575 i.e. if any of m_layout_ranges contains ROW. */
1577 bool
1578 layout::should_print_annotation_line_p (linenum_type row) const
1580 layout_range *range;
1581 int i;
1582 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1584 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1585 return false;
1586 if (range->intersects_line_p (row))
1587 return true;
1589 return false;
1592 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1593 margin, which is empty for annotation lines. Otherwise, do nothing. */
1595 void
1596 layout::start_annotation_line (char margin_char) const
1598 pp_emit_prefix (m_pp);
1599 if (m_show_line_numbers_p)
1601 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1602 of it, right-aligned, padded with spaces. */
1603 int i;
1604 for (i = 0; i < m_linenum_width - 3; i++)
1605 pp_space (m_pp);
1606 for (; i < m_linenum_width; i++)
1607 pp_character (m_pp, margin_char);
1608 pp_string (m_pp, " |");
1612 /* Print a line consisting of the caret/underlines for the given
1613 source line. */
1615 void
1616 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1618 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1619 lbounds.m_last_non_ws_disp_col);
1621 start_annotation_line ();
1622 pp_space (m_pp);
1624 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1626 bool in_range_p;
1627 point_state state;
1628 in_range_p = get_state_at_point (row, column,
1629 lbounds.m_first_non_ws_disp_col,
1630 lbounds.m_last_non_ws_disp_col,
1631 CU_DISPLAY_COLS,
1632 &state);
1633 if (in_range_p)
1635 /* Within a range. Draw either the caret or an underline. */
1636 m_colorizer.set_range (state.range_idx);
1637 if (state.draw_caret_p)
1639 /* Draw the caret. */
1640 char caret_char;
1641 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1642 caret_char = m_context->caret_chars[state.range_idx];
1643 else
1644 caret_char = '^';
1645 pp_character (m_pp, caret_char);
1647 else
1648 pp_character (m_pp, '~');
1650 else
1652 /* Not in a range. */
1653 m_colorizer.set_normal_text ();
1654 pp_character (m_pp, ' ');
1657 print_newline ();
1660 /* Implementation detail of layout::print_any_labels.
1662 A label within the given row of source. */
1664 class line_label
1666 public:
1667 line_label (diagnostic_context *context, int state_idx, int column,
1668 label_text text)
1669 : m_state_idx (state_idx), m_column (column),
1670 m_text (text), m_label_line (0), m_has_vbar (true)
1672 const int bytes = strlen (text.m_buffer);
1673 m_display_width
1674 = cpp_display_width (text.m_buffer, bytes, context->tabstop);
1677 /* Sorting is primarily by column, then by state index. */
1678 static int comparator (const void *p1, const void *p2)
1680 const line_label *ll1 = (const line_label *)p1;
1681 const line_label *ll2 = (const line_label *)p2;
1682 int column_cmp = compare (ll1->m_column, ll2->m_column);
1683 if (column_cmp)
1684 return column_cmp;
1685 /* Order by reverse state index, so that labels are printed
1686 in order of insertion into the rich_location when the
1687 sorted list is walked backwards. */
1688 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1691 int m_state_idx;
1692 int m_column;
1693 label_text m_text;
1694 size_t m_display_width;
1695 int m_label_line;
1696 bool m_has_vbar;
1699 /* Print any labels in this row. */
1700 void
1701 layout::print_any_labels (linenum_type row)
1703 int i;
1704 auto_vec<line_label> labels;
1706 /* Gather the labels that are to be printed into "labels". */
1708 layout_range *range;
1709 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1711 /* Most ranges don't have labels, so reject this first. */
1712 if (range->m_label == NULL)
1713 continue;
1715 /* The range's caret must be on this line. */
1716 if (range->m_caret.m_line != row)
1717 continue;
1719 /* Reject labels that aren't fully visible due to clipping
1720 by m_x_offset_display. */
1721 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1722 if (disp_col <= m_x_offset_display)
1723 continue;
1725 label_text text;
1726 text = range->m_label->get_text (range->m_original_idx);
1728 /* Allow for labels that return NULL from their get_text
1729 implementation (so e.g. such labels can control their own
1730 visibility). */
1731 if (text.m_buffer == NULL)
1732 continue;
1734 labels.safe_push (line_label (m_context, i, disp_col, text));
1738 /* Bail out if there are no labels on this row. */
1739 if (labels.length () == 0)
1740 return;
1742 /* Sort them. */
1743 labels.qsort(line_label::comparator);
1745 /* Figure out how many "label lines" we need, and which
1746 one each label is printed in.
1748 For example, if the labels aren't too densely packed,
1749 we can fit them on the same line, giving two "label lines":
1751 foo + bar
1752 ~~~ ~~~
1753 | | : label line 0
1754 l0 l1 : label line 1
1756 If they would touch each other or overlap, then we need
1757 additional "label lines":
1759 foo + bar
1760 ~~~ ~~~
1761 | | : label line 0
1762 | label 1 : label line 1
1763 label 0 : label line 2
1765 Place the final label on label line 1, and work backwards, adding
1766 label lines as needed.
1768 If multiple labels are at the same place, put them on separate
1769 label lines:
1771 foo + bar
1772 ^ : label line 0
1773 | : label line 1
1774 label 0 : label line 2
1775 label 1 : label line 3. */
1777 int max_label_line = 1;
1779 int next_column = INT_MAX;
1780 line_label *label;
1781 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1783 /* Would this label "touch" or overlap the next label? */
1784 if (label->m_column + label->m_display_width >= (size_t)next_column)
1786 max_label_line++;
1788 /* If we've already seen labels with the same column, suppress the
1789 vertical bar for subsequent ones in this backwards iteration;
1790 hence only the one with the highest label_line has m_has_vbar set. */
1791 if (label->m_column == next_column)
1792 label->m_has_vbar = false;
1795 label->m_label_line = max_label_line;
1796 next_column = label->m_column;
1800 /* Print the "label lines". For each label within the line, print
1801 either a vertical bar ('|') for the labels that are lower down, or the
1802 labels themselves once we've reached their line. */
1804 for (int label_line = 0; label_line <= max_label_line; label_line++)
1806 start_annotation_line ();
1807 pp_space (m_pp);
1808 int column = 1 + m_x_offset_display;
1809 line_label *label;
1810 FOR_EACH_VEC_ELT (labels, i, label)
1812 if (label_line > label->m_label_line)
1813 /* We've printed all the labels for this label line. */
1814 break;
1816 if (label_line == label->m_label_line)
1818 gcc_assert (column <= label->m_column);
1819 move_to_column (&column, label->m_column, true);
1820 /* Colorize the text, unless it's for events in a
1821 diagnostic_path. */
1822 if (!m_diagnostic_path_p)
1823 m_colorizer.set_range (label->m_state_idx);
1824 pp_string (m_pp, label->m_text.m_buffer);
1825 m_colorizer.set_normal_text ();
1826 column += label->m_display_width;
1828 else if (label->m_has_vbar)
1830 gcc_assert (column <= label->m_column);
1831 move_to_column (&column, label->m_column, true);
1832 m_colorizer.set_range (label->m_state_idx);
1833 pp_character (m_pp, '|');
1834 m_colorizer.set_normal_text ();
1835 column++;
1838 print_newline ();
1842 /* Clean up. */
1844 line_label *label;
1845 FOR_EACH_VEC_ELT (labels, i, label)
1846 label->m_text.maybe_free ();
1850 /* If there are any fixit hints inserting new lines before source line ROW,
1851 print them.
1853 They are printed on lines of their own, before the source line
1854 itself, with a leading '+'. */
1856 void
1857 layout::print_leading_fixits (linenum_type row)
1859 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1861 const fixit_hint *hint = m_fixit_hints[i];
1863 if (!hint->ends_with_newline_p ())
1864 /* Not a newline fixit; print it in print_trailing_fixits. */
1865 continue;
1867 gcc_assert (hint->insertion_p ());
1869 if (hint->affects_line_p (m_exploc.file, row))
1871 /* Printing the '+' with normal colorization
1872 and the inserted line with "insert" colorization
1873 helps them stand out from each other, and from
1874 the surrounding text. */
1875 m_colorizer.set_normal_text ();
1876 start_annotation_line ('+');
1877 pp_character (m_pp, '+');
1878 m_colorizer.set_fixit_insert ();
1879 /* Print all but the trailing newline of the fix-it hint.
1880 We have to print the newline separately to avoid
1881 getting additional pp prefixes printed. */
1882 for (size_t i = 0; i < hint->get_length () - 1; i++)
1883 pp_character (m_pp, hint->get_string ()[i]);
1884 m_colorizer.set_normal_text ();
1885 pp_newline (m_pp);
1890 /* Subroutine of layout::print_trailing_fixits.
1892 Determine if the annotation line printed for LINE contained
1893 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
1895 bool
1896 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1897 int finish_column) const
1899 layout_range *range;
1900 int i;
1901 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1902 if (range->m_start.m_line == line
1903 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
1904 && range->m_finish.m_line == line
1905 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
1906 return true;
1907 return false;
1910 /* Classes for printing trailing fix-it hints i.e. those that
1911 don't add new lines.
1913 For insertion, these can look like:
1915 new_text
1917 For replacement, these can look like:
1919 ------------- : underline showing affected range
1920 new_text
1922 For deletion, these can look like:
1924 ------------- : underline showing affected range
1926 This can become confusing if they overlap, and so we need
1927 to do some preprocessing to decide what to print.
1928 We use the list of fixit_hint instances affecting the line
1929 to build a list of "correction" instances, and print the
1930 latter.
1932 For example, consider a set of fix-its for converting
1933 a C-style cast to a C++ const_cast.
1935 Given:
1937 ..000000000111111111122222222223333333333.
1938 ..123456789012345678901234567890123456789.
1939 foo *f = (foo *)ptr->field;
1940 ^~~~~
1942 and the fix-it hints:
1943 - replace col 10 (the open paren) with "const_cast<"
1944 - replace col 16 (the close paren) with "> ("
1945 - insert ")" before col 27
1947 then we would get odd-looking output:
1949 foo *f = (foo *)ptr->field;
1950 ^~~~~
1952 const_cast<
1954 > ( )
1956 It would be better to detect when fixit hints are going to
1957 overlap (those that require new lines), and to consolidate
1958 the printing of such fixits, giving something like:
1960 foo *f = (foo *)ptr->field;
1961 ^~~~~
1962 -----------------
1963 const_cast<foo *> (ptr->field)
1965 This works by detecting when the printing would overlap, and
1966 effectively injecting no-op replace hints into the gaps between
1967 such fix-its, so that the printing joins up.
1969 In the above example, the overlap of:
1970 - replace col 10 (the open paren) with "const_cast<"
1971 and:
1972 - replace col 16 (the close paren) with "> ("
1973 is fixed by injecting a no-op:
1974 - replace cols 11-15 with themselves ("foo *")
1975 and consolidating these, making:
1976 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1977 i.e.:
1978 - replace cols 10-16 with "const_cast<foo *> ("
1980 This overlaps with the final fix-it hint:
1981 - insert ")" before col 27
1982 and so we repeat the consolidation process, by injecting
1983 a no-op:
1984 - replace cols 17-26 with themselves ("ptr->field")
1985 giving:
1986 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1987 i.e.:
1988 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1990 and is thus printed as desired. */
1992 /* A range of (byte or display) columns within a line. */
1994 class column_range
1996 public:
1997 column_range (int start_, int finish_) : start (start_), finish (finish_)
1999 /* We must have either a range, or an insertion. */
2000 gcc_assert (start <= finish || finish == start - 1);
2003 bool operator== (const column_range &other) const
2005 return start == other.start && finish == other.finish;
2008 int start;
2009 int finish;
2012 /* Get the range of bytes or display columns that HINT would affect. */
2013 static column_range
2014 get_affected_range (diagnostic_context *context,
2015 const fixit_hint *hint, enum column_unit col_unit)
2017 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2018 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2019 --exploc_finish.column;
2021 int start_column;
2022 int finish_column;
2023 if (col_unit == CU_DISPLAY_COLS)
2025 start_column
2026 = location_compute_display_column (exploc_start, context->tabstop);
2027 if (hint->insertion_p ())
2028 finish_column = start_column - 1;
2029 else
2030 finish_column
2031 = location_compute_display_column (exploc_finish, context->tabstop);
2033 else
2035 start_column = exploc_start.column;
2036 finish_column = exploc_finish.column;
2038 return column_range (start_column, finish_column);
2041 /* Get the range of display columns that would be printed for HINT. */
2043 static column_range
2044 get_printed_columns (diagnostic_context *context, const fixit_hint *hint)
2046 expanded_location exploc = expand_location (hint->get_start_loc ());
2047 int start_column = location_compute_display_column (exploc, context->tabstop);
2048 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2049 context->tabstop);
2050 int final_hint_column = start_column + hint_width - 1;
2051 if (hint->insertion_p ())
2053 return column_range (start_column, final_hint_column);
2055 else
2057 exploc = expand_location (hint->get_next_loc ());
2058 --exploc.column;
2059 int finish_column
2060 = location_compute_display_column (exploc, context->tabstop);
2061 return column_range (start_column,
2062 MAX (finish_column, final_hint_column));
2066 /* A correction on a particular line.
2067 This describes a plan for how to print one or more fixit_hint
2068 instances that affected the line, potentially consolidating hints
2069 into corrections to make the result easier for the user to read. */
2071 class correction
2073 public:
2074 correction (column_range affected_bytes,
2075 column_range affected_columns,
2076 column_range printed_columns,
2077 const char *new_text, size_t new_text_len,
2078 int tabstop)
2079 : m_affected_bytes (affected_bytes),
2080 m_affected_columns (affected_columns),
2081 m_printed_columns (printed_columns),
2082 m_text (xstrdup (new_text)),
2083 m_byte_length (new_text_len),
2084 m_tabstop (tabstop),
2085 m_alloc_sz (new_text_len + 1)
2087 compute_display_cols ();
2090 ~correction () { free (m_text); }
2092 bool insertion_p () const
2094 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2097 void ensure_capacity (size_t len);
2098 void ensure_terminated ();
2100 void compute_display_cols ()
2102 m_display_cols = cpp_display_width (m_text, m_byte_length, m_tabstop);
2105 void overwrite (int dst_offset, const char_span &src_span)
2107 gcc_assert (dst_offset >= 0);
2108 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2109 memcpy (m_text + dst_offset, src_span.get_buffer (),
2110 src_span.length ());
2113 /* If insert, then start: the column before which the text
2114 is to be inserted, and finish is offset by the length of
2115 the replacement.
2116 If replace, then the range of columns affected. */
2117 column_range m_affected_bytes;
2118 column_range m_affected_columns;
2120 /* If insert, then start: the column before which the text
2121 is to be inserted, and finish is offset by the length of
2122 the replacement.
2123 If replace, then the range of columns affected. */
2124 column_range m_printed_columns;
2126 /* The text to be inserted/used as replacement. */
2127 char *m_text;
2128 size_t m_byte_length; /* Not including null-terminator. */
2129 int m_display_cols;
2130 int m_tabstop;
2131 size_t m_alloc_sz;
2134 /* Ensure that m_text can hold a string of length LEN
2135 (plus 1 for 0-termination). */
2137 void
2138 correction::ensure_capacity (size_t len)
2140 /* Allow 1 extra byte for 0-termination. */
2141 if (m_alloc_sz < (len + 1))
2143 size_t new_alloc_sz = (len + 1) * 2;
2144 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2145 m_alloc_sz = new_alloc_sz;
2149 /* Ensure that m_text is 0-terminated. */
2151 void
2152 correction::ensure_terminated ()
2154 /* 0-terminate the buffer. */
2155 gcc_assert (m_byte_length < m_alloc_sz);
2156 m_text[m_byte_length] = '\0';
2159 /* A list of corrections affecting a particular line.
2160 This is used by layout::print_trailing_fixits for planning
2161 how to print the fix-it hints affecting the line. */
2163 class line_corrections
2165 public:
2166 line_corrections (diagnostic_context *context, const char *filename,
2167 linenum_type row)
2168 : m_context (context), m_filename (filename), m_row (row)
2170 ~line_corrections ();
2172 void add_hint (const fixit_hint *hint);
2174 diagnostic_context *m_context;
2175 const char *m_filename;
2176 linenum_type m_row;
2177 auto_vec <correction *> m_corrections;
2180 /* struct line_corrections. */
2182 line_corrections::~line_corrections ()
2184 unsigned i;
2185 correction *c;
2186 FOR_EACH_VEC_ELT (m_corrections, i, c)
2187 delete c;
2190 /* A struct wrapping a particular source line, allowing
2191 run-time bounds-checking of accesses in a checked build. */
2193 class source_line
2195 public:
2196 source_line (const char *filename, int line);
2198 char_span as_span () { return char_span (chars, width); }
2200 const char *chars;
2201 int width;
2204 /* source_line's ctor. */
2206 source_line::source_line (const char *filename, int line)
2208 char_span span = location_get_source_line (filename, line);
2209 chars = span.get_buffer ();
2210 width = span.length ();
2213 /* Add HINT to the corrections for this line.
2214 Attempt to consolidate nearby hints so that they will not
2215 overlap with printed. */
2217 void
2218 line_corrections::add_hint (const fixit_hint *hint)
2220 column_range affected_bytes = get_affected_range (m_context, hint, CU_BYTES);
2221 column_range affected_columns = get_affected_range (m_context, hint,
2222 CU_DISPLAY_COLS);
2223 column_range printed_columns = get_printed_columns (m_context, hint);
2225 /* Potentially consolidate. */
2226 if (!m_corrections.is_empty ())
2228 correction *last_correction
2229 = m_corrections[m_corrections.length () - 1];
2231 /* The following consolidation code assumes that the fix-it hints
2232 have been sorted by start (done within layout's ctor). */
2233 gcc_assert (affected_bytes.start
2234 >= last_correction->m_affected_bytes.start);
2235 gcc_assert (printed_columns.start
2236 >= last_correction->m_printed_columns.start);
2238 if (printed_columns.start <= last_correction->m_printed_columns.finish)
2240 /* We have two hints for which the printed forms of the hints
2241 would touch or overlap, so we need to consolidate them to avoid
2242 confusing the user.
2243 Attempt to inject a "replace" correction from immediately
2244 after the end of the last hint to immediately before the start
2245 of the next hint. */
2246 column_range between (last_correction->m_affected_bytes.finish + 1,
2247 affected_bytes.start - 1);
2249 /* Try to read the source. */
2250 source_line line (m_filename, m_row);
2251 if (line.chars && between.finish < line.width)
2253 /* Consolidate into the last correction:
2254 add a no-op "replace" of the "between" text, and
2255 add the text from the new hint. */
2256 int old_byte_len = last_correction->m_byte_length;
2257 gcc_assert (old_byte_len >= 0);
2258 int between_byte_len = between.finish + 1 - between.start;
2259 gcc_assert (between_byte_len >= 0);
2260 int new_byte_len
2261 = old_byte_len + between_byte_len + hint->get_length ();
2262 gcc_assert (new_byte_len >= 0);
2263 last_correction->ensure_capacity (new_byte_len);
2264 last_correction->overwrite
2265 (old_byte_len,
2266 line.as_span ().subspan (between.start - 1,
2267 between.finish + 1 - between.start));
2268 last_correction->overwrite (old_byte_len + between_byte_len,
2269 char_span (hint->get_string (),
2270 hint->get_length ()));
2271 last_correction->m_byte_length = new_byte_len;
2272 last_correction->ensure_terminated ();
2273 last_correction->m_affected_bytes.finish
2274 = affected_bytes.finish;
2275 last_correction->m_affected_columns.finish
2276 = affected_columns.finish;
2277 int prev_display_cols = last_correction->m_display_cols;
2278 last_correction->compute_display_cols ();
2279 last_correction->m_printed_columns.finish
2280 += last_correction->m_display_cols - prev_display_cols;
2281 return;
2286 /* If no consolidation happened, add a new correction instance. */
2287 m_corrections.safe_push (new correction (affected_bytes,
2288 affected_columns,
2289 printed_columns,
2290 hint->get_string (),
2291 hint->get_length (),
2292 m_context->tabstop));
2295 /* If there are any fixit hints on source line ROW, print them.
2296 They are printed in order, attempting to combine them onto lines, but
2297 starting new lines if necessary.
2298 Fix-it hints that insert new lines are handled separately,
2299 in layout::print_leading_fixits. */
2301 void
2302 layout::print_trailing_fixits (linenum_type row)
2304 /* Build a list of correction instances for the line,
2305 potentially consolidating hints (for the sake of readability). */
2306 line_corrections corrections (m_context, m_exploc.file, row);
2307 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2309 const fixit_hint *hint = m_fixit_hints[i];
2311 /* Newline fixits are handled by layout::print_leading_fixits. */
2312 if (hint->ends_with_newline_p ())
2313 continue;
2315 if (hint->affects_line_p (m_exploc.file, row))
2316 corrections.add_hint (hint);
2319 /* Now print the corrections. */
2320 unsigned i;
2321 correction *c;
2322 int column = m_x_offset_display;
2324 if (!corrections.m_corrections.is_empty ())
2325 start_annotation_line ();
2327 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2329 /* For now we assume each fixit hint can only touch one line. */
2330 if (c->insertion_p ())
2332 /* This assumes the insertion just affects one line. */
2333 int start_column = c->m_printed_columns.start;
2334 move_to_column (&column, start_column, true);
2335 m_colorizer.set_fixit_insert ();
2336 pp_string (m_pp, c->m_text);
2337 m_colorizer.set_normal_text ();
2338 column += c->m_display_cols;
2340 else
2342 /* If the range of the replacement wasn't printed in the
2343 annotation line, then print an extra underline to
2344 indicate exactly what is being replaced.
2345 Always show it for removals. */
2346 int start_column = c->m_affected_columns.start;
2347 int finish_column = c->m_affected_columns.finish;
2348 if (!annotation_line_showed_range_p (row, start_column,
2349 finish_column)
2350 || c->m_byte_length == 0)
2352 move_to_column (&column, start_column, true);
2353 m_colorizer.set_fixit_delete ();
2354 for (; column <= finish_column; column++)
2355 pp_character (m_pp, '-');
2356 m_colorizer.set_normal_text ();
2358 /* Print the replacement text. REPLACE also covers
2359 removals, so only do this extra work (potentially starting
2360 a new line) if we have actual replacement text. */
2361 if (c->m_byte_length > 0)
2363 move_to_column (&column, start_column, true);
2364 m_colorizer.set_fixit_insert ();
2365 pp_string (m_pp, c->m_text);
2366 m_colorizer.set_normal_text ();
2367 column += c->m_display_cols;
2372 /* Add a trailing newline, if necessary. */
2373 move_to_column (&column, 0, false);
2376 /* Disable any colorization and emit a newline. */
2378 void
2379 layout::print_newline ()
2381 m_colorizer.set_normal_text ();
2382 pp_newline (m_pp);
2385 /* Return true if (ROW/COLUMN) is within a range of the layout.
2386 If it returns true, OUT_STATE is written to, with the
2387 range index, and whether we should draw the caret at
2388 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2389 whether all inputs and outputs are in bytes or display column units. */
2391 bool
2392 layout::get_state_at_point (/* Inputs. */
2393 linenum_type row, int column,
2394 int first_non_ws, int last_non_ws,
2395 enum column_unit col_unit,
2396 /* Outputs. */
2397 point_state *out_state)
2399 layout_range *range;
2400 int i;
2401 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2403 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2404 /* Bail out early, so that such ranges don't affect underlining or
2405 source colorization. */
2406 continue;
2408 if (range->contains_point (row, column, col_unit))
2410 out_state->range_idx = i;
2412 /* Are we at the range's caret? is it visible? */
2413 out_state->draw_caret_p = false;
2414 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2415 && row == range->m_caret.m_line
2416 && column == range->m_caret.m_columns[col_unit])
2417 out_state->draw_caret_p = true;
2419 /* Within a multiline range, don't display any underline
2420 in any leading or trailing whitespace on a line.
2421 We do display carets, however. */
2422 if (!out_state->draw_caret_p)
2423 if (column < first_non_ws || column > last_non_ws)
2424 return false;
2426 /* We are within a range. */
2427 return true;
2431 return false;
2434 /* Helper function for use by layout::print_line when printing the
2435 annotation line under the source line.
2436 Get the display column beyond the rightmost one that could contain a caret
2437 or range marker, given that we stop rendering at trailing whitespace.
2438 ROW is the source line within the given file.
2439 CARET_COLUMN is the display column of range 0's caret.
2440 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2441 character of source (as determined when printing the source line). */
2444 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2445 int last_non_ws_column)
2447 int result = caret_column + 1;
2449 layout_range *range;
2450 int i;
2451 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2453 if (row >= range->m_start.m_line)
2455 if (range->m_finish.m_line == row)
2457 /* On the final line within a range; ensure that
2458 we render up to the end of the range. */
2459 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2460 if (result <= disp_col)
2461 result = disp_col + 1;
2463 else if (row < range->m_finish.m_line)
2465 /* Within a multiline range; ensure that we render up to the
2466 last non-whitespace column. */
2467 if (result <= last_non_ws_column)
2468 result = last_non_ws_column + 1;
2473 return result;
2476 /* Given *COLUMN as an x-coordinate, print spaces to position
2477 successive output at DEST_COLUMN, printing a newline if necessary,
2478 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2479 left margin after any newline. */
2481 void
2482 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2484 /* Start a new line if we need to. */
2485 if (*column > dest_column)
2487 print_newline ();
2488 if (add_left_margin)
2489 start_annotation_line ();
2490 *column = m_x_offset_display;
2493 while (*column < dest_column)
2495 pp_space (m_pp);
2496 (*column)++;
2500 /* For debugging layout issues, render a ruler giving column numbers
2501 (after the 1-column indent). */
2503 void
2504 layout::show_ruler (int max_column) const
2506 /* Hundreds. */
2507 if (max_column > 99)
2509 start_annotation_line ();
2510 pp_space (m_pp);
2511 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2512 if (column % 10 == 0)
2513 pp_character (m_pp, '0' + (column / 100) % 10);
2514 else
2515 pp_space (m_pp);
2516 pp_newline (m_pp);
2519 /* Tens. */
2520 start_annotation_line ();
2521 pp_space (m_pp);
2522 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2523 if (column % 10 == 0)
2524 pp_character (m_pp, '0' + (column / 10) % 10);
2525 else
2526 pp_space (m_pp);
2527 pp_newline (m_pp);
2529 /* Units. */
2530 start_annotation_line ();
2531 pp_space (m_pp);
2532 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2533 pp_character (m_pp, '0' + (column % 10));
2534 pp_newline (m_pp);
2537 /* Print leading fix-its (for new lines inserted before the source line)
2538 then the source line, followed by an annotation line
2539 consisting of any caret/underlines, then any fixits.
2540 If the source line can't be read, print nothing. */
2541 void
2542 layout::print_line (linenum_type row)
2544 char_span line = location_get_source_line (m_exploc.file, row);
2545 if (!line)
2546 return;
2548 print_leading_fixits (row);
2549 const line_bounds lbounds
2550 = print_source_line (row, line.get_buffer (), line.length ());
2551 if (should_print_annotation_line_p (row))
2552 print_annotation_line (row, lbounds);
2553 if (m_show_labels_p)
2554 print_any_labels (row);
2555 print_trailing_fixits (row);
2558 } /* End of anonymous namespace. */
2560 /* If LOC is within the spans of lines that will already be printed for
2561 this gcc_rich_location, then add it as a secondary location and return true.
2563 Otherwise return false. */
2565 bool
2566 gcc_rich_location::add_location_if_nearby (location_t loc,
2567 bool restrict_to_current_line_spans,
2568 const range_label *label)
2570 /* Use the layout location-handling logic to sanitize LOC,
2571 filtering it to the current line spans within a temporary
2572 layout instance. */
2573 layout layout (global_dc, this, DK_ERROR);
2574 location_range loc_range;
2575 loc_range.m_loc = loc;
2576 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2577 if (!layout.maybe_add_location_range (&loc_range, 0,
2578 restrict_to_current_line_spans))
2579 return false;
2581 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2582 return true;
2585 /* Print the physical source code corresponding to the location of
2586 this diagnostic, with additional annotations. */
2588 void
2589 diagnostic_show_locus (diagnostic_context * context,
2590 rich_location *richloc,
2591 diagnostic_t diagnostic_kind)
2593 location_t loc = richloc->get_loc ();
2594 /* Do nothing if source-printing has been disabled. */
2595 if (!context->show_caret)
2596 return;
2598 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2599 if (loc <= BUILTINS_LOCATION)
2600 return;
2602 /* Don't print the same source location twice in a row, unless we have
2603 fix-it hints, or multiple locations, or a label. */
2604 if (loc == context->last_location
2605 && richloc->get_num_fixit_hints () == 0
2606 && richloc->get_num_locations () == 1
2607 && richloc->get_range (0)->m_label == NULL)
2608 return;
2610 context->last_location = loc;
2612 layout layout (context, richloc, diagnostic_kind);
2613 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2614 line_span_idx++)
2616 const line_span *line_span = layout.get_line_span (line_span_idx);
2617 if (context->show_line_numbers_p)
2619 /* With line numbers, we should show whenever the line-numbering
2620 "jumps". */
2621 if (line_span_idx > 0)
2622 layout.print_gap_in_line_numbering ();
2624 else
2626 /* Without line numbers, we print headings for some line spans. */
2627 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2629 expanded_location exploc
2630 = layout.get_expanded_location (line_span);
2631 context->start_span (context, exploc);
2634 /* Iterate over the lines within this span (using linenum_arith_t to
2635 avoid overflow with 0xffffffff causing an infinite loop). */
2636 linenum_arith_t last_line = line_span->get_last_line ();
2637 for (linenum_arith_t row = line_span->get_first_line ();
2638 row <= last_line; row++)
2639 layout.print_line (row);
2643 #if CHECKING_P
2645 namespace selftest {
2647 /* Selftests for diagnostic_show_locus. */
2649 /* For precise tests of the layout, make clear where the source line will
2650 start. test_left_margin sets the total byte count from the left side of the
2651 screen to the start of source lines, after the line number and the separator,
2652 which consists of the three characters " | ". */
2653 static const int test_linenum_sep = 3;
2654 static const int test_left_margin = 7;
2656 /* Helper function for test_layout_x_offset_display_utf8(). */
2657 static void
2658 test_offset_impl (int caret_byte_col, int max_width,
2659 int expected_x_offset_display,
2660 int left_margin = test_left_margin)
2662 test_diagnostic_context dc;
2663 dc.caret_max_width = max_width;
2664 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2665 the line number plus one space after. */
2666 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2667 dc.show_line_numbers_p = true;
2668 rich_location richloc (line_table,
2669 linemap_position_for_column (line_table,
2670 caret_byte_col));
2671 layout test_layout (&dc, &richloc, DK_ERROR);
2672 ASSERT_EQ (left_margin - test_linenum_sep,
2673 test_layout.get_linenum_width ());
2674 ASSERT_EQ (expected_x_offset_display,
2675 test_layout.get_x_offset_display ());
2678 /* Test that layout::calculate_x_offset_display() works. */
2679 static void
2680 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2683 const char *content
2684 = "This line is very long, so that we can use it to test the logic for "
2685 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2686 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2687 "column #102.\n";
2689 /* Number of bytes in the line, subtracting one to remove the newline. */
2690 const int line_bytes = strlen (content) - 1;
2692 /* Number of display columns occupied by the line; each of the 2 emojis
2693 takes up 2 fewer display columns than it does bytes. */
2694 const int line_display_cols = line_bytes - 2*2;
2696 /* The column of the first emoji. Byte or display is the same as there are
2697 no multibyte characters earlier on the line. */
2698 const int emoji_col = 102;
2700 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2701 line_table_test ltt (case_);
2703 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2705 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2707 /* Don't attempt to run the tests if column data might be unavailable. */
2708 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2709 return;
2711 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2712 ASSERT_EQ (1, LOCATION_LINE (line_end));
2713 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
2715 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2716 ASSERT_EQ (line_display_cols,
2717 cpp_display_width (lspan.get_buffer (), lspan.length (),
2718 def_tabstop));
2719 ASSERT_EQ (line_display_cols,
2720 location_compute_display_column (expand_location (line_end),
2721 def_tabstop));
2722 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
2723 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2725 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2727 /* No constraint on the width -> no offset. */
2728 test_offset_impl (emoji_col, 0, 0);
2730 /* Caret is before the beginning -> no offset. */
2731 test_offset_impl (0, 100, 0);
2733 /* Caret is past the end of the line -> no offset. */
2734 test_offset_impl (line_bytes+1, 100, 0);
2736 /* Line fits in the display -> no offset. */
2737 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
2738 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
2740 /* Line is too long for the display but caret location is OK
2741 anyway -> no offset. */
2742 static const int small_width = 24;
2743 test_offset_impl (1, small_width, 0);
2745 /* Width constraint is very small -> no offset. */
2746 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
2748 /* Line would be offset, but due to large line numbers, offsetting
2749 would remove the whole line -> no offset. */
2750 static const int huge_left_margin = 100;
2751 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
2753 /* Line is the same length as the display, but the line number makes it too
2754 long, so offset is required. Caret is at the end so padding on the right
2755 is not in effect. */
2756 for (int excess = 1; excess <= 3; ++excess)
2757 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
2758 excess);
2760 /* Line is much too long for the display, caret is near the end ->
2761 offset should be such that the line fits in the display and caret
2762 remains the same distance from the end that it was. */
2763 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
2764 caret_offset <= max_offset; ++caret_offset)
2765 test_offset_impl (line_bytes - caret_offset, small_width,
2766 line_display_cols + test_left_margin - small_width);
2768 /* As previous case but caret is closer to the middle; now we want it to end
2769 up CARET_LINE_MARGIN bytes from the end. */
2770 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
2771 test_offset_impl (emoji_col, small_width,
2772 emoji_col + test_left_margin
2773 - (small_width - CARET_LINE_MARGIN));
2775 /* Test that the source line is offset as expected when printed. */
2777 test_diagnostic_context dc;
2778 dc.caret_max_width = small_width - 6;
2779 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2780 dc.show_line_numbers_p = true;
2781 dc.show_ruler_p = true;
2782 rich_location richloc (line_table,
2783 linemap_position_for_column (line_table,
2784 emoji_col));
2785 layout test_layout (&dc, &richloc, DK_ERROR);
2786 test_layout.print_line (1);
2787 ASSERT_STREQ (" | 1 \n"
2788 " | 1 \n"
2789 " | 234567890123456789\n"
2790 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2791 "that occupies 8 bytes and 4 display columns, starting at "
2792 "column #102.\n"
2793 " | ^\n\n",
2794 pp_formatted_text (dc.printer));
2797 /* Similar to the previous example, but now the offset called for would split
2798 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2799 it with a padding space in this case. */
2801 test_diagnostic_context dc;
2802 dc.caret_max_width = small_width - 5;
2803 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2804 dc.show_line_numbers_p = true;
2805 dc.show_ruler_p = true;
2806 rich_location richloc (line_table,
2807 linemap_position_for_column (line_table,
2808 emoji_col + 2));
2809 layout test_layout (&dc, &richloc, DK_ERROR);
2810 test_layout.print_line (1);
2811 ASSERT_STREQ (" | 1 1 \n"
2812 " | 1 2 \n"
2813 " | 3456789012345678901\n"
2814 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2815 "that occupies 8 bytes and 4 display columns, starting at "
2816 "column #102.\n"
2817 " | ^\n\n",
2818 pp_formatted_text (dc.printer));
2823 static void
2824 test_layout_x_offset_display_tab (const line_table_case &case_)
2826 const char *content
2827 = "This line is very long, so that we can use it to test the logic for "
2828 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
2829 "a variable number of display columns, starting at column #103.\n";
2831 /* Number of bytes in the line, subtracting one to remove the newline. */
2832 const int line_bytes = strlen (content) - 1;
2834 /* The column where the tab begins. Byte or display is the same as there are
2835 no multibyte characters earlier on the line. */
2836 const int tab_col = 103;
2838 /* Effective extra size of the tab beyond what a single space would have taken
2839 up, indexed by tabstop. */
2840 static const int num_tabstops = 11;
2841 int extra_width[num_tabstops];
2842 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2844 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
2845 extra_width[tabstop] = this_tab_size - 1;
2847 /* Example of this calculation: if tabstop is 10, the tab starting at column
2848 #103 has to expand into 8 spaces, covering columns 103-110, so that the
2849 next character is at column #111. So it takes up 7 more columns than
2850 a space would have taken up. */
2851 ASSERT_EQ (7, extra_width[10]);
2853 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2854 line_table_test ltt (case_);
2856 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2858 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2860 /* Don't attempt to run the tests if column data might be unavailable. */
2861 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2862 return;
2864 /* Check that cpp_display_width handles the tabs as expected. */
2865 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2866 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
2867 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2869 ASSERT_EQ (line_bytes + extra_width[tabstop],
2870 cpp_display_width (lspan.get_buffer (), lspan.length (),
2871 tabstop));
2872 ASSERT_EQ (line_bytes + extra_width[tabstop],
2873 location_compute_display_column (expand_location (line_end),
2874 tabstop));
2877 /* Check that the tab is expanded to the expected number of spaces. */
2878 rich_location richloc (line_table,
2879 linemap_position_for_column (line_table,
2880 tab_col + 1));
2881 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2883 test_diagnostic_context dc;
2884 dc.tabstop = tabstop;
2885 layout test_layout (&dc, &richloc, DK_ERROR);
2886 test_layout.print_line (1);
2887 const char *out = pp_formatted_text (dc.printer);
2888 ASSERT_EQ (NULL, strchr (out, '\t'));
2889 const char *left_quote = strchr (out, '`');
2890 const char *right_quote = strchr (out, '\'');
2891 ASSERT_NE (NULL, left_quote);
2892 ASSERT_NE (NULL, right_quote);
2893 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
2896 /* Check that the line is offset properly and that the tab is broken up
2897 into the expected number of spaces when it is the last character skipped
2898 over. */
2899 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2901 test_diagnostic_context dc;
2902 dc.tabstop = tabstop;
2903 static const int small_width = 24;
2904 dc.caret_max_width = small_width - 4;
2905 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2906 dc.show_line_numbers_p = true;
2907 layout test_layout (&dc, &richloc, DK_ERROR);
2908 test_layout.print_line (1);
2910 /* We have arranged things so that two columns will be printed before
2911 the caret. If the tab results in more than one space, this should
2912 produce two spaces in the output; otherwise, it will be a single space
2913 preceded by the opening quote before the tab character. */
2914 const char *output1
2915 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
2916 "display columns, starting at column #103.\n"
2917 " | ^\n\n";
2918 const char *output2
2919 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
2920 "display columns, starting at column #103.\n"
2921 " | ^\n\n";
2922 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
2923 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
2928 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2930 static void
2931 test_diagnostic_show_locus_unknown_location ()
2933 test_diagnostic_context dc;
2934 rich_location richloc (line_table, UNKNOWN_LOCATION);
2935 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2936 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
2939 /* Verify that diagnostic_show_locus works sanely for various
2940 single-line cases.
2942 All of these work on the following 1-line source file:
2943 .0000000001111111
2944 .1234567890123456
2945 "foo = bar.field;\n"
2946 which is set up by test_diagnostic_show_locus_one_liner and calls
2947 them. */
2949 /* Just a caret. */
2951 static void
2952 test_one_liner_simple_caret ()
2954 test_diagnostic_context dc;
2955 location_t caret = linemap_position_for_column (line_table, 10);
2956 rich_location richloc (line_table, caret);
2957 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2958 ASSERT_STREQ (" foo = bar.field;\n"
2959 " ^\n",
2960 pp_formatted_text (dc.printer));
2963 /* Caret and range. */
2965 static void
2966 test_one_liner_caret_and_range ()
2968 test_diagnostic_context dc;
2969 location_t caret = linemap_position_for_column (line_table, 10);
2970 location_t start = linemap_position_for_column (line_table, 7);
2971 location_t finish = linemap_position_for_column (line_table, 15);
2972 location_t loc = make_location (caret, start, finish);
2973 rich_location richloc (line_table, loc);
2974 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2975 ASSERT_STREQ (" foo = bar.field;\n"
2976 " ~~~^~~~~~\n",
2977 pp_formatted_text (dc.printer));
2980 /* Multiple ranges and carets. */
2982 static void
2983 test_one_liner_multiple_carets_and_ranges ()
2985 test_diagnostic_context dc;
2986 location_t foo
2987 = make_location (linemap_position_for_column (line_table, 2),
2988 linemap_position_for_column (line_table, 1),
2989 linemap_position_for_column (line_table, 3));
2990 dc.caret_chars[0] = 'A';
2992 location_t bar
2993 = make_location (linemap_position_for_column (line_table, 8),
2994 linemap_position_for_column (line_table, 7),
2995 linemap_position_for_column (line_table, 9));
2996 dc.caret_chars[1] = 'B';
2998 location_t field
2999 = make_location (linemap_position_for_column (line_table, 13),
3000 linemap_position_for_column (line_table, 11),
3001 linemap_position_for_column (line_table, 15));
3002 dc.caret_chars[2] = 'C';
3004 rich_location richloc (line_table, foo);
3005 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3006 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3007 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3008 ASSERT_STREQ (" foo = bar.field;\n"
3009 " ~A~ ~B~ ~~C~~\n",
3010 pp_formatted_text (dc.printer));
3013 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3015 static void
3016 test_one_liner_fixit_insert_before ()
3018 test_diagnostic_context dc;
3019 location_t caret = linemap_position_for_column (line_table, 7);
3020 rich_location richloc (line_table, caret);
3021 richloc.add_fixit_insert_before ("&");
3022 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3023 ASSERT_STREQ (" foo = bar.field;\n"
3024 " ^\n"
3025 " &\n",
3026 pp_formatted_text (dc.printer));
3029 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3031 static void
3032 test_one_liner_fixit_insert_after ()
3034 test_diagnostic_context dc;
3035 location_t start = linemap_position_for_column (line_table, 1);
3036 location_t finish = linemap_position_for_column (line_table, 3);
3037 location_t foo = make_location (start, start, finish);
3038 rich_location richloc (line_table, foo);
3039 richloc.add_fixit_insert_after ("[0]");
3040 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3041 ASSERT_STREQ (" foo = bar.field;\n"
3042 " ^~~\n"
3043 " [0]\n",
3044 pp_formatted_text (dc.printer));
3047 /* Removal fix-it hint: removal of the ".field".
3048 Also verify the interaction of pp_set_prefix with rulers and
3049 fix-it hints. */
3051 static void
3052 test_one_liner_fixit_remove ()
3054 location_t start = linemap_position_for_column (line_table, 10);
3055 location_t finish = linemap_position_for_column (line_table, 15);
3056 location_t dot = make_location (start, start, finish);
3057 rich_location richloc (line_table, dot);
3058 richloc.add_fixit_remove ();
3060 /* Normal. */
3062 test_diagnostic_context dc;
3063 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3064 ASSERT_STREQ (" foo = bar.field;\n"
3065 " ^~~~~~\n"
3066 " ------\n",
3067 pp_formatted_text (dc.printer));
3070 /* Test of adding a prefix. */
3072 test_diagnostic_context dc;
3073 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3074 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3075 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3076 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3077 "TEST PREFIX: ^~~~~~\n"
3078 "TEST PREFIX: ------\n",
3079 pp_formatted_text (dc.printer));
3082 /* Normal, with ruler. */
3084 test_diagnostic_context dc;
3085 dc.show_ruler_p = true;
3086 dc.caret_max_width = 104;
3087 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3088 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3089 " 1 2 3 4 5 6 7 8 9 0 \n"
3090 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3091 " foo = bar.field;\n"
3092 " ^~~~~~\n"
3093 " ------\n",
3094 pp_formatted_text (dc.printer));
3097 /* Test of adding a prefix, with ruler. */
3099 test_diagnostic_context dc;
3100 dc.show_ruler_p = true;
3101 dc.caret_max_width = 50;
3102 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3103 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3104 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3105 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3106 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3107 "TEST PREFIX: foo = bar.field;\n"
3108 "TEST PREFIX: ^~~~~~\n"
3109 "TEST PREFIX: ------\n",
3110 pp_formatted_text (dc.printer));
3113 /* Test of adding a prefix, with ruler and line numbers. */
3115 test_diagnostic_context dc;
3116 dc.show_ruler_p = true;
3117 dc.caret_max_width = 50;
3118 dc.show_line_numbers_p = true;
3119 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3120 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3121 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3122 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3123 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3124 "TEST PREFIX: 1 | foo = bar.field;\n"
3125 "TEST PREFIX: | ^~~~~~\n"
3126 "TEST PREFIX: | ------\n",
3127 pp_formatted_text (dc.printer));
3131 /* Replace fix-it hint: replacing "field" with "m_field". */
3133 static void
3134 test_one_liner_fixit_replace ()
3136 test_diagnostic_context dc;
3137 location_t start = linemap_position_for_column (line_table, 11);
3138 location_t finish = linemap_position_for_column (line_table, 15);
3139 location_t field = make_location (start, start, finish);
3140 rich_location richloc (line_table, field);
3141 richloc.add_fixit_replace ("m_field");
3142 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3143 ASSERT_STREQ (" foo = bar.field;\n"
3144 " ^~~~~\n"
3145 " m_field\n",
3146 pp_formatted_text (dc.printer));
3149 /* Replace fix-it hint: replacing "field" with "m_field",
3150 but where the caret was elsewhere. */
3152 static void
3153 test_one_liner_fixit_replace_non_equal_range ()
3155 test_diagnostic_context dc;
3156 location_t equals = linemap_position_for_column (line_table, 5);
3157 location_t start = linemap_position_for_column (line_table, 11);
3158 location_t finish = linemap_position_for_column (line_table, 15);
3159 rich_location richloc (line_table, equals);
3160 source_range range;
3161 range.m_start = start;
3162 range.m_finish = finish;
3163 richloc.add_fixit_replace (range, "m_field");
3164 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3165 /* The replacement range is not indicated in the annotation line, so
3166 it should be indicated via an additional underline. */
3167 ASSERT_STREQ (" foo = bar.field;\n"
3168 " ^\n"
3169 " -----\n"
3170 " m_field\n",
3171 pp_formatted_text (dc.printer));
3174 /* Replace fix-it hint: replacing "field" with "m_field",
3175 where the caret was elsewhere, but where a secondary range
3176 exactly covers "field". */
3178 static void
3179 test_one_liner_fixit_replace_equal_secondary_range ()
3181 test_diagnostic_context dc;
3182 location_t equals = linemap_position_for_column (line_table, 5);
3183 location_t start = linemap_position_for_column (line_table, 11);
3184 location_t finish = linemap_position_for_column (line_table, 15);
3185 rich_location richloc (line_table, equals);
3186 location_t field = make_location (start, start, finish);
3187 richloc.add_range (field);
3188 richloc.add_fixit_replace (field, "m_field");
3189 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3190 /* The replacement range is indicated in the annotation line,
3191 so it shouldn't be indicated via an additional underline. */
3192 ASSERT_STREQ (" foo = bar.field;\n"
3193 " ^ ~~~~~\n"
3194 " m_field\n",
3195 pp_formatted_text (dc.printer));
3198 /* Verify that we can use ad-hoc locations when adding fixits to a
3199 rich_location. */
3201 static void
3202 test_one_liner_fixit_validation_adhoc_locations ()
3204 /* Generate a range that's too long to be packed, so must
3205 be stored as an ad-hoc location (given the defaults
3206 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3207 const location_t c7 = linemap_position_for_column (line_table, 7);
3208 const location_t c47 = linemap_position_for_column (line_table, 47);
3209 const location_t loc = make_location (c7, c7, c47);
3211 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3212 return;
3214 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3216 /* Insert. */
3218 rich_location richloc (line_table, loc);
3219 richloc.add_fixit_insert_before (loc, "test");
3220 /* It should not have been discarded by the validator. */
3221 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3223 test_diagnostic_context dc;
3224 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3225 ASSERT_STREQ (" foo = bar.field;\n"
3226 " ^~~~~~~~~~ \n"
3227 " test\n",
3228 pp_formatted_text (dc.printer));
3231 /* Remove. */
3233 rich_location richloc (line_table, loc);
3234 source_range range = source_range::from_locations (loc, c47);
3235 richloc.add_fixit_remove (range);
3236 /* It should not have been discarded by the validator. */
3237 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3239 test_diagnostic_context dc;
3240 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3241 ASSERT_STREQ (" foo = bar.field;\n"
3242 " ^~~~~~~~~~ \n"
3243 " -----------------------------------------\n",
3244 pp_formatted_text (dc.printer));
3247 /* Replace. */
3249 rich_location richloc (line_table, loc);
3250 source_range range = source_range::from_locations (loc, c47);
3251 richloc.add_fixit_replace (range, "test");
3252 /* It should not have been discarded by the validator. */
3253 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3255 test_diagnostic_context dc;
3256 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3257 ASSERT_STREQ (" foo = bar.field;\n"
3258 " ^~~~~~~~~~ \n"
3259 " test\n",
3260 pp_formatted_text (dc.printer));
3264 /* Test of consolidating insertions at the same location. */
3266 static void
3267 test_one_liner_many_fixits_1 ()
3269 test_diagnostic_context dc;
3270 location_t equals = linemap_position_for_column (line_table, 5);
3271 rich_location richloc (line_table, equals);
3272 for (int i = 0; i < 19; i++)
3273 richloc.add_fixit_insert_before ("a");
3274 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3275 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3276 ASSERT_STREQ (" foo = bar.field;\n"
3277 " ^\n"
3278 " aaaaaaaaaaaaaaaaaaa\n",
3279 pp_formatted_text (dc.printer));
3282 /* Ensure that we can add an arbitrary number of fix-it hints to a
3283 rich_location, even if they are not consolidated. */
3285 static void
3286 test_one_liner_many_fixits_2 ()
3288 test_diagnostic_context dc;
3289 location_t equals = linemap_position_for_column (line_table, 5);
3290 rich_location richloc (line_table, equals);
3291 for (int i = 0; i < 19; i++)
3293 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3294 richloc.add_fixit_insert_before (loc, "a");
3296 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3297 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3298 ASSERT_STREQ (" foo = bar.field;\n"
3299 " ^\n"
3300 " a a a a a a a a a a a a a a a a a a a\n",
3301 pp_formatted_text (dc.printer));
3304 /* Test of labeling the ranges within a rich_location. */
3306 static void
3307 test_one_liner_labels ()
3309 location_t foo
3310 = make_location (linemap_position_for_column (line_table, 1),
3311 linemap_position_for_column (line_table, 1),
3312 linemap_position_for_column (line_table, 3));
3313 location_t bar
3314 = make_location (linemap_position_for_column (line_table, 7),
3315 linemap_position_for_column (line_table, 7),
3316 linemap_position_for_column (line_table, 9));
3317 location_t field
3318 = make_location (linemap_position_for_column (line_table, 11),
3319 linemap_position_for_column (line_table, 11),
3320 linemap_position_for_column (line_table, 15));
3322 /* Example where all the labels fit on one line. */
3324 text_range_label label0 ("0");
3325 text_range_label label1 ("1");
3326 text_range_label label2 ("2");
3327 gcc_rich_location richloc (foo, &label0);
3328 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3329 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3332 test_diagnostic_context dc;
3333 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3334 ASSERT_STREQ (" foo = bar.field;\n"
3335 " ^~~ ~~~ ~~~~~\n"
3336 " | | |\n"
3337 " 0 1 2\n",
3338 pp_formatted_text (dc.printer));
3341 /* Verify that we can disable label-printing. */
3343 test_diagnostic_context dc;
3344 dc.show_labels_p = false;
3345 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3346 ASSERT_STREQ (" foo = bar.field;\n"
3347 " ^~~ ~~~ ~~~~~\n",
3348 pp_formatted_text (dc.printer));
3352 /* Example where the labels need extra lines. */
3354 text_range_label label0 ("label 0");
3355 text_range_label label1 ("label 1");
3356 text_range_label label2 ("label 2");
3357 gcc_rich_location richloc (foo, &label0);
3358 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3359 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3361 test_diagnostic_context dc;
3362 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3363 ASSERT_STREQ (" foo = bar.field;\n"
3364 " ^~~ ~~~ ~~~~~\n"
3365 " | | |\n"
3366 " | | label 2\n"
3367 " | label 1\n"
3368 " label 0\n",
3369 pp_formatted_text (dc.printer));
3372 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3373 but label 1 just touches label 2. */
3375 text_range_label label0 ("aaaaa");
3376 text_range_label label1 ("bbbb");
3377 text_range_label label2 ("c");
3378 gcc_rich_location richloc (foo, &label0);
3379 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3380 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3382 test_diagnostic_context dc;
3383 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3384 ASSERT_STREQ (" foo = bar.field;\n"
3385 " ^~~ ~~~ ~~~~~\n"
3386 " | | |\n"
3387 " | | c\n"
3388 " aaaaa bbbb\n",
3389 pp_formatted_text (dc.printer));
3392 /* Example of out-of-order ranges (thus requiring a sort). */
3394 text_range_label label0 ("0");
3395 text_range_label label1 ("1");
3396 text_range_label label2 ("2");
3397 gcc_rich_location richloc (field, &label0);
3398 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3399 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3401 test_diagnostic_context dc;
3402 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3403 ASSERT_STREQ (" foo = bar.field;\n"
3404 " ~~~ ~~~ ^~~~~\n"
3405 " | | |\n"
3406 " 2 1 0\n",
3407 pp_formatted_text (dc.printer));
3410 /* Ensure we don't ICE if multiple ranges with labels are on
3411 the same point. */
3413 text_range_label label0 ("label 0");
3414 text_range_label label1 ("label 1");
3415 text_range_label label2 ("label 2");
3416 gcc_rich_location richloc (bar, &label0);
3417 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3418 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3420 test_diagnostic_context dc;
3421 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3422 ASSERT_STREQ (" foo = bar.field;\n"
3423 " ^~~\n"
3424 " |\n"
3425 " label 0\n"
3426 " label 1\n"
3427 " label 2\n",
3428 pp_formatted_text (dc.printer));
3431 /* Example of out-of-order ranges (thus requiring a sort), where
3432 they overlap, and there are multiple ranges on the same point. */
3434 text_range_label label_0a ("label 0a");
3435 text_range_label label_1a ("label 1a");
3436 text_range_label label_2a ("label 2a");
3437 text_range_label label_0b ("label 0b");
3438 text_range_label label_1b ("label 1b");
3439 text_range_label label_2b ("label 2b");
3440 text_range_label label_0c ("label 0c");
3441 text_range_label label_1c ("label 1c");
3442 text_range_label label_2c ("label 2c");
3443 gcc_rich_location richloc (field, &label_0a);
3444 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3445 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3447 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3448 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3449 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3451 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3452 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3453 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3455 test_diagnostic_context dc;
3456 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3457 ASSERT_STREQ (" foo = bar.field;\n"
3458 " ~~~ ~~~ ^~~~~\n"
3459 " | | |\n"
3460 " | | label 0a\n"
3461 " | | label 0b\n"
3462 " | | label 0c\n"
3463 " | label 1a\n"
3464 " | label 1b\n"
3465 " | label 1c\n"
3466 " label 2a\n"
3467 " label 2b\n"
3468 " label 2c\n",
3469 pp_formatted_text (dc.printer));
3472 /* Verify that a NULL result from range_label::get_text is
3473 handled gracefully. */
3475 text_range_label label (NULL);
3476 gcc_rich_location richloc (bar, &label);
3478 test_diagnostic_context dc;
3479 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3480 ASSERT_STREQ (" foo = bar.field;\n"
3481 " ^~~\n",
3482 pp_formatted_text (dc.printer));
3485 /* TODO: example of formatted printing (needs to be in
3486 gcc-rich-location.c due to Makefile.in issues). */
3489 /* Run the various one-liner tests. */
3491 static void
3492 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3494 /* Create a tempfile and write some text to it.
3495 ....................0000000001111111.
3496 ....................1234567890123456. */
3497 const char *content = "foo = bar.field;\n";
3498 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3499 line_table_test ltt (case_);
3501 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3503 location_t line_end = linemap_position_for_column (line_table, 16);
3505 /* Don't attempt to run the tests if column data might be unavailable. */
3506 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3507 return;
3509 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3510 ASSERT_EQ (1, LOCATION_LINE (line_end));
3511 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3513 test_one_liner_simple_caret ();
3514 test_one_liner_caret_and_range ();
3515 test_one_liner_multiple_carets_and_ranges ();
3516 test_one_liner_fixit_insert_before ();
3517 test_one_liner_fixit_insert_after ();
3518 test_one_liner_fixit_remove ();
3519 test_one_liner_fixit_replace ();
3520 test_one_liner_fixit_replace_non_equal_range ();
3521 test_one_liner_fixit_replace_equal_secondary_range ();
3522 test_one_liner_fixit_validation_adhoc_locations ();
3523 test_one_liner_many_fixits_1 ();
3524 test_one_liner_many_fixits_2 ();
3525 test_one_liner_labels ();
3528 /* Version of all one-liner tests exercising multibyte awareness. For
3529 simplicity we stick to using two multibyte characters in the test, U+1F602
3530 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3531 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3532 below asserts would be easier to read if we used UTF-8 directly in the
3533 string constants, but it seems better not to demand the host compiler
3534 support this, when it isn't otherwise necessary. Instead, whenever an
3535 extended character appears in a string, we put a line break after it so that
3536 all succeeding characters can appear visually at the correct display column.
3538 All of these work on the following 1-line source file:
3540 .0000000001111111111222222 display
3541 .1234567890123456789012345 columns
3542 "SS_foo = P_bar.SS_fieldP;\n"
3543 .0000000111111111222222223 byte
3544 .1356789012456789134567891 columns
3546 which is set up by test_diagnostic_show_locus_one_liner and calls
3547 them. Here SS represents the two display columns for the U+1F602 emoji and
3548 P represents the one display column for the U+03C0 pi symbol. */
3550 /* Just a caret. */
3552 static void
3553 test_one_liner_simple_caret_utf8 ()
3555 test_diagnostic_context dc;
3556 location_t caret = linemap_position_for_column (line_table, 18);
3557 rich_location richloc (line_table, caret);
3558 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3559 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3560 "_foo = \xcf\x80"
3561 "_bar.\xf0\x9f\x98\x82"
3562 "_field\xcf\x80"
3563 ";\n"
3564 " ^\n",
3565 pp_formatted_text (dc.printer));
3568 /* Caret and range. */
3569 static void
3570 test_one_liner_caret_and_range_utf8 ()
3572 test_diagnostic_context dc;
3573 location_t caret = linemap_position_for_column (line_table, 18);
3574 location_t start = linemap_position_for_column (line_table, 12);
3575 location_t finish = linemap_position_for_column (line_table, 30);
3576 location_t loc = make_location (caret, start, finish);
3577 rich_location richloc (line_table, loc);
3578 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3579 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3580 "_foo = \xcf\x80"
3581 "_bar.\xf0\x9f\x98\x82"
3582 "_field\xcf\x80"
3583 ";\n"
3584 " ~~~~~^~~~~~~~~~\n",
3585 pp_formatted_text (dc.printer));
3588 /* Multiple ranges and carets. */
3590 static void
3591 test_one_liner_multiple_carets_and_ranges_utf8 ()
3593 test_diagnostic_context dc;
3594 location_t foo
3595 = make_location (linemap_position_for_column (line_table, 7),
3596 linemap_position_for_column (line_table, 1),
3597 linemap_position_for_column (line_table, 8));
3598 dc.caret_chars[0] = 'A';
3600 location_t bar
3601 = make_location (linemap_position_for_column (line_table, 16),
3602 linemap_position_for_column (line_table, 12),
3603 linemap_position_for_column (line_table, 17));
3604 dc.caret_chars[1] = 'B';
3606 location_t field
3607 = make_location (linemap_position_for_column (line_table, 26),
3608 linemap_position_for_column (line_table, 19),
3609 linemap_position_for_column (line_table, 30));
3610 dc.caret_chars[2] = 'C';
3611 rich_location richloc (line_table, foo);
3612 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3613 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3614 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3615 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3616 "_foo = \xcf\x80"
3617 "_bar.\xf0\x9f\x98\x82"
3618 "_field\xcf\x80"
3619 ";\n"
3620 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3621 pp_formatted_text (dc.printer));
3624 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3626 static void
3627 test_one_liner_fixit_insert_before_utf8 ()
3629 test_diagnostic_context dc;
3630 location_t caret = linemap_position_for_column (line_table, 12);
3631 rich_location richloc (line_table, caret);
3632 richloc.add_fixit_insert_before ("&");
3633 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3634 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3635 "_foo = \xcf\x80"
3636 "_bar.\xf0\x9f\x98\x82"
3637 "_field\xcf\x80"
3638 ";\n"
3639 " ^\n"
3640 " &\n",
3641 pp_formatted_text (dc.printer));
3644 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3646 static void
3647 test_one_liner_fixit_insert_after_utf8 ()
3649 test_diagnostic_context dc;
3650 location_t start = linemap_position_for_column (line_table, 1);
3651 location_t finish = linemap_position_for_column (line_table, 8);
3652 location_t foo = make_location (start, start, finish);
3653 rich_location richloc (line_table, foo);
3654 richloc.add_fixit_insert_after ("[0]");
3655 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3656 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3657 "_foo = \xcf\x80"
3658 "_bar.\xf0\x9f\x98\x82"
3659 "_field\xcf\x80"
3660 ";\n"
3661 " ^~~~~~\n"
3662 " [0]\n",
3663 pp_formatted_text (dc.printer));
3666 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3668 static void
3669 test_one_liner_fixit_remove_utf8 ()
3671 test_diagnostic_context dc;
3672 location_t start = linemap_position_for_column (line_table, 18);
3673 location_t finish = linemap_position_for_column (line_table, 30);
3674 location_t dot = make_location (start, start, finish);
3675 rich_location richloc (line_table, dot);
3676 richloc.add_fixit_remove ();
3677 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3678 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3679 "_foo = \xcf\x80"
3680 "_bar.\xf0\x9f\x98\x82"
3681 "_field\xcf\x80"
3682 ";\n"
3683 " ^~~~~~~~~~\n"
3684 " ----------\n",
3685 pp_formatted_text (dc.printer));
3688 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3690 static void
3691 test_one_liner_fixit_replace_utf8 ()
3693 test_diagnostic_context dc;
3694 location_t start = linemap_position_for_column (line_table, 19);
3695 location_t finish = linemap_position_for_column (line_table, 30);
3696 location_t field = make_location (start, start, finish);
3697 rich_location richloc (line_table, field);
3698 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3699 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3700 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3701 "_foo = \xcf\x80"
3702 "_bar.\xf0\x9f\x98\x82"
3703 "_field\xcf\x80"
3704 ";\n"
3705 " ^~~~~~~~~\n"
3706 " m_\xf0\x9f\x98\x82"
3707 "_field\xcf\x80\n",
3708 pp_formatted_text (dc.printer));
3711 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3712 but where the caret was elsewhere. */
3714 static void
3715 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3717 test_diagnostic_context dc;
3718 location_t equals = linemap_position_for_column (line_table, 10);
3719 location_t start = linemap_position_for_column (line_table, 19);
3720 location_t finish = linemap_position_for_column (line_table, 30);
3721 rich_location richloc (line_table, equals);
3722 source_range range;
3723 range.m_start = start;
3724 range.m_finish = finish;
3725 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3726 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3727 /* The replacement range is not indicated in the annotation line, so
3728 it should be indicated via an additional underline. */
3729 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3730 "_foo = \xcf\x80"
3731 "_bar.\xf0\x9f\x98\x82"
3732 "_field\xcf\x80"
3733 ";\n"
3734 " ^\n"
3735 " ---------\n"
3736 " m_\xf0\x9f\x98\x82"
3737 "_field\xcf\x80\n",
3738 pp_formatted_text (dc.printer));
3741 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3742 where the caret was elsewhere, but where a secondary range
3743 exactly covers "field". */
3745 static void
3746 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3748 test_diagnostic_context dc;
3749 location_t equals = linemap_position_for_column (line_table, 10);
3750 location_t start = linemap_position_for_column (line_table, 19);
3751 location_t finish = linemap_position_for_column (line_table, 30);
3752 rich_location richloc (line_table, equals);
3753 location_t field = make_location (start, start, finish);
3754 richloc.add_range (field);
3755 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3756 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3757 /* The replacement range is indicated in the annotation line,
3758 so it shouldn't be indicated via an additional underline. */
3759 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3760 "_foo = \xcf\x80"
3761 "_bar.\xf0\x9f\x98\x82"
3762 "_field\xcf\x80"
3763 ";\n"
3764 " ^ ~~~~~~~~~\n"
3765 " m_\xf0\x9f\x98\x82"
3766 "_field\xcf\x80\n",
3767 pp_formatted_text (dc.printer));
3770 /* Verify that we can use ad-hoc locations when adding fixits to a
3771 rich_location. */
3773 static void
3774 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3776 /* Generate a range that's too long to be packed, so must
3777 be stored as an ad-hoc location (given the defaults
3778 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3779 const location_t c12 = linemap_position_for_column (line_table, 12);
3780 const location_t c52 = linemap_position_for_column (line_table, 52);
3781 const location_t loc = make_location (c12, c12, c52);
3783 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3784 return;
3786 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3788 /* Insert. */
3790 rich_location richloc (line_table, loc);
3791 richloc.add_fixit_insert_before (loc, "test");
3792 /* It should not have been discarded by the validator. */
3793 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3795 test_diagnostic_context dc;
3796 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3797 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3798 "_foo = \xcf\x80"
3799 "_bar.\xf0\x9f\x98\x82"
3800 "_field\xcf\x80"
3801 ";\n"
3802 " ^~~~~~~~~~~~~~~~ \n"
3803 " test\n",
3804 pp_formatted_text (dc.printer));
3807 /* Remove. */
3809 rich_location richloc (line_table, loc);
3810 source_range range = source_range::from_locations (loc, c52);
3811 richloc.add_fixit_remove (range);
3812 /* It should not have been discarded by the validator. */
3813 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3815 test_diagnostic_context dc;
3816 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3817 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3818 "_foo = \xcf\x80"
3819 "_bar.\xf0\x9f\x98\x82"
3820 "_field\xcf\x80"
3821 ";\n"
3822 " ^~~~~~~~~~~~~~~~ \n"
3823 " -------------------------------------\n",
3824 pp_formatted_text (dc.printer));
3827 /* Replace. */
3829 rich_location richloc (line_table, loc);
3830 source_range range = source_range::from_locations (loc, c52);
3831 richloc.add_fixit_replace (range, "test");
3832 /* It should not have been discarded by the validator. */
3833 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3835 test_diagnostic_context dc;
3836 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3837 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3838 "_foo = \xcf\x80"
3839 "_bar.\xf0\x9f\x98\x82"
3840 "_field\xcf\x80"
3841 ";\n"
3842 " ^~~~~~~~~~~~~~~~ \n"
3843 " test\n",
3844 pp_formatted_text (dc.printer));
3848 /* Test of consolidating insertions at the same location. */
3850 static void
3851 test_one_liner_many_fixits_1_utf8 ()
3853 test_diagnostic_context dc;
3854 location_t equals = linemap_position_for_column (line_table, 10);
3855 rich_location richloc (line_table, equals);
3856 for (int i = 0; i < 19; i++)
3857 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
3858 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3859 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3860 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3861 "_foo = \xcf\x80"
3862 "_bar.\xf0\x9f\x98\x82"
3863 "_field\xcf\x80"
3864 ";\n"
3865 " ^\n"
3866 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3867 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3868 pp_formatted_text (dc.printer));
3871 /* Ensure that we can add an arbitrary number of fix-it hints to a
3872 rich_location, even if they are not consolidated. */
3874 static void
3875 test_one_liner_many_fixits_2_utf8 ()
3877 test_diagnostic_context dc;
3878 location_t equals = linemap_position_for_column (line_table, 10);
3879 rich_location richloc (line_table, equals);
3880 const int nlocs = 19;
3881 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3882 34, 36, 38, 40, 42, 44};
3883 for (int i = 0; i != nlocs; ++i)
3885 location_t loc = linemap_position_for_column (line_table, locs[i]);
3886 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
3889 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
3890 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3891 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3892 "_foo = \xcf\x80"
3893 "_bar.\xf0\x9f\x98\x82"
3894 "_field\xcf\x80"
3895 ";\n"
3896 " ^\n"
3897 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3898 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3899 pp_formatted_text (dc.printer));
3902 /* Test of labeling the ranges within a rich_location. */
3904 static void
3905 test_one_liner_labels_utf8 ()
3907 location_t foo
3908 = make_location (linemap_position_for_column (line_table, 1),
3909 linemap_position_for_column (line_table, 1),
3910 linemap_position_for_column (line_table, 8));
3911 location_t bar
3912 = make_location (linemap_position_for_column (line_table, 12),
3913 linemap_position_for_column (line_table, 12),
3914 linemap_position_for_column (line_table, 17));
3915 location_t field
3916 = make_location (linemap_position_for_column (line_table, 19),
3917 linemap_position_for_column (line_table, 19),
3918 linemap_position_for_column (line_table, 30));
3920 /* Example where all the labels fit on one line. */
3922 /* These three labels contain multibyte characters such that their byte
3923 lengths are respectively (12, 10, 18), but their display widths are only
3924 (6, 5, 9). All three fit on the line when considering the display
3925 widths, but not when considering the byte widths, so verify that we do
3926 indeed put them all on one line. */
3927 text_range_label label0
3928 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3929 text_range_label label1
3930 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3931 text_range_label label2
3932 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3933 "\xcf\x80");
3934 gcc_rich_location richloc (foo, &label0);
3935 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3936 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3939 test_diagnostic_context dc;
3940 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3941 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3942 "_foo = \xcf\x80"
3943 "_bar.\xf0\x9f\x98\x82"
3944 "_field\xcf\x80"
3945 ";\n"
3946 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3947 " | | |\n"
3948 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3949 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3950 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3951 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3952 pp_formatted_text (dc.printer));
3957 /* Example where the labels need extra lines. */
3959 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
3960 text_range_label label1 ("label 1\xcf\x80");
3961 text_range_label label2 ("label 2\xcf\x80");
3962 gcc_rich_location richloc (foo, &label0);
3963 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3964 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3966 test_diagnostic_context dc;
3967 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3969 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3970 "_foo = \xcf\x80"
3971 "_bar.\xf0\x9f\x98\x82"
3972 "_field\xcf\x80"
3973 ";\n"
3974 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3975 " | | |\n"
3976 " | | label 2\xcf\x80\n"
3977 " | label 1\xcf\x80\n"
3978 " label 0\xf0\x9f\x98\x82\n",
3979 pp_formatted_text (dc.printer));
3982 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3983 but label 1 just touches label 2. */
3985 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3986 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3987 text_range_label label2 ("c");
3988 gcc_rich_location richloc (foo, &label0);
3989 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3990 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3992 test_diagnostic_context dc;
3993 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3994 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3995 "_foo = \xcf\x80"
3996 "_bar.\xf0\x9f\x98\x82"
3997 "_field\xcf\x80"
3998 ";\n"
3999 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4000 " | | |\n"
4001 " | | c\n"
4002 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4003 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4004 pp_formatted_text (dc.printer));
4008 /* Make sure that colorization codes don't interrupt a multibyte
4009 sequence, which would corrupt it. */
4010 static void
4011 test_one_liner_colorized_utf8 ()
4013 test_diagnostic_context dc;
4014 dc.colorize_source_p = true;
4015 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4016 const location_t pi = linemap_position_for_column (line_table, 12);
4017 rich_location richloc (line_table, pi);
4018 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4020 /* In order to avoid having the test depend on exactly how the colorization
4021 was effected, just confirm there are two pi characters in the output. */
4022 const char *result = pp_formatted_text (dc.printer);
4023 const char *null_term = result + strlen (result);
4024 const char *first_pi = strstr (result, "\xcf\x80");
4025 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4026 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4029 /* Run the various one-liner tests. */
4031 static void
4032 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4034 /* Create a tempfile and write some text to it. */
4035 const char *content
4036 /* Display columns.
4037 0000000000000000000000011111111111111111111111111111112222222222222
4038 1111111122222222345678900000000123456666666677777777890123444444445 */
4039 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4040 /* 0000000000000000000001111111111111111111222222222222222222222233333
4041 1111222233334444567890122223333456789999000011112222345678999900001
4042 Byte columns. */
4043 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4044 line_table_test ltt (case_);
4046 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4048 location_t line_end = linemap_position_for_column (line_table, 31);
4050 /* Don't attempt to run the tests if column data might be unavailable. */
4051 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4052 return;
4054 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4055 ASSERT_EQ (1, LOCATION_LINE (line_end));
4056 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4058 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4059 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4060 def_tabstop));
4061 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4062 def_tabstop));
4064 test_one_liner_simple_caret_utf8 ();
4065 test_one_liner_caret_and_range_utf8 ();
4066 test_one_liner_multiple_carets_and_ranges_utf8 ();
4067 test_one_liner_fixit_insert_before_utf8 ();
4068 test_one_liner_fixit_insert_after_utf8 ();
4069 test_one_liner_fixit_remove_utf8 ();
4070 test_one_liner_fixit_replace_utf8 ();
4071 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4072 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4073 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4074 test_one_liner_many_fixits_1_utf8 ();
4075 test_one_liner_many_fixits_2_utf8 ();
4076 test_one_liner_labels_utf8 ();
4077 test_one_liner_colorized_utf8 ();
4080 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4082 static void
4083 test_add_location_if_nearby (const line_table_case &case_)
4085 /* Create a tempfile and write some text to it.
4086 ...000000000111111111122222222223333333333.
4087 ...123456789012345678901234567890123456789. */
4088 const char *content
4089 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4090 "struct different_line\n" /* line 2. */
4091 "{\n" /* line 3. */
4092 " double x;\n" /* line 4. */
4093 " double y;\n" /* line 5. */
4094 ";\n"); /* line 6. */
4095 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4096 line_table_test ltt (case_);
4098 const line_map_ordinary *ord_map
4099 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4100 tmp.get_filename (), 0));
4102 linemap_line_start (line_table, 1, 100);
4104 const location_t final_line_end
4105 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4107 /* Don't attempt to run the tests if column data might be unavailable. */
4108 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4109 return;
4111 /* Test of add_location_if_nearby on the same line as the
4112 primary location. */
4114 const location_t missing_close_brace_1_39
4115 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4116 const location_t matching_open_brace_1_18
4117 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4118 gcc_rich_location richloc (missing_close_brace_1_39);
4119 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4120 ASSERT_TRUE (added);
4121 ASSERT_EQ (2, richloc.get_num_locations ());
4122 test_diagnostic_context dc;
4123 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4124 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4125 " ~ ^\n",
4126 pp_formatted_text (dc.printer));
4129 /* Test of add_location_if_nearby on a different line to the
4130 primary location. */
4132 const location_t missing_close_brace_6_1
4133 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4134 const location_t matching_open_brace_3_1
4135 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4136 gcc_rich_location richloc (missing_close_brace_6_1);
4137 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4138 ASSERT_FALSE (added);
4139 ASSERT_EQ (1, richloc.get_num_locations ());
4143 /* Verify that we print fixits even if they only affect lines
4144 outside those covered by the ranges in the rich_location. */
4146 static void
4147 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4149 /* Create a tempfile and write some text to it.
4150 ...000000000111111111122222222223333333333.
4151 ...123456789012345678901234567890123456789. */
4152 const char *content
4153 = ("struct point { double x; double y; };\n" /* line 1. */
4154 "struct point origin = {x: 0.0,\n" /* line 2. */
4155 " y\n" /* line 3. */
4156 "\n" /* line 4. */
4157 "\n" /* line 5. */
4158 " : 0.0};\n"); /* line 6. */
4159 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4160 line_table_test ltt (case_);
4162 const line_map_ordinary *ord_map
4163 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4164 tmp.get_filename (), 0));
4166 linemap_line_start (line_table, 1, 100);
4168 const location_t final_line_end
4169 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4171 /* Don't attempt to run the tests if column data might be unavailable. */
4172 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4173 return;
4175 /* A pair of tests for modernizing the initializers to C99-style. */
4177 /* The one-liner case (line 2). */
4179 test_diagnostic_context dc;
4180 const location_t x
4181 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4182 const location_t colon
4183 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4184 rich_location richloc (line_table, colon);
4185 richloc.add_fixit_insert_before (x, ".");
4186 richloc.add_fixit_replace (colon, "=");
4187 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4188 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4189 " ^\n"
4190 " .=\n",
4191 pp_formatted_text (dc.printer));
4194 /* The multiline case. The caret for the rich_location is on line 6;
4195 verify that insertion fixit on line 3 is still printed (and that
4196 span starts are printed due to the gap between the span at line 3
4197 and that at line 6). */
4199 test_diagnostic_context dc;
4200 const location_t y
4201 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4202 const location_t colon
4203 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4204 rich_location richloc (line_table, colon);
4205 richloc.add_fixit_insert_before (y, ".");
4206 richloc.add_fixit_replace (colon, "=");
4207 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4208 ASSERT_STREQ ("FILENAME:3:24:\n"
4209 " y\n"
4210 " .\n"
4211 "FILENAME:6:25:\n"
4212 " : 0.0};\n"
4213 " ^\n"
4214 " =\n",
4215 pp_formatted_text (dc.printer));
4218 /* As above, but verify the behavior of multiple line spans
4219 with line-numbering enabled. */
4221 const location_t y
4222 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4223 const location_t colon
4224 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4225 rich_location richloc (line_table, colon);
4226 richloc.add_fixit_insert_before (y, ".");
4227 richloc.add_fixit_replace (colon, "=");
4228 test_diagnostic_context dc;
4229 dc.show_line_numbers_p = true;
4230 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4231 ASSERT_STREQ (" 3 | y\n"
4232 " | .\n"
4233 "......\n"
4234 " 6 | : 0.0};\n"
4235 " | ^\n"
4236 " | =\n",
4237 pp_formatted_text (dc.printer));
4242 /* Verify that fix-it hints are appropriately consolidated.
4244 If any fix-it hints in a rich_location involve locations beyond
4245 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4246 the fix-it as a whole, so there should be none.
4248 Otherwise, verify that consecutive "replace" and "remove" fix-its
4249 are merged, and that other fix-its remain separate. */
4251 static void
4252 test_fixit_consolidation (const line_table_case &case_)
4254 line_table_test ltt (case_);
4256 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4258 const location_t c10 = linemap_position_for_column (line_table, 10);
4259 const location_t c15 = linemap_position_for_column (line_table, 15);
4260 const location_t c16 = linemap_position_for_column (line_table, 16);
4261 const location_t c17 = linemap_position_for_column (line_table, 17);
4262 const location_t c20 = linemap_position_for_column (line_table, 20);
4263 const location_t c21 = linemap_position_for_column (line_table, 21);
4264 const location_t caret = c10;
4266 /* Insert + insert. */
4268 rich_location richloc (line_table, caret);
4269 richloc.add_fixit_insert_before (c10, "foo");
4270 richloc.add_fixit_insert_before (c15, "bar");
4272 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4273 /* Bogus column info for 2nd fixit, so no fixits. */
4274 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4275 else
4276 /* They should not have been merged. */
4277 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4280 /* Insert + replace. */
4282 rich_location richloc (line_table, caret);
4283 richloc.add_fixit_insert_before (c10, "foo");
4284 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4285 "bar");
4287 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4288 /* Bogus column info for 2nd fixit, so no fixits. */
4289 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4290 else
4291 /* They should not have been merged. */
4292 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4295 /* Replace + non-consecutive insert. */
4297 rich_location richloc (line_table, caret);
4298 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4299 "bar");
4300 richloc.add_fixit_insert_before (c17, "foo");
4302 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4303 /* Bogus column info for 2nd fixit, so no fixits. */
4304 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4305 else
4306 /* They should not have been merged. */
4307 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4310 /* Replace + non-consecutive replace. */
4312 rich_location richloc (line_table, caret);
4313 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4314 "foo");
4315 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4316 "bar");
4318 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4319 /* Bogus column info for 2nd fixit, so no fixits. */
4320 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4321 else
4322 /* They should not have been merged. */
4323 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4326 /* Replace + consecutive replace. */
4328 rich_location richloc (line_table, caret);
4329 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4330 "foo");
4331 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4332 "bar");
4334 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4335 /* Bogus column info for 2nd fixit, so no fixits. */
4336 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4337 else
4339 /* They should have been merged into a single "replace". */
4340 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4341 const fixit_hint *hint = richloc.get_fixit_hint (0);
4342 ASSERT_STREQ ("foobar", hint->get_string ());
4343 ASSERT_EQ (c10, hint->get_start_loc ());
4344 ASSERT_EQ (c21, hint->get_next_loc ());
4348 /* Replace + consecutive removal. */
4350 rich_location richloc (line_table, caret);
4351 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4352 "foo");
4353 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4355 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4356 /* Bogus column info for 2nd fixit, so no fixits. */
4357 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4358 else
4360 /* They should have been merged into a single replace, with the
4361 range extended to cover that of the removal. */
4362 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4363 const fixit_hint *hint = richloc.get_fixit_hint (0);
4364 ASSERT_STREQ ("foo", hint->get_string ());
4365 ASSERT_EQ (c10, hint->get_start_loc ());
4366 ASSERT_EQ (c21, hint->get_next_loc ());
4370 /* Consecutive removals. */
4372 rich_location richloc (line_table, caret);
4373 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4374 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4376 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4377 /* Bogus column info for 2nd fixit, so no fixits. */
4378 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4379 else
4381 /* They should have been merged into a single "replace-with-empty". */
4382 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4383 const fixit_hint *hint = richloc.get_fixit_hint (0);
4384 ASSERT_STREQ ("", hint->get_string ());
4385 ASSERT_EQ (c10, hint->get_start_loc ());
4386 ASSERT_EQ (c21, hint->get_next_loc ());
4391 /* Verify that the line_corrections machinery correctly prints
4392 overlapping fixit-hints. */
4394 static void
4395 test_overlapped_fixit_printing (const line_table_case &case_)
4397 /* Create a tempfile and write some text to it.
4398 ...000000000111111111122222222223333333333.
4399 ...123456789012345678901234567890123456789. */
4400 const char *content
4401 = (" foo *f = (foo *)ptr->field;\n");
4402 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4403 line_table_test ltt (case_);
4405 const line_map_ordinary *ord_map
4406 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4407 tmp.get_filename (), 0));
4409 linemap_line_start (line_table, 1, 100);
4411 const location_t final_line_end
4412 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4414 /* Don't attempt to run the tests if column data might be unavailable. */
4415 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4416 return;
4418 /* A test for converting a C-style cast to a C++-style cast. */
4419 const location_t open_paren
4420 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4421 const location_t close_paren
4422 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4423 const location_t expr_start
4424 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4425 const location_t expr_finish
4426 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4427 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4429 /* Various examples of fix-it hints that aren't themselves consolidated,
4430 but for which the *printing* may need consolidation. */
4432 /* Example where 3 fix-it hints are printed as one. */
4434 test_diagnostic_context dc;
4435 rich_location richloc (line_table, expr);
4436 richloc.add_fixit_replace (open_paren, "const_cast<");
4437 richloc.add_fixit_replace (close_paren, "> (");
4438 richloc.add_fixit_insert_after (")");
4440 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4441 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4442 " ^~~~~~~~~~\n"
4443 " -----------------\n"
4444 " const_cast<foo *> (ptr->field)\n",
4445 pp_formatted_text (dc.printer));
4447 /* Unit-test the line_corrections machinery. */
4448 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4449 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4450 ASSERT_EQ (column_range (12, 12),
4451 get_affected_range (&dc, hint_0, CU_BYTES));
4452 ASSERT_EQ (column_range (12, 12),
4453 get_affected_range (&dc, hint_0, CU_DISPLAY_COLS));
4454 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc, hint_0));
4455 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4456 ASSERT_EQ (column_range (18, 18),
4457 get_affected_range (&dc, hint_1, CU_BYTES));
4458 ASSERT_EQ (column_range (18, 18),
4459 get_affected_range (&dc, hint_1, CU_DISPLAY_COLS));
4460 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc, hint_1));
4461 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4462 ASSERT_EQ (column_range (29, 28),
4463 get_affected_range (&dc, hint_2, CU_BYTES));
4464 ASSERT_EQ (column_range (29, 28),
4465 get_affected_range (&dc, hint_2, CU_DISPLAY_COLS));
4466 ASSERT_EQ (column_range (29, 29), get_printed_columns (&dc, hint_2));
4468 /* Add each hint in turn to a line_corrections instance,
4469 and verify that they are consolidated into one correction instance
4470 as expected. */
4471 line_corrections lc (&dc, tmp.get_filename (), 1);
4473 /* The first replace hint by itself. */
4474 lc.add_hint (hint_0);
4475 ASSERT_EQ (1, lc.m_corrections.length ());
4476 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4477 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4478 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4479 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4481 /* After the second replacement hint, they are printed together
4482 as a replacement (along with the text between them). */
4483 lc.add_hint (hint_1);
4484 ASSERT_EQ (1, lc.m_corrections.length ());
4485 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4486 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4487 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4488 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4490 /* After the final insertion hint, they are all printed together
4491 as a replacement (along with the text between them). */
4492 lc.add_hint (hint_2);
4493 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4494 lc.m_corrections[0]->m_text);
4495 ASSERT_EQ (1, lc.m_corrections.length ());
4496 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4497 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4498 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4501 /* Example where two are consolidated during printing. */
4503 test_diagnostic_context dc;
4504 rich_location richloc (line_table, expr);
4505 richloc.add_fixit_replace (open_paren, "CAST (");
4506 richloc.add_fixit_replace (close_paren, ") (");
4507 richloc.add_fixit_insert_after (")");
4509 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4510 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4511 " ^~~~~~~~~~\n"
4512 " -\n"
4513 " CAST (-\n"
4514 " ) ( )\n",
4515 pp_formatted_text (dc.printer));
4518 /* Example where none are consolidated during printing. */
4520 test_diagnostic_context dc;
4521 rich_location richloc (line_table, expr);
4522 richloc.add_fixit_replace (open_paren, "CST (");
4523 richloc.add_fixit_replace (close_paren, ") (");
4524 richloc.add_fixit_insert_after (")");
4526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4527 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4528 " ^~~~~~~~~~\n"
4529 " -\n"
4530 " CST ( -\n"
4531 " ) ( )\n",
4532 pp_formatted_text (dc.printer));
4535 /* Example of deletion fix-it hints. */
4537 test_diagnostic_context dc;
4538 rich_location richloc (line_table, expr);
4539 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4540 source_range victim = {open_paren, close_paren};
4541 richloc.add_fixit_remove (victim);
4543 /* This case is actually handled by fixit-consolidation,
4544 rather than by line_corrections. */
4545 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4547 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4548 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4549 " ^~~~~~~~~~\n"
4550 " -------\n"
4551 " (bar *)\n",
4552 pp_formatted_text (dc.printer));
4555 /* Example of deletion fix-it hints that would overlap. */
4557 test_diagnostic_context dc;
4558 rich_location richloc (line_table, expr);
4559 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4560 source_range victim = {expr_start, expr_finish};
4561 richloc.add_fixit_remove (victim);
4563 /* These fixits are not consolidated. */
4564 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4566 /* But the corrections are. */
4567 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4568 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4569 " ^~~~~~~~~~\n"
4570 " -----------------\n"
4571 " (longer *)(foo *)\n",
4572 pp_formatted_text (dc.printer));
4575 /* Example of insertion fix-it hints that would overlap. */
4577 test_diagnostic_context dc;
4578 rich_location richloc (line_table, expr);
4579 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4580 richloc.add_fixit_insert_after (close_paren, "TEST");
4582 /* The first insertion is long enough that if printed naively,
4583 it would overlap with the second.
4584 Verify that they are printed as a single replacement. */
4585 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4586 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4587 " ^~~~~~~~~~\n"
4588 " -------\n"
4589 " LONGER THAN THE CAST(foo *)TEST\n",
4590 pp_formatted_text (dc.printer));
4594 /* Multibyte-aware version of preceding tests. See comments above
4595 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4596 characters here. */
4598 static void
4599 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4601 /* Create a tempfile and write some text to it. */
4603 const char *content
4604 /* Display columns.
4605 00000000000000000000000111111111111111111111111222222222222222223
4606 12344444444555555556789012344444444555555556789012345678999999990 */
4607 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4608 /* 00000000000000000000011111111111111111111112222222222333333333333
4609 12344445555666677778901234566667777888899990123456789012333344445
4610 Byte columns. */
4612 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4613 line_table_test ltt (case_);
4615 const line_map_ordinary *ord_map
4616 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4617 tmp.get_filename (), 0));
4619 linemap_line_start (line_table, 1, 100);
4621 const location_t final_line_end
4622 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4624 /* Don't attempt to run the tests if column data might be unavailable. */
4625 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4626 return;
4628 /* A test for converting a C-style cast to a C++-style cast. */
4629 const location_t open_paren
4630 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4631 const location_t close_paren
4632 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4633 const location_t expr_start
4634 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4635 const location_t expr_finish
4636 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4637 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4639 /* Various examples of fix-it hints that aren't themselves consolidated,
4640 but for which the *printing* may need consolidation. */
4642 /* Example where 3 fix-it hints are printed as one. */
4644 test_diagnostic_context dc;
4645 rich_location richloc (line_table, expr);
4646 richloc.add_fixit_replace (open_paren, "const_cast<");
4647 richloc.add_fixit_replace (close_paren, "> (");
4648 richloc.add_fixit_insert_after (")");
4650 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4651 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4652 " *f = (f\xf0\x9f\x98\x82"
4653 " *)ptr->field\xcf\x80"
4654 ";\n"
4655 " ^~~~~~~~~~~\n"
4656 " ------------------\n"
4657 " const_cast<f\xf0\x9f\x98\x82"
4658 " *> (ptr->field\xcf\x80"
4659 ")\n",
4660 pp_formatted_text (dc.printer));
4662 /* Unit-test the line_corrections machinery. */
4663 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4664 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4665 ASSERT_EQ (column_range (14, 14),
4666 get_affected_range (&dc, hint_0, CU_BYTES));
4667 ASSERT_EQ (column_range (12, 12),
4668 get_affected_range (&dc, hint_0, CU_DISPLAY_COLS));
4669 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc, hint_0));
4670 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4671 ASSERT_EQ (column_range (22, 22),
4672 get_affected_range (&dc, hint_1, CU_BYTES));
4673 ASSERT_EQ (column_range (18, 18),
4674 get_affected_range (&dc, hint_1, CU_DISPLAY_COLS));
4675 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc, hint_1));
4676 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4677 ASSERT_EQ (column_range (35, 34),
4678 get_affected_range (&dc, hint_2, CU_BYTES));
4679 ASSERT_EQ (column_range (30, 29),
4680 get_affected_range (&dc, hint_2, CU_DISPLAY_COLS));
4681 ASSERT_EQ (column_range (30, 30), get_printed_columns (&dc, hint_2));
4683 /* Add each hint in turn to a line_corrections instance,
4684 and verify that they are consolidated into one correction instance
4685 as expected. */
4686 line_corrections lc (&dc, tmp.get_filename (), 1);
4688 /* The first replace hint by itself. */
4689 lc.add_hint (hint_0);
4690 ASSERT_EQ (1, lc.m_corrections.length ());
4691 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
4692 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4693 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4694 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4696 /* After the second replacement hint, they are printed together
4697 as a replacement (along with the text between them). */
4698 lc.add_hint (hint_1);
4699 ASSERT_EQ (1, lc.m_corrections.length ());
4700 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4701 lc.m_corrections[0]->m_text);
4702 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
4703 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4704 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4706 /* After the final insertion hint, they are all printed together
4707 as a replacement (along with the text between them). */
4708 lc.add_hint (hint_2);
4709 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4710 lc.m_corrections[0]->m_text);
4711 ASSERT_EQ (1, lc.m_corrections.length ());
4712 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
4713 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
4714 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
4717 /* Example where two are consolidated during printing. */
4719 test_diagnostic_context dc;
4720 rich_location richloc (line_table, expr);
4721 richloc.add_fixit_replace (open_paren, "CAST (");
4722 richloc.add_fixit_replace (close_paren, ") (");
4723 richloc.add_fixit_insert_after (")");
4725 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4726 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4727 " *f = (f\xf0\x9f\x98\x82"
4728 " *)ptr->field\xcf\x80"
4729 ";\n"
4730 " ^~~~~~~~~~~\n"
4731 " -\n"
4732 " CAST (-\n"
4733 " ) ( )\n",
4734 pp_formatted_text (dc.printer));
4737 /* Example where none are consolidated during printing. */
4739 test_diagnostic_context dc;
4740 rich_location richloc (line_table, expr);
4741 richloc.add_fixit_replace (open_paren, "CST (");
4742 richloc.add_fixit_replace (close_paren, ") (");
4743 richloc.add_fixit_insert_after (")");
4745 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4746 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4747 " *f = (f\xf0\x9f\x98\x82"
4748 " *)ptr->field\xcf\x80"
4749 ";\n"
4750 " ^~~~~~~~~~~\n"
4751 " -\n"
4752 " CST ( -\n"
4753 " ) ( )\n",
4754 pp_formatted_text (dc.printer));
4757 /* Example of deletion fix-it hints. */
4759 test_diagnostic_context dc;
4760 rich_location richloc (line_table, expr);
4761 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
4762 source_range victim = {open_paren, close_paren};
4763 richloc.add_fixit_remove (victim);
4765 /* This case is actually handled by fixit-consolidation,
4766 rather than by line_corrections. */
4767 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4769 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4770 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4771 " *f = (f\xf0\x9f\x98\x82"
4772 " *)ptr->field\xcf\x80"
4773 ";\n"
4774 " ^~~~~~~~~~~\n"
4775 " -------\n"
4776 " (bar\xf0\x9f\x98\x82"
4777 " *)\n",
4778 pp_formatted_text (dc.printer));
4781 /* Example of deletion fix-it hints that would overlap. */
4783 test_diagnostic_context dc;
4784 rich_location richloc (line_table, expr);
4785 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
4786 source_range victim = {expr_start, expr_finish};
4787 richloc.add_fixit_remove (victim);
4789 /* These fixits are not consolidated. */
4790 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4792 /* But the corrections are. */
4793 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4794 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4795 " *f = (f\xf0\x9f\x98\x82"
4796 " *)ptr->field\xcf\x80"
4797 ";\n"
4798 " ^~~~~~~~~~~\n"
4799 " ------------------\n"
4800 " (long\xf0\x9f\x98\x82"
4801 " *)(f\xf0\x9f\x98\x82"
4802 " *)\n",
4803 pp_formatted_text (dc.printer));
4806 /* Example of insertion fix-it hints that would overlap. */
4808 test_diagnostic_context dc;
4809 rich_location richloc (line_table, expr);
4810 richloc.add_fixit_insert_before
4811 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4812 richloc.add_fixit_insert_after (close_paren, "TEST");
4814 /* The first insertion is long enough that if printed naively,
4815 it would overlap with the second.
4816 Verify that they are printed as a single replacement. */
4817 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4818 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4819 " *f = (f\xf0\x9f\x98\x82"
4820 " *)ptr->field\xcf\x80"
4821 ";\n"
4822 " ^~~~~~~~~~~\n"
4823 " -------\n"
4824 " L\xf0\x9f\x98\x82"
4825 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4826 " *)TEST\n",
4827 pp_formatted_text (dc.printer));
4831 /* Verify that the line_corrections machinery correctly prints
4832 overlapping fixit-hints that have been added in the wrong
4833 order.
4834 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4836 static void
4837 test_overlapped_fixit_printing_2 (const line_table_case &case_)
4839 /* Create a tempfile and write some text to it.
4840 ...000000000111111111122222222223333333333.
4841 ...123456789012345678901234567890123456789. */
4842 const char *content
4843 = ("int a5[][0][0] = { 1, 2 };\n");
4844 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4845 line_table_test ltt (case_);
4847 const line_map_ordinary *ord_map
4848 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4849 tmp.get_filename (), 0));
4851 linemap_line_start (line_table, 1, 100);
4853 const location_t final_line_end
4854 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
4856 /* Don't attempt to run the tests if column data might be unavailable. */
4857 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4858 return;
4860 const location_t col_1
4861 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4862 const location_t col_20
4863 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
4864 const location_t col_21
4865 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
4866 const location_t col_23
4867 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4868 const location_t col_25
4869 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
4871 /* Two insertions, in the wrong order. */
4873 test_diagnostic_context dc;
4875 rich_location richloc (line_table, col_20);
4876 richloc.add_fixit_insert_before (col_23, "{");
4877 richloc.add_fixit_insert_before (col_21, "}");
4879 /* These fixits should be accepted; they can't be consolidated. */
4880 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4881 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4882 ASSERT_EQ (column_range (23, 22),
4883 get_affected_range (&dc, hint_0, CU_BYTES));
4884 ASSERT_EQ (column_range (23, 23), get_printed_columns (&dc, hint_0));
4885 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4886 ASSERT_EQ (column_range (21, 20),
4887 get_affected_range (&dc, hint_1, CU_BYTES));
4888 ASSERT_EQ (column_range (21, 21), get_printed_columns (&dc, hint_1));
4890 /* Verify that they're printed correctly. */
4891 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4892 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4893 " ^\n"
4894 " } {\n",
4895 pp_formatted_text (dc.printer));
4898 /* Various overlapping insertions, some occurring "out of order"
4899 (reproducing the fix-it hints from PR c/81405). */
4901 test_diagnostic_context dc;
4902 rich_location richloc (line_table, col_20);
4904 richloc.add_fixit_insert_before (col_20, "{{");
4905 richloc.add_fixit_insert_before (col_21, "}}");
4906 richloc.add_fixit_insert_before (col_23, "{");
4907 richloc.add_fixit_insert_before (col_21, "}");
4908 richloc.add_fixit_insert_before (col_23, "{{");
4909 richloc.add_fixit_insert_before (col_25, "}");
4910 richloc.add_fixit_insert_before (col_21, "}");
4911 richloc.add_fixit_insert_before (col_1, "{");
4912 richloc.add_fixit_insert_before (col_25, "}");
4913 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4914 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4915 " ^\n"
4916 " { -----\n"
4917 " {{1}}}}, {{{2 }}\n",
4918 pp_formatted_text (dc.printer));
4922 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4924 static void
4925 test_fixit_insert_containing_newline (const line_table_case &case_)
4927 /* Create a tempfile and write some text to it.
4928 .........................0000000001111111.
4929 .........................1234567890123456. */
4930 const char *old_content = (" case 'a':\n" /* line 1. */
4931 " x = a;\n" /* line 2. */
4932 " case 'b':\n" /* line 3. */
4933 " x = b;\n");/* line 4. */
4935 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4936 line_table_test ltt (case_);
4937 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
4939 location_t case_start = linemap_position_for_column (line_table, 5);
4940 location_t case_finish = linemap_position_for_column (line_table, 13);
4941 location_t case_loc = make_location (case_start, case_start, case_finish);
4942 location_t line_start = linemap_position_for_column (line_table, 1);
4944 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4945 return;
4947 /* Add a "break;" on a line by itself before line 3 i.e. before
4948 column 1 of line 3. */
4950 rich_location richloc (line_table, case_loc);
4951 richloc.add_fixit_insert_before (line_start, " break;\n");
4953 /* Without line numbers. */
4955 test_diagnostic_context dc;
4956 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4957 ASSERT_STREQ (" x = a;\n"
4958 "+ break;\n"
4959 " case 'b':\n"
4960 " ^~~~~~~~~\n",
4961 pp_formatted_text (dc.printer));
4964 /* With line numbers. */
4966 test_diagnostic_context dc;
4967 dc.show_line_numbers_p = true;
4968 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4969 ASSERT_STREQ (" 2 | x = a;\n"
4970 " +++ |+ break;\n"
4971 " 3 | case 'b':\n"
4972 " | ^~~~~~~~~\n",
4973 pp_formatted_text (dc.printer));
4977 /* Verify that attempts to add text with a newline fail when the
4978 insertion point is *not* at the start of a line. */
4980 rich_location richloc (line_table, case_loc);
4981 richloc.add_fixit_insert_before (case_start, "break;\n");
4982 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4983 test_diagnostic_context dc;
4984 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4985 ASSERT_STREQ (" case 'b':\n"
4986 " ^~~~~~~~~\n",
4987 pp_formatted_text (dc.printer));
4991 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4992 of the file, where the fix-it is printed in a different line-span
4993 to the primary range of the diagnostic. */
4995 static void
4996 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
4998 /* Create a tempfile and write some text to it.
4999 .........................0000000001111111.
5000 .........................1234567890123456. */
5001 const char *old_content = ("test (int ch)\n" /* line 1. */
5002 "{\n" /* line 2. */
5003 " putchar (ch);\n" /* line 3. */
5004 "}\n"); /* line 4. */
5006 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5007 line_table_test ltt (case_);
5009 const line_map_ordinary *ord_map = linemap_check_ordinary
5010 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5011 linemap_line_start (line_table, 1, 100);
5013 /* The primary range is the "putchar" token. */
5014 location_t putchar_start
5015 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5016 location_t putchar_finish
5017 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5018 location_t putchar_loc
5019 = make_location (putchar_start, putchar_start, putchar_finish);
5020 rich_location richloc (line_table, putchar_loc);
5022 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5023 location_t file_start
5024 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5025 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5027 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5028 return;
5031 test_diagnostic_context dc;
5032 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5033 ASSERT_STREQ ("FILENAME:1:1:\n"
5034 "+#include <stdio.h>\n"
5035 " test (int ch)\n"
5036 "FILENAME:3:2:\n"
5037 " putchar (ch);\n"
5038 " ^~~~~~~\n",
5039 pp_formatted_text (dc.printer));
5042 /* With line-numbering, the line spans are close enough to be
5043 consolidated, since it makes little sense to skip line 2. */
5045 test_diagnostic_context dc;
5046 dc.show_line_numbers_p = true;
5047 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5048 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5049 " 1 | test (int ch)\n"
5050 " 2 | {\n"
5051 " 3 | putchar (ch);\n"
5052 " | ^~~~~~~\n",
5053 pp_formatted_text (dc.printer));
5057 /* Replacement fix-it hint containing a newline.
5058 This will fail, as newlines are only supported when inserting at the
5059 beginning of a line. */
5061 static void
5062 test_fixit_replace_containing_newline (const line_table_case &case_)
5064 /* Create a tempfile and write some text to it.
5065 .........................0000000001111.
5066 .........................1234567890123. */
5067 const char *old_content = "foo = bar ();\n";
5069 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5070 line_table_test ltt (case_);
5071 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5073 /* Replace the " = " with "\n = ", as if we were reformatting an
5074 overly long line. */
5075 location_t start = linemap_position_for_column (line_table, 4);
5076 location_t finish = linemap_position_for_column (line_table, 6);
5077 location_t loc = linemap_position_for_column (line_table, 13);
5078 rich_location richloc (line_table, loc);
5079 source_range range = source_range::from_locations (start, finish);
5080 richloc.add_fixit_replace (range, "\n =");
5082 /* Arbitrary newlines are not yet supported within fix-it hints, so
5083 the fix-it should not be displayed. */
5084 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5086 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5087 return;
5089 test_diagnostic_context dc;
5090 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5091 ASSERT_STREQ (" foo = bar ();\n"
5092 " ^\n",
5093 pp_formatted_text (dc.printer));
5096 /* Fix-it hint, attempting to delete a newline.
5097 This will fail, as we currently only support fix-it hints that
5098 affect one line at a time. */
5100 static void
5101 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5103 /* Create a tempfile and write some text to it.
5104 ..........................0000000001111.
5105 ..........................1234567890123. */
5106 const char *old_content = ("foo = bar (\n"
5107 " );\n");
5109 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5110 line_table_test ltt (case_);
5111 const line_map_ordinary *ord_map = linemap_check_ordinary
5112 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5113 linemap_line_start (line_table, 1, 100);
5115 /* Attempt to delete the " (\n...)". */
5116 location_t start
5117 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5118 location_t caret
5119 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5120 location_t finish
5121 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5122 location_t loc = make_location (caret, start, finish);
5123 rich_location richloc (line_table, loc);
5124 richloc. add_fixit_remove ();
5126 /* Fix-it hints that affect more than one line are not yet supported, so
5127 the fix-it should not be displayed. */
5128 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5130 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5131 return;
5133 test_diagnostic_context dc;
5134 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5135 ASSERT_STREQ (" foo = bar (\n"
5136 " ~^\n"
5137 " );\n"
5138 " ~ \n",
5139 pp_formatted_text (dc.printer));
5142 static void
5143 test_tab_expansion (const line_table_case &case_)
5145 /* Create a tempfile and write some text to it. This example uses a tabstop
5146 of 8, as the column numbers attempt to indicate:
5148 .....................000.01111111111.22222333333 display
5149 .....................123.90123456789.56789012345 columns */
5150 const char *content = " \t This: `\t' is a tab.\n";
5151 /* ....................000 00000011111 11111222222 byte
5152 ....................123 45678901234 56789012345 columns */
5154 const int tabstop = 8;
5155 const int first_non_ws_byte_col = 7;
5156 const int right_quote_byte_col = 15;
5157 const int last_byte_col = 25;
5158 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, tabstop));
5160 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5161 line_table_test ltt (case_);
5162 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5164 /* Don't attempt to run the tests if column data might be unavailable. */
5165 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5166 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5167 return;
5169 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5170 into 11 spaces. Recall that print_line() also puts one space before
5171 everything too. */
5173 test_diagnostic_context dc;
5174 dc.tabstop = tabstop;
5175 rich_location richloc (line_table,
5176 linemap_position_for_column (line_table,
5177 first_non_ws_byte_col));
5178 layout test_layout (&dc, &richloc, DK_ERROR);
5179 test_layout.print_line (1);
5180 ASSERT_STREQ (" This: ` ' is a tab.\n"
5181 " ^\n",
5182 pp_formatted_text (dc.printer));
5185 /* Confirm the display width was tracked correctly across the internal tab
5186 as well. */
5188 test_diagnostic_context dc;
5189 dc.tabstop = tabstop;
5190 rich_location richloc (line_table,
5191 linemap_position_for_column (line_table,
5192 right_quote_byte_col));
5193 layout test_layout (&dc, &richloc, DK_ERROR);
5194 test_layout.print_line (1);
5195 ASSERT_STREQ (" This: ` ' is a tab.\n"
5196 " ^\n",
5197 pp_formatted_text (dc.printer));
5201 /* Verify that line numbers are correctly printed for the case of
5202 a multiline range in which the width of the line numbers changes
5203 (e.g. from "9" to "10"). */
5205 static void
5206 test_line_numbers_multiline_range ()
5208 /* Create a tempfile and write some text to it. */
5209 pretty_printer pp;
5210 for (int i = 0; i < 20; i++)
5211 /* .........0000000001111111.
5212 .............1234567890123456. */
5213 pp_printf (&pp, "this is line %i\n", i + 1);
5214 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5215 line_table_test ltt;
5217 const line_map_ordinary *ord_map = linemap_check_ordinary
5218 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5219 linemap_line_start (line_table, 1, 100);
5221 /* Create a multi-line location, starting at the "line" of line 9, with
5222 a caret on the "is" of line 10, finishing on the "this" line 11. */
5224 location_t start
5225 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5226 location_t caret
5227 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5228 location_t finish
5229 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5230 location_t loc = make_location (caret, start, finish);
5232 test_diagnostic_context dc;
5233 dc.show_line_numbers_p = true;
5234 dc.min_margin_width = 0;
5235 gcc_rich_location richloc (loc);
5236 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5237 ASSERT_STREQ (" 9 | this is line 9\n"
5238 " | ~~~~~~\n"
5239 "10 | this is line 10\n"
5240 " | ~~~~~^~~~~~~~~~\n"
5241 "11 | this is line 11\n"
5242 " | ~~~~ \n",
5243 pp_formatted_text (dc.printer));
5246 /* Run all of the selftests within this file. */
5248 void
5249 diagnostic_show_locus_c_tests ()
5251 test_line_span ();
5253 test_layout_range_for_single_point ();
5254 test_layout_range_for_single_line ();
5255 test_layout_range_for_multiple_lines ();
5257 for_each_line_table_case (test_layout_x_offset_display_utf8);
5258 for_each_line_table_case (test_layout_x_offset_display_tab);
5260 test_get_line_bytes_without_trailing_whitespace ();
5262 test_diagnostic_show_locus_unknown_location ();
5264 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5265 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5266 for_each_line_table_case (test_add_location_if_nearby);
5267 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5268 for_each_line_table_case (test_fixit_consolidation);
5269 for_each_line_table_case (test_overlapped_fixit_printing);
5270 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5271 for_each_line_table_case (test_overlapped_fixit_printing_2);
5272 for_each_line_table_case (test_fixit_insert_containing_newline);
5273 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5274 for_each_line_table_case (test_fixit_replace_containing_newline);
5275 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5276 for_each_line_table_case (test_tab_expansion);
5278 test_line_numbers_multiline_range ();
5281 } // namespace selftest
5283 #endif /* #if CHECKING_P */
5285 #if __GNUC__ >= 10
5286 # pragma GCC diagnostic pop
5287 #endif