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