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