Emit .note.GNU-stack for hard-float linux targets.
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob4618b4edb7d19846e2ce74ad07fc26212d66e761
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2020 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 #include "cpplib.h"
35 #ifdef HAVE_TERMIOS_H
36 # include <termios.h>
37 #endif
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
41 #endif
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
45 #if __GNUC__ >= 10
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
48 #endif
50 /* Classes for rendering source code and diagnostics, within an
51 anonymous namespace.
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
55 namespace {
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
59 this point. */
61 struct point_state
63 int range_idx;
64 bool draw_caret_p;
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
70 - normal text
71 - range 0 (the "primary location")
72 - range 1
73 - range 2
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
83 class colorizer
85 public:
86 colorizer (diagnostic_context *context,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
90 void set_range (int range_idx)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
97 set_state (0);
98 else
99 set_state (range_idx);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
105 private:
106 void set_state (int state);
107 void begin_state (int state);
108 void finish_state (int state);
109 const char *get_color_by_name (const char *);
111 private:
112 static const int STATE_NORMAL_TEXT = -1;
113 static const int STATE_FIXIT_INSERT = -2;
114 static const int STATE_FIXIT_DELETE = -3;
116 diagnostic_context *m_context;
117 diagnostic_t m_diagnostic_kind;
118 int m_current_state;
119 const char *m_range1;
120 const char *m_range2;
121 const char *m_fixit_insert;
122 const char *m_fixit_delete;
123 const char *m_stop_color;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
162 enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col : public expanded_location
177 public:
178 exploc_with_display_col (const expanded_location &exploc)
179 : expanded_location (exploc),
180 m_display_col (location_compute_display_column (exploc)) {}
182 int m_display_col;
186 /* A point within a layout_range; similar to an exploc_with_display_col,
187 but after filtering on file. */
189 class layout_point
191 public:
192 layout_point (const expanded_location &exploc)
193 : m_line (exploc.line)
195 m_columns[CU_BYTES] = exploc.column;
196 m_columns[CU_DISPLAY_COLS] = location_compute_display_column (exploc);
199 linenum_type m_line;
200 int m_columns[CU_NUM_UNITS];
203 /* A class for use by "class layout" below: a filtered location_range. */
205 class layout_range
207 public:
208 layout_range (const expanded_location *start_exploc,
209 const expanded_location *finish_exploc,
210 enum range_display_kind range_display_kind,
211 const expanded_location *caret_exploc,
212 unsigned original_idx,
213 const range_label *label);
215 bool contains_point (linenum_type row, int column,
216 enum column_unit col_unit) const;
217 bool intersects_line_p (linenum_type row) const;
219 layout_point m_start;
220 layout_point m_finish;
221 enum range_display_kind m_range_display_kind;
222 layout_point m_caret;
223 unsigned m_original_idx;
224 const range_label *m_label;
227 /* A struct for use by layout::print_source_line for telling
228 layout::print_annotation_line the extents of the source line that
229 it printed, so that underlines can be clipped appropriately. */
231 struct line_bounds
233 int m_first_non_ws;
234 int m_last_non_ws;
236 void convert_to_display_cols (char_span line)
238 m_first_non_ws = cpp_byte_column_to_display_column (line.get_buffer (),
239 line.length (),
240 m_first_non_ws);
242 m_last_non_ws = cpp_byte_column_to_display_column (line.get_buffer (),
243 line.length (),
244 m_last_non_ws);
248 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
249 or "line 23"). During the layout ctor, layout::calculate_line_spans
250 splits the pertinent source lines into a list of disjoint line_span
251 instances (e.g. lines 5-10, lines 15-20, line 23). */
253 class line_span
255 public:
256 line_span (linenum_type first_line, linenum_type last_line)
257 : m_first_line (first_line), m_last_line (last_line)
259 gcc_assert (first_line <= last_line);
261 linenum_type get_first_line () const { return m_first_line; }
262 linenum_type get_last_line () const { return m_last_line; }
264 bool contains_line_p (linenum_type line) const
266 return line >= m_first_line && line <= m_last_line;
269 static int comparator (const void *p1, const void *p2)
271 const line_span *ls1 = (const line_span *)p1;
272 const line_span *ls2 = (const line_span *)p2;
273 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
274 if (first_line_cmp)
275 return first_line_cmp;
276 return compare (ls1->m_last_line, ls2->m_last_line);
279 linenum_type m_first_line;
280 linenum_type m_last_line;
283 #if CHECKING_P
285 /* Selftests for line_span. */
287 static void
288 test_line_span ()
290 line_span line_one (1, 1);
291 ASSERT_EQ (1, line_one.get_first_line ());
292 ASSERT_EQ (1, line_one.get_last_line ());
293 ASSERT_FALSE (line_one.contains_line_p (0));
294 ASSERT_TRUE (line_one.contains_line_p (1));
295 ASSERT_FALSE (line_one.contains_line_p (2));
297 line_span lines_1_to_3 (1, 3);
298 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
299 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
300 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
301 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
303 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
304 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
305 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
307 /* A linenum > 2^31. */
308 const linenum_type LARGEST_LINE = 0xffffffff;
309 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
310 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
311 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
313 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
314 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
317 #endif /* #if CHECKING_P */
319 /* A class to control the overall layout when printing a diagnostic.
321 The layout is determined within the constructor.
322 It is then printed by repeatedly calling the "print_source_line",
323 "print_annotation_line" and "print_any_fixits" methods.
325 We assume we have disjoint ranges. */
327 class layout
329 public:
330 layout (diagnostic_context *context,
331 rich_location *richloc,
332 diagnostic_t diagnostic_kind);
334 bool maybe_add_location_range (const location_range *loc_range,
335 unsigned original_idx,
336 bool restrict_to_current_line_spans);
338 int get_num_line_spans () const { return m_line_spans.length (); }
339 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
341 int get_linenum_width () const { return m_linenum_width; }
342 int get_x_offset_display () const { return m_x_offset_display; }
344 void print_gap_in_line_numbering ();
345 bool print_heading_for_line_span_index_p (int line_span_idx) const;
347 expanded_location get_expanded_location (const line_span *) const;
349 void print_line (linenum_type row);
351 private:
352 bool will_show_line_p (linenum_type row) const;
353 void print_leading_fixits (linenum_type row);
354 void print_source_line (linenum_type row, const char *line, int line_bytes,
355 line_bounds *lbounds_out);
356 bool should_print_annotation_line_p (linenum_type row) const;
357 void start_annotation_line (char margin_char = ' ') const;
358 void print_annotation_line (linenum_type row, const line_bounds lbounds);
359 void print_any_labels (linenum_type row);
360 void print_trailing_fixits (linenum_type row);
362 bool annotation_line_showed_range_p (linenum_type line, int start_column,
363 int finish_column) const;
364 void show_ruler (int max_column) const;
366 bool validate_fixit_hint_p (const fixit_hint *hint);
368 void calculate_line_spans ();
369 void calculate_linenum_width ();
370 void calculate_x_offset_display ();
372 void print_newline ();
374 bool
375 get_state_at_point (/* Inputs. */
376 linenum_type row, int column,
377 int first_non_ws, int last_non_ws,
378 enum column_unit col_unit,
379 /* Outputs. */
380 point_state *out_state);
383 get_x_bound_for_row (linenum_type row, int caret_column,
384 int last_non_ws);
386 void
387 move_to_column (int *column, int dest_column, bool add_left_margin);
389 private:
390 diagnostic_context *m_context;
391 pretty_printer *m_pp;
392 location_t m_primary_loc;
393 exploc_with_display_col m_exploc;
394 colorizer m_colorizer;
395 bool m_colorize_source_p;
396 bool m_show_labels_p;
397 bool m_show_line_numbers_p;
398 bool m_diagnostic_path_p;
399 auto_vec <layout_range> m_layout_ranges;
400 auto_vec <const fixit_hint *> m_fixit_hints;
401 auto_vec <line_span> m_line_spans;
402 int m_linenum_width;
403 int m_x_offset_display;
406 /* Implementation of "class colorizer". */
408 /* The constructor for "colorizer". Lookup and store color codes for the
409 different kinds of things we might need to print. */
411 colorizer::colorizer (diagnostic_context *context,
412 diagnostic_t diagnostic_kind) :
413 m_context (context),
414 m_diagnostic_kind (diagnostic_kind),
415 m_current_state (STATE_NORMAL_TEXT)
417 m_range1 = get_color_by_name ("range1");
418 m_range2 = get_color_by_name ("range2");
419 m_fixit_insert = get_color_by_name ("fixit-insert");
420 m_fixit_delete = get_color_by_name ("fixit-delete");
421 m_stop_color = colorize_stop (pp_show_color (context->printer));
424 /* The destructor for "colorize". If colorization is on, print a code to
425 turn it off. */
427 colorizer::~colorizer ()
429 finish_state (m_current_state);
432 /* Update state, printing color codes if necessary if there's a state
433 change. */
435 void
436 colorizer::set_state (int new_state)
438 if (m_current_state != new_state)
440 finish_state (m_current_state);
441 m_current_state = new_state;
442 begin_state (new_state);
446 /* Turn on any colorization for STATE. */
448 void
449 colorizer::begin_state (int state)
451 switch (state)
453 case STATE_NORMAL_TEXT:
454 break;
456 case STATE_FIXIT_INSERT:
457 pp_string (m_context->printer, m_fixit_insert);
458 break;
460 case STATE_FIXIT_DELETE:
461 pp_string (m_context->printer, m_fixit_delete);
462 break;
464 case 0:
465 /* Make range 0 be the same color as the "kind" text
466 (error vs warning vs note). */
467 pp_string
468 (m_context->printer,
469 colorize_start (pp_show_color (m_context->printer),
470 diagnostic_get_color_for_kind (m_diagnostic_kind)));
471 break;
473 case 1:
474 pp_string (m_context->printer, m_range1);
475 break;
477 case 2:
478 pp_string (m_context->printer, m_range2);
479 break;
481 default:
482 /* For ranges beyond 2, alternate between color 1 and color 2. */
484 gcc_assert (state > 2);
485 pp_string (m_context->printer,
486 state % 2 ? m_range1 : m_range2);
488 break;
492 /* Turn off any colorization for STATE. */
494 void
495 colorizer::finish_state (int state)
497 if (state != STATE_NORMAL_TEXT)
498 pp_string (m_context->printer, m_stop_color);
501 /* Get the color code for NAME (or the empty string if
502 colorization is disabled). */
504 const char *
505 colorizer::get_color_by_name (const char *name)
507 return colorize_start (pp_show_color (m_context->printer), name);
510 /* Implementation of class layout_range. */
512 /* The constructor for class layout_range.
513 Initialize various layout_point fields from expanded_location
514 equivalents; we've already filtered on file. */
516 layout_range::layout_range (const expanded_location *start_exploc,
517 const expanded_location *finish_exploc,
518 enum range_display_kind range_display_kind,
519 const expanded_location *caret_exploc,
520 unsigned original_idx,
521 const range_label *label)
522 : m_start (*start_exploc),
523 m_finish (*finish_exploc),
524 m_range_display_kind (range_display_kind),
525 m_caret (*caret_exploc),
526 m_original_idx (original_idx),
527 m_label (label)
531 /* Is (column, row) within the given range?
532 We've already filtered on the file.
534 Ranges are closed (both limits are within the range).
536 Example A: a single-line range:
537 start: (col=22, line=2)
538 finish: (col=38, line=2)
540 |00000011111111112222222222333333333344444444444
541 |34567890123456789012345678901234567890123456789
542 --+-----------------------------------------------
543 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
544 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
545 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
547 Example B: a multiline range with
548 start: (col=14, line=3)
549 finish: (col=08, line=5)
551 |00000011111111112222222222333333333344444444444
552 |34567890123456789012345678901234567890123456789
553 --+-----------------------------------------------
554 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
555 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
556 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
557 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
558 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
559 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
560 --+-----------------------------------------------
562 Legend:
563 - 'b' indicates a point *before* the range
564 - 'S' indicates the start of the range
565 - 'w' indicates a point within the range
566 - 'F' indicates the finish of the range (which is
567 within it).
568 - 'a' indicates a subsequent point *after* the range.
570 COL_UNIT controls whether we check the byte column or
571 the display column; one or the other is more convenient
572 depending on the context. */
574 bool
575 layout_range::contains_point (linenum_type row, int column,
576 enum column_unit col_unit) const
578 gcc_assert (m_start.m_line <= m_finish.m_line);
579 /* ...but the equivalent isn't true for the columns;
580 consider example B in the comment above. */
582 if (row < m_start.m_line)
583 /* Points before the first line of the range are
584 outside it (corresponding to line 01 in example A
585 and lines 01 and 02 in example B above). */
586 return false;
588 if (row == m_start.m_line)
589 /* On same line as start of range (corresponding
590 to line 02 in example A and line 03 in example B). */
592 if (column < m_start.m_columns[col_unit])
593 /* Points on the starting line of the range, but
594 before the column in which it begins. */
595 return false;
597 if (row < m_finish.m_line)
598 /* This is a multiline range; the point
599 is within it (corresponds to line 03 in example B
600 from column 14 onwards) */
601 return true;
602 else
604 /* This is a single-line range. */
605 gcc_assert (row == m_finish.m_line);
606 return column <= m_finish.m_columns[col_unit];
610 /* The point is in a line beyond that containing the
611 start of the range: lines 03 onwards in example A,
612 and lines 04 onwards in example B. */
613 gcc_assert (row > m_start.m_line);
615 if (row > m_finish.m_line)
616 /* The point is beyond the final line of the range
617 (lines 03 onwards in example A, and lines 06 onwards
618 in example B). */
619 return false;
621 if (row < m_finish.m_line)
623 /* The point is in a line that's fully within a multiline
624 range (e.g. line 04 in example B). */
625 gcc_assert (m_start.m_line < m_finish.m_line);
626 return true;
629 gcc_assert (row == m_finish.m_line);
631 return column <= m_finish.m_columns[col_unit];
634 /* Does this layout_range contain any part of line ROW? */
636 bool
637 layout_range::intersects_line_p (linenum_type row) const
639 gcc_assert (m_start.m_line <= m_finish.m_line);
640 if (row < m_start.m_line)
641 return false;
642 if (row > m_finish.m_line)
643 return false;
644 return true;
647 #if CHECKING_P
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 (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
666 &start_exploc, 0, NULL);
669 /* Selftests for layout_range::contains_point and
670 layout_range::intersects_line_p. */
672 /* Selftest for layout_range, where the layout_range
673 is a range with start==end i.e. a single point. */
675 static void
676 test_layout_range_for_single_point ()
678 layout_range point = make_range (7, 10, 7, 10);
680 /* Tests for layout_range::contains_point. */
682 for (int i = 0; i != CU_NUM_UNITS; ++i)
684 const enum column_unit col_unit = (enum column_unit) i;
686 /* Before the line. */
687 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
689 /* On the line, but before start. */
690 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
692 /* At the point. */
693 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
695 /* On the line, after the point. */
696 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
698 /* After the line. */
699 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
702 /* Tests for layout_range::intersects_line_p. */
703 ASSERT_FALSE (point.intersects_line_p (6));
704 ASSERT_TRUE (point.intersects_line_p (7));
705 ASSERT_FALSE (point.intersects_line_p (8));
708 /* Selftest for layout_range, where the layout_range
709 is the single-line range shown as "Example A" above. */
711 static void
712 test_layout_range_for_single_line ()
714 layout_range example_a = make_range (2, 22, 2, 38);
716 /* Tests for layout_range::contains_point. */
718 for (int i = 0; i != CU_NUM_UNITS; ++i)
720 const enum column_unit col_unit = (enum column_unit) i;
722 /* Before the line. */
723 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
725 /* On the line, but before start. */
726 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
728 /* On the line, at the start. */
729 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
731 /* On the line, within the range. */
732 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
734 /* On the line, at the end. */
735 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
737 /* On the line, after the end. */
738 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
740 /* After the line. */
741 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
744 /* Tests for layout_range::intersects_line_p. */
745 ASSERT_FALSE (example_a.intersects_line_p (1));
746 ASSERT_TRUE (example_a.intersects_line_p (2));
747 ASSERT_FALSE (example_a.intersects_line_p (3));
750 /* Selftest for layout_range, where the layout_range
751 is the multi-line range shown as "Example B" above. */
753 static void
754 test_layout_range_for_multiple_lines ()
756 layout_range example_b = make_range (3, 14, 5, 8);
758 /* Tests for layout_range::contains_point. */
760 for (int i = 0; i != CU_NUM_UNITS; ++i)
762 const enum column_unit col_unit = (enum column_unit) i;
764 /* Before first line. */
765 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
767 /* On the first line, but before start. */
768 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
770 /* At the start. */
771 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
773 /* On the first line, within the range. */
774 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
776 /* On an interior line.
777 The column number should not matter; try various boundary
778 values. */
779 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
780 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
781 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
782 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
783 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
784 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
785 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
787 /* On the final line, before the end. */
788 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
790 /* On the final line, at the end. */
791 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
793 /* On the final line, after the end. */
794 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
796 /* After the line. */
797 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
800 /* Tests for layout_range::intersects_line_p. */
801 ASSERT_FALSE (example_b.intersects_line_p (2));
802 ASSERT_TRUE (example_b.intersects_line_p (3));
803 ASSERT_TRUE (example_b.intersects_line_p (4));
804 ASSERT_TRUE (example_b.intersects_line_p (5));
805 ASSERT_FALSE (example_b.intersects_line_p (6));
808 #endif /* #if CHECKING_P */
810 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
811 (still in bytes, not display cols) without any trailing whitespace. */
813 static int
814 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
816 int result = line_bytes;
817 while (result > 0)
819 char ch = line[result - 1];
820 if (ch == ' ' || ch == '\t' || ch == '\r')
821 result--;
822 else
823 break;
825 gcc_assert (result >= 0);
826 gcc_assert (result <= line_bytes);
827 gcc_assert (result == 0 ||
828 (line[result - 1] != ' '
829 && line[result -1] != '\t'
830 && line[result -1] != '\r'));
831 return result;
834 #if CHECKING_P
836 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
838 static void
839 assert_eq (const char *line, int expected_bytes)
841 int actual_value
842 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
843 ASSERT_EQ (actual_value, expected_bytes);
846 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
847 various inputs. It is not required to handle newlines. */
849 static void
850 test_get_line_bytes_without_trailing_whitespace ()
852 assert_eq ("", 0);
853 assert_eq (" ", 0);
854 assert_eq ("\t", 0);
855 assert_eq ("\r", 0);
856 assert_eq ("hello world", 11);
857 assert_eq ("hello world ", 11);
858 assert_eq ("hello world \t\t ", 11);
859 assert_eq ("hello world\r", 11);
862 #endif /* #if CHECKING_P */
864 /* Helper function for layout's ctor, for sanitizing locations relative
865 to the primary location within a diagnostic.
867 Compare LOC_A and LOC_B to see if it makes sense to print underlines
868 connecting their expanded locations. Doing so is only guaranteed to
869 make sense if the locations share the same macro expansion "history"
870 i.e. they can be traced through the same macro expansions, eventually
871 reaching an ordinary map.
873 This may be too strong a condition, but it effectively sanitizes
874 PR c++/70105, which has an example of printing an expression where the
875 final location of the expression is in a different macro, which
876 erroneously was leading to hundreds of lines of irrelevant source
877 being printed. */
879 static bool
880 compatible_locations_p (location_t loc_a, location_t loc_b)
882 if (IS_ADHOC_LOC (loc_a))
883 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
884 if (IS_ADHOC_LOC (loc_b))
885 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
887 /* If either location is one of the special locations outside of a
888 linemap, they are only compatible if they are equal. */
889 if (loc_a < RESERVED_LOCATION_COUNT
890 || loc_b < RESERVED_LOCATION_COUNT)
891 return loc_a == loc_b;
893 const line_map *map_a = linemap_lookup (line_table, loc_a);
894 linemap_assert (map_a);
896 const line_map *map_b = linemap_lookup (line_table, loc_b);
897 linemap_assert (map_b);
899 /* Are they within the same map? */
900 if (map_a == map_b)
902 /* Are both within the same macro expansion? */
903 if (linemap_macro_expansion_map_p (map_a))
905 /* Expand each location towards the spelling location, and
906 recurse. */
907 const line_map_macro *macro_map = linemap_check_macro (map_a);
908 location_t loc_a_toward_spelling
909 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
910 macro_map,
911 loc_a);
912 location_t loc_b_toward_spelling
913 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
914 macro_map,
915 loc_b);
916 return compatible_locations_p (loc_a_toward_spelling,
917 loc_b_toward_spelling);
920 /* Otherwise they are within the same ordinary map. */
921 return true;
923 else
925 /* Within different maps. */
927 /* If either is within a macro expansion, they are incompatible. */
928 if (linemap_macro_expansion_map_p (map_a)
929 || linemap_macro_expansion_map_p (map_b))
930 return false;
932 /* Within two different ordinary maps; they are compatible iff they
933 are in the same file. */
934 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
935 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
936 return ord_map_a->to_file == ord_map_b->to_file;
940 /* Comparator for sorting fix-it hints. */
942 static int
943 fixit_cmp (const void *p_a, const void *p_b)
945 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
946 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
947 return hint_a->get_start_loc () - hint_b->get_start_loc ();
950 /* Implementation of class layout. */
952 /* Constructor for class layout.
954 Filter the ranges from the rich_location to those that we can
955 sanely print, populating m_layout_ranges and m_fixit_hints.
956 Determine the range of lines that we will print, splitting them
957 up into an ordered list of disjoint spans of contiguous line numbers.
958 Determine m_x_offset_display, to ensure that the primary caret
959 will fit within the max_width provided by the diagnostic_context. */
961 layout::layout (diagnostic_context * context,
962 rich_location *richloc,
963 diagnostic_t diagnostic_kind)
964 : m_context (context),
965 m_pp (context->printer),
966 m_primary_loc (richloc->get_range (0)->m_loc),
967 m_exploc (richloc->get_expanded_location (0)),
968 m_colorizer (context, diagnostic_kind),
969 m_colorize_source_p (context->colorize_source_p),
970 m_show_labels_p (context->show_labels_p),
971 m_show_line_numbers_p (context->show_line_numbers_p),
972 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
973 m_layout_ranges (richloc->get_num_locations ()),
974 m_fixit_hints (richloc->get_num_fixit_hints ()),
975 m_line_spans (1 + richloc->get_num_locations ()),
976 m_linenum_width (0),
977 m_x_offset_display (0)
979 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
981 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
982 Ignore any ranges that are awkward to handle. */
983 const location_range *loc_range = richloc->get_range (idx);
984 maybe_add_location_range (loc_range, idx, false);
987 /* Populate m_fixit_hints, filtering to only those that are in the
988 same file. */
989 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
991 const fixit_hint *hint = richloc->get_fixit_hint (i);
992 if (validate_fixit_hint_p (hint))
993 m_fixit_hints.safe_push (hint);
996 /* Sort m_fixit_hints. */
997 m_fixit_hints.qsort (fixit_cmp);
999 /* Populate the indicated members. */
1000 calculate_line_spans ();
1001 calculate_linenum_width ();
1002 calculate_x_offset_display ();
1004 if (context->show_ruler_p)
1005 show_ruler (m_x_offset_display + m_context->caret_max_width);
1009 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1010 those that we can sanely print.
1012 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1013 (for use as extrinsic state by label ranges FIXME).
1015 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1016 filtered against this layout instance's current line spans: it
1017 will only be added if the location is fully within the lines
1018 already specified by other locations.
1020 Return true iff LOC_RANGE was added. */
1022 bool
1023 layout::maybe_add_location_range (const location_range *loc_range,
1024 unsigned original_idx,
1025 bool restrict_to_current_line_spans)
1027 gcc_assert (loc_range);
1029 /* Split the "range" into caret and range information. */
1030 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1032 /* Expand the various locations. */
1033 expanded_location start
1034 = linemap_client_expand_location_to_spelling_point
1035 (src_range.m_start, LOCATION_ASPECT_START);
1036 expanded_location finish
1037 = linemap_client_expand_location_to_spelling_point
1038 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1039 expanded_location caret
1040 = linemap_client_expand_location_to_spelling_point
1041 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1043 /* If any part of the range isn't in the same file as the primary
1044 location of this diagnostic, ignore the range. */
1045 if (start.file != m_exploc.file)
1046 return false;
1047 if (finish.file != m_exploc.file)
1048 return false;
1049 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1050 if (caret.file != m_exploc.file)
1051 return false;
1053 /* Sanitize the caret location for non-primary ranges. */
1054 if (m_layout_ranges.length () > 0)
1055 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1056 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1057 /* Discard any non-primary ranges that can't be printed
1058 sanely relative to the primary location. */
1059 return false;
1061 /* Everything is now known to be in the correct source file,
1062 but it may require further sanitization. */
1063 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
1064 original_idx, loc_range->m_label);
1066 /* If we have a range that finishes before it starts (perhaps
1067 from something built via macro expansion), printing the
1068 range is likely to be nonsensical. Also, attempting to do so
1069 breaks assumptions within the printing code (PR c/68473).
1070 Similarly, don't attempt to print ranges if one or both ends
1071 of the range aren't sane to print relative to the
1072 primary location (PR c++/70105). */
1073 if (start.line > finish.line
1074 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1075 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1077 /* Is this the primary location? */
1078 if (m_layout_ranges.length () == 0)
1080 /* We want to print the caret for the primary location, but
1081 we must sanitize away m_start and m_finish. */
1082 ri.m_start = ri.m_caret;
1083 ri.m_finish = ri.m_caret;
1085 else
1086 /* This is a non-primary range; ignore it. */
1087 return false;
1090 /* Potentially filter to just the lines already specified by other
1091 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1092 The layout ctor doesn't use it, and can't because m_line_spans
1093 hasn't been set up at that point. */
1094 if (restrict_to_current_line_spans)
1096 if (!will_show_line_p (start.line))
1097 return false;
1098 if (!will_show_line_p (finish.line))
1099 return false;
1100 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1101 if (!will_show_line_p (caret.line))
1102 return false;
1105 /* Passed all the tests; add the range to m_layout_ranges so that
1106 it will be printed. */
1107 m_layout_ranges.safe_push (ri);
1108 return true;
1111 /* Return true iff ROW is within one of the line spans for this layout. */
1113 bool
1114 layout::will_show_line_p (linenum_type row) const
1116 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1117 line_span_idx++)
1119 const line_span *line_span = get_line_span (line_span_idx);
1120 if (line_span->contains_line_p (row))
1121 return true;
1123 return false;
1126 /* Print a line showing a gap in the line numbers, for showing the boundary
1127 between two line spans. */
1129 void
1130 layout::print_gap_in_line_numbering ()
1132 gcc_assert (m_show_line_numbers_p);
1134 pp_emit_prefix (m_pp);
1136 for (int i = 0; i < m_linenum_width + 1; i++)
1137 pp_character (m_pp, '.');
1139 pp_newline (m_pp);
1142 /* Return true iff we should print a heading when starting the
1143 line span with the given index. */
1145 bool
1146 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1148 /* We print a heading for every change of line span, hence for every
1149 line span after the initial one. */
1150 if (line_span_idx > 0)
1151 return true;
1153 /* We also do it for the initial span if the primary location of the
1154 diagnostic is in a different span. */
1155 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1156 return true;
1158 return false;
1161 /* Get an expanded_location for the first location of interest within
1162 the given line_span.
1163 Used when printing a heading to indicate a new line span. */
1165 expanded_location
1166 layout::get_expanded_location (const line_span *line_span) const
1168 /* Whenever possible, use the caret location. */
1169 if (line_span->contains_line_p (m_exploc.line))
1170 return m_exploc;
1172 /* Otherwise, use the start of the first range that's present
1173 within the line_span. */
1174 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1176 const layout_range *lr = &m_layout_ranges[i];
1177 if (line_span->contains_line_p (lr->m_start.m_line))
1179 expanded_location exploc = m_exploc;
1180 exploc.line = lr->m_start.m_line;
1181 exploc.column = lr->m_start.m_columns[CU_BYTES];
1182 return exploc;
1186 /* Otherwise, use the location of the first fixit-hint present within
1187 the line_span. */
1188 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1190 const fixit_hint *hint = m_fixit_hints[i];
1191 location_t loc = hint->get_start_loc ();
1192 expanded_location exploc = expand_location (loc);
1193 if (line_span->contains_line_p (exploc.line))
1194 return exploc;
1197 /* It should not be possible to have a line span that didn't
1198 contain any of the layout_range or fixit_hint instances. */
1199 gcc_unreachable ();
1200 return m_exploc;
1203 /* Determine if HINT is meaningful to print within this layout. */
1205 bool
1206 layout::validate_fixit_hint_p (const fixit_hint *hint)
1208 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1209 return false;
1210 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1211 return false;
1213 return true;
1216 /* Determine the range of lines affected by HINT.
1217 This assumes that HINT has already been filtered by
1218 validate_fixit_hint_p, and so affects the correct source file. */
1220 static line_span
1221 get_line_span_for_fixit_hint (const fixit_hint *hint)
1223 gcc_assert (hint);
1225 int start_line = LOCATION_LINE (hint->get_start_loc ());
1227 /* For line-insertion fix-it hints, add the previous line to the
1228 span, to give the user more context on the proposed change. */
1229 if (hint->ends_with_newline_p ())
1230 if (start_line > 1)
1231 start_line--;
1233 return line_span (start_line,
1234 LOCATION_LINE (hint->get_next_loc ()));
1237 /* We want to print the pertinent source code at a diagnostic. The
1238 rich_location can contain multiple locations. This will have been
1239 filtered into m_exploc (the caret for the primary location) and
1240 m_layout_ranges, for those ranges within the same source file.
1242 We will print a subset of the lines within the source file in question,
1243 as a collection of "spans" of lines.
1245 This function populates m_line_spans with an ordered, disjoint list of
1246 the line spans of interest.
1248 Printing a gap between line spans takes one line, so, when printing
1249 line numbers, we allow a gap of up to one line between spans when
1250 merging, since it makes more sense to print the source line rather than a
1251 "gap-in-line-numbering" line. When not printing line numbers, it's
1252 better to be more explicit about what's going on, so keeping them as
1253 separate spans is preferred.
1255 For example, if the primary range is on lines 8-10, with secondary ranges
1256 covering lines 5-6 and lines 13-15:
1259 005 |RANGE 1
1260 006 |RANGE 1
1262 008 |PRIMARY RANGE
1263 009 |PRIMARY CARET
1264 010 |PRIMARY RANGE
1267 013 |RANGE 2
1268 014 |RANGE 2
1269 015 |RANGE 2
1272 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1274 With line numbering off (with span headers), we want three spans: lines 5-6,
1275 lines 8-10, and lines 13-15. */
1277 void
1278 layout::calculate_line_spans ()
1280 /* This should only be called once, by the ctor. */
1281 gcc_assert (m_line_spans.length () == 0);
1283 /* Populate tmp_spans with individual spans, for each of
1284 m_exploc, and for m_layout_ranges. */
1285 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1286 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1287 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1289 const layout_range *lr = &m_layout_ranges[i];
1290 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1291 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1292 lr->m_finish.m_line));
1295 /* Also add spans for any fix-it hints, in case they cover other lines. */
1296 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1298 const fixit_hint *hint = m_fixit_hints[i];
1299 gcc_assert (hint);
1300 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1303 /* Sort them. */
1304 tmp_spans.qsort(line_span::comparator);
1306 /* Now iterate through tmp_spans, copying into m_line_spans, and
1307 combining where possible. */
1308 gcc_assert (tmp_spans.length () > 0);
1309 m_line_spans.safe_push (tmp_spans[0]);
1310 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1312 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1313 const line_span *next = &tmp_spans[i];
1314 gcc_assert (next->m_first_line >= current->m_first_line);
1315 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1316 if ((linenum_arith_t)next->m_first_line
1317 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1319 /* We can merge them. */
1320 if (next->m_last_line > current->m_last_line)
1321 current->m_last_line = next->m_last_line;
1323 else
1325 /* No merger possible. */
1326 m_line_spans.safe_push (*next);
1330 /* Verify the result, in m_line_spans. */
1331 gcc_assert (m_line_spans.length () > 0);
1332 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1334 const line_span *prev = &m_line_spans[i - 1];
1335 const line_span *next = &m_line_spans[i];
1336 /* The individual spans must be sane. */
1337 gcc_assert (prev->m_first_line <= prev->m_last_line);
1338 gcc_assert (next->m_first_line <= next->m_last_line);
1339 /* The spans must be ordered. */
1340 gcc_assert (prev->m_first_line < next->m_first_line);
1341 /* There must be a gap of at least one line between separate spans. */
1342 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1346 /* Determine how many display columns need to be reserved for line numbers,
1347 based on the largest line number that will be needed, and populate
1348 m_linenum_width. */
1350 void
1351 layout::calculate_linenum_width ()
1353 gcc_assert (m_line_spans.length () > 0);
1354 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1355 int highest_line = last_span->m_last_line;
1356 if (highest_line < 0)
1357 highest_line = 0;
1358 m_linenum_width = num_digits (highest_line);
1359 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1360 if (m_line_spans.length () > 1)
1361 m_linenum_width = MAX (m_linenum_width, 3);
1362 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1363 after the line number. */
1364 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1367 /* Calculate m_x_offset_display, which improves readability in case the source
1368 line of interest is longer than the user's display. All lines output will be
1369 shifted to the left (so that their beginning is no longer displayed) by
1370 m_x_offset_display display columns, so that the caret is in a reasonable
1371 location. */
1373 void
1374 layout::calculate_x_offset_display ()
1376 m_x_offset_display = 0;
1378 const int max_width = m_context->caret_max_width;
1379 if (!max_width)
1381 /* Nothing to do, the width is not capped. */
1382 return;
1385 const char_span line = location_get_source_line (m_exploc.file,
1386 m_exploc.line);
1387 if (!line)
1389 /* Nothing to do, we couldn't find the source line. */
1390 return;
1392 int caret_display_column = m_exploc.m_display_col;
1393 const int line_bytes
1394 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1395 line.length ());
1396 int eol_display_column
1397 = cpp_display_width (line.get_buffer (), line_bytes);
1398 if (caret_display_column > eol_display_column
1399 || !caret_display_column)
1401 /* This does not make sense, so don't try to do anything in this case. */
1402 return;
1405 /* Adjust caret and eol positions to include the left margin. If we are
1406 outputting line numbers, then the left margin is equal to m_linenum_width
1407 plus three for the " | " which follows it. Otherwise the left margin width
1408 is equal to 1, because layout::print_source_line() will prefix each line
1409 with a space. */
1410 const int source_display_cols = eol_display_column;
1411 int left_margin_size = 1;
1412 if (m_show_line_numbers_p)
1413 left_margin_size = m_linenum_width + 3;
1414 caret_display_column += left_margin_size;
1415 eol_display_column += left_margin_size;
1417 if (eol_display_column <= max_width)
1419 /* Nothing to do, everything fits in the display. */
1420 return;
1423 /* The line is too long for the display. Calculate an offset such that the
1424 caret is not too close to the right edge of the screen. It will be
1425 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1426 than that to the end of the source line anyway. */
1427 int right_margin_size = CARET_LINE_MARGIN;
1428 right_margin_size = MIN (eol_display_column - caret_display_column,
1429 right_margin_size);
1430 if (right_margin_size + left_margin_size >= max_width)
1432 /* The max_width is very small, so anything we try to do will not be very
1433 effective; just punt in this case and output with no offset. */
1434 return;
1436 const int max_caret_display_column = max_width - right_margin_size;
1437 if (caret_display_column > max_caret_display_column)
1439 m_x_offset_display = caret_display_column - max_caret_display_column;
1440 /* Make sure we don't offset the line into oblivion. */
1441 static const int min_cols_visible = 2;
1442 if (source_display_cols - m_x_offset_display < min_cols_visible)
1443 m_x_offset_display = 0;
1447 /* Print line ROW of source code, potentially colorized at any ranges, and
1448 populate *LBOUNDS_OUT.
1449 LINE is the source line (not necessarily 0-terminated) and LINE_BYTES
1450 is its length in bytes.
1451 This function deals only with byte offsets, not display columns, so
1452 m_x_offset_display must be converted from display to byte units. In
1453 particular, LINE_BYTES and LBOUNDS_OUT are in bytes. */
1455 void
1456 layout::print_source_line (linenum_type row, const char *line, int line_bytes,
1457 line_bounds *lbounds_out)
1459 m_colorizer.set_normal_text ();
1461 pp_emit_prefix (m_pp);
1462 if (m_show_line_numbers_p)
1464 int width = num_digits (row);
1465 for (int i = 0; i < m_linenum_width - width; i++)
1466 pp_space (m_pp);
1467 pp_printf (m_pp, "%i | ", row);
1469 else
1470 pp_space (m_pp);
1472 /* We will stop printing the source line at any trailing whitespace, and start
1473 printing it as per m_x_offset_display. */
1474 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1475 line_bytes);
1476 int x_offset_bytes = 0;
1477 if (m_x_offset_display)
1479 x_offset_bytes = cpp_display_column_to_byte_column (line, line_bytes,
1480 m_x_offset_display);
1481 /* In case the leading portion of the line that will be skipped over ends
1482 with a character with wcwidth > 1, then it is possible we skipped too
1483 much, so account for that by padding with spaces. */
1484 const int overage
1485 = cpp_byte_column_to_display_column (line, line_bytes, x_offset_bytes)
1486 - m_x_offset_display;
1487 for (int column = 0; column < overage; ++column)
1488 pp_space (m_pp);
1489 line += x_offset_bytes;
1492 /* Print the line. */
1493 int first_non_ws = INT_MAX;
1494 int last_non_ws = 0;
1495 for (int col_byte = 1 + x_offset_bytes; col_byte <= line_bytes; col_byte++)
1497 /* Assuming colorization is enabled for the caret and underline
1498 characters, we may also colorize the associated characters
1499 within the source line.
1501 For frontends that generate range information, we color the
1502 associated characters in the source line the same as the
1503 carets and underlines in the annotation line, to make it easier
1504 for the reader to see the pertinent code.
1506 For frontends that only generate carets, we don't colorize the
1507 characters above them, since this would look strange (e.g.
1508 colorizing just the first character in a token). */
1509 if (m_colorize_source_p)
1511 bool in_range_p;
1512 point_state state;
1513 in_range_p = get_state_at_point (row, col_byte,
1514 0, INT_MAX,
1515 CU_BYTES,
1516 &state);
1517 if (in_range_p)
1518 m_colorizer.set_range (state.range_idx);
1519 else
1520 m_colorizer.set_normal_text ();
1522 char c = *line;
1523 if (c == '\0' || c == '\t' || c == '\r')
1524 c = ' ';
1525 if (c != ' ')
1527 last_non_ws = col_byte;
1528 if (first_non_ws == INT_MAX)
1529 first_non_ws = col_byte;
1531 pp_character (m_pp, c);
1532 line++;
1534 print_newline ();
1536 lbounds_out->m_first_non_ws = first_non_ws;
1537 lbounds_out->m_last_non_ws = last_non_ws;
1540 /* Determine if we should print an annotation line for ROW.
1541 i.e. if any of m_layout_ranges contains ROW. */
1543 bool
1544 layout::should_print_annotation_line_p (linenum_type row) const
1546 layout_range *range;
1547 int i;
1548 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1550 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1551 return false;
1552 if (range->intersects_line_p (row))
1553 return true;
1555 return false;
1558 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1559 margin, which is empty for annotation lines. Otherwise, do nothing. */
1561 void
1562 layout::start_annotation_line (char margin_char) const
1564 pp_emit_prefix (m_pp);
1565 if (m_show_line_numbers_p)
1567 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1568 of it, right-aligned, padded with spaces. */
1569 int i;
1570 for (i = 0; i < m_linenum_width - 3; i++)
1571 pp_space (m_pp);
1572 for (; i < m_linenum_width; i++)
1573 pp_character (m_pp, margin_char);
1574 pp_string (m_pp, " |");
1578 /* Print a line consisting of the caret/underlines for the given
1579 source line. This function works with display columns, rather than byte
1580 counts; in particular, LBOUNDS should be in display column units. */
1582 void
1583 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1585 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1586 lbounds.m_last_non_ws);
1588 start_annotation_line ();
1589 pp_space (m_pp);
1591 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1593 bool in_range_p;
1594 point_state state;
1595 in_range_p = get_state_at_point (row, column,
1596 lbounds.m_first_non_ws,
1597 lbounds.m_last_non_ws,
1598 CU_DISPLAY_COLS,
1599 &state);
1600 if (in_range_p)
1602 /* Within a range. Draw either the caret or an underline. */
1603 m_colorizer.set_range (state.range_idx);
1604 if (state.draw_caret_p)
1606 /* Draw the caret. */
1607 char caret_char;
1608 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1609 caret_char = m_context->caret_chars[state.range_idx];
1610 else
1611 caret_char = '^';
1612 pp_character (m_pp, caret_char);
1614 else
1615 pp_character (m_pp, '~');
1617 else
1619 /* Not in a range. */
1620 m_colorizer.set_normal_text ();
1621 pp_character (m_pp, ' ');
1624 print_newline ();
1627 /* Implementation detail of layout::print_any_labels.
1629 A label within the given row of source. */
1631 class line_label
1633 public:
1634 line_label (int state_idx, int column, label_text text)
1635 : m_state_idx (state_idx), m_column (column),
1636 m_text (text), m_label_line (0), m_has_vbar (true)
1638 const int bytes = strlen (text.m_buffer);
1639 m_display_width = cpp_display_width (text.m_buffer, bytes);
1642 /* Sorting is primarily by column, then by state index. */
1643 static int comparator (const void *p1, const void *p2)
1645 const line_label *ll1 = (const line_label *)p1;
1646 const line_label *ll2 = (const line_label *)p2;
1647 int column_cmp = compare (ll1->m_column, ll2->m_column);
1648 if (column_cmp)
1649 return column_cmp;
1650 /* Order by reverse state index, so that labels are printed
1651 in order of insertion into the rich_location when the
1652 sorted list is walked backwards. */
1653 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1656 int m_state_idx;
1657 int m_column;
1658 label_text m_text;
1659 size_t m_display_width;
1660 int m_label_line;
1661 bool m_has_vbar;
1664 /* Print any labels in this row. */
1665 void
1666 layout::print_any_labels (linenum_type row)
1668 int i;
1669 auto_vec<line_label> labels;
1671 /* Gather the labels that are to be printed into "labels". */
1673 layout_range *range;
1674 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1676 /* Most ranges don't have labels, so reject this first. */
1677 if (range->m_label == NULL)
1678 continue;
1680 /* The range's caret must be on this line. */
1681 if (range->m_caret.m_line != row)
1682 continue;
1684 /* Reject labels that aren't fully visible due to clipping
1685 by m_x_offset_display. */
1686 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1687 if (disp_col <= m_x_offset_display)
1688 continue;
1690 label_text text;
1691 text = range->m_label->get_text (range->m_original_idx);
1693 /* Allow for labels that return NULL from their get_text
1694 implementation (so e.g. such labels can control their own
1695 visibility). */
1696 if (text.m_buffer == NULL)
1697 continue;
1699 labels.safe_push (line_label (i, disp_col, text));
1703 /* Bail out if there are no labels on this row. */
1704 if (labels.length () == 0)
1705 return;
1707 /* Sort them. */
1708 labels.qsort(line_label::comparator);
1710 /* Figure out how many "label lines" we need, and which
1711 one each label is printed in.
1713 For example, if the labels aren't too densely packed,
1714 we can fit them on the same line, giving two "label lines":
1716 foo + bar
1717 ~~~ ~~~
1718 | | : label line 0
1719 l0 l1 : label line 1
1721 If they would touch each other or overlap, then we need
1722 additional "label lines":
1724 foo + bar
1725 ~~~ ~~~
1726 | | : label line 0
1727 | label 1 : label line 1
1728 label 0 : label line 2
1730 Place the final label on label line 1, and work backwards, adding
1731 label lines as needed.
1733 If multiple labels are at the same place, put them on separate
1734 label lines:
1736 foo + bar
1737 ^ : label line 0
1738 | : label line 1
1739 label 0 : label line 2
1740 label 1 : label line 3. */
1742 int max_label_line = 1;
1744 int next_column = INT_MAX;
1745 line_label *label;
1746 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1748 /* Would this label "touch" or overlap the next label? */
1749 if (label->m_column + label->m_display_width >= (size_t)next_column)
1751 max_label_line++;
1753 /* If we've already seen labels with the same column, suppress the
1754 vertical bar for subsequent ones in this backwards iteration;
1755 hence only the one with the highest label_line has m_has_vbar set. */
1756 if (label->m_column == next_column)
1757 label->m_has_vbar = false;
1760 label->m_label_line = max_label_line;
1761 next_column = label->m_column;
1765 /* Print the "label lines". For each label within the line, print
1766 either a vertical bar ('|') for the labels that are lower down, or the
1767 labels themselves once we've reached their line. */
1769 for (int label_line = 0; label_line <= max_label_line; label_line++)
1771 start_annotation_line ();
1772 pp_space (m_pp);
1773 int column = 1 + m_x_offset_display;
1774 line_label *label;
1775 FOR_EACH_VEC_ELT (labels, i, label)
1777 if (label_line > label->m_label_line)
1778 /* We've printed all the labels for this label line. */
1779 break;
1781 if (label_line == label->m_label_line)
1783 gcc_assert (column <= label->m_column);
1784 move_to_column (&column, label->m_column, true);
1785 /* Colorize the text, unless it's for events in a
1786 diagnostic_path. */
1787 if (!m_diagnostic_path_p)
1788 m_colorizer.set_range (label->m_state_idx);
1789 pp_string (m_pp, label->m_text.m_buffer);
1790 m_colorizer.set_normal_text ();
1791 column += label->m_display_width;
1793 else if (label->m_has_vbar)
1795 gcc_assert (column <= label->m_column);
1796 move_to_column (&column, label->m_column, true);
1797 m_colorizer.set_range (label->m_state_idx);
1798 pp_character (m_pp, '|');
1799 m_colorizer.set_normal_text ();
1800 column++;
1803 print_newline ();
1807 /* Clean up. */
1809 line_label *label;
1810 FOR_EACH_VEC_ELT (labels, i, label)
1811 label->m_text.maybe_free ();
1815 /* If there are any fixit hints inserting new lines before source line ROW,
1816 print them.
1818 They are printed on lines of their own, before the source line
1819 itself, with a leading '+'. */
1821 void
1822 layout::print_leading_fixits (linenum_type row)
1824 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1826 const fixit_hint *hint = m_fixit_hints[i];
1828 if (!hint->ends_with_newline_p ())
1829 /* Not a newline fixit; print it in print_trailing_fixits. */
1830 continue;
1832 gcc_assert (hint->insertion_p ());
1834 if (hint->affects_line_p (m_exploc.file, row))
1836 /* Printing the '+' with normal colorization
1837 and the inserted line with "insert" colorization
1838 helps them stand out from each other, and from
1839 the surrounding text. */
1840 m_colorizer.set_normal_text ();
1841 start_annotation_line ('+');
1842 pp_character (m_pp, '+');
1843 m_colorizer.set_fixit_insert ();
1844 /* Print all but the trailing newline of the fix-it hint.
1845 We have to print the newline separately to avoid
1846 getting additional pp prefixes printed. */
1847 for (size_t i = 0; i < hint->get_length () - 1; i++)
1848 pp_character (m_pp, hint->get_string ()[i]);
1849 m_colorizer.set_normal_text ();
1850 pp_newline (m_pp);
1855 /* Subroutine of layout::print_trailing_fixits.
1857 Determine if the annotation line printed for LINE contained
1858 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
1860 bool
1861 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1862 int finish_column) const
1864 layout_range *range;
1865 int i;
1866 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1867 if (range->m_start.m_line == line
1868 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
1869 && range->m_finish.m_line == line
1870 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
1871 return true;
1872 return false;
1875 /* Classes for printing trailing fix-it hints i.e. those that
1876 don't add new lines.
1878 For insertion, these can look like:
1880 new_text
1882 For replacement, these can look like:
1884 ------------- : underline showing affected range
1885 new_text
1887 For deletion, these can look like:
1889 ------------- : underline showing affected range
1891 This can become confusing if they overlap, and so we need
1892 to do some preprocessing to decide what to print.
1893 We use the list of fixit_hint instances affecting the line
1894 to build a list of "correction" instances, and print the
1895 latter.
1897 For example, consider a set of fix-its for converting
1898 a C-style cast to a C++ const_cast.
1900 Given:
1902 ..000000000111111111122222222223333333333.
1903 ..123456789012345678901234567890123456789.
1904 foo *f = (foo *)ptr->field;
1905 ^~~~~
1907 and the fix-it hints:
1908 - replace col 10 (the open paren) with "const_cast<"
1909 - replace col 16 (the close paren) with "> ("
1910 - insert ")" before col 27
1912 then we would get odd-looking output:
1914 foo *f = (foo *)ptr->field;
1915 ^~~~~
1917 const_cast<
1919 > ( )
1921 It would be better to detect when fixit hints are going to
1922 overlap (those that require new lines), and to consolidate
1923 the printing of such fixits, giving something like:
1925 foo *f = (foo *)ptr->field;
1926 ^~~~~
1927 -----------------
1928 const_cast<foo *> (ptr->field)
1930 This works by detecting when the printing would overlap, and
1931 effectively injecting no-op replace hints into the gaps between
1932 such fix-its, so that the printing joins up.
1934 In the above example, the overlap of:
1935 - replace col 10 (the open paren) with "const_cast<"
1936 and:
1937 - replace col 16 (the close paren) with "> ("
1938 is fixed by injecting a no-op:
1939 - replace cols 11-15 with themselves ("foo *")
1940 and consolidating these, making:
1941 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1942 i.e.:
1943 - replace cols 10-16 with "const_cast<foo *> ("
1945 This overlaps with the final fix-it hint:
1946 - insert ")" before col 27
1947 and so we repeat the consolidation process, by injecting
1948 a no-op:
1949 - replace cols 17-26 with themselves ("ptr->field")
1950 giving:
1951 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1952 i.e.:
1953 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1955 and is thus printed as desired. */
1957 /* A range of (byte or display) columns within a line. */
1959 class column_range
1961 public:
1962 column_range (int start_, int finish_) : start (start_), finish (finish_)
1964 /* We must have either a range, or an insertion. */
1965 gcc_assert (start <= finish || finish == start - 1);
1968 bool operator== (const column_range &other) const
1970 return start == other.start && finish == other.finish;
1973 int start;
1974 int finish;
1977 /* Get the range of bytes or display columns that HINT would affect. */
1978 static column_range
1979 get_affected_range (const fixit_hint *hint, enum column_unit col_unit)
1981 expanded_location exploc_start = expand_location (hint->get_start_loc ());
1982 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
1983 --exploc_finish.column;
1985 int start_column;
1986 int finish_column;
1987 if (col_unit == CU_DISPLAY_COLS)
1989 start_column = location_compute_display_column (exploc_start);
1990 if (hint->insertion_p ())
1991 finish_column = start_column - 1;
1992 else
1993 finish_column = location_compute_display_column (exploc_finish);
1995 else
1997 start_column = exploc_start.column;
1998 finish_column = exploc_finish.column;
2000 return column_range (start_column, finish_column);
2003 /* Get the range of display columns that would be printed for HINT. */
2005 static column_range
2006 get_printed_columns (const fixit_hint *hint)
2008 expanded_location exploc = expand_location (hint->get_start_loc ());
2009 int start_column = location_compute_display_column (exploc);
2010 int hint_width = cpp_display_width (hint->get_string (),
2011 hint->get_length ());
2012 int final_hint_column = start_column + hint_width - 1;
2013 if (hint->insertion_p ())
2015 return column_range (start_column, final_hint_column);
2017 else
2019 exploc = expand_location (hint->get_next_loc ());
2020 --exploc.column;
2021 int finish_column = location_compute_display_column (exploc);
2022 return column_range (start_column,
2023 MAX (finish_column, final_hint_column));
2027 /* A correction on a particular line.
2028 This describes a plan for how to print one or more fixit_hint
2029 instances that affected the line, potentially consolidating hints
2030 into corrections to make the result easier for the user to read. */
2032 class correction
2034 public:
2035 correction (column_range affected_bytes,
2036 column_range affected_columns,
2037 column_range printed_columns,
2038 const char *new_text, size_t new_text_len)
2039 : m_affected_bytes (affected_bytes),
2040 m_affected_columns (affected_columns),
2041 m_printed_columns (printed_columns),
2042 m_text (xstrdup (new_text)),
2043 m_byte_length (new_text_len),
2044 m_alloc_sz (new_text_len + 1)
2046 compute_display_cols ();
2049 ~correction () { free (m_text); }
2051 bool insertion_p () const
2053 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2056 void ensure_capacity (size_t len);
2057 void ensure_terminated ();
2059 void compute_display_cols ()
2061 m_display_cols = cpp_display_width (m_text, m_byte_length);
2064 void overwrite (int dst_offset, const char_span &src_span)
2066 gcc_assert (dst_offset >= 0);
2067 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2068 memcpy (m_text + dst_offset, src_span.get_buffer (),
2069 src_span.length ());
2072 /* If insert, then start: the column before which the text
2073 is to be inserted, and finish is offset by the length of
2074 the replacement.
2075 If replace, then the range of columns affected. */
2076 column_range m_affected_bytes;
2077 column_range m_affected_columns;
2079 /* If insert, then start: the column before which the text
2080 is to be inserted, and finish is offset by the length of
2081 the replacement.
2082 If replace, then the range of columns affected. */
2083 column_range m_printed_columns;
2085 /* The text to be inserted/used as replacement. */
2086 char *m_text;
2087 size_t m_byte_length; /* Not including null-terminator. */
2088 int m_display_cols;
2089 size_t m_alloc_sz;
2092 /* Ensure that m_text can hold a string of length LEN
2093 (plus 1 for 0-termination). */
2095 void
2096 correction::ensure_capacity (size_t len)
2098 /* Allow 1 extra byte for 0-termination. */
2099 if (m_alloc_sz < (len + 1))
2101 size_t new_alloc_sz = (len + 1) * 2;
2102 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2103 m_alloc_sz = new_alloc_sz;
2107 /* Ensure that m_text is 0-terminated. */
2109 void
2110 correction::ensure_terminated ()
2112 /* 0-terminate the buffer. */
2113 gcc_assert (m_byte_length < m_alloc_sz);
2114 m_text[m_byte_length] = '\0';
2117 /* A list of corrections affecting a particular line.
2118 This is used by layout::print_trailing_fixits for planning
2119 how to print the fix-it hints affecting the line. */
2121 class line_corrections
2123 public:
2124 line_corrections (const char *filename, linenum_type row)
2125 : m_filename (filename), m_row (row)
2127 ~line_corrections ();
2129 void add_hint (const fixit_hint *hint);
2131 const char *m_filename;
2132 linenum_type m_row;
2133 auto_vec <correction *> m_corrections;
2136 /* struct line_corrections. */
2138 line_corrections::~line_corrections ()
2140 unsigned i;
2141 correction *c;
2142 FOR_EACH_VEC_ELT (m_corrections, i, c)
2143 delete c;
2146 /* A struct wrapping a particular source line, allowing
2147 run-time bounds-checking of accesses in a checked build. */
2149 class source_line
2151 public:
2152 source_line (const char *filename, int line);
2154 char_span as_span () { return char_span (chars, width); }
2156 const char *chars;
2157 int width;
2160 /* source_line's ctor. */
2162 source_line::source_line (const char *filename, int line)
2164 char_span span = location_get_source_line (filename, line);
2165 chars = span.get_buffer ();
2166 width = span.length ();
2169 /* Add HINT to the corrections for this line.
2170 Attempt to consolidate nearby hints so that they will not
2171 overlap with printed. */
2173 void
2174 line_corrections::add_hint (const fixit_hint *hint)
2176 column_range affected_bytes = get_affected_range (hint, CU_BYTES);
2177 column_range affected_columns = get_affected_range (hint, CU_DISPLAY_COLS);
2178 column_range printed_columns = get_printed_columns (hint);
2180 /* Potentially consolidate. */
2181 if (!m_corrections.is_empty ())
2183 correction *last_correction
2184 = m_corrections[m_corrections.length () - 1];
2186 /* The following consolidation code assumes that the fix-it hints
2187 have been sorted by start (done within layout's ctor). */
2188 gcc_assert (affected_bytes.start
2189 >= last_correction->m_affected_bytes.start);
2190 gcc_assert (printed_columns.start
2191 >= last_correction->m_printed_columns.start);
2193 if (printed_columns.start <= last_correction->m_printed_columns.finish)
2195 /* We have two hints for which the printed forms of the hints
2196 would touch or overlap, so we need to consolidate them to avoid
2197 confusing the user.
2198 Attempt to inject a "replace" correction from immediately
2199 after the end of the last hint to immediately before the start
2200 of the next hint. */
2201 column_range between (last_correction->m_affected_bytes.finish + 1,
2202 affected_bytes.start - 1);
2204 /* Try to read the source. */
2205 source_line line (m_filename, m_row);
2206 if (line.chars && between.finish < line.width)
2208 /* Consolidate into the last correction:
2209 add a no-op "replace" of the "between" text, and
2210 add the text from the new hint. */
2211 int old_byte_len = last_correction->m_byte_length;
2212 gcc_assert (old_byte_len >= 0);
2213 int between_byte_len = between.finish + 1 - between.start;
2214 gcc_assert (between_byte_len >= 0);
2215 int new_byte_len
2216 = old_byte_len + between_byte_len + hint->get_length ();
2217 gcc_assert (new_byte_len >= 0);
2218 last_correction->ensure_capacity (new_byte_len);
2219 last_correction->overwrite
2220 (old_byte_len,
2221 line.as_span ().subspan (between.start - 1,
2222 between.finish + 1 - between.start));
2223 last_correction->overwrite (old_byte_len + between_byte_len,
2224 char_span (hint->get_string (),
2225 hint->get_length ()));
2226 last_correction->m_byte_length = new_byte_len;
2227 last_correction->ensure_terminated ();
2228 last_correction->m_affected_bytes.finish
2229 = affected_bytes.finish;
2230 last_correction->m_affected_columns.finish
2231 = affected_columns.finish;
2232 int prev_display_cols = last_correction->m_display_cols;
2233 last_correction->compute_display_cols ();
2234 last_correction->m_printed_columns.finish
2235 += last_correction->m_display_cols - prev_display_cols;
2236 return;
2241 /* If no consolidation happened, add a new correction instance. */
2242 m_corrections.safe_push (new correction (affected_bytes,
2243 affected_columns,
2244 printed_columns,
2245 hint->get_string (),
2246 hint->get_length ()));
2249 /* If there are any fixit hints on source line ROW, print them.
2250 They are printed in order, attempting to combine them onto lines, but
2251 starting new lines if necessary.
2252 Fix-it hints that insert new lines are handled separately,
2253 in layout::print_leading_fixits. */
2255 void
2256 layout::print_trailing_fixits (linenum_type row)
2258 /* Build a list of correction instances for the line,
2259 potentially consolidating hints (for the sake of readability). */
2260 line_corrections corrections (m_exploc.file, row);
2261 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2263 const fixit_hint *hint = m_fixit_hints[i];
2265 /* Newline fixits are handled by layout::print_leading_fixits. */
2266 if (hint->ends_with_newline_p ())
2267 continue;
2269 if (hint->affects_line_p (m_exploc.file, row))
2270 corrections.add_hint (hint);
2273 /* Now print the corrections. */
2274 unsigned i;
2275 correction *c;
2276 int column = m_x_offset_display;
2278 if (!corrections.m_corrections.is_empty ())
2279 start_annotation_line ();
2281 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2283 /* For now we assume each fixit hint can only touch one line. */
2284 if (c->insertion_p ())
2286 /* This assumes the insertion just affects one line. */
2287 int start_column = c->m_printed_columns.start;
2288 move_to_column (&column, start_column, true);
2289 m_colorizer.set_fixit_insert ();
2290 pp_string (m_pp, c->m_text);
2291 m_colorizer.set_normal_text ();
2292 column += c->m_display_cols;
2294 else
2296 /* If the range of the replacement wasn't printed in the
2297 annotation line, then print an extra underline to
2298 indicate exactly what is being replaced.
2299 Always show it for removals. */
2300 int start_column = c->m_affected_columns.start;
2301 int finish_column = c->m_affected_columns.finish;
2302 if (!annotation_line_showed_range_p (row, start_column,
2303 finish_column)
2304 || c->m_byte_length == 0)
2306 move_to_column (&column, start_column, true);
2307 m_colorizer.set_fixit_delete ();
2308 for (; column <= finish_column; column++)
2309 pp_character (m_pp, '-');
2310 m_colorizer.set_normal_text ();
2312 /* Print the replacement text. REPLACE also covers
2313 removals, so only do this extra work (potentially starting
2314 a new line) if we have actual replacement text. */
2315 if (c->m_byte_length > 0)
2317 move_to_column (&column, start_column, true);
2318 m_colorizer.set_fixit_insert ();
2319 pp_string (m_pp, c->m_text);
2320 m_colorizer.set_normal_text ();
2321 column += c->m_display_cols;
2326 /* Add a trailing newline, if necessary. */
2327 move_to_column (&column, 0, false);
2330 /* Disable any colorization and emit a newline. */
2332 void
2333 layout::print_newline ()
2335 m_colorizer.set_normal_text ();
2336 pp_newline (m_pp);
2339 /* Return true if (ROW/COLUMN) is within a range of the layout.
2340 If it returns true, OUT_STATE is written to, with the
2341 range index, and whether we should draw the caret at
2342 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2343 whether all inputs and outputs are in bytes or display column units. */
2345 bool
2346 layout::get_state_at_point (/* Inputs. */
2347 linenum_type row, int column,
2348 int first_non_ws, int last_non_ws,
2349 enum column_unit col_unit,
2350 /* Outputs. */
2351 point_state *out_state)
2353 layout_range *range;
2354 int i;
2355 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2357 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2358 /* Bail out early, so that such ranges don't affect underlining or
2359 source colorization. */
2360 continue;
2362 if (range->contains_point (row, column, col_unit))
2364 out_state->range_idx = i;
2366 /* Are we at the range's caret? is it visible? */
2367 out_state->draw_caret_p = false;
2368 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2369 && row == range->m_caret.m_line
2370 && column == range->m_caret.m_columns[col_unit])
2371 out_state->draw_caret_p = true;
2373 /* Within a multiline range, don't display any underline
2374 in any leading or trailing whitespace on a line.
2375 We do display carets, however. */
2376 if (!out_state->draw_caret_p)
2377 if (column < first_non_ws || column > last_non_ws)
2378 return false;
2380 /* We are within a range. */
2381 return true;
2385 return false;
2388 /* Helper function for use by layout::print_line when printing the
2389 annotation line under the source line.
2390 Get the display column beyond the rightmost one that could contain a caret
2391 or range marker, given that we stop rendering at trailing whitespace.
2392 ROW is the source line within the given file.
2393 CARET_COLUMN is the display column of range 0's caret.
2394 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2395 character of source (as determined when printing the source line). */
2398 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2399 int last_non_ws_column)
2401 int result = caret_column + 1;
2403 layout_range *range;
2404 int i;
2405 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2407 if (row >= range->m_start.m_line)
2409 if (range->m_finish.m_line == row)
2411 /* On the final line within a range; ensure that
2412 we render up to the end of the range. */
2413 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2414 if (result <= disp_col)
2415 result = disp_col + 1;
2417 else if (row < range->m_finish.m_line)
2419 /* Within a multiline range; ensure that we render up to the
2420 last non-whitespace column. */
2421 if (result <= last_non_ws_column)
2422 result = last_non_ws_column + 1;
2427 return result;
2430 /* Given *COLUMN as an x-coordinate, print spaces to position
2431 successive output at DEST_COLUMN, printing a newline if necessary,
2432 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2433 left margin after any newline. */
2435 void
2436 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2438 /* Start a new line if we need to. */
2439 if (*column > dest_column)
2441 print_newline ();
2442 if (add_left_margin)
2443 start_annotation_line ();
2444 *column = m_x_offset_display;
2447 while (*column < dest_column)
2449 pp_space (m_pp);
2450 (*column)++;
2454 /* For debugging layout issues, render a ruler giving column numbers
2455 (after the 1-column indent). */
2457 void
2458 layout::show_ruler (int max_column) const
2460 /* Hundreds. */
2461 if (max_column > 99)
2463 start_annotation_line ();
2464 pp_space (m_pp);
2465 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2466 if (column % 10 == 0)
2467 pp_character (m_pp, '0' + (column / 100) % 10);
2468 else
2469 pp_space (m_pp);
2470 pp_newline (m_pp);
2473 /* Tens. */
2474 start_annotation_line ();
2475 pp_space (m_pp);
2476 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2477 if (column % 10 == 0)
2478 pp_character (m_pp, '0' + (column / 10) % 10);
2479 else
2480 pp_space (m_pp);
2481 pp_newline (m_pp);
2483 /* Units. */
2484 start_annotation_line ();
2485 pp_space (m_pp);
2486 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2487 pp_character (m_pp, '0' + (column % 10));
2488 pp_newline (m_pp);
2491 /* Print leading fix-its (for new lines inserted before the source line)
2492 then the source line, followed by an annotation line
2493 consisting of any caret/underlines, then any fixits.
2494 If the source line can't be read, print nothing. */
2495 void
2496 layout::print_line (linenum_type row)
2498 char_span line = location_get_source_line (m_exploc.file, row);
2499 if (!line)
2500 return;
2502 line_bounds lbounds;
2503 print_leading_fixits (row);
2504 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2505 if (should_print_annotation_line_p (row))
2507 if (lbounds.m_first_non_ws != INT_MAX)
2508 lbounds.convert_to_display_cols (line);
2509 print_annotation_line (row, lbounds);
2511 if (m_show_labels_p)
2512 print_any_labels (row);
2513 print_trailing_fixits (row);
2516 } /* End of anonymous namespace. */
2518 /* If LOC is within the spans of lines that will already be printed for
2519 this gcc_rich_location, then add it as a secondary location and return true.
2521 Otherwise return false. */
2523 bool
2524 gcc_rich_location::add_location_if_nearby (location_t loc,
2525 bool restrict_to_current_line_spans,
2526 const range_label *label)
2528 /* Use the layout location-handling logic to sanitize LOC,
2529 filtering it to the current line spans within a temporary
2530 layout instance. */
2531 layout layout (global_dc, this, DK_ERROR);
2532 location_range loc_range;
2533 loc_range.m_loc = loc;
2534 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2535 if (!layout.maybe_add_location_range (&loc_range, 0,
2536 restrict_to_current_line_spans))
2537 return false;
2539 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2540 return true;
2543 /* Print the physical source code corresponding to the location of
2544 this diagnostic, with additional annotations. */
2546 void
2547 diagnostic_show_locus (diagnostic_context * context,
2548 rich_location *richloc,
2549 diagnostic_t diagnostic_kind)
2551 location_t loc = richloc->get_loc ();
2552 /* Do nothing if source-printing has been disabled. */
2553 if (!context->show_caret)
2554 return;
2556 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2557 if (loc <= BUILTINS_LOCATION)
2558 return;
2560 /* Don't print the same source location twice in a row, unless we have
2561 fix-it hints. */
2562 if (loc == context->last_location
2563 && richloc->get_num_fixit_hints () == 0)
2564 return;
2566 context->last_location = loc;
2568 layout layout (context, richloc, diagnostic_kind);
2569 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2570 line_span_idx++)
2572 const line_span *line_span = layout.get_line_span (line_span_idx);
2573 if (context->show_line_numbers_p)
2575 /* With line numbers, we should show whenever the line-numbering
2576 "jumps". */
2577 if (line_span_idx > 0)
2578 layout.print_gap_in_line_numbering ();
2580 else
2582 /* Without line numbers, we print headings for some line spans. */
2583 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2585 expanded_location exploc
2586 = layout.get_expanded_location (line_span);
2587 context->start_span (context, exploc);
2590 /* Iterate over the lines within this span (using linenum_arith_t to
2591 avoid overflow with 0xffffffff causing an infinite loop). */
2592 linenum_arith_t last_line = line_span->get_last_line ();
2593 for (linenum_arith_t row = line_span->get_first_line ();
2594 row <= last_line; row++)
2595 layout.print_line (row);
2599 #if CHECKING_P
2601 namespace selftest {
2603 /* Selftests for diagnostic_show_locus. */
2605 /* For precise tests of the layout, make clear where the source line will
2606 start. test_left_margin sets the total byte count from the left side of the
2607 screen to the start of source lines, after the line number and the separator,
2608 which consists of the three characters " | ". */
2609 static const int test_linenum_sep = 3;
2610 static const int test_left_margin = 7;
2612 /* Helper function for test_layout_x_offset_display_utf8(). */
2613 static void
2614 test_offset_impl (int caret_byte_col, int max_width,
2615 int expected_x_offset_display,
2616 int left_margin = test_left_margin)
2618 test_diagnostic_context dc;
2619 dc.caret_max_width = max_width;
2620 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2621 the line number plus one space after. */
2622 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2623 dc.show_line_numbers_p = true;
2624 rich_location richloc (line_table,
2625 linemap_position_for_column (line_table,
2626 caret_byte_col));
2627 layout test_layout (&dc, &richloc, DK_ERROR);
2628 ASSERT_EQ (left_margin - test_linenum_sep,
2629 test_layout.get_linenum_width ());
2630 ASSERT_EQ (expected_x_offset_display,
2631 test_layout.get_x_offset_display ());
2634 /* Test that layout::calculate_x_offset_display() works. */
2635 static void
2636 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2639 const char *content
2640 = "This line is very long, so that we can use it to test the logic for "
2641 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2642 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2643 "column #102.\n";
2645 /* Number of bytes in the line, subtracting one to remove the newline. */
2646 const int line_bytes = strlen (content) - 1;
2648 /* Number of display columns occupied by the line; each of the 2 emojis
2649 takes up 2 fewer display columns than it does bytes. */
2650 const int line_display_cols = line_bytes - 2*2;
2652 /* The column of the first emoji. Byte or display is the same as there are
2653 no multibyte characters earlier on the line. */
2654 const int emoji_col = 102;
2656 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2657 line_table_test ltt (case_);
2659 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2661 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2663 /* Don't attempt to run the tests if column data might be unavailable. */
2664 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2665 return;
2667 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2668 ASSERT_EQ (1, LOCATION_LINE (line_end));
2669 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
2671 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2672 ASSERT_EQ (line_display_cols,
2673 cpp_display_width (lspan.get_buffer (), lspan.length ()));
2674 ASSERT_EQ (line_display_cols,
2675 location_compute_display_column (expand_location (line_end)));
2676 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
2677 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2679 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2681 /* No constraint on the width -> no offset. */
2682 test_offset_impl (emoji_col, 0, 0);
2684 /* Caret is before the beginning -> no offset. */
2685 test_offset_impl (0, 100, 0);
2687 /* Caret is past the end of the line -> no offset. */
2688 test_offset_impl (line_bytes+1, 100, 0);
2690 /* Line fits in the display -> no offset. */
2691 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
2692 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
2694 /* Line is too long for the display but caret location is OK
2695 anyway -> no offset. */
2696 static const int small_width = 24;
2697 test_offset_impl (1, small_width, 0);
2699 /* Width constraint is very small -> no offset. */
2700 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
2702 /* Line would be offset, but due to large line numbers, offsetting
2703 would remove the whole line -> no offset. */
2704 static const int huge_left_margin = 100;
2705 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
2707 /* Line is the same length as the display, but the line number makes it too
2708 long, so offset is required. Caret is at the end so padding on the right
2709 is not in effect. */
2710 for (int excess = 1; excess <= 3; ++excess)
2711 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
2712 excess);
2714 /* Line is much too long for the display, caret is near the end ->
2715 offset should be such that the line fits in the display and caret
2716 remains the same distance from the end that it was. */
2717 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
2718 caret_offset <= max_offset; ++caret_offset)
2719 test_offset_impl (line_bytes - caret_offset, small_width,
2720 line_display_cols + test_left_margin - small_width);
2722 /* As previous case but caret is closer to the middle; now we want it to end
2723 up CARET_LINE_MARGIN bytes from the end. */
2724 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
2725 test_offset_impl (emoji_col, small_width,
2726 emoji_col + test_left_margin
2727 - (small_width - CARET_LINE_MARGIN));
2729 /* Test that the source line is offset as expected when printed. */
2731 test_diagnostic_context dc;
2732 dc.caret_max_width = small_width - 6;
2733 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2734 dc.show_line_numbers_p = true;
2735 dc.show_ruler_p = true;
2736 rich_location richloc (line_table,
2737 linemap_position_for_column (line_table,
2738 emoji_col));
2739 layout test_layout (&dc, &richloc, DK_ERROR);
2740 test_layout.print_line (1);
2741 ASSERT_STREQ (" | 1 \n"
2742 " | 1 \n"
2743 " | 234567890123456789\n"
2744 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2745 "that occupies 8 bytes and 4 display columns, starting at "
2746 "column #102.\n"
2747 " | ^\n\n",
2748 pp_formatted_text (dc.printer));
2751 /* Similar to the previous example, but now the offset called for would split
2752 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2753 it with a padding space in this case. */
2755 test_diagnostic_context dc;
2756 dc.caret_max_width = small_width - 5;
2757 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2758 dc.show_line_numbers_p = true;
2759 dc.show_ruler_p = true;
2760 rich_location richloc (line_table,
2761 linemap_position_for_column (line_table,
2762 emoji_col + 2));
2763 layout test_layout (&dc, &richloc, DK_ERROR);
2764 test_layout.print_line (1);
2765 ASSERT_STREQ (" | 1 1 \n"
2766 " | 1 2 \n"
2767 " | 3456789012345678901\n"
2768 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2769 "that occupies 8 bytes and 4 display columns, starting at "
2770 "column #102.\n"
2771 " | ^\n\n",
2772 pp_formatted_text (dc.printer));
2777 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2779 static void
2780 test_diagnostic_show_locus_unknown_location ()
2782 test_diagnostic_context dc;
2783 rich_location richloc (line_table, UNKNOWN_LOCATION);
2784 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2785 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
2788 /* Verify that diagnostic_show_locus works sanely for various
2789 single-line cases.
2791 All of these work on the following 1-line source file:
2792 .0000000001111111
2793 .1234567890123456
2794 "foo = bar.field;\n"
2795 which is set up by test_diagnostic_show_locus_one_liner and calls
2796 them. */
2798 /* Just a caret. */
2800 static void
2801 test_one_liner_simple_caret ()
2803 test_diagnostic_context dc;
2804 location_t caret = linemap_position_for_column (line_table, 10);
2805 rich_location richloc (line_table, caret);
2806 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2807 ASSERT_STREQ (" foo = bar.field;\n"
2808 " ^\n",
2809 pp_formatted_text (dc.printer));
2812 /* Caret and range. */
2814 static void
2815 test_one_liner_caret_and_range ()
2817 test_diagnostic_context dc;
2818 location_t caret = linemap_position_for_column (line_table, 10);
2819 location_t start = linemap_position_for_column (line_table, 7);
2820 location_t finish = linemap_position_for_column (line_table, 15);
2821 location_t loc = make_location (caret, start, finish);
2822 rich_location richloc (line_table, loc);
2823 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2824 ASSERT_STREQ (" foo = bar.field;\n"
2825 " ~~~^~~~~~\n",
2826 pp_formatted_text (dc.printer));
2829 /* Multiple ranges and carets. */
2831 static void
2832 test_one_liner_multiple_carets_and_ranges ()
2834 test_diagnostic_context dc;
2835 location_t foo
2836 = make_location (linemap_position_for_column (line_table, 2),
2837 linemap_position_for_column (line_table, 1),
2838 linemap_position_for_column (line_table, 3));
2839 dc.caret_chars[0] = 'A';
2841 location_t bar
2842 = make_location (linemap_position_for_column (line_table, 8),
2843 linemap_position_for_column (line_table, 7),
2844 linemap_position_for_column (line_table, 9));
2845 dc.caret_chars[1] = 'B';
2847 location_t field
2848 = make_location (linemap_position_for_column (line_table, 13),
2849 linemap_position_for_column (line_table, 11),
2850 linemap_position_for_column (line_table, 15));
2851 dc.caret_chars[2] = 'C';
2853 rich_location richloc (line_table, foo);
2854 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2855 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2856 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2857 ASSERT_STREQ (" foo = bar.field;\n"
2858 " ~A~ ~B~ ~~C~~\n",
2859 pp_formatted_text (dc.printer));
2862 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2864 static void
2865 test_one_liner_fixit_insert_before ()
2867 test_diagnostic_context dc;
2868 location_t caret = linemap_position_for_column (line_table, 7);
2869 rich_location richloc (line_table, caret);
2870 richloc.add_fixit_insert_before ("&");
2871 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2872 ASSERT_STREQ (" foo = bar.field;\n"
2873 " ^\n"
2874 " &\n",
2875 pp_formatted_text (dc.printer));
2878 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2880 static void
2881 test_one_liner_fixit_insert_after ()
2883 test_diagnostic_context dc;
2884 location_t start = linemap_position_for_column (line_table, 1);
2885 location_t finish = linemap_position_for_column (line_table, 3);
2886 location_t foo = make_location (start, start, finish);
2887 rich_location richloc (line_table, foo);
2888 richloc.add_fixit_insert_after ("[0]");
2889 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2890 ASSERT_STREQ (" foo = bar.field;\n"
2891 " ^~~\n"
2892 " [0]\n",
2893 pp_formatted_text (dc.printer));
2896 /* Removal fix-it hint: removal of the ".field".
2897 Also verify the interaction of pp_set_prefix with rulers and
2898 fix-it hints. */
2900 static void
2901 test_one_liner_fixit_remove ()
2903 location_t start = linemap_position_for_column (line_table, 10);
2904 location_t finish = linemap_position_for_column (line_table, 15);
2905 location_t dot = make_location (start, start, finish);
2906 rich_location richloc (line_table, dot);
2907 richloc.add_fixit_remove ();
2909 /* Normal. */
2911 test_diagnostic_context dc;
2912 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2913 ASSERT_STREQ (" foo = bar.field;\n"
2914 " ^~~~~~\n"
2915 " ------\n",
2916 pp_formatted_text (dc.printer));
2919 /* Test of adding a prefix. */
2921 test_diagnostic_context dc;
2922 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2923 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2924 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2925 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
2926 "TEST PREFIX: ^~~~~~\n"
2927 "TEST PREFIX: ------\n",
2928 pp_formatted_text (dc.printer));
2931 /* Normal, with ruler. */
2933 test_diagnostic_context dc;
2934 dc.show_ruler_p = true;
2935 dc.caret_max_width = 104;
2936 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2937 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
2938 " 1 2 3 4 5 6 7 8 9 0 \n"
2939 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
2940 " foo = bar.field;\n"
2941 " ^~~~~~\n"
2942 " ------\n",
2943 pp_formatted_text (dc.printer));
2946 /* Test of adding a prefix, with ruler. */
2948 test_diagnostic_context dc;
2949 dc.show_ruler_p = true;
2950 dc.caret_max_width = 50;
2951 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2952 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2953 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2954 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
2955 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
2956 "TEST PREFIX: foo = bar.field;\n"
2957 "TEST PREFIX: ^~~~~~\n"
2958 "TEST PREFIX: ------\n",
2959 pp_formatted_text (dc.printer));
2962 /* Test of adding a prefix, with ruler and line numbers. */
2964 test_diagnostic_context dc;
2965 dc.show_ruler_p = true;
2966 dc.caret_max_width = 50;
2967 dc.show_line_numbers_p = true;
2968 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2969 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2970 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2971 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
2972 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
2973 "TEST PREFIX: 1 | foo = bar.field;\n"
2974 "TEST PREFIX: | ^~~~~~\n"
2975 "TEST PREFIX: | ------\n",
2976 pp_formatted_text (dc.printer));
2980 /* Replace fix-it hint: replacing "field" with "m_field". */
2982 static void
2983 test_one_liner_fixit_replace ()
2985 test_diagnostic_context dc;
2986 location_t start = linemap_position_for_column (line_table, 11);
2987 location_t finish = linemap_position_for_column (line_table, 15);
2988 location_t field = make_location (start, start, finish);
2989 rich_location richloc (line_table, field);
2990 richloc.add_fixit_replace ("m_field");
2991 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2992 ASSERT_STREQ (" foo = bar.field;\n"
2993 " ^~~~~\n"
2994 " m_field\n",
2995 pp_formatted_text (dc.printer));
2998 /* Replace fix-it hint: replacing "field" with "m_field",
2999 but where the caret was elsewhere. */
3001 static void
3002 test_one_liner_fixit_replace_non_equal_range ()
3004 test_diagnostic_context dc;
3005 location_t equals = linemap_position_for_column (line_table, 5);
3006 location_t start = linemap_position_for_column (line_table, 11);
3007 location_t finish = linemap_position_for_column (line_table, 15);
3008 rich_location richloc (line_table, equals);
3009 source_range range;
3010 range.m_start = start;
3011 range.m_finish = finish;
3012 richloc.add_fixit_replace (range, "m_field");
3013 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3014 /* The replacement range is not indicated in the annotation line, so
3015 it should be indicated via an additional underline. */
3016 ASSERT_STREQ (" foo = bar.field;\n"
3017 " ^\n"
3018 " -----\n"
3019 " m_field\n",
3020 pp_formatted_text (dc.printer));
3023 /* Replace fix-it hint: replacing "field" with "m_field",
3024 where the caret was elsewhere, but where a secondary range
3025 exactly covers "field". */
3027 static void
3028 test_one_liner_fixit_replace_equal_secondary_range ()
3030 test_diagnostic_context dc;
3031 location_t equals = linemap_position_for_column (line_table, 5);
3032 location_t start = linemap_position_for_column (line_table, 11);
3033 location_t finish = linemap_position_for_column (line_table, 15);
3034 rich_location richloc (line_table, equals);
3035 location_t field = make_location (start, start, finish);
3036 richloc.add_range (field);
3037 richloc.add_fixit_replace (field, "m_field");
3038 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3039 /* The replacement range is indicated in the annotation line,
3040 so it shouldn't be indicated via an additional underline. */
3041 ASSERT_STREQ (" foo = bar.field;\n"
3042 " ^ ~~~~~\n"
3043 " m_field\n",
3044 pp_formatted_text (dc.printer));
3047 /* Verify that we can use ad-hoc locations when adding fixits to a
3048 rich_location. */
3050 static void
3051 test_one_liner_fixit_validation_adhoc_locations ()
3053 /* Generate a range that's too long to be packed, so must
3054 be stored as an ad-hoc location (given the defaults
3055 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3056 const location_t c7 = linemap_position_for_column (line_table, 7);
3057 const location_t c47 = linemap_position_for_column (line_table, 47);
3058 const location_t loc = make_location (c7, c7, c47);
3060 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3061 return;
3063 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3065 /* Insert. */
3067 rich_location richloc (line_table, loc);
3068 richloc.add_fixit_insert_before (loc, "test");
3069 /* It should not have been discarded by the validator. */
3070 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3072 test_diagnostic_context dc;
3073 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3074 ASSERT_STREQ (" foo = bar.field;\n"
3075 " ^~~~~~~~~~ \n"
3076 " test\n",
3077 pp_formatted_text (dc.printer));
3080 /* Remove. */
3082 rich_location richloc (line_table, loc);
3083 source_range range = source_range::from_locations (loc, c47);
3084 richloc.add_fixit_remove (range);
3085 /* It should not have been discarded by the validator. */
3086 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3088 test_diagnostic_context dc;
3089 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3090 ASSERT_STREQ (" foo = bar.field;\n"
3091 " ^~~~~~~~~~ \n"
3092 " -----------------------------------------\n",
3093 pp_formatted_text (dc.printer));
3096 /* Replace. */
3098 rich_location richloc (line_table, loc);
3099 source_range range = source_range::from_locations (loc, c47);
3100 richloc.add_fixit_replace (range, "test");
3101 /* It should not have been discarded by the validator. */
3102 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3104 test_diagnostic_context dc;
3105 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3106 ASSERT_STREQ (" foo = bar.field;\n"
3107 " ^~~~~~~~~~ \n"
3108 " test\n",
3109 pp_formatted_text (dc.printer));
3113 /* Test of consolidating insertions at the same location. */
3115 static void
3116 test_one_liner_many_fixits_1 ()
3118 test_diagnostic_context dc;
3119 location_t equals = linemap_position_for_column (line_table, 5);
3120 rich_location richloc (line_table, equals);
3121 for (int i = 0; i < 19; i++)
3122 richloc.add_fixit_insert_before ("a");
3123 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3124 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3125 ASSERT_STREQ (" foo = bar.field;\n"
3126 " ^\n"
3127 " aaaaaaaaaaaaaaaaaaa\n",
3128 pp_formatted_text (dc.printer));
3131 /* Ensure that we can add an arbitrary number of fix-it hints to a
3132 rich_location, even if they are not consolidated. */
3134 static void
3135 test_one_liner_many_fixits_2 ()
3137 test_diagnostic_context dc;
3138 location_t equals = linemap_position_for_column (line_table, 5);
3139 rich_location richloc (line_table, equals);
3140 for (int i = 0; i < 19; i++)
3142 location_t loc = linemap_position_for_column (line_table, i * 2);
3143 richloc.add_fixit_insert_before (loc, "a");
3145 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3146 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3147 ASSERT_STREQ (" foo = bar.field;\n"
3148 " ^\n"
3149 "a a a a a a a a a a a a a a a a a a a\n",
3150 pp_formatted_text (dc.printer));
3153 /* Test of labeling the ranges within a rich_location. */
3155 static void
3156 test_one_liner_labels ()
3158 location_t foo
3159 = make_location (linemap_position_for_column (line_table, 1),
3160 linemap_position_for_column (line_table, 1),
3161 linemap_position_for_column (line_table, 3));
3162 location_t bar
3163 = make_location (linemap_position_for_column (line_table, 7),
3164 linemap_position_for_column (line_table, 7),
3165 linemap_position_for_column (line_table, 9));
3166 location_t field
3167 = make_location (linemap_position_for_column (line_table, 11),
3168 linemap_position_for_column (line_table, 11),
3169 linemap_position_for_column (line_table, 15));
3171 /* Example where all the labels fit on one line. */
3173 text_range_label label0 ("0");
3174 text_range_label label1 ("1");
3175 text_range_label label2 ("2");
3176 gcc_rich_location richloc (foo, &label0);
3177 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3178 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3181 test_diagnostic_context dc;
3182 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3183 ASSERT_STREQ (" foo = bar.field;\n"
3184 " ^~~ ~~~ ~~~~~\n"
3185 " | | |\n"
3186 " 0 1 2\n",
3187 pp_formatted_text (dc.printer));
3190 /* Verify that we can disable label-printing. */
3192 test_diagnostic_context dc;
3193 dc.show_labels_p = false;
3194 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3195 ASSERT_STREQ (" foo = bar.field;\n"
3196 " ^~~ ~~~ ~~~~~\n",
3197 pp_formatted_text (dc.printer));
3201 /* Example where the labels need extra lines. */
3203 text_range_label label0 ("label 0");
3204 text_range_label label1 ("label 1");
3205 text_range_label label2 ("label 2");
3206 gcc_rich_location richloc (foo, &label0);
3207 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3208 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3210 test_diagnostic_context dc;
3211 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3212 ASSERT_STREQ (" foo = bar.field;\n"
3213 " ^~~ ~~~ ~~~~~\n"
3214 " | | |\n"
3215 " | | label 2\n"
3216 " | label 1\n"
3217 " label 0\n",
3218 pp_formatted_text (dc.printer));
3221 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3222 but label 1 just touches label 2. */
3224 text_range_label label0 ("aaaaa");
3225 text_range_label label1 ("bbbb");
3226 text_range_label label2 ("c");
3227 gcc_rich_location richloc (foo, &label0);
3228 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3229 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3231 test_diagnostic_context dc;
3232 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3233 ASSERT_STREQ (" foo = bar.field;\n"
3234 " ^~~ ~~~ ~~~~~\n"
3235 " | | |\n"
3236 " | | c\n"
3237 " aaaaa bbbb\n",
3238 pp_formatted_text (dc.printer));
3241 /* Example of out-of-order ranges (thus requiring a sort). */
3243 text_range_label label0 ("0");
3244 text_range_label label1 ("1");
3245 text_range_label label2 ("2");
3246 gcc_rich_location richloc (field, &label0);
3247 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3248 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3250 test_diagnostic_context dc;
3251 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3252 ASSERT_STREQ (" foo = bar.field;\n"
3253 " ~~~ ~~~ ^~~~~\n"
3254 " | | |\n"
3255 " 2 1 0\n",
3256 pp_formatted_text (dc.printer));
3259 /* Ensure we don't ICE if multiple ranges with labels are on
3260 the same point. */
3262 text_range_label label0 ("label 0");
3263 text_range_label label1 ("label 1");
3264 text_range_label label2 ("label 2");
3265 gcc_rich_location richloc (bar, &label0);
3266 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3267 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3269 test_diagnostic_context dc;
3270 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3271 ASSERT_STREQ (" foo = bar.field;\n"
3272 " ^~~\n"
3273 " |\n"
3274 " label 0\n"
3275 " label 1\n"
3276 " label 2\n",
3277 pp_formatted_text (dc.printer));
3280 /* Example of out-of-order ranges (thus requiring a sort), where
3281 they overlap, and there are multiple ranges on the same point. */
3283 text_range_label label_0a ("label 0a");
3284 text_range_label label_1a ("label 1a");
3285 text_range_label label_2a ("label 2a");
3286 text_range_label label_0b ("label 0b");
3287 text_range_label label_1b ("label 1b");
3288 text_range_label label_2b ("label 2b");
3289 text_range_label label_0c ("label 0c");
3290 text_range_label label_1c ("label 1c");
3291 text_range_label label_2c ("label 2c");
3292 gcc_rich_location richloc (field, &label_0a);
3293 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3294 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3296 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3297 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3298 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3300 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3301 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3302 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3304 test_diagnostic_context dc;
3305 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3306 ASSERT_STREQ (" foo = bar.field;\n"
3307 " ~~~ ~~~ ^~~~~\n"
3308 " | | |\n"
3309 " | | label 0a\n"
3310 " | | label 0b\n"
3311 " | | label 0c\n"
3312 " | label 1a\n"
3313 " | label 1b\n"
3314 " | label 1c\n"
3315 " label 2a\n"
3316 " label 2b\n"
3317 " label 2c\n",
3318 pp_formatted_text (dc.printer));
3321 /* Verify that a NULL result from range_label::get_text is
3322 handled gracefully. */
3324 text_range_label label (NULL);
3325 gcc_rich_location richloc (bar, &label);
3327 test_diagnostic_context dc;
3328 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3329 ASSERT_STREQ (" foo = bar.field;\n"
3330 " ^~~\n",
3331 pp_formatted_text (dc.printer));
3334 /* TODO: example of formatted printing (needs to be in
3335 gcc-rich-location.c due to Makefile.in issues). */
3338 /* Run the various one-liner tests. */
3340 static void
3341 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3343 /* Create a tempfile and write some text to it.
3344 ....................0000000001111111.
3345 ....................1234567890123456. */
3346 const char *content = "foo = bar.field;\n";
3347 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3348 line_table_test ltt (case_);
3350 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3352 location_t line_end = linemap_position_for_column (line_table, 16);
3354 /* Don't attempt to run the tests if column data might be unavailable. */
3355 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3356 return;
3358 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3359 ASSERT_EQ (1, LOCATION_LINE (line_end));
3360 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3362 test_one_liner_simple_caret ();
3363 test_one_liner_caret_and_range ();
3364 test_one_liner_multiple_carets_and_ranges ();
3365 test_one_liner_fixit_insert_before ();
3366 test_one_liner_fixit_insert_after ();
3367 test_one_liner_fixit_remove ();
3368 test_one_liner_fixit_replace ();
3369 test_one_liner_fixit_replace_non_equal_range ();
3370 test_one_liner_fixit_replace_equal_secondary_range ();
3371 test_one_liner_fixit_validation_adhoc_locations ();
3372 test_one_liner_many_fixits_1 ();
3373 test_one_liner_many_fixits_2 ();
3374 test_one_liner_labels ();
3377 /* Version of all one-liner tests exercising multibyte awareness. For
3378 simplicity we stick to using two multibyte characters in the test, U+1F602
3379 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3380 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3381 below asserts would be easier to read if we used UTF-8 directly in the
3382 string constants, but it seems better not to demand the host compiler
3383 support this, when it isn't otherwise necessary. Instead, whenever an
3384 extended character appears in a string, we put a line break after it so that
3385 all succeeding characters can appear visually at the correct display column.
3387 All of these work on the following 1-line source file:
3389 .0000000001111111111222222 display
3390 .1234567890123456789012345 columns
3391 "SS_foo = P_bar.SS_fieldP;\n"
3392 .0000000111111111222222223 byte
3393 .1356789012456789134567891 columns
3395 which is set up by test_diagnostic_show_locus_one_liner and calls
3396 them. Here SS represents the two display columns for the U+1F602 emoji and
3397 P represents the one display column for the U+03C0 pi symbol. */
3399 /* Just a caret. */
3401 static void
3402 test_one_liner_simple_caret_utf8 ()
3404 test_diagnostic_context dc;
3405 location_t caret = linemap_position_for_column (line_table, 18);
3406 rich_location richloc (line_table, caret);
3407 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3408 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3409 "_foo = \xcf\x80"
3410 "_bar.\xf0\x9f\x98\x82"
3411 "_field\xcf\x80"
3412 ";\n"
3413 " ^\n",
3414 pp_formatted_text (dc.printer));
3417 /* Caret and range. */
3418 static void
3419 test_one_liner_caret_and_range_utf8 ()
3421 test_diagnostic_context dc;
3422 location_t caret = linemap_position_for_column (line_table, 18);
3423 location_t start = linemap_position_for_column (line_table, 12);
3424 location_t finish = linemap_position_for_column (line_table, 30);
3425 location_t loc = make_location (caret, start, finish);
3426 rich_location richloc (line_table, loc);
3427 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3428 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3429 "_foo = \xcf\x80"
3430 "_bar.\xf0\x9f\x98\x82"
3431 "_field\xcf\x80"
3432 ";\n"
3433 " ~~~~~^~~~~~~~~~\n",
3434 pp_formatted_text (dc.printer));
3437 /* Multiple ranges and carets. */
3439 static void
3440 test_one_liner_multiple_carets_and_ranges_utf8 ()
3442 test_diagnostic_context dc;
3443 location_t foo
3444 = make_location (linemap_position_for_column (line_table, 7),
3445 linemap_position_for_column (line_table, 1),
3446 linemap_position_for_column (line_table, 8));
3447 dc.caret_chars[0] = 'A';
3449 location_t bar
3450 = make_location (linemap_position_for_column (line_table, 16),
3451 linemap_position_for_column (line_table, 12),
3452 linemap_position_for_column (line_table, 17));
3453 dc.caret_chars[1] = 'B';
3455 location_t field
3456 = make_location (linemap_position_for_column (line_table, 26),
3457 linemap_position_for_column (line_table, 19),
3458 linemap_position_for_column (line_table, 30));
3459 dc.caret_chars[2] = 'C';
3460 rich_location richloc (line_table, foo);
3461 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3462 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3463 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3464 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3465 "_foo = \xcf\x80"
3466 "_bar.\xf0\x9f\x98\x82"
3467 "_field\xcf\x80"
3468 ";\n"
3469 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3470 pp_formatted_text (dc.printer));
3473 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3475 static void
3476 test_one_liner_fixit_insert_before_utf8 ()
3478 test_diagnostic_context dc;
3479 location_t caret = linemap_position_for_column (line_table, 12);
3480 rich_location richloc (line_table, caret);
3481 richloc.add_fixit_insert_before ("&");
3482 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3483 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3484 "_foo = \xcf\x80"
3485 "_bar.\xf0\x9f\x98\x82"
3486 "_field\xcf\x80"
3487 ";\n"
3488 " ^\n"
3489 " &\n",
3490 pp_formatted_text (dc.printer));
3493 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3495 static void
3496 test_one_liner_fixit_insert_after_utf8 ()
3498 test_diagnostic_context dc;
3499 location_t start = linemap_position_for_column (line_table, 1);
3500 location_t finish = linemap_position_for_column (line_table, 8);
3501 location_t foo = make_location (start, start, finish);
3502 rich_location richloc (line_table, foo);
3503 richloc.add_fixit_insert_after ("[0]");
3504 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3505 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3506 "_foo = \xcf\x80"
3507 "_bar.\xf0\x9f\x98\x82"
3508 "_field\xcf\x80"
3509 ";\n"
3510 " ^~~~~~\n"
3511 " [0]\n",
3512 pp_formatted_text (dc.printer));
3515 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3517 static void
3518 test_one_liner_fixit_remove_utf8 ()
3520 test_diagnostic_context dc;
3521 location_t start = linemap_position_for_column (line_table, 18);
3522 location_t finish = linemap_position_for_column (line_table, 30);
3523 location_t dot = make_location (start, start, finish);
3524 rich_location richloc (line_table, dot);
3525 richloc.add_fixit_remove ();
3526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3527 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3528 "_foo = \xcf\x80"
3529 "_bar.\xf0\x9f\x98\x82"
3530 "_field\xcf\x80"
3531 ";\n"
3532 " ^~~~~~~~~~\n"
3533 " ----------\n",
3534 pp_formatted_text (dc.printer));
3537 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3539 static void
3540 test_one_liner_fixit_replace_utf8 ()
3542 test_diagnostic_context dc;
3543 location_t start = linemap_position_for_column (line_table, 19);
3544 location_t finish = linemap_position_for_column (line_table, 30);
3545 location_t field = make_location (start, start, finish);
3546 rich_location richloc (line_table, field);
3547 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3548 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3549 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3550 "_foo = \xcf\x80"
3551 "_bar.\xf0\x9f\x98\x82"
3552 "_field\xcf\x80"
3553 ";\n"
3554 " ^~~~~~~~~\n"
3555 " m_\xf0\x9f\x98\x82"
3556 "_field\xcf\x80\n",
3557 pp_formatted_text (dc.printer));
3560 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3561 but where the caret was elsewhere. */
3563 static void
3564 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3566 test_diagnostic_context dc;
3567 location_t equals = linemap_position_for_column (line_table, 10);
3568 location_t start = linemap_position_for_column (line_table, 19);
3569 location_t finish = linemap_position_for_column (line_table, 30);
3570 rich_location richloc (line_table, equals);
3571 source_range range;
3572 range.m_start = start;
3573 range.m_finish = finish;
3574 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3575 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3576 /* The replacement range is not indicated in the annotation line, so
3577 it should be indicated via an additional underline. */
3578 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3579 "_foo = \xcf\x80"
3580 "_bar.\xf0\x9f\x98\x82"
3581 "_field\xcf\x80"
3582 ";\n"
3583 " ^\n"
3584 " ---------\n"
3585 " m_\xf0\x9f\x98\x82"
3586 "_field\xcf\x80\n",
3587 pp_formatted_text (dc.printer));
3590 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3591 where the caret was elsewhere, but where a secondary range
3592 exactly covers "field". */
3594 static void
3595 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3597 test_diagnostic_context dc;
3598 location_t equals = linemap_position_for_column (line_table, 10);
3599 location_t start = linemap_position_for_column (line_table, 19);
3600 location_t finish = linemap_position_for_column (line_table, 30);
3601 rich_location richloc (line_table, equals);
3602 location_t field = make_location (start, start, finish);
3603 richloc.add_range (field);
3604 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3605 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3606 /* The replacement range is indicated in the annotation line,
3607 so it shouldn't be indicated via an additional underline. */
3608 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3609 "_foo = \xcf\x80"
3610 "_bar.\xf0\x9f\x98\x82"
3611 "_field\xcf\x80"
3612 ";\n"
3613 " ^ ~~~~~~~~~\n"
3614 " m_\xf0\x9f\x98\x82"
3615 "_field\xcf\x80\n",
3616 pp_formatted_text (dc.printer));
3619 /* Verify that we can use ad-hoc locations when adding fixits to a
3620 rich_location. */
3622 static void
3623 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3625 /* Generate a range that's too long to be packed, so must
3626 be stored as an ad-hoc location (given the defaults
3627 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3628 const location_t c12 = linemap_position_for_column (line_table, 12);
3629 const location_t c52 = linemap_position_for_column (line_table, 52);
3630 const location_t loc = make_location (c12, c12, c52);
3632 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3633 return;
3635 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3637 /* Insert. */
3639 rich_location richloc (line_table, loc);
3640 richloc.add_fixit_insert_before (loc, "test");
3641 /* It should not have been discarded by the validator. */
3642 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3644 test_diagnostic_context dc;
3645 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3646 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3647 "_foo = \xcf\x80"
3648 "_bar.\xf0\x9f\x98\x82"
3649 "_field\xcf\x80"
3650 ";\n"
3651 " ^~~~~~~~~~~~~~~~ \n"
3652 " test\n",
3653 pp_formatted_text (dc.printer));
3656 /* Remove. */
3658 rich_location richloc (line_table, loc);
3659 source_range range = source_range::from_locations (loc, c52);
3660 richloc.add_fixit_remove (range);
3661 /* It should not have been discarded by the validator. */
3662 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3664 test_diagnostic_context dc;
3665 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3666 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3667 "_foo = \xcf\x80"
3668 "_bar.\xf0\x9f\x98\x82"
3669 "_field\xcf\x80"
3670 ";\n"
3671 " ^~~~~~~~~~~~~~~~ \n"
3672 " -------------------------------------\n",
3673 pp_formatted_text (dc.printer));
3676 /* Replace. */
3678 rich_location richloc (line_table, loc);
3679 source_range range = source_range::from_locations (loc, c52);
3680 richloc.add_fixit_replace (range, "test");
3681 /* It should not have been discarded by the validator. */
3682 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3684 test_diagnostic_context dc;
3685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3686 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3687 "_foo = \xcf\x80"
3688 "_bar.\xf0\x9f\x98\x82"
3689 "_field\xcf\x80"
3690 ";\n"
3691 " ^~~~~~~~~~~~~~~~ \n"
3692 " test\n",
3693 pp_formatted_text (dc.printer));
3697 /* Test of consolidating insertions at the same location. */
3699 static void
3700 test_one_liner_many_fixits_1_utf8 ()
3702 test_diagnostic_context dc;
3703 location_t equals = linemap_position_for_column (line_table, 10);
3704 rich_location richloc (line_table, equals);
3705 for (int i = 0; i < 19; i++)
3706 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
3707 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3708 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3709 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3710 "_foo = \xcf\x80"
3711 "_bar.\xf0\x9f\x98\x82"
3712 "_field\xcf\x80"
3713 ";\n"
3714 " ^\n"
3715 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3716 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3717 pp_formatted_text (dc.printer));
3720 /* Ensure that we can add an arbitrary number of fix-it hints to a
3721 rich_location, even if they are not consolidated. */
3723 static void
3724 test_one_liner_many_fixits_2_utf8 ()
3726 test_diagnostic_context dc;
3727 location_t equals = linemap_position_for_column (line_table, 10);
3728 rich_location richloc (line_table, equals);
3729 const int nlocs = 19;
3730 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3731 34, 36, 38, 40, 42, 44};
3732 for (int i = 0; i != nlocs; ++i)
3734 location_t loc = linemap_position_for_column (line_table, locs[i]);
3735 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
3738 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
3739 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3740 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3741 "_foo = \xcf\x80"
3742 "_bar.\xf0\x9f\x98\x82"
3743 "_field\xcf\x80"
3744 ";\n"
3745 " ^\n"
3746 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3747 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3748 pp_formatted_text (dc.printer));
3751 /* Test of labeling the ranges within a rich_location. */
3753 static void
3754 test_one_liner_labels_utf8 ()
3756 location_t foo
3757 = make_location (linemap_position_for_column (line_table, 1),
3758 linemap_position_for_column (line_table, 1),
3759 linemap_position_for_column (line_table, 8));
3760 location_t bar
3761 = make_location (linemap_position_for_column (line_table, 12),
3762 linemap_position_for_column (line_table, 12),
3763 linemap_position_for_column (line_table, 17));
3764 location_t field
3765 = make_location (linemap_position_for_column (line_table, 19),
3766 linemap_position_for_column (line_table, 19),
3767 linemap_position_for_column (line_table, 30));
3769 /* Example where all the labels fit on one line. */
3771 /* These three labels contain multibyte characters such that their byte
3772 lengths are respectively (12, 10, 18), but their display widths are only
3773 (6, 5, 9). All three fit on the line when considering the display
3774 widths, but not when considering the byte widths, so verify that we do
3775 indeed put them all on one line. */
3776 text_range_label label0
3777 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3778 text_range_label label1
3779 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3780 text_range_label label2
3781 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3782 "\xcf\x80");
3783 gcc_rich_location richloc (foo, &label0);
3784 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3785 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3788 test_diagnostic_context dc;
3789 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3790 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3791 "_foo = \xcf\x80"
3792 "_bar.\xf0\x9f\x98\x82"
3793 "_field\xcf\x80"
3794 ";\n"
3795 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3796 " | | |\n"
3797 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3798 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3799 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3800 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3801 pp_formatted_text (dc.printer));
3806 /* Example where the labels need extra lines. */
3808 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
3809 text_range_label label1 ("label 1\xcf\x80");
3810 text_range_label label2 ("label 2\xcf\x80");
3811 gcc_rich_location richloc (foo, &label0);
3812 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3813 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3815 test_diagnostic_context dc;
3816 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3818 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3819 "_foo = \xcf\x80"
3820 "_bar.\xf0\x9f\x98\x82"
3821 "_field\xcf\x80"
3822 ";\n"
3823 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3824 " | | |\n"
3825 " | | label 2\xcf\x80\n"
3826 " | label 1\xcf\x80\n"
3827 " label 0\xf0\x9f\x98\x82\n",
3828 pp_formatted_text (dc.printer));
3831 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3832 but label 1 just touches label 2. */
3834 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3835 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3836 text_range_label label2 ("c");
3837 gcc_rich_location richloc (foo, &label0);
3838 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3839 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3841 test_diagnostic_context dc;
3842 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3843 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3844 "_foo = \xcf\x80"
3845 "_bar.\xf0\x9f\x98\x82"
3846 "_field\xcf\x80"
3847 ";\n"
3848 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3849 " | | |\n"
3850 " | | c\n"
3851 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
3852 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
3853 pp_formatted_text (dc.printer));
3857 /* Run the various one-liner tests. */
3859 static void
3860 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
3862 /* Create a tempfile and write some text to it. */
3863 const char *content
3864 /* Display columns.
3865 0000000000000000000000011111111111111111111111111111112222222222222
3866 1111111122222222345678900000000123456666666677777777890123444444445 */
3867 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
3868 /* 0000000000000000000001111111111111111111222222222222222222222233333
3869 1111222233334444567890122223333456789999000011112222345678999900001
3870 Byte columns. */
3871 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3872 line_table_test ltt (case_);
3874 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3876 location_t line_end = linemap_position_for_column (line_table, 31);
3878 /* Don't attempt to run the tests if column data might be unavailable. */
3879 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3880 return;
3882 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3883 ASSERT_EQ (1, LOCATION_LINE (line_end));
3884 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
3886 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3887 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length ()));
3888 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end)));
3890 test_one_liner_simple_caret_utf8 ();
3891 test_one_liner_caret_and_range_utf8 ();
3892 test_one_liner_multiple_carets_and_ranges_utf8 ();
3893 test_one_liner_fixit_insert_before_utf8 ();
3894 test_one_liner_fixit_insert_after_utf8 ();
3895 test_one_liner_fixit_remove_utf8 ();
3896 test_one_liner_fixit_replace_utf8 ();
3897 test_one_liner_fixit_replace_non_equal_range_utf8 ();
3898 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
3899 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
3900 test_one_liner_many_fixits_1_utf8 ();
3901 test_one_liner_many_fixits_2_utf8 ();
3902 test_one_liner_labels_utf8 ();
3905 /* Verify that gcc_rich_location::add_location_if_nearby works. */
3907 static void
3908 test_add_location_if_nearby (const line_table_case &case_)
3910 /* Create a tempfile and write some text to it.
3911 ...000000000111111111122222222223333333333.
3912 ...123456789012345678901234567890123456789. */
3913 const char *content
3914 = ("struct same_line { double x; double y; ;\n" /* line 1. */
3915 "struct different_line\n" /* line 2. */
3916 "{\n" /* line 3. */
3917 " double x;\n" /* line 4. */
3918 " double y;\n" /* line 5. */
3919 ";\n"); /* line 6. */
3920 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3921 line_table_test ltt (case_);
3923 const line_map_ordinary *ord_map
3924 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3925 tmp.get_filename (), 0));
3927 linemap_line_start (line_table, 1, 100);
3929 const location_t final_line_end
3930 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
3932 /* Don't attempt to run the tests if column data might be unavailable. */
3933 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3934 return;
3936 /* Test of add_location_if_nearby on the same line as the
3937 primary location. */
3939 const location_t missing_close_brace_1_39
3940 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
3941 const location_t matching_open_brace_1_18
3942 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3943 gcc_rich_location richloc (missing_close_brace_1_39);
3944 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
3945 ASSERT_TRUE (added);
3946 ASSERT_EQ (2, richloc.get_num_locations ());
3947 test_diagnostic_context dc;
3948 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3949 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
3950 " ~ ^\n",
3951 pp_formatted_text (dc.printer));
3954 /* Test of add_location_if_nearby on a different line to the
3955 primary location. */
3957 const location_t missing_close_brace_6_1
3958 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
3959 const location_t matching_open_brace_3_1
3960 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
3961 gcc_rich_location richloc (missing_close_brace_6_1);
3962 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
3963 ASSERT_FALSE (added);
3964 ASSERT_EQ (1, richloc.get_num_locations ());
3968 /* Verify that we print fixits even if they only affect lines
3969 outside those covered by the ranges in the rich_location. */
3971 static void
3972 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
3974 /* Create a tempfile and write some text to it.
3975 ...000000000111111111122222222223333333333.
3976 ...123456789012345678901234567890123456789. */
3977 const char *content
3978 = ("struct point { double x; double y; };\n" /* line 1. */
3979 "struct point origin = {x: 0.0,\n" /* line 2. */
3980 " y\n" /* line 3. */
3981 "\n" /* line 4. */
3982 "\n" /* line 5. */
3983 " : 0.0};\n"); /* line 6. */
3984 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3985 line_table_test ltt (case_);
3987 const line_map_ordinary *ord_map
3988 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3989 tmp.get_filename (), 0));
3991 linemap_line_start (line_table, 1, 100);
3993 const location_t final_line_end
3994 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3996 /* Don't attempt to run the tests if column data might be unavailable. */
3997 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3998 return;
4000 /* A pair of tests for modernizing the initializers to C99-style. */
4002 /* The one-liner case (line 2). */
4004 test_diagnostic_context dc;
4005 const location_t x
4006 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4007 const location_t colon
4008 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4009 rich_location richloc (line_table, colon);
4010 richloc.add_fixit_insert_before (x, ".");
4011 richloc.add_fixit_replace (colon, "=");
4012 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4013 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4014 " ^\n"
4015 " .=\n",
4016 pp_formatted_text (dc.printer));
4019 /* The multiline case. The caret for the rich_location is on line 6;
4020 verify that insertion fixit on line 3 is still printed (and that
4021 span starts are printed due to the gap between the span at line 3
4022 and that at line 6). */
4024 test_diagnostic_context dc;
4025 const location_t y
4026 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4027 const location_t colon
4028 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4029 rich_location richloc (line_table, colon);
4030 richloc.add_fixit_insert_before (y, ".");
4031 richloc.add_fixit_replace (colon, "=");
4032 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4033 ASSERT_STREQ ("FILENAME:3:24:\n"
4034 " y\n"
4035 " .\n"
4036 "FILENAME:6:25:\n"
4037 " : 0.0};\n"
4038 " ^\n"
4039 " =\n",
4040 pp_formatted_text (dc.printer));
4043 /* As above, but verify the behavior of multiple line spans
4044 with line-numbering enabled. */
4046 const location_t y
4047 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4048 const location_t colon
4049 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4050 rich_location richloc (line_table, colon);
4051 richloc.add_fixit_insert_before (y, ".");
4052 richloc.add_fixit_replace (colon, "=");
4053 test_diagnostic_context dc;
4054 dc.show_line_numbers_p = true;
4055 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4056 ASSERT_STREQ (" 3 | y\n"
4057 " | .\n"
4058 "......\n"
4059 " 6 | : 0.0};\n"
4060 " | ^\n"
4061 " | =\n",
4062 pp_formatted_text (dc.printer));
4067 /* Verify that fix-it hints are appropriately consolidated.
4069 If any fix-it hints in a rich_location involve locations beyond
4070 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4071 the fix-it as a whole, so there should be none.
4073 Otherwise, verify that consecutive "replace" and "remove" fix-its
4074 are merged, and that other fix-its remain separate. */
4076 static void
4077 test_fixit_consolidation (const line_table_case &case_)
4079 line_table_test ltt (case_);
4081 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4083 const location_t c10 = linemap_position_for_column (line_table, 10);
4084 const location_t c15 = linemap_position_for_column (line_table, 15);
4085 const location_t c16 = linemap_position_for_column (line_table, 16);
4086 const location_t c17 = linemap_position_for_column (line_table, 17);
4087 const location_t c20 = linemap_position_for_column (line_table, 20);
4088 const location_t c21 = linemap_position_for_column (line_table, 21);
4089 const location_t caret = c10;
4091 /* Insert + insert. */
4093 rich_location richloc (line_table, caret);
4094 richloc.add_fixit_insert_before (c10, "foo");
4095 richloc.add_fixit_insert_before (c15, "bar");
4097 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4098 /* Bogus column info for 2nd fixit, so no fixits. */
4099 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4100 else
4101 /* They should not have been merged. */
4102 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4105 /* Insert + replace. */
4107 rich_location richloc (line_table, caret);
4108 richloc.add_fixit_insert_before (c10, "foo");
4109 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4110 "bar");
4112 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4113 /* Bogus column info for 2nd fixit, so no fixits. */
4114 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4115 else
4116 /* They should not have been merged. */
4117 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4120 /* Replace + non-consecutive insert. */
4122 rich_location richloc (line_table, caret);
4123 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4124 "bar");
4125 richloc.add_fixit_insert_before (c17, "foo");
4127 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4128 /* Bogus column info for 2nd fixit, so no fixits. */
4129 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4130 else
4131 /* They should not have been merged. */
4132 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4135 /* Replace + non-consecutive replace. */
4137 rich_location richloc (line_table, caret);
4138 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4139 "foo");
4140 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4141 "bar");
4143 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4144 /* Bogus column info for 2nd fixit, so no fixits. */
4145 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4146 else
4147 /* They should not have been merged. */
4148 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4151 /* Replace + consecutive replace. */
4153 rich_location richloc (line_table, caret);
4154 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4155 "foo");
4156 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4157 "bar");
4159 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4160 /* Bogus column info for 2nd fixit, so no fixits. */
4161 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4162 else
4164 /* They should have been merged into a single "replace". */
4165 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4166 const fixit_hint *hint = richloc.get_fixit_hint (0);
4167 ASSERT_STREQ ("foobar", hint->get_string ());
4168 ASSERT_EQ (c10, hint->get_start_loc ());
4169 ASSERT_EQ (c21, hint->get_next_loc ());
4173 /* Replace + consecutive removal. */
4175 rich_location richloc (line_table, caret);
4176 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4177 "foo");
4178 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4180 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4181 /* Bogus column info for 2nd fixit, so no fixits. */
4182 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4183 else
4185 /* They should have been merged into a single replace, with the
4186 range extended to cover that of the removal. */
4187 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4188 const fixit_hint *hint = richloc.get_fixit_hint (0);
4189 ASSERT_STREQ ("foo", hint->get_string ());
4190 ASSERT_EQ (c10, hint->get_start_loc ());
4191 ASSERT_EQ (c21, hint->get_next_loc ());
4195 /* Consecutive removals. */
4197 rich_location richloc (line_table, caret);
4198 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4199 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4201 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4202 /* Bogus column info for 2nd fixit, so no fixits. */
4203 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4204 else
4206 /* They should have been merged into a single "replace-with-empty". */
4207 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4208 const fixit_hint *hint = richloc.get_fixit_hint (0);
4209 ASSERT_STREQ ("", hint->get_string ());
4210 ASSERT_EQ (c10, hint->get_start_loc ());
4211 ASSERT_EQ (c21, hint->get_next_loc ());
4216 /* Verify that the line_corrections machinery correctly prints
4217 overlapping fixit-hints. */
4219 static void
4220 test_overlapped_fixit_printing (const line_table_case &case_)
4222 /* Create a tempfile and write some text to it.
4223 ...000000000111111111122222222223333333333.
4224 ...123456789012345678901234567890123456789. */
4225 const char *content
4226 = (" foo *f = (foo *)ptr->field;\n");
4227 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4228 line_table_test ltt (case_);
4230 const line_map_ordinary *ord_map
4231 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4232 tmp.get_filename (), 0));
4234 linemap_line_start (line_table, 1, 100);
4236 const location_t final_line_end
4237 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4239 /* Don't attempt to run the tests if column data might be unavailable. */
4240 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4241 return;
4243 /* A test for converting a C-style cast to a C++-style cast. */
4244 const location_t open_paren
4245 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4246 const location_t close_paren
4247 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4248 const location_t expr_start
4249 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4250 const location_t expr_finish
4251 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4252 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4254 /* Various examples of fix-it hints that aren't themselves consolidated,
4255 but for which the *printing* may need consolidation. */
4257 /* Example where 3 fix-it hints are printed as one. */
4259 test_diagnostic_context dc;
4260 rich_location richloc (line_table, expr);
4261 richloc.add_fixit_replace (open_paren, "const_cast<");
4262 richloc.add_fixit_replace (close_paren, "> (");
4263 richloc.add_fixit_insert_after (")");
4265 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4266 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4267 " ^~~~~~~~~~\n"
4268 " -----------------\n"
4269 " const_cast<foo *> (ptr->field)\n",
4270 pp_formatted_text (dc.printer));
4272 /* Unit-test the line_corrections machinery. */
4273 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4274 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4275 ASSERT_EQ (column_range (12, 12), get_affected_range (hint_0, CU_BYTES));
4276 ASSERT_EQ (column_range (12, 12),
4277 get_affected_range (hint_0, CU_DISPLAY_COLS));
4278 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
4279 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4280 ASSERT_EQ (column_range (18, 18), get_affected_range (hint_1, CU_BYTES));
4281 ASSERT_EQ (column_range (18, 18),
4282 get_affected_range (hint_1, CU_DISPLAY_COLS));
4283 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
4284 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4285 ASSERT_EQ (column_range (29, 28), get_affected_range (hint_2, CU_BYTES));
4286 ASSERT_EQ (column_range (29, 28),
4287 get_affected_range (hint_2, CU_DISPLAY_COLS));
4288 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
4290 /* Add each hint in turn to a line_corrections instance,
4291 and verify that they are consolidated into one correction instance
4292 as expected. */
4293 line_corrections lc (tmp.get_filename (), 1);
4295 /* The first replace hint by itself. */
4296 lc.add_hint (hint_0);
4297 ASSERT_EQ (1, lc.m_corrections.length ());
4298 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4299 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4300 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4301 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4303 /* After the second replacement hint, they are printed together
4304 as a replacement (along with the text between them). */
4305 lc.add_hint (hint_1);
4306 ASSERT_EQ (1, lc.m_corrections.length ());
4307 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4308 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4309 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4310 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4312 /* After the final insertion hint, they are all printed together
4313 as a replacement (along with the text between them). */
4314 lc.add_hint (hint_2);
4315 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4316 lc.m_corrections[0]->m_text);
4317 ASSERT_EQ (1, lc.m_corrections.length ());
4318 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4319 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4320 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4323 /* Example where two are consolidated during printing. */
4325 test_diagnostic_context dc;
4326 rich_location richloc (line_table, expr);
4327 richloc.add_fixit_replace (open_paren, "CAST (");
4328 richloc.add_fixit_replace (close_paren, ") (");
4329 richloc.add_fixit_insert_after (")");
4331 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4332 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4333 " ^~~~~~~~~~\n"
4334 " -\n"
4335 " CAST (-\n"
4336 " ) ( )\n",
4337 pp_formatted_text (dc.printer));
4340 /* Example where none are consolidated during printing. */
4342 test_diagnostic_context dc;
4343 rich_location richloc (line_table, expr);
4344 richloc.add_fixit_replace (open_paren, "CST (");
4345 richloc.add_fixit_replace (close_paren, ") (");
4346 richloc.add_fixit_insert_after (")");
4348 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4349 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4350 " ^~~~~~~~~~\n"
4351 " -\n"
4352 " CST ( -\n"
4353 " ) ( )\n",
4354 pp_formatted_text (dc.printer));
4357 /* Example of deletion fix-it hints. */
4359 test_diagnostic_context dc;
4360 rich_location richloc (line_table, expr);
4361 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4362 source_range victim = {open_paren, close_paren};
4363 richloc.add_fixit_remove (victim);
4365 /* This case is actually handled by fixit-consolidation,
4366 rather than by line_corrections. */
4367 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4369 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4370 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4371 " ^~~~~~~~~~\n"
4372 " -------\n"
4373 " (bar *)\n",
4374 pp_formatted_text (dc.printer));
4377 /* Example of deletion fix-it hints that would overlap. */
4379 test_diagnostic_context dc;
4380 rich_location richloc (line_table, expr);
4381 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4382 source_range victim = {expr_start, expr_finish};
4383 richloc.add_fixit_remove (victim);
4385 /* These fixits are not consolidated. */
4386 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4388 /* But the corrections are. */
4389 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4390 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4391 " ^~~~~~~~~~\n"
4392 " -----------------\n"
4393 " (longer *)(foo *)\n",
4394 pp_formatted_text (dc.printer));
4397 /* Example of insertion fix-it hints that would overlap. */
4399 test_diagnostic_context dc;
4400 rich_location richloc (line_table, expr);
4401 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4402 richloc.add_fixit_insert_after (close_paren, "TEST");
4404 /* The first insertion is long enough that if printed naively,
4405 it would overlap with the second.
4406 Verify that they are printed as a single replacement. */
4407 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4408 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4409 " ^~~~~~~~~~\n"
4410 " -------\n"
4411 " LONGER THAN THE CAST(foo *)TEST\n",
4412 pp_formatted_text (dc.printer));
4416 /* Multibyte-aware version of preceding tests. See comments above
4417 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4418 characters here. */
4420 static void
4421 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4423 /* Create a tempfile and write some text to it. */
4425 const char *content
4426 /* Display columns.
4427 00000000000000000000000111111111111111111111111222222222222222223
4428 12344444444555555556789012344444444555555556789012345678999999990 */
4429 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4430 /* 00000000000000000000011111111111111111111112222222222333333333333
4431 12344445555666677778901234566667777888899990123456789012333344445
4432 Byte columns. */
4434 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4435 line_table_test ltt (case_);
4437 const line_map_ordinary *ord_map
4438 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4439 tmp.get_filename (), 0));
4441 linemap_line_start (line_table, 1, 100);
4443 const location_t final_line_end
4444 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4446 /* Don't attempt to run the tests if column data might be unavailable. */
4447 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4448 return;
4450 /* A test for converting a C-style cast to a C++-style cast. */
4451 const location_t open_paren
4452 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4453 const location_t close_paren
4454 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4455 const location_t expr_start
4456 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4457 const location_t expr_finish
4458 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4459 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4461 /* Various examples of fix-it hints that aren't themselves consolidated,
4462 but for which the *printing* may need consolidation. */
4464 /* Example where 3 fix-it hints are printed as one. */
4466 test_diagnostic_context dc;
4467 rich_location richloc (line_table, expr);
4468 richloc.add_fixit_replace (open_paren, "const_cast<");
4469 richloc.add_fixit_replace (close_paren, "> (");
4470 richloc.add_fixit_insert_after (")");
4472 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4473 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4474 " *f = (f\xf0\x9f\x98\x82"
4475 " *)ptr->field\xcf\x80"
4476 ";\n"
4477 " ^~~~~~~~~~~\n"
4478 " ------------------\n"
4479 " const_cast<f\xf0\x9f\x98\x82"
4480 " *> (ptr->field\xcf\x80"
4481 ")\n",
4482 pp_formatted_text (dc.printer));
4484 /* Unit-test the line_corrections machinery. */
4485 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4486 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4487 ASSERT_EQ (column_range (14, 14), get_affected_range (hint_0, CU_BYTES));
4488 ASSERT_EQ (column_range (12, 12),
4489 get_affected_range (hint_0, CU_DISPLAY_COLS));
4490 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
4491 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4492 ASSERT_EQ (column_range (22, 22), get_affected_range (hint_1, CU_BYTES));
4493 ASSERT_EQ (column_range (18, 18),
4494 get_affected_range (hint_1, CU_DISPLAY_COLS));
4495 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
4496 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4497 ASSERT_EQ (column_range (35, 34), get_affected_range (hint_2, CU_BYTES));
4498 ASSERT_EQ (column_range (30, 29),
4499 get_affected_range (hint_2, CU_DISPLAY_COLS));
4500 ASSERT_EQ (column_range (30, 30), get_printed_columns (hint_2));
4502 /* Add each hint in turn to a line_corrections instance,
4503 and verify that they are consolidated into one correction instance
4504 as expected. */
4505 line_corrections lc (tmp.get_filename (), 1);
4507 /* The first replace hint by itself. */
4508 lc.add_hint (hint_0);
4509 ASSERT_EQ (1, lc.m_corrections.length ());
4510 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
4511 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4512 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4513 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4515 /* After the second replacement hint, they are printed together
4516 as a replacement (along with the text between them). */
4517 lc.add_hint (hint_1);
4518 ASSERT_EQ (1, lc.m_corrections.length ());
4519 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4520 lc.m_corrections[0]->m_text);
4521 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
4522 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4523 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4525 /* After the final insertion hint, they are all printed together
4526 as a replacement (along with the text between them). */
4527 lc.add_hint (hint_2);
4528 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4529 lc.m_corrections[0]->m_text);
4530 ASSERT_EQ (1, lc.m_corrections.length ());
4531 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
4532 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
4533 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
4536 /* Example where two are consolidated during printing. */
4538 test_diagnostic_context dc;
4539 rich_location richloc (line_table, expr);
4540 richloc.add_fixit_replace (open_paren, "CAST (");
4541 richloc.add_fixit_replace (close_paren, ") (");
4542 richloc.add_fixit_insert_after (")");
4544 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4545 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4546 " *f = (f\xf0\x9f\x98\x82"
4547 " *)ptr->field\xcf\x80"
4548 ";\n"
4549 " ^~~~~~~~~~~\n"
4550 " -\n"
4551 " CAST (-\n"
4552 " ) ( )\n",
4553 pp_formatted_text (dc.printer));
4556 /* Example where none are consolidated during printing. */
4558 test_diagnostic_context dc;
4559 rich_location richloc (line_table, expr);
4560 richloc.add_fixit_replace (open_paren, "CST (");
4561 richloc.add_fixit_replace (close_paren, ") (");
4562 richloc.add_fixit_insert_after (")");
4564 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4565 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4566 " *f = (f\xf0\x9f\x98\x82"
4567 " *)ptr->field\xcf\x80"
4568 ";\n"
4569 " ^~~~~~~~~~~\n"
4570 " -\n"
4571 " CST ( -\n"
4572 " ) ( )\n",
4573 pp_formatted_text (dc.printer));
4576 /* Example of deletion fix-it hints. */
4578 test_diagnostic_context dc;
4579 rich_location richloc (line_table, expr);
4580 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
4581 source_range victim = {open_paren, close_paren};
4582 richloc.add_fixit_remove (victim);
4584 /* This case is actually handled by fixit-consolidation,
4585 rather than by line_corrections. */
4586 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4588 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4589 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4590 " *f = (f\xf0\x9f\x98\x82"
4591 " *)ptr->field\xcf\x80"
4592 ";\n"
4593 " ^~~~~~~~~~~\n"
4594 " -------\n"
4595 " (bar\xf0\x9f\x98\x82"
4596 " *)\n",
4597 pp_formatted_text (dc.printer));
4600 /* Example of deletion fix-it hints that would overlap. */
4602 test_diagnostic_context dc;
4603 rich_location richloc (line_table, expr);
4604 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
4605 source_range victim = {expr_start, expr_finish};
4606 richloc.add_fixit_remove (victim);
4608 /* These fixits are not consolidated. */
4609 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4611 /* But the corrections are. */
4612 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4613 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4614 " *f = (f\xf0\x9f\x98\x82"
4615 " *)ptr->field\xcf\x80"
4616 ";\n"
4617 " ^~~~~~~~~~~\n"
4618 " ------------------\n"
4619 " (long\xf0\x9f\x98\x82"
4620 " *)(f\xf0\x9f\x98\x82"
4621 " *)\n",
4622 pp_formatted_text (dc.printer));
4625 /* Example of insertion fix-it hints that would overlap. */
4627 test_diagnostic_context dc;
4628 rich_location richloc (line_table, expr);
4629 richloc.add_fixit_insert_before
4630 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4631 richloc.add_fixit_insert_after (close_paren, "TEST");
4633 /* The first insertion is long enough that if printed naively,
4634 it would overlap with the second.
4635 Verify that they are printed as a single replacement. */
4636 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4637 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4638 " *f = (f\xf0\x9f\x98\x82"
4639 " *)ptr->field\xcf\x80"
4640 ";\n"
4641 " ^~~~~~~~~~~\n"
4642 " -------\n"
4643 " L\xf0\x9f\x98\x82"
4644 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4645 " *)TEST\n",
4646 pp_formatted_text (dc.printer));
4650 /* Verify that the line_corrections machinery correctly prints
4651 overlapping fixit-hints that have been added in the wrong
4652 order.
4653 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4655 static void
4656 test_overlapped_fixit_printing_2 (const line_table_case &case_)
4658 /* Create a tempfile and write some text to it.
4659 ...000000000111111111122222222223333333333.
4660 ...123456789012345678901234567890123456789. */
4661 const char *content
4662 = ("int a5[][0][0] = { 1, 2 };\n");
4663 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4664 line_table_test ltt (case_);
4666 const line_map_ordinary *ord_map
4667 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4668 tmp.get_filename (), 0));
4670 linemap_line_start (line_table, 1, 100);
4672 const location_t final_line_end
4673 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
4675 /* Don't attempt to run the tests if column data might be unavailable. */
4676 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4677 return;
4679 const location_t col_1
4680 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4681 const location_t col_20
4682 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
4683 const location_t col_21
4684 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
4685 const location_t col_23
4686 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4687 const location_t col_25
4688 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
4690 /* Two insertions, in the wrong order. */
4692 rich_location richloc (line_table, col_20);
4693 richloc.add_fixit_insert_before (col_23, "{");
4694 richloc.add_fixit_insert_before (col_21, "}");
4696 /* These fixits should be accepted; they can't be consolidated. */
4697 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4698 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4699 ASSERT_EQ (column_range (23, 22), get_affected_range (hint_0, CU_BYTES));
4700 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
4701 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4702 ASSERT_EQ (column_range (21, 20), get_affected_range (hint_1, CU_BYTES));
4703 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
4705 /* Verify that they're printed correctly. */
4706 test_diagnostic_context dc;
4707 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4708 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4709 " ^\n"
4710 " } {\n",
4711 pp_formatted_text (dc.printer));
4714 /* Various overlapping insertions, some occurring "out of order"
4715 (reproducing the fix-it hints from PR c/81405). */
4717 test_diagnostic_context dc;
4718 rich_location richloc (line_table, col_20);
4720 richloc.add_fixit_insert_before (col_20, "{{");
4721 richloc.add_fixit_insert_before (col_21, "}}");
4722 richloc.add_fixit_insert_before (col_23, "{");
4723 richloc.add_fixit_insert_before (col_21, "}");
4724 richloc.add_fixit_insert_before (col_23, "{{");
4725 richloc.add_fixit_insert_before (col_25, "}");
4726 richloc.add_fixit_insert_before (col_21, "}");
4727 richloc.add_fixit_insert_before (col_1, "{");
4728 richloc.add_fixit_insert_before (col_25, "}");
4729 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4730 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4731 " ^\n"
4732 " { -----\n"
4733 " {{1}}}}, {{{2 }}\n",
4734 pp_formatted_text (dc.printer));
4738 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4740 static void
4741 test_fixit_insert_containing_newline (const line_table_case &case_)
4743 /* Create a tempfile and write some text to it.
4744 .........................0000000001111111.
4745 .........................1234567890123456. */
4746 const char *old_content = (" case 'a':\n" /* line 1. */
4747 " x = a;\n" /* line 2. */
4748 " case 'b':\n" /* line 3. */
4749 " x = b;\n");/* line 4. */
4751 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4752 line_table_test ltt (case_);
4753 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
4755 location_t case_start = linemap_position_for_column (line_table, 5);
4756 location_t case_finish = linemap_position_for_column (line_table, 13);
4757 location_t case_loc = make_location (case_start, case_start, case_finish);
4758 location_t line_start = linemap_position_for_column (line_table, 1);
4760 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4761 return;
4763 /* Add a "break;" on a line by itself before line 3 i.e. before
4764 column 1 of line 3. */
4766 rich_location richloc (line_table, case_loc);
4767 richloc.add_fixit_insert_before (line_start, " break;\n");
4769 /* Without line numbers. */
4771 test_diagnostic_context dc;
4772 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4773 ASSERT_STREQ (" x = a;\n"
4774 "+ break;\n"
4775 " case 'b':\n"
4776 " ^~~~~~~~~\n",
4777 pp_formatted_text (dc.printer));
4780 /* With line numbers. */
4782 test_diagnostic_context dc;
4783 dc.show_line_numbers_p = true;
4784 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4785 ASSERT_STREQ (" 2 | x = a;\n"
4786 " +++ |+ break;\n"
4787 " 3 | case 'b':\n"
4788 " | ^~~~~~~~~\n",
4789 pp_formatted_text (dc.printer));
4793 /* Verify that attempts to add text with a newline fail when the
4794 insertion point is *not* at the start of a line. */
4796 rich_location richloc (line_table, case_loc);
4797 richloc.add_fixit_insert_before (case_start, "break;\n");
4798 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4799 test_diagnostic_context dc;
4800 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4801 ASSERT_STREQ (" case 'b':\n"
4802 " ^~~~~~~~~\n",
4803 pp_formatted_text (dc.printer));
4807 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4808 of the file, where the fix-it is printed in a different line-span
4809 to the primary range of the diagnostic. */
4811 static void
4812 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
4814 /* Create a tempfile and write some text to it.
4815 .........................0000000001111111.
4816 .........................1234567890123456. */
4817 const char *old_content = ("test (int ch)\n" /* line 1. */
4818 "{\n" /* line 2. */
4819 " putchar (ch);\n" /* line 3. */
4820 "}\n"); /* line 4. */
4822 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4823 line_table_test ltt (case_);
4825 const line_map_ordinary *ord_map = linemap_check_ordinary
4826 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4827 linemap_line_start (line_table, 1, 100);
4829 /* The primary range is the "putchar" token. */
4830 location_t putchar_start
4831 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
4832 location_t putchar_finish
4833 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
4834 location_t putchar_loc
4835 = make_location (putchar_start, putchar_start, putchar_finish);
4836 rich_location richloc (line_table, putchar_loc);
4838 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
4839 location_t file_start
4840 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4841 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
4843 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4844 return;
4847 test_diagnostic_context dc;
4848 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4849 ASSERT_STREQ ("FILENAME:1:1:\n"
4850 "+#include <stdio.h>\n"
4851 " test (int ch)\n"
4852 "FILENAME:3:2:\n"
4853 " putchar (ch);\n"
4854 " ^~~~~~~\n",
4855 pp_formatted_text (dc.printer));
4858 /* With line-numbering, the line spans are close enough to be
4859 consolidated, since it makes little sense to skip line 2. */
4861 test_diagnostic_context dc;
4862 dc.show_line_numbers_p = true;
4863 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4864 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
4865 " 1 | test (int ch)\n"
4866 " 2 | {\n"
4867 " 3 | putchar (ch);\n"
4868 " | ^~~~~~~\n",
4869 pp_formatted_text (dc.printer));
4873 /* Replacement fix-it hint containing a newline.
4874 This will fail, as newlines are only supported when inserting at the
4875 beginning of a line. */
4877 static void
4878 test_fixit_replace_containing_newline (const line_table_case &case_)
4880 /* Create a tempfile and write some text to it.
4881 .........................0000000001111.
4882 .........................1234567890123. */
4883 const char *old_content = "foo = bar ();\n";
4885 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4886 line_table_test ltt (case_);
4887 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4889 /* Replace the " = " with "\n = ", as if we were reformatting an
4890 overly long line. */
4891 location_t start = linemap_position_for_column (line_table, 4);
4892 location_t finish = linemap_position_for_column (line_table, 6);
4893 location_t loc = linemap_position_for_column (line_table, 13);
4894 rich_location richloc (line_table, loc);
4895 source_range range = source_range::from_locations (start, finish);
4896 richloc.add_fixit_replace (range, "\n =");
4898 /* Arbitrary newlines are not yet supported within fix-it hints, so
4899 the fix-it should not be displayed. */
4900 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4902 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4903 return;
4905 test_diagnostic_context dc;
4906 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4907 ASSERT_STREQ (" foo = bar ();\n"
4908 " ^\n",
4909 pp_formatted_text (dc.printer));
4912 /* Fix-it hint, attempting to delete a newline.
4913 This will fail, as we currently only support fix-it hints that
4914 affect one line at a time. */
4916 static void
4917 test_fixit_deletion_affecting_newline (const line_table_case &case_)
4919 /* Create a tempfile and write some text to it.
4920 ..........................0000000001111.
4921 ..........................1234567890123. */
4922 const char *old_content = ("foo = bar (\n"
4923 " );\n");
4925 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4926 line_table_test ltt (case_);
4927 const line_map_ordinary *ord_map = linemap_check_ordinary
4928 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4929 linemap_line_start (line_table, 1, 100);
4931 /* Attempt to delete the " (\n...)". */
4932 location_t start
4933 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
4934 location_t caret
4935 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
4936 location_t finish
4937 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
4938 location_t loc = make_location (caret, start, finish);
4939 rich_location richloc (line_table, loc);
4940 richloc. add_fixit_remove ();
4942 /* Fix-it hints that affect more than one line are not yet supported, so
4943 the fix-it should not be displayed. */
4944 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4946 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4947 return;
4949 test_diagnostic_context dc;
4950 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4951 ASSERT_STREQ (" foo = bar (\n"
4952 " ~^\n"
4953 " );\n"
4954 " ~ \n",
4955 pp_formatted_text (dc.printer));
4958 /* Verify that line numbers are correctly printed for the case of
4959 a multiline range in which the width of the line numbers changes
4960 (e.g. from "9" to "10"). */
4962 static void
4963 test_line_numbers_multiline_range ()
4965 /* Create a tempfile and write some text to it. */
4966 pretty_printer pp;
4967 for (int i = 0; i < 20; i++)
4968 /* .........0000000001111111.
4969 .............1234567890123456. */
4970 pp_printf (&pp, "this is line %i\n", i + 1);
4971 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
4972 line_table_test ltt;
4974 const line_map_ordinary *ord_map = linemap_check_ordinary
4975 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4976 linemap_line_start (line_table, 1, 100);
4978 /* Create a multi-line location, starting at the "line" of line 9, with
4979 a caret on the "is" of line 10, finishing on the "this" line 11. */
4981 location_t start
4982 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
4983 location_t caret
4984 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
4985 location_t finish
4986 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
4987 location_t loc = make_location (caret, start, finish);
4989 test_diagnostic_context dc;
4990 dc.show_line_numbers_p = true;
4991 dc.min_margin_width = 0;
4992 gcc_rich_location richloc (loc);
4993 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4994 ASSERT_STREQ (" 9 | this is line 9\n"
4995 " | ~~~~~~\n"
4996 "10 | this is line 10\n"
4997 " | ~~~~~^~~~~~~~~~\n"
4998 "11 | this is line 11\n"
4999 " | ~~~~ \n",
5000 pp_formatted_text (dc.printer));
5003 /* Run all of the selftests within this file. */
5005 void
5006 diagnostic_show_locus_c_tests ()
5008 test_line_span ();
5010 test_layout_range_for_single_point ();
5011 test_layout_range_for_single_line ();
5012 test_layout_range_for_multiple_lines ();
5014 for_each_line_table_case (test_layout_x_offset_display_utf8);
5016 test_get_line_bytes_without_trailing_whitespace ();
5018 test_diagnostic_show_locus_unknown_location ();
5020 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5021 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5022 for_each_line_table_case (test_add_location_if_nearby);
5023 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5024 for_each_line_table_case (test_fixit_consolidation);
5025 for_each_line_table_case (test_overlapped_fixit_printing);
5026 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5027 for_each_line_table_case (test_overlapped_fixit_printing_2);
5028 for_each_line_table_case (test_fixit_insert_containing_newline);
5029 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5030 for_each_line_table_case (test_fixit_replace_containing_newline);
5031 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5033 test_line_numbers_multiline_range ();
5036 } // namespace selftest
5038 #endif /* #if CHECKING_P */
5040 #if __GNUC__ >= 10
5041 # pragma GCC diagnostic pop
5042 #endif