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