Fix typo in t-dimode
[official-gcc.git] / gcc / diagnostic-show-locus.c
blob67be213c5a2a89bc00ee8f405239004aee802362
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 #include "cpplib.h"
35 #ifdef HAVE_TERMIOS_H
36 # include <termios.h>
37 #endif
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
41 #endif
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
45 #if __GNUC__ >= 10
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
48 #endif
50 /* Classes for rendering source code and diagnostics, within an
51 anonymous namespace.
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
55 namespace {
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
59 this point. */
61 struct point_state
63 int range_idx;
64 bool draw_caret_p;
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
70 - normal text
71 - range 0 (the "primary location")
72 - range 1
73 - range 2
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
83 class colorizer
85 public:
86 colorizer (diagnostic_context *context,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
90 void set_range (int range_idx)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
97 set_state (0);
98 else
99 set_state (range_idx);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
105 private:
106 void set_state (int state);
107 void begin_state (int state);
108 void finish_state (int state);
109 const char *get_color_by_name (const char *);
111 private:
112 static const int STATE_NORMAL_TEXT = -1;
113 static const int STATE_FIXIT_INSERT = -2;
114 static const int STATE_FIXIT_DELETE = -3;
116 diagnostic_context *m_context;
117 diagnostic_t m_diagnostic_kind;
118 int m_current_state;
119 const char *m_range1;
120 const char *m_range2;
121 const char *m_fixit_insert;
122 const char *m_fixit_delete;
123 const char *m_stop_color;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
162 enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col : public expanded_location
177 public:
178 exploc_with_display_col (const expanded_location &exploc,
179 const cpp_char_column_policy &policy,
180 enum location_aspect aspect)
181 : expanded_location (exploc),
182 m_display_col (location_compute_display_column (exploc, policy))
184 if (exploc.column > 0)
186 /* m_display_col is now the final column of the byte.
187 If escaping has happened, we may want the first column instead. */
188 if (aspect != LOCATION_ASPECT_FINISH)
190 expanded_location prev_exploc (exploc);
191 prev_exploc.column--;
192 int prev_display_col
193 = (location_compute_display_column (prev_exploc, policy));
194 m_display_col = prev_display_col + 1;
199 int m_display_col;
203 /* A point within a layout_range; similar to an exploc_with_display_col,
204 but after filtering on file. */
206 class layout_point
208 public:
209 layout_point (const exploc_with_display_col &exploc)
210 : m_line (exploc.line)
212 m_columns[CU_BYTES] = exploc.column;
213 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
216 linenum_type m_line;
217 int m_columns[CU_NUM_UNITS];
220 /* A class for use by "class layout" below: a filtered location_range. */
222 class layout_range
224 public:
225 layout_range (const exploc_with_display_col &start_exploc,
226 const exploc_with_display_col &finish_exploc,
227 enum range_display_kind range_display_kind,
228 const exploc_with_display_col &caret_exploc,
229 unsigned original_idx,
230 const range_label *label);
232 bool contains_point (linenum_type row, int column,
233 enum column_unit col_unit) const;
234 bool intersects_line_p (linenum_type row) const;
236 layout_point m_start;
237 layout_point m_finish;
238 enum range_display_kind m_range_display_kind;
239 layout_point m_caret;
240 unsigned m_original_idx;
241 const range_label *m_label;
244 /* A struct for use by layout::print_source_line for telling
245 layout::print_annotation_line the extents of the source line that
246 it printed, so that underlines can be clipped appropriately. Units
247 are 1-based display columns. */
249 struct line_bounds
251 int m_first_non_ws_disp_col;
252 int m_last_non_ws_disp_col;
254 line_bounds ()
256 m_first_non_ws_disp_col = INT_MAX;
257 m_last_non_ws_disp_col = 0;
261 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
262 or "line 23"). During the layout ctor, layout::calculate_line_spans
263 splits the pertinent source lines into a list of disjoint line_span
264 instances (e.g. lines 5-10, lines 15-20, line 23). */
266 class line_span
268 public:
269 line_span (linenum_type first_line, linenum_type last_line)
270 : m_first_line (first_line), m_last_line (last_line)
272 gcc_assert (first_line <= last_line);
274 linenum_type get_first_line () const { return m_first_line; }
275 linenum_type get_last_line () const { return m_last_line; }
277 bool contains_line_p (linenum_type line) const
279 return line >= m_first_line && line <= m_last_line;
282 static int comparator (const void *p1, const void *p2)
284 const line_span *ls1 = (const line_span *)p1;
285 const line_span *ls2 = (const line_span *)p2;
286 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
287 if (first_line_cmp)
288 return first_line_cmp;
289 return compare (ls1->m_last_line, ls2->m_last_line);
292 linenum_type m_first_line;
293 linenum_type m_last_line;
296 #if CHECKING_P
298 /* Selftests for line_span. */
300 static void
301 test_line_span ()
303 line_span line_one (1, 1);
304 ASSERT_EQ (1, line_one.get_first_line ());
305 ASSERT_EQ (1, line_one.get_last_line ());
306 ASSERT_FALSE (line_one.contains_line_p (0));
307 ASSERT_TRUE (line_one.contains_line_p (1));
308 ASSERT_FALSE (line_one.contains_line_p (2));
310 line_span lines_1_to_3 (1, 3);
311 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
312 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
313 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
316 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
317 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
318 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
320 /* A linenum > 2^31. */
321 const linenum_type LARGEST_LINE = 0xffffffff;
322 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
323 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
326 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
327 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
330 #endif /* #if CHECKING_P */
332 /* A bundle of information containing how to print unicode
333 characters and bytes when quoting source code.
335 Provides a unified place to support escaping some subset
336 of characters to some format.
338 Extends char_column_policy; printing is split out to avoid
339 libcpp having to know about pretty_printer. */
341 struct char_display_policy : public cpp_char_column_policy
343 public:
344 char_display_policy (int tabstop,
345 int (*width_cb) (cppchar_t c),
346 void (*print_cb) (pretty_printer *pp,
347 const cpp_decoded_char &cp))
348 : cpp_char_column_policy (tabstop, width_cb),
349 m_print_cb (print_cb)
353 void (*m_print_cb) (pretty_printer *pp,
354 const cpp_decoded_char &cp);
357 /* A class to control the overall layout when printing a diagnostic.
359 The layout is determined within the constructor.
360 It is then printed by repeatedly calling the "print_source_line",
361 "print_annotation_line" and "print_any_fixits" methods.
363 We assume we have disjoint ranges. */
365 class layout
367 public:
368 layout (diagnostic_context *context,
369 rich_location *richloc,
370 diagnostic_t diagnostic_kind);
372 bool maybe_add_location_range (const location_range *loc_range,
373 unsigned original_idx,
374 bool restrict_to_current_line_spans);
376 int get_num_line_spans () const { return m_line_spans.length (); }
377 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
379 int get_linenum_width () const { return m_linenum_width; }
380 int get_x_offset_display () const { return m_x_offset_display; }
382 void print_gap_in_line_numbering ();
383 bool print_heading_for_line_span_index_p (int line_span_idx) const;
385 expanded_location get_expanded_location (const line_span *) const;
387 void print_line (linenum_type row);
389 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
391 private:
392 bool will_show_line_p (linenum_type row) const;
393 void print_leading_fixits (linenum_type row);
394 line_bounds print_source_line (linenum_type row, const char *line,
395 int line_bytes);
396 bool should_print_annotation_line_p (linenum_type row) const;
397 void start_annotation_line (char margin_char = ' ') const;
398 void print_annotation_line (linenum_type row, const line_bounds lbounds);
399 void print_any_labels (linenum_type row);
400 void print_trailing_fixits (linenum_type row);
402 bool annotation_line_showed_range_p (linenum_type line, int start_column,
403 int finish_column) const;
404 void show_ruler (int max_column) const;
406 bool validate_fixit_hint_p (const fixit_hint *hint);
408 void calculate_line_spans ();
409 void calculate_linenum_width ();
410 void calculate_x_offset_display ();
412 void print_newline ();
414 bool
415 get_state_at_point (/* Inputs. */
416 linenum_type row, int column,
417 int first_non_ws, int last_non_ws,
418 enum column_unit col_unit,
419 /* Outputs. */
420 point_state *out_state);
423 get_x_bound_for_row (linenum_type row, int caret_column,
424 int last_non_ws);
426 void
427 move_to_column (int *column, int dest_column, bool add_left_margin);
429 private:
430 diagnostic_context *m_context;
431 pretty_printer *m_pp;
432 char_display_policy m_policy;
433 location_t m_primary_loc;
434 exploc_with_display_col m_exploc;
435 colorizer m_colorizer;
436 bool m_colorize_source_p;
437 bool m_show_labels_p;
438 bool m_show_line_numbers_p;
439 bool m_diagnostic_path_p;
440 auto_vec <layout_range> m_layout_ranges;
441 auto_vec <const fixit_hint *> m_fixit_hints;
442 auto_vec <line_span> m_line_spans;
443 int m_linenum_width;
444 int m_x_offset_display;
445 bool m_escape_on_output;
448 /* Implementation of "class colorizer". */
450 /* The constructor for "colorizer". Lookup and store color codes for the
451 different kinds of things we might need to print. */
453 colorizer::colorizer (diagnostic_context *context,
454 diagnostic_t diagnostic_kind) :
455 m_context (context),
456 m_diagnostic_kind (diagnostic_kind),
457 m_current_state (STATE_NORMAL_TEXT)
459 m_range1 = get_color_by_name ("range1");
460 m_range2 = get_color_by_name ("range2");
461 m_fixit_insert = get_color_by_name ("fixit-insert");
462 m_fixit_delete = get_color_by_name ("fixit-delete");
463 m_stop_color = colorize_stop (pp_show_color (context->printer));
466 /* The destructor for "colorize". If colorization is on, print a code to
467 turn it off. */
469 colorizer::~colorizer ()
471 finish_state (m_current_state);
474 /* Update state, printing color codes if necessary if there's a state
475 change. */
477 void
478 colorizer::set_state (int new_state)
480 if (m_current_state != new_state)
482 finish_state (m_current_state);
483 m_current_state = new_state;
484 begin_state (new_state);
488 /* Turn on any colorization for STATE. */
490 void
491 colorizer::begin_state (int state)
493 switch (state)
495 case STATE_NORMAL_TEXT:
496 break;
498 case STATE_FIXIT_INSERT:
499 pp_string (m_context->printer, m_fixit_insert);
500 break;
502 case STATE_FIXIT_DELETE:
503 pp_string (m_context->printer, m_fixit_delete);
504 break;
506 case 0:
507 /* Make range 0 be the same color as the "kind" text
508 (error vs warning vs note). */
509 pp_string
510 (m_context->printer,
511 colorize_start (pp_show_color (m_context->printer),
512 diagnostic_get_color_for_kind (m_diagnostic_kind)));
513 break;
515 case 1:
516 pp_string (m_context->printer, m_range1);
517 break;
519 case 2:
520 pp_string (m_context->printer, m_range2);
521 break;
523 default:
524 /* For ranges beyond 2, alternate between color 1 and color 2. */
526 gcc_assert (state > 2);
527 pp_string (m_context->printer,
528 state % 2 ? m_range1 : m_range2);
530 break;
534 /* Turn off any colorization for STATE. */
536 void
537 colorizer::finish_state (int state)
539 if (state != STATE_NORMAL_TEXT)
540 pp_string (m_context->printer, m_stop_color);
543 /* Get the color code for NAME (or the empty string if
544 colorization is disabled). */
546 const char *
547 colorizer::get_color_by_name (const char *name)
549 return colorize_start (pp_show_color (m_context->printer), name);
552 /* Implementation of class layout_range. */
554 /* The constructor for class layout_range.
555 Initialize various layout_point fields from expanded_location
556 equivalents; we've already filtered on file. */
558 layout_range::layout_range (const exploc_with_display_col &start_exploc,
559 const exploc_with_display_col &finish_exploc,
560 enum range_display_kind range_display_kind,
561 const exploc_with_display_col &caret_exploc,
562 unsigned original_idx,
563 const range_label *label)
564 : m_start (start_exploc),
565 m_finish (finish_exploc),
566 m_range_display_kind (range_display_kind),
567 m_caret (caret_exploc),
568 m_original_idx (original_idx),
569 m_label (label)
573 /* Is (column, row) within the given range?
574 We've already filtered on the file.
576 Ranges are closed (both limits are within the range).
578 Example A: a single-line range:
579 start: (col=22, line=2)
580 finish: (col=38, line=2)
582 |00000011111111112222222222333333333344444444444
583 |34567890123456789012345678901234567890123456789
584 --+-----------------------------------------------
585 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
586 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
587 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
589 Example B: a multiline range with
590 start: (col=14, line=3)
591 finish: (col=08, line=5)
593 |00000011111111112222222222333333333344444444444
594 |34567890123456789012345678901234567890123456789
595 --+-----------------------------------------------
596 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
597 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
598 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
599 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
600 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
601 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
602 --+-----------------------------------------------
604 Legend:
605 - 'b' indicates a point *before* the range
606 - 'S' indicates the start of the range
607 - 'w' indicates a point within the range
608 - 'F' indicates the finish of the range (which is
609 within it).
610 - 'a' indicates a subsequent point *after* the range.
612 COL_UNIT controls whether we check the byte column or
613 the display column; one or the other is more convenient
614 depending on the context. */
616 bool
617 layout_range::contains_point (linenum_type row, int column,
618 enum column_unit col_unit) const
620 gcc_assert (m_start.m_line <= m_finish.m_line);
621 /* ...but the equivalent isn't true for the columns;
622 consider example B in the comment above. */
624 if (row < m_start.m_line)
625 /* Points before the first line of the range are
626 outside it (corresponding to line 01 in example A
627 and lines 01 and 02 in example B above). */
628 return false;
630 if (row == m_start.m_line)
631 /* On same line as start of range (corresponding
632 to line 02 in example A and line 03 in example B). */
634 if (column < m_start.m_columns[col_unit])
635 /* Points on the starting line of the range, but
636 before the column in which it begins. */
637 return false;
639 if (row < m_finish.m_line)
640 /* This is a multiline range; the point
641 is within it (corresponds to line 03 in example B
642 from column 14 onwards) */
643 return true;
644 else
646 /* This is a single-line range. */
647 gcc_assert (row == m_finish.m_line);
648 return column <= m_finish.m_columns[col_unit];
652 /* The point is in a line beyond that containing the
653 start of the range: lines 03 onwards in example A,
654 and lines 04 onwards in example B. */
655 gcc_assert (row > m_start.m_line);
657 if (row > m_finish.m_line)
658 /* The point is beyond the final line of the range
659 (lines 03 onwards in example A, and lines 06 onwards
660 in example B). */
661 return false;
663 if (row < m_finish.m_line)
665 /* The point is in a line that's fully within a multiline
666 range (e.g. line 04 in example B). */
667 gcc_assert (m_start.m_line < m_finish.m_line);
668 return true;
671 gcc_assert (row == m_finish.m_line);
673 return column <= m_finish.m_columns[col_unit];
676 /* Does this layout_range contain any part of line ROW? */
678 bool
679 layout_range::intersects_line_p (linenum_type row) const
681 gcc_assert (m_start.m_line <= m_finish.m_line);
682 if (row < m_start.m_line)
683 return false;
684 if (row > m_finish.m_line)
685 return false;
686 return true;
689 #if CHECKING_P
691 /* Default for when we don't care what the tab expansion is set to. */
692 static const int def_tabstop = 8;
694 static cpp_char_column_policy def_policy ()
696 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
699 /* Create some expanded locations for testing layout_range. The filename
700 member of the explocs is set to the empty string. This member will only be
701 inspected by the calls to location_compute_display_column() made from the
702 layout_point constructors. That function will check for an empty filename
703 argument and not attempt to open it, rather treating the non-existent data
704 as if the display width were the same as the byte count. Tests exercising a
705 real difference between byte count and display width are performed later,
706 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
708 static layout_range
709 make_range (int start_line, int start_col, int end_line, int end_col)
711 const expanded_location start_exploc
712 = {"", start_line, start_col, NULL, false};
713 const expanded_location finish_exploc
714 = {"", end_line, end_col, NULL, false};
715 return layout_range (exploc_with_display_col (start_exploc, def_policy (),
716 LOCATION_ASPECT_START),
717 exploc_with_display_col (finish_exploc, def_policy (),
718 LOCATION_ASPECT_FINISH),
719 SHOW_RANGE_WITHOUT_CARET,
720 exploc_with_display_col (start_exploc, def_policy (),
721 LOCATION_ASPECT_CARET),
722 0, NULL);
725 /* Selftests for layout_range::contains_point and
726 layout_range::intersects_line_p. */
728 /* Selftest for layout_range, where the layout_range
729 is a range with start==end i.e. a single point. */
731 static void
732 test_layout_range_for_single_point ()
734 layout_range point = make_range (7, 10, 7, 10);
736 /* Tests for layout_range::contains_point. */
738 for (int i = 0; i != CU_NUM_UNITS; ++i)
740 const enum column_unit col_unit = (enum column_unit) i;
742 /* Before the line. */
743 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
745 /* On the line, but before start. */
746 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
748 /* At the point. */
749 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
751 /* On the line, after the point. */
752 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
754 /* After the line. */
755 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
758 /* Tests for layout_range::intersects_line_p. */
759 ASSERT_FALSE (point.intersects_line_p (6));
760 ASSERT_TRUE (point.intersects_line_p (7));
761 ASSERT_FALSE (point.intersects_line_p (8));
764 /* Selftest for layout_range, where the layout_range
765 is the single-line range shown as "Example A" above. */
767 static void
768 test_layout_range_for_single_line ()
770 layout_range example_a = make_range (2, 22, 2, 38);
772 /* Tests for layout_range::contains_point. */
774 for (int i = 0; i != CU_NUM_UNITS; ++i)
776 const enum column_unit col_unit = (enum column_unit) i;
778 /* Before the line. */
779 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
781 /* On the line, but before start. */
782 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
784 /* On the line, at the start. */
785 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
787 /* On the line, within the range. */
788 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
790 /* On the line, at the end. */
791 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
793 /* On the line, after the end. */
794 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
796 /* After the line. */
797 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
800 /* Tests for layout_range::intersects_line_p. */
801 ASSERT_FALSE (example_a.intersects_line_p (1));
802 ASSERT_TRUE (example_a.intersects_line_p (2));
803 ASSERT_FALSE (example_a.intersects_line_p (3));
806 /* Selftest for layout_range, where the layout_range
807 is the multi-line range shown as "Example B" above. */
809 static void
810 test_layout_range_for_multiple_lines ()
812 layout_range example_b = make_range (3, 14, 5, 8);
814 /* Tests for layout_range::contains_point. */
816 for (int i = 0; i != CU_NUM_UNITS; ++i)
818 const enum column_unit col_unit = (enum column_unit) i;
820 /* Before first line. */
821 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
823 /* On the first line, but before start. */
824 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
826 /* At the start. */
827 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
829 /* On the first line, within the range. */
830 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
832 /* On an interior line.
833 The column number should not matter; try various boundary
834 values. */
835 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
836 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
837 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
838 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
839 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
840 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
841 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
843 /* On the final line, before the end. */
844 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
846 /* On the final line, at the end. */
847 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
849 /* On the final line, after the end. */
850 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
852 /* After the line. */
853 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
856 /* Tests for layout_range::intersects_line_p. */
857 ASSERT_FALSE (example_b.intersects_line_p (2));
858 ASSERT_TRUE (example_b.intersects_line_p (3));
859 ASSERT_TRUE (example_b.intersects_line_p (4));
860 ASSERT_TRUE (example_b.intersects_line_p (5));
861 ASSERT_FALSE (example_b.intersects_line_p (6));
864 #endif /* #if CHECKING_P */
866 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
867 (still in bytes, not display cols) without any trailing whitespace. */
869 static int
870 get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
872 int result = line_bytes;
873 while (result > 0)
875 char ch = line[result - 1];
876 if (ch == ' ' || ch == '\t' || ch == '\r')
877 result--;
878 else
879 break;
881 gcc_assert (result >= 0);
882 gcc_assert (result <= line_bytes);
883 gcc_assert (result == 0 ||
884 (line[result - 1] != ' '
885 && line[result -1] != '\t'
886 && line[result -1] != '\r'));
887 return result;
890 #if CHECKING_P
892 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
894 static void
895 assert_eq (const char *line, int expected_bytes)
897 int actual_value
898 = get_line_bytes_without_trailing_whitespace (line, strlen (line));
899 ASSERT_EQ (actual_value, expected_bytes);
902 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
903 various inputs. It is not required to handle newlines. */
905 static void
906 test_get_line_bytes_without_trailing_whitespace ()
908 assert_eq ("", 0);
909 assert_eq (" ", 0);
910 assert_eq ("\t", 0);
911 assert_eq ("\r", 0);
912 assert_eq ("hello world", 11);
913 assert_eq ("hello world ", 11);
914 assert_eq ("hello world \t\t ", 11);
915 assert_eq ("hello world\r", 11);
918 #endif /* #if CHECKING_P */
920 /* Helper function for layout's ctor, for sanitizing locations relative
921 to the primary location within a diagnostic.
923 Compare LOC_A and LOC_B to see if it makes sense to print underlines
924 connecting their expanded locations. Doing so is only guaranteed to
925 make sense if the locations share the same macro expansion "history"
926 i.e. they can be traced through the same macro expansions, eventually
927 reaching an ordinary map.
929 This may be too strong a condition, but it effectively sanitizes
930 PR c++/70105, which has an example of printing an expression where the
931 final location of the expression is in a different macro, which
932 erroneously was leading to hundreds of lines of irrelevant source
933 being printed. */
935 static bool
936 compatible_locations_p (location_t loc_a, location_t loc_b)
938 if (IS_ADHOC_LOC (loc_a))
939 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
940 if (IS_ADHOC_LOC (loc_b))
941 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
943 /* If either location is one of the special locations outside of a
944 linemap, they are only compatible if they are equal. */
945 if (loc_a < RESERVED_LOCATION_COUNT
946 || loc_b < RESERVED_LOCATION_COUNT)
947 return loc_a == loc_b;
949 const line_map *map_a = linemap_lookup (line_table, loc_a);
950 linemap_assert (map_a);
952 const line_map *map_b = linemap_lookup (line_table, loc_b);
953 linemap_assert (map_b);
955 /* Are they within the same map? */
956 if (map_a == map_b)
958 /* Are both within the same macro expansion? */
959 if (linemap_macro_expansion_map_p (map_a))
961 /* If so, then they're only compatible if either both are
962 from the macro definition, or both from the macro arguments. */
963 bool loc_a_from_defn
964 = linemap_location_from_macro_definition_p (line_table, loc_a);
965 bool loc_b_from_defn
966 = linemap_location_from_macro_definition_p (line_table, loc_b);
967 if (loc_a_from_defn != loc_b_from_defn)
968 return false;
970 /* Expand each location towards the spelling location, and
971 recurse. */
972 const line_map_macro *macro_map = linemap_check_macro (map_a);
973 location_t loc_a_toward_spelling
974 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
975 macro_map,
976 loc_a);
977 location_t loc_b_toward_spelling
978 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
979 macro_map,
980 loc_b);
981 return compatible_locations_p (loc_a_toward_spelling,
982 loc_b_toward_spelling);
985 /* Otherwise they are within the same ordinary map. */
986 return true;
988 else
990 /* Within different maps. */
992 /* If either is within a macro expansion, they are incompatible. */
993 if (linemap_macro_expansion_map_p (map_a)
994 || linemap_macro_expansion_map_p (map_b))
995 return false;
997 /* Within two different ordinary maps; they are compatible iff they
998 are in the same file. */
999 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1000 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1001 return ord_map_a->to_file == ord_map_b->to_file;
1005 /* Comparator for sorting fix-it hints. */
1007 static int
1008 fixit_cmp (const void *p_a, const void *p_b)
1010 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1011 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1012 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1015 /* Callbacks for use when not escaping the source. */
1017 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1019 /* Callback for char_display_policy::m_print_cb for printing source chars
1020 when not escaping the source. */
1022 static void
1023 default_print_decoded_ch (pretty_printer *pp,
1024 const cpp_decoded_char &decoded_ch)
1026 for (const char *ptr = decoded_ch.m_start_byte;
1027 ptr != decoded_ch.m_next_byte; ptr++)
1029 if (*ptr == '\0' || *ptr == '\r')
1031 pp_space (pp);
1032 continue;
1035 pp_character (pp, *ptr);
1039 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1041 static const int width_per_escaped_byte = 4;
1043 /* Callback for char_column_policy::m_width_cb for determining the
1044 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1046 static int
1047 escape_as_bytes_width (cppchar_t ch)
1049 if (ch < 0x80 && ISPRINT (ch))
1050 return cpp_wcwidth (ch);
1051 else
1053 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1054 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1055 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1056 return 4 * width_per_escaped_byte;
1060 /* Callback for char_display_policy::m_print_cb for printing source chars
1061 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1063 static void
1064 escape_as_bytes_print (pretty_printer *pp,
1065 const cpp_decoded_char &decoded_ch)
1067 if (!decoded_ch.m_valid_ch)
1069 for (const char *iter = decoded_ch.m_start_byte;
1070 iter != decoded_ch.m_next_byte; ++iter)
1072 char buf[16];
1073 sprintf (buf, "<%02x>", (unsigned char)*iter);
1074 pp_string (pp, buf);
1076 return;
1079 cppchar_t ch = decoded_ch.m_ch;
1080 if (ch < 0x80 && ISPRINT (ch))
1081 pp_character (pp, ch);
1082 else
1084 for (const char *iter = decoded_ch.m_start_byte;
1085 iter < decoded_ch.m_next_byte; ++iter)
1087 char buf[16];
1088 sprintf (buf, "<%02x>", (unsigned char)*iter);
1089 pp_string (pp, buf);
1094 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1096 /* Callback for char_column_policy::m_width_cb for determining the
1097 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1099 static int
1100 escape_as_unicode_width (cppchar_t ch)
1102 if (ch < 0x80 && ISPRINT (ch))
1103 return cpp_wcwidth (ch);
1104 else
1106 // Width of "<U+%04x>"
1107 if (ch > 0xfffff)
1108 return 10;
1109 else if (ch > 0xffff)
1110 return 9;
1111 else
1112 return 8;
1116 /* Callback for char_display_policy::m_print_cb for printing source chars
1117 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1119 static void
1120 escape_as_unicode_print (pretty_printer *pp,
1121 const cpp_decoded_char &decoded_ch)
1123 if (!decoded_ch.m_valid_ch)
1125 escape_as_bytes_print (pp, decoded_ch);
1126 return;
1129 cppchar_t ch = decoded_ch.m_ch;
1130 if (ch < 0x80 && ISPRINT (ch))
1131 pp_character (pp, ch);
1132 else
1134 char buf[16];
1135 sprintf (buf, "<U+%04X>", ch);
1136 pp_string (pp, buf);
1140 /* Populate a char_display_policy based on DC and RICHLOC. */
1142 static char_display_policy
1143 make_policy (const diagnostic_context &dc,
1144 const rich_location &richloc)
1146 /* The default is to not escape non-ASCII bytes. */
1147 char_display_policy result
1148 (dc.tabstop, cpp_wcwidth, default_print_decoded_ch);
1150 /* If the diagnostic suggests escaping non-ASCII bytes, then
1151 use policy from user-supplied options. */
1152 if (richloc.escape_on_output_p ())
1154 result.m_undecoded_byte_width = width_per_escaped_byte;
1155 switch (dc.escape_format)
1157 default:
1158 gcc_unreachable ();
1159 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1160 result.m_width_cb = escape_as_unicode_width;
1161 result.m_print_cb = escape_as_unicode_print;
1162 break;
1163 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1164 result.m_width_cb = escape_as_bytes_width;
1165 result.m_print_cb = escape_as_bytes_print;
1166 break;
1170 return result;
1173 /* Implementation of class layout. */
1175 /* Constructor for class layout.
1177 Filter the ranges from the rich_location to those that we can
1178 sanely print, populating m_layout_ranges and m_fixit_hints.
1179 Determine the range of lines that we will print, splitting them
1180 up into an ordered list of disjoint spans of contiguous line numbers.
1181 Determine m_x_offset_display, to ensure that the primary caret
1182 will fit within the max_width provided by the diagnostic_context. */
1184 layout::layout (diagnostic_context * context,
1185 rich_location *richloc,
1186 diagnostic_t diagnostic_kind)
1187 : m_context (context),
1188 m_pp (context->printer),
1189 m_policy (make_policy (*context, *richloc)),
1190 m_primary_loc (richloc->get_range (0)->m_loc),
1191 m_exploc (richloc->get_expanded_location (0), m_policy,
1192 LOCATION_ASPECT_CARET),
1193 m_colorizer (context, diagnostic_kind),
1194 m_colorize_source_p (context->colorize_source_p),
1195 m_show_labels_p (context->show_labels_p),
1196 m_show_line_numbers_p (context->show_line_numbers_p),
1197 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1198 m_layout_ranges (richloc->get_num_locations ()),
1199 m_fixit_hints (richloc->get_num_fixit_hints ()),
1200 m_line_spans (1 + richloc->get_num_locations ()),
1201 m_linenum_width (0),
1202 m_x_offset_display (0),
1203 m_escape_on_output (richloc->escape_on_output_p ())
1205 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
1207 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1208 Ignore any ranges that are awkward to handle. */
1209 const location_range *loc_range = richloc->get_range (idx);
1210 maybe_add_location_range (loc_range, idx, false);
1213 /* Populate m_fixit_hints, filtering to only those that are in the
1214 same file. */
1215 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1217 const fixit_hint *hint = richloc->get_fixit_hint (i);
1218 if (validate_fixit_hint_p (hint))
1219 m_fixit_hints.safe_push (hint);
1222 /* Sort m_fixit_hints. */
1223 m_fixit_hints.qsort (fixit_cmp);
1225 /* Populate the indicated members. */
1226 calculate_line_spans ();
1227 calculate_linenum_width ();
1228 calculate_x_offset_display ();
1230 if (context->show_ruler_p)
1231 show_ruler (m_x_offset_display + m_context->caret_max_width);
1235 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1236 those that we can sanely print.
1238 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1239 (for use as extrinsic state by label ranges FIXME).
1241 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1242 filtered against this layout instance's current line spans: it
1243 will only be added if the location is fully within the lines
1244 already specified by other locations.
1246 Return true iff LOC_RANGE was added. */
1248 bool
1249 layout::maybe_add_location_range (const location_range *loc_range,
1250 unsigned original_idx,
1251 bool restrict_to_current_line_spans)
1253 gcc_assert (loc_range);
1255 /* Split the "range" into caret and range information. */
1256 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
1258 /* Expand the various locations. */
1259 expanded_location start
1260 = linemap_client_expand_location_to_spelling_point
1261 (src_range.m_start, LOCATION_ASPECT_START);
1262 expanded_location finish
1263 = linemap_client_expand_location_to_spelling_point
1264 (src_range.m_finish, LOCATION_ASPECT_FINISH);
1265 expanded_location caret
1266 = linemap_client_expand_location_to_spelling_point
1267 (loc_range->m_loc, LOCATION_ASPECT_CARET);
1269 /* If any part of the range isn't in the same file as the primary
1270 location of this diagnostic, ignore the range. */
1271 if (start.file != m_exploc.file)
1272 return false;
1273 if (finish.file != m_exploc.file)
1274 return false;
1275 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1276 if (caret.file != m_exploc.file)
1277 return false;
1279 /* Sanitize the caret location for non-primary ranges. */
1280 if (m_layout_ranges.length () > 0)
1281 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1282 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1283 /* Discard any non-primary ranges that can't be printed
1284 sanely relative to the primary location. */
1285 return false;
1287 /* Everything is now known to be in the correct source file,
1288 but it may require further sanitization. */
1289 layout_range ri (exploc_with_display_col (start, m_policy,
1290 LOCATION_ASPECT_START),
1291 exploc_with_display_col (finish, m_policy,
1292 LOCATION_ASPECT_FINISH),
1293 loc_range->m_range_display_kind,
1294 exploc_with_display_col (caret, m_policy,
1295 LOCATION_ASPECT_CARET),
1296 original_idx, loc_range->m_label);
1298 /* If we have a range that finishes before it starts (perhaps
1299 from something built via macro expansion), printing the
1300 range is likely to be nonsensical. Also, attempting to do so
1301 breaks assumptions within the printing code (PR c/68473).
1302 Similarly, don't attempt to print ranges if one or both ends
1303 of the range aren't sane to print relative to the
1304 primary location (PR c++/70105). */
1305 if (start.line > finish.line
1306 || !compatible_locations_p (src_range.m_start, m_primary_loc)
1307 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1309 /* Is this the primary location? */
1310 if (m_layout_ranges.length () == 0)
1312 /* We want to print the caret for the primary location, but
1313 we must sanitize away m_start and m_finish. */
1314 ri.m_start = ri.m_caret;
1315 ri.m_finish = ri.m_caret;
1317 else
1318 /* This is a non-primary range; ignore it. */
1319 return false;
1322 /* Potentially filter to just the lines already specified by other
1323 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1324 The layout ctor doesn't use it, and can't because m_line_spans
1325 hasn't been set up at that point. */
1326 if (restrict_to_current_line_spans)
1328 if (!will_show_line_p (start.line))
1329 return false;
1330 if (!will_show_line_p (finish.line))
1331 return false;
1332 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1333 if (!will_show_line_p (caret.line))
1334 return false;
1337 /* Passed all the tests; add the range to m_layout_ranges so that
1338 it will be printed. */
1339 m_layout_ranges.safe_push (ri);
1340 return true;
1343 /* Return true iff ROW is within one of the line spans for this layout. */
1345 bool
1346 layout::will_show_line_p (linenum_type row) const
1348 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1349 line_span_idx++)
1351 const line_span *line_span = get_line_span (line_span_idx);
1352 if (line_span->contains_line_p (row))
1353 return true;
1355 return false;
1358 /* Print a line showing a gap in the line numbers, for showing the boundary
1359 between two line spans. */
1361 void
1362 layout::print_gap_in_line_numbering ()
1364 gcc_assert (m_show_line_numbers_p);
1366 pp_emit_prefix (m_pp);
1368 for (int i = 0; i < m_linenum_width + 1; i++)
1369 pp_character (m_pp, '.');
1371 pp_newline (m_pp);
1374 /* Return true iff we should print a heading when starting the
1375 line span with the given index. */
1377 bool
1378 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1380 /* We print a heading for every change of line span, hence for every
1381 line span after the initial one. */
1382 if (line_span_idx > 0)
1383 return true;
1385 /* We also do it for the initial span if the primary location of the
1386 diagnostic is in a different span. */
1387 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1388 return true;
1390 return false;
1393 /* Get an expanded_location for the first location of interest within
1394 the given line_span.
1395 Used when printing a heading to indicate a new line span. */
1397 expanded_location
1398 layout::get_expanded_location (const line_span *line_span) const
1400 /* Whenever possible, use the caret location. */
1401 if (line_span->contains_line_p (m_exploc.line))
1402 return m_exploc;
1404 /* Otherwise, use the start of the first range that's present
1405 within the line_span. */
1406 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1408 const layout_range *lr = &m_layout_ranges[i];
1409 if (line_span->contains_line_p (lr->m_start.m_line))
1411 expanded_location exploc = m_exploc;
1412 exploc.line = lr->m_start.m_line;
1413 exploc.column = lr->m_start.m_columns[CU_BYTES];
1414 return exploc;
1418 /* Otherwise, use the location of the first fixit-hint present within
1419 the line_span. */
1420 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1422 const fixit_hint *hint = m_fixit_hints[i];
1423 location_t loc = hint->get_start_loc ();
1424 expanded_location exploc = expand_location (loc);
1425 if (line_span->contains_line_p (exploc.line))
1426 return exploc;
1429 /* It should not be possible to have a line span that didn't
1430 contain any of the layout_range or fixit_hint instances. */
1431 gcc_unreachable ();
1432 return m_exploc;
1435 /* Determine if HINT is meaningful to print within this layout. */
1437 bool
1438 layout::validate_fixit_hint_p (const fixit_hint *hint)
1440 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1441 return false;
1442 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1443 return false;
1445 return true;
1448 /* Determine the range of lines affected by HINT.
1449 This assumes that HINT has already been filtered by
1450 validate_fixit_hint_p, and so affects the correct source file. */
1452 static line_span
1453 get_line_span_for_fixit_hint (const fixit_hint *hint)
1455 gcc_assert (hint);
1457 int start_line = LOCATION_LINE (hint->get_start_loc ());
1459 /* For line-insertion fix-it hints, add the previous line to the
1460 span, to give the user more context on the proposed change. */
1461 if (hint->ends_with_newline_p ())
1462 if (start_line > 1)
1463 start_line--;
1465 return line_span (start_line,
1466 LOCATION_LINE (hint->get_next_loc ()));
1469 /* We want to print the pertinent source code at a diagnostic. The
1470 rich_location can contain multiple locations. This will have been
1471 filtered into m_exploc (the caret for the primary location) and
1472 m_layout_ranges, for those ranges within the same source file.
1474 We will print a subset of the lines within the source file in question,
1475 as a collection of "spans" of lines.
1477 This function populates m_line_spans with an ordered, disjoint list of
1478 the line spans of interest.
1480 Printing a gap between line spans takes one line, so, when printing
1481 line numbers, we allow a gap of up to one line between spans when
1482 merging, since it makes more sense to print the source line rather than a
1483 "gap-in-line-numbering" line. When not printing line numbers, it's
1484 better to be more explicit about what's going on, so keeping them as
1485 separate spans is preferred.
1487 For example, if the primary range is on lines 8-10, with secondary ranges
1488 covering lines 5-6 and lines 13-15:
1491 005 |RANGE 1
1492 006 |RANGE 1
1494 008 |PRIMARY RANGE
1495 009 |PRIMARY CARET
1496 010 |PRIMARY RANGE
1499 013 |RANGE 2
1500 014 |RANGE 2
1501 015 |RANGE 2
1504 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1506 With line numbering off (with span headers), we want three spans: lines 5-6,
1507 lines 8-10, and lines 13-15. */
1509 void
1510 layout::calculate_line_spans ()
1512 /* This should only be called once, by the ctor. */
1513 gcc_assert (m_line_spans.length () == 0);
1515 /* Populate tmp_spans with individual spans, for each of
1516 m_exploc, and for m_layout_ranges. */
1517 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1518 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1519 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1521 const layout_range *lr = &m_layout_ranges[i];
1522 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1523 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1524 lr->m_finish.m_line));
1527 /* Also add spans for any fix-it hints, in case they cover other lines. */
1528 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1530 const fixit_hint *hint = m_fixit_hints[i];
1531 gcc_assert (hint);
1532 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1535 /* Sort them. */
1536 tmp_spans.qsort(line_span::comparator);
1538 /* Now iterate through tmp_spans, copying into m_line_spans, and
1539 combining where possible. */
1540 gcc_assert (tmp_spans.length () > 0);
1541 m_line_spans.safe_push (tmp_spans[0]);
1542 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1544 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1545 const line_span *next = &tmp_spans[i];
1546 gcc_assert (next->m_first_line >= current->m_first_line);
1547 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1548 if ((linenum_arith_t)next->m_first_line
1549 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1551 /* We can merge them. */
1552 if (next->m_last_line > current->m_last_line)
1553 current->m_last_line = next->m_last_line;
1555 else
1557 /* No merger possible. */
1558 m_line_spans.safe_push (*next);
1562 /* Verify the result, in m_line_spans. */
1563 gcc_assert (m_line_spans.length () > 0);
1564 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1566 const line_span *prev = &m_line_spans[i - 1];
1567 const line_span *next = &m_line_spans[i];
1568 /* The individual spans must be sane. */
1569 gcc_assert (prev->m_first_line <= prev->m_last_line);
1570 gcc_assert (next->m_first_line <= next->m_last_line);
1571 /* The spans must be ordered. */
1572 gcc_assert (prev->m_first_line < next->m_first_line);
1573 /* There must be a gap of at least one line between separate spans. */
1574 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1578 /* Determine how many display columns need to be reserved for line numbers,
1579 based on the largest line number that will be needed, and populate
1580 m_linenum_width. */
1582 void
1583 layout::calculate_linenum_width ()
1585 gcc_assert (m_line_spans.length () > 0);
1586 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1587 int highest_line = last_span->m_last_line;
1588 if (highest_line < 0)
1589 highest_line = 0;
1590 m_linenum_width = num_digits (highest_line);
1591 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1592 if (m_line_spans.length () > 1)
1593 m_linenum_width = MAX (m_linenum_width, 3);
1594 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1595 after the line number. */
1596 m_linenum_width = MAX (m_linenum_width, m_context->min_margin_width - 1);
1599 /* Calculate m_x_offset_display, which improves readability in case the source
1600 line of interest is longer than the user's display. All lines output will be
1601 shifted to the left (so that their beginning is no longer displayed) by
1602 m_x_offset_display display columns, so that the caret is in a reasonable
1603 location. */
1605 void
1606 layout::calculate_x_offset_display ()
1608 m_x_offset_display = 0;
1610 const int max_width = m_context->caret_max_width;
1611 if (!max_width)
1613 /* Nothing to do, the width is not capped. */
1614 return;
1617 const char_span line = location_get_source_line (m_exploc.file,
1618 m_exploc.line);
1619 if (!line)
1621 /* Nothing to do, we couldn't find the source line. */
1622 return;
1624 int caret_display_column = m_exploc.m_display_col;
1625 const int line_bytes
1626 = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1627 line.length ());
1628 int eol_display_column
1629 = cpp_display_width (line.get_buffer (), line_bytes, m_policy);
1630 if (caret_display_column > eol_display_column
1631 || !caret_display_column)
1633 /* This does not make sense, so don't try to do anything in this case. */
1634 return;
1637 /* Adjust caret and eol positions to include the left margin. If we are
1638 outputting line numbers, then the left margin is equal to m_linenum_width
1639 plus three for the " | " which follows it. Otherwise the left margin width
1640 is equal to 1, because layout::print_source_line() will prefix each line
1641 with a space. */
1642 const int source_display_cols = eol_display_column;
1643 int left_margin_size = 1;
1644 if (m_show_line_numbers_p)
1645 left_margin_size = m_linenum_width + 3;
1646 caret_display_column += left_margin_size;
1647 eol_display_column += left_margin_size;
1649 if (eol_display_column <= max_width)
1651 /* Nothing to do, everything fits in the display. */
1652 return;
1655 /* The line is too long for the display. Calculate an offset such that the
1656 caret is not too close to the right edge of the screen. It will be
1657 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1658 than that to the end of the source line anyway. */
1659 int right_margin_size = CARET_LINE_MARGIN;
1660 right_margin_size = MIN (eol_display_column - caret_display_column,
1661 right_margin_size);
1662 if (right_margin_size + left_margin_size >= max_width)
1664 /* The max_width is very small, so anything we try to do will not be very
1665 effective; just punt in this case and output with no offset. */
1666 return;
1668 const int max_caret_display_column = max_width - right_margin_size;
1669 if (caret_display_column > max_caret_display_column)
1671 m_x_offset_display = caret_display_column - max_caret_display_column;
1672 /* Make sure we don't offset the line into oblivion. */
1673 static const int min_cols_visible = 2;
1674 if (source_display_cols - m_x_offset_display < min_cols_visible)
1675 m_x_offset_display = 0;
1679 /* Print line ROW of source code, potentially colorized at any ranges, and
1680 return the line bounds. LINE is the source line (not necessarily
1681 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1682 colorization and tab expansion, this function tracks the line position in
1683 both byte and display column units. */
1685 line_bounds
1686 layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1688 m_colorizer.set_normal_text ();
1690 pp_emit_prefix (m_pp);
1691 if (m_show_line_numbers_p)
1693 int width = num_digits (row);
1694 for (int i = 0; i < m_linenum_width - width; i++)
1695 pp_space (m_pp);
1696 pp_printf (m_pp, "%i | ", row);
1698 else
1699 pp_space (m_pp);
1701 /* We will stop printing the source line at any trailing whitespace. */
1702 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1703 line_bytes);
1705 /* This object helps to keep track of which display column we are at, which is
1706 necessary for computing the line bounds in display units, for doing
1707 tab expansion, and for implementing m_x_offset_display. */
1708 cpp_display_width_computation dw (line, line_bytes, m_policy);
1710 /* Skip the first m_x_offset_display display columns. In case the leading
1711 portion that will be skipped ends with a character with wcwidth > 1, then
1712 it is possible we skipped too much, so account for that by padding with
1713 spaces. Note that this does the right thing too in case a tab was the last
1714 character to be skipped over; the tab is effectively replaced by the
1715 correct number of trailing spaces needed to offset by the desired number of
1716 display columns. */
1717 for (int skipped_display_cols = dw.advance_display_cols (m_x_offset_display);
1718 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1719 pp_space (m_pp);
1721 /* Print the line and compute the line_bounds. */
1722 line_bounds lbounds;
1723 while (!dw.done ())
1725 /* Assuming colorization is enabled for the caret and underline
1726 characters, we may also colorize the associated characters
1727 within the source line.
1729 For frontends that generate range information, we color the
1730 associated characters in the source line the same as the
1731 carets and underlines in the annotation line, to make it easier
1732 for the reader to see the pertinent code.
1734 For frontends that only generate carets, we don't colorize the
1735 characters above them, since this would look strange (e.g.
1736 colorizing just the first character in a token). */
1737 if (m_colorize_source_p)
1739 bool in_range_p;
1740 point_state state;
1741 const int start_byte_col = dw.bytes_processed () + 1;
1742 in_range_p = get_state_at_point (row, start_byte_col,
1743 0, INT_MAX,
1744 CU_BYTES,
1745 &state);
1746 if (in_range_p)
1747 m_colorizer.set_range (state.range_idx);
1748 else
1749 m_colorizer.set_normal_text ();
1752 /* Get the display width of the next character to be output, expanding
1753 tabs and replacing some control bytes with spaces as necessary. */
1754 const char *c = dw.next_byte ();
1755 const int start_disp_col = dw.display_cols_processed () + 1;
1756 cpp_decoded_char cp;
1757 const int this_display_width = dw.process_next_codepoint (&cp);
1758 if (*c == '\t')
1760 /* The returned display width is the number of spaces into which the
1761 tab should be expanded. */
1762 for (int i = 0; i != this_display_width; ++i)
1763 pp_space (m_pp);
1764 continue;
1767 /* We have a (possibly multibyte) character to output; update the line
1768 bounds if it is not whitespace. */
1769 if (*c != ' ')
1771 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1772 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1773 lbounds.m_first_non_ws_disp_col = start_disp_col;
1776 /* Output the character. */
1777 m_policy.m_print_cb (m_pp, cp);
1778 c = dw.next_byte ();
1780 print_newline ();
1781 return lbounds;
1784 /* Determine if we should print an annotation line for ROW.
1785 i.e. if any of m_layout_ranges contains ROW. */
1787 bool
1788 layout::should_print_annotation_line_p (linenum_type row) const
1790 layout_range *range;
1791 int i;
1792 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1794 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1795 return false;
1796 if (range->intersects_line_p (row))
1797 return true;
1799 return false;
1802 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1803 margin, which is empty for annotation lines. Otherwise, do nothing. */
1805 void
1806 layout::start_annotation_line (char margin_char) const
1808 pp_emit_prefix (m_pp);
1809 if (m_show_line_numbers_p)
1811 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1812 of it, right-aligned, padded with spaces. */
1813 int i;
1814 for (i = 0; i < m_linenum_width - 3; i++)
1815 pp_space (m_pp);
1816 for (; i < m_linenum_width; i++)
1817 pp_character (m_pp, margin_char);
1818 pp_string (m_pp, " |");
1822 /* Print a line consisting of the caret/underlines for the given
1823 source line. */
1825 void
1826 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1828 int x_bound = get_x_bound_for_row (row, m_exploc.m_display_col,
1829 lbounds.m_last_non_ws_disp_col);
1831 start_annotation_line ();
1832 pp_space (m_pp);
1834 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1836 bool in_range_p;
1837 point_state state;
1838 in_range_p = get_state_at_point (row, column,
1839 lbounds.m_first_non_ws_disp_col,
1840 lbounds.m_last_non_ws_disp_col,
1841 CU_DISPLAY_COLS,
1842 &state);
1843 if (in_range_p)
1845 /* Within a range. Draw either the caret or an underline. */
1846 m_colorizer.set_range (state.range_idx);
1847 if (state.draw_caret_p)
1849 /* Draw the caret. */
1850 char caret_char;
1851 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1852 caret_char = m_context->caret_chars[state.range_idx];
1853 else
1854 caret_char = '^';
1855 pp_character (m_pp, caret_char);
1857 else
1858 pp_character (m_pp, '~');
1860 else
1862 /* Not in a range. */
1863 m_colorizer.set_normal_text ();
1864 pp_character (m_pp, ' ');
1867 print_newline ();
1870 /* Implementation detail of layout::print_any_labels.
1872 A label within the given row of source. */
1874 class line_label
1876 public:
1877 line_label (const cpp_char_column_policy &policy,
1878 int state_idx, int column,
1879 label_text text)
1880 : m_state_idx (state_idx), m_column (column),
1881 m_text (text), m_label_line (0), m_has_vbar (true)
1883 const int bytes = strlen (text.m_buffer);
1884 m_display_width = cpp_display_width (text.m_buffer, bytes, policy);
1887 /* Sorting is primarily by column, then by state index. */
1888 static int comparator (const void *p1, const void *p2)
1890 const line_label *ll1 = (const line_label *)p1;
1891 const line_label *ll2 = (const line_label *)p2;
1892 int column_cmp = compare (ll1->m_column, ll2->m_column);
1893 if (column_cmp)
1894 return column_cmp;
1895 /* Order by reverse state index, so that labels are printed
1896 in order of insertion into the rich_location when the
1897 sorted list is walked backwards. */
1898 return -compare (ll1->m_state_idx, ll2->m_state_idx);
1901 int m_state_idx;
1902 int m_column;
1903 label_text m_text;
1904 size_t m_display_width;
1905 int m_label_line;
1906 bool m_has_vbar;
1909 /* Print any labels in this row. */
1910 void
1911 layout::print_any_labels (linenum_type row)
1913 int i;
1914 auto_vec<line_label> labels;
1916 /* Gather the labels that are to be printed into "labels". */
1918 layout_range *range;
1919 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1921 /* Most ranges don't have labels, so reject this first. */
1922 if (range->m_label == NULL)
1923 continue;
1925 /* The range's caret must be on this line. */
1926 if (range->m_caret.m_line != row)
1927 continue;
1929 /* Reject labels that aren't fully visible due to clipping
1930 by m_x_offset_display. */
1931 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1932 if (disp_col <= m_x_offset_display)
1933 continue;
1935 label_text text;
1936 text = range->m_label->get_text (range->m_original_idx);
1938 /* Allow for labels that return NULL from their get_text
1939 implementation (so e.g. such labels can control their own
1940 visibility). */
1941 if (text.m_buffer == NULL)
1942 continue;
1944 labels.safe_push (line_label (m_policy, i, disp_col, text));
1948 /* Bail out if there are no labels on this row. */
1949 if (labels.length () == 0)
1950 return;
1952 /* Sort them. */
1953 labels.qsort(line_label::comparator);
1955 /* Figure out how many "label lines" we need, and which
1956 one each label is printed in.
1958 For example, if the labels aren't too densely packed,
1959 we can fit them on the same line, giving two "label lines":
1961 foo + bar
1962 ~~~ ~~~
1963 | | : label line 0
1964 l0 l1 : label line 1
1966 If they would touch each other or overlap, then we need
1967 additional "label lines":
1969 foo + bar
1970 ~~~ ~~~
1971 | | : label line 0
1972 | label 1 : label line 1
1973 label 0 : label line 2
1975 Place the final label on label line 1, and work backwards, adding
1976 label lines as needed.
1978 If multiple labels are at the same place, put them on separate
1979 label lines:
1981 foo + bar
1982 ^ : label line 0
1983 | : label line 1
1984 label 0 : label line 2
1985 label 1 : label line 3. */
1987 int max_label_line = 1;
1989 int next_column = INT_MAX;
1990 line_label *label;
1991 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1993 /* Would this label "touch" or overlap the next label? */
1994 if (label->m_column + label->m_display_width >= (size_t)next_column)
1996 max_label_line++;
1998 /* If we've already seen labels with the same column, suppress the
1999 vertical bar for subsequent ones in this backwards iteration;
2000 hence only the one with the highest label_line has m_has_vbar set. */
2001 if (label->m_column == next_column)
2002 label->m_has_vbar = false;
2005 label->m_label_line = max_label_line;
2006 next_column = label->m_column;
2010 /* Print the "label lines". For each label within the line, print
2011 either a vertical bar ('|') for the labels that are lower down, or the
2012 labels themselves once we've reached their line. */
2014 for (int label_line = 0; label_line <= max_label_line; label_line++)
2016 start_annotation_line ();
2017 pp_space (m_pp);
2018 int column = 1 + m_x_offset_display;
2019 line_label *label;
2020 FOR_EACH_VEC_ELT (labels, i, label)
2022 if (label_line > label->m_label_line)
2023 /* We've printed all the labels for this label line. */
2024 break;
2026 if (label_line == label->m_label_line)
2028 gcc_assert (column <= label->m_column);
2029 move_to_column (&column, label->m_column, true);
2030 /* Colorize the text, unless it's for events in a
2031 diagnostic_path. */
2032 if (!m_diagnostic_path_p)
2033 m_colorizer.set_range (label->m_state_idx);
2034 pp_string (m_pp, label->m_text.m_buffer);
2035 m_colorizer.set_normal_text ();
2036 column += label->m_display_width;
2038 else if (label->m_has_vbar)
2040 gcc_assert (column <= label->m_column);
2041 move_to_column (&column, label->m_column, true);
2042 m_colorizer.set_range (label->m_state_idx);
2043 pp_character (m_pp, '|');
2044 m_colorizer.set_normal_text ();
2045 column++;
2048 print_newline ();
2052 /* Clean up. */
2054 line_label *label;
2055 FOR_EACH_VEC_ELT (labels, i, label)
2056 label->m_text.maybe_free ();
2060 /* If there are any fixit hints inserting new lines before source line ROW,
2061 print them.
2063 They are printed on lines of their own, before the source line
2064 itself, with a leading '+'. */
2066 void
2067 layout::print_leading_fixits (linenum_type row)
2069 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2071 const fixit_hint *hint = m_fixit_hints[i];
2073 if (!hint->ends_with_newline_p ())
2074 /* Not a newline fixit; print it in print_trailing_fixits. */
2075 continue;
2077 gcc_assert (hint->insertion_p ());
2079 if (hint->affects_line_p (m_exploc.file, row))
2081 /* Printing the '+' with normal colorization
2082 and the inserted line with "insert" colorization
2083 helps them stand out from each other, and from
2084 the surrounding text. */
2085 m_colorizer.set_normal_text ();
2086 start_annotation_line ('+');
2087 pp_character (m_pp, '+');
2088 m_colorizer.set_fixit_insert ();
2089 /* Print all but the trailing newline of the fix-it hint.
2090 We have to print the newline separately to avoid
2091 getting additional pp prefixes printed. */
2092 for (size_t i = 0; i < hint->get_length () - 1; i++)
2093 pp_character (m_pp, hint->get_string ()[i]);
2094 m_colorizer.set_normal_text ();
2095 pp_newline (m_pp);
2100 /* Subroutine of layout::print_trailing_fixits.
2102 Determine if the annotation line printed for LINE contained
2103 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2105 bool
2106 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2107 int finish_column) const
2109 layout_range *range;
2110 int i;
2111 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2112 if (range->m_start.m_line == line
2113 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2114 && range->m_finish.m_line == line
2115 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2116 return true;
2117 return false;
2120 /* Classes for printing trailing fix-it hints i.e. those that
2121 don't add new lines.
2123 For insertion, these can look like:
2125 new_text
2127 For replacement, these can look like:
2129 ------------- : underline showing affected range
2130 new_text
2132 For deletion, these can look like:
2134 ------------- : underline showing affected range
2136 This can become confusing if they overlap, and so we need
2137 to do some preprocessing to decide what to print.
2138 We use the list of fixit_hint instances affecting the line
2139 to build a list of "correction" instances, and print the
2140 latter.
2142 For example, consider a set of fix-its for converting
2143 a C-style cast to a C++ const_cast.
2145 Given:
2147 ..000000000111111111122222222223333333333.
2148 ..123456789012345678901234567890123456789.
2149 foo *f = (foo *)ptr->field;
2150 ^~~~~
2152 and the fix-it hints:
2153 - replace col 10 (the open paren) with "const_cast<"
2154 - replace col 16 (the close paren) with "> ("
2155 - insert ")" before col 27
2157 then we would get odd-looking output:
2159 foo *f = (foo *)ptr->field;
2160 ^~~~~
2162 const_cast<
2164 > ( )
2166 It would be better to detect when fixit hints are going to
2167 overlap (those that require new lines), and to consolidate
2168 the printing of such fixits, giving something like:
2170 foo *f = (foo *)ptr->field;
2171 ^~~~~
2172 -----------------
2173 const_cast<foo *> (ptr->field)
2175 This works by detecting when the printing would overlap, and
2176 effectively injecting no-op replace hints into the gaps between
2177 such fix-its, so that the printing joins up.
2179 In the above example, the overlap of:
2180 - replace col 10 (the open paren) with "const_cast<"
2181 and:
2182 - replace col 16 (the close paren) with "> ("
2183 is fixed by injecting a no-op:
2184 - replace cols 11-15 with themselves ("foo *")
2185 and consolidating these, making:
2186 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2187 i.e.:
2188 - replace cols 10-16 with "const_cast<foo *> ("
2190 This overlaps with the final fix-it hint:
2191 - insert ")" before col 27
2192 and so we repeat the consolidation process, by injecting
2193 a no-op:
2194 - replace cols 17-26 with themselves ("ptr->field")
2195 giving:
2196 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2197 i.e.:
2198 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2200 and is thus printed as desired. */
2202 /* A range of (byte or display) columns within a line. */
2204 class column_range
2206 public:
2207 column_range (int start_, int finish_) : start (start_), finish (finish_)
2209 /* We must have either a range, or an insertion. */
2210 gcc_assert (start <= finish || finish == start - 1);
2213 bool operator== (const column_range &other) const
2215 return start == other.start && finish == other.finish;
2218 int start;
2219 int finish;
2222 /* Get the range of bytes or display columns that HINT would affect. */
2223 static column_range
2224 get_affected_range (const cpp_char_column_policy &policy,
2225 const fixit_hint *hint, enum column_unit col_unit)
2227 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2228 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2229 --exploc_finish.column;
2231 int start_column;
2232 int finish_column;
2233 if (col_unit == CU_DISPLAY_COLS)
2235 start_column = location_compute_display_column (exploc_start, policy);
2236 if (hint->insertion_p ())
2237 finish_column = start_column - 1;
2238 else
2239 finish_column = location_compute_display_column (exploc_finish, policy);
2241 else
2243 start_column = exploc_start.column;
2244 finish_column = exploc_finish.column;
2246 return column_range (start_column, finish_column);
2249 /* Get the range of display columns that would be printed for HINT. */
2251 static column_range
2252 get_printed_columns (const cpp_char_column_policy &policy,
2253 const fixit_hint *hint)
2255 expanded_location exploc = expand_location (hint->get_start_loc ());
2256 int start_column = location_compute_display_column (exploc, policy);
2257 int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2258 policy);
2259 int final_hint_column = start_column + hint_width - 1;
2260 if (hint->insertion_p ())
2262 return column_range (start_column, final_hint_column);
2264 else
2266 exploc = expand_location (hint->get_next_loc ());
2267 --exploc.column;
2268 int finish_column = location_compute_display_column (exploc, policy);
2269 return column_range (start_column,
2270 MAX (finish_column, final_hint_column));
2274 /* A correction on a particular line.
2275 This describes a plan for how to print one or more fixit_hint
2276 instances that affected the line, potentially consolidating hints
2277 into corrections to make the result easier for the user to read. */
2279 class correction
2281 public:
2282 correction (column_range affected_bytes,
2283 column_range affected_columns,
2284 column_range printed_columns,
2285 const char *new_text, size_t new_text_len,
2286 const cpp_char_column_policy &policy)
2287 : m_affected_bytes (affected_bytes),
2288 m_affected_columns (affected_columns),
2289 m_printed_columns (printed_columns),
2290 m_text (xstrdup (new_text)),
2291 m_byte_length (new_text_len),
2292 m_policy (policy),
2293 m_alloc_sz (new_text_len + 1)
2295 compute_display_cols ();
2298 ~correction () { free (m_text); }
2300 bool insertion_p () const
2302 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2305 void ensure_capacity (size_t len);
2306 void ensure_terminated ();
2308 void compute_display_cols ()
2310 m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2313 void overwrite (int dst_offset, const char_span &src_span)
2315 gcc_assert (dst_offset >= 0);
2316 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2317 memcpy (m_text + dst_offset, src_span.get_buffer (),
2318 src_span.length ());
2321 /* If insert, then start: the column before which the text
2322 is to be inserted, and finish is offset by the length of
2323 the replacement.
2324 If replace, then the range of columns affected. */
2325 column_range m_affected_bytes;
2326 column_range m_affected_columns;
2328 /* If insert, then start: the column before which the text
2329 is to be inserted, and finish is offset by the length of
2330 the replacement.
2331 If replace, then the range of columns affected. */
2332 column_range m_printed_columns;
2334 /* The text to be inserted/used as replacement. */
2335 char *m_text;
2336 size_t m_byte_length; /* Not including null-terminator. */
2337 int m_display_cols;
2338 const cpp_char_column_policy &m_policy;
2339 size_t m_alloc_sz;
2342 /* Ensure that m_text can hold a string of length LEN
2343 (plus 1 for 0-termination). */
2345 void
2346 correction::ensure_capacity (size_t len)
2348 /* Allow 1 extra byte for 0-termination. */
2349 if (m_alloc_sz < (len + 1))
2351 size_t new_alloc_sz = (len + 1) * 2;
2352 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2353 m_alloc_sz = new_alloc_sz;
2357 /* Ensure that m_text is 0-terminated. */
2359 void
2360 correction::ensure_terminated ()
2362 /* 0-terminate the buffer. */
2363 gcc_assert (m_byte_length < m_alloc_sz);
2364 m_text[m_byte_length] = '\0';
2367 /* A list of corrections affecting a particular line.
2368 This is used by layout::print_trailing_fixits for planning
2369 how to print the fix-it hints affecting the line. */
2371 class line_corrections
2373 public:
2374 line_corrections (const char_display_policy &policy,
2375 const char *filename,
2376 linenum_type row)
2377 : m_policy (policy), m_filename (filename), m_row (row)
2379 ~line_corrections ();
2381 void add_hint (const fixit_hint *hint);
2383 const char_display_policy &m_policy;
2384 const char *m_filename;
2385 linenum_type m_row;
2386 auto_vec <correction *> m_corrections;
2389 /* struct line_corrections. */
2391 line_corrections::~line_corrections ()
2393 unsigned i;
2394 correction *c;
2395 FOR_EACH_VEC_ELT (m_corrections, i, c)
2396 delete c;
2399 /* A struct wrapping a particular source line, allowing
2400 run-time bounds-checking of accesses in a checked build. */
2402 class source_line
2404 public:
2405 source_line (const char *filename, int line);
2407 char_span as_span () { return char_span (chars, width); }
2409 const char *chars;
2410 int width;
2413 /* source_line's ctor. */
2415 source_line::source_line (const char *filename, int line)
2417 char_span span = location_get_source_line (filename, line);
2418 chars = span.get_buffer ();
2419 width = span.length ();
2422 /* Add HINT to the corrections for this line.
2423 Attempt to consolidate nearby hints so that they will not
2424 overlap with printed. */
2426 void
2427 line_corrections::add_hint (const fixit_hint *hint)
2429 column_range affected_bytes = get_affected_range (m_policy, hint, CU_BYTES);
2430 column_range affected_columns = get_affected_range (m_policy, hint,
2431 CU_DISPLAY_COLS);
2432 column_range printed_columns = get_printed_columns (m_policy, hint);
2434 /* Potentially consolidate. */
2435 if (!m_corrections.is_empty ())
2437 correction *last_correction
2438 = m_corrections[m_corrections.length () - 1];
2440 /* The following consolidation code assumes that the fix-it hints
2441 have been sorted by start (done within layout's ctor). */
2442 gcc_assert (affected_bytes.start
2443 >= last_correction->m_affected_bytes.start);
2444 gcc_assert (printed_columns.start
2445 >= last_correction->m_printed_columns.start);
2447 if (printed_columns.start <= last_correction->m_printed_columns.finish)
2449 /* We have two hints for which the printed forms of the hints
2450 would touch or overlap, so we need to consolidate them to avoid
2451 confusing the user.
2452 Attempt to inject a "replace" correction from immediately
2453 after the end of the last hint to immediately before the start
2454 of the next hint. */
2455 column_range between (last_correction->m_affected_bytes.finish + 1,
2456 affected_bytes.start - 1);
2458 /* Try to read the source. */
2459 source_line line (m_filename, m_row);
2460 if (line.chars && between.finish < line.width)
2462 /* Consolidate into the last correction:
2463 add a no-op "replace" of the "between" text, and
2464 add the text from the new hint. */
2465 int old_byte_len = last_correction->m_byte_length;
2466 gcc_assert (old_byte_len >= 0);
2467 int between_byte_len = between.finish + 1 - between.start;
2468 gcc_assert (between_byte_len >= 0);
2469 int new_byte_len
2470 = old_byte_len + between_byte_len + hint->get_length ();
2471 gcc_assert (new_byte_len >= 0);
2472 last_correction->ensure_capacity (new_byte_len);
2473 last_correction->overwrite
2474 (old_byte_len,
2475 line.as_span ().subspan (between.start - 1,
2476 between.finish + 1 - between.start));
2477 last_correction->overwrite (old_byte_len + between_byte_len,
2478 char_span (hint->get_string (),
2479 hint->get_length ()));
2480 last_correction->m_byte_length = new_byte_len;
2481 last_correction->ensure_terminated ();
2482 last_correction->m_affected_bytes.finish
2483 = affected_bytes.finish;
2484 last_correction->m_affected_columns.finish
2485 = affected_columns.finish;
2486 int prev_display_cols = last_correction->m_display_cols;
2487 last_correction->compute_display_cols ();
2488 last_correction->m_printed_columns.finish
2489 += last_correction->m_display_cols - prev_display_cols;
2490 return;
2495 /* If no consolidation happened, add a new correction instance. */
2496 m_corrections.safe_push (new correction (affected_bytes,
2497 affected_columns,
2498 printed_columns,
2499 hint->get_string (),
2500 hint->get_length (),
2501 m_policy));
2504 /* If there are any fixit hints on source line ROW, print them.
2505 They are printed in order, attempting to combine them onto lines, but
2506 starting new lines if necessary.
2507 Fix-it hints that insert new lines are handled separately,
2508 in layout::print_leading_fixits. */
2510 void
2511 layout::print_trailing_fixits (linenum_type row)
2513 /* Build a list of correction instances for the line,
2514 potentially consolidating hints (for the sake of readability). */
2515 line_corrections corrections (m_policy, m_exploc.file, row);
2516 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2518 const fixit_hint *hint = m_fixit_hints[i];
2520 /* Newline fixits are handled by layout::print_leading_fixits. */
2521 if (hint->ends_with_newline_p ())
2522 continue;
2524 if (hint->affects_line_p (m_exploc.file, row))
2525 corrections.add_hint (hint);
2528 /* Now print the corrections. */
2529 unsigned i;
2530 correction *c;
2531 int column = m_x_offset_display;
2533 if (!corrections.m_corrections.is_empty ())
2534 start_annotation_line ();
2536 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2538 /* For now we assume each fixit hint can only touch one line. */
2539 if (c->insertion_p ())
2541 /* This assumes the insertion just affects one line. */
2542 int start_column = c->m_printed_columns.start;
2543 move_to_column (&column, start_column, true);
2544 m_colorizer.set_fixit_insert ();
2545 pp_string (m_pp, c->m_text);
2546 m_colorizer.set_normal_text ();
2547 column += c->m_display_cols;
2549 else
2551 /* If the range of the replacement wasn't printed in the
2552 annotation line, then print an extra underline to
2553 indicate exactly what is being replaced.
2554 Always show it for removals. */
2555 int start_column = c->m_affected_columns.start;
2556 int finish_column = c->m_affected_columns.finish;
2557 if (!annotation_line_showed_range_p (row, start_column,
2558 finish_column)
2559 || c->m_byte_length == 0)
2561 move_to_column (&column, start_column, true);
2562 m_colorizer.set_fixit_delete ();
2563 for (; column <= finish_column; column++)
2564 pp_character (m_pp, '-');
2565 m_colorizer.set_normal_text ();
2567 /* Print the replacement text. REPLACE also covers
2568 removals, so only do this extra work (potentially starting
2569 a new line) if we have actual replacement text. */
2570 if (c->m_byte_length > 0)
2572 move_to_column (&column, start_column, true);
2573 m_colorizer.set_fixit_insert ();
2574 pp_string (m_pp, c->m_text);
2575 m_colorizer.set_normal_text ();
2576 column += c->m_display_cols;
2581 /* Add a trailing newline, if necessary. */
2582 move_to_column (&column, 0, false);
2585 /* Disable any colorization and emit a newline. */
2587 void
2588 layout::print_newline ()
2590 m_colorizer.set_normal_text ();
2591 pp_newline (m_pp);
2594 /* Return true if (ROW/COLUMN) is within a range of the layout.
2595 If it returns true, OUT_STATE is written to, with the
2596 range index, and whether we should draw the caret at
2597 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2598 whether all inputs and outputs are in bytes or display column units. */
2600 bool
2601 layout::get_state_at_point (/* Inputs. */
2602 linenum_type row, int column,
2603 int first_non_ws, int last_non_ws,
2604 enum column_unit col_unit,
2605 /* Outputs. */
2606 point_state *out_state)
2608 layout_range *range;
2609 int i;
2610 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2612 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2613 /* Bail out early, so that such ranges don't affect underlining or
2614 source colorization. */
2615 continue;
2617 if (range->contains_point (row, column, col_unit))
2619 out_state->range_idx = i;
2621 /* Are we at the range's caret? is it visible? */
2622 out_state->draw_caret_p = false;
2623 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2624 && row == range->m_caret.m_line
2625 && column == range->m_caret.m_columns[col_unit])
2626 out_state->draw_caret_p = true;
2628 /* Within a multiline range, don't display any underline
2629 in any leading or trailing whitespace on a line.
2630 We do display carets, however. */
2631 if (!out_state->draw_caret_p)
2632 if (column < first_non_ws || column > last_non_ws)
2633 return false;
2635 /* We are within a range. */
2636 return true;
2640 return false;
2643 /* Helper function for use by layout::print_line when printing the
2644 annotation line under the source line.
2645 Get the display column beyond the rightmost one that could contain a caret
2646 or range marker, given that we stop rendering at trailing whitespace.
2647 ROW is the source line within the given file.
2648 CARET_COLUMN is the display column of range 0's caret.
2649 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2650 character of source (as determined when printing the source line). */
2653 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2654 int last_non_ws_column)
2656 int result = caret_column + 1;
2658 layout_range *range;
2659 int i;
2660 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2662 if (row >= range->m_start.m_line)
2664 if (range->m_finish.m_line == row)
2666 /* On the final line within a range; ensure that
2667 we render up to the end of the range. */
2668 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2669 if (result <= disp_col)
2670 result = disp_col + 1;
2672 else if (row < range->m_finish.m_line)
2674 /* Within a multiline range; ensure that we render up to the
2675 last non-whitespace column. */
2676 if (result <= last_non_ws_column)
2677 result = last_non_ws_column + 1;
2682 return result;
2685 /* Given *COLUMN as an x-coordinate, print spaces to position
2686 successive output at DEST_COLUMN, printing a newline if necessary,
2687 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2688 left margin after any newline. */
2690 void
2691 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2693 /* Start a new line if we need to. */
2694 if (*column > dest_column)
2696 print_newline ();
2697 if (add_left_margin)
2698 start_annotation_line ();
2699 *column = m_x_offset_display;
2702 while (*column < dest_column)
2704 pp_space (m_pp);
2705 (*column)++;
2709 /* For debugging layout issues, render a ruler giving column numbers
2710 (after the 1-column indent). */
2712 void
2713 layout::show_ruler (int max_column) const
2715 /* Hundreds. */
2716 if (max_column > 99)
2718 start_annotation_line ();
2719 pp_space (m_pp);
2720 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2721 if (column % 10 == 0)
2722 pp_character (m_pp, '0' + (column / 100) % 10);
2723 else
2724 pp_space (m_pp);
2725 pp_newline (m_pp);
2728 /* Tens. */
2729 start_annotation_line ();
2730 pp_space (m_pp);
2731 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2732 if (column % 10 == 0)
2733 pp_character (m_pp, '0' + (column / 10) % 10);
2734 else
2735 pp_space (m_pp);
2736 pp_newline (m_pp);
2738 /* Units. */
2739 start_annotation_line ();
2740 pp_space (m_pp);
2741 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2742 pp_character (m_pp, '0' + (column % 10));
2743 pp_newline (m_pp);
2746 /* Print leading fix-its (for new lines inserted before the source line)
2747 then the source line, followed by an annotation line
2748 consisting of any caret/underlines, then any fixits.
2749 If the source line can't be read, print nothing. */
2750 void
2751 layout::print_line (linenum_type row)
2753 char_span line = location_get_source_line (m_exploc.file, row);
2754 if (!line)
2755 return;
2757 print_leading_fixits (row);
2758 const line_bounds lbounds
2759 = print_source_line (row, line.get_buffer (), line.length ());
2760 if (should_print_annotation_line_p (row))
2761 print_annotation_line (row, lbounds);
2762 if (m_show_labels_p)
2763 print_any_labels (row);
2764 print_trailing_fixits (row);
2767 } /* End of anonymous namespace. */
2769 /* If LOC is within the spans of lines that will already be printed for
2770 this gcc_rich_location, then add it as a secondary location and return true.
2772 Otherwise return false. */
2774 bool
2775 gcc_rich_location::add_location_if_nearby (location_t loc,
2776 bool restrict_to_current_line_spans,
2777 const range_label *label)
2779 /* Use the layout location-handling logic to sanitize LOC,
2780 filtering it to the current line spans within a temporary
2781 layout instance. */
2782 layout layout (global_dc, this, DK_ERROR);
2783 location_range loc_range;
2784 loc_range.m_loc = loc;
2785 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2786 if (!layout.maybe_add_location_range (&loc_range, 0,
2787 restrict_to_current_line_spans))
2788 return false;
2790 add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
2791 return true;
2794 /* Print the physical source code corresponding to the location of
2795 this diagnostic, with additional annotations. */
2797 void
2798 diagnostic_show_locus (diagnostic_context * context,
2799 rich_location *richloc,
2800 diagnostic_t diagnostic_kind)
2802 location_t loc = richloc->get_loc ();
2803 /* Do nothing if source-printing has been disabled. */
2804 if (!context->show_caret)
2805 return;
2807 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2808 if (loc <= BUILTINS_LOCATION)
2809 return;
2811 /* Don't print the same source location twice in a row, unless we have
2812 fix-it hints, or multiple locations, or a label. */
2813 if (loc == context->last_location
2814 && richloc->get_num_fixit_hints () == 0
2815 && richloc->get_num_locations () == 1
2816 && richloc->get_range (0)->m_label == NULL)
2817 return;
2819 context->last_location = loc;
2821 layout layout (context, richloc, diagnostic_kind);
2822 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2823 line_span_idx++)
2825 const line_span *line_span = layout.get_line_span (line_span_idx);
2826 if (context->show_line_numbers_p)
2828 /* With line numbers, we should show whenever the line-numbering
2829 "jumps". */
2830 if (line_span_idx > 0)
2831 layout.print_gap_in_line_numbering ();
2833 else
2835 /* Without line numbers, we print headings for some line spans. */
2836 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2838 expanded_location exploc
2839 = layout.get_expanded_location (line_span);
2840 context->start_span (context, exploc);
2843 /* Iterate over the lines within this span (using linenum_arith_t to
2844 avoid overflow with 0xffffffff causing an infinite loop). */
2845 linenum_arith_t last_line = line_span->get_last_line ();
2846 for (linenum_arith_t row = line_span->get_first_line ();
2847 row <= last_line; row++)
2848 layout.print_line (row);
2852 #if CHECKING_P
2854 namespace selftest {
2856 /* Selftests for diagnostic_show_locus. */
2858 /* Verify that cpp_display_width correctly handles escaping. */
2860 static void
2861 test_display_widths ()
2863 gcc_rich_location richloc (UNKNOWN_LOCATION);
2865 /* U+03C0 "GREEK SMALL LETTER PI". */
2866 const char *pi = "\xCF\x80";
2867 /* U+1F642 "SLIGHTLY SMILING FACE". */
2868 const char *emoji = "\xF0\x9F\x99\x82";
2869 /* Stray trailing byte of a UTF-8 character. */
2870 const char *stray = "\xBF";
2871 /* U+10FFFF. */
2872 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2874 /* No escaping. */
2876 test_diagnostic_context dc;
2877 char_display_policy policy (make_policy (dc, richloc));
2878 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2879 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2880 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2881 /* Don't check width of U+10FFFF; it's in a private use plane. */
2884 richloc.set_escape_on_output (true);
2887 test_diagnostic_context dc;
2888 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
2889 char_display_policy policy (make_policy (dc, richloc));
2890 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2891 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2892 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2893 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2894 policy),
2895 strlen ("<U+10FFFF>"));
2899 test_diagnostic_context dc;
2900 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
2901 char_display_policy policy (make_policy (dc, richloc));
2902 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2903 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2904 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2905 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2906 policy),
2907 16);
2911 /* For precise tests of the layout, make clear where the source line will
2912 start. test_left_margin sets the total byte count from the left side of the
2913 screen to the start of source lines, after the line number and the separator,
2914 which consists of the three characters " | ". */
2915 static const int test_linenum_sep = 3;
2916 static const int test_left_margin = 7;
2918 /* Helper function for test_layout_x_offset_display_utf8(). */
2919 static void
2920 test_offset_impl (int caret_byte_col, int max_width,
2921 int expected_x_offset_display,
2922 int left_margin = test_left_margin)
2924 test_diagnostic_context dc;
2925 dc.caret_max_width = max_width;
2926 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2927 the line number plus one space after. */
2928 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2929 dc.show_line_numbers_p = true;
2930 rich_location richloc (line_table,
2931 linemap_position_for_column (line_table,
2932 caret_byte_col));
2933 layout test_layout (&dc, &richloc, DK_ERROR);
2934 ASSERT_EQ (left_margin - test_linenum_sep,
2935 test_layout.get_linenum_width ());
2936 ASSERT_EQ (expected_x_offset_display,
2937 test_layout.get_x_offset_display ());
2940 /* Test that layout::calculate_x_offset_display() works. */
2941 static void
2942 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2945 const char *content
2946 = "This line is very long, so that we can use it to test the logic for "
2947 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2948 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2949 "column #102.\n";
2951 /* Number of bytes in the line, subtracting one to remove the newline. */
2952 const int line_bytes = strlen (content) - 1;
2954 /* Number of display columns occupied by the line; each of the 2 emojis
2955 takes up 2 fewer display columns than it does bytes. */
2956 const int line_display_cols = line_bytes - 2*2;
2958 /* The column of the first emoji. Byte or display is the same as there are
2959 no multibyte characters earlier on the line. */
2960 const int emoji_col = 102;
2962 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2963 line_table_test ltt (case_);
2965 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2967 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2969 /* Don't attempt to run the tests if column data might be unavailable. */
2970 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2971 return;
2973 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2974 ASSERT_EQ (1, LOCATION_LINE (line_end));
2975 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
2977 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2978 ASSERT_EQ (line_display_cols,
2979 cpp_display_width (lspan.get_buffer (), lspan.length (),
2980 def_policy ()));
2981 ASSERT_EQ (line_display_cols,
2982 location_compute_display_column (expand_location (line_end),
2983 def_policy ()));
2984 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
2985 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2987 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2989 /* No constraint on the width -> no offset. */
2990 test_offset_impl (emoji_col, 0, 0);
2992 /* Caret is before the beginning -> no offset. */
2993 test_offset_impl (0, 100, 0);
2995 /* Caret is past the end of the line -> no offset. */
2996 test_offset_impl (line_bytes+1, 100, 0);
2998 /* Line fits in the display -> no offset. */
2999 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3000 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3002 /* Line is too long for the display but caret location is OK
3003 anyway -> no offset. */
3004 static const int small_width = 24;
3005 test_offset_impl (1, small_width, 0);
3007 /* Width constraint is very small -> no offset. */
3008 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3010 /* Line would be offset, but due to large line numbers, offsetting
3011 would remove the whole line -> no offset. */
3012 static const int huge_left_margin = 100;
3013 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3015 /* Line is the same length as the display, but the line number makes it too
3016 long, so offset is required. Caret is at the end so padding on the right
3017 is not in effect. */
3018 for (int excess = 1; excess <= 3; ++excess)
3019 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3020 excess);
3022 /* Line is much too long for the display, caret is near the end ->
3023 offset should be such that the line fits in the display and caret
3024 remains the same distance from the end that it was. */
3025 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3026 caret_offset <= max_offset; ++caret_offset)
3027 test_offset_impl (line_bytes - caret_offset, small_width,
3028 line_display_cols + test_left_margin - small_width);
3030 /* As previous case but caret is closer to the middle; now we want it to end
3031 up CARET_LINE_MARGIN bytes from the end. */
3032 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3033 test_offset_impl (emoji_col, small_width,
3034 emoji_col + test_left_margin
3035 - (small_width - CARET_LINE_MARGIN));
3037 /* Test that the source line is offset as expected when printed. */
3039 test_diagnostic_context dc;
3040 dc.caret_max_width = small_width - 6;
3041 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3042 dc.show_line_numbers_p = true;
3043 dc.show_ruler_p = true;
3044 rich_location richloc (line_table,
3045 linemap_position_for_column (line_table,
3046 emoji_col));
3047 layout test_layout (&dc, &richloc, DK_ERROR);
3048 test_layout.print_line (1);
3049 ASSERT_STREQ (" | 1 \n"
3050 " | 1 \n"
3051 " | 234567890123456789\n"
3052 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3053 "that occupies 8 bytes and 4 display columns, starting at "
3054 "column #102.\n"
3055 " | ^\n\n",
3056 pp_formatted_text (dc.printer));
3059 /* Similar to the previous example, but now the offset called for would split
3060 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3061 it with a padding space in this case. */
3063 test_diagnostic_context dc;
3064 dc.caret_max_width = small_width - 5;
3065 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3066 dc.show_line_numbers_p = true;
3067 dc.show_ruler_p = true;
3068 rich_location richloc (line_table,
3069 linemap_position_for_column (line_table,
3070 emoji_col + 2));
3071 layout test_layout (&dc, &richloc, DK_ERROR);
3072 test_layout.print_line (1);
3073 ASSERT_STREQ (" | 1 1 \n"
3074 " | 1 2 \n"
3075 " | 3456789012345678901\n"
3076 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3077 "that occupies 8 bytes and 4 display columns, starting at "
3078 "column #102.\n"
3079 " | ^\n\n",
3080 pp_formatted_text (dc.printer));
3085 static void
3086 test_layout_x_offset_display_tab (const line_table_case &case_)
3088 const char *content
3089 = "This line is very long, so that we can use it to test the logic for "
3090 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3091 "a variable number of display columns, starting at column #103.\n";
3093 /* Number of bytes in the line, subtracting one to remove the newline. */
3094 const int line_bytes = strlen (content) - 1;
3096 /* The column where the tab begins. Byte or display is the same as there are
3097 no multibyte characters earlier on the line. */
3098 const int tab_col = 103;
3100 /* Effective extra size of the tab beyond what a single space would have taken
3101 up, indexed by tabstop. */
3102 static const int num_tabstops = 11;
3103 int extra_width[num_tabstops];
3104 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3106 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3107 extra_width[tabstop] = this_tab_size - 1;
3109 /* Example of this calculation: if tabstop is 10, the tab starting at column
3110 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3111 next character is at column #111. So it takes up 7 more columns than
3112 a space would have taken up. */
3113 ASSERT_EQ (7, extra_width[10]);
3115 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3116 line_table_test ltt (case_);
3118 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3120 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3122 /* Don't attempt to run the tests if column data might be unavailable. */
3123 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3124 return;
3126 /* Check that cpp_display_width handles the tabs as expected. */
3127 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3128 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3129 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3131 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3132 ASSERT_EQ (line_bytes + extra_width[tabstop],
3133 cpp_display_width (lspan.get_buffer (), lspan.length (),
3134 policy));
3135 ASSERT_EQ (line_bytes + extra_width[tabstop],
3136 location_compute_display_column (expand_location (line_end),
3137 policy));
3140 /* Check that the tab is expanded to the expected number of spaces. */
3141 rich_location richloc (line_table,
3142 linemap_position_for_column (line_table,
3143 tab_col + 1));
3144 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3146 test_diagnostic_context dc;
3147 dc.tabstop = tabstop;
3148 layout test_layout (&dc, &richloc, DK_ERROR);
3149 test_layout.print_line (1);
3150 const char *out = pp_formatted_text (dc.printer);
3151 ASSERT_EQ (NULL, strchr (out, '\t'));
3152 const char *left_quote = strchr (out, '`');
3153 const char *right_quote = strchr (out, '\'');
3154 ASSERT_NE (NULL, left_quote);
3155 ASSERT_NE (NULL, right_quote);
3156 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3159 /* Check that the line is offset properly and that the tab is broken up
3160 into the expected number of spaces when it is the last character skipped
3161 over. */
3162 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3164 test_diagnostic_context dc;
3165 dc.tabstop = tabstop;
3166 static const int small_width = 24;
3167 dc.caret_max_width = small_width - 4;
3168 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
3169 dc.show_line_numbers_p = true;
3170 layout test_layout (&dc, &richloc, DK_ERROR);
3171 test_layout.print_line (1);
3173 /* We have arranged things so that two columns will be printed before
3174 the caret. If the tab results in more than one space, this should
3175 produce two spaces in the output; otherwise, it will be a single space
3176 preceded by the opening quote before the tab character. */
3177 const char *output1
3178 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3179 "display columns, starting at column #103.\n"
3180 " | ^\n\n";
3181 const char *output2
3182 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3183 "display columns, starting at column #103.\n"
3184 " | ^\n\n";
3185 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3186 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3191 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3193 static void
3194 test_diagnostic_show_locus_unknown_location ()
3196 test_diagnostic_context dc;
3197 rich_location richloc (line_table, UNKNOWN_LOCATION);
3198 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3199 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3202 /* Verify that diagnostic_show_locus works sanely for various
3203 single-line cases.
3205 All of these work on the following 1-line source file:
3206 .0000000001111111
3207 .1234567890123456
3208 "foo = bar.field;\n"
3209 which is set up by test_diagnostic_show_locus_one_liner and calls
3210 them. */
3212 /* Just a caret. */
3214 static void
3215 test_one_liner_simple_caret ()
3217 test_diagnostic_context dc;
3218 location_t caret = linemap_position_for_column (line_table, 10);
3219 rich_location richloc (line_table, caret);
3220 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3221 ASSERT_STREQ (" foo = bar.field;\n"
3222 " ^\n",
3223 pp_formatted_text (dc.printer));
3226 /* Caret and range. */
3228 static void
3229 test_one_liner_caret_and_range ()
3231 test_diagnostic_context dc;
3232 location_t caret = linemap_position_for_column (line_table, 10);
3233 location_t start = linemap_position_for_column (line_table, 7);
3234 location_t finish = linemap_position_for_column (line_table, 15);
3235 location_t loc = make_location (caret, start, finish);
3236 rich_location richloc (line_table, loc);
3237 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3238 ASSERT_STREQ (" foo = bar.field;\n"
3239 " ~~~^~~~~~\n",
3240 pp_formatted_text (dc.printer));
3243 /* Multiple ranges and carets. */
3245 static void
3246 test_one_liner_multiple_carets_and_ranges ()
3248 test_diagnostic_context dc;
3249 location_t foo
3250 = make_location (linemap_position_for_column (line_table, 2),
3251 linemap_position_for_column (line_table, 1),
3252 linemap_position_for_column (line_table, 3));
3253 dc.caret_chars[0] = 'A';
3255 location_t bar
3256 = make_location (linemap_position_for_column (line_table, 8),
3257 linemap_position_for_column (line_table, 7),
3258 linemap_position_for_column (line_table, 9));
3259 dc.caret_chars[1] = 'B';
3261 location_t field
3262 = make_location (linemap_position_for_column (line_table, 13),
3263 linemap_position_for_column (line_table, 11),
3264 linemap_position_for_column (line_table, 15));
3265 dc.caret_chars[2] = 'C';
3267 rich_location richloc (line_table, foo);
3268 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3269 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3270 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3271 ASSERT_STREQ (" foo = bar.field;\n"
3272 " ~A~ ~B~ ~~C~~\n",
3273 pp_formatted_text (dc.printer));
3276 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3278 static void
3279 test_one_liner_fixit_insert_before ()
3281 test_diagnostic_context dc;
3282 location_t caret = linemap_position_for_column (line_table, 7);
3283 rich_location richloc (line_table, caret);
3284 richloc.add_fixit_insert_before ("&");
3285 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3286 ASSERT_STREQ (" foo = bar.field;\n"
3287 " ^\n"
3288 " &\n",
3289 pp_formatted_text (dc.printer));
3292 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3294 static void
3295 test_one_liner_fixit_insert_after ()
3297 test_diagnostic_context dc;
3298 location_t start = linemap_position_for_column (line_table, 1);
3299 location_t finish = linemap_position_for_column (line_table, 3);
3300 location_t foo = make_location (start, start, finish);
3301 rich_location richloc (line_table, foo);
3302 richloc.add_fixit_insert_after ("[0]");
3303 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3304 ASSERT_STREQ (" foo = bar.field;\n"
3305 " ^~~\n"
3306 " [0]\n",
3307 pp_formatted_text (dc.printer));
3310 /* Removal fix-it hint: removal of the ".field".
3311 Also verify the interaction of pp_set_prefix with rulers and
3312 fix-it hints. */
3314 static void
3315 test_one_liner_fixit_remove ()
3317 location_t start = linemap_position_for_column (line_table, 10);
3318 location_t finish = linemap_position_for_column (line_table, 15);
3319 location_t dot = make_location (start, start, finish);
3320 rich_location richloc (line_table, dot);
3321 richloc.add_fixit_remove ();
3323 /* Normal. */
3325 test_diagnostic_context dc;
3326 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3327 ASSERT_STREQ (" foo = bar.field;\n"
3328 " ^~~~~~\n"
3329 " ------\n",
3330 pp_formatted_text (dc.printer));
3333 /* Test of adding a prefix. */
3335 test_diagnostic_context dc;
3336 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3337 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3338 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3339 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3340 "TEST PREFIX: ^~~~~~\n"
3341 "TEST PREFIX: ------\n",
3342 pp_formatted_text (dc.printer));
3345 /* Normal, with ruler. */
3347 test_diagnostic_context dc;
3348 dc.show_ruler_p = true;
3349 dc.caret_max_width = 104;
3350 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3351 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3352 " 1 2 3 4 5 6 7 8 9 0 \n"
3353 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3354 " foo = bar.field;\n"
3355 " ^~~~~~\n"
3356 " ------\n",
3357 pp_formatted_text (dc.printer));
3360 /* Test of adding a prefix, with ruler. */
3362 test_diagnostic_context dc;
3363 dc.show_ruler_p = true;
3364 dc.caret_max_width = 50;
3365 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3366 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3367 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3368 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3369 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3370 "TEST PREFIX: foo = bar.field;\n"
3371 "TEST PREFIX: ^~~~~~\n"
3372 "TEST PREFIX: ------\n",
3373 pp_formatted_text (dc.printer));
3376 /* Test of adding a prefix, with ruler and line numbers. */
3378 test_diagnostic_context dc;
3379 dc.show_ruler_p = true;
3380 dc.caret_max_width = 50;
3381 dc.show_line_numbers_p = true;
3382 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3383 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3384 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3385 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3386 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3387 "TEST PREFIX: 1 | foo = bar.field;\n"
3388 "TEST PREFIX: | ^~~~~~\n"
3389 "TEST PREFIX: | ------\n",
3390 pp_formatted_text (dc.printer));
3394 /* Replace fix-it hint: replacing "field" with "m_field". */
3396 static void
3397 test_one_liner_fixit_replace ()
3399 test_diagnostic_context dc;
3400 location_t start = linemap_position_for_column (line_table, 11);
3401 location_t finish = linemap_position_for_column (line_table, 15);
3402 location_t field = make_location (start, start, finish);
3403 rich_location richloc (line_table, field);
3404 richloc.add_fixit_replace ("m_field");
3405 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3406 ASSERT_STREQ (" foo = bar.field;\n"
3407 " ^~~~~\n"
3408 " m_field\n",
3409 pp_formatted_text (dc.printer));
3412 /* Replace fix-it hint: replacing "field" with "m_field",
3413 but where the caret was elsewhere. */
3415 static void
3416 test_one_liner_fixit_replace_non_equal_range ()
3418 test_diagnostic_context dc;
3419 location_t equals = linemap_position_for_column (line_table, 5);
3420 location_t start = linemap_position_for_column (line_table, 11);
3421 location_t finish = linemap_position_for_column (line_table, 15);
3422 rich_location richloc (line_table, equals);
3423 source_range range;
3424 range.m_start = start;
3425 range.m_finish = finish;
3426 richloc.add_fixit_replace (range, "m_field");
3427 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3428 /* The replacement range is not indicated in the annotation line, so
3429 it should be indicated via an additional underline. */
3430 ASSERT_STREQ (" foo = bar.field;\n"
3431 " ^\n"
3432 " -----\n"
3433 " m_field\n",
3434 pp_formatted_text (dc.printer));
3437 /* Replace fix-it hint: replacing "field" with "m_field",
3438 where the caret was elsewhere, but where a secondary range
3439 exactly covers "field". */
3441 static void
3442 test_one_liner_fixit_replace_equal_secondary_range ()
3444 test_diagnostic_context dc;
3445 location_t equals = linemap_position_for_column (line_table, 5);
3446 location_t start = linemap_position_for_column (line_table, 11);
3447 location_t finish = linemap_position_for_column (line_table, 15);
3448 rich_location richloc (line_table, equals);
3449 location_t field = make_location (start, start, finish);
3450 richloc.add_range (field);
3451 richloc.add_fixit_replace (field, "m_field");
3452 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3453 /* The replacement range is indicated in the annotation line,
3454 so it shouldn't be indicated via an additional underline. */
3455 ASSERT_STREQ (" foo = bar.field;\n"
3456 " ^ ~~~~~\n"
3457 " m_field\n",
3458 pp_formatted_text (dc.printer));
3461 /* Verify that we can use ad-hoc locations when adding fixits to a
3462 rich_location. */
3464 static void
3465 test_one_liner_fixit_validation_adhoc_locations ()
3467 /* Generate a range that's too long to be packed, so must
3468 be stored as an ad-hoc location (given the defaults
3469 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3470 const location_t c7 = linemap_position_for_column (line_table, 7);
3471 const location_t c47 = linemap_position_for_column (line_table, 47);
3472 const location_t loc = make_location (c7, c7, c47);
3474 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3475 return;
3477 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3479 /* Insert. */
3481 rich_location richloc (line_table, loc);
3482 richloc.add_fixit_insert_before (loc, "test");
3483 /* It should not have been discarded by the validator. */
3484 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3486 test_diagnostic_context dc;
3487 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3488 ASSERT_STREQ (" foo = bar.field;\n"
3489 " ^~~~~~~~~~ \n"
3490 " test\n",
3491 pp_formatted_text (dc.printer));
3494 /* Remove. */
3496 rich_location richloc (line_table, loc);
3497 source_range range = source_range::from_locations (loc, c47);
3498 richloc.add_fixit_remove (range);
3499 /* It should not have been discarded by the validator. */
3500 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3502 test_diagnostic_context dc;
3503 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3504 ASSERT_STREQ (" foo = bar.field;\n"
3505 " ^~~~~~~~~~ \n"
3506 " -----------------------------------------\n",
3507 pp_formatted_text (dc.printer));
3510 /* Replace. */
3512 rich_location richloc (line_table, loc);
3513 source_range range = source_range::from_locations (loc, c47);
3514 richloc.add_fixit_replace (range, "test");
3515 /* It should not have been discarded by the validator. */
3516 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3518 test_diagnostic_context dc;
3519 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3520 ASSERT_STREQ (" foo = bar.field;\n"
3521 " ^~~~~~~~~~ \n"
3522 " test\n",
3523 pp_formatted_text (dc.printer));
3527 /* Test of consolidating insertions at the same location. */
3529 static void
3530 test_one_liner_many_fixits_1 ()
3532 test_diagnostic_context dc;
3533 location_t equals = linemap_position_for_column (line_table, 5);
3534 rich_location richloc (line_table, equals);
3535 for (int i = 0; i < 19; i++)
3536 richloc.add_fixit_insert_before ("a");
3537 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3538 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3539 ASSERT_STREQ (" foo = bar.field;\n"
3540 " ^\n"
3541 " aaaaaaaaaaaaaaaaaaa\n",
3542 pp_formatted_text (dc.printer));
3545 /* Ensure that we can add an arbitrary number of fix-it hints to a
3546 rich_location, even if they are not consolidated. */
3548 static void
3549 test_one_liner_many_fixits_2 ()
3551 test_diagnostic_context dc;
3552 location_t equals = linemap_position_for_column (line_table, 5);
3553 rich_location richloc (line_table, equals);
3554 for (int i = 0; i < 19; i++)
3556 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3557 richloc.add_fixit_insert_before (loc, "a");
3559 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3560 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3561 ASSERT_STREQ (" foo = bar.field;\n"
3562 " ^\n"
3563 " a a a a a a a a a a a a a a a a a a a\n",
3564 pp_formatted_text (dc.printer));
3567 /* Test of labeling the ranges within a rich_location. */
3569 static void
3570 test_one_liner_labels ()
3572 location_t foo
3573 = make_location (linemap_position_for_column (line_table, 1),
3574 linemap_position_for_column (line_table, 1),
3575 linemap_position_for_column (line_table, 3));
3576 location_t bar
3577 = make_location (linemap_position_for_column (line_table, 7),
3578 linemap_position_for_column (line_table, 7),
3579 linemap_position_for_column (line_table, 9));
3580 location_t field
3581 = make_location (linemap_position_for_column (line_table, 11),
3582 linemap_position_for_column (line_table, 11),
3583 linemap_position_for_column (line_table, 15));
3585 /* Example where all the labels fit on one line. */
3587 text_range_label label0 ("0");
3588 text_range_label label1 ("1");
3589 text_range_label label2 ("2");
3590 gcc_rich_location richloc (foo, &label0);
3591 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3592 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3595 test_diagnostic_context dc;
3596 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3597 ASSERT_STREQ (" foo = bar.field;\n"
3598 " ^~~ ~~~ ~~~~~\n"
3599 " | | |\n"
3600 " 0 1 2\n",
3601 pp_formatted_text (dc.printer));
3604 /* Verify that we can disable label-printing. */
3606 test_diagnostic_context dc;
3607 dc.show_labels_p = false;
3608 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3609 ASSERT_STREQ (" foo = bar.field;\n"
3610 " ^~~ ~~~ ~~~~~\n",
3611 pp_formatted_text (dc.printer));
3615 /* Example where the labels need extra lines. */
3617 text_range_label label0 ("label 0");
3618 text_range_label label1 ("label 1");
3619 text_range_label label2 ("label 2");
3620 gcc_rich_location richloc (foo, &label0);
3621 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3622 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3624 test_diagnostic_context dc;
3625 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3626 ASSERT_STREQ (" foo = bar.field;\n"
3627 " ^~~ ~~~ ~~~~~\n"
3628 " | | |\n"
3629 " | | label 2\n"
3630 " | label 1\n"
3631 " label 0\n",
3632 pp_formatted_text (dc.printer));
3635 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3636 but label 1 just touches label 2. */
3638 text_range_label label0 ("aaaaa");
3639 text_range_label label1 ("bbbb");
3640 text_range_label label2 ("c");
3641 gcc_rich_location richloc (foo, &label0);
3642 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3643 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3645 test_diagnostic_context dc;
3646 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3647 ASSERT_STREQ (" foo = bar.field;\n"
3648 " ^~~ ~~~ ~~~~~\n"
3649 " | | |\n"
3650 " | | c\n"
3651 " aaaaa bbbb\n",
3652 pp_formatted_text (dc.printer));
3655 /* Example of out-of-order ranges (thus requiring a sort). */
3657 text_range_label label0 ("0");
3658 text_range_label label1 ("1");
3659 text_range_label label2 ("2");
3660 gcc_rich_location richloc (field, &label0);
3661 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3662 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3664 test_diagnostic_context dc;
3665 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3666 ASSERT_STREQ (" foo = bar.field;\n"
3667 " ~~~ ~~~ ^~~~~\n"
3668 " | | |\n"
3669 " 2 1 0\n",
3670 pp_formatted_text (dc.printer));
3673 /* Ensure we don't ICE if multiple ranges with labels are on
3674 the same point. */
3676 text_range_label label0 ("label 0");
3677 text_range_label label1 ("label 1");
3678 text_range_label label2 ("label 2");
3679 gcc_rich_location richloc (bar, &label0);
3680 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3681 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3683 test_diagnostic_context dc;
3684 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3685 ASSERT_STREQ (" foo = bar.field;\n"
3686 " ^~~\n"
3687 " |\n"
3688 " label 0\n"
3689 " label 1\n"
3690 " label 2\n",
3691 pp_formatted_text (dc.printer));
3694 /* Example of out-of-order ranges (thus requiring a sort), where
3695 they overlap, and there are multiple ranges on the same point. */
3697 text_range_label label_0a ("label 0a");
3698 text_range_label label_1a ("label 1a");
3699 text_range_label label_2a ("label 2a");
3700 text_range_label label_0b ("label 0b");
3701 text_range_label label_1b ("label 1b");
3702 text_range_label label_2b ("label 2b");
3703 text_range_label label_0c ("label 0c");
3704 text_range_label label_1c ("label 1c");
3705 text_range_label label_2c ("label 2c");
3706 gcc_rich_location richloc (field, &label_0a);
3707 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3708 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3710 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3711 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3712 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3714 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3715 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3716 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3718 test_diagnostic_context dc;
3719 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3720 ASSERT_STREQ (" foo = bar.field;\n"
3721 " ~~~ ~~~ ^~~~~\n"
3722 " | | |\n"
3723 " | | label 0a\n"
3724 " | | label 0b\n"
3725 " | | label 0c\n"
3726 " | label 1a\n"
3727 " | label 1b\n"
3728 " | label 1c\n"
3729 " label 2a\n"
3730 " label 2b\n"
3731 " label 2c\n",
3732 pp_formatted_text (dc.printer));
3735 /* Verify that a NULL result from range_label::get_text is
3736 handled gracefully. */
3738 text_range_label label (NULL);
3739 gcc_rich_location richloc (bar, &label);
3741 test_diagnostic_context dc;
3742 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3743 ASSERT_STREQ (" foo = bar.field;\n"
3744 " ^~~\n",
3745 pp_formatted_text (dc.printer));
3748 /* TODO: example of formatted printing (needs to be in
3749 gcc-rich-location.c due to Makefile.in issues). */
3752 /* Run the various one-liner tests. */
3754 static void
3755 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3757 /* Create a tempfile and write some text to it.
3758 ....................0000000001111111.
3759 ....................1234567890123456. */
3760 const char *content = "foo = bar.field;\n";
3761 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3762 line_table_test ltt (case_);
3764 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3766 location_t line_end = linemap_position_for_column (line_table, 16);
3768 /* Don't attempt to run the tests if column data might be unavailable. */
3769 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3770 return;
3772 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3773 ASSERT_EQ (1, LOCATION_LINE (line_end));
3774 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3776 test_one_liner_simple_caret ();
3777 test_one_liner_caret_and_range ();
3778 test_one_liner_multiple_carets_and_ranges ();
3779 test_one_liner_fixit_insert_before ();
3780 test_one_liner_fixit_insert_after ();
3781 test_one_liner_fixit_remove ();
3782 test_one_liner_fixit_replace ();
3783 test_one_liner_fixit_replace_non_equal_range ();
3784 test_one_liner_fixit_replace_equal_secondary_range ();
3785 test_one_liner_fixit_validation_adhoc_locations ();
3786 test_one_liner_many_fixits_1 ();
3787 test_one_liner_many_fixits_2 ();
3788 test_one_liner_labels ();
3791 /* Version of all one-liner tests exercising multibyte awareness. For
3792 simplicity we stick to using two multibyte characters in the test, U+1F602
3793 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3794 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3795 below asserts would be easier to read if we used UTF-8 directly in the
3796 string constants, but it seems better not to demand the host compiler
3797 support this, when it isn't otherwise necessary. Instead, whenever an
3798 extended character appears in a string, we put a line break after it so that
3799 all succeeding characters can appear visually at the correct display column.
3801 All of these work on the following 1-line source file:
3803 .0000000001111111111222222 display
3804 .1234567890123456789012345 columns
3805 "SS_foo = P_bar.SS_fieldP;\n"
3806 .0000000111111111222222223 byte
3807 .1356789012456789134567891 columns
3809 which is set up by test_diagnostic_show_locus_one_liner and calls
3810 them. Here SS represents the two display columns for the U+1F602 emoji and
3811 P represents the one display column for the U+03C0 pi symbol. */
3813 /* Just a caret. */
3815 static void
3816 test_one_liner_simple_caret_utf8 ()
3818 test_diagnostic_context dc;
3819 location_t caret = linemap_position_for_column (line_table, 18);
3820 rich_location richloc (line_table, caret);
3821 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3822 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3823 "_foo = \xcf\x80"
3824 "_bar.\xf0\x9f\x98\x82"
3825 "_field\xcf\x80"
3826 ";\n"
3827 " ^\n",
3828 pp_formatted_text (dc.printer));
3831 /* Caret and range. */
3832 static void
3833 test_one_liner_caret_and_range_utf8 ()
3835 test_diagnostic_context dc;
3836 location_t caret = linemap_position_for_column (line_table, 18);
3837 location_t start = linemap_position_for_column (line_table, 12);
3838 location_t finish = linemap_position_for_column (line_table, 30);
3839 location_t loc = make_location (caret, start, finish);
3840 rich_location richloc (line_table, loc);
3841 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3842 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3843 "_foo = \xcf\x80"
3844 "_bar.\xf0\x9f\x98\x82"
3845 "_field\xcf\x80"
3846 ";\n"
3847 " ~~~~~^~~~~~~~~~\n",
3848 pp_formatted_text (dc.printer));
3851 /* Multiple ranges and carets. */
3853 static void
3854 test_one_liner_multiple_carets_and_ranges_utf8 ()
3856 test_diagnostic_context dc;
3857 location_t foo
3858 = make_location (linemap_position_for_column (line_table, 7),
3859 linemap_position_for_column (line_table, 1),
3860 linemap_position_for_column (line_table, 8));
3861 dc.caret_chars[0] = 'A';
3863 location_t bar
3864 = make_location (linemap_position_for_column (line_table, 16),
3865 linemap_position_for_column (line_table, 12),
3866 linemap_position_for_column (line_table, 17));
3867 dc.caret_chars[1] = 'B';
3869 location_t field
3870 = make_location (linemap_position_for_column (line_table, 26),
3871 linemap_position_for_column (line_table, 19),
3872 linemap_position_for_column (line_table, 30));
3873 dc.caret_chars[2] = 'C';
3874 rich_location richloc (line_table, foo);
3875 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3876 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3877 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3878 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3879 "_foo = \xcf\x80"
3880 "_bar.\xf0\x9f\x98\x82"
3881 "_field\xcf\x80"
3882 ";\n"
3883 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3884 pp_formatted_text (dc.printer));
3887 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3889 static void
3890 test_one_liner_fixit_insert_before_utf8 ()
3892 test_diagnostic_context dc;
3893 location_t caret = linemap_position_for_column (line_table, 12);
3894 rich_location richloc (line_table, caret);
3895 richloc.add_fixit_insert_before ("&");
3896 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3897 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3898 "_foo = \xcf\x80"
3899 "_bar.\xf0\x9f\x98\x82"
3900 "_field\xcf\x80"
3901 ";\n"
3902 " ^\n"
3903 " &\n",
3904 pp_formatted_text (dc.printer));
3907 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3909 static void
3910 test_one_liner_fixit_insert_after_utf8 ()
3912 test_diagnostic_context dc;
3913 location_t start = linemap_position_for_column (line_table, 1);
3914 location_t finish = linemap_position_for_column (line_table, 8);
3915 location_t foo = make_location (start, start, finish);
3916 rich_location richloc (line_table, foo);
3917 richloc.add_fixit_insert_after ("[0]");
3918 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3919 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3920 "_foo = \xcf\x80"
3921 "_bar.\xf0\x9f\x98\x82"
3922 "_field\xcf\x80"
3923 ";\n"
3924 " ^~~~~~\n"
3925 " [0]\n",
3926 pp_formatted_text (dc.printer));
3929 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3931 static void
3932 test_one_liner_fixit_remove_utf8 ()
3934 test_diagnostic_context dc;
3935 location_t start = linemap_position_for_column (line_table, 18);
3936 location_t finish = linemap_position_for_column (line_table, 30);
3937 location_t dot = make_location (start, start, finish);
3938 rich_location richloc (line_table, dot);
3939 richloc.add_fixit_remove ();
3940 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3941 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3942 "_foo = \xcf\x80"
3943 "_bar.\xf0\x9f\x98\x82"
3944 "_field\xcf\x80"
3945 ";\n"
3946 " ^~~~~~~~~~\n"
3947 " ----------\n",
3948 pp_formatted_text (dc.printer));
3951 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3953 static void
3954 test_one_liner_fixit_replace_utf8 ()
3956 test_diagnostic_context dc;
3957 location_t start = linemap_position_for_column (line_table, 19);
3958 location_t finish = linemap_position_for_column (line_table, 30);
3959 location_t field = make_location (start, start, finish);
3960 rich_location richloc (line_table, field);
3961 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3962 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3963 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3964 "_foo = \xcf\x80"
3965 "_bar.\xf0\x9f\x98\x82"
3966 "_field\xcf\x80"
3967 ";\n"
3968 " ^~~~~~~~~\n"
3969 " m_\xf0\x9f\x98\x82"
3970 "_field\xcf\x80\n",
3971 pp_formatted_text (dc.printer));
3974 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3975 but where the caret was elsewhere. */
3977 static void
3978 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3980 test_diagnostic_context dc;
3981 location_t equals = linemap_position_for_column (line_table, 10);
3982 location_t start = linemap_position_for_column (line_table, 19);
3983 location_t finish = linemap_position_for_column (line_table, 30);
3984 rich_location richloc (line_table, equals);
3985 source_range range;
3986 range.m_start = start;
3987 range.m_finish = finish;
3988 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3989 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3990 /* The replacement range is not indicated in the annotation line, so
3991 it should be indicated via an additional underline. */
3992 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3993 "_foo = \xcf\x80"
3994 "_bar.\xf0\x9f\x98\x82"
3995 "_field\xcf\x80"
3996 ";\n"
3997 " ^\n"
3998 " ---------\n"
3999 " m_\xf0\x9f\x98\x82"
4000 "_field\xcf\x80\n",
4001 pp_formatted_text (dc.printer));
4004 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4005 where the caret was elsewhere, but where a secondary range
4006 exactly covers "field". */
4008 static void
4009 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4011 test_diagnostic_context dc;
4012 location_t equals = linemap_position_for_column (line_table, 10);
4013 location_t start = linemap_position_for_column (line_table, 19);
4014 location_t finish = linemap_position_for_column (line_table, 30);
4015 rich_location richloc (line_table, equals);
4016 location_t field = make_location (start, start, finish);
4017 richloc.add_range (field);
4018 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4019 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4020 /* The replacement range is indicated in the annotation line,
4021 so it shouldn't be indicated via an additional underline. */
4022 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4023 "_foo = \xcf\x80"
4024 "_bar.\xf0\x9f\x98\x82"
4025 "_field\xcf\x80"
4026 ";\n"
4027 " ^ ~~~~~~~~~\n"
4028 " m_\xf0\x9f\x98\x82"
4029 "_field\xcf\x80\n",
4030 pp_formatted_text (dc.printer));
4033 /* Verify that we can use ad-hoc locations when adding fixits to a
4034 rich_location. */
4036 static void
4037 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4039 /* Generate a range that's too long to be packed, so must
4040 be stored as an ad-hoc location (given the defaults
4041 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4042 const location_t c12 = linemap_position_for_column (line_table, 12);
4043 const location_t c52 = linemap_position_for_column (line_table, 52);
4044 const location_t loc = make_location (c12, c12, c52);
4046 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4047 return;
4049 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4051 /* Insert. */
4053 rich_location richloc (line_table, loc);
4054 richloc.add_fixit_insert_before (loc, "test");
4055 /* It should not have been discarded by the validator. */
4056 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4058 test_diagnostic_context dc;
4059 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4060 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4061 "_foo = \xcf\x80"
4062 "_bar.\xf0\x9f\x98\x82"
4063 "_field\xcf\x80"
4064 ";\n"
4065 " ^~~~~~~~~~~~~~~~ \n"
4066 " test\n",
4067 pp_formatted_text (dc.printer));
4070 /* Remove. */
4072 rich_location richloc (line_table, loc);
4073 source_range range = source_range::from_locations (loc, c52);
4074 richloc.add_fixit_remove (range);
4075 /* It should not have been discarded by the validator. */
4076 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4078 test_diagnostic_context dc;
4079 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4080 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4081 "_foo = \xcf\x80"
4082 "_bar.\xf0\x9f\x98\x82"
4083 "_field\xcf\x80"
4084 ";\n"
4085 " ^~~~~~~~~~~~~~~~ \n"
4086 " -------------------------------------\n",
4087 pp_formatted_text (dc.printer));
4090 /* Replace. */
4092 rich_location richloc (line_table, loc);
4093 source_range range = source_range::from_locations (loc, c52);
4094 richloc.add_fixit_replace (range, "test");
4095 /* It should not have been discarded by the validator. */
4096 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4098 test_diagnostic_context dc;
4099 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4100 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4101 "_foo = \xcf\x80"
4102 "_bar.\xf0\x9f\x98\x82"
4103 "_field\xcf\x80"
4104 ";\n"
4105 " ^~~~~~~~~~~~~~~~ \n"
4106 " test\n",
4107 pp_formatted_text (dc.printer));
4111 /* Test of consolidating insertions at the same location. */
4113 static void
4114 test_one_liner_many_fixits_1_utf8 ()
4116 test_diagnostic_context dc;
4117 location_t equals = linemap_position_for_column (line_table, 10);
4118 rich_location richloc (line_table, equals);
4119 for (int i = 0; i < 19; i++)
4120 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4121 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4122 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4123 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4124 "_foo = \xcf\x80"
4125 "_bar.\xf0\x9f\x98\x82"
4126 "_field\xcf\x80"
4127 ";\n"
4128 " ^\n"
4129 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4130 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4131 pp_formatted_text (dc.printer));
4134 /* Ensure that we can add an arbitrary number of fix-it hints to a
4135 rich_location, even if they are not consolidated. */
4137 static void
4138 test_one_liner_many_fixits_2_utf8 ()
4140 test_diagnostic_context dc;
4141 location_t equals = linemap_position_for_column (line_table, 10);
4142 rich_location richloc (line_table, equals);
4143 const int nlocs = 19;
4144 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4145 34, 36, 38, 40, 42, 44};
4146 for (int i = 0; i != nlocs; ++i)
4148 location_t loc = linemap_position_for_column (line_table, locs[i]);
4149 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4152 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4153 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4154 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4155 "_foo = \xcf\x80"
4156 "_bar.\xf0\x9f\x98\x82"
4157 "_field\xcf\x80"
4158 ";\n"
4159 " ^\n"
4160 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4161 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4162 pp_formatted_text (dc.printer));
4165 /* Test of labeling the ranges within a rich_location. */
4167 static void
4168 test_one_liner_labels_utf8 ()
4170 location_t foo
4171 = make_location (linemap_position_for_column (line_table, 1),
4172 linemap_position_for_column (line_table, 1),
4173 linemap_position_for_column (line_table, 8));
4174 location_t bar
4175 = make_location (linemap_position_for_column (line_table, 12),
4176 linemap_position_for_column (line_table, 12),
4177 linemap_position_for_column (line_table, 17));
4178 location_t field
4179 = make_location (linemap_position_for_column (line_table, 19),
4180 linemap_position_for_column (line_table, 19),
4181 linemap_position_for_column (line_table, 30));
4183 /* Example where all the labels fit on one line. */
4185 /* These three labels contain multibyte characters such that their byte
4186 lengths are respectively (12, 10, 18), but their display widths are only
4187 (6, 5, 9). All three fit on the line when considering the display
4188 widths, but not when considering the byte widths, so verify that we do
4189 indeed put them all on one line. */
4190 text_range_label label0
4191 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4192 text_range_label label1
4193 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4194 text_range_label label2
4195 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4196 "\xcf\x80");
4197 gcc_rich_location richloc (foo, &label0);
4198 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4199 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4202 test_diagnostic_context dc;
4203 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4204 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4205 "_foo = \xcf\x80"
4206 "_bar.\xf0\x9f\x98\x82"
4207 "_field\xcf\x80"
4208 ";\n"
4209 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4210 " | | |\n"
4211 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4212 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4213 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4214 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4215 pp_formatted_text (dc.printer));
4220 /* Example where the labels need extra lines. */
4222 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4223 text_range_label label1 ("label 1\xcf\x80");
4224 text_range_label label2 ("label 2\xcf\x80");
4225 gcc_rich_location richloc (foo, &label0);
4226 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4227 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4229 test_diagnostic_context dc;
4230 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4232 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4233 "_foo = \xcf\x80"
4234 "_bar.\xf0\x9f\x98\x82"
4235 "_field\xcf\x80"
4236 ";\n"
4237 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4238 " | | |\n"
4239 " | | label 2\xcf\x80\n"
4240 " | label 1\xcf\x80\n"
4241 " label 0\xf0\x9f\x98\x82\n",
4242 pp_formatted_text (dc.printer));
4245 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4246 but label 1 just touches label 2. */
4248 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4249 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4250 text_range_label label2 ("c");
4251 gcc_rich_location richloc (foo, &label0);
4252 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4253 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4255 test_diagnostic_context dc;
4256 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4257 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4258 "_foo = \xcf\x80"
4259 "_bar.\xf0\x9f\x98\x82"
4260 "_field\xcf\x80"
4261 ";\n"
4262 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4263 " | | |\n"
4264 " | | c\n"
4265 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4266 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4267 pp_formatted_text (dc.printer));
4270 /* Example of escaping the source lines. */
4272 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4273 text_range_label label1 ("label 1\xcf\x80");
4274 text_range_label label2 ("label 2\xcf\x80");
4275 gcc_rich_location richloc (foo, &label0);
4276 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4277 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4278 richloc.set_escape_on_output (true);
4281 test_diagnostic_context dc;
4282 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
4283 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4284 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4285 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4286 " | | |\n"
4287 " | | label 2\xcf\x80\n"
4288 " | label 1\xcf\x80\n"
4289 " label 0\xf0\x9f\x98\x82\n",
4290 pp_formatted_text (dc.printer));
4293 test_diagnostic_context dc;
4294 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
4295 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4296 ASSERT_STREQ
4297 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4298 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4299 " | | |\n"
4300 " | | label 2\xcf\x80\n"
4301 " | label 1\xcf\x80\n"
4302 " label 0\xf0\x9f\x98\x82\n",
4303 pp_formatted_text (dc.printer));
4308 /* Make sure that colorization codes don't interrupt a multibyte
4309 sequence, which would corrupt it. */
4310 static void
4311 test_one_liner_colorized_utf8 ()
4313 test_diagnostic_context dc;
4314 dc.colorize_source_p = true;
4315 diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4316 const location_t pi = linemap_position_for_column (line_table, 12);
4317 rich_location richloc (line_table, pi);
4318 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4320 /* In order to avoid having the test depend on exactly how the colorization
4321 was effected, just confirm there are two pi characters in the output. */
4322 const char *result = pp_formatted_text (dc.printer);
4323 const char *null_term = result + strlen (result);
4324 const char *first_pi = strstr (result, "\xcf\x80");
4325 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4326 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4329 /* Run the various one-liner tests. */
4331 static void
4332 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4334 /* Create a tempfile and write some text to it. */
4335 const char *content
4336 /* Display columns.
4337 0000000000000000000000011111111111111111111111111111112222222222222
4338 1111111122222222345678900000000123456666666677777777890123444444445 */
4339 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4340 /* 0000000000000000000001111111111111111111222222222222222222222233333
4341 1111222233334444567890122223333456789999000011112222345678999900001
4342 Byte columns. */
4343 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4344 line_table_test ltt (case_);
4346 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4348 location_t line_end = linemap_position_for_column (line_table, 31);
4350 /* Don't attempt to run the tests if column data might be unavailable. */
4351 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4352 return;
4354 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4355 ASSERT_EQ (1, LOCATION_LINE (line_end));
4356 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4358 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
4359 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4360 def_policy ()));
4361 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end),
4362 def_policy ()));
4364 test_one_liner_simple_caret_utf8 ();
4365 test_one_liner_caret_and_range_utf8 ();
4366 test_one_liner_multiple_carets_and_ranges_utf8 ();
4367 test_one_liner_fixit_insert_before_utf8 ();
4368 test_one_liner_fixit_insert_after_utf8 ();
4369 test_one_liner_fixit_remove_utf8 ();
4370 test_one_liner_fixit_replace_utf8 ();
4371 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4372 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4373 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4374 test_one_liner_many_fixits_1_utf8 ();
4375 test_one_liner_many_fixits_2_utf8 ();
4376 test_one_liner_labels_utf8 ();
4377 test_one_liner_colorized_utf8 ();
4380 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4382 static void
4383 test_add_location_if_nearby (const line_table_case &case_)
4385 /* Create a tempfile and write some text to it.
4386 ...000000000111111111122222222223333333333.
4387 ...123456789012345678901234567890123456789. */
4388 const char *content
4389 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4390 "struct different_line\n" /* line 2. */
4391 "{\n" /* line 3. */
4392 " double x;\n" /* line 4. */
4393 " double y;\n" /* line 5. */
4394 ";\n"); /* line 6. */
4395 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4396 line_table_test ltt (case_);
4398 const line_map_ordinary *ord_map
4399 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4400 tmp.get_filename (), 0));
4402 linemap_line_start (line_table, 1, 100);
4404 const location_t final_line_end
4405 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
4407 /* Don't attempt to run the tests if column data might be unavailable. */
4408 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4409 return;
4411 /* Test of add_location_if_nearby on the same line as the
4412 primary location. */
4414 const location_t missing_close_brace_1_39
4415 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
4416 const location_t matching_open_brace_1_18
4417 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4418 gcc_rich_location richloc (missing_close_brace_1_39);
4419 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
4420 ASSERT_TRUE (added);
4421 ASSERT_EQ (2, richloc.get_num_locations ());
4422 test_diagnostic_context dc;
4423 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4424 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4425 " ~ ^\n",
4426 pp_formatted_text (dc.printer));
4429 /* Test of add_location_if_nearby on a different line to the
4430 primary location. */
4432 const location_t missing_close_brace_6_1
4433 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
4434 const location_t matching_open_brace_3_1
4435 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
4436 gcc_rich_location richloc (missing_close_brace_6_1);
4437 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
4438 ASSERT_FALSE (added);
4439 ASSERT_EQ (1, richloc.get_num_locations ());
4443 /* Verify that we print fixits even if they only affect lines
4444 outside those covered by the ranges in the rich_location. */
4446 static void
4447 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4449 /* Create a tempfile and write some text to it.
4450 ...000000000111111111122222222223333333333.
4451 ...123456789012345678901234567890123456789. */
4452 const char *content
4453 = ("struct point { double x; double y; };\n" /* line 1. */
4454 "struct point origin = {x: 0.0,\n" /* line 2. */
4455 " y\n" /* line 3. */
4456 "\n" /* line 4. */
4457 "\n" /* line 5. */
4458 " : 0.0};\n"); /* line 6. */
4459 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4460 line_table_test ltt (case_);
4462 const line_map_ordinary *ord_map
4463 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4464 tmp.get_filename (), 0));
4466 linemap_line_start (line_table, 1, 100);
4468 const location_t final_line_end
4469 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4471 /* Don't attempt to run the tests if column data might be unavailable. */
4472 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4473 return;
4475 /* A pair of tests for modernizing the initializers to C99-style. */
4477 /* The one-liner case (line 2). */
4479 test_diagnostic_context dc;
4480 const location_t x
4481 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
4482 const location_t colon
4483 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
4484 rich_location richloc (line_table, colon);
4485 richloc.add_fixit_insert_before (x, ".");
4486 richloc.add_fixit_replace (colon, "=");
4487 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4488 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4489 " ^\n"
4490 " .=\n",
4491 pp_formatted_text (dc.printer));
4494 /* The multiline case. The caret for the rich_location is on line 6;
4495 verify that insertion fixit on line 3 is still printed (and that
4496 span starts are printed due to the gap between the span at line 3
4497 and that at line 6). */
4499 test_diagnostic_context dc;
4500 const location_t y
4501 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4502 const location_t colon
4503 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4504 rich_location richloc (line_table, colon);
4505 richloc.add_fixit_insert_before (y, ".");
4506 richloc.add_fixit_replace (colon, "=");
4507 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4508 ASSERT_STREQ ("FILENAME:3:24:\n"
4509 " y\n"
4510 " .\n"
4511 "FILENAME:6:25:\n"
4512 " : 0.0};\n"
4513 " ^\n"
4514 " =\n",
4515 pp_formatted_text (dc.printer));
4518 /* As above, but verify the behavior of multiple line spans
4519 with line-numbering enabled. */
4521 const location_t y
4522 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4523 const location_t colon
4524 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4525 rich_location richloc (line_table, colon);
4526 richloc.add_fixit_insert_before (y, ".");
4527 richloc.add_fixit_replace (colon, "=");
4528 test_diagnostic_context dc;
4529 dc.show_line_numbers_p = true;
4530 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4531 ASSERT_STREQ (" 3 | y\n"
4532 " | .\n"
4533 "......\n"
4534 " 6 | : 0.0};\n"
4535 " | ^\n"
4536 " | =\n",
4537 pp_formatted_text (dc.printer));
4542 /* Verify that fix-it hints are appropriately consolidated.
4544 If any fix-it hints in a rich_location involve locations beyond
4545 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4546 the fix-it as a whole, so there should be none.
4548 Otherwise, verify that consecutive "replace" and "remove" fix-its
4549 are merged, and that other fix-its remain separate. */
4551 static void
4552 test_fixit_consolidation (const line_table_case &case_)
4554 line_table_test ltt (case_);
4556 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4558 const location_t c10 = linemap_position_for_column (line_table, 10);
4559 const location_t c15 = linemap_position_for_column (line_table, 15);
4560 const location_t c16 = linemap_position_for_column (line_table, 16);
4561 const location_t c17 = linemap_position_for_column (line_table, 17);
4562 const location_t c20 = linemap_position_for_column (line_table, 20);
4563 const location_t c21 = linemap_position_for_column (line_table, 21);
4564 const location_t caret = c10;
4566 /* Insert + insert. */
4568 rich_location richloc (line_table, caret);
4569 richloc.add_fixit_insert_before (c10, "foo");
4570 richloc.add_fixit_insert_before (c15, "bar");
4572 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4573 /* Bogus column info for 2nd fixit, so no fixits. */
4574 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4575 else
4576 /* They should not have been merged. */
4577 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4580 /* Insert + replace. */
4582 rich_location richloc (line_table, caret);
4583 richloc.add_fixit_insert_before (c10, "foo");
4584 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4585 "bar");
4587 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4588 /* Bogus column info for 2nd fixit, so no fixits. */
4589 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4590 else
4591 /* They should not have been merged. */
4592 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4595 /* Replace + non-consecutive insert. */
4597 rich_location richloc (line_table, caret);
4598 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4599 "bar");
4600 richloc.add_fixit_insert_before (c17, "foo");
4602 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4603 /* Bogus column info for 2nd fixit, so no fixits. */
4604 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4605 else
4606 /* They should not have been merged. */
4607 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4610 /* Replace + non-consecutive replace. */
4612 rich_location richloc (line_table, caret);
4613 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4614 "foo");
4615 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4616 "bar");
4618 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4619 /* Bogus column info for 2nd fixit, so no fixits. */
4620 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4621 else
4622 /* They should not have been merged. */
4623 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4626 /* Replace + consecutive replace. */
4628 rich_location richloc (line_table, caret);
4629 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4630 "foo");
4631 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4632 "bar");
4634 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4635 /* Bogus column info for 2nd fixit, so no fixits. */
4636 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4637 else
4639 /* They should have been merged into a single "replace". */
4640 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4641 const fixit_hint *hint = richloc.get_fixit_hint (0);
4642 ASSERT_STREQ ("foobar", hint->get_string ());
4643 ASSERT_EQ (c10, hint->get_start_loc ());
4644 ASSERT_EQ (c21, hint->get_next_loc ());
4648 /* Replace + consecutive removal. */
4650 rich_location richloc (line_table, caret);
4651 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4652 "foo");
4653 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4655 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4656 /* Bogus column info for 2nd fixit, so no fixits. */
4657 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4658 else
4660 /* They should have been merged into a single replace, with the
4661 range extended to cover that of the removal. */
4662 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4663 const fixit_hint *hint = richloc.get_fixit_hint (0);
4664 ASSERT_STREQ ("foo", hint->get_string ());
4665 ASSERT_EQ (c10, hint->get_start_loc ());
4666 ASSERT_EQ (c21, hint->get_next_loc ());
4670 /* Consecutive removals. */
4672 rich_location richloc (line_table, caret);
4673 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4674 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4676 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4677 /* Bogus column info for 2nd fixit, so no fixits. */
4678 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4679 else
4681 /* They should have been merged into a single "replace-with-empty". */
4682 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4683 const fixit_hint *hint = richloc.get_fixit_hint (0);
4684 ASSERT_STREQ ("", hint->get_string ());
4685 ASSERT_EQ (c10, hint->get_start_loc ());
4686 ASSERT_EQ (c21, hint->get_next_loc ());
4691 /* Verify that the line_corrections machinery correctly prints
4692 overlapping fixit-hints. */
4694 static void
4695 test_overlapped_fixit_printing (const line_table_case &case_)
4697 /* Create a tempfile and write some text to it.
4698 ...000000000111111111122222222223333333333.
4699 ...123456789012345678901234567890123456789. */
4700 const char *content
4701 = (" foo *f = (foo *)ptr->field;\n");
4702 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4703 line_table_test ltt (case_);
4705 const line_map_ordinary *ord_map
4706 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4707 tmp.get_filename (), 0));
4709 linemap_line_start (line_table, 1, 100);
4711 const location_t final_line_end
4712 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4714 /* Don't attempt to run the tests if column data might be unavailable. */
4715 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4716 return;
4718 /* A test for converting a C-style cast to a C++-style cast. */
4719 const location_t open_paren
4720 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4721 const location_t close_paren
4722 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4723 const location_t expr_start
4724 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4725 const location_t expr_finish
4726 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4727 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4729 /* Various examples of fix-it hints that aren't themselves consolidated,
4730 but for which the *printing* may need consolidation. */
4732 /* Example where 3 fix-it hints are printed as one. */
4734 test_diagnostic_context dc;
4735 rich_location richloc (line_table, expr);
4736 richloc.add_fixit_replace (open_paren, "const_cast<");
4737 richloc.add_fixit_replace (close_paren, "> (");
4738 richloc.add_fixit_insert_after (")");
4740 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4741 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4742 " ^~~~~~~~~~\n"
4743 " -----------------\n"
4744 " const_cast<foo *> (ptr->field)\n",
4745 pp_formatted_text (dc.printer));
4747 /* Unit-test the line_corrections machinery. */
4748 char_display_policy policy (make_policy (dc, richloc));
4749 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4750 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4751 ASSERT_EQ (column_range (12, 12),
4752 get_affected_range (policy, hint_0, CU_BYTES));
4753 ASSERT_EQ (column_range (12, 12),
4754 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4755 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4756 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4757 ASSERT_EQ (column_range (18, 18),
4758 get_affected_range (policy, hint_1, CU_BYTES));
4759 ASSERT_EQ (column_range (18, 18),
4760 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4761 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4762 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4763 ASSERT_EQ (column_range (29, 28),
4764 get_affected_range (policy, hint_2, CU_BYTES));
4765 ASSERT_EQ (column_range (29, 28),
4766 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4767 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy, hint_2));
4769 /* Add each hint in turn to a line_corrections instance,
4770 and verify that they are consolidated into one correction instance
4771 as expected. */
4772 line_corrections lc (policy, tmp.get_filename (), 1);
4774 /* The first replace hint by itself. */
4775 lc.add_hint (hint_0);
4776 ASSERT_EQ (1, lc.m_corrections.length ());
4777 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4778 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4779 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4780 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4782 /* After the second replacement hint, they are printed together
4783 as a replacement (along with the text between them). */
4784 lc.add_hint (hint_1);
4785 ASSERT_EQ (1, lc.m_corrections.length ());
4786 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4787 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4788 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4789 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4791 /* After the final insertion hint, they are all printed together
4792 as a replacement (along with the text between them). */
4793 lc.add_hint (hint_2);
4794 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4795 lc.m_corrections[0]->m_text);
4796 ASSERT_EQ (1, lc.m_corrections.length ());
4797 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4798 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4799 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4802 /* Example where two are consolidated during printing. */
4804 test_diagnostic_context dc;
4805 rich_location richloc (line_table, expr);
4806 richloc.add_fixit_replace (open_paren, "CAST (");
4807 richloc.add_fixit_replace (close_paren, ") (");
4808 richloc.add_fixit_insert_after (")");
4810 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4811 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4812 " ^~~~~~~~~~\n"
4813 " -\n"
4814 " CAST (-\n"
4815 " ) ( )\n",
4816 pp_formatted_text (dc.printer));
4819 /* Example where none are consolidated during printing. */
4821 test_diagnostic_context dc;
4822 rich_location richloc (line_table, expr);
4823 richloc.add_fixit_replace (open_paren, "CST (");
4824 richloc.add_fixit_replace (close_paren, ") (");
4825 richloc.add_fixit_insert_after (")");
4827 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4828 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4829 " ^~~~~~~~~~\n"
4830 " -\n"
4831 " CST ( -\n"
4832 " ) ( )\n",
4833 pp_formatted_text (dc.printer));
4836 /* Example of deletion fix-it hints. */
4838 test_diagnostic_context dc;
4839 rich_location richloc (line_table, expr);
4840 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4841 source_range victim = {open_paren, close_paren};
4842 richloc.add_fixit_remove (victim);
4844 /* This case is actually handled by fixit-consolidation,
4845 rather than by line_corrections. */
4846 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4848 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4849 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4850 " ^~~~~~~~~~\n"
4851 " -------\n"
4852 " (bar *)\n",
4853 pp_formatted_text (dc.printer));
4856 /* Example of deletion fix-it hints that would overlap. */
4858 test_diagnostic_context dc;
4859 rich_location richloc (line_table, expr);
4860 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4861 source_range victim = {expr_start, expr_finish};
4862 richloc.add_fixit_remove (victim);
4864 /* These fixits are not consolidated. */
4865 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4867 /* But the corrections are. */
4868 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4869 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4870 " ^~~~~~~~~~\n"
4871 " -----------------\n"
4872 " (longer *)(foo *)\n",
4873 pp_formatted_text (dc.printer));
4876 /* Example of insertion fix-it hints that would overlap. */
4878 test_diagnostic_context dc;
4879 rich_location richloc (line_table, expr);
4880 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4881 richloc.add_fixit_insert_after (close_paren, "TEST");
4883 /* The first insertion is long enough that if printed naively,
4884 it would overlap with the second.
4885 Verify that they are printed as a single replacement. */
4886 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4887 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4888 " ^~~~~~~~~~\n"
4889 " -------\n"
4890 " LONGER THAN THE CAST(foo *)TEST\n",
4891 pp_formatted_text (dc.printer));
4895 /* Multibyte-aware version of preceding tests. See comments above
4896 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4897 characters here. */
4899 static void
4900 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4902 /* Create a tempfile and write some text to it. */
4904 const char *content
4905 /* Display columns.
4906 00000000000000000000000111111111111111111111111222222222222222223
4907 12344444444555555556789012344444444555555556789012345678999999990 */
4908 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4909 /* 00000000000000000000011111111111111111111112222222222333333333333
4910 12344445555666677778901234566667777888899990123456789012333344445
4911 Byte columns. */
4913 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4914 line_table_test ltt (case_);
4916 const line_map_ordinary *ord_map
4917 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4918 tmp.get_filename (), 0));
4920 linemap_line_start (line_table, 1, 100);
4922 const location_t final_line_end
4923 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4925 /* Don't attempt to run the tests if column data might be unavailable. */
4926 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4927 return;
4929 /* A test for converting a C-style cast to a C++-style cast. */
4930 const location_t open_paren
4931 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4932 const location_t close_paren
4933 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4934 const location_t expr_start
4935 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4936 const location_t expr_finish
4937 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4938 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4940 /* Various examples of fix-it hints that aren't themselves consolidated,
4941 but for which the *printing* may need consolidation. */
4943 /* Example where 3 fix-it hints are printed as one. */
4945 test_diagnostic_context dc;
4946 rich_location richloc (line_table, expr);
4947 richloc.add_fixit_replace (open_paren, "const_cast<");
4948 richloc.add_fixit_replace (close_paren, "> (");
4949 richloc.add_fixit_insert_after (")");
4951 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4952 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4953 " *f = (f\xf0\x9f\x98\x82"
4954 " *)ptr->field\xcf\x80"
4955 ";\n"
4956 " ^~~~~~~~~~~\n"
4957 " ------------------\n"
4958 " const_cast<f\xf0\x9f\x98\x82"
4959 " *> (ptr->field\xcf\x80"
4960 ")\n",
4961 pp_formatted_text (dc.printer));
4963 /* Unit-test the line_corrections machinery. */
4964 char_display_policy policy (make_policy (dc, richloc));
4965 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4966 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4967 ASSERT_EQ (column_range (14, 14),
4968 get_affected_range (policy, hint_0, CU_BYTES));
4969 ASSERT_EQ (column_range (12, 12),
4970 get_affected_range (policy, hint_0, CU_DISPLAY_COLS));
4971 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy, hint_0));
4972 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4973 ASSERT_EQ (column_range (22, 22),
4974 get_affected_range (policy, hint_1, CU_BYTES));
4975 ASSERT_EQ (column_range (18, 18),
4976 get_affected_range (policy, hint_1, CU_DISPLAY_COLS));
4977 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy, hint_1));
4978 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4979 ASSERT_EQ (column_range (35, 34),
4980 get_affected_range (policy, hint_2, CU_BYTES));
4981 ASSERT_EQ (column_range (30, 29),
4982 get_affected_range (policy, hint_2, CU_DISPLAY_COLS));
4983 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy, hint_2));
4985 /* Add each hint in turn to a line_corrections instance,
4986 and verify that they are consolidated into one correction instance
4987 as expected. */
4988 line_corrections lc (policy, tmp.get_filename (), 1);
4990 /* The first replace hint by itself. */
4991 lc.add_hint (hint_0);
4992 ASSERT_EQ (1, lc.m_corrections.length ());
4993 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
4994 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4995 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4996 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4998 /* After the second replacement hint, they are printed together
4999 as a replacement (along with the text between them). */
5000 lc.add_hint (hint_1);
5001 ASSERT_EQ (1, lc.m_corrections.length ());
5002 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5003 lc.m_corrections[0]->m_text);
5004 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5005 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5006 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5008 /* After the final insertion hint, they are all printed together
5009 as a replacement (along with the text between them). */
5010 lc.add_hint (hint_2);
5011 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5012 lc.m_corrections[0]->m_text);
5013 ASSERT_EQ (1, lc.m_corrections.length ());
5014 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5015 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5016 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5019 /* Example where two are consolidated during printing. */
5021 test_diagnostic_context dc;
5022 rich_location richloc (line_table, expr);
5023 richloc.add_fixit_replace (open_paren, "CAST (");
5024 richloc.add_fixit_replace (close_paren, ") (");
5025 richloc.add_fixit_insert_after (")");
5027 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5028 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5029 " *f = (f\xf0\x9f\x98\x82"
5030 " *)ptr->field\xcf\x80"
5031 ";\n"
5032 " ^~~~~~~~~~~\n"
5033 " -\n"
5034 " CAST (-\n"
5035 " ) ( )\n",
5036 pp_formatted_text (dc.printer));
5039 /* Example where none are consolidated during printing. */
5041 test_diagnostic_context dc;
5042 rich_location richloc (line_table, expr);
5043 richloc.add_fixit_replace (open_paren, "CST (");
5044 richloc.add_fixit_replace (close_paren, ") (");
5045 richloc.add_fixit_insert_after (")");
5047 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5048 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5049 " *f = (f\xf0\x9f\x98\x82"
5050 " *)ptr->field\xcf\x80"
5051 ";\n"
5052 " ^~~~~~~~~~~\n"
5053 " -\n"
5054 " CST ( -\n"
5055 " ) ( )\n",
5056 pp_formatted_text (dc.printer));
5059 /* Example of deletion fix-it hints. */
5061 test_diagnostic_context dc;
5062 rich_location richloc (line_table, expr);
5063 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5064 source_range victim = {open_paren, close_paren};
5065 richloc.add_fixit_remove (victim);
5067 /* This case is actually handled by fixit-consolidation,
5068 rather than by line_corrections. */
5069 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5071 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5072 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5073 " *f = (f\xf0\x9f\x98\x82"
5074 " *)ptr->field\xcf\x80"
5075 ";\n"
5076 " ^~~~~~~~~~~\n"
5077 " -------\n"
5078 " (bar\xf0\x9f\x98\x82"
5079 " *)\n",
5080 pp_formatted_text (dc.printer));
5083 /* Example of deletion fix-it hints that would overlap. */
5085 test_diagnostic_context dc;
5086 rich_location richloc (line_table, expr);
5087 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5088 source_range victim = {expr_start, expr_finish};
5089 richloc.add_fixit_remove (victim);
5091 /* These fixits are not consolidated. */
5092 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5094 /* But the corrections are. */
5095 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5096 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5097 " *f = (f\xf0\x9f\x98\x82"
5098 " *)ptr->field\xcf\x80"
5099 ";\n"
5100 " ^~~~~~~~~~~\n"
5101 " ------------------\n"
5102 " (long\xf0\x9f\x98\x82"
5103 " *)(f\xf0\x9f\x98\x82"
5104 " *)\n",
5105 pp_formatted_text (dc.printer));
5108 /* Example of insertion fix-it hints that would overlap. */
5110 test_diagnostic_context dc;
5111 rich_location richloc (line_table, expr);
5112 richloc.add_fixit_insert_before
5113 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5114 richloc.add_fixit_insert_after (close_paren, "TEST");
5116 /* The first insertion is long enough that if printed naively,
5117 it would overlap with the second.
5118 Verify that they are printed as a single replacement. */
5119 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5120 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5121 " *f = (f\xf0\x9f\x98\x82"
5122 " *)ptr->field\xcf\x80"
5123 ";\n"
5124 " ^~~~~~~~~~~\n"
5125 " -------\n"
5126 " L\xf0\x9f\x98\x82"
5127 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5128 " *)TEST\n",
5129 pp_formatted_text (dc.printer));
5133 /* Verify that the line_corrections machinery correctly prints
5134 overlapping fixit-hints that have been added in the wrong
5135 order.
5136 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5138 static void
5139 test_overlapped_fixit_printing_2 (const line_table_case &case_)
5141 /* Create a tempfile and write some text to it.
5142 ...000000000111111111122222222223333333333.
5143 ...123456789012345678901234567890123456789. */
5144 const char *content
5145 = ("int a5[][0][0] = { 1, 2 };\n");
5146 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5147 line_table_test ltt (case_);
5149 const line_map_ordinary *ord_map
5150 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5151 tmp.get_filename (), 0));
5153 linemap_line_start (line_table, 1, 100);
5155 const location_t final_line_end
5156 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5158 /* Don't attempt to run the tests if column data might be unavailable. */
5159 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5160 return;
5162 const location_t col_1
5163 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5164 const location_t col_20
5165 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5166 const location_t col_21
5167 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5168 const location_t col_23
5169 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5170 const location_t col_25
5171 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5173 /* Two insertions, in the wrong order. */
5175 test_diagnostic_context dc;
5177 rich_location richloc (line_table, col_20);
5178 richloc.add_fixit_insert_before (col_23, "{");
5179 richloc.add_fixit_insert_before (col_21, "}");
5181 /* These fixits should be accepted; they can't be consolidated. */
5182 char_display_policy policy (make_policy (dc, richloc));
5183 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5184 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5185 ASSERT_EQ (column_range (23, 22),
5186 get_affected_range (policy, hint_0, CU_BYTES));
5187 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy, hint_0));
5188 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5189 ASSERT_EQ (column_range (21, 20),
5190 get_affected_range (policy, hint_1, CU_BYTES));
5191 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy, hint_1));
5193 /* Verify that they're printed correctly. */
5194 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5195 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5196 " ^\n"
5197 " } {\n",
5198 pp_formatted_text (dc.printer));
5201 /* Various overlapping insertions, some occurring "out of order"
5202 (reproducing the fix-it hints from PR c/81405). */
5204 test_diagnostic_context dc;
5205 rich_location richloc (line_table, col_20);
5207 richloc.add_fixit_insert_before (col_20, "{{");
5208 richloc.add_fixit_insert_before (col_21, "}}");
5209 richloc.add_fixit_insert_before (col_23, "{");
5210 richloc.add_fixit_insert_before (col_21, "}");
5211 richloc.add_fixit_insert_before (col_23, "{{");
5212 richloc.add_fixit_insert_before (col_25, "}");
5213 richloc.add_fixit_insert_before (col_21, "}");
5214 richloc.add_fixit_insert_before (col_1, "{");
5215 richloc.add_fixit_insert_before (col_25, "}");
5216 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5217 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5218 " ^\n"
5219 " { -----\n"
5220 " {{1}}}}, {{{2 }}\n",
5221 pp_formatted_text (dc.printer));
5225 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5227 static void
5228 test_fixit_insert_containing_newline (const line_table_case &case_)
5230 /* Create a tempfile and write some text to it.
5231 .........................0000000001111111.
5232 .........................1234567890123456. */
5233 const char *old_content = (" case 'a':\n" /* line 1. */
5234 " x = a;\n" /* line 2. */
5235 " case 'b':\n" /* line 3. */
5236 " x = b;\n");/* line 4. */
5238 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5239 line_table_test ltt (case_);
5240 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5242 location_t case_start = linemap_position_for_column (line_table, 5);
5243 location_t case_finish = linemap_position_for_column (line_table, 13);
5244 location_t case_loc = make_location (case_start, case_start, case_finish);
5245 location_t line_start = linemap_position_for_column (line_table, 1);
5247 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5248 return;
5250 /* Add a "break;" on a line by itself before line 3 i.e. before
5251 column 1 of line 3. */
5253 rich_location richloc (line_table, case_loc);
5254 richloc.add_fixit_insert_before (line_start, " break;\n");
5256 /* Without line numbers. */
5258 test_diagnostic_context dc;
5259 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5260 ASSERT_STREQ (" x = a;\n"
5261 "+ break;\n"
5262 " case 'b':\n"
5263 " ^~~~~~~~~\n",
5264 pp_formatted_text (dc.printer));
5267 /* With line numbers. */
5269 test_diagnostic_context dc;
5270 dc.show_line_numbers_p = true;
5271 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5272 ASSERT_STREQ (" 2 | x = a;\n"
5273 " +++ |+ break;\n"
5274 " 3 | case 'b':\n"
5275 " | ^~~~~~~~~\n",
5276 pp_formatted_text (dc.printer));
5280 /* Verify that attempts to add text with a newline fail when the
5281 insertion point is *not* at the start of a line. */
5283 rich_location richloc (line_table, case_loc);
5284 richloc.add_fixit_insert_before (case_start, "break;\n");
5285 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5286 test_diagnostic_context dc;
5287 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5288 ASSERT_STREQ (" case 'b':\n"
5289 " ^~~~~~~~~\n",
5290 pp_formatted_text (dc.printer));
5294 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5295 of the file, where the fix-it is printed in a different line-span
5296 to the primary range of the diagnostic. */
5298 static void
5299 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5301 /* Create a tempfile and write some text to it.
5302 .........................0000000001111111.
5303 .........................1234567890123456. */
5304 const char *old_content = ("test (int ch)\n" /* line 1. */
5305 "{\n" /* line 2. */
5306 " putchar (ch);\n" /* line 3. */
5307 "}\n"); /* line 4. */
5309 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5310 line_table_test ltt (case_);
5312 const line_map_ordinary *ord_map = linemap_check_ordinary
5313 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5314 linemap_line_start (line_table, 1, 100);
5316 /* The primary range is the "putchar" token. */
5317 location_t putchar_start
5318 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5319 location_t putchar_finish
5320 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5321 location_t putchar_loc
5322 = make_location (putchar_start, putchar_start, putchar_finish);
5323 rich_location richloc (line_table, putchar_loc);
5325 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5326 location_t file_start
5327 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5328 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5330 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5331 return;
5334 test_diagnostic_context dc;
5335 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5336 ASSERT_STREQ ("FILENAME:1:1:\n"
5337 "+#include <stdio.h>\n"
5338 " test (int ch)\n"
5339 "FILENAME:3:2:\n"
5340 " putchar (ch);\n"
5341 " ^~~~~~~\n",
5342 pp_formatted_text (dc.printer));
5345 /* With line-numbering, the line spans are close enough to be
5346 consolidated, since it makes little sense to skip line 2. */
5348 test_diagnostic_context dc;
5349 dc.show_line_numbers_p = true;
5350 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5351 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5352 " 1 | test (int ch)\n"
5353 " 2 | {\n"
5354 " 3 | putchar (ch);\n"
5355 " | ^~~~~~~\n",
5356 pp_formatted_text (dc.printer));
5360 /* Replacement fix-it hint containing a newline.
5361 This will fail, as newlines are only supported when inserting at the
5362 beginning of a line. */
5364 static void
5365 test_fixit_replace_containing_newline (const line_table_case &case_)
5367 /* Create a tempfile and write some text to it.
5368 .........................0000000001111.
5369 .........................1234567890123. */
5370 const char *old_content = "foo = bar ();\n";
5372 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5373 line_table_test ltt (case_);
5374 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5376 /* Replace the " = " with "\n = ", as if we were reformatting an
5377 overly long line. */
5378 location_t start = linemap_position_for_column (line_table, 4);
5379 location_t finish = linemap_position_for_column (line_table, 6);
5380 location_t loc = linemap_position_for_column (line_table, 13);
5381 rich_location richloc (line_table, loc);
5382 source_range range = source_range::from_locations (start, finish);
5383 richloc.add_fixit_replace (range, "\n =");
5385 /* Arbitrary newlines are not yet supported within fix-it hints, so
5386 the fix-it should not be displayed. */
5387 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5389 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5390 return;
5392 test_diagnostic_context dc;
5393 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5394 ASSERT_STREQ (" foo = bar ();\n"
5395 " ^\n",
5396 pp_formatted_text (dc.printer));
5399 /* Fix-it hint, attempting to delete a newline.
5400 This will fail, as we currently only support fix-it hints that
5401 affect one line at a time. */
5403 static void
5404 test_fixit_deletion_affecting_newline (const line_table_case &case_)
5406 /* Create a tempfile and write some text to it.
5407 ..........................0000000001111.
5408 ..........................1234567890123. */
5409 const char *old_content = ("foo = bar (\n"
5410 " );\n");
5412 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5413 line_table_test ltt (case_);
5414 const line_map_ordinary *ord_map = linemap_check_ordinary
5415 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5416 linemap_line_start (line_table, 1, 100);
5418 /* Attempt to delete the " (\n...)". */
5419 location_t start
5420 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
5421 location_t caret
5422 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5423 location_t finish
5424 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
5425 location_t loc = make_location (caret, start, finish);
5426 rich_location richloc (line_table, loc);
5427 richloc. add_fixit_remove ();
5429 /* Fix-it hints that affect more than one line are not yet supported, so
5430 the fix-it should not be displayed. */
5431 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5433 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5434 return;
5436 test_diagnostic_context dc;
5437 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5438 ASSERT_STREQ (" foo = bar (\n"
5439 " ~^\n"
5440 " );\n"
5441 " ~ \n",
5442 pp_formatted_text (dc.printer));
5445 static void
5446 test_tab_expansion (const line_table_case &case_)
5448 /* Create a tempfile and write some text to it. This example uses a tabstop
5449 of 8, as the column numbers attempt to indicate:
5451 .....................000.01111111111.22222333333 display
5452 .....................123.90123456789.56789012345 columns */
5453 const char *content = " \t This: `\t' is a tab.\n";
5454 /* ....................000 00000011111 11111222222 byte
5455 ....................123 45678901234 56789012345 columns */
5457 const int tabstop = 8;
5458 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5459 const int first_non_ws_byte_col = 7;
5460 const int right_quote_byte_col = 15;
5461 const int last_byte_col = 25;
5462 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5464 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5465 line_table_test ltt (case_);
5466 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5468 /* Don't attempt to run the tests if column data might be unavailable. */
5469 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5470 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5471 return;
5473 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5474 into 11 spaces. Recall that print_line() also puts one space before
5475 everything too. */
5477 test_diagnostic_context dc;
5478 dc.tabstop = tabstop;
5479 rich_location richloc (line_table,
5480 linemap_position_for_column (line_table,
5481 first_non_ws_byte_col));
5482 layout test_layout (&dc, &richloc, DK_ERROR);
5483 test_layout.print_line (1);
5484 ASSERT_STREQ (" This: ` ' is a tab.\n"
5485 " ^\n",
5486 pp_formatted_text (dc.printer));
5489 /* Confirm the display width was tracked correctly across the internal tab
5490 as well. */
5492 test_diagnostic_context dc;
5493 dc.tabstop = tabstop;
5494 rich_location richloc (line_table,
5495 linemap_position_for_column (line_table,
5496 right_quote_byte_col));
5497 layout test_layout (&dc, &richloc, DK_ERROR);
5498 test_layout.print_line (1);
5499 ASSERT_STREQ (" This: ` ' is a tab.\n"
5500 " ^\n",
5501 pp_formatted_text (dc.printer));
5505 /* Verify that the escaping machinery can cope with a variety of different
5506 invalid bytes. */
5508 static void
5509 test_escaping_bytes_1 (const line_table_case &case_)
5511 const char content[] = "before\0\1\2\3\r\x80\xff""after\n";
5512 const size_t sz = sizeof (content);
5513 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5514 line_table_test ltt (case_);
5515 const line_map_ordinary *ord_map = linemap_check_ordinary
5516 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5517 linemap_line_start (line_table, 1, 100);
5519 location_t finish
5520 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5521 strlen (content));
5523 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5524 return;
5526 /* Locations of the NUL and \r bytes. */
5527 location_t nul_loc
5528 = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
5529 location_t r_loc
5530 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
5531 gcc_rich_location richloc (nul_loc);
5532 richloc.add_range (r_loc);
5535 test_diagnostic_context dc;
5536 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5537 ASSERT_STREQ (" before \1\2\3 \x80\xff""after\n"
5538 " ^ ~\n",
5539 pp_formatted_text (dc.printer));
5541 richloc.set_escape_on_output (true);
5543 test_diagnostic_context dc;
5544 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5545 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5546 ASSERT_STREQ
5547 (" before<U+0000><U+0001><U+0002><U+0003><U+000D><80><ff>after\n"
5548 " ^~~~~~~~ ~~~~~~~~\n",
5549 pp_formatted_text (dc.printer));
5552 test_diagnostic_context dc;
5553 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5554 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5555 ASSERT_STREQ (" before<00><01><02><03><0d><80><ff>after\n"
5556 " ^~~~ ~~~~\n",
5557 pp_formatted_text (dc.printer));
5561 /* As above, but verify that we handle the initial byte of a line
5562 correctly. */
5564 static void
5565 test_escaping_bytes_2 (const line_table_case &case_)
5567 const char content[] = "\0after\n";
5568 const size_t sz = sizeof (content);
5569 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5570 line_table_test ltt (case_);
5571 const line_map_ordinary *ord_map = linemap_check_ordinary
5572 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5573 linemap_line_start (line_table, 1, 100);
5575 location_t finish
5576 = linemap_position_for_line_and_column (line_table, ord_map, 1,
5577 strlen (content));
5579 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5580 return;
5582 /* Location of the NUL byte. */
5583 location_t nul_loc
5584 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5585 gcc_rich_location richloc (nul_loc);
5588 test_diagnostic_context dc;
5589 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5590 ASSERT_STREQ (" after\n"
5591 " ^\n",
5592 pp_formatted_text (dc.printer));
5594 richloc.set_escape_on_output (true);
5596 test_diagnostic_context dc;
5597 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_UNICODE;
5598 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5599 ASSERT_STREQ (" <U+0000>after\n"
5600 " ^~~~~~~~\n",
5601 pp_formatted_text (dc.printer));
5604 test_diagnostic_context dc;
5605 dc.escape_format = DIAGNOSTICS_ESCAPE_FORMAT_BYTES;
5606 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5607 ASSERT_STREQ (" <00>after\n"
5608 " ^~~~\n",
5609 pp_formatted_text (dc.printer));
5613 /* Verify that line numbers are correctly printed for the case of
5614 a multiline range in which the width of the line numbers changes
5615 (e.g. from "9" to "10"). */
5617 static void
5618 test_line_numbers_multiline_range ()
5620 /* Create a tempfile and write some text to it. */
5621 pretty_printer pp;
5622 for (int i = 0; i < 20; i++)
5623 /* .........0000000001111111.
5624 .............1234567890123456. */
5625 pp_printf (&pp, "this is line %i\n", i + 1);
5626 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5627 line_table_test ltt;
5629 const line_map_ordinary *ord_map = linemap_check_ordinary
5630 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5631 linemap_line_start (line_table, 1, 100);
5633 /* Create a multi-line location, starting at the "line" of line 9, with
5634 a caret on the "is" of line 10, finishing on the "this" line 11. */
5636 location_t start
5637 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
5638 location_t caret
5639 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
5640 location_t finish
5641 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
5642 location_t loc = make_location (caret, start, finish);
5644 test_diagnostic_context dc;
5645 dc.show_line_numbers_p = true;
5646 dc.min_margin_width = 0;
5647 gcc_rich_location richloc (loc);
5648 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
5649 ASSERT_STREQ (" 9 | this is line 9\n"
5650 " | ~~~~~~\n"
5651 "10 | this is line 10\n"
5652 " | ~~~~~^~~~~~~~~~\n"
5653 "11 | this is line 11\n"
5654 " | ~~~~ \n",
5655 pp_formatted_text (dc.printer));
5658 /* Run all of the selftests within this file. */
5660 void
5661 diagnostic_show_locus_c_tests ()
5663 test_line_span ();
5665 test_layout_range_for_single_point ();
5666 test_layout_range_for_single_line ();
5667 test_layout_range_for_multiple_lines ();
5669 test_display_widths ();
5671 for_each_line_table_case (test_layout_x_offset_display_utf8);
5672 for_each_line_table_case (test_layout_x_offset_display_tab);
5674 test_get_line_bytes_without_trailing_whitespace ();
5676 test_diagnostic_show_locus_unknown_location ();
5678 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5679 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5680 for_each_line_table_case (test_add_location_if_nearby);
5681 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5682 for_each_line_table_case (test_fixit_consolidation);
5683 for_each_line_table_case (test_overlapped_fixit_printing);
5684 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5685 for_each_line_table_case (test_overlapped_fixit_printing_2);
5686 for_each_line_table_case (test_fixit_insert_containing_newline);
5687 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5688 for_each_line_table_case (test_fixit_replace_containing_newline);
5689 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5690 for_each_line_table_case (test_tab_expansion);
5691 for_each_line_table_case (test_escaping_bytes_1);
5692 for_each_line_table_case (test_escaping_bytes_2);
5694 test_line_numbers_multiline_range ();
5697 } // namespace selftest
5699 #endif /* #if CHECKING_P */
5701 #if __GNUC__ >= 10
5702 # pragma GCC diagnostic pop
5703 #endif