testsuite: Update scanning symbol sections to support AIX.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blobda3c5b6a92dd25dc6eb08c6f30b73057f07ca917
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2020 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 /* Expand each location towards the spelling location, and
909 recurse. */
910 const line_map_macro *macro_map = linemap_check_macro (map_a);
911 location_t loc_a_toward_spelling
912 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
913 macro_map,
914 loc_a);
915 location_t loc_b_toward_spelling
916 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
917 macro_map,
918 loc_b);
919 return compatible_locations_p (loc_a_toward_spelling,
920 loc_b_toward_spelling);
923 /* Otherwise they are within the same ordinary map. */
924 return true;
926 else
928 /* Within different maps. */
930 /* If either is within a macro expansion, they are incompatible. */
931 if (linemap_macro_expansion_map_p (map_a)
932 || linemap_macro_expansion_map_p (map_b))
933 return false;
935 /* Within two different ordinary maps; they are compatible iff they
936 are in the same file. */
937 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
938 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
939 return ord_map_a->to_file == ord_map_b->to_file;
943 /* Comparator for sorting fix-it hints. */
945 static int
946 fixit_cmp (const void *p_a, const void *p_b)
948 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
949 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
950 return hint_a->get_start_loc () - hint_b->get_start_loc ();
953 /* Implementation of class layout. */
955 /* Constructor for class layout.
957 Filter the ranges from the rich_location to those that we can
958 sanely print, populating m_layout_ranges and m_fixit_hints.
959 Determine the range of lines that we will print, splitting them
960 up into an ordered list of disjoint spans of contiguous line numbers.
961 Determine m_x_offset_display, to ensure that the primary caret
962 will fit within the max_width provided by the diagnostic_context. */
964 layout::layout (diagnostic_context * context,
965 rich_location *richloc,
966 diagnostic_t diagnostic_kind)
967 : m_context (context),
968 m_pp (context->printer),
969 m_primary_loc (richloc->get_range (0)->m_loc),
970 m_exploc (richloc->get_expanded_location (0), context->tabstop),
971 m_colorizer (context, diagnostic_kind),
972 m_colorize_source_p (context->colorize_source_p),
973 m_show_labels_p (context->show_labels_p),
974 m_show_line_numbers_p (context->show_line_numbers_p),
975 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
976 m_layout_ranges (richloc->get_num_locations ()),
977 m_fixit_hints (richloc->get_num_fixit_hints ()),
978 m_line_spans (1 + richloc->get_num_locations ()),
979 m_linenum_width (0),
980 m_x_offset_display (0)
982 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
984 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
985 Ignore any ranges that are awkward to handle. */
986 const location_range *loc_range = richloc->get_range (idx);
987 maybe_add_location_range (loc_range, idx, false);
990 /* Populate m_fixit_hints, filtering to only those that are in the
991 same file. */
992 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
994 const fixit_hint *hint = richloc->get_fixit_hint (i);
995 if (validate_fixit_hint_p (hint))
996 m_fixit_hints.safe_push (hint);
999 /* Sort m_fixit_hints. */
1000 m_fixit_hints.qsort (fixit_cmp);
1002 /* Populate the indicated members. */
1003 calculate_line_spans ();
1004 calculate_linenum_width ();
1005 calculate_x_offset_display ();
1007 if (context->show_ruler_p)
1008 show_ruler (m_x_offset_display + m_context->caret_max_width);
1012 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1013 those that we can sanely print.
1015 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1016 (for use as extrinsic state by label ranges FIXME).
1018 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1019 filtered against this layout instance's current line spans: it
1020 will only be added if the location is fully within the lines
1021 already specified by other locations.
1023 Return true iff LOC_RANGE was added. */
1025 bool
1026 layout::maybe_add_location_range (const location_range *loc_range,
1027 unsigned original_idx,
1028 bool restrict_to_current_line_spans)
1030 gcc_assert (loc_range);
1032 /* Split the "range" into caret and range information. */
1033 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1035 /* Expand the various locations. */
1036 expanded_location start
1037 = linemap_client_expand_location_to_spelling_point
1038 (src_range.m_start, LOCATION_ASPECT_START);
1039 expanded_location finish
1040 = linemap_client_expand_location_to_spelling_point
1041 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1042 expanded_location caret
1043 = linemap_client_expand_location_to_spelling_point
1044 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1046 /* If any part of the range isn't in the same file as the primary
1047 location of this diagnostic, ignore the range. */
1048 if (start.file != m_exploc.file)
1049 return false;
1050 if (finish.file != m_exploc.file)
1051 return false;
1052 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1053 if (caret.file != m_exploc.file)
1054 return false;
1056 /* Sanitize the caret location for non-primary ranges. */
1057 if (m_layout_ranges.length () > 0)
1058 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1059 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1060 /* Discard any non-primary ranges that can't be printed
1061 sanely relative to the primary location. */
1062 return false;
1064 /* Everything is now known to be in the correct source file,
1065 but it may require further sanitization. */
1066 layout_range ri (exploc_with_display_col (start, m_context->tabstop),
1067 exploc_with_display_col (finish, m_context->tabstop),
1068 loc_range->m_range_display_kind,
1069 exploc_with_display_col (caret, m_context->tabstop),
1070 original_idx, loc_range->m_label);
1072 /* If we have a range that finishes before it starts (perhaps
1073 from something built via macro expansion), printing the
1074 range is likely to be nonsensical. Also, attempting to do so
1075 breaks assumptions within the printing code (PR c/68473).
1076 Similarly, don't attempt to print ranges if one or both ends
1077 of the range aren't sane to print relative to the
1078 primary location (PR c++/70105). */
1079 if (start.line > finish.line
1080 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1081 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1083 /* Is this the primary location? */
1084 if (m_layout_ranges.length () == 0)
1086 /* We want to print the caret for the primary location, but
1087 we must sanitize away m_start and m_finish. */
1088 ri.m_start = ri.m_caret;
1089 ri.m_finish = ri.m_caret;
1091 else
1092 /* This is a non-primary range; ignore it. */
1093 return false;
1096 /* Potentially filter to just the lines already specified by other
1097 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1098 The layout ctor doesn't use it, and can't because m_line_spans
1099 hasn't been set up at that point. */
1100 if (restrict_to_current_line_spans)
1102 if (!will_show_line_p (start.line))
1103 return false;
1104 if (!will_show_line_p (finish.line))
1105 return false;
1106 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1107 if (!will_show_line_p (caret.line))
1108 return false;
1111 /* Passed all the tests; add the range to m_layout_ranges so that
1112 it will be printed. */
1113 m_layout_ranges.safe_push (ri);
1114 return true;
1117 /* Return true iff ROW is within one of the line spans for this layout. */
1119 bool
1120 layout::will_show_line_p (linenum_type row) const
1122 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1123 line_span_idx++)
1125 const line_span *line_span = get_line_span (line_span_idx);
1126 if (line_span->contains_line_p (row))
1127 return true;
1129 return false;
1132 /* Print a line showing a gap in the line numbers, for showing the boundary
1133 between two line spans. */
1135 void
1136 layout::print_gap_in_line_numbering ()
1138 gcc_assert (m_show_line_numbers_p);
1140 pp_emit_prefix (m_pp);
1142 for (int i = 0; i < m_linenum_width + 1; i++)
1143 pp_character (m_pp, '.');
1145 pp_newline (m_pp);
1148 /* Return true iff we should print a heading when starting the
1149 line span with the given index. */
1151 bool
1152 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1154 /* We print a heading for every change of line span, hence for every
1155 line span after the initial one. */
1156 if (line_span_idx > 0)
1157 return true;
1159 /* We also do it for the initial span if the primary location of the
1160 diagnostic is in a different span. */
1161 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1162 return true;
1164 return false;
1167 /* Get an expanded_location for the first location of interest within
1168 the given line_span.
1169 Used when printing a heading to indicate a new line span. */
1171 expanded_location
1172 layout::get_expanded_location (const line_span *line_span) const
1174 /* Whenever possible, use the caret location. */
1175 if (line_span->contains_line_p (m_exploc.line))
1176 return m_exploc;
1178 /* Otherwise, use the start of the first range that's present
1179 within the line_span. */
1180 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1182 const layout_range *lr = &m_layout_ranges[i];
1183 if (line_span->contains_line_p (lr->m_start.m_line))
1185 expanded_location exploc = m_exploc;
1186 exploc.line = lr->m_start.m_line;
1187 exploc.column = lr->m_start.m_columns[CU_BYTES];
1188 return exploc;
1192 /* Otherwise, use the location of the first fixit-hint present within
1193 the line_span. */
1194 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1196 const fixit_hint *hint = m_fixit_hints[i];
1197 location_t loc = hint->get_start_loc ();
1198 expanded_location exploc = expand_location (loc);
1199 if (line_span->contains_line_p (exploc.line))
1200 return exploc;
1203 /* It should not be possible to have a line span that didn't
1204 contain any of the layout_range or fixit_hint instances. */
1205 gcc_unreachable ();
1206 return m_exploc;
1209 /* Determine if HINT is meaningful to print within this layout. */
1211 bool
1212 layout::validate_fixit_hint_p (const fixit_hint *hint)
1214 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1215 return false;
1216 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1217 return false;
1219 return true;
1222 /* Determine the range of lines affected by HINT.
1223 This assumes that HINT has already been filtered by
1224 validate_fixit_hint_p, and so affects the correct source file. */
1226 static line_span
1227 get_line_span_for_fixit_hint (const fixit_hint *hint)
1229 gcc_assert (hint);
1231 int start_line = LOCATION_LINE (hint->get_start_loc ());
1233 /* For line-insertion fix-it hints, add the previous line to the
1234 span, to give the user more context on the proposed change. */
1235 if (hint->ends_with_newline_p ())
1236 if (start_line > 1)
1237 start_line--;
1239 return line_span (start_line,
1240 LOCATION_LINE (hint->get_next_loc ()));
1243 /* We want to print the pertinent source code at a diagnostic. The
1244 rich_location can contain multiple locations. This will have been
1245 filtered into m_exploc (the caret for the primary location) and
1246 m_layout_ranges, for those ranges within the same source file.
1248 We will print a subset of the lines within the source file in question,
1249 as a collection of "spans" of lines.
1251 This function populates m_line_spans with an ordered, disjoint list of
1252 the line spans of interest.
1254 Printing a gap between line spans takes one line, so, when printing
1255 line numbers, we allow a gap of up to one line between spans when
1256 merging, since it makes more sense to print the source line rather than a
1257 "gap-in-line-numbering" line. When not printing line numbers, it's
1258 better to be more explicit about what's going on, so keeping them as
1259 separate spans is preferred.
1261 For example, if the primary range is on lines 8-10, with secondary ranges
1262 covering lines 5-6 and lines 13-15:
1265 005 |RANGE 1
1266 006 |RANGE 1
1268 008 |PRIMARY RANGE
1269 009 |PRIMARY CARET
1270 010 |PRIMARY RANGE
1273 013 |RANGE 2
1274 014 |RANGE 2
1275 015 |RANGE 2
1278 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1280 With line numbering off (with span headers), we want three spans: lines 5-6,
1281 lines 8-10, and lines 13-15. */
1283 void
1284 layout::calculate_line_spans ()
1286 /* This should only be called once, by the ctor. */
1287 gcc_assert (m_line_spans.length () == 0);
1289 /* Populate tmp_spans with individual spans, for each of
1290 m_exploc, and for m_layout_ranges. */
1291 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1292 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1293 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1295 const layout_range *lr = &m_layout_ranges[i];
1296 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1297 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1298 lr->m_finish.m_line));
1301 /* Also add spans for any fix-it hints, in case they cover other lines. */
1302 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1304 const fixit_hint *hint = m_fixit_hints[i];
1305 gcc_assert (hint);
1306 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1309 /* Sort them. */
1310 tmp_spans.qsort(line_span::comparator);
1312 /* Now iterate through tmp_spans, copying into m_line_spans, and
1313 combining where possible. */
1314 gcc_assert (tmp_spans.length () > 0);
1315 m_line_spans.safe_push (tmp_spans[0]);
1316 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1318 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1319 const line_span *next = &tmp_spans[i];
1320 gcc_assert (next->m_first_line >= current->m_first_line);
1321 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1322 if ((linenum_arith_t)next->m_first_line
1323 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1325 /* We can merge them. */
1326 if (next->m_last_line > current->m_last_line)
1327 current->m_last_line = next->m_last_line;
1329 else
1331 /* No merger possible. */
1332 m_line_spans.safe_push (*next);
1336 /* Verify the result, in m_line_spans. */
1337 gcc_assert (m_line_spans.length () > 0);
1338 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1340 const line_span *prev = &m_line_spans[i - 1];
1341 const line_span *next = &m_line_spans[i];
1342 /* The individual spans must be sane. */
1343 gcc_assert (prev->m_first_line <= prev->m_last_line);
1344 gcc_assert (next->m_first_line <= next->m_last_line);
1345 /* The spans must be ordered. */
1346 gcc_assert (prev->m_first_line < next->m_first_line);
1347 /* There must be a gap of at least one line between separate spans. */
1348 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1352 /* Determine how many display columns need to be reserved for line numbers,
1353 based on the largest line number that will be needed, and populate
1354 m_linenum_width. */
1356 void
1357 layout::calculate_linenum_width ()
1359 gcc_assert (m_line_spans.length () > 0);
1360 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1361 int highest_line = last_span->m_last_line;
1362 if (highest_line < 0)
1363 highest_line = 0;
1364 m_linenum_width = num_digits (highest_line);
1365 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1366 if (m_line_spans.length () > 1)
1367 m_linenum_width = MAX (m_linenum_width, 3);
1368 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1369 after the line number. */
1370 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1373 /* Calculate m_x_offset_display, which improves readability in case the source
1374 line of interest is longer than the user's display. All lines output will be
1375 shifted to the left (so that their beginning is no longer displayed) by
1376 m_x_offset_display display columns, so that the caret is in a reasonable
1377 location. */
1379 void
1380 layout::calculate_x_offset_display ()
1382 m_x_offset_display = 0;
1384 const int max_width = m_context->caret_max_width;
1385 if (!max_width)
1387 /* Nothing to do, the width is not capped. */
1388 return;
1391 const char_span line = location_get_source_line (m_exploc.file,
1392 m_exploc.line);
1393 if (!line)
1395 /* Nothing to do, we couldn't find the source line. */
1396 return;
1398 int caret_display_column = m_exploc.m_display_col;
1399 const int line_bytes
1400 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1401 line.length ());
1402 int eol_display_column
1403 = cpp_display_width (line.get_buffer (), line_bytes, m_context->tabstop);
1404 if (caret_display_column > eol_display_column
1405 || !caret_display_column)
1407 /* This does not make sense, so don't try to do anything in this case. */
1408 return;
1411 /* Adjust caret and eol positions to include the left margin. If we are
1412 outputting line numbers, then the left margin is equal to m_linenum_width
1413 plus three for the " | " which follows it. Otherwise the left margin width
1414 is equal to 1, because layout::print_source_line() will prefix each line
1415 with a space. */
1416 const int source_display_cols = eol_display_column;
1417 int left_margin_size = 1;
1418 if (m_show_line_numbers_p)
1419 left_margin_size = m_linenum_width + 3;
1420 caret_display_column += left_margin_size;
1421 eol_display_column += left_margin_size;
1423 if (eol_display_column <= max_width)
1425 /* Nothing to do, everything fits in the display. */
1426 return;
1429 /* The line is too long for the display. Calculate an offset such that the
1430 caret is not too close to the right edge of the screen. It will be
1431 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1432 than that to the end of the source line anyway. */
1433 int right_margin_size = CARET_LINE_MARGIN;
1434 right_margin_size = MIN (eol_display_column - caret_display_column,
1435 right_margin_size);
1436 if (right_margin_size + left_margin_size >= max_width)
1438 /* The max_width is very small, so anything we try to do will not be very
1439 effective; just punt in this case and output with no offset. */
1440 return;
1442 const int max_caret_display_column = max_width - right_margin_size;
1443 if (caret_display_column > max_caret_display_column)
1445 m_x_offset_display = caret_display_column - max_caret_display_column;
1446 /* Make sure we don't offset the line into oblivion. */
1447 static const int min_cols_visible = 2;
1448 if (source_display_cols - m_x_offset_display < min_cols_visible)
1449 m_x_offset_display = 0;
1453 /* Print line ROW of source code, potentially colorized at any ranges, and
1454 return the line bounds. LINE is the source line (not necessarily
1455 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1456 colorization and tab expansion, this function tracks the line position in
1457 both byte and display column units. */
1459 line_bounds
1460 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1462 m_colorizer.set_normal_text ();
1464 pp_emit_prefix (m_pp);
1465 if (m_show_line_numbers_p)
1467 int width = num_digits (row);
1468 for (int i = 0; i < m_linenum_width - width; i++)
1469 pp_space (m_pp);
1470 pp_printf (m_pp, "%i | ", row);
1472 else
1473 pp_space (m_pp);
1475 /* We will stop printing the source line at any trailing whitespace. */
1476 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1477 line_bytes);
1479 /* This object helps to keep track of which display column we are at, which is
1480 necessary for computing the line bounds in display units, for doing
1481 tab expansion, and for implementing m_x_offset_display. */
1482 cpp_display_width_computation dw (line, line_bytes, m_context->tabstop);
1484 /* Skip the first m_x_offset_display display columns. In case the leading
1485 portion that will be skipped ends with a character with wcwidth > 1, then
1486 it is possible we skipped too much, so account for that by padding with
1487 spaces. Note that this does the right thing too in case a tab was the last
1488 character to be skipped over; the tab is effectively replaced by the
1489 correct number of trailing spaces needed to offset by the desired number of
1490 display columns. */
1491 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1492 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1493 pp_space (m_pp);
1495 /* Print the line and compute the line_bounds. */
1496 line_bounds lbounds;
1497 while (!dw.done ())
1499 /* Assuming colorization is enabled for the caret and underline
1500 characters, we may also colorize the associated characters
1501 within the source line.
1503 For frontends that generate range information, we color the
1504 associated characters in the source line the same as the
1505 carets and underlines in the annotation line, to make it easier
1506 for the reader to see the pertinent code.
1508 For frontends that only generate carets, we don't colorize the
1509 characters above them, since this would look strange (e.g.
1510 colorizing just the first character in a token). */
1511 if (m_colorize_source_p)
1513 bool in_range_p;
1514 point_state state;
1515 const int start_byte_col = dw.bytes_processed () + 1;
1516 in_range_p = get_state_at_point (row, start_byte_col,
1517 0, INT_MAX,
1518 CU_BYTES,
1519 &state);
1520 if (in_range_p)
1521 m_colorizer.set_range (state.range_idx);
1522 else
1523 m_colorizer.set_normal_text ();
1526 /* Get the display width of the next character to be output, expanding
1527 tabs and replacing some control bytes with spaces as necessary. */
1528 const char *c = dw.next_byte ();
1529 const int start_disp_col = dw.display_cols_processed () + 1;
1530 const int this_display_width = dw.process_next_codepoint ();
1531 if (*c == '\t')
1533 /* The returned display width is the number of spaces into which the
1534 tab should be expanded. */
1535 for (int i = 0; i != this_display_width; ++i)
1536 pp_space (m_pp);
1537 continue;
1539 if (*c == '\0' || *c == '\r')
1541 /* cpp_wcwidth() promises to return 1 for all control bytes, and we
1542 want to output these as a single space too, so this case is
1543 actually the same as the '\t' case. */
1544 gcc_assert (this_display_width == 1);
1545 pp_space (m_pp);
1546 continue;
1549 /* We have a (possibly multibyte) character to output; update the line
1550 bounds if it is not whitespace. */
1551 if (*c != ' ')
1553 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1554 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1555 lbounds.m_first_non_ws_disp_col = start_disp_col;
1558 /* Output the character. */
1559 while (c != dw.next_byte ()) pp_character (m_pp, *c++);
1561 print_newline ();
1562 return lbounds;
1565 /* Determine if we should print an annotation line for ROW.
1566 i.e. if any of m_layout_ranges contains ROW. */
1568 bool
1569 layout::should_print_annotation_line_p (linenum_type row) const
1571 layout_range *range;
1572 int i;
1573 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1575 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1576 return false;
1577 if (range->intersects_line_p (row))
1578 return true;
1580 return false;
1583 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1584 margin, which is empty for annotation lines. Otherwise, do nothing. */
1586 void
1587 layout::start_annotation_line (char margin_char) const
1589 pp_emit_prefix (m_pp);
1590 if (m_show_line_numbers_p)
1592 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1593 of it, right-aligned, padded with spaces. */
1594 int i;
1595 for (i = 0; i < m_linenum_width - 3; i++)
1596 pp_space (m_pp);
1597 for (; i < m_linenum_width; i++)
1598 pp_character (m_pp, margin_char);
1599 pp_string (m_pp, " |");
1603 /* Print a line consisting of the caret/underlines for the given
1604 source line. */
1606 void
1607 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1609 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1610 lbounds.m_last_non_ws_disp_col);
1612 start_annotation_line ();
1613 pp_space (m_pp);
1615 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1617 bool in_range_p;
1618 point_state state;
1619 in_range_p = get_state_at_point (row, column,
1620 lbounds.m_first_non_ws_disp_col,
1621 lbounds.m_last_non_ws_disp_col,
1622 CU_DISPLAY_COLS,
1623 &state);
1624 if (in_range_p)
1626 /* Within a range. Draw either the caret or an underline. */
1627 m_colorizer.set_range (state.range_idx);
1628 if (state.draw_caret_p)
1630 /* Draw the caret. */
1631 char caret_char;
1632 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1633 caret_char = m_context->caret_chars[state.range_idx];
1634 else
1635 caret_char = '^';
1636 pp_character (m_pp, caret_char);
1638 else
1639 pp_character (m_pp, '~');
1641 else
1643 /* Not in a range. */
1644 m_colorizer.set_normal_text ();
1645 pp_character (m_pp, ' ');
1648 print_newline ();
1651 /* Implementation detail of layout::print_any_labels.
1653 A label within the given row of source. */
1655 class line_label
1657 public:
1658 line_label (diagnostic_context *context, int state_idx, int column,
1659 label_text text)
1660 : m_state_idx (state_idx), m_column (column),
1661 m_text (text), m_label_line (0), m_has_vbar (true)
1663 const int bytes = strlen (text.m_buffer);
1664 m_display_width
1665 = cpp_display_width (text.m_buffer, bytes, context->tabstop);
1668 /* Sorting is primarily by column, then by state index. */
1669 static int comparator (const void *p1, const void *p2)
1671 const line_label *ll1 = (const line_label *)p1;
1672 const line_label *ll2 = (const line_label *)p2;
1673 int column_cmp = compare (ll1->m_column, ll2->m_column);
1674 if (column_cmp)
1675 return column_cmp;
1676 /* Order by reverse state index, so that labels are printed
1677 in order of insertion into the rich_location when the
1678 sorted list is walked backwards. */
1679 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1682 int m_state_idx;
1683 int m_column;
1684 label_text m_text;
1685 size_t m_display_width;
1686 int m_label_line;
1687 bool m_has_vbar;
1690 /* Print any labels in this row. */
1691 void
1692 layout::print_any_labels (linenum_type row)
1694 int i;
1695 auto_vec<line_label> labels;
1697 /* Gather the labels that are to be printed into "labels". */
1699 layout_range *range;
1700 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1702 /* Most ranges don't have labels, so reject this first. */
1703 if (range->m_label == NULL)
1704 continue;
1706 /* The range's caret must be on this line. */
1707 if (range->m_caret.m_line != row)
1708 continue;
1710 /* Reject labels that aren't fully visible due to clipping
1711 by m_x_offset_display. */
1712 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1713 if (disp_col <= m_x_offset_display)
1714 continue;
1716 label_text text;
1717 text = range->m_label->get_text (range->m_original_idx);
1719 /* Allow for labels that return NULL from their get_text
1720 implementation (so e.g. such labels can control their own
1721 visibility). */
1722 if (text.m_buffer == NULL)
1723 continue;
1725 labels.safe_push (line_label (m_context, i, disp_col, text));
1729 /* Bail out if there are no labels on this row. */
1730 if (labels.length () == 0)
1731 return;
1733 /* Sort them. */
1734 labels.qsort(line_label::comparator);
1736 /* Figure out how many "label lines" we need, and which
1737 one each label is printed in.
1739 For example, if the labels aren't too densely packed,
1740 we can fit them on the same line, giving two "label lines":
1742 foo + bar
1743 ~~~ ~~~
1744 | | : label line 0
1745 l0 l1 : label line 1
1747 If they would touch each other or overlap, then we need
1748 additional "label lines":
1750 foo + bar
1751 ~~~ ~~~
1752 | | : label line 0
1753 | label 1 : label line 1
1754 label 0 : label line 2
1756 Place the final label on label line 1, and work backwards, adding
1757 label lines as needed.
1759 If multiple labels are at the same place, put them on separate
1760 label lines:
1762 foo + bar
1763 ^ : label line 0
1764 | : label line 1
1765 label 0 : label line 2
1766 label 1 : label line 3. */
1768 int max_label_line = 1;
1770 int next_column = INT_MAX;
1771 line_label *label;
1772 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1774 /* Would this label "touch" or overlap the next label? */
1775 if (label->m_column + label->m_display_width >= (size_t)next_column)
1777 max_label_line++;
1779 /* If we've already seen labels with the same column, suppress the
1780 vertical bar for subsequent ones in this backwards iteration;
1781 hence only the one with the highest label_line has m_has_vbar set. */
1782 if (label->m_column == next_column)
1783 label->m_has_vbar = false;
1786 label->m_label_line = max_label_line;
1787 next_column = label->m_column;
1791 /* Print the "label lines". For each label within the line, print
1792 either a vertical bar ('|') for the labels that are lower down, or the
1793 labels themselves once we've reached their line. */
1795 for (int label_line = 0; label_line <= max_label_line; label_line++)
1797 start_annotation_line ();
1798 pp_space (m_pp);
1799 int column = 1 + m_x_offset_display;
1800 line_label *label;
1801 FOR_EACH_VEC_ELT (labels, i, label)
1803 if (label_line > label->m_label_line)
1804 /* We've printed all the labels for this label line. */
1805 break;
1807 if (label_line == label->m_label_line)
1809 gcc_assert (column <= label->m_column);
1810 move_to_column (&column, label->m_column, true);
1811 /* Colorize the text, unless it's for events in a
1812 diagnostic_path. */
1813 if (!m_diagnostic_path_p)
1814 m_colorizer.set_range (label->m_state_idx);
1815 pp_string (m_pp, label->m_text.m_buffer);
1816 m_colorizer.set_normal_text ();
1817 column += label->m_display_width;
1819 else if (label->m_has_vbar)
1821 gcc_assert (column <= label->m_column);
1822 move_to_column (&column, label->m_column, true);
1823 m_colorizer.set_range (label->m_state_idx);
1824 pp_character (m_pp, '|');
1825 m_colorizer.set_normal_text ();
1826 column++;
1829 print_newline ();
1833 /* Clean up. */
1835 line_label *label;
1836 FOR_EACH_VEC_ELT (labels, i, label)
1837 label->m_text.maybe_free ();
1841 /* If there are any fixit hints inserting new lines before source line ROW,
1842 print them.
1844 They are printed on lines of their own, before the source line
1845 itself, with a leading '+'. */
1847 void
1848 layout::print_leading_fixits (linenum_type row)
1850 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1852 const fixit_hint *hint = m_fixit_hints[i];
1854 if (!hint->ends_with_newline_p ())
1855 /* Not a newline fixit; print it in print_trailing_fixits. */
1856 continue;
1858 gcc_assert (hint->insertion_p ());
1860 if (hint->affects_line_p (m_exploc.file, row))
1862 /* Printing the '+' with normal colorization
1863 and the inserted line with "insert" colorization
1864 helps them stand out from each other, and from
1865 the surrounding text. */
1866 m_colorizer.set_normal_text ();
1867 start_annotation_line ('+');
1868 pp_character (m_pp, '+');
1869 m_colorizer.set_fixit_insert ();
1870 /* Print all but the trailing newline of the fix-it hint.
1871 We have to print the newline separately to avoid
1872 getting additional pp prefixes printed. */
1873 for (size_t i = 0; i < hint->get_length () - 1; i++)
1874 pp_character (m_pp, hint->get_string ()[i]);
1875 m_colorizer.set_normal_text ();
1876 pp_newline (m_pp);
1881 /* Subroutine of layout::print_trailing_fixits.
1883 Determine if the annotation line printed for LINE contained
1884 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
1886 bool
1887 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1888 int finish_column) const
1890 layout_range *range;
1891 int i;
1892 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1893 if (range->m_start.m_line == line
1894 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
1895 && range->m_finish.m_line == line
1896 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
1897 return true;
1898 return false;
1901 /* Classes for printing trailing fix-it hints i.e. those that
1902 don't add new lines.
1904 For insertion, these can look like:
1906 new_text
1908 For replacement, these can look like:
1910 ------------- : underline showing affected range
1911 new_text
1913 For deletion, these can look like:
1915 ------------- : underline showing affected range
1917 This can become confusing if they overlap, and so we need
1918 to do some preprocessing to decide what to print.
1919 We use the list of fixit_hint instances affecting the line
1920 to build a list of "correction" instances, and print the
1921 latter.
1923 For example, consider a set of fix-its for converting
1924 a C-style cast to a C++ const_cast.
1926 Given:
1928 ..000000000111111111122222222223333333333.
1929 ..123456789012345678901234567890123456789.
1930 foo *f = (foo *)ptr->field;
1931 ^~~~~
1933 and the fix-it hints:
1934 - replace col 10 (the open paren) with "const_cast<"
1935 - replace col 16 (the close paren) with "> ("
1936 - insert ")" before col 27
1938 then we would get odd-looking output:
1940 foo *f = (foo *)ptr->field;
1941 ^~~~~
1943 const_cast<
1945 > ( )
1947 It would be better to detect when fixit hints are going to
1948 overlap (those that require new lines), and to consolidate
1949 the printing of such fixits, giving something like:
1951 foo *f = (foo *)ptr->field;
1952 ^~~~~
1953 -----------------
1954 const_cast<foo *> (ptr->field)
1956 This works by detecting when the printing would overlap, and
1957 effectively injecting no-op replace hints into the gaps between
1958 such fix-its, so that the printing joins up.
1960 In the above example, the overlap of:
1961 - replace col 10 (the open paren) with "const_cast<"
1962 and:
1963 - replace col 16 (the close paren) with "> ("
1964 is fixed by injecting a no-op:
1965 - replace cols 11-15 with themselves ("foo *")
1966 and consolidating these, making:
1967 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1968 i.e.:
1969 - replace cols 10-16 with "const_cast<foo *> ("
1971 This overlaps with the final fix-it hint:
1972 - insert ")" before col 27
1973 and so we repeat the consolidation process, by injecting
1974 a no-op:
1975 - replace cols 17-26 with themselves ("ptr->field")
1976 giving:
1977 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1978 i.e.:
1979 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1981 and is thus printed as desired. */
1983 /* A range of (byte or display) columns within a line. */
1985 class column_range
1987 public:
1988 column_range (int start_, int finish_) : start (start_), finish (finish_)
1990 /* We must have either a range, or an insertion. */
1991 gcc_assert (start <= finish || finish == start - 1);
1994 bool operator== (const column_range &other) const
1996 return start == other.start && finish == other.finish;
1999 int start;
2000 int finish;
2003 /* Get the range of bytes or display columns that HINT would affect. */
2004 static column_range
2005 get_affected_range (diagnostic_context *context,
2006 const fixit_hint *hint, enum column_unit col_unit)
2008 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2009 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2010 --exploc_finish.column;
2012 int start_column;
2013 int finish_column;
2014 if (col_unit == CU_DISPLAY_COLS)
2016 start_column
2017 = location_compute_display_column (exploc_start, context->tabstop);
2018 if (hint->insertion_p ())
2019 finish_column = start_column - 1;
2020 else
2021 finish_column
2022 = location_compute_display_column (exploc_finish, context->tabstop);
2024 else
2026 start_column = exploc_start.column;
2027 finish_column = exploc_finish.column;
2029 return column_range (start_column, finish_column);
2032 /* Get the range of display columns that would be printed for HINT. */
2034 static column_range
2035 get_printed_columns (diagnostic_context *context, const fixit_hint *hint)
2037 expanded_location exploc = expand_location (hint->get_start_loc ());
2038 int start_column = location_compute_display_column (exploc, context->tabstop);
2039 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2040 context->tabstop);
2041 int final_hint_column = start_column + hint_width - 1;
2042 if (hint->insertion_p ())
2044 return column_range (start_column, final_hint_column);
2046 else
2048 exploc = expand_location (hint->get_next_loc ());
2049 --exploc.column;
2050 int finish_column
2051 = location_compute_display_column (exploc, context->tabstop);
2052 return column_range (start_column,
2053 MAX (finish_column, final_hint_column));
2057 /* A correction on a particular line.
2058 This describes a plan for how to print one or more fixit_hint
2059 instances that affected the line, potentially consolidating hints
2060 into corrections to make the result easier for the user to read. */
2062 class correction
2064 public:
2065 correction (column_range affected_bytes,
2066 column_range affected_columns,
2067 column_range printed_columns,
2068 const char *new_text, size_t new_text_len,
2069 int tabstop)
2070 : m_affected_bytes (affected_bytes),
2071 m_affected_columns (affected_columns),
2072 m_printed_columns (printed_columns),
2073 m_text (xstrdup (new_text)),
2074 m_byte_length (new_text_len),
2075 m_tabstop (tabstop),
2076 m_alloc_sz (new_text_len + 1)
2078 compute_display_cols ();
2081 ~correction () { free (m_text); }
2083 bool insertion_p () const
2085 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2088 void ensure_capacity (size_t len);
2089 void ensure_terminated ();
2091 void compute_display_cols ()
2093 m_display_cols = cpp_display_width (m_text, m_byte_length, m_tabstop);
2096 void overwrite (int dst_offset, const char_span &src_span)
2098 gcc_assert (dst_offset >= 0);
2099 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2100 memcpy (m_text + dst_offset, src_span.get_buffer (),
2101 src_span.length ());
2104 /* If insert, then start: the column before which the text
2105 is to be inserted, and finish is offset by the length of
2106 the replacement.
2107 If replace, then the range of columns affected. */
2108 column_range m_affected_bytes;
2109 column_range m_affected_columns;
2111 /* If insert, then start: the column before which the text
2112 is to be inserted, and finish is offset by the length of
2113 the replacement.
2114 If replace, then the range of columns affected. */
2115 column_range m_printed_columns;
2117 /* The text to be inserted/used as replacement. */
2118 char *m_text;
2119 size_t m_byte_length; /* Not including null-terminator. */
2120 int m_display_cols;
2121 int m_tabstop;
2122 size_t m_alloc_sz;
2125 /* Ensure that m_text can hold a string of length LEN
2126 (plus 1 for 0-termination). */
2128 void
2129 correction::ensure_capacity (size_t len)
2131 /* Allow 1 extra byte for 0-termination. */
2132 if (m_alloc_sz < (len + 1))
2134 size_t new_alloc_sz = (len + 1) * 2;
2135 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2136 m_alloc_sz = new_alloc_sz;
2140 /* Ensure that m_text is 0-terminated. */
2142 void
2143 correction::ensure_terminated ()
2145 /* 0-terminate the buffer. */
2146 gcc_assert (m_byte_length < m_alloc_sz);
2147 m_text[m_byte_length] = '\0';
2150 /* A list of corrections affecting a particular line.
2151 This is used by layout::print_trailing_fixits for planning
2152 how to print the fix-it hints affecting the line. */
2154 class line_corrections
2156 public:
2157 line_corrections (diagnostic_context *context, const char *filename,
2158 linenum_type row)
2159 : m_context (context), m_filename (filename), m_row (row)
2161 ~line_corrections ();
2163 void add_hint (const fixit_hint *hint);
2165 diagnostic_context *m_context;
2166 const char *m_filename;
2167 linenum_type m_row;
2168 auto_vec <correction *> m_corrections;
2171 /* struct line_corrections. */
2173 line_corrections::~line_corrections ()
2175 unsigned i;
2176 correction *c;
2177 FOR_EACH_VEC_ELT (m_corrections, i, c)
2178 delete c;
2181 /* A struct wrapping a particular source line, allowing
2182 run-time bounds-checking of accesses in a checked build. */
2184 class source_line
2186 public:
2187 source_line (const char *filename, int line);
2189 char_span as_span () { return char_span (chars, width); }
2191 const char *chars;
2192 int width;
2195 /* source_line's ctor. */
2197 source_line::source_line (const char *filename, int line)
2199 char_span span = location_get_source_line (filename, line);
2200 chars = span.get_buffer ();
2201 width = span.length ();
2204 /* Add HINT to the corrections for this line.
2205 Attempt to consolidate nearby hints so that they will not
2206 overlap with printed. */
2208 void
2209 line_corrections::add_hint (const fixit_hint *hint)
2211 column_range affected_bytes = get_affected_range (m_context, hint, CU_BYTES);
2212 column_range affected_columns = get_affected_range (m_context, hint,
2213 CU_DISPLAY_COLS);
2214 column_range printed_columns = get_printed_columns (m_context, hint);
2216 /* Potentially consolidate. */
2217 if (!m_corrections.is_empty ())
2219 correction *last_correction
2220 = m_corrections[m_corrections.length () - 1];
2222 /* The following consolidation code assumes that the fix-it hints
2223 have been sorted by start (done within layout's ctor). */
2224 gcc_assert (affected_bytes.start
2225 >= last_correction->m_affected_bytes.start);
2226 gcc_assert (printed_columns.start
2227 >= last_correction->m_printed_columns.start);
2229 if (printed_columns.start <= last_correction->m_printed_columns.finish)
2231 /* We have two hints for which the printed forms of the hints
2232 would touch or overlap, so we need to consolidate them to avoid
2233 confusing the user.
2234 Attempt to inject a "replace" correction from immediately
2235 after the end of the last hint to immediately before the start
2236 of the next hint. */
2237 column_range between (last_correction->m_affected_bytes.finish + 1,
2238 affected_bytes.start - 1);
2240 /* Try to read the source. */
2241 source_line line (m_filename, m_row);
2242 if (line.chars && between.finish < line.width)
2244 /* Consolidate into the last correction:
2245 add a no-op "replace" of the "between" text, and
2246 add the text from the new hint. */
2247 int old_byte_len = last_correction->m_byte_length;
2248 gcc_assert (old_byte_len >= 0);
2249 int between_byte_len = between.finish + 1 - between.start;
2250 gcc_assert (between_byte_len >= 0);
2251 int new_byte_len
2252 = old_byte_len + between_byte_len + hint->get_length ();
2253 gcc_assert (new_byte_len >= 0);
2254 last_correction->ensure_capacity (new_byte_len);
2255 last_correction->overwrite
2256 (old_byte_len,
2257 line.as_span ().subspan (between.start - 1,
2258 between.finish + 1 - between.start));
2259 last_correction->overwrite (old_byte_len + between_byte_len,
2260 char_span (hint->get_string (),
2261 hint->get_length ()));
2262 last_correction->m_byte_length = new_byte_len;
2263 last_correction->ensure_terminated ();
2264 last_correction->m_affected_bytes.finish
2265 = affected_bytes.finish;
2266 last_correction->m_affected_columns.finish
2267 = affected_columns.finish;
2268 int prev_display_cols = last_correction->m_display_cols;
2269 last_correction->compute_display_cols ();
2270 last_correction->m_printed_columns.finish
2271 += last_correction->m_display_cols - prev_display_cols;
2272 return;
2277 /* If no consolidation happened, add a new correction instance. */
2278 m_corrections.safe_push (new correction (affected_bytes,
2279 affected_columns,
2280 printed_columns,
2281 hint->get_string (),
2282 hint->get_length (),
2283 m_context->tabstop));
2286 /* If there are any fixit hints on source line ROW, print them.
2287 They are printed in order, attempting to combine them onto lines, but
2288 starting new lines if necessary.
2289 Fix-it hints that insert new lines are handled separately,
2290 in layout::print_leading_fixits. */
2292 void
2293 layout::print_trailing_fixits (linenum_type row)
2295 /* Build a list of correction instances for the line,
2296 potentially consolidating hints (for the sake of readability). */
2297 line_corrections corrections (m_context, m_exploc.file, row);
2298 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2300 const fixit_hint *hint = m_fixit_hints[i];
2302 /* Newline fixits are handled by layout::print_leading_fixits. */
2303 if (hint->ends_with_newline_p ())
2304 continue;
2306 if (hint->affects_line_p (m_exploc.file, row))
2307 corrections.add_hint (hint);
2310 /* Now print the corrections. */
2311 unsigned i;
2312 correction *c;
2313 int column = m_x_offset_display;
2315 if (!corrections.m_corrections.is_empty ())
2316 start_annotation_line ();
2318 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2320 /* For now we assume each fixit hint can only touch one line. */
2321 if (c->insertion_p ())
2323 /* This assumes the insertion just affects one line. */
2324 int start_column = c->m_printed_columns.start;
2325 move_to_column (&column, start_column, true);
2326 m_colorizer.set_fixit_insert ();
2327 pp_string (m_pp, c->m_text);
2328 m_colorizer.set_normal_text ();
2329 column += c->m_display_cols;
2331 else
2333 /* If the range of the replacement wasn't printed in the
2334 annotation line, then print an extra underline to
2335 indicate exactly what is being replaced.
2336 Always show it for removals. */
2337 int start_column = c->m_affected_columns.start;
2338 int finish_column = c->m_affected_columns.finish;
2339 if (!annotation_line_showed_range_p (row, start_column,
2340 finish_column)
2341 || c->m_byte_length == 0)
2343 move_to_column (&column, start_column, true);
2344 m_colorizer.set_fixit_delete ();
2345 for (; column <= finish_column; column++)
2346 pp_character (m_pp, '-');
2347 m_colorizer.set_normal_text ();
2349 /* Print the replacement text. REPLACE also covers
2350 removals, so only do this extra work (potentially starting
2351 a new line) if we have actual replacement text. */
2352 if (c->m_byte_length > 0)
2354 move_to_column (&column, start_column, true);
2355 m_colorizer.set_fixit_insert ();
2356 pp_string (m_pp, c->m_text);
2357 m_colorizer.set_normal_text ();
2358 column += c->m_display_cols;
2363 /* Add a trailing newline, if necessary. */
2364 move_to_column (&column, 0, false);
2367 /* Disable any colorization and emit a newline. */
2369 void
2370 layout::print_newline ()
2372 m_colorizer.set_normal_text ();
2373 pp_newline (m_pp);
2376 /* Return true if (ROW/COLUMN) is within a range of the layout.
2377 If it returns true, OUT_STATE is written to, with the
2378 range index, and whether we should draw the caret at
2379 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2380 whether all inputs and outputs are in bytes or display column units. */
2382 bool
2383 layout::get_state_at_point (/* Inputs. */
2384 linenum_type row, int column,
2385 int first_non_ws, int last_non_ws,
2386 enum column_unit col_unit,
2387 /* Outputs. */
2388 point_state *out_state)
2390 layout_range *range;
2391 int i;
2392 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2394 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2395 /* Bail out early, so that such ranges don't affect underlining or
2396 source colorization. */
2397 continue;
2399 if (range->contains_point (row, column, col_unit))
2401 out_state->range_idx = i;
2403 /* Are we at the range's caret? is it visible? */
2404 out_state->draw_caret_p = false;
2405 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2406 && row == range->m_caret.m_line
2407 && column == range->m_caret.m_columns[col_unit])
2408 out_state->draw_caret_p = true;
2410 /* Within a multiline range, don't display any underline
2411 in any leading or trailing whitespace on a line.
2412 We do display carets, however. */
2413 if (!out_state->draw_caret_p)
2414 if (column < first_non_ws || column > last_non_ws)
2415 return false;
2417 /* We are within a range. */
2418 return true;
2422 return false;
2425 /* Helper function for use by layout::print_line when printing the
2426 annotation line under the source line.
2427 Get the display column beyond the rightmost one that could contain a caret
2428 or range marker, given that we stop rendering at trailing whitespace.
2429 ROW is the source line within the given file.
2430 CARET_COLUMN is the display column of range 0's caret.
2431 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2432 character of source (as determined when printing the source line). */
2435 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2436 int last_non_ws_column)
2438 int result = caret_column + 1;
2440 layout_range *range;
2441 int i;
2442 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2444 if (row >= range->m_start.m_line)
2446 if (range->m_finish.m_line == row)
2448 /* On the final line within a range; ensure that
2449 we render up to the end of the range. */
2450 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2451 if (result <= disp_col)
2452 result = disp_col + 1;
2454 else if (row < range->m_finish.m_line)
2456 /* Within a multiline range; ensure that we render up to the
2457 last non-whitespace column. */
2458 if (result <= last_non_ws_column)
2459 result = last_non_ws_column + 1;
2464 return result;
2467 /* Given *COLUMN as an x-coordinate, print spaces to position
2468 successive output at DEST_COLUMN, printing a newline if necessary,
2469 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2470 left margin after any newline. */
2472 void
2473 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2475 /* Start a new line if we need to. */
2476 if (*column > dest_column)
2478 print_newline ();
2479 if (add_left_margin)
2480 start_annotation_line ();
2481 *column = m_x_offset_display;
2484 while (*column < dest_column)
2486 pp_space (m_pp);
2487 (*column)++;
2491 /* For debugging layout issues, render a ruler giving column numbers
2492 (after the 1-column indent). */
2494 void
2495 layout::show_ruler (int max_column) const
2497 /* Hundreds. */
2498 if (max_column > 99)
2500 start_annotation_line ();
2501 pp_space (m_pp);
2502 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2503 if (column % 10 == 0)
2504 pp_character (m_pp, '0' + (column / 100) % 10);
2505 else
2506 pp_space (m_pp);
2507 pp_newline (m_pp);
2510 /* Tens. */
2511 start_annotation_line ();
2512 pp_space (m_pp);
2513 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2514 if (column % 10 == 0)
2515 pp_character (m_pp, '0' + (column / 10) % 10);
2516 else
2517 pp_space (m_pp);
2518 pp_newline (m_pp);
2520 /* Units. */
2521 start_annotation_line ();
2522 pp_space (m_pp);
2523 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2524 pp_character (m_pp, '0' + (column % 10));
2525 pp_newline (m_pp);
2528 /* Print leading fix-its (for new lines inserted before the source line)
2529 then the source line, followed by an annotation line
2530 consisting of any caret/underlines, then any fixits.
2531 If the source line can't be read, print nothing. */
2532 void
2533 layout::print_line (linenum_type row)
2535 char_span line = location_get_source_line (m_exploc.file, row);
2536 if (!line)
2537 return;
2539 print_leading_fixits (row);
2540 const line_bounds lbounds
2541 = print_source_line (row, line.get_buffer (), line.length ());
2542 if (should_print_annotation_line_p (row))
2543 print_annotation_line (row, lbounds);
2544 if (m_show_labels_p)
2545 print_any_labels (row);
2546 print_trailing_fixits (row);
2549 } /* End of anonymous namespace. */
2551 /* If LOC is within the spans of lines that will already be printed for
2552 this gcc_rich_location, then add it as a secondary location and return true.
2554 Otherwise return false. */
2556 bool
2557 gcc_rich_location::add_location_if_nearby (location_t loc,
2558 bool restrict_to_current_line_spans,
2559 const range_label *label)
2561 /* Use the layout location-handling logic to sanitize LOC,
2562 filtering it to the current line spans within a temporary
2563 layout instance. */
2564 layout layout (global_dc, this, DK_ERROR);
2565 location_range loc_range;
2566 loc_range.m_loc = loc;
2567 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2568 if (!layout.maybe_add_location_range (&loc_range, 0,
2569 restrict_to_current_line_spans))
2570 return false;
2572 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2573 return true;
2576 /* Print the physical source code corresponding to the location of
2577 this diagnostic, with additional annotations. */
2579 void
2580 diagnostic_show_locus (diagnostic_context * context,
2581 rich_location *richloc,
2582 diagnostic_t diagnostic_kind)
2584 location_t loc = richloc->get_loc ();
2585 /* Do nothing if source-printing has been disabled. */
2586 if (!context->show_caret)
2587 return;
2589 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2590 if (loc <= BUILTINS_LOCATION)
2591 return;
2593 /* Don't print the same source location twice in a row, unless we have
2594 fix-it hints. */
2595 if (loc == context->last_location
2596 && richloc->get_num_fixit_hints () == 0)
2597 return;
2599 context->last_location = loc;
2601 layout layout (context, richloc, diagnostic_kind);
2602 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2603 line_span_idx++)
2605 const line_span *line_span = layout.get_line_span (line_span_idx);
2606 if (context->show_line_numbers_p)
2608 /* With line numbers, we should show whenever the line-numbering
2609 "jumps". */
2610 if (line_span_idx > 0)
2611 layout.print_gap_in_line_numbering ();
2613 else
2615 /* Without line numbers, we print headings for some line spans. */
2616 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2618 expanded_location exploc
2619 = layout.get_expanded_location (line_span);
2620 context->start_span (context, exploc);
2623 /* Iterate over the lines within this span (using linenum_arith_t to
2624 avoid overflow with 0xffffffff causing an infinite loop). */
2625 linenum_arith_t last_line = line_span->get_last_line ();
2626 for (linenum_arith_t row = line_span->get_first_line ();
2627 row <= last_line; row++)
2628 layout.print_line (row);
2632 #if CHECKING_P
2634 namespace selftest {
2636 /* Selftests for diagnostic_show_locus. */
2638 /* For precise tests of the layout, make clear where the source line will
2639 start. test_left_margin sets the total byte count from the left side of the
2640 screen to the start of source lines, after the line number and the separator,
2641 which consists of the three characters " | ". */
2642 static const int test_linenum_sep = 3;
2643 static const int test_left_margin = 7;
2645 /* Helper function for test_layout_x_offset_display_utf8(). */
2646 static void
2647 test_offset_impl (int caret_byte_col, int max_width,
2648 int expected_x_offset_display,
2649 int left_margin = test_left_margin)
2651 test_diagnostic_context dc;
2652 dc.caret_max_width = max_width;
2653 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2654 the line number plus one space after. */
2655 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2656 dc.show_line_numbers_p = true;
2657 rich_location richloc (line_table,
2658 linemap_position_for_column (line_table,
2659 caret_byte_col));
2660 layout test_layout (&dc, &richloc, DK_ERROR);
2661 ASSERT_EQ (left_margin - test_linenum_sep,
2662 test_layout.get_linenum_width ());
2663 ASSERT_EQ (expected_x_offset_display,
2664 test_layout.get_x_offset_display ());
2667 /* Test that layout::calculate_x_offset_display() works. */
2668 static void
2669 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2672 const char *content
2673 = "This line is very long, so that we can use it to test the logic for "
2674 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2675 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2676 "column #102.\n";
2678 /* Number of bytes in the line, subtracting one to remove the newline. */
2679 const int line_bytes = strlen (content) - 1;
2681 /* Number of display columns occupied by the line; each of the 2 emojis
2682 takes up 2 fewer display columns than it does bytes. */
2683 const int line_display_cols = line_bytes - 2*2;
2685 /* The column of the first emoji. Byte or display is the same as there are
2686 no multibyte characters earlier on the line. */
2687 const int emoji_col = 102;
2689 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2690 line_table_test ltt (case_);
2692 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2694 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2696 /* Don't attempt to run the tests if column data might be unavailable. */
2697 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2698 return;
2700 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2701 ASSERT_EQ (1, LOCATION_LINE (line_end));
2702 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
2704 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2705 ASSERT_EQ (line_display_cols,
2706 cpp_display_width (lspan.get_buffer (), lspan.length (),
2707 def_tabstop));
2708 ASSERT_EQ (line_display_cols,
2709 location_compute_display_column (expand_location (line_end),
2710 def_tabstop));
2711 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
2712 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2714 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2716 /* No constraint on the width -> no offset. */
2717 test_offset_impl (emoji_col, 0, 0);
2719 /* Caret is before the beginning -> no offset. */
2720 test_offset_impl (0, 100, 0);
2722 /* Caret is past the end of the line -> no offset. */
2723 test_offset_impl (line_bytes+1, 100, 0);
2725 /* Line fits in the display -> no offset. */
2726 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
2727 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
2729 /* Line is too long for the display but caret location is OK
2730 anyway -> no offset. */
2731 static const int small_width = 24;
2732 test_offset_impl (1, small_width, 0);
2734 /* Width constraint is very small -> no offset. */
2735 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
2737 /* Line would be offset, but due to large line numbers, offsetting
2738 would remove the whole line -> no offset. */
2739 static const int huge_left_margin = 100;
2740 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
2742 /* Line is the same length as the display, but the line number makes it too
2743 long, so offset is required. Caret is at the end so padding on the right
2744 is not in effect. */
2745 for (int excess = 1; excess <= 3; ++excess)
2746 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
2747 excess);
2749 /* Line is much too long for the display, caret is near the end ->
2750 offset should be such that the line fits in the display and caret
2751 remains the same distance from the end that it was. */
2752 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
2753 caret_offset <= max_offset; ++caret_offset)
2754 test_offset_impl (line_bytes - caret_offset, small_width,
2755 line_display_cols + test_left_margin - small_width);
2757 /* As previous case but caret is closer to the middle; now we want it to end
2758 up CARET_LINE_MARGIN bytes from the end. */
2759 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
2760 test_offset_impl (emoji_col, small_width,
2761 emoji_col + test_left_margin
2762 - (small_width - CARET_LINE_MARGIN));
2764 /* Test that the source line is offset as expected when printed. */
2766 test_diagnostic_context dc;
2767 dc.caret_max_width = small_width - 6;
2768 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2769 dc.show_line_numbers_p = true;
2770 dc.show_ruler_p = true;
2771 rich_location richloc (line_table,
2772 linemap_position_for_column (line_table,
2773 emoji_col));
2774 layout test_layout (&dc, &richloc, DK_ERROR);
2775 test_layout.print_line (1);
2776 ASSERT_STREQ (" | 1 \n"
2777 " | 1 \n"
2778 " | 234567890123456789\n"
2779 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2780 "that occupies 8 bytes and 4 display columns, starting at "
2781 "column #102.\n"
2782 " | ^\n\n",
2783 pp_formatted_text (dc.printer));
2786 /* Similar to the previous example, but now the offset called for would split
2787 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2788 it with a padding space in this case. */
2790 test_diagnostic_context dc;
2791 dc.caret_max_width = small_width - 5;
2792 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2793 dc.show_line_numbers_p = true;
2794 dc.show_ruler_p = true;
2795 rich_location richloc (line_table,
2796 linemap_position_for_column (line_table,
2797 emoji_col + 2));
2798 layout test_layout (&dc, &richloc, DK_ERROR);
2799 test_layout.print_line (1);
2800 ASSERT_STREQ (" | 1 1 \n"
2801 " | 1 2 \n"
2802 " | 3456789012345678901\n"
2803 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2804 "that occupies 8 bytes and 4 display columns, starting at "
2805 "column #102.\n"
2806 " | ^\n\n",
2807 pp_formatted_text (dc.printer));
2812 static void
2813 test_layout_x_offset_display_tab (const line_table_case &case_)
2815 const char *content
2816 = "This line is very long, so that we can use it to test the logic for "
2817 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
2818 "a variable number of display columns, starting at column #103.\n";
2820 /* Number of bytes in the line, subtracting one to remove the newline. */
2821 const int line_bytes = strlen (content) - 1;
2823 /* The column where the tab begins. Byte or display is the same as there are
2824 no multibyte characters earlier on the line. */
2825 const int tab_col = 103;
2827 /* Effective extra size of the tab beyond what a single space would have taken
2828 up, indexed by tabstop. */
2829 static const int num_tabstops = 11;
2830 int extra_width[num_tabstops];
2831 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2833 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
2834 extra_width[tabstop] = this_tab_size - 1;
2836 /* Example of this calculation: if tabstop is 10, the tab starting at column
2837 #103 has to expand into 8 spaces, covering columns 103-110, so that the
2838 next character is at column #111. So it takes up 7 more columns than
2839 a space would have taken up. */
2840 ASSERT_EQ (7, extra_width[10]);
2842 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2843 line_table_test ltt (case_);
2845 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2847 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2849 /* Don't attempt to run the tests if column data might be unavailable. */
2850 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2851 return;
2853 /* Check that cpp_display_width handles the tabs as expected. */
2854 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2855 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
2856 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2858 ASSERT_EQ (line_bytes + extra_width[tabstop],
2859 cpp_display_width (lspan.get_buffer (), lspan.length (),
2860 tabstop));
2861 ASSERT_EQ (line_bytes + extra_width[tabstop],
2862 location_compute_display_column (expand_location (line_end),
2863 tabstop));
2866 /* Check that the tab is expanded to the expected number of spaces. */
2867 rich_location richloc (line_table,
2868 linemap_position_for_column (line_table,
2869 tab_col + 1));
2870 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2872 test_diagnostic_context dc;
2873 dc.tabstop = tabstop;
2874 layout test_layout (&dc, &richloc, DK_ERROR);
2875 test_layout.print_line (1);
2876 const char *out = pp_formatted_text (dc.printer);
2877 ASSERT_EQ (NULL, strchr (out, '\t'));
2878 const char *left_quote = strchr (out, '`');
2879 const char *right_quote = strchr (out, '\'');
2880 ASSERT_NE (NULL, left_quote);
2881 ASSERT_NE (NULL, right_quote);
2882 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
2885 /* Check that the line is offset properly and that the tab is broken up
2886 into the expected number of spaces when it is the last character skipped
2887 over. */
2888 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
2890 test_diagnostic_context dc;
2891 dc.tabstop = tabstop;
2892 static const int small_width = 24;
2893 dc.caret_max_width = small_width - 4;
2894 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2895 dc.show_line_numbers_p = true;
2896 layout test_layout (&dc, &richloc, DK_ERROR);
2897 test_layout.print_line (1);
2899 /* We have arranged things so that two columns will be printed before
2900 the caret. If the tab results in more than one space, this should
2901 produce two spaces in the output; otherwise, it will be a single space
2902 preceded by the opening quote before the tab character. */
2903 const char *output1
2904 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
2905 "display columns, starting at column #103.\n"
2906 " | ^\n\n";
2907 const char *output2
2908 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
2909 "display columns, starting at column #103.\n"
2910 " | ^\n\n";
2911 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
2912 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
2917 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2919 static void
2920 test_diagnostic_show_locus_unknown_location ()
2922 test_diagnostic_context dc;
2923 rich_location richloc (line_table, UNKNOWN_LOCATION);
2924 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2925 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
2928 /* Verify that diagnostic_show_locus works sanely for various
2929 single-line cases.
2931 All of these work on the following 1-line source file:
2932 .0000000001111111
2933 .1234567890123456
2934 "foo = bar.field;\n"
2935 which is set up by test_diagnostic_show_locus_one_liner and calls
2936 them. */
2938 /* Just a caret. */
2940 static void
2941 test_one_liner_simple_caret ()
2943 test_diagnostic_context dc;
2944 location_t caret = linemap_position_for_column (line_table, 10);
2945 rich_location richloc (line_table, caret);
2946 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2947 ASSERT_STREQ (" foo = bar.field;\n"
2948 " ^\n",
2949 pp_formatted_text (dc.printer));
2952 /* Caret and range. */
2954 static void
2955 test_one_liner_caret_and_range ()
2957 test_diagnostic_context dc;
2958 location_t caret = linemap_position_for_column (line_table, 10);
2959 location_t start = linemap_position_for_column (line_table, 7);
2960 location_t finish = linemap_position_for_column (line_table, 15);
2961 location_t loc = make_location (caret, start, finish);
2962 rich_location richloc (line_table, loc);
2963 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2964 ASSERT_STREQ (" foo = bar.field;\n"
2965 " ~~~^~~~~~\n",
2966 pp_formatted_text (dc.printer));
2969 /* Multiple ranges and carets. */
2971 static void
2972 test_one_liner_multiple_carets_and_ranges ()
2974 test_diagnostic_context dc;
2975 location_t foo
2976 = make_location (linemap_position_for_column (line_table, 2),
2977 linemap_position_for_column (line_table, 1),
2978 linemap_position_for_column (line_table, 3));
2979 dc.caret_chars[0] = 'A';
2981 location_t bar
2982 = make_location (linemap_position_for_column (line_table, 8),
2983 linemap_position_for_column (line_table, 7),
2984 linemap_position_for_column (line_table, 9));
2985 dc.caret_chars[1] = 'B';
2987 location_t field
2988 = make_location (linemap_position_for_column (line_table, 13),
2989 linemap_position_for_column (line_table, 11),
2990 linemap_position_for_column (line_table, 15));
2991 dc.caret_chars[2] = 'C';
2993 rich_location richloc (line_table, foo);
2994 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2995 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2996 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2997 ASSERT_STREQ (" foo = bar.field;\n"
2998 " ~A~ ~B~ ~~C~~\n",
2999 pp_formatted_text (dc.printer));
3002 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3004 static void
3005 test_one_liner_fixit_insert_before ()
3007 test_diagnostic_context dc;
3008 location_t caret = linemap_position_for_column (line_table, 7);
3009 rich_location richloc (line_table, caret);
3010 richloc.add_fixit_insert_before ("&");
3011 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3012 ASSERT_STREQ (" foo = bar.field;\n"
3013 " ^\n"
3014 " &\n",
3015 pp_formatted_text (dc.printer));
3018 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3020 static void
3021 test_one_liner_fixit_insert_after ()
3023 test_diagnostic_context dc;
3024 location_t start = linemap_position_for_column (line_table, 1);
3025 location_t finish = linemap_position_for_column (line_table, 3);
3026 location_t foo = make_location (start, start, finish);
3027 rich_location richloc (line_table, foo);
3028 richloc.add_fixit_insert_after ("[0]");
3029 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3030 ASSERT_STREQ (" foo = bar.field;\n"
3031 " ^~~\n"
3032 " [0]\n",
3033 pp_formatted_text (dc.printer));
3036 /* Removal fix-it hint: removal of the ".field".
3037 Also verify the interaction of pp_set_prefix with rulers and
3038 fix-it hints. */
3040 static void
3041 test_one_liner_fixit_remove ()
3043 location_t start = linemap_position_for_column (line_table, 10);
3044 location_t finish = linemap_position_for_column (line_table, 15);
3045 location_t dot = make_location (start, start, finish);
3046 rich_location richloc (line_table, dot);
3047 richloc.add_fixit_remove ();
3049 /* Normal. */
3051 test_diagnostic_context dc;
3052 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3053 ASSERT_STREQ (" foo = bar.field;\n"
3054 " ^~~~~~\n"
3055 " ------\n",
3056 pp_formatted_text (dc.printer));
3059 /* Test of adding a prefix. */
3061 test_diagnostic_context dc;
3062 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3063 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3064 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3065 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3066 "TEST PREFIX: ^~~~~~\n"
3067 "TEST PREFIX: ------\n",
3068 pp_formatted_text (dc.printer));
3071 /* Normal, with ruler. */
3073 test_diagnostic_context dc;
3074 dc.show_ruler_p = true;
3075 dc.caret_max_width = 104;
3076 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3077 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3078 " 1 2 3 4 5 6 7 8 9 0 \n"
3079 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3080 " foo = bar.field;\n"
3081 " ^~~~~~\n"
3082 " ------\n",
3083 pp_formatted_text (dc.printer));
3086 /* Test of adding a prefix, with ruler. */
3088 test_diagnostic_context dc;
3089 dc.show_ruler_p = true;
3090 dc.caret_max_width = 50;
3091 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3092 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3093 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3094 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3095 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3096 "TEST PREFIX: foo = bar.field;\n"
3097 "TEST PREFIX: ^~~~~~\n"
3098 "TEST PREFIX: ------\n",
3099 pp_formatted_text (dc.printer));
3102 /* Test of adding a prefix, with ruler and line numbers. */
3104 test_diagnostic_context dc;
3105 dc.show_ruler_p = true;
3106 dc.caret_max_width = 50;
3107 dc.show_line_numbers_p = true;
3108 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3109 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3110 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3111 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3112 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3113 "TEST PREFIX: 1 | foo = bar.field;\n"
3114 "TEST PREFIX: | ^~~~~~\n"
3115 "TEST PREFIX: | ------\n",
3116 pp_formatted_text (dc.printer));
3120 /* Replace fix-it hint: replacing "field" with "m_field". */
3122 static void
3123 test_one_liner_fixit_replace ()
3125 test_diagnostic_context dc;
3126 location_t start = linemap_position_for_column (line_table, 11);
3127 location_t finish = linemap_position_for_column (line_table, 15);
3128 location_t field = make_location (start, start, finish);
3129 rich_location richloc (line_table, field);
3130 richloc.add_fixit_replace ("m_field");
3131 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3132 ASSERT_STREQ (" foo = bar.field;\n"
3133 " ^~~~~\n"
3134 " m_field\n",
3135 pp_formatted_text (dc.printer));
3138 /* Replace fix-it hint: replacing "field" with "m_field",
3139 but where the caret was elsewhere. */
3141 static void
3142 test_one_liner_fixit_replace_non_equal_range ()
3144 test_diagnostic_context dc;
3145 location_t equals = linemap_position_for_column (line_table, 5);
3146 location_t start = linemap_position_for_column (line_table, 11);
3147 location_t finish = linemap_position_for_column (line_table, 15);
3148 rich_location richloc (line_table, equals);
3149 source_range range;
3150 range.m_start = start;
3151 range.m_finish = finish;
3152 richloc.add_fixit_replace (range, "m_field");
3153 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3154 /* The replacement range is not indicated in the annotation line, so
3155 it should be indicated via an additional underline. */
3156 ASSERT_STREQ (" foo = bar.field;\n"
3157 " ^\n"
3158 " -----\n"
3159 " m_field\n",
3160 pp_formatted_text (dc.printer));
3163 /* Replace fix-it hint: replacing "field" with "m_field",
3164 where the caret was elsewhere, but where a secondary range
3165 exactly covers "field". */
3167 static void
3168 test_one_liner_fixit_replace_equal_secondary_range ()
3170 test_diagnostic_context dc;
3171 location_t equals = linemap_position_for_column (line_table, 5);
3172 location_t start = linemap_position_for_column (line_table, 11);
3173 location_t finish = linemap_position_for_column (line_table, 15);
3174 rich_location richloc (line_table, equals);
3175 location_t field = make_location (start, start, finish);
3176 richloc.add_range (field);
3177 richloc.add_fixit_replace (field, "m_field");
3178 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3179 /* The replacement range is indicated in the annotation line,
3180 so it shouldn't be indicated via an additional underline. */
3181 ASSERT_STREQ (" foo = bar.field;\n"
3182 " ^ ~~~~~\n"
3183 " m_field\n",
3184 pp_formatted_text (dc.printer));
3187 /* Verify that we can use ad-hoc locations when adding fixits to a
3188 rich_location. */
3190 static void
3191 test_one_liner_fixit_validation_adhoc_locations ()
3193 /* Generate a range that's too long to be packed, so must
3194 be stored as an ad-hoc location (given the defaults
3195 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3196 const location_t c7 = linemap_position_for_column (line_table, 7);
3197 const location_t c47 = linemap_position_for_column (line_table, 47);
3198 const location_t loc = make_location (c7, c7, c47);
3200 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3201 return;
3203 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3205 /* Insert. */
3207 rich_location richloc (line_table, loc);
3208 richloc.add_fixit_insert_before (loc, "test");
3209 /* It should not have been discarded by the validator. */
3210 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3212 test_diagnostic_context dc;
3213 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3214 ASSERT_STREQ (" foo = bar.field;\n"
3215 " ^~~~~~~~~~ \n"
3216 " test\n",
3217 pp_formatted_text (dc.printer));
3220 /* Remove. */
3222 rich_location richloc (line_table, loc);
3223 source_range range = source_range::from_locations (loc, c47);
3224 richloc.add_fixit_remove (range);
3225 /* It should not have been discarded by the validator. */
3226 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3228 test_diagnostic_context dc;
3229 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3230 ASSERT_STREQ (" foo = bar.field;\n"
3231 " ^~~~~~~~~~ \n"
3232 " -----------------------------------------\n",
3233 pp_formatted_text (dc.printer));
3236 /* Replace. */
3238 rich_location richloc (line_table, loc);
3239 source_range range = source_range::from_locations (loc, c47);
3240 richloc.add_fixit_replace (range, "test");
3241 /* It should not have been discarded by the validator. */
3242 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3244 test_diagnostic_context dc;
3245 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3246 ASSERT_STREQ (" foo = bar.field;\n"
3247 " ^~~~~~~~~~ \n"
3248 " test\n",
3249 pp_formatted_text (dc.printer));
3253 /* Test of consolidating insertions at the same location. */
3255 static void
3256 test_one_liner_many_fixits_1 ()
3258 test_diagnostic_context dc;
3259 location_t equals = linemap_position_for_column (line_table, 5);
3260 rich_location richloc (line_table, equals);
3261 for (int i = 0; i < 19; i++)
3262 richloc.add_fixit_insert_before ("a");
3263 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3264 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3265 ASSERT_STREQ (" foo = bar.field;\n"
3266 " ^\n"
3267 " aaaaaaaaaaaaaaaaaaa\n",
3268 pp_formatted_text (dc.printer));
3271 /* Ensure that we can add an arbitrary number of fix-it hints to a
3272 rich_location, even if they are not consolidated. */
3274 static void
3275 test_one_liner_many_fixits_2 ()
3277 test_diagnostic_context dc;
3278 location_t equals = linemap_position_for_column (line_table, 5);
3279 rich_location richloc (line_table, equals);
3280 for (int i = 0; i < 19; i++)
3282 location_t loc = linemap_position_for_column (line_table, i * 2);
3283 richloc.add_fixit_insert_before (loc, "a");
3285 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3286 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3287 ASSERT_STREQ (" foo = bar.field;\n"
3288 " ^\n"
3289 "a a a a a a a a a a a a a a a a a a a\n",
3290 pp_formatted_text (dc.printer));
3293 /* Test of labeling the ranges within a rich_location. */
3295 static void
3296 test_one_liner_labels ()
3298 location_t foo
3299 = make_location (linemap_position_for_column (line_table, 1),
3300 linemap_position_for_column (line_table, 1),
3301 linemap_position_for_column (line_table, 3));
3302 location_t bar
3303 = make_location (linemap_position_for_column (line_table, 7),
3304 linemap_position_for_column (line_table, 7),
3305 linemap_position_for_column (line_table, 9));
3306 location_t field
3307 = make_location (linemap_position_for_column (line_table, 11),
3308 linemap_position_for_column (line_table, 11),
3309 linemap_position_for_column (line_table, 15));
3311 /* Example where all the labels fit on one line. */
3313 text_range_label label0 ("0");
3314 text_range_label label1 ("1");
3315 text_range_label label2 ("2");
3316 gcc_rich_location richloc (foo, &label0);
3317 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3318 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3321 test_diagnostic_context dc;
3322 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3323 ASSERT_STREQ (" foo = bar.field;\n"
3324 " ^~~ ~~~ ~~~~~\n"
3325 " | | |\n"
3326 " 0 1 2\n",
3327 pp_formatted_text (dc.printer));
3330 /* Verify that we can disable label-printing. */
3332 test_diagnostic_context dc;
3333 dc.show_labels_p = false;
3334 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3335 ASSERT_STREQ (" foo = bar.field;\n"
3336 " ^~~ ~~~ ~~~~~\n",
3337 pp_formatted_text (dc.printer));
3341 /* Example where the labels need extra lines. */
3343 text_range_label label0 ("label 0");
3344 text_range_label label1 ("label 1");
3345 text_range_label label2 ("label 2");
3346 gcc_rich_location richloc (foo, &label0);
3347 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3348 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3350 test_diagnostic_context dc;
3351 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3352 ASSERT_STREQ (" foo = bar.field;\n"
3353 " ^~~ ~~~ ~~~~~\n"
3354 " | | |\n"
3355 " | | label 2\n"
3356 " | label 1\n"
3357 " label 0\n",
3358 pp_formatted_text (dc.printer));
3361 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3362 but label 1 just touches label 2. */
3364 text_range_label label0 ("aaaaa");
3365 text_range_label label1 ("bbbb");
3366 text_range_label label2 ("c");
3367 gcc_rich_location richloc (foo, &label0);
3368 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3369 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3371 test_diagnostic_context dc;
3372 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3373 ASSERT_STREQ (" foo = bar.field;\n"
3374 " ^~~ ~~~ ~~~~~\n"
3375 " | | |\n"
3376 " | | c\n"
3377 " aaaaa bbbb\n",
3378 pp_formatted_text (dc.printer));
3381 /* Example of out-of-order ranges (thus requiring a sort). */
3383 text_range_label label0 ("0");
3384 text_range_label label1 ("1");
3385 text_range_label label2 ("2");
3386 gcc_rich_location richloc (field, &label0);
3387 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3388 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3390 test_diagnostic_context dc;
3391 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3392 ASSERT_STREQ (" foo = bar.field;\n"
3393 " ~~~ ~~~ ^~~~~\n"
3394 " | | |\n"
3395 " 2 1 0\n",
3396 pp_formatted_text (dc.printer));
3399 /* Ensure we don't ICE if multiple ranges with labels are on
3400 the same point. */
3402 text_range_label label0 ("label 0");
3403 text_range_label label1 ("label 1");
3404 text_range_label label2 ("label 2");
3405 gcc_rich_location richloc (bar, &label0);
3406 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3407 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3409 test_diagnostic_context dc;
3410 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3411 ASSERT_STREQ (" foo = bar.field;\n"
3412 " ^~~\n"
3413 " |\n"
3414 " label 0\n"
3415 " label 1\n"
3416 " label 2\n",
3417 pp_formatted_text (dc.printer));
3420 /* Example of out-of-order ranges (thus requiring a sort), where
3421 they overlap, and there are multiple ranges on the same point. */
3423 text_range_label label_0a ("label 0a");
3424 text_range_label label_1a ("label 1a");
3425 text_range_label label_2a ("label 2a");
3426 text_range_label label_0b ("label 0b");
3427 text_range_label label_1b ("label 1b");
3428 text_range_label label_2b ("label 2b");
3429 text_range_label label_0c ("label 0c");
3430 text_range_label label_1c ("label 1c");
3431 text_range_label label_2c ("label 2c");
3432 gcc_rich_location richloc (field, &label_0a);
3433 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3434 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3436 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3437 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3438 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3440 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3441 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3442 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3444 test_diagnostic_context dc;
3445 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3446 ASSERT_STREQ (" foo = bar.field;\n"
3447 " ~~~ ~~~ ^~~~~\n"
3448 " | | |\n"
3449 " | | label 0a\n"
3450 " | | label 0b\n"
3451 " | | label 0c\n"
3452 " | label 1a\n"
3453 " | label 1b\n"
3454 " | label 1c\n"
3455 " label 2a\n"
3456 " label 2b\n"
3457 " label 2c\n",
3458 pp_formatted_text (dc.printer));
3461 /* Verify that a NULL result from range_label::get_text is
3462 handled gracefully. */
3464 text_range_label label (NULL);
3465 gcc_rich_location richloc (bar, &label);
3467 test_diagnostic_context dc;
3468 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3469 ASSERT_STREQ (" foo = bar.field;\n"
3470 " ^~~\n",
3471 pp_formatted_text (dc.printer));
3474 /* TODO: example of formatted printing (needs to be in
3475 gcc-rich-location.c due to Makefile.in issues). */
3478 /* Run the various one-liner tests. */
3480 static void
3481 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3483 /* Create a tempfile and write some text to it.
3484 ....................0000000001111111.
3485 ....................1234567890123456. */
3486 const char *content = "foo = bar.field;\n";
3487 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3488 line_table_test ltt (case_);
3490 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3492 location_t line_end = linemap_position_for_column (line_table, 16);
3494 /* Don't attempt to run the tests if column data might be unavailable. */
3495 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3496 return;
3498 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3499 ASSERT_EQ (1, LOCATION_LINE (line_end));
3500 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3502 test_one_liner_simple_caret ();
3503 test_one_liner_caret_and_range ();
3504 test_one_liner_multiple_carets_and_ranges ();
3505 test_one_liner_fixit_insert_before ();
3506 test_one_liner_fixit_insert_after ();
3507 test_one_liner_fixit_remove ();
3508 test_one_liner_fixit_replace ();
3509 test_one_liner_fixit_replace_non_equal_range ();
3510 test_one_liner_fixit_replace_equal_secondary_range ();
3511 test_one_liner_fixit_validation_adhoc_locations ();
3512 test_one_liner_many_fixits_1 ();
3513 test_one_liner_many_fixits_2 ();
3514 test_one_liner_labels ();
3517 /* Version of all one-liner tests exercising multibyte awareness. For
3518 simplicity we stick to using two multibyte characters in the test, U+1F602
3519 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3520 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3521 below asserts would be easier to read if we used UTF-8 directly in the
3522 string constants, but it seems better not to demand the host compiler
3523 support this, when it isn't otherwise necessary. Instead, whenever an
3524 extended character appears in a string, we put a line break after it so that
3525 all succeeding characters can appear visually at the correct display column.
3527 All of these work on the following 1-line source file:
3529 .0000000001111111111222222 display
3530 .1234567890123456789012345 columns
3531 "SS_foo = P_bar.SS_fieldP;\n"
3532 .0000000111111111222222223 byte
3533 .1356789012456789134567891 columns
3535 which is set up by test_diagnostic_show_locus_one_liner and calls
3536 them. Here SS represents the two display columns for the U+1F602 emoji and
3537 P represents the one display column for the U+03C0 pi symbol. */
3539 /* Just a caret. */
3541 static void
3542 test_one_liner_simple_caret_utf8 ()
3544 test_diagnostic_context dc;
3545 location_t caret = linemap_position_for_column (line_table, 18);
3546 rich_location richloc (line_table, caret);
3547 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3548 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3549 "_foo = \xcf\x80"
3550 "_bar.\xf0\x9f\x98\x82"
3551 "_field\xcf\x80"
3552 ";\n"
3553 " ^\n",
3554 pp_formatted_text (dc.printer));
3557 /* Caret and range. */
3558 static void
3559 test_one_liner_caret_and_range_utf8 ()
3561 test_diagnostic_context dc;
3562 location_t caret = linemap_position_for_column (line_table, 18);
3563 location_t start = linemap_position_for_column (line_table, 12);
3564 location_t finish = linemap_position_for_column (line_table, 30);
3565 location_t loc = make_location (caret, start, finish);
3566 rich_location richloc (line_table, loc);
3567 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3568 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3569 "_foo = \xcf\x80"
3570 "_bar.\xf0\x9f\x98\x82"
3571 "_field\xcf\x80"
3572 ";\n"
3573 " ~~~~~^~~~~~~~~~\n",
3574 pp_formatted_text (dc.printer));
3577 /* Multiple ranges and carets. */
3579 static void
3580 test_one_liner_multiple_carets_and_ranges_utf8 ()
3582 test_diagnostic_context dc;
3583 location_t foo
3584 = make_location (linemap_position_for_column (line_table, 7),
3585 linemap_position_for_column (line_table, 1),
3586 linemap_position_for_column (line_table, 8));
3587 dc.caret_chars[0] = 'A';
3589 location_t bar
3590 = make_location (linemap_position_for_column (line_table, 16),
3591 linemap_position_for_column (line_table, 12),
3592 linemap_position_for_column (line_table, 17));
3593 dc.caret_chars[1] = 'B';
3595 location_t field
3596 = make_location (linemap_position_for_column (line_table, 26),
3597 linemap_position_for_column (line_table, 19),
3598 linemap_position_for_column (line_table, 30));
3599 dc.caret_chars[2] = 'C';
3600 rich_location richloc (line_table, foo);
3601 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3602 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3603 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3604 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3605 "_foo = \xcf\x80"
3606 "_bar.\xf0\x9f\x98\x82"
3607 "_field\xcf\x80"
3608 ";\n"
3609 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3610 pp_formatted_text (dc.printer));
3613 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3615 static void
3616 test_one_liner_fixit_insert_before_utf8 ()
3618 test_diagnostic_context dc;
3619 location_t caret = linemap_position_for_column (line_table, 12);
3620 rich_location richloc (line_table, caret);
3621 richloc.add_fixit_insert_before ("&");
3622 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3623 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3624 "_foo = \xcf\x80"
3625 "_bar.\xf0\x9f\x98\x82"
3626 "_field\xcf\x80"
3627 ";\n"
3628 " ^\n"
3629 " &\n",
3630 pp_formatted_text (dc.printer));
3633 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3635 static void
3636 test_one_liner_fixit_insert_after_utf8 ()
3638 test_diagnostic_context dc;
3639 location_t start = linemap_position_for_column (line_table, 1);
3640 location_t finish = linemap_position_for_column (line_table, 8);
3641 location_t foo = make_location (start, start, finish);
3642 rich_location richloc (line_table, foo);
3643 richloc.add_fixit_insert_after ("[0]");
3644 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3645 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3646 "_foo = \xcf\x80"
3647 "_bar.\xf0\x9f\x98\x82"
3648 "_field\xcf\x80"
3649 ";\n"
3650 " ^~~~~~\n"
3651 " [0]\n",
3652 pp_formatted_text (dc.printer));
3655 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3657 static void
3658 test_one_liner_fixit_remove_utf8 ()
3660 test_diagnostic_context dc;
3661 location_t start = linemap_position_for_column (line_table, 18);
3662 location_t finish = linemap_position_for_column (line_table, 30);
3663 location_t dot = make_location (start, start, finish);
3664 rich_location richloc (line_table, dot);
3665 richloc.add_fixit_remove ();
3666 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3667 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3668 "_foo = \xcf\x80"
3669 "_bar.\xf0\x9f\x98\x82"
3670 "_field\xcf\x80"
3671 ";\n"
3672 " ^~~~~~~~~~\n"
3673 " ----------\n",
3674 pp_formatted_text (dc.printer));
3677 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3679 static void
3680 test_one_liner_fixit_replace_utf8 ()
3682 test_diagnostic_context dc;
3683 location_t start = linemap_position_for_column (line_table, 19);
3684 location_t finish = linemap_position_for_column (line_table, 30);
3685 location_t field = make_location (start, start, finish);
3686 rich_location richloc (line_table, field);
3687 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3688 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3689 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3690 "_foo = \xcf\x80"
3691 "_bar.\xf0\x9f\x98\x82"
3692 "_field\xcf\x80"
3693 ";\n"
3694 " ^~~~~~~~~\n"
3695 " m_\xf0\x9f\x98\x82"
3696 "_field\xcf\x80\n",
3697 pp_formatted_text (dc.printer));
3700 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3701 but where the caret was elsewhere. */
3703 static void
3704 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3706 test_diagnostic_context dc;
3707 location_t equals = linemap_position_for_column (line_table, 10);
3708 location_t start = linemap_position_for_column (line_table, 19);
3709 location_t finish = linemap_position_for_column (line_table, 30);
3710 rich_location richloc (line_table, equals);
3711 source_range range;
3712 range.m_start = start;
3713 range.m_finish = finish;
3714 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3715 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3716 /* The replacement range is not indicated in the annotation line, so
3717 it should be indicated via an additional underline. */
3718 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3719 "_foo = \xcf\x80"
3720 "_bar.\xf0\x9f\x98\x82"
3721 "_field\xcf\x80"
3722 ";\n"
3723 " ^\n"
3724 " ---------\n"
3725 " m_\xf0\x9f\x98\x82"
3726 "_field\xcf\x80\n",
3727 pp_formatted_text (dc.printer));
3730 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3731 where the caret was elsewhere, but where a secondary range
3732 exactly covers "field". */
3734 static void
3735 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3737 test_diagnostic_context dc;
3738 location_t equals = linemap_position_for_column (line_table, 10);
3739 location_t start = linemap_position_for_column (line_table, 19);
3740 location_t finish = linemap_position_for_column (line_table, 30);
3741 rich_location richloc (line_table, equals);
3742 location_t field = make_location (start, start, finish);
3743 richloc.add_range (field);
3744 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3745 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3746 /* The replacement range is indicated in the annotation line,
3747 so it shouldn't be indicated via an additional underline. */
3748 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3749 "_foo = \xcf\x80"
3750 "_bar.\xf0\x9f\x98\x82"
3751 "_field\xcf\x80"
3752 ";\n"
3753 " ^ ~~~~~~~~~\n"
3754 " m_\xf0\x9f\x98\x82"
3755 "_field\xcf\x80\n",
3756 pp_formatted_text (dc.printer));
3759 /* Verify that we can use ad-hoc locations when adding fixits to a
3760 rich_location. */
3762 static void
3763 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3765 /* Generate a range that's too long to be packed, so must
3766 be stored as an ad-hoc location (given the defaults
3767 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3768 const location_t c12 = linemap_position_for_column (line_table, 12);
3769 const location_t c52 = linemap_position_for_column (line_table, 52);
3770 const location_t loc = make_location (c12, c12, c52);
3772 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3773 return;
3775 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3777 /* Insert. */
3779 rich_location richloc (line_table, loc);
3780 richloc.add_fixit_insert_before (loc, "test");
3781 /* It should not have been discarded by the validator. */
3782 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3784 test_diagnostic_context dc;
3785 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3786 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3787 "_foo = \xcf\x80"
3788 "_bar.\xf0\x9f\x98\x82"
3789 "_field\xcf\x80"
3790 ";\n"
3791 " ^~~~~~~~~~~~~~~~ \n"
3792 " test\n",
3793 pp_formatted_text (dc.printer));
3796 /* Remove. */
3798 rich_location richloc (line_table, loc);
3799 source_range range = source_range::from_locations (loc, c52);
3800 richloc.add_fixit_remove (range);
3801 /* It should not have been discarded by the validator. */
3802 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3804 test_diagnostic_context dc;
3805 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3806 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3807 "_foo = \xcf\x80"
3808 "_bar.\xf0\x9f\x98\x82"
3809 "_field\xcf\x80"
3810 ";\n"
3811 " ^~~~~~~~~~~~~~~~ \n"
3812 " -------------------------------------\n",
3813 pp_formatted_text (dc.printer));
3816 /* Replace. */
3818 rich_location richloc (line_table, loc);
3819 source_range range = source_range::from_locations (loc, c52);
3820 richloc.add_fixit_replace (range, "test");
3821 /* It should not have been discarded by the validator. */
3822 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3824 test_diagnostic_context dc;
3825 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3826 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3827 "_foo = \xcf\x80"
3828 "_bar.\xf0\x9f\x98\x82"
3829 "_field\xcf\x80"
3830 ";\n"
3831 " ^~~~~~~~~~~~~~~~ \n"
3832 " test\n",
3833 pp_formatted_text (dc.printer));
3837 /* Test of consolidating insertions at the same location. */
3839 static void
3840 test_one_liner_many_fixits_1_utf8 ()
3842 test_diagnostic_context dc;
3843 location_t equals = linemap_position_for_column (line_table, 10);
3844 rich_location richloc (line_table, equals);
3845 for (int i = 0; i < 19; i++)
3846 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
3847 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3848 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3849 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3850 "_foo = \xcf\x80"
3851 "_bar.\xf0\x9f\x98\x82"
3852 "_field\xcf\x80"
3853 ";\n"
3854 " ^\n"
3855 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3856 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3857 pp_formatted_text (dc.printer));
3860 /* Ensure that we can add an arbitrary number of fix-it hints to a
3861 rich_location, even if they are not consolidated. */
3863 static void
3864 test_one_liner_many_fixits_2_utf8 ()
3866 test_diagnostic_context dc;
3867 location_t equals = linemap_position_for_column (line_table, 10);
3868 rich_location richloc (line_table, equals);
3869 const int nlocs = 19;
3870 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3871 34, 36, 38, 40, 42, 44};
3872 for (int i = 0; i != nlocs; ++i)
3874 location_t loc = linemap_position_for_column (line_table, locs[i]);
3875 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
3878 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
3879 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3880 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3881 "_foo = \xcf\x80"
3882 "_bar.\xf0\x9f\x98\x82"
3883 "_field\xcf\x80"
3884 ";\n"
3885 " ^\n"
3886 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3887 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3888 pp_formatted_text (dc.printer));
3891 /* Test of labeling the ranges within a rich_location. */
3893 static void
3894 test_one_liner_labels_utf8 ()
3896 location_t foo
3897 = make_location (linemap_position_for_column (line_table, 1),
3898 linemap_position_for_column (line_table, 1),
3899 linemap_position_for_column (line_table, 8));
3900 location_t bar
3901 = make_location (linemap_position_for_column (line_table, 12),
3902 linemap_position_for_column (line_table, 12),
3903 linemap_position_for_column (line_table, 17));
3904 location_t field
3905 = make_location (linemap_position_for_column (line_table, 19),
3906 linemap_position_for_column (line_table, 19),
3907 linemap_position_for_column (line_table, 30));
3909 /* Example where all the labels fit on one line. */
3911 /* These three labels contain multibyte characters such that their byte
3912 lengths are respectively (12, 10, 18), but their display widths are only
3913 (6, 5, 9). All three fit on the line when considering the display
3914 widths, but not when considering the byte widths, so verify that we do
3915 indeed put them all on one line. */
3916 text_range_label label0
3917 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3918 text_range_label label1
3919 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3920 text_range_label label2
3921 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3922 "\xcf\x80");
3923 gcc_rich_location richloc (foo, &label0);
3924 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3925 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3928 test_diagnostic_context dc;
3929 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3930 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3931 "_foo = \xcf\x80"
3932 "_bar.\xf0\x9f\x98\x82"
3933 "_field\xcf\x80"
3934 ";\n"
3935 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3936 " | | |\n"
3937 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3938 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3939 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3940 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3941 pp_formatted_text (dc.printer));
3946 /* Example where the labels need extra lines. */
3948 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
3949 text_range_label label1 ("label 1\xcf\x80");
3950 text_range_label label2 ("label 2\xcf\x80");
3951 gcc_rich_location richloc (foo, &label0);
3952 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3953 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3955 test_diagnostic_context dc;
3956 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3958 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3959 "_foo = \xcf\x80"
3960 "_bar.\xf0\x9f\x98\x82"
3961 "_field\xcf\x80"
3962 ";\n"
3963 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3964 " | | |\n"
3965 " | | label 2\xcf\x80\n"
3966 " | label 1\xcf\x80\n"
3967 " label 0\xf0\x9f\x98\x82\n",
3968 pp_formatted_text (dc.printer));
3971 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3972 but label 1 just touches label 2. */
3974 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3975 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3976 text_range_label label2 ("c");
3977 gcc_rich_location richloc (foo, &label0);
3978 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3979 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3981 test_diagnostic_context dc;
3982 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3983 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3984 "_foo = \xcf\x80"
3985 "_bar.\xf0\x9f\x98\x82"
3986 "_field\xcf\x80"
3987 ";\n"
3988 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3989 " | | |\n"
3990 " | | c\n"
3991 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
3992 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
3993 pp_formatted_text (dc.printer));
3997 /* Make sure that colorization codes don't interrupt a multibyte
3998 sequence, which would corrupt it. */
3999 static void
4000 test_one_liner_colorized_utf8 ()
4002 test_diagnostic_context dc;
4003 dc.colorize_source_p = true;
4004 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4005 const location_t pi = linemap_position_for_column (line_table, 12);
4006 rich_location richloc (line_table, pi);
4007 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4009 /* In order to avoid having the test depend on exactly how the colorization
4010 was effected, just confirm there are two pi characters in the output. */
4011 const char *result = pp_formatted_text (dc.printer);
4012 const char *null_term = result + strlen (result);
4013 const char *first_pi = strstr (result, "\xcf\x80");
4014 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4015 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4018 /* Run the various one-liner tests. */
4020 static void
4021 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4023 /* Create a tempfile and write some text to it. */
4024 const char *content
4025 /* Display columns.
4026 0000000000000000000000011111111111111111111111111111112222222222222
4027 1111111122222222345678900000000123456666666677777777890123444444445 */
4028 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4029 /* 0000000000000000000001111111111111111111222222222222222222222233333
4030 1111222233334444567890122223333456789999000011112222345678999900001
4031 Byte columns. */
4032 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4033 line_table_test ltt (case_);
4035 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4037 location_t line_end = linemap_position_for_column (line_table, 31);
4039 /* Don't attempt to run the tests if column data might be unavailable. */
4040 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4041 return;
4043 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4044 ASSERT_EQ (1, LOCATION_LINE (line_end));
4045 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4047 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4048 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4049 def_tabstop));
4050 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4051 def_tabstop));
4053 test_one_liner_simple_caret_utf8 ();
4054 test_one_liner_caret_and_range_utf8 ();
4055 test_one_liner_multiple_carets_and_ranges_utf8 ();
4056 test_one_liner_fixit_insert_before_utf8 ();
4057 test_one_liner_fixit_insert_after_utf8 ();
4058 test_one_liner_fixit_remove_utf8 ();
4059 test_one_liner_fixit_replace_utf8 ();
4060 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4061 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4062 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4063 test_one_liner_many_fixits_1_utf8 ();
4064 test_one_liner_many_fixits_2_utf8 ();
4065 test_one_liner_labels_utf8 ();
4066 test_one_liner_colorized_utf8 ();
4069 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4071 static void
4072 test_add_location_if_nearby (const line_table_case &case_)
4074 /* Create a tempfile and write some text to it.
4075 ...000000000111111111122222222223333333333.
4076 ...123456789012345678901234567890123456789. */
4077 const char *content
4078 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4079 "struct different_line\n" /* line 2. */
4080 "{\n" /* line 3. */
4081 " double x;\n" /* line 4. */
4082 " double y;\n" /* line 5. */
4083 ";\n"); /* line 6. */
4084 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4085 line_table_test ltt (case_);
4087 const line_map_ordinary *ord_map
4088 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4089 tmp.get_filename (), 0));
4091 linemap_line_start (line_table, 1, 100);
4093 const location_t final_line_end
4094 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4096 /* Don't attempt to run the tests if column data might be unavailable. */
4097 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4098 return;
4100 /* Test of add_location_if_nearby on the same line as the
4101 primary location. */
4103 const location_t missing_close_brace_1_39
4104 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4105 const location_t matching_open_brace_1_18
4106 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4107 gcc_rich_location richloc (missing_close_brace_1_39);
4108 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4109 ASSERT_TRUE (added);
4110 ASSERT_EQ (2, richloc.get_num_locations ());
4111 test_diagnostic_context dc;
4112 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4113 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4114 " ~ ^\n",
4115 pp_formatted_text (dc.printer));
4118 /* Test of add_location_if_nearby on a different line to the
4119 primary location. */
4121 const location_t missing_close_brace_6_1
4122 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4123 const location_t matching_open_brace_3_1
4124 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4125 gcc_rich_location richloc (missing_close_brace_6_1);
4126 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4127 ASSERT_FALSE (added);
4128 ASSERT_EQ (1, richloc.get_num_locations ());
4132 /* Verify that we print fixits even if they only affect lines
4133 outside those covered by the ranges in the rich_location. */
4135 static void
4136 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4138 /* Create a tempfile and write some text to it.
4139 ...000000000111111111122222222223333333333.
4140 ...123456789012345678901234567890123456789. */
4141 const char *content
4142 = ("struct point { double x; double y; };\n" /* line 1. */
4143 "struct point origin = {x: 0.0,\n" /* line 2. */
4144 " y\n" /* line 3. */
4145 "\n" /* line 4. */
4146 "\n" /* line 5. */
4147 " : 0.0};\n"); /* line 6. */
4148 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4149 line_table_test ltt (case_);
4151 const line_map_ordinary *ord_map
4152 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4153 tmp.get_filename (), 0));
4155 linemap_line_start (line_table, 1, 100);
4157 const location_t final_line_end
4158 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4160 /* Don't attempt to run the tests if column data might be unavailable. */
4161 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4162 return;
4164 /* A pair of tests for modernizing the initializers to C99-style. */
4166 /* The one-liner case (line 2). */
4168 test_diagnostic_context dc;
4169 const location_t x
4170 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4171 const location_t colon
4172 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4173 rich_location richloc (line_table, colon);
4174 richloc.add_fixit_insert_before (x, ".");
4175 richloc.add_fixit_replace (colon, "=");
4176 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4177 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4178 " ^\n"
4179 " .=\n",
4180 pp_formatted_text (dc.printer));
4183 /* The multiline case. The caret for the rich_location is on line 6;
4184 verify that insertion fixit on line 3 is still printed (and that
4185 span starts are printed due to the gap between the span at line 3
4186 and that at line 6). */
4188 test_diagnostic_context dc;
4189 const location_t y
4190 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4191 const location_t colon
4192 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4193 rich_location richloc (line_table, colon);
4194 richloc.add_fixit_insert_before (y, ".");
4195 richloc.add_fixit_replace (colon, "=");
4196 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4197 ASSERT_STREQ ("FILENAME:3:24:\n"
4198 " y\n"
4199 " .\n"
4200 "FILENAME:6:25:\n"
4201 " : 0.0};\n"
4202 " ^\n"
4203 " =\n",
4204 pp_formatted_text (dc.printer));
4207 /* As above, but verify the behavior of multiple line spans
4208 with line-numbering enabled. */
4210 const location_t y
4211 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4212 const location_t colon
4213 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4214 rich_location richloc (line_table, colon);
4215 richloc.add_fixit_insert_before (y, ".");
4216 richloc.add_fixit_replace (colon, "=");
4217 test_diagnostic_context dc;
4218 dc.show_line_numbers_p = true;
4219 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4220 ASSERT_STREQ (" 3 | y\n"
4221 " | .\n"
4222 "......\n"
4223 " 6 | : 0.0};\n"
4224 " | ^\n"
4225 " | =\n",
4226 pp_formatted_text (dc.printer));
4231 /* Verify that fix-it hints are appropriately consolidated.
4233 If any fix-it hints in a rich_location involve locations beyond
4234 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4235 the fix-it as a whole, so there should be none.
4237 Otherwise, verify that consecutive "replace" and "remove" fix-its
4238 are merged, and that other fix-its remain separate. */
4240 static void
4241 test_fixit_consolidation (const line_table_case &case_)
4243 line_table_test ltt (case_);
4245 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4247 const location_t c10 = linemap_position_for_column (line_table, 10);
4248 const location_t c15 = linemap_position_for_column (line_table, 15);
4249 const location_t c16 = linemap_position_for_column (line_table, 16);
4250 const location_t c17 = linemap_position_for_column (line_table, 17);
4251 const location_t c20 = linemap_position_for_column (line_table, 20);
4252 const location_t c21 = linemap_position_for_column (line_table, 21);
4253 const location_t caret = c10;
4255 /* Insert + insert. */
4257 rich_location richloc (line_table, caret);
4258 richloc.add_fixit_insert_before (c10, "foo");
4259 richloc.add_fixit_insert_before (c15, "bar");
4261 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4262 /* Bogus column info for 2nd fixit, so no fixits. */
4263 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4264 else
4265 /* They should not have been merged. */
4266 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4269 /* Insert + replace. */
4271 rich_location richloc (line_table, caret);
4272 richloc.add_fixit_insert_before (c10, "foo");
4273 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4274 "bar");
4276 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4277 /* Bogus column info for 2nd fixit, so no fixits. */
4278 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4279 else
4280 /* They should not have been merged. */
4281 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4284 /* Replace + non-consecutive insert. */
4286 rich_location richloc (line_table, caret);
4287 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4288 "bar");
4289 richloc.add_fixit_insert_before (c17, "foo");
4291 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4292 /* Bogus column info for 2nd fixit, so no fixits. */
4293 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4294 else
4295 /* They should not have been merged. */
4296 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4299 /* Replace + non-consecutive replace. */
4301 rich_location richloc (line_table, caret);
4302 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4303 "foo");
4304 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4305 "bar");
4307 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4308 /* Bogus column info for 2nd fixit, so no fixits. */
4309 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4310 else
4311 /* They should not have been merged. */
4312 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4315 /* Replace + consecutive replace. */
4317 rich_location richloc (line_table, caret);
4318 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4319 "foo");
4320 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4321 "bar");
4323 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4324 /* Bogus column info for 2nd fixit, so no fixits. */
4325 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4326 else
4328 /* They should have been merged into a single "replace". */
4329 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4330 const fixit_hint *hint = richloc.get_fixit_hint (0);
4331 ASSERT_STREQ ("foobar", hint->get_string ());
4332 ASSERT_EQ (c10, hint->get_start_loc ());
4333 ASSERT_EQ (c21, hint->get_next_loc ());
4337 /* Replace + consecutive removal. */
4339 rich_location richloc (line_table, caret);
4340 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4341 "foo");
4342 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4344 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4345 /* Bogus column info for 2nd fixit, so no fixits. */
4346 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4347 else
4349 /* They should have been merged into a single replace, with the
4350 range extended to cover that of the removal. */
4351 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4352 const fixit_hint *hint = richloc.get_fixit_hint (0);
4353 ASSERT_STREQ ("foo", hint->get_string ());
4354 ASSERT_EQ (c10, hint->get_start_loc ());
4355 ASSERT_EQ (c21, hint->get_next_loc ());
4359 /* Consecutive removals. */
4361 rich_location richloc (line_table, caret);
4362 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4363 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4365 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4366 /* Bogus column info for 2nd fixit, so no fixits. */
4367 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4368 else
4370 /* They should have been merged into a single "replace-with-empty". */
4371 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4372 const fixit_hint *hint = richloc.get_fixit_hint (0);
4373 ASSERT_STREQ ("", hint->get_string ());
4374 ASSERT_EQ (c10, hint->get_start_loc ());
4375 ASSERT_EQ (c21, hint->get_next_loc ());
4380 /* Verify that the line_corrections machinery correctly prints
4381 overlapping fixit-hints. */
4383 static void
4384 test_overlapped_fixit_printing (const line_table_case &case_)
4386 /* Create a tempfile and write some text to it.
4387 ...000000000111111111122222222223333333333.
4388 ...123456789012345678901234567890123456789. */
4389 const char *content
4390 = (" foo *f = (foo *)ptr->field;\n");
4391 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4392 line_table_test ltt (case_);
4394 const line_map_ordinary *ord_map
4395 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4396 tmp.get_filename (), 0));
4398 linemap_line_start (line_table, 1, 100);
4400 const location_t final_line_end
4401 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4403 /* Don't attempt to run the tests if column data might be unavailable. */
4404 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4405 return;
4407 /* A test for converting a C-style cast to a C++-style cast. */
4408 const location_t open_paren
4409 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4410 const location_t close_paren
4411 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4412 const location_t expr_start
4413 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4414 const location_t expr_finish
4415 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4416 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4418 /* Various examples of fix-it hints that aren't themselves consolidated,
4419 but for which the *printing* may need consolidation. */
4421 /* Example where 3 fix-it hints are printed as one. */
4423 test_diagnostic_context dc;
4424 rich_location richloc (line_table, expr);
4425 richloc.add_fixit_replace (open_paren, "const_cast<");
4426 richloc.add_fixit_replace (close_paren, "> (");
4427 richloc.add_fixit_insert_after (")");
4429 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4430 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4431 " ^~~~~~~~~~\n"
4432 " -----------------\n"
4433 " const_cast<foo *> (ptr->field)\n",
4434 pp_formatted_text (dc.printer));
4436 /* Unit-test the line_corrections machinery. */
4437 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4438 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4439 ASSERT_EQ (column_range (12, 12),
4440 get_affected_range (&dc, hint_0, CU_BYTES));
4441 ASSERT_EQ (column_range (12, 12),
4442 get_affected_range (&dc, hint_0, CU_DISPLAY_COLS));
4443 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc, hint_0));
4444 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4445 ASSERT_EQ (column_range (18, 18),
4446 get_affected_range (&dc, hint_1, CU_BYTES));
4447 ASSERT_EQ (column_range (18, 18),
4448 get_affected_range (&dc, hint_1, CU_DISPLAY_COLS));
4449 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc, hint_1));
4450 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4451 ASSERT_EQ (column_range (29, 28),
4452 get_affected_range (&dc, hint_2, CU_BYTES));
4453 ASSERT_EQ (column_range (29, 28),
4454 get_affected_range (&dc, hint_2, CU_DISPLAY_COLS));
4455 ASSERT_EQ (column_range (29, 29), get_printed_columns (&dc, hint_2));
4457 /* Add each hint in turn to a line_corrections instance,
4458 and verify that they are consolidated into one correction instance
4459 as expected. */
4460 line_corrections lc (&dc, tmp.get_filename (), 1);
4462 /* The first replace hint by itself. */
4463 lc.add_hint (hint_0);
4464 ASSERT_EQ (1, lc.m_corrections.length ());
4465 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4466 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4467 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4468 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4470 /* After the second replacement hint, they are printed together
4471 as a replacement (along with the text between them). */
4472 lc.add_hint (hint_1);
4473 ASSERT_EQ (1, lc.m_corrections.length ());
4474 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4475 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4476 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4477 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4479 /* After the final insertion hint, they are all printed together
4480 as a replacement (along with the text between them). */
4481 lc.add_hint (hint_2);
4482 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4483 lc.m_corrections[0]->m_text);
4484 ASSERT_EQ (1, lc.m_corrections.length ());
4485 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4486 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4487 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4490 /* Example where two are consolidated during printing. */
4492 test_diagnostic_context dc;
4493 rich_location richloc (line_table, expr);
4494 richloc.add_fixit_replace (open_paren, "CAST (");
4495 richloc.add_fixit_replace (close_paren, ") (");
4496 richloc.add_fixit_insert_after (")");
4498 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4499 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4500 " ^~~~~~~~~~\n"
4501 " -\n"
4502 " CAST (-\n"
4503 " ) ( )\n",
4504 pp_formatted_text (dc.printer));
4507 /* Example where none are consolidated during printing. */
4509 test_diagnostic_context dc;
4510 rich_location richloc (line_table, expr);
4511 richloc.add_fixit_replace (open_paren, "CST (");
4512 richloc.add_fixit_replace (close_paren, ") (");
4513 richloc.add_fixit_insert_after (")");
4515 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4516 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4517 " ^~~~~~~~~~\n"
4518 " -\n"
4519 " CST ( -\n"
4520 " ) ( )\n",
4521 pp_formatted_text (dc.printer));
4524 /* Example of deletion fix-it hints. */
4526 test_diagnostic_context dc;
4527 rich_location richloc (line_table, expr);
4528 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4529 source_range victim = {open_paren, close_paren};
4530 richloc.add_fixit_remove (victim);
4532 /* This case is actually handled by fixit-consolidation,
4533 rather than by line_corrections. */
4534 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4536 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4537 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4538 " ^~~~~~~~~~\n"
4539 " -------\n"
4540 " (bar *)\n",
4541 pp_formatted_text (dc.printer));
4544 /* Example of deletion fix-it hints that would overlap. */
4546 test_diagnostic_context dc;
4547 rich_location richloc (line_table, expr);
4548 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4549 source_range victim = {expr_start, expr_finish};
4550 richloc.add_fixit_remove (victim);
4552 /* These fixits are not consolidated. */
4553 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4555 /* But the corrections are. */
4556 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4557 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4558 " ^~~~~~~~~~\n"
4559 " -----------------\n"
4560 " (longer *)(foo *)\n",
4561 pp_formatted_text (dc.printer));
4564 /* Example of insertion fix-it hints that would overlap. */
4566 test_diagnostic_context dc;
4567 rich_location richloc (line_table, expr);
4568 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4569 richloc.add_fixit_insert_after (close_paren, "TEST");
4571 /* The first insertion is long enough that if printed naively,
4572 it would overlap with the second.
4573 Verify that they are printed as a single replacement. */
4574 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4575 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4576 " ^~~~~~~~~~\n"
4577 " -------\n"
4578 " LONGER THAN THE CAST(foo *)TEST\n",
4579 pp_formatted_text (dc.printer));
4583 /* Multibyte-aware version of preceding tests. See comments above
4584 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4585 characters here. */
4587 static void
4588 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4590 /* Create a tempfile and write some text to it. */
4592 const char *content
4593 /* Display columns.
4594 00000000000000000000000111111111111111111111111222222222222222223
4595 12344444444555555556789012344444444555555556789012345678999999990 */
4596 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4597 /* 00000000000000000000011111111111111111111112222222222333333333333
4598 12344445555666677778901234566667777888899990123456789012333344445
4599 Byte columns. */
4601 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4602 line_table_test ltt (case_);
4604 const line_map_ordinary *ord_map
4605 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4606 tmp.get_filename (), 0));
4608 linemap_line_start (line_table, 1, 100);
4610 const location_t final_line_end
4611 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4613 /* Don't attempt to run the tests if column data might be unavailable. */
4614 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4615 return;
4617 /* A test for converting a C-style cast to a C++-style cast. */
4618 const location_t open_paren
4619 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4620 const location_t close_paren
4621 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4622 const location_t expr_start
4623 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4624 const location_t expr_finish
4625 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4626 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4628 /* Various examples of fix-it hints that aren't themselves consolidated,
4629 but for which the *printing* may need consolidation. */
4631 /* Example where 3 fix-it hints are printed as one. */
4633 test_diagnostic_context dc;
4634 rich_location richloc (line_table, expr);
4635 richloc.add_fixit_replace (open_paren, "const_cast<");
4636 richloc.add_fixit_replace (close_paren, "> (");
4637 richloc.add_fixit_insert_after (")");
4639 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4640 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4641 " *f = (f\xf0\x9f\x98\x82"
4642 " *)ptr->field\xcf\x80"
4643 ";\n"
4644 " ^~~~~~~~~~~\n"
4645 " ------------------\n"
4646 " const_cast<f\xf0\x9f\x98\x82"
4647 " *> (ptr->field\xcf\x80"
4648 ")\n",
4649 pp_formatted_text (dc.printer));
4651 /* Unit-test the line_corrections machinery. */
4652 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4653 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4654 ASSERT_EQ (column_range (14, 14),
4655 get_affected_range (&dc, hint_0, CU_BYTES));
4656 ASSERT_EQ (column_range (12, 12),
4657 get_affected_range (&dc, hint_0, CU_DISPLAY_COLS));
4658 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc, hint_0));
4659 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4660 ASSERT_EQ (column_range (22, 22),
4661 get_affected_range (&dc, hint_1, CU_BYTES));
4662 ASSERT_EQ (column_range (18, 18),
4663 get_affected_range (&dc, hint_1, CU_DISPLAY_COLS));
4664 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc, hint_1));
4665 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4666 ASSERT_EQ (column_range (35, 34),
4667 get_affected_range (&dc, hint_2, CU_BYTES));
4668 ASSERT_EQ (column_range (30, 29),
4669 get_affected_range (&dc, hint_2, CU_DISPLAY_COLS));
4670 ASSERT_EQ (column_range (30, 30), get_printed_columns (&dc, hint_2));
4672 /* Add each hint in turn to a line_corrections instance,
4673 and verify that they are consolidated into one correction instance
4674 as expected. */
4675 line_corrections lc (&dc, tmp.get_filename (), 1);
4677 /* The first replace hint by itself. */
4678 lc.add_hint (hint_0);
4679 ASSERT_EQ (1, lc.m_corrections.length ());
4680 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
4681 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4682 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4683 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4685 /* After the second replacement hint, they are printed together
4686 as a replacement (along with the text between them). */
4687 lc.add_hint (hint_1);
4688 ASSERT_EQ (1, lc.m_corrections.length ());
4689 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4690 lc.m_corrections[0]->m_text);
4691 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
4692 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4693 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4695 /* After the final insertion hint, they are all printed together
4696 as a replacement (along with the text between them). */
4697 lc.add_hint (hint_2);
4698 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4699 lc.m_corrections[0]->m_text);
4700 ASSERT_EQ (1, lc.m_corrections.length ());
4701 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
4702 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
4703 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
4706 /* Example where two are consolidated during printing. */
4708 test_diagnostic_context dc;
4709 rich_location richloc (line_table, expr);
4710 richloc.add_fixit_replace (open_paren, "CAST (");
4711 richloc.add_fixit_replace (close_paren, ") (");
4712 richloc.add_fixit_insert_after (")");
4714 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4715 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4716 " *f = (f\xf0\x9f\x98\x82"
4717 " *)ptr->field\xcf\x80"
4718 ";\n"
4719 " ^~~~~~~~~~~\n"
4720 " -\n"
4721 " CAST (-\n"
4722 " ) ( )\n",
4723 pp_formatted_text (dc.printer));
4726 /* Example where none are consolidated during printing. */
4728 test_diagnostic_context dc;
4729 rich_location richloc (line_table, expr);
4730 richloc.add_fixit_replace (open_paren, "CST (");
4731 richloc.add_fixit_replace (close_paren, ") (");
4732 richloc.add_fixit_insert_after (")");
4734 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4735 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4736 " *f = (f\xf0\x9f\x98\x82"
4737 " *)ptr->field\xcf\x80"
4738 ";\n"
4739 " ^~~~~~~~~~~\n"
4740 " -\n"
4741 " CST ( -\n"
4742 " ) ( )\n",
4743 pp_formatted_text (dc.printer));
4746 /* Example of deletion fix-it hints. */
4748 test_diagnostic_context dc;
4749 rich_location richloc (line_table, expr);
4750 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
4751 source_range victim = {open_paren, close_paren};
4752 richloc.add_fixit_remove (victim);
4754 /* This case is actually handled by fixit-consolidation,
4755 rather than by line_corrections. */
4756 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4758 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4759 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4760 " *f = (f\xf0\x9f\x98\x82"
4761 " *)ptr->field\xcf\x80"
4762 ";\n"
4763 " ^~~~~~~~~~~\n"
4764 " -------\n"
4765 " (bar\xf0\x9f\x98\x82"
4766 " *)\n",
4767 pp_formatted_text (dc.printer));
4770 /* Example of deletion fix-it hints that would overlap. */
4772 test_diagnostic_context dc;
4773 rich_location richloc (line_table, expr);
4774 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
4775 source_range victim = {expr_start, expr_finish};
4776 richloc.add_fixit_remove (victim);
4778 /* These fixits are not consolidated. */
4779 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4781 /* But the corrections are. */
4782 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4783 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4784 " *f = (f\xf0\x9f\x98\x82"
4785 " *)ptr->field\xcf\x80"
4786 ";\n"
4787 " ^~~~~~~~~~~\n"
4788 " ------------------\n"
4789 " (long\xf0\x9f\x98\x82"
4790 " *)(f\xf0\x9f\x98\x82"
4791 " *)\n",
4792 pp_formatted_text (dc.printer));
4795 /* Example of insertion fix-it hints that would overlap. */
4797 test_diagnostic_context dc;
4798 rich_location richloc (line_table, expr);
4799 richloc.add_fixit_insert_before
4800 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4801 richloc.add_fixit_insert_after (close_paren, "TEST");
4803 /* The first insertion is long enough that if printed naively,
4804 it would overlap with the second.
4805 Verify that they are printed as a single replacement. */
4806 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4807 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4808 " *f = (f\xf0\x9f\x98\x82"
4809 " *)ptr->field\xcf\x80"
4810 ";\n"
4811 " ^~~~~~~~~~~\n"
4812 " -------\n"
4813 " L\xf0\x9f\x98\x82"
4814 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4815 " *)TEST\n",
4816 pp_formatted_text (dc.printer));
4820 /* Verify that the line_corrections machinery correctly prints
4821 overlapping fixit-hints that have been added in the wrong
4822 order.
4823 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4825 static void
4826 test_overlapped_fixit_printing_2 (const line_table_case &case_)
4828 /* Create a tempfile and write some text to it.
4829 ...000000000111111111122222222223333333333.
4830 ...123456789012345678901234567890123456789. */
4831 const char *content
4832 = ("int a5[][0][0] = { 1, 2 };\n");
4833 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4834 line_table_test ltt (case_);
4836 const line_map_ordinary *ord_map
4837 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4838 tmp.get_filename (), 0));
4840 linemap_line_start (line_table, 1, 100);
4842 const location_t final_line_end
4843 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
4845 /* Don't attempt to run the tests if column data might be unavailable. */
4846 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4847 return;
4849 const location_t col_1
4850 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4851 const location_t col_20
4852 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
4853 const location_t col_21
4854 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
4855 const location_t col_23
4856 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4857 const location_t col_25
4858 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
4860 /* Two insertions, in the wrong order. */
4862 test_diagnostic_context dc;
4864 rich_location richloc (line_table, col_20);
4865 richloc.add_fixit_insert_before (col_23, "{");
4866 richloc.add_fixit_insert_before (col_21, "}");
4868 /* These fixits should be accepted; they can't be consolidated. */
4869 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4870 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4871 ASSERT_EQ (column_range (23, 22),
4872 get_affected_range (&dc, hint_0, CU_BYTES));
4873 ASSERT_EQ (column_range (23, 23), get_printed_columns (&dc, hint_0));
4874 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4875 ASSERT_EQ (column_range (21, 20),
4876 get_affected_range (&dc, hint_1, CU_BYTES));
4877 ASSERT_EQ (column_range (21, 21), get_printed_columns (&dc, hint_1));
4879 /* Verify that they're printed correctly. */
4880 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4881 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4882 " ^\n"
4883 " } {\n",
4884 pp_formatted_text (dc.printer));
4887 /* Various overlapping insertions, some occurring "out of order"
4888 (reproducing the fix-it hints from PR c/81405). */
4890 test_diagnostic_context dc;
4891 rich_location richloc (line_table, col_20);
4893 richloc.add_fixit_insert_before (col_20, "{{");
4894 richloc.add_fixit_insert_before (col_21, "}}");
4895 richloc.add_fixit_insert_before (col_23, "{");
4896 richloc.add_fixit_insert_before (col_21, "}");
4897 richloc.add_fixit_insert_before (col_23, "{{");
4898 richloc.add_fixit_insert_before (col_25, "}");
4899 richloc.add_fixit_insert_before (col_21, "}");
4900 richloc.add_fixit_insert_before (col_1, "{");
4901 richloc.add_fixit_insert_before (col_25, "}");
4902 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4903 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4904 " ^\n"
4905 " { -----\n"
4906 " {{1}}}}, {{{2 }}\n",
4907 pp_formatted_text (dc.printer));
4911 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4913 static void
4914 test_fixit_insert_containing_newline (const line_table_case &case_)
4916 /* Create a tempfile and write some text to it.
4917 .........................0000000001111111.
4918 .........................1234567890123456. */
4919 const char *old_content = (" case 'a':\n" /* line 1. */
4920 " x = a;\n" /* line 2. */
4921 " case 'b':\n" /* line 3. */
4922 " x = b;\n");/* line 4. */
4924 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4925 line_table_test ltt (case_);
4926 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
4928 location_t case_start = linemap_position_for_column (line_table, 5);
4929 location_t case_finish = linemap_position_for_column (line_table, 13);
4930 location_t case_loc = make_location (case_start, case_start, case_finish);
4931 location_t line_start = linemap_position_for_column (line_table, 1);
4933 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4934 return;
4936 /* Add a "break;" on a line by itself before line 3 i.e. before
4937 column 1 of line 3. */
4939 rich_location richloc (line_table, case_loc);
4940 richloc.add_fixit_insert_before (line_start, " break;\n");
4942 /* Without line numbers. */
4944 test_diagnostic_context dc;
4945 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4946 ASSERT_STREQ (" x = a;\n"
4947 "+ break;\n"
4948 " case 'b':\n"
4949 " ^~~~~~~~~\n",
4950 pp_formatted_text (dc.printer));
4953 /* With line numbers. */
4955 test_diagnostic_context dc;
4956 dc.show_line_numbers_p = true;
4957 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4958 ASSERT_STREQ (" 2 | x = a;\n"
4959 " +++ |+ break;\n"
4960 " 3 | case 'b':\n"
4961 " | ^~~~~~~~~\n",
4962 pp_formatted_text (dc.printer));
4966 /* Verify that attempts to add text with a newline fail when the
4967 insertion point is *not* at the start of a line. */
4969 rich_location richloc (line_table, case_loc);
4970 richloc.add_fixit_insert_before (case_start, "break;\n");
4971 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4972 test_diagnostic_context dc;
4973 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4974 ASSERT_STREQ (" case 'b':\n"
4975 " ^~~~~~~~~\n",
4976 pp_formatted_text (dc.printer));
4980 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4981 of the file, where the fix-it is printed in a different line-span
4982 to the primary range of the diagnostic. */
4984 static void
4985 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
4987 /* Create a tempfile and write some text to it.
4988 .........................0000000001111111.
4989 .........................1234567890123456. */
4990 const char *old_content = ("test (int ch)\n" /* line 1. */
4991 "{\n" /* line 2. */
4992 " putchar (ch);\n" /* line 3. */
4993 "}\n"); /* line 4. */
4995 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4996 line_table_test ltt (case_);
4998 const line_map_ordinary *ord_map = linemap_check_ordinary
4999 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5000 linemap_line_start (line_table, 1, 100);
5002 /* The primary range is the "putchar" token. */
5003 location_t putchar_start
5004 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5005 location_t putchar_finish
5006 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5007 location_t putchar_loc
5008 = make_location (putchar_start, putchar_start, putchar_finish);
5009 rich_location richloc (line_table, putchar_loc);
5011 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5012 location_t file_start
5013 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5014 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5016 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5017 return;
5020 test_diagnostic_context dc;
5021 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5022 ASSERT_STREQ ("FILENAME:1:1:\n"
5023 "+#include <stdio.h>\n"
5024 " test (int ch)\n"
5025 "FILENAME:3:2:\n"
5026 " putchar (ch);\n"
5027 " ^~~~~~~\n",
5028 pp_formatted_text (dc.printer));
5031 /* With line-numbering, the line spans are close enough to be
5032 consolidated, since it makes little sense to skip line 2. */
5034 test_diagnostic_context dc;
5035 dc.show_line_numbers_p = true;
5036 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5037 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5038 " 1 | test (int ch)\n"
5039 " 2 | {\n"
5040 " 3 | putchar (ch);\n"
5041 " | ^~~~~~~\n",
5042 pp_formatted_text (dc.printer));
5046 /* Replacement fix-it hint containing a newline.
5047 This will fail, as newlines are only supported when inserting at the
5048 beginning of a line. */
5050 static void
5051 test_fixit_replace_containing_newline (const line_table_case &case_)
5053 /* Create a tempfile and write some text to it.
5054 .........................0000000001111.
5055 .........................1234567890123. */
5056 const char *old_content = "foo = bar ();\n";
5058 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5059 line_table_test ltt (case_);
5060 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5062 /* Replace the " = " with "\n = ", as if we were reformatting an
5063 overly long line. */
5064 location_t start = linemap_position_for_column (line_table, 4);
5065 location_t finish = linemap_position_for_column (line_table, 6);
5066 location_t loc = linemap_position_for_column (line_table, 13);
5067 rich_location richloc (line_table, loc);
5068 source_range range = source_range::from_locations (start, finish);
5069 richloc.add_fixit_replace (range, "\n =");
5071 /* Arbitrary newlines are not yet supported within fix-it hints, so
5072 the fix-it should not be displayed. */
5073 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5075 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5076 return;
5078 test_diagnostic_context dc;
5079 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5080 ASSERT_STREQ (" foo = bar ();\n"
5081 " ^\n",
5082 pp_formatted_text (dc.printer));
5085 /* Fix-it hint, attempting to delete a newline.
5086 This will fail, as we currently only support fix-it hints that
5087 affect one line at a time. */
5089 static void
5090 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5092 /* Create a tempfile and write some text to it.
5093 ..........................0000000001111.
5094 ..........................1234567890123. */
5095 const char *old_content = ("foo = bar (\n"
5096 " );\n");
5098 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5099 line_table_test ltt (case_);
5100 const line_map_ordinary *ord_map = linemap_check_ordinary
5101 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5102 linemap_line_start (line_table, 1, 100);
5104 /* Attempt to delete the " (\n...)". */
5105 location_t start
5106 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5107 location_t caret
5108 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5109 location_t finish
5110 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5111 location_t loc = make_location (caret, start, finish);
5112 rich_location richloc (line_table, loc);
5113 richloc. add_fixit_remove ();
5115 /* Fix-it hints that affect more than one line are not yet supported, so
5116 the fix-it should not be displayed. */
5117 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5119 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5120 return;
5122 test_diagnostic_context dc;
5123 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5124 ASSERT_STREQ (" foo = bar (\n"
5125 " ~^\n"
5126 " );\n"
5127 " ~ \n",
5128 pp_formatted_text (dc.printer));
5131 static void
5132 test_tab_expansion (const line_table_case &case_)
5134 /* Create a tempfile and write some text to it. This example uses a tabstop
5135 of 8, as the column numbers attempt to indicate:
5137 .....................000.01111111111.22222333333 display
5138 .....................123.90123456789.56789012345 columns */
5139 const char *content = " \t This: `\t' is a tab.\n";
5140 /* ....................000 00000011111 11111222222 byte
5141 ....................123 45678901234 56789012345 columns */
5143 const int tabstop = 8;
5144 const int first_non_ws_byte_col = 7;
5145 const int right_quote_byte_col = 15;
5146 const int last_byte_col = 25;
5147 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, tabstop));
5149 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5150 line_table_test ltt (case_);
5151 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5153 /* Don't attempt to run the tests if column data might be unavailable. */
5154 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5155 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5156 return;
5158 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5159 into 11 spaces. Recall that print_line() also puts one space before
5160 everything too. */
5162 test_diagnostic_context dc;
5163 dc.tabstop = tabstop;
5164 rich_location richloc (line_table,
5165 linemap_position_for_column (line_table,
5166 first_non_ws_byte_col));
5167 layout test_layout (&dc, &richloc, DK_ERROR);
5168 test_layout.print_line (1);
5169 ASSERT_STREQ (" This: ` ' is a tab.\n"
5170 " ^\n",
5171 pp_formatted_text (dc.printer));
5174 /* Confirm the display width was tracked correctly across the internal tab
5175 as well. */
5177 test_diagnostic_context dc;
5178 dc.tabstop = tabstop;
5179 rich_location richloc (line_table,
5180 linemap_position_for_column (line_table,
5181 right_quote_byte_col));
5182 layout test_layout (&dc, &richloc, DK_ERROR);
5183 test_layout.print_line (1);
5184 ASSERT_STREQ (" This: ` ' is a tab.\n"
5185 " ^\n",
5186 pp_formatted_text (dc.printer));
5190 /* Verify that line numbers are correctly printed for the case of
5191 a multiline range in which the width of the line numbers changes
5192 (e.g. from "9" to "10"). */
5194 static void
5195 test_line_numbers_multiline_range ()
5197 /* Create a tempfile and write some text to it. */
5198 pretty_printer pp;
5199 for (int i = 0; i < 20; i++)
5200 /* .........0000000001111111.
5201 .............1234567890123456. */
5202 pp_printf (&pp, "this is line %i\n", i + 1);
5203 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5204 line_table_test ltt;
5206 const line_map_ordinary *ord_map = linemap_check_ordinary
5207 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5208 linemap_line_start (line_table, 1, 100);
5210 /* Create a multi-line location, starting at the "line" of line 9, with
5211 a caret on the "is" of line 10, finishing on the "this" line 11. */
5213 location_t start
5214 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5215 location_t caret
5216 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5217 location_t finish
5218 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5219 location_t loc = make_location (caret, start, finish);
5221 test_diagnostic_context dc;
5222 dc.show_line_numbers_p = true;
5223 dc.min_margin_width = 0;
5224 gcc_rich_location richloc (loc);
5225 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5226 ASSERT_STREQ (" 9 | this is line 9\n"
5227 " | ~~~~~~\n"
5228 "10 | this is line 10\n"
5229 " | ~~~~~^~~~~~~~~~\n"
5230 "11 | this is line 11\n"
5231 " | ~~~~ \n",
5232 pp_formatted_text (dc.printer));
5235 /* Run all of the selftests within this file. */
5237 void
5238 diagnostic_show_locus_c_tests ()
5240 test_line_span ();
5242 test_layout_range_for_single_point ();
5243 test_layout_range_for_single_line ();
5244 test_layout_range_for_multiple_lines ();
5246 for_each_line_table_case (test_layout_x_offset_display_utf8);
5247 for_each_line_table_case (test_layout_x_offset_display_tab);
5249 test_get_line_bytes_without_trailing_whitespace ();
5251 test_diagnostic_show_locus_unknown_location ();
5253 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5254 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5255 for_each_line_table_case (test_add_location_if_nearby);
5256 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5257 for_each_line_table_case (test_fixit_consolidation);
5258 for_each_line_table_case (test_overlapped_fixit_printing);
5259 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5260 for_each_line_table_case (test_overlapped_fixit_printing_2);
5261 for_each_line_table_case (test_fixit_insert_containing_newline);
5262 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5263 for_each_line_table_case (test_fixit_replace_containing_newline);
5264 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5265 for_each_line_table_case (test_tab_expansion);
5267 test_line_numbers_multiline_range ();
5270 } // namespace selftest
5272 #endif /* #if CHECKING_P */
5274 #if __GNUC__ >= 10
5275 # pragma GCC diagnostic pop
5276 #endif