1/* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2024 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
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"
34
35#ifdef HAVE_TERMIOS_H
36# include <termios.h>
37#endif
38
39#ifdef GWINSZ_IN_SYS_IOCTL
40# include <sys/ioctl.h>
41#endif
42
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
49
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. */
54
55namespace {
56
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. */
60
61struct point_state
62{
63 int range_idx;
64 bool draw_caret_p;
65};
66
67/* A class to inject colorization codes when printing the diagnostic locus.
68
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
74
75 The class caches the lookup of the color codes for the above.
76
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. */
82
83class colorizer
84{
85 public:
86 colorizer (pretty_printer *pp,
87 diagnostic_t diagnostic_kind);
88 ~colorizer ();
89
90 void set_range (int range_idx)
91 {
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);
100 }
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); }
104
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 *);
110
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;
115
116 pretty_printer *m_pp;
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;
124};
125
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.
133
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
136
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
142
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).
149
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.
158
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. */
161
162enum column_unit {
163 /* Measured in raw bytes. */
164 CU_BYTES = 0,
165
166 /* Measured in display units. */
167 CU_DISPLAY_COLS,
168
169 /* For arrays indexed by column_unit. */
170 CU_NUM_UNITS
171};
172
173/* Utility class to augment an exploc with the corresponding display column. */
174
175class exploc_with_display_col : public expanded_location
176{
177 public:
178 exploc_with_display_col (file_cache &fc,
179 const expanded_location &exploc,
180 const cpp_char_column_policy &policy,
181 enum location_aspect aspect)
182 : expanded_location (exploc),
183 m_display_col (location_compute_display_column (fc, exploc, policy))
184 {
185 if (exploc.column > 0)
186 {
187 /* m_display_col is now the final column of the byte.
188 If escaping has happened, we may want the first column instead. */
189 if (aspect != LOCATION_ASPECT_FINISH)
190 {
191 expanded_location prev_exploc (exploc);
192 prev_exploc.column--;
193 int prev_display_col
194 = (location_compute_display_column (fc, exploc: prev_exploc, policy));
195 m_display_col = prev_display_col + 1;
196 }
197 }
198 }
199
200 int m_display_col;
201};
202
203
204/* A point within a layout_range; similar to an exploc_with_display_col,
205 but after filtering on file. */
206
207class layout_point
208{
209 public:
210 layout_point (const exploc_with_display_col &exploc)
211 : m_line (exploc.line)
212 {
213 m_columns[CU_BYTES] = exploc.column;
214 m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
215 }
216
217 linenum_type m_line;
218 int m_columns[CU_NUM_UNITS];
219};
220
221/* A class for use by "class layout" below: a filtered location_range. */
222
223class layout_range
224{
225 public:
226 layout_range (const exploc_with_display_col &start_exploc,
227 const exploc_with_display_col &finish_exploc,
228 enum range_display_kind range_display_kind,
229 const exploc_with_display_col &caret_exploc,
230 unsigned original_idx,
231 const range_label *label);
232
233 bool contains_point (linenum_type row, int column,
234 enum column_unit col_unit) const;
235 bool intersects_line_p (linenum_type row) const;
236
237 layout_point m_start;
238 layout_point m_finish;
239 enum range_display_kind m_range_display_kind;
240 layout_point m_caret;
241 unsigned m_original_idx;
242 const range_label *m_label;
243};
244
245/* A struct for use by layout::print_source_line for telling
246 layout::print_annotation_line the extents of the source line that
247 it printed, so that underlines can be clipped appropriately. Units
248 are 1-based display columns. */
249
250struct line_bounds
251{
252 int m_first_non_ws_disp_col;
253 int m_last_non_ws_disp_col;
254
255 line_bounds ()
256 {
257 m_first_non_ws_disp_col = INT_MAX;
258 m_last_non_ws_disp_col = 0;
259 }
260};
261
262/* A range of contiguous source lines within a layout (e.g. "lines 5-10"
263 or "line 23"). During the layout ctor, layout::calculate_line_spans
264 splits the pertinent source lines into a list of disjoint line_span
265 instances (e.g. lines 5-10, lines 15-20, line 23). */
266
267class line_span
268{
269public:
270 line_span (linenum_type first_line, linenum_type last_line)
271 : m_first_line (first_line), m_last_line (last_line)
272 {
273 gcc_assert (first_line <= last_line);
274 }
275 linenum_type get_first_line () const { return m_first_line; }
276 linenum_type get_last_line () const { return m_last_line; }
277
278 bool contains_line_p (linenum_type line) const
279 {
280 return line >= m_first_line && line <= m_last_line;
281 }
282
283 static int comparator (const void *p1, const void *p2)
284 {
285 const line_span *ls1 = (const line_span *)p1;
286 const line_span *ls2 = (const line_span *)p2;
287 int first_line_cmp = compare (lhs: ls1->m_first_line, rhs: ls2->m_first_line);
288 if (first_line_cmp)
289 return first_line_cmp;
290 return compare (lhs: ls1->m_last_line, rhs: ls2->m_last_line);
291 }
292
293 linenum_type m_first_line;
294 linenum_type m_last_line;
295};
296
297#if CHECKING_P
298
299/* Selftests for line_span. */
300
301static void
302test_line_span ()
303{
304 line_span line_one (1, 1);
305 ASSERT_EQ (1, line_one.get_first_line ());
306 ASSERT_EQ (1, line_one.get_last_line ());
307 ASSERT_FALSE (line_one.contains_line_p (0));
308 ASSERT_TRUE (line_one.contains_line_p (1));
309 ASSERT_FALSE (line_one.contains_line_p (2));
310
311 line_span lines_1_to_3 (1, 3);
312 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
313 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
314 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
315 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
316
317 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
318 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
319 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
320
321 /* A linenum > 2^31. */
322 const linenum_type LARGEST_LINE = 0xffffffff;
323 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
324 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
325 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
326
327 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
328 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
329}
330
331#endif /* #if CHECKING_P */
332
333/* A bundle of information containing how to print unicode
334 characters and bytes when quoting source code.
335
336 Provides a unified place to support escaping some subset
337 of characters to some format.
338
339 Extends char_column_policy; printing is split out to avoid
340 libcpp having to know about pretty_printer. */
341
342struct char_display_policy : public cpp_char_column_policy
343{
344 public:
345 char_display_policy (int tabstop,
346 int (*width_cb) (cppchar_t c),
347 void (*print_cb) (pretty_printer *pp,
348 const cpp_decoded_char &cp))
349 : cpp_char_column_policy (tabstop, width_cb),
350 m_print_cb (print_cb)
351 {
352 }
353
354 void (*m_print_cb) (pretty_printer *pp,
355 const cpp_decoded_char &cp);
356};
357
358/* A class to control the overall layout when printing a diagnostic.
359
360 The layout is determined within the constructor.
361 It is then printed by repeatedly calling the "print_source_line",
362 "print_annotation_line" and "print_any_fixits" methods.
363
364 We assume we have disjoint ranges. */
365
366class layout
367{
368 public:
369 layout (const diagnostic_context &context,
370 const rich_location &richloc,
371 diagnostic_t diagnostic_kind,
372 pretty_printer *pp);
373
374 bool maybe_add_location_range (const location_range *loc_range,
375 unsigned original_idx,
376 bool restrict_to_current_line_spans);
377
378 int get_num_line_spans () const { return m_line_spans.length (); }
379 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
380
381 int get_linenum_width () const { return m_linenum_width; }
382 int get_x_offset_display () const { return m_x_offset_display; }
383
384 void print_gap_in_line_numbering ();
385 bool print_heading_for_line_span_index_p (int line_span_idx) const;
386
387 expanded_location get_expanded_location (const line_span *) const;
388
389 void print_line (linenum_type row);
390
391 void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
392
393 private:
394 bool will_show_line_p (linenum_type row) const;
395 void print_leading_fixits (linenum_type row);
396 line_bounds print_source_line (linenum_type row, const char *line,
397 int line_bytes);
398 bool should_print_annotation_line_p (linenum_type row) const;
399 void start_annotation_line (char margin_char = ' ') const;
400 void print_annotation_line (linenum_type row, const line_bounds lbounds);
401 void print_any_labels (linenum_type row);
402 void print_trailing_fixits (linenum_type row);
403
404 bool annotation_line_showed_range_p (linenum_type line, int start_column,
405 int finish_column) const;
406 void show_ruler (int max_column) const;
407
408 bool validate_fixit_hint_p (const fixit_hint *hint);
409
410 void calculate_line_spans ();
411 void calculate_linenum_width ();
412 void calculate_x_offset_display ();
413
414 void print_newline ();
415
416 bool
417 get_state_at_point (/* Inputs. */
418 linenum_type row, int column,
419 int first_non_ws, int last_non_ws,
420 enum column_unit col_unit,
421 /* Outputs. */
422 point_state *out_state);
423
424 int
425 get_x_bound_for_row (linenum_type row, int caret_column,
426 int last_non_ws);
427
428 void
429 move_to_column (int *column, int dest_column, bool add_left_margin);
430
431 private:
432 bool compatible_locations_p (location_t loc_a, location_t loc_b) const;
433
434 const diagnostic_source_printing_options &m_options;
435 const line_maps *m_line_table;
436 file_cache &m_file_cache;
437 pretty_printer *m_pp;
438 char_display_policy m_policy;
439 location_t m_primary_loc;
440 exploc_with_display_col m_exploc;
441 colorizer m_colorizer;
442 bool m_diagnostic_path_p;
443 auto_vec <layout_range> m_layout_ranges;
444 auto_vec <const fixit_hint *> m_fixit_hints;
445 auto_vec <line_span> m_line_spans;
446 int m_linenum_width;
447 int m_x_offset_display;
448 bool m_escape_on_output;
449};
450
451/* Implementation of "class colorizer". */
452
453/* The constructor for "colorizer". Lookup and store color codes for the
454 different kinds of things we might need to print. */
455
456colorizer::colorizer (pretty_printer *pp,
457 diagnostic_t diagnostic_kind) :
458 m_pp (pp),
459 m_diagnostic_kind (diagnostic_kind),
460 m_current_state (STATE_NORMAL_TEXT)
461{
462 m_range1 = get_color_by_name ("range1");
463 m_range2 = get_color_by_name ("range2");
464 m_fixit_insert = get_color_by_name ("fixit-insert");
465 m_fixit_delete = get_color_by_name ("fixit-delete");
466 m_stop_color = colorize_stop (pp_show_color (m_pp));
467}
468
469/* The destructor for "colorize". If colorization is on, print a code to
470 turn it off. */
471
472colorizer::~colorizer ()
473{
474 finish_state (state: m_current_state);
475}
476
477/* Update state, printing color codes if necessary if there's a state
478 change. */
479
480void
481colorizer::set_state (int new_state)
482{
483 if (m_current_state != new_state)
484 {
485 finish_state (state: m_current_state);
486 m_current_state = new_state;
487 begin_state (state: new_state);
488 }
489}
490
491/* Turn on any colorization for STATE. */
492
493void
494colorizer::begin_state (int state)
495{
496 switch (state)
497 {
498 case STATE_NORMAL_TEXT:
499 break;
500
501 case STATE_FIXIT_INSERT:
502 pp_string (m_pp, m_fixit_insert);
503 break;
504
505 case STATE_FIXIT_DELETE:
506 pp_string (m_pp, m_fixit_delete);
507 break;
508
509 case 0:
510 /* Make range 0 be the same color as the "kind" text
511 (error vs warning vs note). */
512 pp_string
513 (m_pp,
514 colorize_start (pp_show_color (m_pp),
515 name: diagnostic_get_color_for_kind (kind: m_diagnostic_kind)));
516 break;
517
518 case 1:
519 pp_string (m_pp, m_range1);
520 break;
521
522 case 2:
523 pp_string (m_pp, m_range2);
524 break;
525
526 default:
527 /* For ranges beyond 2, alternate between color 1 and color 2. */
528 {
529 gcc_assert (state > 2);
530 pp_string (m_pp,
531 state % 2 ? m_range1 : m_range2);
532 }
533 break;
534 }
535}
536
537/* Turn off any colorization for STATE. */
538
539void
540colorizer::finish_state (int state)
541{
542 if (state != STATE_NORMAL_TEXT)
543 pp_string (m_pp, m_stop_color);
544}
545
546/* Get the color code for NAME (or the empty string if
547 colorization is disabled). */
548
549const char *
550colorizer::get_color_by_name (const char *name)
551{
552 return colorize_start (pp_show_color (m_pp), name);
553}
554
555/* Implementation of class layout_range. */
556
557/* The constructor for class layout_range.
558 Initialize various layout_point fields from expanded_location
559 equivalents; we've already filtered on file. */
560
561layout_range::layout_range (const exploc_with_display_col &start_exploc,
562 const exploc_with_display_col &finish_exploc,
563 enum range_display_kind range_display_kind,
564 const exploc_with_display_col &caret_exploc,
565 unsigned original_idx,
566 const range_label *label)
567: m_start (start_exploc),
568 m_finish (finish_exploc),
569 m_range_display_kind (range_display_kind),
570 m_caret (caret_exploc),
571 m_original_idx (original_idx),
572 m_label (label)
573{
574}
575
576/* Is (column, row) within the given range?
577 We've already filtered on the file.
578
579 Ranges are closed (both limits are within the range).
580
581 Example A: a single-line range:
582 start: (col=22, line=2)
583 finish: (col=38, line=2)
584
585 |00000011111111112222222222333333333344444444444
586 |34567890123456789012345678901234567890123456789
587--+-----------------------------------------------
58801|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
58902|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
59003|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
591
592 Example B: a multiline range with
593 start: (col=14, line=3)
594 finish: (col=08, line=5)
595
596 |00000011111111112222222222333333333344444444444
597 |34567890123456789012345678901234567890123456789
598--+-----------------------------------------------
59901|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
60002|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
60103|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
60204|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
60305|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
60406|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
605--+-----------------------------------------------
606
607 Legend:
608 - 'b' indicates a point *before* the range
609 - 'S' indicates the start of the range
610 - 'w' indicates a point within the range
611 - 'F' indicates the finish of the range (which is
612 within it).
613 - 'a' indicates a subsequent point *after* the range.
614
615 COL_UNIT controls whether we check the byte column or
616 the display column; one or the other is more convenient
617 depending on the context. */
618
619bool
620layout_range::contains_point (linenum_type row, int column,
621 enum column_unit col_unit) const
622{
623 gcc_assert (m_start.m_line <= m_finish.m_line);
624 /* ...but the equivalent isn't true for the columns;
625 consider example B in the comment above. */
626
627 if (row < m_start.m_line)
628 /* Points before the first line of the range are
629 outside it (corresponding to line 01 in example A
630 and lines 01 and 02 in example B above). */
631 return false;
632
633 if (row == m_start.m_line)
634 /* On same line as start of range (corresponding
635 to line 02 in example A and line 03 in example B). */
636 {
637 if (column < m_start.m_columns[col_unit])
638 /* Points on the starting line of the range, but
639 before the column in which it begins. */
640 return false;
641
642 if (row < m_finish.m_line)
643 /* This is a multiline range; the point
644 is within it (corresponds to line 03 in example B
645 from column 14 onwards) */
646 return true;
647 else
648 {
649 /* This is a single-line range. */
650 gcc_assert (row == m_finish.m_line);
651 return column <= m_finish.m_columns[col_unit];
652 }
653 }
654
655 /* The point is in a line beyond that containing the
656 start of the range: lines 03 onwards in example A,
657 and lines 04 onwards in example B. */
658 gcc_assert (row > m_start.m_line);
659
660 if (row > m_finish.m_line)
661 /* The point is beyond the final line of the range
662 (lines 03 onwards in example A, and lines 06 onwards
663 in example B). */
664 return false;
665
666 if (row < m_finish.m_line)
667 {
668 /* The point is in a line that's fully within a multiline
669 range (e.g. line 04 in example B). */
670 gcc_assert (m_start.m_line < m_finish.m_line);
671 return true;
672 }
673
674 gcc_assert (row == m_finish.m_line);
675
676 return column <= m_finish.m_columns[col_unit];
677}
678
679/* Does this layout_range contain any part of line ROW? */
680
681bool
682layout_range::intersects_line_p (linenum_type row) const
683{
684 gcc_assert (m_start.m_line <= m_finish.m_line);
685 if (row < m_start.m_line)
686 return false;
687 if (row > m_finish.m_line)
688 return false;
689 return true;
690}
691
692#if CHECKING_P
693
694/* Default for when we don't care what the tab expansion is set to. */
695static const int def_tabstop = 8;
696
697static cpp_char_column_policy def_policy ()
698{
699 return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
700}
701
702/* Create some expanded locations for testing layout_range. The filename
703 member of the explocs is set to the empty string. This member will only be
704 inspected by the calls to location_compute_display_column() made from the
705 layout_point constructors. That function will check for an empty filename
706 argument and not attempt to open it, rather treating the non-existent data
707 as if the display width were the same as the byte count. Tests exercising a
708 real difference between byte count and display width are performed later,
709 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
710
711static layout_range
712make_range (file_cache &fc,
713 int start_line, int start_col, int end_line, int end_col)
714{
715 const expanded_location start_exploc
716 = {.file: "", .line: start_line, .column: start_col, NULL, .sysp: false};
717 const expanded_location finish_exploc
718 = {.file: "", .line: end_line, .column: end_col, NULL, .sysp: false};
719 return layout_range (exploc_with_display_col (fc,
720 start_exploc, def_policy (),
721 LOCATION_ASPECT_START),
722 exploc_with_display_col (fc,
723 finish_exploc, def_policy (),
724 LOCATION_ASPECT_FINISH),
725 SHOW_RANGE_WITHOUT_CARET,
726 exploc_with_display_col (fc,
727 start_exploc, def_policy (),
728 LOCATION_ASPECT_CARET),
729 0, NULL);
730}
731
732/* Selftests for layout_range::contains_point and
733 layout_range::intersects_line_p. */
734
735/* Selftest for layout_range, where the layout_range
736 is a range with start==end i.e. a single point. */
737
738static void
739test_layout_range_for_single_point ()
740{
741 file_cache fc;
742 layout_range point = make_range (fc, start_line: 7, start_col: 10, end_line: 7, end_col: 10);
743
744 /* Tests for layout_range::contains_point. */
745
746 for (int i = 0; i != CU_NUM_UNITS; ++i)
747 {
748 const enum column_unit col_unit = (enum column_unit) i;
749
750 /* Before the line. */
751 ASSERT_FALSE (point.contains_point (6, 1, col_unit));
752
753 /* On the line, but before start. */
754 ASSERT_FALSE (point.contains_point (7, 9, col_unit));
755
756 /* At the point. */
757 ASSERT_TRUE (point.contains_point (7, 10, col_unit));
758
759 /* On the line, after the point. */
760 ASSERT_FALSE (point.contains_point (7, 11, col_unit));
761
762 /* After the line. */
763 ASSERT_FALSE (point.contains_point (8, 1, col_unit));
764 }
765
766 /* Tests for layout_range::intersects_line_p. */
767 ASSERT_FALSE (point.intersects_line_p (6));
768 ASSERT_TRUE (point.intersects_line_p (7));
769 ASSERT_FALSE (point.intersects_line_p (8));
770}
771
772/* Selftest for layout_range, where the layout_range
773 is the single-line range shown as "Example A" above. */
774
775static void
776test_layout_range_for_single_line ()
777{
778 file_cache fc;
779 layout_range example_a = make_range (fc, start_line: 2, start_col: 22, end_line: 2, end_col: 38);
780
781 /* Tests for layout_range::contains_point. */
782
783 for (int i = 0; i != CU_NUM_UNITS; ++i)
784 {
785 const enum column_unit col_unit = (enum column_unit) i;
786
787 /* Before the line. */
788 ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
789
790 /* On the line, but before start. */
791 ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
792
793 /* On the line, at the start. */
794 ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
795
796 /* On the line, within the range. */
797 ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
798
799 /* On the line, at the end. */
800 ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
801
802 /* On the line, after the end. */
803 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
804
805 /* After the line. */
806 ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
807 }
808
809 /* Tests for layout_range::intersects_line_p. */
810 ASSERT_FALSE (example_a.intersects_line_p (1));
811 ASSERT_TRUE (example_a.intersects_line_p (2));
812 ASSERT_FALSE (example_a.intersects_line_p (3));
813}
814
815/* Selftest for layout_range, where the layout_range
816 is the multi-line range shown as "Example B" above. */
817
818static void
819test_layout_range_for_multiple_lines ()
820{
821 file_cache fc;
822 layout_range example_b = make_range (fc, start_line: 3, start_col: 14, end_line: 5, end_col: 8);
823
824 /* Tests for layout_range::contains_point. */
825
826 for (int i = 0; i != CU_NUM_UNITS; ++i)
827 {
828 const enum column_unit col_unit = (enum column_unit) i;
829
830 /* Before first line. */
831 ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
832
833 /* On the first line, but before start. */
834 ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
835
836 /* At the start. */
837 ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
838
839 /* On the first line, within the range. */
840 ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
841
842 /* On an interior line.
843 The column number should not matter; try various boundary
844 values. */
845 ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
846 ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
847 ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
848 ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
849 ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
850 ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
851 ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
852
853 /* On the final line, before the end. */
854 ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
855
856 /* On the final line, at the end. */
857 ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
858
859 /* On the final line, after the end. */
860 ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
861
862 /* After the line. */
863 ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
864 }
865
866 /* Tests for layout_range::intersects_line_p. */
867 ASSERT_FALSE (example_b.intersects_line_p (2));
868 ASSERT_TRUE (example_b.intersects_line_p (3));
869 ASSERT_TRUE (example_b.intersects_line_p (4));
870 ASSERT_TRUE (example_b.intersects_line_p (5));
871 ASSERT_FALSE (example_b.intersects_line_p (6));
872}
873
874#endif /* #if CHECKING_P */
875
876/* Given a source line LINE of length LINE_BYTES bytes, determine the length
877 (still in bytes, not display cols) without any trailing whitespace. */
878
879static int
880get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
881{
882 int result = line_bytes;
883 while (result > 0)
884 {
885 char ch = line[result - 1];
886 if (ch == ' ' || ch == '\t' || ch == '\r')
887 result--;
888 else
889 break;
890 }
891 gcc_assert (result >= 0);
892 gcc_assert (result <= line_bytes);
893 gcc_assert (result == 0 ||
894 (line[result - 1] != ' '
895 && line[result -1] != '\t'
896 && line[result -1] != '\r'));
897 return result;
898}
899
900#if CHECKING_P
901
902/* A helper function for testing get_line_bytes_without_trailing_whitespace. */
903
904static void
905assert_eq (const char *line, int expected_bytes)
906{
907 int actual_value
908 = get_line_bytes_without_trailing_whitespace (line, line_bytes: strlen (s: line));
909 ASSERT_EQ (actual_value, expected_bytes);
910}
911
912/* Verify that get_line_bytes_without_trailing_whitespace is sane for
913 various inputs. It is not required to handle newlines. */
914
915static void
916test_get_line_bytes_without_trailing_whitespace ()
917{
918 assert_eq (line: "", expected_bytes: 0);
919 assert_eq (line: " ", expected_bytes: 0);
920 assert_eq (line: "\t", expected_bytes: 0);
921 assert_eq (line: "\r", expected_bytes: 0);
922 assert_eq (line: "hello world", expected_bytes: 11);
923 assert_eq (line: "hello world ", expected_bytes: 11);
924 assert_eq (line: "hello world \t\t ", expected_bytes: 11);
925 assert_eq (line: "hello world\r", expected_bytes: 11);
926}
927
928#endif /* #if CHECKING_P */
929
930/* Helper function for layout's ctor, for sanitizing locations relative
931 to the primary location within a diagnostic.
932
933 Compare LOC_A and LOC_B to see if it makes sense to print underlines
934 connecting their expanded locations. Doing so is only guaranteed to
935 make sense if the locations share the same macro expansion "history"
936 i.e. they can be traced through the same macro expansions, eventually
937 reaching an ordinary map.
938
939 This may be too strong a condition, but it effectively sanitizes
940 PR c++/70105, which has an example of printing an expression where the
941 final location of the expression is in a different macro, which
942 erroneously was leading to hundreds of lines of irrelevant source
943 being printed. */
944
945bool
946layout::compatible_locations_p (location_t loc_a, location_t loc_b) const
947{
948 if (IS_ADHOC_LOC (loc: loc_a))
949 loc_a = get_location_from_adhoc_loc (m_line_table, loc_a);
950 if (IS_ADHOC_LOC (loc: loc_b))
951 loc_b = get_location_from_adhoc_loc (m_line_table, loc_b);
952
953 /* If either location is one of the special locations outside of a
954 linemap, they are only compatible if they are equal. */
955 if (loc_a < RESERVED_LOCATION_COUNT
956 || loc_b < RESERVED_LOCATION_COUNT)
957 return loc_a == loc_b;
958
959 const line_map *map_a = linemap_lookup (m_line_table, loc_a);
960 linemap_assert (map_a);
961
962 const line_map *map_b = linemap_lookup (m_line_table, loc_b);
963 linemap_assert (map_b);
964
965 /* Are they within the same map? */
966 if (map_a == map_b)
967 {
968 /* Are both within the same macro expansion? */
969 if (linemap_macro_expansion_map_p (map_a))
970 {
971 /* If so, then they're only compatible if either both are
972 from the macro definition, or both from the macro arguments. */
973 bool loc_a_from_defn
974 = linemap_location_from_macro_definition_p (m_line_table, loc_a);
975 bool loc_b_from_defn
976 = linemap_location_from_macro_definition_p (m_line_table, loc_b);
977 if (loc_a_from_defn != loc_b_from_defn)
978 return false;
979
980 /* Expand each location towards the spelling location, and
981 recurse. */
982 const line_map_macro *macro_map = linemap_check_macro (map: map_a);
983 location_t loc_a_toward_spelling
984 = linemap_macro_map_loc_unwind_toward_spelling (set: m_line_table,
985 macro_map,
986 location: loc_a);
987 location_t loc_b_toward_spelling
988 = linemap_macro_map_loc_unwind_toward_spelling (set: m_line_table,
989 macro_map,
990 location: loc_b);
991 return compatible_locations_p (loc_a: loc_a_toward_spelling,
992 loc_b: loc_b_toward_spelling);
993 }
994
995 /* Otherwise they are within the same ordinary map. */
996 return true;
997 }
998 else
999 {
1000 /* Within different maps. */
1001
1002 /* If either is within a macro expansion, they are incompatible. */
1003 if (linemap_macro_expansion_map_p (map_a)
1004 || linemap_macro_expansion_map_p (map_b))
1005 return false;
1006
1007 /* Within two different ordinary maps; they are compatible iff they
1008 are in the same file. */
1009 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map: map_a);
1010 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map: map_b);
1011 return ord_map_a->to_file == ord_map_b->to_file;
1012 }
1013}
1014
1015/* Comparator for sorting fix-it hints. */
1016
1017static int
1018fixit_cmp (const void *p_a, const void *p_b)
1019{
1020 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1021 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1022 return hint_a->get_start_loc () - hint_b->get_start_loc ();
1023}
1024
1025/* Callbacks for use when not escaping the source. */
1026
1027/* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1028
1029/* Callback for char_display_policy::m_print_cb for printing source chars
1030 when not escaping the source. */
1031
1032static void
1033default_print_decoded_ch (pretty_printer *pp,
1034 const cpp_decoded_char &decoded_ch)
1035{
1036 for (const char *ptr = decoded_ch.m_start_byte;
1037 ptr != decoded_ch.m_next_byte; ptr++)
1038 {
1039 if (*ptr == '\0' || *ptr == '\r')
1040 {
1041 pp_space (pp);
1042 continue;
1043 }
1044
1045 pp_character (pp, *ptr);
1046 }
1047}
1048
1049/* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1050
1051static const int width_per_escaped_byte = 4;
1052
1053/* Callback for char_column_policy::m_width_cb for determining the
1054 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1055
1056static int
1057escape_as_bytes_width (cppchar_t ch)
1058{
1059 if (ch < 0x80 && ISPRINT (ch))
1060 return cpp_wcwidth (c: ch);
1061 else
1062 {
1063 if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1064 if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1065 if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1066 return 4 * width_per_escaped_byte;
1067 }
1068}
1069
1070/* Callback for char_display_policy::m_print_cb for printing source chars
1071 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1072
1073static void
1074escape_as_bytes_print (pretty_printer *pp,
1075 const cpp_decoded_char &decoded_ch)
1076{
1077 if (!decoded_ch.m_valid_ch)
1078 {
1079 for (const char *iter = decoded_ch.m_start_byte;
1080 iter != decoded_ch.m_next_byte; ++iter)
1081 {
1082 char buf[16];
1083 sprintf (s: buf, format: "<%02x>", (unsigned char)*iter);
1084 pp_string (pp, buf);
1085 }
1086 return;
1087 }
1088
1089 cppchar_t ch = decoded_ch.m_ch;
1090 if (ch < 0x80 && ISPRINT (ch))
1091 pp_character (pp, ch);
1092 else
1093 {
1094 for (const char *iter = decoded_ch.m_start_byte;
1095 iter < decoded_ch.m_next_byte; ++iter)
1096 {
1097 char buf[16];
1098 sprintf (s: buf, format: "<%02x>", (unsigned char)*iter);
1099 pp_string (pp, buf);
1100 }
1101 }
1102}
1103
1104/* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1105
1106/* Callback for char_column_policy::m_width_cb for determining the
1107 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1108
1109static int
1110escape_as_unicode_width (cppchar_t ch)
1111{
1112 if (ch < 0x80 && ISPRINT (ch))
1113 return cpp_wcwidth (c: ch);
1114 else
1115 {
1116 // Width of "<U+%04x>"
1117 if (ch > 0xfffff)
1118 return 10;
1119 else if (ch > 0xffff)
1120 return 9;
1121 else
1122 return 8;
1123 }
1124}
1125
1126/* Callback for char_display_policy::m_print_cb for printing source chars
1127 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1128
1129static void
1130escape_as_unicode_print (pretty_printer *pp,
1131 const cpp_decoded_char &decoded_ch)
1132{
1133 if (!decoded_ch.m_valid_ch)
1134 {
1135 escape_as_bytes_print (pp, decoded_ch);
1136 return;
1137 }
1138
1139 cppchar_t ch = decoded_ch.m_ch;
1140 if (ch < 0x80 && ISPRINT (ch))
1141 pp_character (pp, ch);
1142 else
1143 {
1144 char buf[16];
1145 sprintf (s: buf, format: "<U+%04X>", ch);
1146 pp_string (pp, buf);
1147 }
1148}
1149
1150/* Populate a char_display_policy based on DC and RICHLOC. */
1151
1152static char_display_policy
1153make_policy (const diagnostic_context &dc,
1154 const rich_location &richloc)
1155{
1156 /* The default is to not escape non-ASCII bytes. */
1157 char_display_policy result
1158 (dc.m_tabstop, cpp_wcwidth, default_print_decoded_ch);
1159
1160 /* If the diagnostic suggests escaping non-ASCII bytes, then
1161 use policy from user-supplied options. */
1162 if (richloc.escape_on_output_p ())
1163 {
1164 result.m_undecoded_byte_width = width_per_escaped_byte;
1165 switch (dc.get_escape_format ())
1166 {
1167 default:
1168 gcc_unreachable ();
1169 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1170 result.m_width_cb = escape_as_unicode_width;
1171 result.m_print_cb = escape_as_unicode_print;
1172 break;
1173 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1174 result.m_width_cb = escape_as_bytes_width;
1175 result.m_print_cb = escape_as_bytes_print;
1176 break;
1177 }
1178 }
1179
1180 return result;
1181}
1182
1183/* Implementation of class layout. */
1184
1185/* Constructor for class layout.
1186
1187 Filter the ranges from the rich_location to those that we can
1188 sanely print, populating m_layout_ranges and m_fixit_hints.
1189 Determine the range of lines that we will print, splitting them
1190 up into an ordered list of disjoint spans of contiguous line numbers.
1191 Determine m_x_offset_display, to ensure that the primary caret
1192 will fit within the max_width provided by the diagnostic_context. */
1193
1194layout::layout (const diagnostic_context &context,
1195 const rich_location &richloc,
1196 diagnostic_t diagnostic_kind,
1197 pretty_printer *pp)
1198: m_options (context.m_source_printing),
1199 m_line_table (richloc.get_line_table ()),
1200 m_file_cache (context.get_file_cache ()),
1201 m_pp (pp ? pp : context.printer),
1202 m_policy (make_policy (dc: context, richloc)),
1203 m_primary_loc (richloc.get_range (idx: 0)->m_loc),
1204 m_exploc (m_file_cache,
1205 richloc.get_expanded_location (idx: 0), m_policy,
1206 LOCATION_ASPECT_CARET),
1207 m_colorizer (m_pp, diagnostic_kind),
1208 m_diagnostic_path_p (diagnostic_kind == DK_DIAGNOSTIC_PATH),
1209 m_layout_ranges (richloc.get_num_locations ()),
1210 m_fixit_hints (richloc.get_num_fixit_hints ()),
1211 m_line_spans (1 + richloc.get_num_locations ()),
1212 m_linenum_width (0),
1213 m_x_offset_display (0),
1214 m_escape_on_output (richloc.escape_on_output_p ())
1215{
1216 for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
1217 {
1218 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1219 Ignore any ranges that are awkward to handle. */
1220 const location_range *loc_range = richloc.get_range (idx);
1221 maybe_add_location_range (loc_range, original_idx: idx, restrict_to_current_line_spans: false);
1222 }
1223
1224 /* Populate m_fixit_hints, filtering to only those that are in the
1225 same file. */
1226 for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1227 {
1228 const fixit_hint *hint = richloc.get_fixit_hint (idx: i);
1229 if (validate_fixit_hint_p (hint))
1230 m_fixit_hints.safe_push (obj: hint);
1231 }
1232
1233 /* Sort m_fixit_hints. */
1234 m_fixit_hints.qsort (fixit_cmp);
1235
1236 /* Populate the indicated members. */
1237 calculate_line_spans ();
1238 calculate_linenum_width ();
1239 calculate_x_offset_display ();
1240
1241 if (m_options.show_ruler_p)
1242 show_ruler (max_column: m_x_offset_display + m_options.max_width);
1243}
1244
1245
1246/* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1247 those that we can sanely print.
1248
1249 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1250 (for use as extrinsic state by label ranges FIXME).
1251
1252 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1253 filtered against this layout instance's current line spans: it
1254 will only be added if the location is fully within the lines
1255 already specified by other locations.
1256
1257 Return true iff LOC_RANGE was added. */
1258
1259bool
1260layout::maybe_add_location_range (const location_range *loc_range,
1261 unsigned original_idx,
1262 bool restrict_to_current_line_spans)
1263{
1264 gcc_assert (loc_range);
1265
1266 /* Split the "range" into caret and range information. */
1267 source_range src_range = get_range_from_loc (set: m_line_table, loc: loc_range->m_loc);
1268
1269 /* Expand the various locations. */
1270 expanded_location start
1271 = linemap_client_expand_location_to_spelling_point
1272 (m_line_table, src_range.m_start, LOCATION_ASPECT_START);
1273 expanded_location finish
1274 = linemap_client_expand_location_to_spelling_point
1275 (m_line_table, src_range.m_finish, LOCATION_ASPECT_FINISH);
1276 expanded_location caret
1277 = linemap_client_expand_location_to_spelling_point
1278 (m_line_table, loc_range->m_loc, LOCATION_ASPECT_CARET);
1279
1280 /* If any part of the range isn't in the same file as the primary
1281 location of this diagnostic, ignore the range. */
1282 if (start.file != m_exploc.file)
1283 return false;
1284 if (finish.file != m_exploc.file)
1285 return false;
1286 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1287 if (caret.file != m_exploc.file)
1288 return false;
1289
1290 /* Sanitize the caret location for non-primary ranges. */
1291 if (m_layout_ranges.length () > 0)
1292 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1293 if (!compatible_locations_p (loc_a: loc_range->m_loc, loc_b: m_primary_loc))
1294 /* Discard any non-primary ranges that can't be printed
1295 sanely relative to the primary location. */
1296 return false;
1297
1298 /* If there's no column information, then don't try to print
1299 annotation lines for this range. */
1300 enum range_display_kind range_display_kind
1301 = loc_range->m_range_display_kind;
1302 if (start.column == 0
1303 || finish.column == 0
1304 || caret.column == 0)
1305 range_display_kind = SHOW_LINES_WITHOUT_RANGE;
1306
1307 /* Everything is now known to be in the correct source file,
1308 but it may require further sanitization. */
1309 layout_range ri (exploc_with_display_col (m_file_cache,
1310 start, m_policy,
1311 LOCATION_ASPECT_START),
1312 exploc_with_display_col (m_file_cache,
1313 finish, m_policy,
1314 LOCATION_ASPECT_FINISH),
1315 range_display_kind,
1316 exploc_with_display_col (m_file_cache,
1317 caret, m_policy,
1318 LOCATION_ASPECT_CARET),
1319 original_idx, loc_range->m_label);
1320
1321 /* If we have a range that finishes before it starts (perhaps
1322 from something built via macro expansion), printing the
1323 range is likely to be nonsensical. Also, attempting to do so
1324 breaks assumptions within the printing code (PR c/68473).
1325 Similarly, don't attempt to print ranges if one or both ends
1326 of the range aren't sane to print relative to the
1327 primary location (PR c++/70105). */
1328 if (start.line > finish.line
1329 || !compatible_locations_p (loc_a: src_range.m_start, loc_b: m_primary_loc)
1330 || !compatible_locations_p (loc_a: src_range.m_finish, loc_b: m_primary_loc))
1331 {
1332 /* Is this the primary location? */
1333 if (m_layout_ranges.length () == 0)
1334 {
1335 /* We want to print the caret for the primary location, but
1336 we must sanitize away m_start and m_finish. */
1337 ri.m_start = ri.m_caret;
1338 ri.m_finish = ri.m_caret;
1339 }
1340 else
1341 /* This is a non-primary range; ignore it. */
1342 return false;
1343 }
1344
1345 /* Potentially filter to just the lines already specified by other
1346 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1347 The layout ctor doesn't use it, and can't because m_line_spans
1348 hasn't been set up at that point. */
1349 if (restrict_to_current_line_spans)
1350 {
1351 if (!will_show_line_p (row: start.line))
1352 return false;
1353 if (!will_show_line_p (row: finish.line))
1354 return false;
1355 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1356 if (!will_show_line_p (row: caret.line))
1357 return false;
1358 }
1359
1360 /* Passed all the tests; add the range to m_layout_ranges so that
1361 it will be printed. */
1362 m_layout_ranges.safe_push (obj: ri);
1363 return true;
1364}
1365
1366/* Return true iff ROW is within one of the line spans for this layout. */
1367
1368bool
1369layout::will_show_line_p (linenum_type row) const
1370{
1371 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1372 line_span_idx++)
1373 {
1374 const line_span *line_span = get_line_span (idx: line_span_idx);
1375 if (line_span->contains_line_p (line: row))
1376 return true;
1377 }
1378 return false;
1379}
1380
1381/* Print a line showing a gap in the line numbers, for showing the boundary
1382 between two line spans. */
1383
1384void
1385layout::print_gap_in_line_numbering ()
1386{
1387 gcc_assert (m_options.show_line_numbers_p);
1388
1389 pp_emit_prefix (m_pp);
1390
1391 for (int i = 0; i < m_linenum_width + 1; i++)
1392 pp_character (m_pp, '.');
1393
1394 pp_newline (m_pp);
1395}
1396
1397/* Return true iff we should print a heading when starting the
1398 line span with the given index. */
1399
1400bool
1401layout::print_heading_for_line_span_index_p (int line_span_idx) const
1402{
1403 /* We print a heading for every change of line span, hence for every
1404 line span after the initial one. */
1405 if (line_span_idx > 0)
1406 return true;
1407
1408 /* We also do it for the initial span if the primary location of the
1409 diagnostic is in a different span. */
1410 if (m_exploc.line > (int)get_line_span (idx: 0)->m_last_line)
1411 return true;
1412
1413 return false;
1414}
1415
1416/* Get an expanded_location for the first location of interest within
1417 the given line_span.
1418 Used when printing a heading to indicate a new line span. */
1419
1420expanded_location
1421layout::get_expanded_location (const line_span *line_span) const
1422{
1423 /* Whenever possible, use the caret location. */
1424 if (line_span->contains_line_p (line: m_exploc.line))
1425 return m_exploc;
1426
1427 /* Otherwise, use the start of the first range that's present
1428 within the line_span. */
1429 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1430 {
1431 const layout_range *lr = &m_layout_ranges[i];
1432 if (line_span->contains_line_p (line: lr->m_start.m_line))
1433 {
1434 expanded_location exploc = m_exploc;
1435 exploc.line = lr->m_start.m_line;
1436 exploc.column = lr->m_start.m_columns[CU_BYTES];
1437 return exploc;
1438 }
1439 }
1440
1441 /* Otherwise, use the location of the first fixit-hint present within
1442 the line_span. */
1443 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1444 {
1445 const fixit_hint *hint = m_fixit_hints[i];
1446 location_t loc = hint->get_start_loc ();
1447 expanded_location exploc = expand_location (loc);
1448 if (line_span->contains_line_p (line: exploc.line))
1449 return exploc;
1450 }
1451
1452 /* It should not be possible to have a line span that didn't
1453 contain any of the layout_range or fixit_hint instances. */
1454 gcc_unreachable ();
1455 return m_exploc;
1456}
1457
1458/* Determine if HINT is meaningful to print within this layout. */
1459
1460bool
1461layout::validate_fixit_hint_p (const fixit_hint *hint)
1462{
1463 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1464 return false;
1465 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1466 return false;
1467
1468 return true;
1469}
1470
1471/* Determine the range of lines affected by HINT.
1472 This assumes that HINT has already been filtered by
1473 validate_fixit_hint_p, and so affects the correct source file. */
1474
1475static line_span
1476get_line_span_for_fixit_hint (const fixit_hint *hint)
1477{
1478 gcc_assert (hint);
1479
1480 int start_line = LOCATION_LINE (hint->get_start_loc ());
1481
1482 /* For line-insertion fix-it hints, add the previous line to the
1483 span, to give the user more context on the proposed change. */
1484 if (hint->ends_with_newline_p ())
1485 if (start_line > 1)
1486 start_line--;
1487
1488 return line_span (start_line,
1489 LOCATION_LINE (hint->get_next_loc ()));
1490}
1491
1492/* We want to print the pertinent source code at a diagnostic. The
1493 rich_location can contain multiple locations. This will have been
1494 filtered into m_exploc (the caret for the primary location) and
1495 m_layout_ranges, for those ranges within the same source file.
1496
1497 We will print a subset of the lines within the source file in question,
1498 as a collection of "spans" of lines.
1499
1500 This function populates m_line_spans with an ordered, disjoint list of
1501 the line spans of interest.
1502
1503 Printing a gap between line spans takes one line, so, when printing
1504 line numbers, we allow a gap of up to one line between spans when
1505 merging, since it makes more sense to print the source line rather than a
1506 "gap-in-line-numbering" line. When not printing line numbers, it's
1507 better to be more explicit about what's going on, so keeping them as
1508 separate spans is preferred.
1509
1510 For example, if the primary range is on lines 8-10, with secondary ranges
1511 covering lines 5-6 and lines 13-15:
1512
1513 004
1514 005 |RANGE 1
1515 006 |RANGE 1
1516 007
1517 008 |PRIMARY RANGE
1518 009 |PRIMARY CARET
1519 010 |PRIMARY RANGE
1520 011
1521 012
1522 013 |RANGE 2
1523 014 |RANGE 2
1524 015 |RANGE 2
1525 016
1526
1527 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1528
1529 With line numbering off (with span headers), we want three spans: lines 5-6,
1530 lines 8-10, and lines 13-15. */
1531
1532void
1533layout::calculate_line_spans ()
1534{
1535 /* This should only be called once, by the ctor. */
1536 gcc_assert (m_line_spans.length () == 0);
1537
1538 /* Populate tmp_spans with individual spans, for each of
1539 m_exploc, and for m_layout_ranges. */
1540 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1541 tmp_spans.safe_push (obj: line_span (m_exploc.line, m_exploc.line));
1542 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1543 {
1544 const layout_range *lr = &m_layout_ranges[i];
1545 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1546 tmp_spans.safe_push (obj: line_span (lr->m_start.m_line,
1547 lr->m_finish.m_line));
1548 }
1549
1550 /* Also add spans for any fix-it hints, in case they cover other lines. */
1551 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1552 {
1553 const fixit_hint *hint = m_fixit_hints[i];
1554 gcc_assert (hint);
1555 tmp_spans.safe_push (obj: get_line_span_for_fixit_hint (hint));
1556 }
1557
1558 /* Sort them. */
1559 tmp_spans.qsort(line_span::comparator);
1560
1561 /* Now iterate through tmp_spans, copying into m_line_spans, and
1562 combining where possible. */
1563 gcc_assert (tmp_spans.length () > 0);
1564 m_line_spans.safe_push (obj: tmp_spans[0]);
1565 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1566 {
1567 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1568 const line_span *next = &tmp_spans[i];
1569 gcc_assert (next->m_first_line >= current->m_first_line);
1570 const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
1571 if ((linenum_arith_t)next->m_first_line
1572 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1573 {
1574 /* We can merge them. */
1575 if (next->m_last_line > current->m_last_line)
1576 current->m_last_line = next->m_last_line;
1577 }
1578 else
1579 {
1580 /* No merger possible. */
1581 m_line_spans.safe_push (obj: *next);
1582 }
1583 }
1584
1585 /* Verify the result, in m_line_spans. */
1586 gcc_assert (m_line_spans.length () > 0);
1587 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1588 {
1589 const line_span *prev = &m_line_spans[i - 1];
1590 const line_span *next = &m_line_spans[i];
1591 /* The individual spans must be sane. */
1592 gcc_assert (prev->m_first_line <= prev->m_last_line);
1593 gcc_assert (next->m_first_line <= next->m_last_line);
1594 /* The spans must be ordered. */
1595 gcc_assert (prev->m_first_line < next->m_first_line);
1596 /* There must be a gap of at least one line between separate spans. */
1597 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1598 }
1599}
1600
1601/* Determine how many display columns need to be reserved for line numbers,
1602 based on the largest line number that will be needed, and populate
1603 m_linenum_width. */
1604
1605void
1606layout::calculate_linenum_width ()
1607{
1608 gcc_assert (m_line_spans.length () > 0);
1609 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1610 int highest_line = last_span->m_last_line;
1611 if (highest_line < 0)
1612 highest_line = 0;
1613 m_linenum_width = num_digits (highest_line);
1614 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1615 if (m_line_spans.length () > 1)
1616 m_linenum_width = MAX (m_linenum_width, 3);
1617 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1618 after the line number. */
1619 m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
1620}
1621
1622/* Calculate m_x_offset_display, which improves readability in case the source
1623 line of interest is longer than the user's display. All lines output will be
1624 shifted to the left (so that their beginning is no longer displayed) by
1625 m_x_offset_display display columns, so that the caret is in a reasonable
1626 location. */
1627
1628void
1629layout::calculate_x_offset_display ()
1630{
1631 m_x_offset_display = 0;
1632
1633 const int max_width = m_options.max_width;
1634 if (!max_width)
1635 {
1636 /* Nothing to do, the width is not capped. */
1637 return;
1638 }
1639
1640 const char_span line = m_file_cache.get_source_line (file_path: m_exploc.file,
1641 line: m_exploc.line);
1642 if (!line)
1643 {
1644 /* Nothing to do, we couldn't find the source line. */
1645 return;
1646 }
1647 int caret_display_column = m_exploc.m_display_col;
1648 const int line_bytes
1649 = get_line_bytes_without_trailing_whitespace (line: line.get_buffer (),
1650 line_bytes: line.length ());
1651 int eol_display_column
1652 = cpp_display_width (data: line.get_buffer (), data_length: line_bytes, policy: m_policy);
1653 if (caret_display_column > eol_display_column
1654 || !caret_display_column)
1655 {
1656 /* This does not make sense, so don't try to do anything in this case. */
1657 return;
1658 }
1659
1660 /* Adjust caret and eol positions to include the left margin. If we are
1661 outputting line numbers, then the left margin is equal to m_linenum_width
1662 plus three for the " | " which follows it. Otherwise the left margin width
1663 is equal to 1, because layout::print_source_line() will prefix each line
1664 with a space. */
1665 const int source_display_cols = eol_display_column;
1666 int left_margin_size = 1;
1667 if (m_options.show_line_numbers_p)
1668 left_margin_size = m_linenum_width + 3;
1669 caret_display_column += left_margin_size;
1670 eol_display_column += left_margin_size;
1671
1672 if (eol_display_column <= max_width)
1673 {
1674 /* Nothing to do, everything fits in the display. */
1675 return;
1676 }
1677
1678 /* The line is too long for the display. Calculate an offset such that the
1679 caret is not too close to the right edge of the screen. It will be
1680 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1681 than that to the end of the source line anyway. */
1682 int right_margin_size = CARET_LINE_MARGIN;
1683 right_margin_size = MIN (eol_display_column - caret_display_column,
1684 right_margin_size);
1685 if (right_margin_size + left_margin_size >= max_width)
1686 {
1687 /* The max_width is very small, so anything we try to do will not be very
1688 effective; just punt in this case and output with no offset. */
1689 return;
1690 }
1691 const int max_caret_display_column = max_width - right_margin_size;
1692 if (caret_display_column > max_caret_display_column)
1693 {
1694 m_x_offset_display = caret_display_column - max_caret_display_column;
1695 /* Make sure we don't offset the line into oblivion. */
1696 static const int min_cols_visible = 2;
1697 if (source_display_cols - m_x_offset_display < min_cols_visible)
1698 m_x_offset_display = 0;
1699 }
1700}
1701
1702/* Print line ROW of source code, potentially colorized at any ranges, and
1703 return the line bounds. LINE is the source line (not necessarily
1704 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1705 colorization and tab expansion, this function tracks the line position in
1706 both byte and display column units. */
1707
1708line_bounds
1709layout::print_source_line (linenum_type row, const char *line, int line_bytes)
1710{
1711 m_colorizer.set_normal_text ();
1712
1713 pp_emit_prefix (m_pp);
1714 if (m_options.show_line_numbers_p)
1715 {
1716 int width = num_digits (row);
1717 for (int i = 0; i < m_linenum_width - width; i++)
1718 pp_space (m_pp);
1719 pp_printf (m_pp, "%i | ", row);
1720 }
1721 else
1722 pp_space (m_pp);
1723
1724 /* We will stop printing the source line at any trailing whitespace. */
1725 line_bytes = get_line_bytes_without_trailing_whitespace (line,
1726 line_bytes);
1727
1728 /* This object helps to keep track of which display column we are at, which is
1729 necessary for computing the line bounds in display units, for doing
1730 tab expansion, and for implementing m_x_offset_display. */
1731 cpp_display_width_computation dw (line, line_bytes, m_policy);
1732
1733 /* Skip the first m_x_offset_display display columns. In case the leading
1734 portion that will be skipped ends with a character with wcwidth > 1, then
1735 it is possible we skipped too much, so account for that by padding with
1736 spaces. Note that this does the right thing too in case a tab was the last
1737 character to be skipped over; the tab is effectively replaced by the
1738 correct number of trailing spaces needed to offset by the desired number of
1739 display columns. */
1740 for (int skipped_display_cols = dw.advance_display_cols (n: m_x_offset_display);
1741 skipped_display_cols > m_x_offset_display; --skipped_display_cols)
1742 pp_space (m_pp);
1743
1744 /* Print the line and compute the line_bounds. */
1745 line_bounds lbounds;
1746 while (!dw.done ())
1747 {
1748 /* Assuming colorization is enabled for the caret and underline
1749 characters, we may also colorize the associated characters
1750 within the source line.
1751
1752 For frontends that generate range information, we color the
1753 associated characters in the source line the same as the
1754 carets and underlines in the annotation line, to make it easier
1755 for the reader to see the pertinent code.
1756
1757 For frontends that only generate carets, we don't colorize the
1758 characters above them, since this would look strange (e.g.
1759 colorizing just the first character in a token). */
1760 if (m_options.colorize_source_p)
1761 {
1762 bool in_range_p;
1763 point_state state;
1764 const int start_byte_col = dw.bytes_processed () + 1;
1765 in_range_p = get_state_at_point (row, column: start_byte_col,
1766 first_non_ws: 0, INT_MAX,
1767 col_unit: CU_BYTES,
1768 out_state: &state);
1769 if (in_range_p)
1770 m_colorizer.set_range (state.range_idx);
1771 else
1772 m_colorizer.set_normal_text ();
1773 }
1774
1775 /* Get the display width of the next character to be output, expanding
1776 tabs and replacing some control bytes with spaces as necessary. */
1777 const char *c = dw.next_byte ();
1778 const int start_disp_col = dw.display_cols_processed () + 1;
1779 cpp_decoded_char cp;
1780 const int this_display_width = dw.process_next_codepoint (out: &cp);
1781 if (*c == '\t')
1782 {
1783 /* The returned display width is the number of spaces into which the
1784 tab should be expanded. */
1785 for (int i = 0; i != this_display_width; ++i)
1786 pp_space (m_pp);
1787 continue;
1788 }
1789
1790 /* We have a (possibly multibyte) character to output; update the line
1791 bounds if it is not whitespace. */
1792 if (*c != ' ')
1793 {
1794 lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1795 if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1796 lbounds.m_first_non_ws_disp_col = start_disp_col;
1797 }
1798
1799 /* Output the character. */
1800 m_policy.m_print_cb (m_pp, cp);
1801 c = dw.next_byte ();
1802 }
1803 print_newline ();
1804 return lbounds;
1805}
1806
1807/* Determine if we should print an annotation line for ROW.
1808 i.e. if any of m_layout_ranges contains ROW. */
1809
1810bool
1811layout::should_print_annotation_line_p (linenum_type row) const
1812{
1813 layout_range *range;
1814 int i;
1815 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1816 {
1817 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1818 return false;
1819 if (range->intersects_line_p (row))
1820 return true;
1821 }
1822 return false;
1823}
1824
1825/* Begin an annotation line. If m_show_line_numbers_p, print the left
1826 margin, which is empty for annotation lines. Otherwise, do nothing. */
1827
1828void
1829layout::start_annotation_line (char margin_char) const
1830{
1831 pp_emit_prefix (m_pp);
1832 if (m_options.show_line_numbers_p)
1833 {
1834 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1835 of it, right-aligned, padded with spaces. */
1836 int i;
1837 for (i = 0; i < m_linenum_width - 3; i++)
1838 pp_space (m_pp);
1839 for (; i < m_linenum_width; i++)
1840 pp_character (m_pp, margin_char);
1841 pp_string (m_pp, " |");
1842 }
1843}
1844
1845/* Print a line consisting of the caret/underlines for the given
1846 source line. */
1847
1848void
1849layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1850{
1851 int x_bound = get_x_bound_for_row (row, caret_column: m_exploc.m_display_col,
1852 last_non_ws: lbounds.m_last_non_ws_disp_col);
1853
1854 start_annotation_line ();
1855 pp_space (m_pp);
1856
1857 for (int column = 1 + m_x_offset_display; column < x_bound; column++)
1858 {
1859 bool in_range_p;
1860 point_state state;
1861 in_range_p = get_state_at_point (row, column,
1862 first_non_ws: lbounds.m_first_non_ws_disp_col,
1863 last_non_ws: lbounds.m_last_non_ws_disp_col,
1864 col_unit: CU_DISPLAY_COLS,
1865 out_state: &state);
1866 if (in_range_p)
1867 {
1868 /* Within a range. Draw either the caret or an underline. */
1869 m_colorizer.set_range (state.range_idx);
1870 if (state.draw_caret_p)
1871 {
1872 /* Draw the caret. */
1873 char caret_char;
1874 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1875 caret_char = m_options.caret_chars[state.range_idx];
1876 else
1877 caret_char = '^';
1878 pp_character (m_pp, caret_char);
1879 }
1880 else
1881 pp_character (m_pp, '~');
1882 }
1883 else
1884 {
1885 /* Not in a range. */
1886 m_colorizer.set_normal_text ();
1887 pp_character (m_pp, ' ');
1888 }
1889 }
1890 print_newline ();
1891}
1892
1893/* A version of label_text that can live inside a vec.
1894 Requires manual cleanup via maybe_free. */
1895
1896struct pod_label_text
1897{
1898 pod_label_text ()
1899 : m_buffer (NULL), m_caller_owned (false)
1900 {}
1901
1902 pod_label_text (label_text &&other)
1903 : m_buffer (const_cast<char*> (other.get ())),
1904 m_caller_owned (other.is_owner ())
1905 {
1906 other.release ();
1907 }
1908
1909 void maybe_free ()
1910 {
1911 if (m_caller_owned)
1912 free (ptr: m_buffer);
1913 }
1914
1915 char *m_buffer;
1916 bool m_caller_owned;
1917};
1918
1919/* Implementation detail of layout::print_any_labels.
1920
1921 A label within the given row of source. */
1922
1923class line_label
1924{
1925public:
1926 line_label (const cpp_char_column_policy &policy,
1927 int state_idx, int column,
1928 label_text text)
1929 : m_state_idx (state_idx), m_column (column),
1930 m_text (std::move (text)), m_label_line (0), m_has_vbar (true)
1931 {
1932 const int bytes = strlen (s: m_text.m_buffer);
1933 m_display_width = cpp_display_width (data: m_text.m_buffer, data_length: bytes, policy);
1934 }
1935
1936 /* Sorting is primarily by column, then by state index. */
1937 static int comparator (const void *p1, const void *p2)
1938 {
1939 const line_label *ll1 = (const line_label *)p1;
1940 const line_label *ll2 = (const line_label *)p2;
1941 int column_cmp = compare (lhs: ll1->m_column, rhs: ll2->m_column);
1942 if (column_cmp)
1943 return column_cmp;
1944 /* Order by reverse state index, so that labels are printed
1945 in order of insertion into the rich_location when the
1946 sorted list is walked backwards. */
1947 return -compare (lhs: ll1->m_state_idx, rhs: ll2->m_state_idx);
1948 }
1949
1950 int m_state_idx;
1951 int m_column;
1952 pod_label_text m_text;
1953 size_t m_display_width;
1954 int m_label_line;
1955 bool m_has_vbar;
1956};
1957
1958/* Print any labels in this row. */
1959void
1960layout::print_any_labels (linenum_type row)
1961{
1962 int i;
1963 auto_vec<line_label> labels;
1964
1965 /* Gather the labels that are to be printed into "labels". */
1966 {
1967 layout_range *range;
1968 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1969 {
1970 /* Most ranges don't have labels, so reject this first. */
1971 if (range->m_label == NULL)
1972 continue;
1973
1974 /* The range's caret must be on this line. */
1975 if (range->m_caret.m_line != row)
1976 continue;
1977
1978 /* Reject labels that aren't fully visible due to clipping
1979 by m_x_offset_display. */
1980 const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
1981 if (disp_col <= m_x_offset_display)
1982 continue;
1983
1984 label_text text;
1985 text = range->m_label->get_text (range_idx: range->m_original_idx);
1986
1987 /* Allow for labels that return NULL from their get_text
1988 implementation (so e.g. such labels can control their own
1989 visibility). */
1990 if (text.get () == NULL)
1991 continue;
1992
1993 labels.safe_push (obj: line_label (m_policy, i, disp_col, std::move (text)));
1994 }
1995 }
1996
1997 /* Bail out if there are no labels on this row. */
1998 if (labels.length () == 0)
1999 return;
2000
2001 /* Sort them. */
2002 labels.qsort(line_label::comparator);
2003
2004 /* Figure out how many "label lines" we need, and which
2005 one each label is printed in.
2006
2007 For example, if the labels aren't too densely packed,
2008 we can fit them on the same line, giving two "label lines":
2009
2010 foo + bar
2011 ~~~ ~~~
2012 | | : label line 0
2013 l0 l1 : label line 1
2014
2015 If they would touch each other or overlap, then we need
2016 additional "label lines":
2017
2018 foo + bar
2019 ~~~ ~~~
2020 | | : label line 0
2021 | label 1 : label line 1
2022 label 0 : label line 2
2023
2024 Place the final label on label line 1, and work backwards, adding
2025 label lines as needed.
2026
2027 If multiple labels are at the same place, put them on separate
2028 label lines:
2029
2030 foo + bar
2031 ^ : label line 0
2032 | : label line 1
2033 label 0 : label line 2
2034 label 1 : label line 3. */
2035
2036 int max_label_line = 1;
2037 {
2038 int next_column = INT_MAX;
2039 line_label *label;
2040 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2041 {
2042 /* Would this label "touch" or overlap the next label? */
2043 if (label->m_column + label->m_display_width >= (size_t)next_column)
2044 {
2045 max_label_line++;
2046
2047 /* If we've already seen labels with the same column, suppress the
2048 vertical bar for subsequent ones in this backwards iteration;
2049 hence only the one with the highest label_line has m_has_vbar set. */
2050 if (label->m_column == next_column)
2051 label->m_has_vbar = false;
2052 }
2053
2054 label->m_label_line = max_label_line;
2055 next_column = label->m_column;
2056 }
2057 }
2058
2059 /* Print the "label lines". For each label within the line, print
2060 either a vertical bar ('|') for the labels that are lower down, or the
2061 labels themselves once we've reached their line. */
2062 {
2063 for (int label_line = 0; label_line <= max_label_line; label_line++)
2064 {
2065 start_annotation_line ();
2066 pp_space (m_pp);
2067 int column = 1 + m_x_offset_display;
2068 line_label *label;
2069 FOR_EACH_VEC_ELT (labels, i, label)
2070 {
2071 if (label_line > label->m_label_line)
2072 /* We've printed all the labels for this label line. */
2073 break;
2074
2075 if (label_line == label->m_label_line)
2076 {
2077 gcc_assert (column <= label->m_column);
2078 move_to_column (column: &column, dest_column: label->m_column, add_left_margin: true);
2079 /* Colorize the text, unless it's for events in a
2080 diagnostic_path. */
2081 if (!m_diagnostic_path_p)
2082 m_colorizer.set_range (label->m_state_idx);
2083 pp_string (m_pp, label->m_text.m_buffer);
2084 m_colorizer.set_normal_text ();
2085 column += label->m_display_width;
2086 }
2087 else if (label->m_has_vbar)
2088 {
2089 gcc_assert (column <= label->m_column);
2090 move_to_column (column: &column, dest_column: label->m_column, add_left_margin: true);
2091 m_colorizer.set_range (label->m_state_idx);
2092 pp_character (m_pp, '|');
2093 m_colorizer.set_normal_text ();
2094 column++;
2095 }
2096 }
2097 print_newline ();
2098 }
2099 }
2100
2101 /* Clean up. */
2102 {
2103 line_label *label;
2104 FOR_EACH_VEC_ELT (labels, i, label)
2105 label->m_text.maybe_free ();
2106 }
2107}
2108
2109/* If there are any fixit hints inserting new lines before source line ROW,
2110 print them.
2111
2112 They are printed on lines of their own, before the source line
2113 itself, with a leading '+'. */
2114
2115void
2116layout::print_leading_fixits (linenum_type row)
2117{
2118 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2119 {
2120 const fixit_hint *hint = m_fixit_hints[i];
2121
2122 if (!hint->ends_with_newline_p ())
2123 /* Not a newline fixit; print it in print_trailing_fixits. */
2124 continue;
2125
2126 gcc_assert (hint->insertion_p ());
2127
2128 if (hint->affects_line_p (set: m_line_table, file: m_exploc.file, line: row))
2129 {
2130 /* Printing the '+' with normal colorization
2131 and the inserted line with "insert" colorization
2132 helps them stand out from each other, and from
2133 the surrounding text. */
2134 m_colorizer.set_normal_text ();
2135 start_annotation_line (margin_char: '+');
2136 pp_character (m_pp, '+');
2137 m_colorizer.set_fixit_insert ();
2138 /* Print all but the trailing newline of the fix-it hint.
2139 We have to print the newline separately to avoid
2140 getting additional pp prefixes printed. */
2141 for (size_t i = 0; i < hint->get_length () - 1; i++)
2142 pp_character (m_pp, hint->get_string ()[i]);
2143 m_colorizer.set_normal_text ();
2144 pp_newline (m_pp);
2145 }
2146 }
2147}
2148
2149/* Subroutine of layout::print_trailing_fixits.
2150
2151 Determine if the annotation line printed for LINE contained
2152 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2153
2154bool
2155layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2156 int finish_column) const
2157{
2158 layout_range *range;
2159 int i;
2160 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2161 if (range->m_start.m_line == line
2162 && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2163 && range->m_finish.m_line == line
2164 && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2165 return true;
2166 return false;
2167}
2168
2169/* Classes for printing trailing fix-it hints i.e. those that
2170 don't add new lines.
2171
2172 For insertion, these can look like:
2173
2174 new_text
2175
2176 For replacement, these can look like:
2177
2178 ------------- : underline showing affected range
2179 new_text
2180
2181 For deletion, these can look like:
2182
2183 ------------- : underline showing affected range
2184
2185 This can become confusing if they overlap, and so we need
2186 to do some preprocessing to decide what to print.
2187 We use the list of fixit_hint instances affecting the line
2188 to build a list of "correction" instances, and print the
2189 latter.
2190
2191 For example, consider a set of fix-its for converting
2192 a C-style cast to a C++ const_cast.
2193
2194 Given:
2195
2196 ..000000000111111111122222222223333333333.
2197 ..123456789012345678901234567890123456789.
2198 foo *f = (foo *)ptr->field;
2199 ^~~~~
2200
2201 and the fix-it hints:
2202 - replace col 10 (the open paren) with "const_cast<"
2203 - replace col 16 (the close paren) with "> ("
2204 - insert ")" before col 27
2205
2206 then we would get odd-looking output:
2207
2208 foo *f = (foo *)ptr->field;
2209 ^~~~~
2210 -
2211 const_cast<
2212 -
2213 > ( )
2214
2215 It would be better to detect when fixit hints are going to
2216 overlap (those that require new lines), and to consolidate
2217 the printing of such fixits, giving something like:
2218
2219 foo *f = (foo *)ptr->field;
2220 ^~~~~
2221 -----------------
2222 const_cast<foo *> (ptr->field)
2223
2224 This works by detecting when the printing would overlap, and
2225 effectively injecting no-op replace hints into the gaps between
2226 such fix-its, so that the printing joins up.
2227
2228 In the above example, the overlap of:
2229 - replace col 10 (the open paren) with "const_cast<"
2230 and:
2231 - replace col 16 (the close paren) with "> ("
2232 is fixed by injecting a no-op:
2233 - replace cols 11-15 with themselves ("foo *")
2234 and consolidating these, making:
2235 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2236 i.e.:
2237 - replace cols 10-16 with "const_cast<foo *> ("
2238
2239 This overlaps with the final fix-it hint:
2240 - insert ")" before col 27
2241 and so we repeat the consolidation process, by injecting
2242 a no-op:
2243 - replace cols 17-26 with themselves ("ptr->field")
2244 giving:
2245 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2246 i.e.:
2247 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2248
2249 and is thus printed as desired. */
2250
2251/* A range of (byte or display) columns within a line. */
2252
2253class column_range
2254{
2255public:
2256 column_range (int start_, int finish_) : start (start_), finish (finish_)
2257 {
2258 gcc_assert (valid_p (start, finish));
2259 }
2260
2261 bool operator== (const column_range &other) const
2262 {
2263 return start == other.start && finish == other.finish;
2264 }
2265
2266 static bool valid_p (int start, int finish)
2267 {
2268 /* We must have either a range, or an insertion. */
2269 return (start <= finish || finish == start - 1);
2270 }
2271
2272 int start;
2273 int finish;
2274};
2275
2276/* Get the range of bytes or display columns that HINT would affect. */
2277static column_range
2278get_affected_range (file_cache &fc,
2279 const cpp_char_column_policy &policy,
2280 const fixit_hint *hint, enum column_unit col_unit)
2281{
2282 expanded_location exploc_start = expand_location (hint->get_start_loc ());
2283 expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2284 --exploc_finish.column;
2285
2286 int start_column;
2287 int finish_column;
2288 if (col_unit == CU_DISPLAY_COLS)
2289 {
2290 start_column = location_compute_display_column (fc, exploc: exploc_start, policy);
2291 if (hint->insertion_p ())
2292 finish_column = start_column - 1;
2293 else
2294 finish_column
2295 = location_compute_display_column (fc, exploc: exploc_finish, policy);
2296 }
2297 else
2298 {
2299 start_column = exploc_start.column;
2300 finish_column = exploc_finish.column;
2301 }
2302 return column_range (start_column, finish_column);
2303}
2304
2305/* Get the range of display columns that would be printed for HINT. */
2306
2307static column_range
2308get_printed_columns (file_cache &fc,
2309 const cpp_char_column_policy &policy,
2310 const fixit_hint *hint)
2311{
2312 expanded_location exploc = expand_location (hint->get_start_loc ());
2313 int start_column = location_compute_display_column (fc, exploc, policy);
2314 int hint_width = cpp_display_width (data: hint->get_string (), data_length: hint->get_length (),
2315 policy);
2316 int final_hint_column = start_column + hint_width - 1;
2317 if (hint->insertion_p ())
2318 {
2319 return column_range (start_column, final_hint_column);
2320 }
2321 else
2322 {
2323 exploc = expand_location (hint->get_next_loc ());
2324 --exploc.column;
2325 int finish_column = location_compute_display_column (fc, exploc, policy);
2326 return column_range (start_column,
2327 MAX (finish_column, final_hint_column));
2328 }
2329}
2330
2331/* A correction on a particular line.
2332 This describes a plan for how to print one or more fixit_hint
2333 instances that affected the line, potentially consolidating hints
2334 into corrections to make the result easier for the user to read. */
2335
2336class correction
2337{
2338public:
2339 correction (column_range affected_bytes,
2340 column_range affected_columns,
2341 column_range printed_columns,
2342 const char *new_text, size_t new_text_len,
2343 const cpp_char_column_policy &policy)
2344 : m_affected_bytes (affected_bytes),
2345 m_affected_columns (affected_columns),
2346 m_printed_columns (printed_columns),
2347 m_text (xstrdup (new_text)),
2348 m_byte_length (new_text_len),
2349 m_policy (policy),
2350 m_alloc_sz (new_text_len + 1)
2351 {
2352 compute_display_cols ();
2353 }
2354
2355 ~correction () { free (ptr: m_text); }
2356
2357 bool insertion_p () const
2358 {
2359 return m_affected_bytes.start == m_affected_bytes.finish + 1;
2360 }
2361
2362 void ensure_capacity (size_t len);
2363 void ensure_terminated ();
2364
2365 void compute_display_cols ()
2366 {
2367 m_display_cols = cpp_display_width (data: m_text, data_length: m_byte_length, policy: m_policy);
2368 }
2369
2370 void overwrite (int dst_offset, const char_span &src_span)
2371 {
2372 gcc_assert (dst_offset >= 0);
2373 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2374 memcpy (dest: m_text + dst_offset, src: src_span.get_buffer (),
2375 n: src_span.length ());
2376 }
2377
2378 /* If insert, then start: the column before which the text
2379 is to be inserted, and finish is offset by the length of
2380 the replacement.
2381 If replace, then the range of columns affected. */
2382 column_range m_affected_bytes;
2383 column_range m_affected_columns;
2384
2385 /* If insert, then start: the column before which the text
2386 is to be inserted, and finish is offset by the length of
2387 the replacement.
2388 If replace, then the range of columns affected. */
2389 column_range m_printed_columns;
2390
2391 /* The text to be inserted/used as replacement. */
2392 char *m_text;
2393 size_t m_byte_length; /* Not including null-terminator. */
2394 int m_display_cols;
2395 const cpp_char_column_policy &m_policy;
2396 size_t m_alloc_sz;
2397};
2398
2399/* Ensure that m_text can hold a string of length LEN
2400 (plus 1 for 0-termination). */
2401
2402void
2403correction::ensure_capacity (size_t len)
2404{
2405 /* Allow 1 extra byte for 0-termination. */
2406 if (m_alloc_sz < (len + 1))
2407 {
2408 size_t new_alloc_sz = (len + 1) * 2;
2409 m_text = (char *)xrealloc (m_text, new_alloc_sz);
2410 m_alloc_sz = new_alloc_sz;
2411 }
2412}
2413
2414/* Ensure that m_text is 0-terminated. */
2415
2416void
2417correction::ensure_terminated ()
2418{
2419 /* 0-terminate the buffer. */
2420 gcc_assert (m_byte_length < m_alloc_sz);
2421 m_text[m_byte_length] = '\0';
2422}
2423
2424/* A list of corrections affecting a particular line.
2425 This is used by layout::print_trailing_fixits for planning
2426 how to print the fix-it hints affecting the line. */
2427
2428class line_corrections
2429{
2430public:
2431 line_corrections (file_cache &fc,
2432 const char_display_policy &policy,
2433 const char *filename,
2434 linenum_type row)
2435 : m_file_cache (fc),
2436 m_policy (policy), m_filename (filename), m_row (row)
2437 {}
2438 ~line_corrections ();
2439
2440 void add_hint (const fixit_hint *hint);
2441
2442 file_cache &m_file_cache;
2443 const char_display_policy &m_policy;
2444 const char *m_filename;
2445 linenum_type m_row;
2446 auto_vec <correction *> m_corrections;
2447};
2448
2449/* struct line_corrections. */
2450
2451line_corrections::~line_corrections ()
2452{
2453 unsigned i;
2454 correction *c;
2455 FOR_EACH_VEC_ELT (m_corrections, i, c)
2456 delete c;
2457}
2458
2459/* A struct wrapping a particular source line, allowing
2460 run-time bounds-checking of accesses in a checked build. */
2461
2462class source_line
2463{
2464public:
2465 source_line (file_cache &fc, const char *filename, int line);
2466
2467 char_span as_span () { return char_span (chars, width); }
2468
2469 const char *chars;
2470 int width;
2471};
2472
2473/* source_line's ctor. */
2474
2475source_line::source_line (file_cache &fc, const char *filename, int line)
2476{
2477 char_span span = fc.get_source_line (file_path: filename, line);
2478 chars = span.get_buffer ();
2479 width = span.length ();
2480}
2481
2482/* Add HINT to the corrections for this line.
2483 Attempt to consolidate nearby hints so that they will not
2484 overlap with printed. */
2485
2486void
2487line_corrections::add_hint (const fixit_hint *hint)
2488{
2489 column_range affected_bytes
2490 = get_affected_range (fc&: m_file_cache, policy: m_policy, hint, col_unit: CU_BYTES);
2491 column_range affected_columns
2492 = get_affected_range (fc&: m_file_cache, policy: m_policy, hint, col_unit: CU_DISPLAY_COLS);
2493 column_range printed_columns
2494 = get_printed_columns (fc&: m_file_cache, policy: m_policy, hint);
2495
2496 /* Potentially consolidate. */
2497 if (!m_corrections.is_empty ())
2498 {
2499 correction *last_correction
2500 = m_corrections[m_corrections.length () - 1];
2501
2502 /* The following consolidation code assumes that the fix-it hints
2503 have been sorted by start (done within layout's ctor). */
2504 gcc_assert (affected_bytes.start
2505 >= last_correction->m_affected_bytes.start);
2506 gcc_assert (printed_columns.start
2507 >= last_correction->m_printed_columns.start);
2508
2509 if (printed_columns.start <= last_correction->m_printed_columns.finish
2510 && column_range::valid_p (start: last_correction->m_affected_bytes.finish + 1,
2511 finish: affected_bytes.start - 1))
2512 {
2513 /* We have two hints for which the printed forms of the hints
2514 would touch or overlap, so we need to consolidate them to avoid
2515 confusing the user.
2516 Attempt to inject a "replace" correction from immediately
2517 after the end of the last hint to immediately before the start
2518 of the next hint. */
2519 column_range between (last_correction->m_affected_bytes.finish + 1,
2520 affected_bytes.start - 1);
2521
2522 /* Try to read the source. */
2523 source_line line (m_file_cache, m_filename, m_row);
2524 if (line.chars && between.finish < line.width)
2525 {
2526 /* Consolidate into the last correction:
2527 add a no-op "replace" of the "between" text, and
2528 add the text from the new hint. */
2529 int old_byte_len = last_correction->m_byte_length;
2530 gcc_assert (old_byte_len >= 0);
2531 int between_byte_len = between.finish + 1 - between.start;
2532 gcc_assert (between_byte_len >= 0);
2533 int new_byte_len
2534 = old_byte_len + between_byte_len + hint->get_length ();
2535 gcc_assert (new_byte_len >= 0);
2536 last_correction->ensure_capacity (len: new_byte_len);
2537 last_correction->overwrite
2538 (dst_offset: old_byte_len,
2539 src_span: line.as_span ().subspan (offset: between.start - 1,
2540 n_elts: between.finish + 1 - between.start));
2541 last_correction->overwrite (dst_offset: old_byte_len + between_byte_len,
2542 src_span: char_span (hint->get_string (),
2543 hint->get_length ()));
2544 last_correction->m_byte_length = new_byte_len;
2545 last_correction->ensure_terminated ();
2546 last_correction->m_affected_bytes.finish
2547 = affected_bytes.finish;
2548 last_correction->m_affected_columns.finish
2549 = affected_columns.finish;
2550 int prev_display_cols = last_correction->m_display_cols;
2551 last_correction->compute_display_cols ();
2552 last_correction->m_printed_columns.finish
2553 += last_correction->m_display_cols - prev_display_cols;
2554 return;
2555 }
2556 }
2557 }
2558
2559 /* If no consolidation happened, add a new correction instance. */
2560 m_corrections.safe_push (obj: new correction (affected_bytes,
2561 affected_columns,
2562 printed_columns,
2563 hint->get_string (),
2564 hint->get_length (),
2565 m_policy));
2566}
2567
2568/* If there are any fixit hints on source line ROW, print them.
2569 They are printed in order, attempting to combine them onto lines, but
2570 starting new lines if necessary.
2571 Fix-it hints that insert new lines are handled separately,
2572 in layout::print_leading_fixits. */
2573
2574void
2575layout::print_trailing_fixits (linenum_type row)
2576{
2577 /* Build a list of correction instances for the line,
2578 potentially consolidating hints (for the sake of readability). */
2579 line_corrections corrections (m_file_cache, m_policy, m_exploc.file, row);
2580 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
2581 {
2582 const fixit_hint *hint = m_fixit_hints[i];
2583
2584 /* Newline fixits are handled by layout::print_leading_fixits. */
2585 if (hint->ends_with_newline_p ())
2586 continue;
2587
2588 if (hint->affects_line_p (set: m_line_table, file: m_exploc.file, line: row))
2589 corrections.add_hint (hint);
2590 }
2591
2592 /* Now print the corrections. */
2593 unsigned i;
2594 correction *c;
2595 int column = m_x_offset_display;
2596
2597 if (!corrections.m_corrections.is_empty ())
2598 start_annotation_line ();
2599
2600 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2601 {
2602 /* For now we assume each fixit hint can only touch one line. */
2603 if (c->insertion_p ())
2604 {
2605 /* This assumes the insertion just affects one line. */
2606 int start_column = c->m_printed_columns.start;
2607 move_to_column (column: &column, dest_column: start_column, add_left_margin: true);
2608 m_colorizer.set_fixit_insert ();
2609 pp_string (m_pp, c->m_text);
2610 m_colorizer.set_normal_text ();
2611 column += c->m_display_cols;
2612 }
2613 else
2614 {
2615 /* If the range of the replacement wasn't printed in the
2616 annotation line, then print an extra underline to
2617 indicate exactly what is being replaced.
2618 Always show it for removals. */
2619 int start_column = c->m_affected_columns.start;
2620 int finish_column = c->m_affected_columns.finish;
2621 if (!annotation_line_showed_range_p (line: row, start_column,
2622 finish_column)
2623 || c->m_byte_length == 0)
2624 {
2625 move_to_column (column: &column, dest_column: start_column, add_left_margin: true);
2626 m_colorizer.set_fixit_delete ();
2627 for (; column <= finish_column; column++)
2628 pp_character (m_pp, '-');
2629 m_colorizer.set_normal_text ();
2630 }
2631 /* Print the replacement text. REPLACE also covers
2632 removals, so only do this extra work (potentially starting
2633 a new line) if we have actual replacement text. */
2634 if (c->m_byte_length > 0)
2635 {
2636 move_to_column (column: &column, dest_column: start_column, add_left_margin: true);
2637 m_colorizer.set_fixit_insert ();
2638 pp_string (m_pp, c->m_text);
2639 m_colorizer.set_normal_text ();
2640 column += c->m_display_cols;
2641 }
2642 }
2643 }
2644
2645 /* Add a trailing newline, if necessary. */
2646 move_to_column (column: &column, dest_column: 0, add_left_margin: false);
2647}
2648
2649/* Disable any colorization and emit a newline. */
2650
2651void
2652layout::print_newline ()
2653{
2654 m_colorizer.set_normal_text ();
2655 pp_newline (m_pp);
2656}
2657
2658/* Return true if (ROW/COLUMN) is within a range of the layout.
2659 If it returns true, OUT_STATE is written to, with the
2660 range index, and whether we should draw the caret at
2661 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2662 whether all inputs and outputs are in bytes or display column units. */
2663
2664bool
2665layout::get_state_at_point (/* Inputs. */
2666 linenum_type row, int column,
2667 int first_non_ws, int last_non_ws,
2668 enum column_unit col_unit,
2669 /* Outputs. */
2670 point_state *out_state)
2671{
2672 layout_range *range;
2673 int i;
2674 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2675 {
2676 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2677 /* Bail out early, so that such ranges don't affect underlining or
2678 source colorization. */
2679 continue;
2680
2681 if (range->contains_point (row, column, col_unit))
2682 {
2683 out_state->range_idx = i;
2684
2685 /* Are we at the range's caret? is it visible? */
2686 out_state->draw_caret_p = false;
2687 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2688 && row == range->m_caret.m_line
2689 && column == range->m_caret.m_columns[col_unit])
2690 out_state->draw_caret_p = true;
2691
2692 /* Within a multiline range, don't display any underline
2693 in any leading or trailing whitespace on a line.
2694 We do display carets, however. */
2695 if (!out_state->draw_caret_p)
2696 if (column < first_non_ws || column > last_non_ws)
2697 return false;
2698
2699 /* We are within a range. */
2700 return true;
2701 }
2702 }
2703
2704 return false;
2705}
2706
2707/* Helper function for use by layout::print_line when printing the
2708 annotation line under the source line.
2709 Get the display column beyond the rightmost one that could contain a caret
2710 or range marker, given that we stop rendering at trailing whitespace.
2711 ROW is the source line within the given file.
2712 CARET_COLUMN is the display column of range 0's caret.
2713 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2714 character of source (as determined when printing the source line). */
2715
2716int
2717layout::get_x_bound_for_row (linenum_type row, int caret_column,
2718 int last_non_ws_column)
2719{
2720 int result = caret_column + 1;
2721
2722 layout_range *range;
2723 int i;
2724 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2725 {
2726 if (row >= range->m_start.m_line)
2727 {
2728 if (range->m_finish.m_line == row)
2729 {
2730 /* On the final line within a range; ensure that
2731 we render up to the end of the range. */
2732 const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
2733 if (result <= disp_col)
2734 result = disp_col + 1;
2735 }
2736 else if (row < range->m_finish.m_line)
2737 {
2738 /* Within a multiline range; ensure that we render up to the
2739 last non-whitespace column. */
2740 if (result <= last_non_ws_column)
2741 result = last_non_ws_column + 1;
2742 }
2743 }
2744 }
2745
2746 return result;
2747}
2748
2749/* Given *COLUMN as an x-coordinate, print spaces to position
2750 successive output at DEST_COLUMN, printing a newline if necessary,
2751 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2752 left margin after any newline. */
2753
2754void
2755layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2756{
2757 /* Start a new line if we need to. */
2758 if (*column > dest_column)
2759 {
2760 print_newline ();
2761 if (add_left_margin)
2762 start_annotation_line ();
2763 *column = m_x_offset_display;
2764 }
2765
2766 while (*column < dest_column)
2767 {
2768 pp_space (m_pp);
2769 (*column)++;
2770 }
2771}
2772
2773/* For debugging layout issues, render a ruler giving column numbers
2774 (after the 1-column indent). */
2775
2776void
2777layout::show_ruler (int max_column) const
2778{
2779 /* Hundreds. */
2780 if (max_column > 99)
2781 {
2782 start_annotation_line ();
2783 pp_space (m_pp);
2784 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2785 if (column % 10 == 0)
2786 pp_character (m_pp, '0' + (column / 100) % 10);
2787 else
2788 pp_space (m_pp);
2789 pp_newline (m_pp);
2790 }
2791
2792 /* Tens. */
2793 start_annotation_line ();
2794 pp_space (m_pp);
2795 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2796 if (column % 10 == 0)
2797 pp_character (m_pp, '0' + (column / 10) % 10);
2798 else
2799 pp_space (m_pp);
2800 pp_newline (m_pp);
2801
2802 /* Units. */
2803 start_annotation_line ();
2804 pp_space (m_pp);
2805 for (int column = 1 + m_x_offset_display; column <= max_column; column++)
2806 pp_character (m_pp, '0' + (column % 10));
2807 pp_newline (m_pp);
2808}
2809
2810/* Print leading fix-its (for new lines inserted before the source line)
2811 then the source line, followed by an annotation line
2812 consisting of any caret/underlines, then any fixits.
2813 If the source line can't be read, print nothing. */
2814void
2815layout::print_line (linenum_type row)
2816{
2817 char_span line = m_file_cache.get_source_line (file_path: m_exploc.file, line: row);
2818 if (!line)
2819 return;
2820
2821 print_leading_fixits (row);
2822 const line_bounds lbounds
2823 = print_source_line (row, line: line.get_buffer (), line_bytes: line.length ());
2824 if (should_print_annotation_line_p (row))
2825 print_annotation_line (row, lbounds);
2826 if (m_options.show_labels_p)
2827 print_any_labels (row);
2828 print_trailing_fixits (row);
2829}
2830
2831} /* End of anonymous namespace. */
2832
2833/* If LOC is within the spans of lines that will already be printed for
2834 this gcc_rich_location, then add it as a secondary location and return true.
2835
2836 Otherwise return false. */
2837
2838bool
2839gcc_rich_location::add_location_if_nearby (location_t loc,
2840 bool restrict_to_current_line_spans,
2841 const range_label *label)
2842{
2843 /* Use the layout location-handling logic to sanitize LOC,
2844 filtering it to the current line spans within a temporary
2845 layout instance. */
2846 layout layout (*global_dc, *this, DK_ERROR, nullptr);
2847 location_range loc_range;
2848 loc_range.m_loc = loc;
2849 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2850 if (!layout.maybe_add_location_range (loc_range: &loc_range, original_idx: 0,
2851 restrict_to_current_line_spans))
2852 return false;
2853
2854 add_range (loc, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label);
2855 return true;
2856}
2857
2858/* As per diagnostic_context::show_locus, but don't print anything
2859 if source printing is disabled, or if the location hasn't changed. */
2860
2861void
2862diagnostic_context::maybe_show_locus (const rich_location &richloc,
2863 diagnostic_t diagnostic_kind,
2864 pretty_printer *pp)
2865{
2866 const location_t loc = richloc.get_loc ();
2867 /* Do nothing if source-printing has been disabled. */
2868 if (!m_source_printing.enabled)
2869 return;
2870
2871 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2872 if (loc <= BUILTINS_LOCATION)
2873 return;
2874
2875 /* Don't print the same source location twice in a row, unless we have
2876 fix-it hints, or multiple locations, or a label. */
2877 if (loc == m_last_location
2878 && richloc.get_num_fixit_hints () == 0
2879 && richloc.get_num_locations () == 1
2880 && richloc.get_range (idx: 0)->m_label == NULL)
2881 return;
2882
2883 m_last_location = loc;
2884
2885 show_locus (richloc, diagnostic_kind, pp);
2886}
2887
2888/* Print the physical source code corresponding to the location of
2889 this diagnostic, with additional annotations.
2890 If PP is non-null, then use it rather than this context's printer. */
2891
2892void
2893diagnostic_context::show_locus (const rich_location &richloc,
2894 diagnostic_t diagnostic_kind,
2895 pretty_printer *pp)
2896{
2897 layout layout (*this, richloc, diagnostic_kind, pp);
2898 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2899 line_span_idx++)
2900 {
2901 const line_span *line_span = layout.get_line_span (idx: line_span_idx);
2902 if (m_source_printing.show_line_numbers_p)
2903 {
2904 /* With line numbers, we should show whenever the line-numbering
2905 "jumps". */
2906 if (line_span_idx > 0)
2907 layout.print_gap_in_line_numbering ();
2908 }
2909 else
2910 {
2911 /* Without line numbers, we print headings for some line spans. */
2912 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2913 {
2914 expanded_location exploc
2915 = layout.get_expanded_location (line_span);
2916 m_text_callbacks.m_start_span (this, exploc);
2917 }
2918 }
2919 /* Iterate over the lines within this span (using linenum_arith_t to
2920 avoid overflow with 0xffffffff causing an infinite loop). */
2921 linenum_arith_t last_line = line_span->get_last_line ();
2922 for (linenum_arith_t row = line_span->get_first_line ();
2923 row <= last_line; row++)
2924 layout.print_line (row);
2925 }
2926}
2927
2928#if CHECKING_P
2929
2930namespace selftest {
2931
2932/* Selftests for diagnostic_show_locus. */
2933
2934/* Verify that cpp_display_width correctly handles escaping. */
2935
2936static void
2937test_display_widths ()
2938{
2939 gcc_rich_location richloc (UNKNOWN_LOCATION);
2940
2941 /* U+03C0 "GREEK SMALL LETTER PI". */
2942 const char *pi = "\xCF\x80";
2943 /* U+1F642 "SLIGHTLY SMILING FACE". */
2944 const char *emoji = "\xF0\x9F\x99\x82";
2945 /* Stray trailing byte of a UTF-8 character. */
2946 const char *stray = "\xBF";
2947 /* U+10FFFF. */
2948 const char *max_codepoint = "\xF4\x8F\xBF\xBF";
2949
2950 /* No escaping. */
2951 {
2952 test_diagnostic_context dc;
2953 char_display_policy policy (make_policy (dc, richloc));
2954 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
2955 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
2956 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
2957 /* Don't check width of U+10FFFF; it's in a private use plane. */
2958 }
2959
2960 richloc.set_escape_on_output (true);
2961
2962 {
2963 test_diagnostic_context dc;
2964 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
2965 char_display_policy policy (make_policy (dc, richloc));
2966 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2967 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
2968 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2969 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2970 policy),
2971 strlen ("<U+10FFFF>"));
2972 }
2973
2974 {
2975 test_diagnostic_context dc;
2976 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
2977 char_display_policy policy (make_policy (dc, richloc));
2978 ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
2979 ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
2980 ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
2981 ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
2982 policy),
2983 16);
2984 }
2985}
2986
2987/* For precise tests of the layout, make clear where the source line will
2988 start. test_left_margin sets the total byte count from the left side of the
2989 screen to the start of source lines, after the line number and the separator,
2990 which consists of the three characters " | ". */
2991static const int test_linenum_sep = 3;
2992static const int test_left_margin = 7;
2993
2994/* Helper function for test_layout_x_offset_display_utf8(). */
2995static void
2996test_offset_impl (int caret_byte_col, int max_width,
2997 int expected_x_offset_display,
2998 int left_margin = test_left_margin)
2999{
3000 test_diagnostic_context dc;
3001 dc.m_source_printing.max_width = max_width;
3002 /* diagnostic_context::min_margin_width sets the minimum space reserved for
3003 the line number plus one space after. */
3004 dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
3005 dc.m_source_printing.show_line_numbers_p = true;
3006 rich_location richloc (line_table,
3007 linemap_position_for_column (line_table,
3008 caret_byte_col));
3009 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3010 ASSERT_EQ (left_margin - test_linenum_sep,
3011 test_layout.get_linenum_width ());
3012 ASSERT_EQ (expected_x_offset_display,
3013 test_layout.get_x_offset_display ());
3014}
3015
3016/* Test that layout::calculate_x_offset_display() works. */
3017static void
3018test_layout_x_offset_display_utf8 (const line_table_case &case_)
3019{
3020
3021 const char *content
3022 = "This line is very long, so that we can use it to test the logic for "
3023 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
3024 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
3025 "column #102.\n";
3026
3027 /* Number of bytes in the line, subtracting one to remove the newline. */
3028 const int line_bytes = strlen (s: content) - 1;
3029
3030 /* Number of display columns occupied by the line; each of the 2 emojis
3031 takes up 2 fewer display columns than it does bytes. */
3032 const int line_display_cols = line_bytes - 2*2;
3033
3034 /* The column of the first emoji. Byte or display is the same as there are
3035 no multibyte characters earlier on the line. */
3036 const int emoji_col = 102;
3037
3038 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3039 file_cache fc;
3040 line_table_test ltt (case_);
3041
3042 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
3043
3044 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3045
3046 /* Don't attempt to run the tests if column data might be unavailable. */
3047 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3048 return;
3049
3050 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3051 ASSERT_EQ (1, LOCATION_LINE (line_end));
3052 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3053
3054 char_span lspan = fc.get_source_line (file_path: tmp.get_filename (), line: 1);
3055 ASSERT_EQ (line_display_cols,
3056 cpp_display_width (lspan.get_buffer (), lspan.length (),
3057 def_policy ()));
3058 ASSERT_EQ (line_display_cols,
3059 location_compute_display_column (fc,
3060 expand_location (line_end),
3061 def_policy ()));
3062 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3063 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3064
3065 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3066
3067 /* No constraint on the width -> no offset. */
3068 test_offset_impl (caret_byte_col: emoji_col, max_width: 0, expected_x_offset_display: 0);
3069
3070 /* Caret is before the beginning -> no offset. */
3071 test_offset_impl (caret_byte_col: 0, max_width: 100, expected_x_offset_display: 0);
3072
3073 /* Caret is past the end of the line -> no offset. */
3074 test_offset_impl (caret_byte_col: line_bytes+1, max_width: 100, expected_x_offset_display: 0);
3075
3076 /* Line fits in the display -> no offset. */
3077 test_offset_impl (caret_byte_col: line_bytes, max_width: line_display_cols + test_left_margin, expected_x_offset_display: 0);
3078 test_offset_impl (caret_byte_col: emoji_col, max_width: line_display_cols + test_left_margin, expected_x_offset_display: 0);
3079
3080 /* Line is too long for the display but caret location is OK
3081 anyway -> no offset. */
3082 static const int small_width = 24;
3083 test_offset_impl (caret_byte_col: 1, max_width: small_width, expected_x_offset_display: 0);
3084
3085 /* Width constraint is very small -> no offset. */
3086 test_offset_impl (caret_byte_col: emoji_col, max_width: CARET_LINE_MARGIN, expected_x_offset_display: 0);
3087
3088 /* Line would be offset, but due to large line numbers, offsetting
3089 would remove the whole line -> no offset. */
3090 static const int huge_left_margin = 100;
3091 test_offset_impl (caret_byte_col: emoji_col, max_width: huge_left_margin, expected_x_offset_display: 0, left_margin: huge_left_margin);
3092
3093 /* Line is the same length as the display, but the line number makes it too
3094 long, so offset is required. Caret is at the end so padding on the right
3095 is not in effect. */
3096 for (int excess = 1; excess <= 3; ++excess)
3097 test_offset_impl (caret_byte_col: line_bytes, max_width: line_display_cols + test_left_margin - excess,
3098 expected_x_offset_display: excess);
3099
3100 /* Line is much too long for the display, caret is near the end ->
3101 offset should be such that the line fits in the display and caret
3102 remains the same distance from the end that it was. */
3103 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3104 caret_offset <= max_offset; ++caret_offset)
3105 test_offset_impl (caret_byte_col: line_bytes - caret_offset, max_width: small_width,
3106 expected_x_offset_display: line_display_cols + test_left_margin - small_width);
3107
3108 /* As previous case but caret is closer to the middle; now we want it to end
3109 up CARET_LINE_MARGIN bytes from the end. */
3110 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3111 test_offset_impl (caret_byte_col: emoji_col, max_width: small_width,
3112 expected_x_offset_display: emoji_col + test_left_margin
3113 - (small_width - CARET_LINE_MARGIN));
3114
3115 /* Test that the source line is offset as expected when printed. */
3116 {
3117 test_diagnostic_context dc;
3118 dc.m_source_printing.max_width = small_width - 6;
3119 dc.m_source_printing.min_margin_width
3120 = test_left_margin - test_linenum_sep + 1;
3121 dc.m_source_printing.show_line_numbers_p = true;
3122 dc.m_source_printing.show_ruler_p = true;
3123 rich_location richloc (line_table,
3124 linemap_position_for_column (line_table,
3125 emoji_col));
3126 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3127 test_layout.print_line (row: 1);
3128 ASSERT_STREQ (" | 1 \n"
3129 " | 1 \n"
3130 " | 234567890123456789\n"
3131 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3132 "that occupies 8 bytes and 4 display columns, starting at "
3133 "column #102.\n"
3134 " | ^\n\n",
3135 pp_formatted_text (dc.printer));
3136 }
3137
3138 /* Similar to the previous example, but now the offset called for would split
3139 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3140 it with a padding space in this case. */
3141 {
3142 test_diagnostic_context dc;
3143 dc.m_source_printing.max_width = small_width - 5;
3144 dc.m_source_printing.min_margin_width
3145 = test_left_margin - test_linenum_sep + 1;
3146 dc.m_source_printing.show_line_numbers_p = true;
3147 dc.m_source_printing.show_ruler_p = true;
3148 rich_location richloc (line_table,
3149 linemap_position_for_column (line_table,
3150 emoji_col + 2));
3151 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3152 test_layout.print_line (row: 1);
3153 ASSERT_STREQ (" | 1 1 \n"
3154 " | 1 2 \n"
3155 " | 3456789012345678901\n"
3156 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3157 "that occupies 8 bytes and 4 display columns, starting at "
3158 "column #102.\n"
3159 " | ^\n\n",
3160 pp_formatted_text (dc.printer));
3161 }
3162
3163}
3164
3165static void
3166test_layout_x_offset_display_tab (const line_table_case &case_)
3167{
3168 const char *content
3169 = "This line is very long, so that we can use it to test the logic for "
3170 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3171 "a variable number of display columns, starting at column #103.\n";
3172
3173 /* Number of bytes in the line, subtracting one to remove the newline. */
3174 const int line_bytes = strlen (s: content) - 1;
3175
3176 /* The column where the tab begins. Byte or display is the same as there are
3177 no multibyte characters earlier on the line. */
3178 const int tab_col = 103;
3179
3180 /* Effective extra size of the tab beyond what a single space would have taken
3181 up, indexed by tabstop. */
3182 static const int num_tabstops = 11;
3183 int extra_width[num_tabstops];
3184 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3185 {
3186 const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3187 extra_width[tabstop] = this_tab_size - 1;
3188 }
3189 /* Example of this calculation: if tabstop is 10, the tab starting at column
3190 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3191 next character is at column #111. So it takes up 7 more columns than
3192 a space would have taken up. */
3193 ASSERT_EQ (7, extra_width[10]);
3194
3195 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3196 file_cache fc;
3197 line_table_test ltt (case_);
3198
3199 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
3200
3201 location_t line_end = linemap_position_for_column (line_table, line_bytes);
3202
3203 /* Don't attempt to run the tests if column data might be unavailable. */
3204 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3205 return;
3206
3207 /* Check that cpp_display_width handles the tabs as expected. */
3208 char_span lspan = fc.get_source_line (file_path: tmp.get_filename (), line: 1);
3209 ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3210 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3211 {
3212 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3213 ASSERT_EQ (line_bytes + extra_width[tabstop],
3214 cpp_display_width (lspan.get_buffer (), lspan.length (),
3215 policy));
3216 ASSERT_EQ (line_bytes + extra_width[tabstop],
3217 location_compute_display_column (fc,
3218 expand_location (line_end),
3219 policy));
3220 }
3221
3222 /* Check that the tab is expanded to the expected number of spaces. */
3223 rich_location richloc (line_table,
3224 linemap_position_for_column (line_table,
3225 tab_col + 1));
3226 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3227 {
3228 test_diagnostic_context dc;
3229 dc.m_tabstop = tabstop;
3230 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3231 test_layout.print_line (row: 1);
3232 const char *out = pp_formatted_text (dc.printer);
3233 ASSERT_EQ (NULL, strchr (out, '\t'));
3234 const char *left_quote = strchr (s: out, c: '`');
3235 const char *right_quote = strchr (s: out, c: '\'');
3236 ASSERT_NE (NULL, left_quote);
3237 ASSERT_NE (NULL, right_quote);
3238 ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3239 }
3240
3241 /* Check that the line is offset properly and that the tab is broken up
3242 into the expected number of spaces when it is the last character skipped
3243 over. */
3244 for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3245 {
3246 test_diagnostic_context dc;
3247 dc.m_tabstop = tabstop;
3248 static const int small_width = 24;
3249 dc.m_source_printing.max_width = small_width - 4;
3250 dc.m_source_printing.min_margin_width
3251 = test_left_margin - test_linenum_sep + 1;
3252 dc.m_source_printing.show_line_numbers_p = true;
3253 layout test_layout (dc, richloc, DK_ERROR, nullptr);
3254 test_layout.print_line (row: 1);
3255
3256 /* We have arranged things so that two columns will be printed before
3257 the caret. If the tab results in more than one space, this should
3258 produce two spaces in the output; otherwise, it will be a single space
3259 preceded by the opening quote before the tab character. */
3260 const char *output1
3261 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3262 "display columns, starting at column #103.\n"
3263 " | ^\n\n";
3264 const char *output2
3265 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3266 "display columns, starting at column #103.\n"
3267 " | ^\n\n";
3268 const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3269 ASSERT_STREQ (expected_output, pp_formatted_text (dc.printer));
3270 }
3271}
3272
3273
3274/* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3275
3276static void
3277test_diagnostic_show_locus_unknown_location ()
3278{
3279 test_diagnostic_context dc;
3280 rich_location richloc (line_table, UNKNOWN_LOCATION);
3281 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3282 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
3283}
3284
3285/* Verify that diagnostic_show_locus works sanely for various
3286 single-line cases.
3287
3288 All of these work on the following 1-line source file:
3289 .0000000001111111
3290 .1234567890123456
3291 "foo = bar.field;\n"
3292 which is set up by test_diagnostic_show_locus_one_liner and calls
3293 them. */
3294
3295/* Just a caret. */
3296
3297static void
3298test_one_liner_simple_caret ()
3299{
3300 test_diagnostic_context dc;
3301 location_t caret = linemap_position_for_column (line_table, 10);
3302 rich_location richloc (line_table, caret);
3303 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3304 ASSERT_STREQ (" foo = bar.field;\n"
3305 " ^\n",
3306 pp_formatted_text (dc.printer));
3307}
3308
3309/* No column information (column == 0).
3310 No annotation line should be printed. */
3311
3312static void
3313test_one_liner_no_column ()
3314{
3315 test_diagnostic_context dc;
3316 location_t caret = linemap_position_for_column (line_table, 0);
3317 rich_location richloc (line_table, caret);
3318 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3319 ASSERT_STREQ (" foo = bar.field;\n",
3320 pp_formatted_text (dc.printer));
3321}
3322
3323/* Caret and range. */
3324
3325static void
3326test_one_liner_caret_and_range ()
3327{
3328 test_diagnostic_context dc;
3329 location_t caret = linemap_position_for_column (line_table, 10);
3330 location_t start = linemap_position_for_column (line_table, 7);
3331 location_t finish = linemap_position_for_column (line_table, 15);
3332 location_t loc = make_location (caret, start, finish);
3333 rich_location richloc (line_table, loc);
3334 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3335 ASSERT_STREQ (" foo = bar.field;\n"
3336 " ~~~^~~~~~\n",
3337 pp_formatted_text (dc.printer));
3338}
3339
3340/* Multiple ranges and carets. */
3341
3342static void
3343test_one_liner_multiple_carets_and_ranges ()
3344{
3345 test_diagnostic_context dc;
3346 location_t foo
3347 = make_location (caret: linemap_position_for_column (line_table, 2),
3348 start: linemap_position_for_column (line_table, 1),
3349 finish: linemap_position_for_column (line_table, 3));
3350 dc.m_source_printing.caret_chars[0] = 'A';
3351
3352 location_t bar
3353 = make_location (caret: linemap_position_for_column (line_table, 8),
3354 start: linemap_position_for_column (line_table, 7),
3355 finish: linemap_position_for_column (line_table, 9));
3356 dc.m_source_printing.caret_chars[1] = 'B';
3357
3358 location_t field
3359 = make_location (caret: linemap_position_for_column (line_table, 13),
3360 start: linemap_position_for_column (line_table, 11),
3361 finish: linemap_position_for_column (line_table, 15));
3362 dc.m_source_printing.caret_chars[2] = 'C';
3363
3364 rich_location richloc (line_table, foo);
3365 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITH_CARET);
3366 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITH_CARET);
3367 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3368 ASSERT_STREQ (" foo = bar.field;\n"
3369 " ~A~ ~B~ ~~C~~\n",
3370 pp_formatted_text (dc.printer));
3371}
3372
3373/* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3374
3375static void
3376test_one_liner_fixit_insert_before ()
3377{
3378 test_diagnostic_context dc;
3379 location_t caret = linemap_position_for_column (line_table, 7);
3380 rich_location richloc (line_table, caret);
3381 richloc.add_fixit_insert_before (new_content: "&");
3382 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3383 ASSERT_STREQ (" foo = bar.field;\n"
3384 " ^\n"
3385 " &\n",
3386 pp_formatted_text (dc.printer));
3387}
3388
3389/* Insertion fix-it hint: adding a "[0]" after "foo". */
3390
3391static void
3392test_one_liner_fixit_insert_after ()
3393{
3394 test_diagnostic_context dc;
3395 location_t start = linemap_position_for_column (line_table, 1);
3396 location_t finish = linemap_position_for_column (line_table, 3);
3397 location_t foo = make_location (caret: start, start, finish);
3398 rich_location richloc (line_table, foo);
3399 richloc.add_fixit_insert_after (new_content: "[0]");
3400 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3401 ASSERT_STREQ (" foo = bar.field;\n"
3402 " ^~~\n"
3403 " [0]\n",
3404 pp_formatted_text (dc.printer));
3405}
3406
3407/* Removal fix-it hint: removal of the ".field".
3408 Also verify the interaction of pp_set_prefix with rulers and
3409 fix-it hints. */
3410
3411static void
3412test_one_liner_fixit_remove ()
3413{
3414 location_t start = linemap_position_for_column (line_table, 10);
3415 location_t finish = linemap_position_for_column (line_table, 15);
3416 location_t dot = make_location (caret: start, start, finish);
3417 rich_location richloc (line_table, dot);
3418 richloc.add_fixit_remove ();
3419
3420 /* Normal. */
3421 {
3422 test_diagnostic_context dc;
3423 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3424 ASSERT_STREQ (" foo = bar.field;\n"
3425 " ^~~~~~\n"
3426 " ------\n",
3427 pp_formatted_text (dc.printer));
3428 }
3429
3430 /* Test of adding a prefix. */
3431 {
3432 test_diagnostic_context dc;
3433 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3434 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3435 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3436 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3437 "TEST PREFIX: ^~~~~~\n"
3438 "TEST PREFIX: ------\n",
3439 pp_formatted_text (dc.printer));
3440 }
3441
3442 /* Normal, with ruler. */
3443 {
3444 test_diagnostic_context dc;
3445 dc.m_source_printing.show_ruler_p = true;
3446 dc.m_source_printing.max_width = 104;
3447 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3448 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3449 " 1 2 3 4 5 6 7 8 9 0 \n"
3450 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3451 " foo = bar.field;\n"
3452 " ^~~~~~\n"
3453 " ------\n",
3454 pp_formatted_text (dc.printer));
3455 }
3456
3457 /* Test of adding a prefix, with ruler. */
3458 {
3459 test_diagnostic_context dc;
3460 dc.m_source_printing.show_ruler_p = true;
3461 dc.m_source_printing.max_width = 50;
3462 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3463 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3464 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3465 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3466 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3467 "TEST PREFIX: foo = bar.field;\n"
3468 "TEST PREFIX: ^~~~~~\n"
3469 "TEST PREFIX: ------\n",
3470 pp_formatted_text (dc.printer));
3471 }
3472
3473 /* Test of adding a prefix, with ruler and line numbers. */
3474 {
3475 test_diagnostic_context dc;
3476 dc.m_source_printing.show_ruler_p = true;
3477 dc.m_source_printing.max_width = 50;
3478 dc.m_source_printing.show_line_numbers_p = true;
3479 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3480 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
3481 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3482 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3483 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3484 "TEST PREFIX: 1 | foo = bar.field;\n"
3485 "TEST PREFIX: | ^~~~~~\n"
3486 "TEST PREFIX: | ------\n",
3487 pp_formatted_text (dc.printer));
3488 }
3489}
3490
3491/* Replace fix-it hint: replacing "field" with "m_field". */
3492
3493static void
3494test_one_liner_fixit_replace ()
3495{
3496 test_diagnostic_context dc;
3497 location_t start = linemap_position_for_column (line_table, 11);
3498 location_t finish = linemap_position_for_column (line_table, 15);
3499 location_t field = make_location (caret: start, start, finish);
3500 rich_location richloc (line_table, field);
3501 richloc.add_fixit_replace (new_content: "m_field");
3502 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3503 ASSERT_STREQ (" foo = bar.field;\n"
3504 " ^~~~~\n"
3505 " m_field\n",
3506 pp_formatted_text (dc.printer));
3507}
3508
3509/* Replace fix-it hint: replacing "field" with "m_field",
3510 but where the caret was elsewhere. */
3511
3512static void
3513test_one_liner_fixit_replace_non_equal_range ()
3514{
3515 test_diagnostic_context dc;
3516 location_t equals = linemap_position_for_column (line_table, 5);
3517 location_t start = linemap_position_for_column (line_table, 11);
3518 location_t finish = linemap_position_for_column (line_table, 15);
3519 rich_location richloc (line_table, equals);
3520 source_range range;
3521 range.m_start = start;
3522 range.m_finish = finish;
3523 richloc.add_fixit_replace (src_range: range, new_content: "m_field");
3524 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3525 /* The replacement range is not indicated in the annotation line, so
3526 it should be indicated via an additional underline. */
3527 ASSERT_STREQ (" foo = bar.field;\n"
3528 " ^\n"
3529 " -----\n"
3530 " m_field\n",
3531 pp_formatted_text (dc.printer));
3532}
3533
3534/* Replace fix-it hint: replacing "field" with "m_field",
3535 where the caret was elsewhere, but where a secondary range
3536 exactly covers "field". */
3537
3538static void
3539test_one_liner_fixit_replace_equal_secondary_range ()
3540{
3541 test_diagnostic_context dc;
3542 location_t equals = linemap_position_for_column (line_table, 5);
3543 location_t start = linemap_position_for_column (line_table, 11);
3544 location_t finish = linemap_position_for_column (line_table, 15);
3545 rich_location richloc (line_table, equals);
3546 location_t field = make_location (caret: start, start, finish);
3547 richloc.add_range (loc: field);
3548 richloc.add_fixit_replace (where: field, new_content: "m_field");
3549 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3550 /* The replacement range is indicated in the annotation line,
3551 so it shouldn't be indicated via an additional underline. */
3552 ASSERT_STREQ (" foo = bar.field;\n"
3553 " ^ ~~~~~\n"
3554 " m_field\n",
3555 pp_formatted_text (dc.printer));
3556}
3557
3558/* Verify that we can use ad-hoc locations when adding fixits to a
3559 rich_location. */
3560
3561static void
3562test_one_liner_fixit_validation_adhoc_locations ()
3563{
3564 /* Generate a range that's too long to be packed, so must
3565 be stored as an ad-hoc location (given the defaults
3566 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3567 const location_t c7 = linemap_position_for_column (line_table, 7);
3568 const location_t c47 = linemap_position_for_column (line_table, 47);
3569 const location_t loc = make_location (caret: c7, start: c7, finish: c47);
3570
3571 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3572 return;
3573
3574 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3575
3576 /* Insert. */
3577 {
3578 rich_location richloc (line_table, loc);
3579 richloc.add_fixit_insert_before (where: loc, new_content: "test");
3580 /* It should not have been discarded by the validator. */
3581 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3582
3583 test_diagnostic_context dc;
3584 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3585 ASSERT_STREQ (" foo = bar.field;\n"
3586 " ^~~~~~~~~~ \n"
3587 " test\n",
3588 pp_formatted_text (dc.printer));
3589 }
3590
3591 /* Remove. */
3592 {
3593 rich_location richloc (line_table, loc);
3594 source_range range = source_range::from_locations (start: loc, finish: c47);
3595 richloc.add_fixit_remove (src_range: range);
3596 /* It should not have been discarded by the validator. */
3597 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3598
3599 test_diagnostic_context dc;
3600 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3601 ASSERT_STREQ (" foo = bar.field;\n"
3602 " ^~~~~~~~~~ \n"
3603 " -----------------------------------------\n",
3604 pp_formatted_text (dc.printer));
3605 }
3606
3607 /* Replace. */
3608 {
3609 rich_location richloc (line_table, loc);
3610 source_range range = source_range::from_locations (start: loc, finish: c47);
3611 richloc.add_fixit_replace (src_range: range, new_content: "test");
3612 /* It should not have been discarded by the validator. */
3613 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3614
3615 test_diagnostic_context dc;
3616 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3617 ASSERT_STREQ (" foo = bar.field;\n"
3618 " ^~~~~~~~~~ \n"
3619 " test\n",
3620 pp_formatted_text (dc.printer));
3621 }
3622}
3623
3624/* Test of consolidating insertions at the same location. */
3625
3626static void
3627test_one_liner_many_fixits_1 ()
3628{
3629 test_diagnostic_context dc;
3630 location_t equals = linemap_position_for_column (line_table, 5);
3631 rich_location richloc (line_table, equals);
3632 for (int i = 0; i < 19; i++)
3633 richloc.add_fixit_insert_before (new_content: "a");
3634 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3635 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3636 ASSERT_STREQ (" foo = bar.field;\n"
3637 " ^\n"
3638 " aaaaaaaaaaaaaaaaaaa\n",
3639 pp_formatted_text (dc.printer));
3640}
3641
3642/* Ensure that we can add an arbitrary number of fix-it hints to a
3643 rich_location, even if they are not consolidated. */
3644
3645static void
3646test_one_liner_many_fixits_2 ()
3647{
3648 test_diagnostic_context dc;
3649 location_t equals = linemap_position_for_column (line_table, 5);
3650 rich_location richloc (line_table, equals);
3651 for (int i = 0; i < 19; i++)
3652 {
3653 location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
3654 richloc.add_fixit_insert_before (where: loc, new_content: "a");
3655 }
3656 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3657 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3658 ASSERT_STREQ (" foo = bar.field;\n"
3659 " ^\n"
3660 " a a a a a a a a a a a a a a a a a a a\n",
3661 pp_formatted_text (dc.printer));
3662}
3663
3664/* Test of labeling the ranges within a rich_location. */
3665
3666static void
3667test_one_liner_labels ()
3668{
3669 location_t foo
3670 = make_location (caret: linemap_position_for_column (line_table, 1),
3671 start: linemap_position_for_column (line_table, 1),
3672 finish: linemap_position_for_column (line_table, 3));
3673 location_t bar
3674 = make_location (caret: linemap_position_for_column (line_table, 7),
3675 start: linemap_position_for_column (line_table, 7),
3676 finish: linemap_position_for_column (line_table, 9));
3677 location_t field
3678 = make_location (caret: linemap_position_for_column (line_table, 11),
3679 start: linemap_position_for_column (line_table, 11),
3680 finish: linemap_position_for_column (line_table, 15));
3681
3682 /* Example where all the labels fit on one line. */
3683 {
3684 text_range_label label0 ("0");
3685 text_range_label label1 ("1");
3686 text_range_label label2 ("2");
3687 gcc_rich_location richloc (foo, &label0);
3688 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
3689 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
3690
3691 {
3692 test_diagnostic_context dc;
3693 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3694 ASSERT_STREQ (" foo = bar.field;\n"
3695 " ^~~ ~~~ ~~~~~\n"
3696 " | | |\n"
3697 " 0 1 2\n",
3698 pp_formatted_text (dc.printer));
3699 }
3700
3701 /* Verify that we can disable label-printing. */
3702 {
3703 test_diagnostic_context dc;
3704 dc.m_source_printing.show_labels_p = false;
3705 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3706 ASSERT_STREQ (" foo = bar.field;\n"
3707 " ^~~ ~~~ ~~~~~\n",
3708 pp_formatted_text (dc.printer));
3709 }
3710 }
3711
3712 /* Example where the labels need extra lines. */
3713 {
3714 text_range_label label0 ("label 0");
3715 text_range_label label1 ("label 1");
3716 text_range_label label2 ("label 2");
3717 gcc_rich_location richloc (foo, &label0);
3718 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
3719 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
3720
3721 test_diagnostic_context dc;
3722 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3723 ASSERT_STREQ (" foo = bar.field;\n"
3724 " ^~~ ~~~ ~~~~~\n"
3725 " | | |\n"
3726 " | | label 2\n"
3727 " | label 1\n"
3728 " label 0\n",
3729 pp_formatted_text (dc.printer));
3730 }
3731
3732 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3733 but label 1 just touches label 2. */
3734 {
3735 text_range_label label0 ("aaaaa");
3736 text_range_label label1 ("bbbb");
3737 text_range_label label2 ("c");
3738 gcc_rich_location richloc (foo, &label0);
3739 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
3740 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
3741
3742 test_diagnostic_context dc;
3743 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3744 ASSERT_STREQ (" foo = bar.field;\n"
3745 " ^~~ ~~~ ~~~~~\n"
3746 " | | |\n"
3747 " | | c\n"
3748 " aaaaa bbbb\n",
3749 pp_formatted_text (dc.printer));
3750 }
3751
3752 /* Example of out-of-order ranges (thus requiring a sort). */
3753 {
3754 text_range_label label0 ("0");
3755 text_range_label label1 ("1");
3756 text_range_label label2 ("2");
3757 gcc_rich_location richloc (field, &label0);
3758 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
3759 richloc.add_range (loc: foo, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
3760
3761 test_diagnostic_context dc;
3762 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3763 ASSERT_STREQ (" foo = bar.field;\n"
3764 " ~~~ ~~~ ^~~~~\n"
3765 " | | |\n"
3766 " 2 1 0\n",
3767 pp_formatted_text (dc.printer));
3768 }
3769
3770 /* Ensure we don't ICE if multiple ranges with labels are on
3771 the same point. */
3772 {
3773 text_range_label label0 ("label 0");
3774 text_range_label label1 ("label 1");
3775 text_range_label label2 ("label 2");
3776 gcc_rich_location richloc (bar, &label0);
3777 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
3778 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
3779
3780 test_diagnostic_context dc;
3781 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3782 ASSERT_STREQ (" foo = bar.field;\n"
3783 " ^~~\n"
3784 " |\n"
3785 " label 0\n"
3786 " label 1\n"
3787 " label 2\n",
3788 pp_formatted_text (dc.printer));
3789 }
3790
3791 /* Example of out-of-order ranges (thus requiring a sort), where
3792 they overlap, and there are multiple ranges on the same point. */
3793 {
3794 text_range_label label_0a ("label 0a");
3795 text_range_label label_1a ("label 1a");
3796 text_range_label label_2a ("label 2a");
3797 text_range_label label_0b ("label 0b");
3798 text_range_label label_1b ("label 1b");
3799 text_range_label label_2b ("label 2b");
3800 text_range_label label_0c ("label 0c");
3801 text_range_label label_1c ("label 1c");
3802 text_range_label label_2c ("label 2c");
3803 gcc_rich_location richloc (field, &label_0a);
3804 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_1a);
3805 richloc.add_range (loc: foo, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_2a);
3806
3807 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_0b);
3808 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_1b);
3809 richloc.add_range (loc: foo, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_2b);
3810
3811 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_0c);
3812 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_1c);
3813 richloc.add_range (loc: foo, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label_2c);
3814
3815 test_diagnostic_context dc;
3816 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3817 ASSERT_STREQ (" foo = bar.field;\n"
3818 " ~~~ ~~~ ^~~~~\n"
3819 " | | |\n"
3820 " | | label 0a\n"
3821 " | | label 0b\n"
3822 " | | label 0c\n"
3823 " | label 1a\n"
3824 " | label 1b\n"
3825 " | label 1c\n"
3826 " label 2a\n"
3827 " label 2b\n"
3828 " label 2c\n",
3829 pp_formatted_text (dc.printer));
3830 }
3831
3832 /* Verify that a NULL result from range_label::get_text is
3833 handled gracefully. */
3834 {
3835 text_range_label label (NULL);
3836 gcc_rich_location richloc (bar, &label);
3837
3838 test_diagnostic_context dc;
3839 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3840 ASSERT_STREQ (" foo = bar.field;\n"
3841 " ^~~\n",
3842 pp_formatted_text (dc.printer));
3843 }
3844
3845 /* TODO: example of formatted printing (needs to be in
3846 gcc-rich-location.cc due to Makefile.in issues). */
3847}
3848
3849/* Run the various one-liner tests. */
3850
3851static void
3852test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3853{
3854 /* Create a tempfile and write some text to it.
3855 ....................0000000001111111.
3856 ....................1234567890123456. */
3857 const char *content = "foo = bar.field;\n";
3858 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3859 line_table_test ltt (case_);
3860
3861 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
3862
3863 location_t line_end = linemap_position_for_column (line_table, 16);
3864
3865 /* Don't attempt to run the tests if column data might be unavailable. */
3866 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3867 return;
3868
3869 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3870 ASSERT_EQ (1, LOCATION_LINE (line_end));
3871 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3872
3873 test_one_liner_simple_caret ();
3874 test_one_liner_no_column ();
3875 test_one_liner_caret_and_range ();
3876 test_one_liner_multiple_carets_and_ranges ();
3877 test_one_liner_fixit_insert_before ();
3878 test_one_liner_fixit_insert_after ();
3879 test_one_liner_fixit_remove ();
3880 test_one_liner_fixit_replace ();
3881 test_one_liner_fixit_replace_non_equal_range ();
3882 test_one_liner_fixit_replace_equal_secondary_range ();
3883 test_one_liner_fixit_validation_adhoc_locations ();
3884 test_one_liner_many_fixits_1 ();
3885 test_one_liner_many_fixits_2 ();
3886 test_one_liner_labels ();
3887}
3888
3889/* Version of all one-liner tests exercising multibyte awareness. For
3890 simplicity we stick to using two multibyte characters in the test, U+1F602
3891 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3892 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3893 below asserts would be easier to read if we used UTF-8 directly in the
3894 string constants, but it seems better not to demand the host compiler
3895 support this, when it isn't otherwise necessary. Instead, whenever an
3896 extended character appears in a string, we put a line break after it so that
3897 all succeeding characters can appear visually at the correct display column.
3898
3899 All of these work on the following 1-line source file:
3900
3901 .0000000001111111111222222 display
3902 .1234567890123456789012345 columns
3903 "SS_foo = P_bar.SS_fieldP;\n"
3904 .0000000111111111222222223 byte
3905 .1356789012456789134567891 columns
3906
3907 which is set up by test_diagnostic_show_locus_one_liner and calls
3908 them. Here SS represents the two display columns for the U+1F602 emoji and
3909 P represents the one display column for the U+03C0 pi symbol. */
3910
3911/* Just a caret. */
3912
3913static void
3914test_one_liner_simple_caret_utf8 ()
3915{
3916 test_diagnostic_context dc;
3917 location_t caret = linemap_position_for_column (line_table, 18);
3918 rich_location richloc (line_table, caret);
3919 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3920 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3921 "_foo = \xcf\x80"
3922 "_bar.\xf0\x9f\x98\x82"
3923 "_field\xcf\x80"
3924 ";\n"
3925 " ^\n",
3926 pp_formatted_text (dc.printer));
3927}
3928
3929/* Caret and range. */
3930static void
3931test_one_liner_caret_and_range_utf8 ()
3932{
3933 test_diagnostic_context dc;
3934 location_t caret = linemap_position_for_column (line_table, 18);
3935 location_t start = linemap_position_for_column (line_table, 12);
3936 location_t finish = linemap_position_for_column (line_table, 30);
3937 location_t loc = make_location (caret, start, finish);
3938 rich_location richloc (line_table, loc);
3939 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3940 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3941 "_foo = \xcf\x80"
3942 "_bar.\xf0\x9f\x98\x82"
3943 "_field\xcf\x80"
3944 ";\n"
3945 " ~~~~~^~~~~~~~~~\n",
3946 pp_formatted_text (dc.printer));
3947}
3948
3949/* Multiple ranges and carets. */
3950
3951static void
3952test_one_liner_multiple_carets_and_ranges_utf8 ()
3953{
3954 test_diagnostic_context dc;
3955 location_t foo
3956 = make_location (caret: linemap_position_for_column (line_table, 7),
3957 start: linemap_position_for_column (line_table, 1),
3958 finish: linemap_position_for_column (line_table, 8));
3959 dc.m_source_printing.caret_chars[0] = 'A';
3960
3961 location_t bar
3962 = make_location (caret: linemap_position_for_column (line_table, 16),
3963 start: linemap_position_for_column (line_table, 12),
3964 finish: linemap_position_for_column (line_table, 17));
3965 dc.m_source_printing.caret_chars[1] = 'B';
3966
3967 location_t field
3968 = make_location (caret: linemap_position_for_column (line_table, 26),
3969 start: linemap_position_for_column (line_table, 19),
3970 finish: linemap_position_for_column (line_table, 30));
3971 dc.m_source_printing.caret_chars[2] = 'C';
3972 rich_location richloc (line_table, foo);
3973 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITH_CARET);
3974 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITH_CARET);
3975 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3976 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3977 "_foo = \xcf\x80"
3978 "_bar.\xf0\x9f\x98\x82"
3979 "_field\xcf\x80"
3980 ";\n"
3981 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3982 pp_formatted_text (dc.printer));
3983}
3984
3985/* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3986
3987static void
3988test_one_liner_fixit_insert_before_utf8 ()
3989{
3990 test_diagnostic_context dc;
3991 location_t caret = linemap_position_for_column (line_table, 12);
3992 rich_location richloc (line_table, caret);
3993 richloc.add_fixit_insert_before (new_content: "&");
3994 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
3995 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3996 "_foo = \xcf\x80"
3997 "_bar.\xf0\x9f\x98\x82"
3998 "_field\xcf\x80"
3999 ";\n"
4000 " ^\n"
4001 " &\n",
4002 pp_formatted_text (dc.printer));
4003}
4004
4005/* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
4006
4007static void
4008test_one_liner_fixit_insert_after_utf8 ()
4009{
4010 test_diagnostic_context dc;
4011 location_t start = linemap_position_for_column (line_table, 1);
4012 location_t finish = linemap_position_for_column (line_table, 8);
4013 location_t foo = make_location (caret: start, start, finish);
4014 rich_location richloc (line_table, foo);
4015 richloc.add_fixit_insert_after (new_content: "[0]");
4016 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4017 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4018 "_foo = \xcf\x80"
4019 "_bar.\xf0\x9f\x98\x82"
4020 "_field\xcf\x80"
4021 ";\n"
4022 " ^~~~~~\n"
4023 " [0]\n",
4024 pp_formatted_text (dc.printer));
4025}
4026
4027/* Removal fix-it hint: removal of the ".SS_fieldP". */
4028
4029static void
4030test_one_liner_fixit_remove_utf8 ()
4031{
4032 test_diagnostic_context dc;
4033 location_t start = linemap_position_for_column (line_table, 18);
4034 location_t finish = linemap_position_for_column (line_table, 30);
4035 location_t dot = make_location (caret: start, start, finish);
4036 rich_location richloc (line_table, dot);
4037 richloc.add_fixit_remove ();
4038 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4039 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4040 "_foo = \xcf\x80"
4041 "_bar.\xf0\x9f\x98\x82"
4042 "_field\xcf\x80"
4043 ";\n"
4044 " ^~~~~~~~~~\n"
4045 " ----------\n",
4046 pp_formatted_text (dc.printer));
4047}
4048
4049/* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4050
4051static void
4052test_one_liner_fixit_replace_utf8 ()
4053{
4054 test_diagnostic_context dc;
4055 location_t start = linemap_position_for_column (line_table, 19);
4056 location_t finish = linemap_position_for_column (line_table, 30);
4057 location_t field = make_location (caret: start, start, finish);
4058 rich_location richloc (line_table, field);
4059 richloc.add_fixit_replace (new_content: "m_\xf0\x9f\x98\x82_field\xcf\x80");
4060 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4061 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4062 "_foo = \xcf\x80"
4063 "_bar.\xf0\x9f\x98\x82"
4064 "_field\xcf\x80"
4065 ";\n"
4066 " ^~~~~~~~~\n"
4067 " m_\xf0\x9f\x98\x82"
4068 "_field\xcf\x80\n",
4069 pp_formatted_text (dc.printer));
4070}
4071
4072/* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4073 but where the caret was elsewhere. */
4074
4075static void
4076test_one_liner_fixit_replace_non_equal_range_utf8 ()
4077{
4078 test_diagnostic_context dc;
4079 location_t equals = linemap_position_for_column (line_table, 10);
4080 location_t start = linemap_position_for_column (line_table, 19);
4081 location_t finish = linemap_position_for_column (line_table, 30);
4082 rich_location richloc (line_table, equals);
4083 source_range range;
4084 range.m_start = start;
4085 range.m_finish = finish;
4086 richloc.add_fixit_replace (src_range: range, new_content: "m_\xf0\x9f\x98\x82_field\xcf\x80");
4087 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4088 /* The replacement range is not indicated in the annotation line, so
4089 it should be indicated via an additional underline. */
4090 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4091 "_foo = \xcf\x80"
4092 "_bar.\xf0\x9f\x98\x82"
4093 "_field\xcf\x80"
4094 ";\n"
4095 " ^\n"
4096 " ---------\n"
4097 " m_\xf0\x9f\x98\x82"
4098 "_field\xcf\x80\n",
4099 pp_formatted_text (dc.printer));
4100}
4101
4102/* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4103 where the caret was elsewhere, but where a secondary range
4104 exactly covers "field". */
4105
4106static void
4107test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4108{
4109 test_diagnostic_context dc;
4110 location_t equals = linemap_position_for_column (line_table, 10);
4111 location_t start = linemap_position_for_column (line_table, 19);
4112 location_t finish = linemap_position_for_column (line_table, 30);
4113 rich_location richloc (line_table, equals);
4114 location_t field = make_location (caret: start, start, finish);
4115 richloc.add_range (loc: field);
4116 richloc.add_fixit_replace (where: field, new_content: "m_\xf0\x9f\x98\x82_field\xcf\x80");
4117 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4118 /* The replacement range is indicated in the annotation line,
4119 so it shouldn't be indicated via an additional underline. */
4120 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4121 "_foo = \xcf\x80"
4122 "_bar.\xf0\x9f\x98\x82"
4123 "_field\xcf\x80"
4124 ";\n"
4125 " ^ ~~~~~~~~~\n"
4126 " m_\xf0\x9f\x98\x82"
4127 "_field\xcf\x80\n",
4128 pp_formatted_text (dc.printer));
4129}
4130
4131/* Verify that we can use ad-hoc locations when adding fixits to a
4132 rich_location. */
4133
4134static void
4135test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4136{
4137 /* Generate a range that's too long to be packed, so must
4138 be stored as an ad-hoc location (given the defaults
4139 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4140 const location_t c12 = linemap_position_for_column (line_table, 12);
4141 const location_t c52 = linemap_position_for_column (line_table, 52);
4142 const location_t loc = make_location (caret: c12, start: c12, finish: c52);
4143
4144 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4145 return;
4146
4147 ASSERT_TRUE (IS_ADHOC_LOC (loc));
4148
4149 /* Insert. */
4150 {
4151 rich_location richloc (line_table, loc);
4152 richloc.add_fixit_insert_before (where: loc, new_content: "test");
4153 /* It should not have been discarded by the validator. */
4154 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4155
4156 test_diagnostic_context dc;
4157 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4158 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4159 "_foo = \xcf\x80"
4160 "_bar.\xf0\x9f\x98\x82"
4161 "_field\xcf\x80"
4162 ";\n"
4163 " ^~~~~~~~~~~~~~~~ \n"
4164 " test\n",
4165 pp_formatted_text (dc.printer));
4166 }
4167
4168 /* Remove. */
4169 {
4170 rich_location richloc (line_table, loc);
4171 source_range range = source_range::from_locations (start: loc, finish: c52);
4172 richloc.add_fixit_remove (src_range: range);
4173 /* It should not have been discarded by the validator. */
4174 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4175
4176 test_diagnostic_context dc;
4177 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4178 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4179 "_foo = \xcf\x80"
4180 "_bar.\xf0\x9f\x98\x82"
4181 "_field\xcf\x80"
4182 ";\n"
4183 " ^~~~~~~~~~~~~~~~ \n"
4184 " -------------------------------------\n",
4185 pp_formatted_text (dc.printer));
4186 }
4187
4188 /* Replace. */
4189 {
4190 rich_location richloc (line_table, loc);
4191 source_range range = source_range::from_locations (start: loc, finish: c52);
4192 richloc.add_fixit_replace (src_range: range, new_content: "test");
4193 /* It should not have been discarded by the validator. */
4194 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4195
4196 test_diagnostic_context dc;
4197 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4198 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4199 "_foo = \xcf\x80"
4200 "_bar.\xf0\x9f\x98\x82"
4201 "_field\xcf\x80"
4202 ";\n"
4203 " ^~~~~~~~~~~~~~~~ \n"
4204 " test\n",
4205 pp_formatted_text (dc.printer));
4206 }
4207}
4208
4209/* Test of consolidating insertions at the same location. */
4210
4211static void
4212test_one_liner_many_fixits_1_utf8 ()
4213{
4214 test_diagnostic_context dc;
4215 location_t equals = linemap_position_for_column (line_table, 10);
4216 rich_location richloc (line_table, equals);
4217 for (int i = 0; i < 19; i++)
4218 richloc.add_fixit_insert_before (new_content: i & 1 ? "@" : "\xcf\x80");
4219 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4220 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4221 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4222 "_foo = \xcf\x80"
4223 "_bar.\xf0\x9f\x98\x82"
4224 "_field\xcf\x80"
4225 ";\n"
4226 " ^\n"
4227 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4228 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4229 pp_formatted_text (dc.printer));
4230}
4231
4232/* Ensure that we can add an arbitrary number of fix-it hints to a
4233 rich_location, even if they are not consolidated. */
4234
4235static void
4236test_one_liner_many_fixits_2_utf8 ()
4237{
4238 test_diagnostic_context dc;
4239 location_t equals = linemap_position_for_column (line_table, 10);
4240 rich_location richloc (line_table, equals);
4241 const int nlocs = 19;
4242 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4243 34, 36, 38, 40, 42, 44};
4244 for (int i = 0; i != nlocs; ++i)
4245 {
4246 location_t loc = linemap_position_for_column (line_table, locs[i]);
4247 richloc.add_fixit_insert_before (where: loc, new_content: i & 1 ? "@" : "\xcf\x80");
4248 }
4249
4250 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4251 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4252 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4253 "_foo = \xcf\x80"
4254 "_bar.\xf0\x9f\x98\x82"
4255 "_field\xcf\x80"
4256 ";\n"
4257 " ^\n"
4258 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4259 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4260 pp_formatted_text (dc.printer));
4261}
4262
4263/* Test of labeling the ranges within a rich_location. */
4264
4265static void
4266test_one_liner_labels_utf8 ()
4267{
4268 location_t foo
4269 = make_location (caret: linemap_position_for_column (line_table, 1),
4270 start: linemap_position_for_column (line_table, 1),
4271 finish: linemap_position_for_column (line_table, 8));
4272 location_t bar
4273 = make_location (caret: linemap_position_for_column (line_table, 12),
4274 start: linemap_position_for_column (line_table, 12),
4275 finish: linemap_position_for_column (line_table, 17));
4276 location_t field
4277 = make_location (caret: linemap_position_for_column (line_table, 19),
4278 start: linemap_position_for_column (line_table, 19),
4279 finish: linemap_position_for_column (line_table, 30));
4280
4281 /* Example where all the labels fit on one line. */
4282 {
4283 /* These three labels contain multibyte characters such that their byte
4284 lengths are respectively (12, 10, 18), but their display widths are only
4285 (6, 5, 9). All three fit on the line when considering the display
4286 widths, but not when considering the byte widths, so verify that we do
4287 indeed put them all on one line. */
4288 text_range_label label0
4289 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4290 text_range_label label1
4291 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4292 text_range_label label2
4293 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4294 "\xcf\x80");
4295 gcc_rich_location richloc (foo, &label0);
4296 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
4297 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
4298
4299 {
4300 test_diagnostic_context dc;
4301 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4302 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4303 "_foo = \xcf\x80"
4304 "_bar.\xf0\x9f\x98\x82"
4305 "_field\xcf\x80"
4306 ";\n"
4307 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4308 " | | |\n"
4309 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4310 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4311 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4312 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4313 pp_formatted_text (dc.printer));
4314 }
4315
4316 }
4317
4318 /* Example where the labels need extra lines. */
4319 {
4320 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4321 text_range_label label1 ("label 1\xcf\x80");
4322 text_range_label label2 ("label 2\xcf\x80");
4323 gcc_rich_location richloc (foo, &label0);
4324 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
4325 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
4326
4327 test_diagnostic_context dc;
4328 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4329
4330 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4331 "_foo = \xcf\x80"
4332 "_bar.\xf0\x9f\x98\x82"
4333 "_field\xcf\x80"
4334 ";\n"
4335 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4336 " | | |\n"
4337 " | | label 2\xcf\x80\n"
4338 " | label 1\xcf\x80\n"
4339 " label 0\xf0\x9f\x98\x82\n",
4340 pp_formatted_text (dc.printer));
4341 }
4342
4343 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4344 but label 1 just touches label 2. */
4345 {
4346 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4347 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4348 text_range_label label2 ("c");
4349 gcc_rich_location richloc (foo, &label0);
4350 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
4351 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
4352
4353 test_diagnostic_context dc;
4354 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4355 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4356 "_foo = \xcf\x80"
4357 "_bar.\xf0\x9f\x98\x82"
4358 "_field\xcf\x80"
4359 ";\n"
4360 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4361 " | | |\n"
4362 " | | c\n"
4363 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4364 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4365 pp_formatted_text (dc.printer));
4366 }
4367
4368 /* Example of escaping the source lines. */
4369 {
4370 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4371 text_range_label label1 ("label 1\xcf\x80");
4372 text_range_label label2 ("label 2\xcf\x80");
4373 gcc_rich_location richloc (foo, &label0);
4374 richloc.add_range (loc: bar, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label1);
4375 richloc.add_range (loc: field, range_display_kind: SHOW_RANGE_WITHOUT_CARET, label: &label2);
4376 richloc.set_escape_on_output (true);
4377
4378 {
4379 test_diagnostic_context dc;
4380 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
4381 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4382 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4383 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4384 " | | |\n"
4385 " | | label 2\xcf\x80\n"
4386 " | label 1\xcf\x80\n"
4387 " label 0\xf0\x9f\x98\x82\n",
4388 pp_formatted_text (dc.printer));
4389 }
4390 {
4391 test_diagnostic_context dc;
4392 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
4393 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4394 ASSERT_STREQ
4395 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4396 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4397 " | | |\n"
4398 " | | label 2\xcf\x80\n"
4399 " | label 1\xcf\x80\n"
4400 " label 0\xf0\x9f\x98\x82\n",
4401 pp_formatted_text (dc.printer));
4402 }
4403 }
4404}
4405
4406/* Make sure that colorization codes don't interrupt a multibyte
4407 sequence, which would corrupt it. */
4408static void
4409test_one_liner_colorized_utf8 ()
4410{
4411 test_diagnostic_context dc;
4412 dc.m_source_printing.colorize_source_p = true;
4413 diagnostic_color_init (context: &dc, value: DIAGNOSTICS_COLOR_YES);
4414 const location_t pi = linemap_position_for_column (line_table, 12);
4415 rich_location richloc (line_table, pi);
4416 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4417
4418 /* In order to avoid having the test depend on exactly how the colorization
4419 was effected, just confirm there are two pi characters in the output. */
4420 const char *result = pp_formatted_text (dc.printer);
4421 const char *null_term = result + strlen (s: result);
4422 const char *first_pi = strstr (haystack: result, needle: "\xcf\x80");
4423 ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4424 ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4425}
4426
4427/* Run the various one-liner tests. */
4428
4429static void
4430test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4431{
4432 /* Create a tempfile and write some text to it. */
4433 const char *content
4434 /* Display columns.
4435 0000000000000000000000011111111111111111111111111111112222222222222
4436 1111111122222222345678900000000123456666666677777777890123444444445 */
4437 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4438 /* 0000000000000000000001111111111111111111222222222222222222222233333
4439 1111222233334444567890122223333456789999000011112222345678999900001
4440 Byte columns. */
4441 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4442 file_cache fc;
4443 line_table_test ltt (case_);
4444
4445 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
4446
4447 location_t line_end = linemap_position_for_column (line_table, 31);
4448
4449 /* Don't attempt to run the tests if column data might be unavailable. */
4450 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4451 return;
4452
4453 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
4454 ASSERT_EQ (1, LOCATION_LINE (line_end));
4455 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4456
4457 char_span lspan = fc.get_source_line (file_path: tmp.get_filename (), line: 1);
4458 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4459 def_policy ()));
4460 ASSERT_EQ (25, location_compute_display_column (fc,
4461 expand_location (line_end),
4462 def_policy ()));
4463
4464 test_one_liner_simple_caret_utf8 ();
4465 test_one_liner_caret_and_range_utf8 ();
4466 test_one_liner_multiple_carets_and_ranges_utf8 ();
4467 test_one_liner_fixit_insert_before_utf8 ();
4468 test_one_liner_fixit_insert_after_utf8 ();
4469 test_one_liner_fixit_remove_utf8 ();
4470 test_one_liner_fixit_replace_utf8 ();
4471 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4472 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4473 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4474 test_one_liner_many_fixits_1_utf8 ();
4475 test_one_liner_many_fixits_2_utf8 ();
4476 test_one_liner_labels_utf8 ();
4477 test_one_liner_colorized_utf8 ();
4478}
4479
4480/* Verify that gcc_rich_location::add_location_if_nearby works. */
4481
4482static void
4483test_add_location_if_nearby (const line_table_case &case_)
4484{
4485 /* Create a tempfile and write some text to it.
4486 ...000000000111111111122222222223333333333.
4487 ...123456789012345678901234567890123456789. */
4488 const char *content
4489 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4490 "struct different_line\n" /* line 2. */
4491 "{\n" /* line 3. */
4492 " double x;\n" /* line 4. */
4493 " double y;\n" /* line 5. */
4494 ";\n"); /* line 6. */
4495 temp_source_file tmp (SELFTEST_LOCATION, ".c", content,
4496
4497 /* gcc_rich_location::add_location_if_nearby implicitly
4498 uses global_dc's file_cache, so we need to evict
4499 tmp when we're done. */
4500 &global_dc->get_file_cache ());
4501 line_table_test ltt (case_);
4502
4503 const line_map_ordinary *ord_map
4504 = linemap_check_ordinary (map: linemap_add (line_table, LC_ENTER, sysp: false,
4505 to_file: tmp.get_filename (), to_line: 0));
4506
4507 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
4508
4509 const location_t final_line_end
4510 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 7);
4511
4512 /* Don't attempt to run the tests if column data might be unavailable. */
4513 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4514 return;
4515
4516 /* Test of add_location_if_nearby on the same line as the
4517 primary location. */
4518 {
4519 const location_t missing_close_brace_1_39
4520 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 39);
4521 const location_t matching_open_brace_1_18
4522 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 18);
4523 gcc_rich_location richloc (missing_close_brace_1_39);
4524 bool added = richloc.add_location_if_nearby (loc: matching_open_brace_1_18);
4525 ASSERT_TRUE (added);
4526 ASSERT_EQ (2, richloc.get_num_locations ());
4527 test_diagnostic_context dc;
4528 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4529 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4530 " ~ ^\n",
4531 pp_formatted_text (dc.printer));
4532 }
4533
4534 /* Test of add_location_if_nearby on a different line to the
4535 primary location. */
4536 {
4537 const location_t missing_close_brace_6_1
4538 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 1);
4539 const location_t matching_open_brace_3_1
4540 = linemap_position_for_line_and_column (set: line_table, ord_map, 3, 1);
4541 gcc_rich_location richloc (missing_close_brace_6_1);
4542 bool added = richloc.add_location_if_nearby (loc: matching_open_brace_3_1);
4543 ASSERT_FALSE (added);
4544 ASSERT_EQ (1, richloc.get_num_locations ());
4545 }
4546}
4547
4548/* Verify that we print fixits even if they only affect lines
4549 outside those covered by the ranges in the rich_location. */
4550
4551static void
4552test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
4553{
4554 /* Create a tempfile and write some text to it.
4555 ...000000000111111111122222222223333333333.
4556 ...123456789012345678901234567890123456789. */
4557 const char *content
4558 = ("struct point { double x; double y; };\n" /* line 1. */
4559 "struct point origin = {x: 0.0,\n" /* line 2. */
4560 " y\n" /* line 3. */
4561 "\n" /* line 4. */
4562 "\n" /* line 5. */
4563 " : 0.0};\n"); /* line 6. */
4564 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4565 line_table_test ltt (case_);
4566
4567 const line_map_ordinary *ord_map
4568 = linemap_check_ordinary (map: linemap_add (line_table, LC_ENTER, sysp: false,
4569 to_file: tmp.get_filename (), to_line: 0));
4570
4571 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
4572
4573 const location_t final_line_end
4574 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 36);
4575
4576 /* Don't attempt to run the tests if column data might be unavailable. */
4577 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4578 return;
4579
4580 /* A pair of tests for modernizing the initializers to C99-style. */
4581
4582 /* The one-liner case (line 2). */
4583 {
4584 test_diagnostic_context dc;
4585 const location_t x
4586 = linemap_position_for_line_and_column (set: line_table, ord_map, 2, 24);
4587 const location_t colon
4588 = linemap_position_for_line_and_column (set: line_table, ord_map, 2, 25);
4589 rich_location richloc (line_table, colon);
4590 richloc.add_fixit_insert_before (where: x, new_content: ".");
4591 richloc.add_fixit_replace (where: colon, new_content: "=");
4592 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4593 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4594 " ^\n"
4595 " .=\n",
4596 pp_formatted_text (dc.printer));
4597 }
4598
4599 /* The multiline case. The caret for the rich_location is on line 6;
4600 verify that insertion fixit on line 3 is still printed (and that
4601 span starts are printed due to the gap between the span at line 3
4602 and that at line 6). */
4603 {
4604 test_diagnostic_context dc;
4605 const location_t y
4606 = linemap_position_for_line_and_column (set: line_table, ord_map, 3, 24);
4607 const location_t colon
4608 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 25);
4609 rich_location richloc (line_table, colon);
4610 richloc.add_fixit_insert_before (where: y, new_content: ".");
4611 richloc.add_fixit_replace (where: colon, new_content: "=");
4612 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4613 ASSERT_STREQ ("FILENAME:3:24:\n"
4614 " y\n"
4615 " .\n"
4616 "FILENAME:6:25:\n"
4617 " : 0.0};\n"
4618 " ^\n"
4619 " =\n",
4620 pp_formatted_text (dc.printer));
4621 }
4622
4623 /* As above, but verify the behavior of multiple line spans
4624 with line-numbering enabled. */
4625 {
4626 const location_t y
4627 = linemap_position_for_line_and_column (set: line_table, ord_map, 3, 24);
4628 const location_t colon
4629 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 25);
4630 rich_location richloc (line_table, colon);
4631 richloc.add_fixit_insert_before (where: y, new_content: ".");
4632 richloc.add_fixit_replace (where: colon, new_content: "=");
4633 test_diagnostic_context dc;
4634 dc.m_source_printing.show_line_numbers_p = true;
4635 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4636 ASSERT_STREQ (" 3 | y\n"
4637 " | .\n"
4638 "......\n"
4639 " 6 | : 0.0};\n"
4640 " | ^\n"
4641 " | =\n",
4642 pp_formatted_text (dc.printer));
4643 }
4644}
4645
4646
4647/* Verify that fix-it hints are appropriately consolidated.
4648
4649 If any fix-it hints in a rich_location involve locations beyond
4650 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4651 the fix-it as a whole, so there should be none.
4652
4653 Otherwise, verify that consecutive "replace" and "remove" fix-its
4654 are merged, and that other fix-its remain separate. */
4655
4656static void
4657test_fixit_consolidation (const line_table_case &case_)
4658{
4659 line_table_test ltt (case_);
4660
4661 linemap_add (line_table, LC_ENTER, sysp: false, to_file: "test.c", to_line: 1);
4662
4663 const location_t c10 = linemap_position_for_column (line_table, 10);
4664 const location_t c15 = linemap_position_for_column (line_table, 15);
4665 const location_t c16 = linemap_position_for_column (line_table, 16);
4666 const location_t c17 = linemap_position_for_column (line_table, 17);
4667 const location_t c20 = linemap_position_for_column (line_table, 20);
4668 const location_t c21 = linemap_position_for_column (line_table, 21);
4669 const location_t caret = c10;
4670
4671 /* Insert + insert. */
4672 {
4673 rich_location richloc (line_table, caret);
4674 richloc.add_fixit_insert_before (where: c10, new_content: "foo");
4675 richloc.add_fixit_insert_before (where: c15, new_content: "bar");
4676
4677 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4678 /* Bogus column info for 2nd fixit, so no fixits. */
4679 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4680 else
4681 /* They should not have been merged. */
4682 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4683 }
4684
4685 /* Insert + replace. */
4686 {
4687 rich_location richloc (line_table, caret);
4688 richloc.add_fixit_insert_before (where: c10, new_content: "foo");
4689 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c15, finish: c17),
4690 new_content: "bar");
4691
4692 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4693 /* Bogus column info for 2nd fixit, so no fixits. */
4694 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4695 else
4696 /* They should not have been merged. */
4697 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4698 }
4699
4700 /* Replace + non-consecutive insert. */
4701 {
4702 rich_location richloc (line_table, caret);
4703 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c10, finish: c15),
4704 new_content: "bar");
4705 richloc.add_fixit_insert_before (where: c17, new_content: "foo");
4706
4707 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4708 /* Bogus column info for 2nd fixit, so no fixits. */
4709 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4710 else
4711 /* They should not have been merged. */
4712 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4713 }
4714
4715 /* Replace + non-consecutive replace. */
4716 {
4717 rich_location richloc (line_table, caret);
4718 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c10, finish: c15),
4719 new_content: "foo");
4720 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c17, finish: c20),
4721 new_content: "bar");
4722
4723 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4724 /* Bogus column info for 2nd fixit, so no fixits. */
4725 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4726 else
4727 /* They should not have been merged. */
4728 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4729 }
4730
4731 /* Replace + consecutive replace. */
4732 {
4733 rich_location richloc (line_table, caret);
4734 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c10, finish: c15),
4735 new_content: "foo");
4736 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c16, finish: c20),
4737 new_content: "bar");
4738
4739 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4740 /* Bogus column info for 2nd fixit, so no fixits. */
4741 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4742 else
4743 {
4744 /* They should have been merged into a single "replace". */
4745 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4746 const fixit_hint *hint = richloc.get_fixit_hint (idx: 0);
4747 ASSERT_STREQ ("foobar", hint->get_string ());
4748 ASSERT_EQ (c10, hint->get_start_loc ());
4749 ASSERT_EQ (c21, hint->get_next_loc ());
4750 }
4751 }
4752
4753 /* Replace + consecutive removal. */
4754 {
4755 rich_location richloc (line_table, caret);
4756 richloc.add_fixit_replace (src_range: source_range::from_locations (start: c10, finish: c15),
4757 new_content: "foo");
4758 richloc.add_fixit_remove (src_range: source_range::from_locations (start: c16, finish: c20));
4759
4760 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4761 /* Bogus column info for 2nd fixit, so no fixits. */
4762 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4763 else
4764 {
4765 /* They should have been merged into a single replace, with the
4766 range extended to cover that of the removal. */
4767 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4768 const fixit_hint *hint = richloc.get_fixit_hint (idx: 0);
4769 ASSERT_STREQ ("foo", hint->get_string ());
4770 ASSERT_EQ (c10, hint->get_start_loc ());
4771 ASSERT_EQ (c21, hint->get_next_loc ());
4772 }
4773 }
4774
4775 /* Consecutive removals. */
4776 {
4777 rich_location richloc (line_table, caret);
4778 richloc.add_fixit_remove (src_range: source_range::from_locations (start: c10, finish: c15));
4779 richloc.add_fixit_remove (src_range: source_range::from_locations (start: c16, finish: c20));
4780
4781 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4782 /* Bogus column info for 2nd fixit, so no fixits. */
4783 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4784 else
4785 {
4786 /* They should have been merged into a single "replace-with-empty". */
4787 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4788 const fixit_hint *hint = richloc.get_fixit_hint (idx: 0);
4789 ASSERT_STREQ ("", hint->get_string ());
4790 ASSERT_EQ (c10, hint->get_start_loc ());
4791 ASSERT_EQ (c21, hint->get_next_loc ());
4792 }
4793 }
4794}
4795
4796/* Verify that the line_corrections machinery correctly prints
4797 overlapping fixit-hints. */
4798
4799static void
4800test_overlapped_fixit_printing (const line_table_case &case_)
4801{
4802 /* Create a tempfile and write some text to it.
4803 ...000000000111111111122222222223333333333.
4804 ...123456789012345678901234567890123456789. */
4805 const char *content
4806 = (" foo *f = (foo *)ptr->field;\n");
4807 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4808 file_cache fc;
4809 line_table_test ltt (case_);
4810
4811 const line_map_ordinary *ord_map
4812 = linemap_check_ordinary (map: linemap_add (line_table, LC_ENTER, sysp: false,
4813 to_file: tmp.get_filename (), to_line: 0));
4814
4815 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
4816
4817 const location_t final_line_end
4818 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 36);
4819
4820 /* Don't attempt to run the tests if column data might be unavailable. */
4821 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4822 return;
4823
4824 /* A test for converting a C-style cast to a C++-style cast. */
4825 const location_t open_paren
4826 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 12);
4827 const location_t close_paren
4828 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 18);
4829 const location_t expr_start
4830 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 19);
4831 const location_t expr_finish
4832 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 28);
4833 const location_t expr = make_location (caret: expr_start, start: expr_start, finish: expr_finish);
4834
4835 /* Various examples of fix-it hints that aren't themselves consolidated,
4836 but for which the *printing* may need consolidation. */
4837
4838 /* Example where 3 fix-it hints are printed as one. */
4839 {
4840 test_diagnostic_context dc;
4841 rich_location richloc (line_table, expr);
4842 richloc.add_fixit_replace (where: open_paren, new_content: "const_cast<");
4843 richloc.add_fixit_replace (where: close_paren, new_content: "> (");
4844 richloc.add_fixit_insert_after (new_content: ")");
4845
4846 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4847 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4848 " ^~~~~~~~~~\n"
4849 " -----------------\n"
4850 " const_cast<foo *> (ptr->field)\n",
4851 pp_formatted_text (dc.printer));
4852
4853 /* Unit-test the line_corrections machinery. */
4854 char_display_policy policy (make_policy (dc, richloc));
4855 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4856 const fixit_hint *hint_0 = richloc.get_fixit_hint (idx: 0);
4857 ASSERT_EQ (column_range (12, 12),
4858 get_affected_range (fc, policy, hint_0, CU_BYTES));
4859 ASSERT_EQ (column_range (12, 12),
4860 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
4861 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
4862 const fixit_hint *hint_1 = richloc.get_fixit_hint (idx: 1);
4863 ASSERT_EQ (column_range (18, 18),
4864 get_affected_range (fc, policy, hint_1, CU_BYTES));
4865 ASSERT_EQ (column_range (18, 18),
4866 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
4867 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
4868 const fixit_hint *hint_2 = richloc.get_fixit_hint (idx: 2);
4869 ASSERT_EQ (column_range (29, 28),
4870 get_affected_range (fc, policy, hint_2, CU_BYTES));
4871 ASSERT_EQ (column_range (29, 28),
4872 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
4873 ASSERT_EQ (column_range (29, 29), get_printed_columns (fc, policy, hint_2));
4874
4875 /* Add each hint in turn to a line_corrections instance,
4876 and verify that they are consolidated into one correction instance
4877 as expected. */
4878 line_corrections lc (fc, policy, tmp.get_filename (), 1);
4879
4880 /* The first replace hint by itself. */
4881 lc.add_hint (hint: hint_0);
4882 ASSERT_EQ (1, lc.m_corrections.length ());
4883 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4884 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4885 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4886 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4887
4888 /* After the second replacement hint, they are printed together
4889 as a replacement (along with the text between them). */
4890 lc.add_hint (hint: hint_1);
4891 ASSERT_EQ (1, lc.m_corrections.length ());
4892 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4893 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4894 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4895 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4896
4897 /* After the final insertion hint, they are all printed together
4898 as a replacement (along with the text between them). */
4899 lc.add_hint (hint: hint_2);
4900 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4901 lc.m_corrections[0]->m_text);
4902 ASSERT_EQ (1, lc.m_corrections.length ());
4903 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4904 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4905 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4906 }
4907
4908 /* Example where two are consolidated during printing. */
4909 {
4910 test_diagnostic_context dc;
4911 rich_location richloc (line_table, expr);
4912 richloc.add_fixit_replace (where: open_paren, new_content: "CAST (");
4913 richloc.add_fixit_replace (where: close_paren, new_content: ") (");
4914 richloc.add_fixit_insert_after (new_content: ")");
4915
4916 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4917 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4918 " ^~~~~~~~~~\n"
4919 " -\n"
4920 " CAST (-\n"
4921 " ) ( )\n",
4922 pp_formatted_text (dc.printer));
4923 }
4924
4925 /* Example where none are consolidated during printing. */
4926 {
4927 test_diagnostic_context dc;
4928 rich_location richloc (line_table, expr);
4929 richloc.add_fixit_replace (where: open_paren, new_content: "CST (");
4930 richloc.add_fixit_replace (where: close_paren, new_content: ") (");
4931 richloc.add_fixit_insert_after (new_content: ")");
4932
4933 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4934 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4935 " ^~~~~~~~~~\n"
4936 " -\n"
4937 " CST ( -\n"
4938 " ) ( )\n",
4939 pp_formatted_text (dc.printer));
4940 }
4941
4942 /* Example of deletion fix-it hints. */
4943 {
4944 test_diagnostic_context dc;
4945 rich_location richloc (line_table, expr);
4946 richloc.add_fixit_insert_before (where: open_paren, new_content: "(bar *)");
4947 source_range victim = {.m_start: open_paren, .m_finish: close_paren};
4948 richloc.add_fixit_remove (src_range: victim);
4949
4950 /* This case is actually handled by fixit-consolidation,
4951 rather than by line_corrections. */
4952 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4953
4954 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4955 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4956 " ^~~~~~~~~~\n"
4957 " -------\n"
4958 " (bar *)\n",
4959 pp_formatted_text (dc.printer));
4960 }
4961
4962 /* Example of deletion fix-it hints that would overlap. */
4963 {
4964 test_diagnostic_context dc;
4965 rich_location richloc (line_table, expr);
4966 richloc.add_fixit_insert_before (where: open_paren, new_content: "(longer *)");
4967 source_range victim = {.m_start: expr_start, .m_finish: expr_finish};
4968 richloc.add_fixit_remove (src_range: victim);
4969
4970 /* These fixits are not consolidated. */
4971 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4972
4973 /* But the corrections are. */
4974 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4975 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4976 " ^~~~~~~~~~\n"
4977 " -----------------\n"
4978 " (longer *)(foo *)\n",
4979 pp_formatted_text (dc.printer));
4980 }
4981
4982 /* Example of insertion fix-it hints that would overlap. */
4983 {
4984 test_diagnostic_context dc;
4985 rich_location richloc (line_table, expr);
4986 richloc.add_fixit_insert_before (where: open_paren, new_content: "LONGER THAN THE CAST");
4987 richloc.add_fixit_insert_after (where: close_paren, new_content: "TEST");
4988
4989 /* The first insertion is long enough that if printed naively,
4990 it would overlap with the second.
4991 Verify that they are printed as a single replacement. */
4992 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
4993 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4994 " ^~~~~~~~~~\n"
4995 " -------\n"
4996 " LONGER THAN THE CAST(foo *)TEST\n",
4997 pp_formatted_text (dc.printer));
4998 }
4999}
5000
5001/* Multibyte-aware version of preceding tests. See comments above
5002 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
5003 characters here. */
5004
5005static void
5006test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
5007{
5008 /* Create a tempfile and write some text to it. */
5009
5010 const char *content
5011 /* Display columns.
5012 00000000000000000000000111111111111111111111111222222222222222223
5013 12344444444555555556789012344444444555555556789012345678999999990 */
5014 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
5015 /* 00000000000000000000011111111111111111111112222222222333333333333
5016 12344445555666677778901234566667777888899990123456789012333344445
5017 Byte columns. */
5018
5019 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
5020 line_table_test ltt (case_);
5021
5022 const line_map_ordinary *ord_map
5023 = linemap_check_ordinary (map: linemap_add (line_table, LC_ENTER, sysp: false,
5024 to_file: tmp.get_filename (), to_line: 0));
5025
5026 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5027
5028 const location_t final_line_end
5029 = linemap_position_for_line_and_column (set: line_table, ord_map, 6, 50);
5030
5031 /* Don't attempt to run the tests if column data might be unavailable. */
5032 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5033 return;
5034
5035 /* A test for converting a C-style cast to a C++-style cast. */
5036 const location_t open_paren
5037 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 14);
5038 const location_t close_paren
5039 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 22);
5040 const location_t expr_start
5041 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 23);
5042 const location_t expr_finish
5043 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 34);
5044 const location_t expr = make_location (caret: expr_start, start: expr_start, finish: expr_finish);
5045
5046 /* Various examples of fix-it hints that aren't themselves consolidated,
5047 but for which the *printing* may need consolidation. */
5048
5049 /* Example where 3 fix-it hints are printed as one. */
5050 {
5051 test_diagnostic_context dc;
5052 file_cache &fc = dc.get_file_cache ();
5053 rich_location richloc (line_table, expr);
5054 richloc.add_fixit_replace (where: open_paren, new_content: "const_cast<");
5055 richloc.add_fixit_replace (where: close_paren, new_content: "> (");
5056 richloc.add_fixit_insert_after (new_content: ")");
5057
5058 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5059 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5060 " *f = (f\xf0\x9f\x98\x82"
5061 " *)ptr->field\xcf\x80"
5062 ";\n"
5063 " ^~~~~~~~~~~\n"
5064 " ------------------\n"
5065 " const_cast<f\xf0\x9f\x98\x82"
5066 " *> (ptr->field\xcf\x80"
5067 ")\n",
5068 pp_formatted_text (dc.printer));
5069
5070 /* Unit-test the line_corrections machinery. */
5071 char_display_policy policy (make_policy (dc, richloc));
5072 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5073 const fixit_hint *hint_0 = richloc.get_fixit_hint (idx: 0);
5074 ASSERT_EQ (column_range (14, 14),
5075 get_affected_range (fc, policy, hint_0, CU_BYTES));
5076 ASSERT_EQ (column_range (12, 12),
5077 get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
5078 ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
5079 const fixit_hint *hint_1 = richloc.get_fixit_hint (idx: 1);
5080 ASSERT_EQ (column_range (22, 22),
5081 get_affected_range (fc, policy, hint_1, CU_BYTES));
5082 ASSERT_EQ (column_range (18, 18),
5083 get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
5084 ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
5085 const fixit_hint *hint_2 = richloc.get_fixit_hint (idx: 2);
5086 ASSERT_EQ (column_range (35, 34),
5087 get_affected_range (fc, policy, hint_2, CU_BYTES));
5088 ASSERT_EQ (column_range (30, 29),
5089 get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
5090 ASSERT_EQ (column_range (30, 30), get_printed_columns (fc, policy, hint_2));
5091
5092 /* Add each hint in turn to a line_corrections instance,
5093 and verify that they are consolidated into one correction instance
5094 as expected. */
5095 line_corrections lc (fc, policy, tmp.get_filename (), 1);
5096
5097 /* The first replace hint by itself. */
5098 lc.add_hint (hint: hint_0);
5099 ASSERT_EQ (1, lc.m_corrections.length ());
5100 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5101 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5102 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5103 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5104
5105 /* After the second replacement hint, they are printed together
5106 as a replacement (along with the text between them). */
5107 lc.add_hint (hint: hint_1);
5108 ASSERT_EQ (1, lc.m_corrections.length ());
5109 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5110 lc.m_corrections[0]->m_text);
5111 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5112 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5113 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5114
5115 /* After the final insertion hint, they are all printed together
5116 as a replacement (along with the text between them). */
5117 lc.add_hint (hint: hint_2);
5118 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5119 lc.m_corrections[0]->m_text);
5120 ASSERT_EQ (1, lc.m_corrections.length ());
5121 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5122 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5123 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5124 }
5125
5126 /* Example where two are consolidated during printing. */
5127 {
5128 test_diagnostic_context dc;
5129 rich_location richloc (line_table, expr);
5130 richloc.add_fixit_replace (where: open_paren, new_content: "CAST (");
5131 richloc.add_fixit_replace (where: close_paren, new_content: ") (");
5132 richloc.add_fixit_insert_after (new_content: ")");
5133
5134 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5135 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5136 " *f = (f\xf0\x9f\x98\x82"
5137 " *)ptr->field\xcf\x80"
5138 ";\n"
5139 " ^~~~~~~~~~~\n"
5140 " -\n"
5141 " CAST (-\n"
5142 " ) ( )\n",
5143 pp_formatted_text (dc.printer));
5144 }
5145
5146 /* Example where none are consolidated during printing. */
5147 {
5148 test_diagnostic_context dc;
5149 rich_location richloc (line_table, expr);
5150 richloc.add_fixit_replace (where: open_paren, new_content: "CST (");
5151 richloc.add_fixit_replace (where: close_paren, new_content: ") (");
5152 richloc.add_fixit_insert_after (new_content: ")");
5153
5154 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5155 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5156 " *f = (f\xf0\x9f\x98\x82"
5157 " *)ptr->field\xcf\x80"
5158 ";\n"
5159 " ^~~~~~~~~~~\n"
5160 " -\n"
5161 " CST ( -\n"
5162 " ) ( )\n",
5163 pp_formatted_text (dc.printer));
5164 }
5165
5166 /* Example of deletion fix-it hints. */
5167 {
5168 test_diagnostic_context dc;
5169 rich_location richloc (line_table, expr);
5170 richloc.add_fixit_insert_before (where: open_paren, new_content: "(bar\xf0\x9f\x98\x82 *)");
5171 source_range victim = {.m_start: open_paren, .m_finish: close_paren};
5172 richloc.add_fixit_remove (src_range: victim);
5173
5174 /* This case is actually handled by fixit-consolidation,
5175 rather than by line_corrections. */
5176 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5177
5178 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5179 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5180 " *f = (f\xf0\x9f\x98\x82"
5181 " *)ptr->field\xcf\x80"
5182 ";\n"
5183 " ^~~~~~~~~~~\n"
5184 " -------\n"
5185 " (bar\xf0\x9f\x98\x82"
5186 " *)\n",
5187 pp_formatted_text (dc.printer));
5188 }
5189
5190 /* Example of deletion fix-it hints that would overlap. */
5191 {
5192 test_diagnostic_context dc;
5193 rich_location richloc (line_table, expr);
5194 richloc.add_fixit_insert_before (where: open_paren, new_content: "(long\xf0\x9f\x98\x82 *)");
5195 source_range victim = {.m_start: expr_start, .m_finish: expr_finish};
5196 richloc.add_fixit_remove (src_range: victim);
5197
5198 /* These fixits are not consolidated. */
5199 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5200
5201 /* But the corrections are. */
5202 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5203 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5204 " *f = (f\xf0\x9f\x98\x82"
5205 " *)ptr->field\xcf\x80"
5206 ";\n"
5207 " ^~~~~~~~~~~\n"
5208 " ------------------\n"
5209 " (long\xf0\x9f\x98\x82"
5210 " *)(f\xf0\x9f\x98\x82"
5211 " *)\n",
5212 pp_formatted_text (dc.printer));
5213 }
5214
5215 /* Example of insertion fix-it hints that would overlap. */
5216 {
5217 test_diagnostic_context dc;
5218 rich_location richloc (line_table, expr);
5219 richloc.add_fixit_insert_before
5220 (where: open_paren, new_content: "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5221 richloc.add_fixit_insert_after (where: close_paren, new_content: "TEST");
5222
5223 /* The first insertion is long enough that if printed naively,
5224 it would overlap with the second.
5225 Verify that they are printed as a single replacement. */
5226 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5227 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5228 " *f = (f\xf0\x9f\x98\x82"
5229 " *)ptr->field\xcf\x80"
5230 ";\n"
5231 " ^~~~~~~~~~~\n"
5232 " -------\n"
5233 " L\xf0\x9f\x98\x82"
5234 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5235 " *)TEST\n",
5236 pp_formatted_text (dc.printer));
5237 }
5238}
5239
5240/* Verify that the line_corrections machinery correctly prints
5241 overlapping fixit-hints that have been added in the wrong
5242 order.
5243 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5244
5245static void
5246test_overlapped_fixit_printing_2 (const line_table_case &case_)
5247{
5248 /* Create a tempfile and write some text to it.
5249 ...000000000111111111122222222223333333333.
5250 ...123456789012345678901234567890123456789. */
5251 const char *content
5252 = ("int a5[][0][0] = { 1, 2 };\n");
5253 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5254 line_table_test ltt (case_);
5255
5256 const line_map_ordinary *ord_map
5257 = linemap_check_ordinary (map: linemap_add (line_table, LC_ENTER, sysp: false,
5258 to_file: tmp.get_filename (), to_line: 0));
5259
5260 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5261
5262 const location_t final_line_end
5263 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 100);
5264
5265 /* Don't attempt to run the tests if column data might be unavailable. */
5266 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5267 return;
5268
5269 const location_t col_1
5270 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 1);
5271 const location_t col_20
5272 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 20);
5273 const location_t col_21
5274 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 21);
5275 const location_t col_23
5276 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 23);
5277 const location_t col_25
5278 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 25);
5279
5280 /* Two insertions, in the wrong order. */
5281 {
5282 test_diagnostic_context dc;
5283 file_cache &fc = dc.get_file_cache ();
5284
5285 rich_location richloc (line_table, col_20);
5286 richloc.add_fixit_insert_before (where: col_23, new_content: "{");
5287 richloc.add_fixit_insert_before (where: col_21, new_content: "}");
5288
5289 /* These fixits should be accepted; they can't be consolidated. */
5290 char_display_policy policy (make_policy (dc, richloc));
5291 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5292 const fixit_hint *hint_0 = richloc.get_fixit_hint (idx: 0);
5293 ASSERT_EQ (column_range (23, 22),
5294 get_affected_range (fc, policy, hint_0, CU_BYTES));
5295 ASSERT_EQ (column_range (23, 23), get_printed_columns (fc, policy, hint_0));
5296 const fixit_hint *hint_1 = richloc.get_fixit_hint (idx: 1);
5297 ASSERT_EQ (column_range (21, 20),
5298 get_affected_range (fc, policy, hint_1, CU_BYTES));
5299 ASSERT_EQ (column_range (21, 21), get_printed_columns (fc, policy, hint_1));
5300
5301 /* Verify that they're printed correctly. */
5302 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5303 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5304 " ^\n"
5305 " } {\n",
5306 pp_formatted_text (dc.printer));
5307 }
5308
5309 /* Various overlapping insertions, some occurring "out of order"
5310 (reproducing the fix-it hints from PR c/81405). */
5311 {
5312 test_diagnostic_context dc;
5313 rich_location richloc (line_table, col_20);
5314
5315 richloc.add_fixit_insert_before (where: col_20, new_content: "{{");
5316 richloc.add_fixit_insert_before (where: col_21, new_content: "}}");
5317 richloc.add_fixit_insert_before (where: col_23, new_content: "{");
5318 richloc.add_fixit_insert_before (where: col_21, new_content: "}");
5319 richloc.add_fixit_insert_before (where: col_23, new_content: "{{");
5320 richloc.add_fixit_insert_before (where: col_25, new_content: "}");
5321 richloc.add_fixit_insert_before (where: col_21, new_content: "}");
5322 richloc.add_fixit_insert_before (where: col_1, new_content: "{");
5323 richloc.add_fixit_insert_before (where: col_25, new_content: "}");
5324 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5325 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5326 " ^\n"
5327 " { -----\n"
5328 " {{1}}}}, {{{2 }}\n",
5329 pp_formatted_text (dc.printer));
5330 }
5331}
5332
5333/* Insertion fix-it hint: adding a "break;" on a line by itself. */
5334
5335static void
5336test_fixit_insert_containing_newline (const line_table_case &case_)
5337{
5338 /* Create a tempfile and write some text to it.
5339 .........................0000000001111111.
5340 .........................1234567890123456. */
5341 const char *old_content = (" case 'a':\n" /* line 1. */
5342 " x = a;\n" /* line 2. */
5343 " case 'b':\n" /* line 3. */
5344 " x = b;\n");/* line 4. */
5345
5346 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5347 line_table_test ltt (case_);
5348 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 3);
5349
5350 location_t case_start = linemap_position_for_column (line_table, 5);
5351 location_t case_finish = linemap_position_for_column (line_table, 13);
5352 location_t case_loc = make_location (caret: case_start, start: case_start, finish: case_finish);
5353 location_t line_start = linemap_position_for_column (line_table, 1);
5354
5355 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5356 return;
5357
5358 /* Add a "break;" on a line by itself before line 3 i.e. before
5359 column 1 of line 3. */
5360 {
5361 rich_location richloc (line_table, case_loc);
5362 richloc.add_fixit_insert_before (where: line_start, new_content: " break;\n");
5363
5364 /* Without line numbers. */
5365 {
5366 test_diagnostic_context dc;
5367 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5368 ASSERT_STREQ (" x = a;\n"
5369 "+ break;\n"
5370 " case 'b':\n"
5371 " ^~~~~~~~~\n",
5372 pp_formatted_text (dc.printer));
5373 }
5374
5375 /* With line numbers. */
5376 {
5377 test_diagnostic_context dc;
5378 dc.m_source_printing.show_line_numbers_p = true;
5379 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5380 ASSERT_STREQ (" 2 | x = a;\n"
5381 " +++ |+ break;\n"
5382 " 3 | case 'b':\n"
5383 " | ^~~~~~~~~\n",
5384 pp_formatted_text (dc.printer));
5385 }
5386 }
5387
5388 /* Verify that attempts to add text with a newline fail when the
5389 insertion point is *not* at the start of a line. */
5390 {
5391 rich_location richloc (line_table, case_loc);
5392 richloc.add_fixit_insert_before (where: case_start, new_content: "break;\n");
5393 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5394 test_diagnostic_context dc;
5395 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5396 ASSERT_STREQ (" case 'b':\n"
5397 " ^~~~~~~~~\n",
5398 pp_formatted_text (dc.printer));
5399 }
5400}
5401
5402/* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5403 of the file, where the fix-it is printed in a different line-span
5404 to the primary range of the diagnostic. */
5405
5406static void
5407test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5408{
5409 /* Create a tempfile and write some text to it.
5410 .........................0000000001111111.
5411 .........................1234567890123456. */
5412 const char *old_content = ("test (int ch)\n" /* line 1. */
5413 "{\n" /* line 2. */
5414 " putchar (ch);\n" /* line 3. */
5415 "}\n"); /* line 4. */
5416
5417 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5418 line_table_test ltt (case_);
5419
5420 const line_map_ordinary *ord_map = linemap_check_ordinary
5421 (map: linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 0));
5422 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5423
5424 /* The primary range is the "putchar" token. */
5425 location_t putchar_start
5426 = linemap_position_for_line_and_column (set: line_table, ord_map, 3, 2);
5427 location_t putchar_finish
5428 = linemap_position_for_line_and_column (set: line_table, ord_map, 3, 8);
5429 location_t putchar_loc
5430 = make_location (caret: putchar_start, start: putchar_start, finish: putchar_finish);
5431 rich_location richloc (line_table, putchar_loc);
5432
5433 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5434 location_t file_start
5435 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 1);
5436 richloc.add_fixit_insert_before (where: file_start, new_content: "#include <stdio.h>\n");
5437
5438 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5439 return;
5440
5441 {
5442 test_diagnostic_context dc;
5443 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5444 ASSERT_STREQ ("FILENAME:1:1:\n"
5445 "+#include <stdio.h>\n"
5446 " test (int ch)\n"
5447 "FILENAME:3:2:\n"
5448 " putchar (ch);\n"
5449 " ^~~~~~~\n",
5450 pp_formatted_text (dc.printer));
5451 }
5452
5453 /* With line-numbering, the line spans are close enough to be
5454 consolidated, since it makes little sense to skip line 2. */
5455 {
5456 test_diagnostic_context dc;
5457 dc.m_source_printing.show_line_numbers_p = true;
5458 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5459 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5460 " 1 | test (int ch)\n"
5461 " 2 | {\n"
5462 " 3 | putchar (ch);\n"
5463 " | ^~~~~~~\n",
5464 pp_formatted_text (dc.printer));
5465 }
5466}
5467
5468/* Replacement fix-it hint containing a newline.
5469 This will fail, as newlines are only supported when inserting at the
5470 beginning of a line. */
5471
5472static void
5473test_fixit_replace_containing_newline (const line_table_case &case_)
5474{
5475 /* Create a tempfile and write some text to it.
5476 .........................0000000001111.
5477 .........................1234567890123. */
5478 const char *old_content = "foo = bar ();\n";
5479
5480 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5481 line_table_test ltt (case_);
5482 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
5483
5484 /* Replace the " = " with "\n = ", as if we were reformatting an
5485 overly long line. */
5486 location_t start = linemap_position_for_column (line_table, 4);
5487 location_t finish = linemap_position_for_column (line_table, 6);
5488 location_t loc = linemap_position_for_column (line_table, 13);
5489 rich_location richloc (line_table, loc);
5490 source_range range = source_range::from_locations (start, finish);
5491 richloc.add_fixit_replace (src_range: range, new_content: "\n =");
5492
5493 /* Arbitrary newlines are not yet supported within fix-it hints, so
5494 the fix-it should not be displayed. */
5495 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5496
5497 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5498 return;
5499
5500 test_diagnostic_context dc;
5501 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5502 ASSERT_STREQ (" foo = bar ();\n"
5503 " ^\n",
5504 pp_formatted_text (dc.printer));
5505}
5506
5507/* Fix-it hint, attempting to delete a newline.
5508 This will fail, as we currently only support fix-it hints that
5509 affect one line at a time. */
5510
5511static void
5512test_fixit_deletion_affecting_newline (const line_table_case &case_)
5513{
5514 /* Create a tempfile and write some text to it.
5515 ..........................0000000001111.
5516 ..........................1234567890123. */
5517 const char *old_content = ("foo = bar (\n"
5518 " );\n");
5519
5520 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5521 line_table_test ltt (case_);
5522 const line_map_ordinary *ord_map = linemap_check_ordinary
5523 (map: linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 0));
5524 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5525
5526 /* Attempt to delete the " (\n...)". */
5527 location_t start
5528 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 10);
5529 location_t caret
5530 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 11);
5531 location_t finish
5532 = linemap_position_for_line_and_column (set: line_table, ord_map, 2, 7);
5533 location_t loc = make_location (caret, start, finish);
5534 rich_location richloc (line_table, loc);
5535 richloc. add_fixit_remove ();
5536
5537 /* Fix-it hints that affect more than one line are not yet supported, so
5538 the fix-it should not be displayed. */
5539 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5540
5541 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5542 return;
5543
5544 test_diagnostic_context dc;
5545 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5546 ASSERT_STREQ (" foo = bar (\n"
5547 " ~^\n"
5548 " );\n"
5549 " ~ \n",
5550 pp_formatted_text (dc.printer));
5551}
5552
5553static void
5554test_tab_expansion (const line_table_case &case_)
5555{
5556 /* Create a tempfile and write some text to it. This example uses a tabstop
5557 of 8, as the column numbers attempt to indicate:
5558
5559 .....................000.01111111111.22222333333 display
5560 .....................123.90123456789.56789012345 columns */
5561 const char *content = " \t This: `\t' is a tab.\n";
5562 /* ....................000 00000011111 11111222222 byte
5563 ....................123 45678901234 56789012345 columns */
5564
5565 const int tabstop = 8;
5566 cpp_char_column_policy policy (tabstop, cpp_wcwidth);
5567 const int first_non_ws_byte_col = 7;
5568 const int right_quote_byte_col = 15;
5569 const int last_byte_col = 25;
5570 ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
5571
5572 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5573 line_table_test ltt (case_);
5574 linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 1);
5575
5576 /* Don't attempt to run the tests if column data might be unavailable. */
5577 location_t line_end = linemap_position_for_column (line_table, last_byte_col);
5578 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5579 return;
5580
5581 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5582 into 11 spaces. Recall that print_line() also puts one space before
5583 everything too. */
5584 {
5585 test_diagnostic_context dc;
5586 dc.m_tabstop = tabstop;
5587 rich_location richloc (line_table,
5588 linemap_position_for_column (line_table,
5589 first_non_ws_byte_col));
5590 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5591 test_layout.print_line (row: 1);
5592 ASSERT_STREQ (" This: ` ' is a tab.\n"
5593 " ^\n",
5594 pp_formatted_text (dc.printer));
5595 }
5596
5597 /* Confirm the display width was tracked correctly across the internal tab
5598 as well. */
5599 {
5600 test_diagnostic_context dc;
5601 dc.m_tabstop = tabstop;
5602 rich_location richloc (line_table,
5603 linemap_position_for_column (line_table,
5604 right_quote_byte_col));
5605 layout test_layout (dc, richloc, DK_ERROR, nullptr);
5606 test_layout.print_line (row: 1);
5607 ASSERT_STREQ (" This: ` ' is a tab.\n"
5608 " ^\n",
5609 pp_formatted_text (dc.printer));
5610 }
5611}
5612
5613/* Verify that the escaping machinery can cope with a variety of different
5614 invalid bytes. */
5615
5616static void
5617test_escaping_bytes_1 (const line_table_case &case_)
5618{
5619 const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
5620 const size_t sz = sizeof (content);
5621 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5622 line_table_test ltt (case_);
5623 const line_map_ordinary *ord_map = linemap_check_ordinary
5624 (map: linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 0));
5625 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5626
5627 location_t finish
5628 = linemap_position_for_line_and_column (set: line_table, ord_map, 1,
5629 strlen (s: content));
5630
5631 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5632 return;
5633
5634 /* Locations of the NUL and \v bytes. */
5635 location_t nul_loc
5636 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 7);
5637 location_t v_loc
5638 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 11);
5639 gcc_rich_location richloc (nul_loc);
5640 richloc.add_range (loc: v_loc);
5641
5642 {
5643 test_diagnostic_context dc;
5644 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5645 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5646 " ^ ~\n",
5647 pp_formatted_text (dc.printer));
5648 }
5649 richloc.set_escape_on_output (true);
5650 {
5651 test_diagnostic_context dc;
5652 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5653 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5654 ASSERT_STREQ
5655 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5656 " ^~~~~~~~ ~~~~~~~~\n",
5657 pp_formatted_text (dc.printer));
5658 }
5659 {
5660 test_diagnostic_context dc;
5661 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5662 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5663 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5664 " ^~~~ ~~~~\n",
5665 pp_formatted_text (dc.printer));
5666 }
5667}
5668
5669/* As above, but verify that we handle the initial byte of a line
5670 correctly. */
5671
5672static void
5673test_escaping_bytes_2 (const line_table_case &case_)
5674{
5675 const char content[] = "\0after\n";
5676 const size_t sz = sizeof (content);
5677 temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
5678 line_table_test ltt (case_);
5679 const line_map_ordinary *ord_map = linemap_check_ordinary
5680 (map: linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 0));
5681 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5682
5683 location_t finish
5684 = linemap_position_for_line_and_column (set: line_table, ord_map, 1,
5685 strlen (s: content));
5686
5687 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5688 return;
5689
5690 /* Location of the NUL byte. */
5691 location_t nul_loc
5692 = linemap_position_for_line_and_column (set: line_table, ord_map, 1, 1);
5693 gcc_rich_location richloc (nul_loc);
5694
5695 {
5696 test_diagnostic_context dc;
5697 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5698 ASSERT_STREQ (" after\n"
5699 " ^\n",
5700 pp_formatted_text (dc.printer));
5701 }
5702 richloc.set_escape_on_output (true);
5703 {
5704 test_diagnostic_context dc;
5705 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
5706 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5707 ASSERT_STREQ (" <U+0000>after\n"
5708 " ^~~~~~~~\n",
5709 pp_formatted_text (dc.printer));
5710 }
5711 {
5712 test_diagnostic_context dc;
5713 dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
5714 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5715 ASSERT_STREQ (" <00>after\n"
5716 " ^~~~\n",
5717 pp_formatted_text (dc.printer));
5718 }
5719}
5720
5721/* Verify that line numbers are correctly printed for the case of
5722 a multiline range in which the width of the line numbers changes
5723 (e.g. from "9" to "10"). */
5724
5725static void
5726test_line_numbers_multiline_range ()
5727{
5728 /* Create a tempfile and write some text to it. */
5729 pretty_printer pp;
5730 for (int i = 0; i < 20; i++)
5731 /* .........0000000001111111.
5732 .............1234567890123456. */
5733 pp_printf (&pp, "this is line %i\n", i + 1);
5734 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
5735 line_table_test ltt;
5736
5737 const line_map_ordinary *ord_map = linemap_check_ordinary
5738 (map: linemap_add (line_table, LC_ENTER, sysp: false, to_file: tmp.get_filename (), to_line: 0));
5739 linemap_line_start (set: line_table, to_line: 1, max_column_hint: 100);
5740
5741 /* Create a multi-line location, starting at the "line" of line 9, with
5742 a caret on the "is" of line 10, finishing on the "this" line 11. */
5743
5744 location_t start
5745 = linemap_position_for_line_and_column (set: line_table, ord_map, 9, 9);
5746 location_t caret
5747 = linemap_position_for_line_and_column (set: line_table, ord_map, 10, 6);
5748 location_t finish
5749 = linemap_position_for_line_and_column (set: line_table, ord_map, 11, 4);
5750 location_t loc = make_location (caret, start, finish);
5751
5752 test_diagnostic_context dc;
5753 dc.m_source_printing.show_line_numbers_p = true;
5754 dc.m_source_printing.min_margin_width = 0;
5755 gcc_rich_location richloc (loc);
5756 diagnostic_show_locus (context: &dc, richloc: &richloc, diagnostic_kind: DK_ERROR);
5757 ASSERT_STREQ (" 9 | this is line 9\n"
5758 " | ~~~~~~\n"
5759 "10 | this is line 10\n"
5760 " | ~~~~~^~~~~~~~~~\n"
5761 "11 | this is line 11\n"
5762 " | ~~~~ \n",
5763 pp_formatted_text (dc.printer));
5764}
5765
5766/* Run all of the selftests within this file. */
5767
5768void
5769diagnostic_show_locus_cc_tests ()
5770{
5771 test_line_span ();
5772
5773 test_layout_range_for_single_point ();
5774 test_layout_range_for_single_line ();
5775 test_layout_range_for_multiple_lines ();
5776
5777 test_display_widths ();
5778
5779 for_each_line_table_case (testcase: test_layout_x_offset_display_utf8);
5780 for_each_line_table_case (testcase: test_layout_x_offset_display_tab);
5781
5782 test_get_line_bytes_without_trailing_whitespace ();
5783
5784 test_diagnostic_show_locus_unknown_location ();
5785
5786 for_each_line_table_case (testcase: test_diagnostic_show_locus_one_liner);
5787 for_each_line_table_case (testcase: test_diagnostic_show_locus_one_liner_utf8);
5788 for_each_line_table_case (testcase: test_add_location_if_nearby);
5789 for_each_line_table_case (testcase: test_diagnostic_show_locus_fixit_lines);
5790 for_each_line_table_case (testcase: test_fixit_consolidation);
5791 for_each_line_table_case (testcase: test_overlapped_fixit_printing);
5792 for_each_line_table_case (testcase: test_overlapped_fixit_printing_utf8);
5793 for_each_line_table_case (testcase: test_overlapped_fixit_printing_2);
5794 for_each_line_table_case (testcase: test_fixit_insert_containing_newline);
5795 for_each_line_table_case (testcase: test_fixit_insert_containing_newline_2);
5796 for_each_line_table_case (testcase: test_fixit_replace_containing_newline);
5797 for_each_line_table_case (testcase: test_fixit_deletion_affecting_newline);
5798 for_each_line_table_case (testcase: test_tab_expansion);
5799 for_each_line_table_case (testcase: test_escaping_bytes_1);
5800 for_each_line_table_case (testcase: test_escaping_bytes_2);
5801
5802 test_line_numbers_multiline_range ();
5803}
5804
5805} // namespace selftest
5806
5807#endif /* #if CHECKING_P */
5808
5809#if __GNUC__ >= 10
5810# pragma GCC diagnostic pop
5811#endif
5812

source code of gcc/diagnostic-show-locus.cc