]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/diagnostic-show-locus.c
C++: more location wrapper nodes (PR c++/43064, PR c++/43486)
[thirdparty/gcc.git] / gcc / diagnostic-show-locus.c
CommitLineData
fee30e05 1/* Diagnostic subroutines for printing source-code
8e8f6434 2 Copyright (C) 1999-2018 Free Software Foundation, Inc.
fee30e05 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"
a8a31e3d 30#include "gcc-rich-location.h"
99b4f3a2 31#include "selftest.h"
db7ff53d 32#include "selftest-diagnostic.h"
fee30e05 33
34#ifdef HAVE_TERMIOS_H
35# include <termios.h>
36#endif
37
38#ifdef GWINSZ_IN_SYS_IOCTL
39# include <sys/ioctl.h>
40#endif
41
f0479000 42/* Classes for rendering source code and diagnostics, within an
43 anonymous namespace.
44 The work is done by "class layout", which embeds and uses
45 "class colorizer" and "class layout_range" to get things done. */
46
47namespace {
48
49/* The state at a given point of the source code, assuming that we're
50 in a range: which range are we in, and whether we should draw a caret at
51 this point. */
52
53struct point_state
54{
55 int range_idx;
56 bool draw_caret_p;
57};
58
59/* A class to inject colorization codes when printing the diagnostic locus.
60
61 It has one kind of colorization for each of:
62 - normal text
63 - range 0 (the "primary location")
64 - range 1
65 - range 2
66
67 The class caches the lookup of the color codes for the above.
68
69 The class also has responsibility for tracking which of the above is
70 active, filtering out unnecessary changes. This allows
71 layout::print_source_line and layout::print_annotation_line
72 to simply request a colorization code for *every* character they print,
73 via this class, and have the filtering be done for them here. */
74
75class colorizer
76{
77 public:
78 colorizer (diagnostic_context *context,
48a7392b 79 diagnostic_t diagnostic_kind);
f0479000 80 ~colorizer ();
81
82 void set_range (int range_idx) { set_state (range_idx); }
83 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
df4248fb 84 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
85 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
f0479000 86
87 private:
88 void set_state (int state);
89 void begin_state (int state);
90 void finish_state (int state);
df4248fb 91 const char *get_color_by_name (const char *);
f0479000 92
93 private:
94 static const int STATE_NORMAL_TEXT = -1;
df4248fb 95 static const int STATE_FIXIT_INSERT = -2;
96 static const int STATE_FIXIT_DELETE = -3;
f0479000 97
98 diagnostic_context *m_context;
48a7392b 99 diagnostic_t m_diagnostic_kind;
f0479000 100 int m_current_state;
df4248fb 101 const char *m_range1;
102 const char *m_range2;
103 const char *m_fixit_insert;
104 const char *m_fixit_delete;
105 const char *m_stop_color;
f0479000 106};
107
108/* A point within a layout_range; similar to an expanded_location,
109 but after filtering on file. */
110
111class layout_point
112{
113 public:
114 layout_point (const expanded_location &exploc)
115 : m_line (exploc.line),
116 m_column (exploc.column) {}
117
d73881b0 118 linenum_type m_line;
f0479000 119 int m_column;
120};
121
122/* A class for use by "class layout" below: a filtered location_range. */
123
124class layout_range
125{
126 public:
83108969 127 layout_range (const expanded_location *start_exploc,
128 const expanded_location *finish_exploc,
5fe20025 129 enum range_display_kind range_display_kind,
b7bb5264 130 const expanded_location *caret_exploc,
a2507e3d 131 unsigned original_idx,
b7bb5264 132 const range_label *label);
f0479000 133
d73881b0 134 bool contains_point (linenum_type row, int column) const;
135 bool intersects_line_p (linenum_type row) const;
f0479000 136
137 layout_point m_start;
138 layout_point m_finish;
5fe20025 139 enum range_display_kind m_range_display_kind;
f0479000 140 layout_point m_caret;
a2507e3d 141 unsigned m_original_idx;
b7bb5264 142 const range_label *m_label;
f0479000 143};
144
145/* A struct for use by layout::print_source_line for telling
146 layout::print_annotation_line the extents of the source line that
147 it printed, so that underlines can be clipped appropriately. */
148
149struct line_bounds
150{
151 int m_first_non_ws;
152 int m_last_non_ws;
153};
154
aec1f4bd 155/* A range of contiguous source lines within a layout (e.g. "lines 5-10"
156 or "line 23"). During the layout ctor, layout::calculate_line_spans
157 splits the pertinent source lines into a list of disjoint line_span
158 instances (e.g. lines 5-10, lines 15-20, line 23). */
159
160struct line_span
161{
162 line_span (linenum_type first_line, linenum_type last_line)
163 : m_first_line (first_line), m_last_line (last_line)
164 {
165 gcc_assert (first_line <= last_line);
166 }
167 linenum_type get_first_line () const { return m_first_line; }
168 linenum_type get_last_line () const { return m_last_line; }
169
170 bool contains_line_p (linenum_type line) const
171 {
172 return line >= m_first_line && line <= m_last_line;
173 }
174
175 static int comparator (const void *p1, const void *p2)
176 {
177 const line_span *ls1 = (const line_span *)p1;
178 const line_span *ls2 = (const line_span *)p2;
d73881b0 179 int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
180 if (first_line_cmp)
181 return first_line_cmp;
182 return compare (ls1->m_last_line, ls2->m_last_line);
aec1f4bd 183 }
184
185 linenum_type m_first_line;
186 linenum_type m_last_line;
187};
188
d73881b0 189#if CHECKING_P
190
191/* Selftests for line_span. */
192
193static void
194test_line_span ()
195{
196 line_span line_one (1, 1);
197 ASSERT_EQ (1, line_one.get_first_line ());
198 ASSERT_EQ (1, line_one.get_last_line ());
199 ASSERT_FALSE (line_one.contains_line_p (0));
200 ASSERT_TRUE (line_one.contains_line_p (1));
201 ASSERT_FALSE (line_one.contains_line_p (2));
202
203 line_span lines_1_to_3 (1, 3);
204 ASSERT_EQ (1, lines_1_to_3.get_first_line ());
205 ASSERT_EQ (3, lines_1_to_3.get_last_line ());
206 ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
207 ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
208
209 ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
210 ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
211 ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
212
213 /* A linenum > 2^31. */
214 const linenum_type LARGEST_LINE = 0xffffffff;
215 line_span largest_line (LARGEST_LINE, LARGEST_LINE);
216 ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
217 ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
218
219 ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
220 ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
221}
222
223#endif /* #if CHECKING_P */
224
f0479000 225/* A class to control the overall layout when printing a diagnostic.
226
227 The layout is determined within the constructor.
734caf84 228 It is then printed by repeatedly calling the "print_source_line",
229 "print_annotation_line" and "print_any_fixits" methods.
f0479000 230
231 We assume we have disjoint ranges. */
232
233class layout
234{
235 public:
236 layout (diagnostic_context *context,
48a7392b 237 rich_location *richloc,
238 diagnostic_t diagnostic_kind);
f0479000 239
a8a31e3d 240 bool maybe_add_location_range (const location_range *loc_range,
a2507e3d 241 unsigned original_idx,
a8a31e3d 242 bool restrict_to_current_line_spans);
243
aec1f4bd 244 int get_num_line_spans () const { return m_line_spans.length (); }
245 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
246
dd9ed701 247 void print_gap_in_line_numbering ();
aec1f4bd 248 bool print_heading_for_line_span_index_p (int line_span_idx) const;
249
250 expanded_location get_expanded_location (const line_span *) const;
f0479000 251
d73881b0 252 void print_line (linenum_type row);
896d130e 253
254 private:
d73881b0 255 bool will_show_line_p (linenum_type row) const;
256 void print_leading_fixits (linenum_type row);
257 void print_source_line (linenum_type row, const char *line, int line_width,
896d130e 258 line_bounds *lbounds_out);
d73881b0 259 bool should_print_annotation_line_p (linenum_type row) const;
87c50f50 260 void start_annotation_line (char margin_char = ' ') const;
d73881b0 261 void print_annotation_line (linenum_type row, const line_bounds lbounds);
b7bb5264 262 void print_any_labels (linenum_type row);
d73881b0 263 void print_trailing_fixits (linenum_type row);
896d130e 264
d73881b0 265 bool annotation_line_showed_range_p (linenum_type line, int start_column,
73ffb7be 266 int finish_column) const;
abf93a25 267 void show_ruler (int max_column) const;
268
70017c99 269 bool validate_fixit_hint_p (const fixit_hint *hint);
270
aec1f4bd 271 void calculate_line_spans ();
272
3752e5b1 273 void print_newline ();
274
f0479000 275 bool
276 get_state_at_point (/* Inputs. */
d73881b0 277 linenum_type row, int column,
f0479000 278 int first_non_ws, int last_non_ws,
279 /* Outputs. */
280 point_state *out_state);
281
282 int
d73881b0 283 get_x_bound_for_row (linenum_type row, int caret_column,
f0479000 284 int last_non_ws);
285
734caf84 286 void
ff7410b8 287 move_to_column (int *column, int dest_column, bool add_left_margin);
734caf84 288
f0479000 289 private:
290 diagnostic_context *m_context;
291 pretty_printer *m_pp;
a8a31e3d 292 location_t m_primary_loc;
f0479000 293 expanded_location m_exploc;
294 colorizer m_colorizer;
295 bool m_colorize_source_p;
b7bb5264 296 bool m_show_labels_p;
ff7410b8 297 bool m_show_line_numbers_p;
f0479000 298 auto_vec <layout_range> m_layout_ranges;
70017c99 299 auto_vec <const fixit_hint *> m_fixit_hints;
aec1f4bd 300 auto_vec <line_span> m_line_spans;
ff7410b8 301 int m_linenum_width;
f0479000 302 int m_x_offset;
303};
304
305/* Implementation of "class colorizer". */
306
307/* The constructor for "colorizer". Lookup and store color codes for the
308 different kinds of things we might need to print. */
309
310colorizer::colorizer (diagnostic_context *context,
48a7392b 311 diagnostic_t diagnostic_kind) :
f0479000 312 m_context (context),
48a7392b 313 m_diagnostic_kind (diagnostic_kind),
f0479000 314 m_current_state (STATE_NORMAL_TEXT)
315{
df4248fb 316 m_range1 = get_color_by_name ("range1");
317 m_range2 = get_color_by_name ("range2");
318 m_fixit_insert = get_color_by_name ("fixit-insert");
319 m_fixit_delete = get_color_by_name ("fixit-delete");
320 m_stop_color = colorize_stop (pp_show_color (context->printer));
f0479000 321}
322
323/* The destructor for "colorize". If colorization is on, print a code to
324 turn it off. */
325
326colorizer::~colorizer ()
327{
328 finish_state (m_current_state);
329}
330
331/* Update state, printing color codes if necessary if there's a state
332 change. */
333
334void
335colorizer::set_state (int new_state)
336{
337 if (m_current_state != new_state)
fee30e05 338 {
f0479000 339 finish_state (m_current_state);
340 m_current_state = new_state;
341 begin_state (new_state);
fee30e05 342 }
fee30e05 343}
344
f0479000 345/* Turn on any colorization for STATE. */
346
fee30e05 347void
f0479000 348colorizer::begin_state (int state)
fee30e05 349{
f0479000 350 switch (state)
351 {
352 case STATE_NORMAL_TEXT:
353 break;
fee30e05 354
df4248fb 355 case STATE_FIXIT_INSERT:
356 pp_string (m_context->printer, m_fixit_insert);
357 break;
358
359 case STATE_FIXIT_DELETE:
360 pp_string (m_context->printer, m_fixit_delete);
361 break;
362
f0479000 363 case 0:
364 /* Make range 0 be the same color as the "kind" text
365 (error vs warning vs note). */
366 pp_string
367 (m_context->printer,
368 colorize_start (pp_show_color (m_context->printer),
48a7392b 369 diagnostic_get_color_for_kind (m_diagnostic_kind)));
f0479000 370 break;
fee30e05 371
f0479000 372 case 1:
df4248fb 373 pp_string (m_context->printer, m_range1);
f0479000 374 break;
fee30e05 375
f0479000 376 case 2:
df4248fb 377 pp_string (m_context->printer, m_range2);
f0479000 378 break;
379
380 default:
d6dd1b60 381 /* For ranges beyond 2, alternate between color 1 and color 2. */
382 {
383 gcc_assert (state > 2);
384 pp_string (m_context->printer,
385 state % 2 ? m_range1 : m_range2);
386 }
f0479000 387 break;
388 }
fee30e05 389}
390
f0479000 391/* Turn off any colorization for STATE. */
392
fee30e05 393void
f0479000 394colorizer::finish_state (int state)
395{
df4248fb 396 if (state != STATE_NORMAL_TEXT)
397 pp_string (m_context->printer, m_stop_color);
398}
f0479000 399
df4248fb 400/* Get the color code for NAME (or the empty string if
401 colorization is disabled). */
f0479000 402
df4248fb 403const char *
404colorizer::get_color_by_name (const char *name)
405{
406 return colorize_start (pp_show_color (m_context->printer), name);
f0479000 407}
408
409/* Implementation of class layout_range. */
410
411/* The constructor for class layout_range.
412 Initialize various layout_point fields from expanded_location
413 equivalents; we've already filtered on file. */
414
83108969 415layout_range::layout_range (const expanded_location *start_exploc,
416 const expanded_location *finish_exploc,
5fe20025 417 enum range_display_kind range_display_kind,
b7bb5264 418 const expanded_location *caret_exploc,
a2507e3d 419 unsigned original_idx,
b7bb5264 420 const range_label *label)
83108969 421: m_start (*start_exploc),
422 m_finish (*finish_exploc),
5fe20025 423 m_range_display_kind (range_display_kind),
b7bb5264 424 m_caret (*caret_exploc),
a2507e3d 425 m_original_idx (original_idx),
b7bb5264 426 m_label (label)
f0479000 427{
428}
429
430/* Is (column, row) within the given range?
431 We've already filtered on the file.
432
433 Ranges are closed (both limits are within the range).
434
435 Example A: a single-line range:
436 start: (col=22, line=2)
437 finish: (col=38, line=2)
438
439 |00000011111111112222222222333333333344444444444
440 |34567890123456789012345678901234567890123456789
441--+-----------------------------------------------
44201|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
44302|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
44403|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
445
446 Example B: a multiline range with
447 start: (col=14, line=3)
448 finish: (col=08, line=5)
449
450 |00000011111111112222222222333333333344444444444
451 |34567890123456789012345678901234567890123456789
452--+-----------------------------------------------
45301|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
45402|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
45503|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
45604|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
45705|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
45806|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
459--+-----------------------------------------------
460
461 Legend:
462 - 'b' indicates a point *before* the range
463 - 'S' indicates the start of the range
464 - 'w' indicates a point within the range
465 - 'F' indicates the finish of the range (which is
466 within it).
467 - 'a' indicates a subsequent point *after* the range. */
468
469bool
d73881b0 470layout_range::contains_point (linenum_type row, int column) const
f0479000 471{
472 gcc_assert (m_start.m_line <= m_finish.m_line);
473 /* ...but the equivalent isn't true for the columns;
474 consider example B in the comment above. */
475
476 if (row < m_start.m_line)
477 /* Points before the first line of the range are
478 outside it (corresponding to line 01 in example A
479 and lines 01 and 02 in example B above). */
480 return false;
481
482 if (row == m_start.m_line)
483 /* On same line as start of range (corresponding
484 to line 02 in example A and line 03 in example B). */
485 {
486 if (column < m_start.m_column)
487 /* Points on the starting line of the range, but
488 before the column in which it begins. */
489 return false;
490
491 if (row < m_finish.m_line)
492 /* This is a multiline range; the point
493 is within it (corresponds to line 03 in example B
494 from column 14 onwards) */
495 return true;
496 else
497 {
498 /* This is a single-line range. */
499 gcc_assert (row == m_finish.m_line);
500 return column <= m_finish.m_column;
501 }
502 }
503
504 /* The point is in a line beyond that containing the
505 start of the range: lines 03 onwards in example A,
506 and lines 04 onwards in example B. */
507 gcc_assert (row > m_start.m_line);
508
509 if (row > m_finish.m_line)
510 /* The point is beyond the final line of the range
511 (lines 03 onwards in example A, and lines 06 onwards
512 in example B). */
513 return false;
514
515 if (row < m_finish.m_line)
516 {
517 /* The point is in a line that's fully within a multiline
518 range (e.g. line 04 in example B). */
519 gcc_assert (m_start.m_line < m_finish.m_line);
520 return true;
521 }
522
523 gcc_assert (row == m_finish.m_line);
524
525 return column <= m_finish.m_column;
526}
527
70017c99 528/* Does this layout_range contain any part of line ROW? */
529
530bool
d73881b0 531layout_range::intersects_line_p (linenum_type row) const
70017c99 532{
533 gcc_assert (m_start.m_line <= m_finish.m_line);
534 if (row < m_start.m_line)
535 return false;
536 if (row > m_finish.m_line)
537 return false;
538 return true;
539}
540
99b4f3a2 541#if CHECKING_P
542
70017c99 543/* A helper function for testing layout_range. */
99b4f3a2 544
545static layout_range
546make_range (int start_line, int start_col, int end_line, int end_col)
547{
548 const expanded_location start_exploc
549 = {"test.c", start_line, start_col, NULL, false};
550 const expanded_location finish_exploc
551 = {"test.c", end_line, end_col, NULL, false};
5fe20025 552 return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
a2507e3d 553 &start_exploc, 0, NULL);
99b4f3a2 554}
555
70017c99 556/* Selftests for layout_range::contains_point and
557 layout_range::intersects_line_p. */
99b4f3a2 558
70017c99 559/* Selftest for layout_range, where the layout_range
99b4f3a2 560 is a range with start==end i.e. a single point. */
561
562static void
70017c99 563test_layout_range_for_single_point ()
99b4f3a2 564{
565 layout_range point = make_range (7, 10, 7, 10);
566
70017c99 567 /* Tests for layout_range::contains_point. */
568
99b4f3a2 569 /* Before the line. */
570 ASSERT_FALSE (point.contains_point (6, 1));
571
572 /* On the line, but before start. */
573 ASSERT_FALSE (point.contains_point (7, 9));
574
575 /* At the point. */
576 ASSERT_TRUE (point.contains_point (7, 10));
577
578 /* On the line, after the point. */
579 ASSERT_FALSE (point.contains_point (7, 11));
580
581 /* After the line. */
582 ASSERT_FALSE (point.contains_point (8, 1));
70017c99 583
584 /* Tests for layout_range::intersects_line_p. */
585 ASSERT_FALSE (point.intersects_line_p (6));
586 ASSERT_TRUE (point.intersects_line_p (7));
587 ASSERT_FALSE (point.intersects_line_p (8));
99b4f3a2 588}
589
70017c99 590/* Selftest for layout_range, where the layout_range
99b4f3a2 591 is the single-line range shown as "Example A" above. */
592
593static void
70017c99 594test_layout_range_for_single_line ()
99b4f3a2 595{
596 layout_range example_a = make_range (2, 22, 2, 38);
597
70017c99 598 /* Tests for layout_range::contains_point. */
599
99b4f3a2 600 /* Before the line. */
601 ASSERT_FALSE (example_a.contains_point (1, 1));
602
603 /* On the line, but before start. */
604 ASSERT_FALSE (example_a.contains_point (2, 21));
605
606 /* On the line, at the start. */
607 ASSERT_TRUE (example_a.contains_point (2, 22));
608
609 /* On the line, within the range. */
610 ASSERT_TRUE (example_a.contains_point (2, 23));
611
612 /* On the line, at the end. */
613 ASSERT_TRUE (example_a.contains_point (2, 38));
614
615 /* On the line, after the end. */
616 ASSERT_FALSE (example_a.contains_point (2, 39));
617
618 /* After the line. */
619 ASSERT_FALSE (example_a.contains_point (2, 39));
70017c99 620
621 /* Tests for layout_range::intersects_line_p. */
622 ASSERT_FALSE (example_a.intersects_line_p (1));
623 ASSERT_TRUE (example_a.intersects_line_p (2));
624 ASSERT_FALSE (example_a.intersects_line_p (3));
99b4f3a2 625}
626
70017c99 627/* Selftest for layout_range, where the layout_range
99b4f3a2 628 is the multi-line range shown as "Example B" above. */
629
630static void
70017c99 631test_layout_range_for_multiple_lines ()
99b4f3a2 632{
633 layout_range example_b = make_range (3, 14, 5, 8);
634
70017c99 635 /* Tests for layout_range::contains_point. */
636
99b4f3a2 637 /* Before first line. */
638 ASSERT_FALSE (example_b.contains_point (1, 1));
639
640 /* On the first line, but before start. */
641 ASSERT_FALSE (example_b.contains_point (3, 13));
642
643 /* At the start. */
644 ASSERT_TRUE (example_b.contains_point (3, 14));
645
646 /* On the first line, within the range. */
647 ASSERT_TRUE (example_b.contains_point (3, 15));
648
649 /* On an interior line.
650 The column number should not matter; try various boundary
651 values. */
652 ASSERT_TRUE (example_b.contains_point (4, 1));
653 ASSERT_TRUE (example_b.contains_point (4, 7));
654 ASSERT_TRUE (example_b.contains_point (4, 8));
655 ASSERT_TRUE (example_b.contains_point (4, 9));
656 ASSERT_TRUE (example_b.contains_point (4, 13));
657 ASSERT_TRUE (example_b.contains_point (4, 14));
658 ASSERT_TRUE (example_b.contains_point (4, 15));
659
660 /* On the final line, before the end. */
661 ASSERT_TRUE (example_b.contains_point (5, 7));
662
663 /* On the final line, at the end. */
664 ASSERT_TRUE (example_b.contains_point (5, 8));
665
666 /* On the final line, after the end. */
667 ASSERT_FALSE (example_b.contains_point (5, 9));
668
669 /* After the line. */
670 ASSERT_FALSE (example_b.contains_point (6, 1));
70017c99 671
672 /* Tests for layout_range::intersects_line_p. */
673 ASSERT_FALSE (example_b.intersects_line_p (2));
674 ASSERT_TRUE (example_b.intersects_line_p (3));
675 ASSERT_TRUE (example_b.intersects_line_p (4));
676 ASSERT_TRUE (example_b.intersects_line_p (5));
677 ASSERT_FALSE (example_b.intersects_line_p (6));
99b4f3a2 678}
679
680#endif /* #if CHECKING_P */
681
f0479000 682/* Given a source line LINE of length LINE_WIDTH, determine the width
683 without any trailing whitespace. */
684
685static int
686get_line_width_without_trailing_whitespace (const char *line, int line_width)
687{
688 int result = line_width;
689 while (result > 0)
690 {
691 char ch = line[result - 1];
28bd6e12 692 if (ch == ' ' || ch == '\t' || ch == '\r')
f0479000 693 result--;
694 else
695 break;
696 }
697 gcc_assert (result >= 0);
698 gcc_assert (result <= line_width);
699 gcc_assert (result == 0 ||
700 (line[result - 1] != ' '
28bd6e12 701 && line[result -1] != '\t'
702 && line[result -1] != '\r'));
f0479000 703 return result;
704}
705
99b4f3a2 706#if CHECKING_P
707
708/* A helper function for testing get_line_width_without_trailing_whitespace. */
709
710static void
711assert_eq (const char *line, int expected_width)
712{
713 int actual_value
714 = get_line_width_without_trailing_whitespace (line, strlen (line));
715 ASSERT_EQ (actual_value, expected_width);
716}
717
718/* Verify that get_line_width_without_trailing_whitespace is sane for
719 various inputs. It is not required to handle newlines. */
720
721static void
722test_get_line_width_without_trailing_whitespace ()
723{
724 assert_eq ("", 0);
725 assert_eq (" ", 0);
726 assert_eq ("\t", 0);
28bd6e12 727 assert_eq ("\r", 0);
99b4f3a2 728 assert_eq ("hello world", 11);
729 assert_eq ("hello world ", 11);
730 assert_eq ("hello world \t\t ", 11);
28bd6e12 731 assert_eq ("hello world\r", 11);
99b4f3a2 732}
733
734#endif /* #if CHECKING_P */
735
c24757cf 736/* Helper function for layout's ctor, for sanitizing locations relative
737 to the primary location within a diagnostic.
738
739 Compare LOC_A and LOC_B to see if it makes sense to print underlines
740 connecting their expanded locations. Doing so is only guaranteed to
741 make sense if the locations share the same macro expansion "history"
742 i.e. they can be traced through the same macro expansions, eventually
743 reaching an ordinary map.
744
745 This may be too strong a condition, but it effectively sanitizes
746 PR c++/70105, which has an example of printing an expression where the
747 final location of the expression is in a different macro, which
748 erroneously was leading to hundreds of lines of irrelevant source
749 being printed. */
750
751static bool
752compatible_locations_p (location_t loc_a, location_t loc_b)
753{
754 if (IS_ADHOC_LOC (loc_a))
755 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
756 if (IS_ADHOC_LOC (loc_b))
757 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
758
7378dbfb 759 /* If either location is one of the special locations outside of a
760 linemap, they are only compatible if they are equal. */
761 if (loc_a < RESERVED_LOCATION_COUNT
762 || loc_b < RESERVED_LOCATION_COUNT)
763 return loc_a == loc_b;
764
c24757cf 765 const line_map *map_a = linemap_lookup (line_table, loc_a);
766 linemap_assert (map_a);
767
768 const line_map *map_b = linemap_lookup (line_table, loc_b);
769 linemap_assert (map_b);
770
771 /* Are they within the same map? */
772 if (map_a == map_b)
773 {
774 /* Are both within the same macro expansion? */
775 if (linemap_macro_expansion_map_p (map_a))
776 {
777 /* Expand each location towards the spelling location, and
778 recurse. */
779 const line_map_macro *macro_map = linemap_check_macro (map_a);
be1e7283 780 location_t loc_a_toward_spelling
c24757cf 781 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
782 macro_map,
783 loc_a);
be1e7283 784 location_t loc_b_toward_spelling
c24757cf 785 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
786 macro_map,
787 loc_b);
788 return compatible_locations_p (loc_a_toward_spelling,
789 loc_b_toward_spelling);
790 }
791
792 /* Otherwise they are within the same ordinary map. */
793 return true;
794 }
795 else
796 {
797 /* Within different maps. */
798
799 /* If either is within a macro expansion, they are incompatible. */
800 if (linemap_macro_expansion_map_p (map_a)
801 || linemap_macro_expansion_map_p (map_b))
802 return false;
803
804 /* Within two different ordinary maps; they are compatible iff they
805 are in the same file. */
806 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
807 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
808 return ord_map_a->to_file == ord_map_b->to_file;
809 }
810}
811
f907f132 812/* Comparator for sorting fix-it hints. */
813
814static int
815fixit_cmp (const void *p_a, const void *p_b)
816{
817 const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
818 const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
819 return hint_a->get_start_loc () - hint_b->get_start_loc ();
820}
821
f0479000 822/* Implementation of class layout. */
823
824/* Constructor for class layout.
825
826 Filter the ranges from the rich_location to those that we can
70017c99 827 sanely print, populating m_layout_ranges and m_fixit_hints.
aec1f4bd 828 Determine the range of lines that we will print, splitting them
829 up into an ordered list of disjoint spans of contiguous line numbers.
f0479000 830 Determine m_x_offset, to ensure that the primary caret
831 will fit within the max_width provided by the diagnostic_context. */
832
833layout::layout (diagnostic_context * context,
48a7392b 834 rich_location *richloc,
835 diagnostic_t diagnostic_kind)
f0479000 836: m_context (context),
837 m_pp (context->printer),
a8a31e3d 838 m_primary_loc (richloc->get_range (0)->m_loc),
48a7392b 839 m_exploc (richloc->get_expanded_location (0)),
840 m_colorizer (context, diagnostic_kind),
f0479000 841 m_colorize_source_p (context->colorize_source_p),
b7bb5264 842 m_show_labels_p (context->show_labels_p),
ff7410b8 843 m_show_line_numbers_p (context->show_line_numbers_p),
d6dd1b60 844 m_layout_ranges (richloc->get_num_locations ()),
70017c99 845 m_fixit_hints (richloc->get_num_fixit_hints ()),
d6dd1b60 846 m_line_spans (1 + richloc->get_num_locations ()),
ff7410b8 847 m_linenum_width (0),
f0479000 848 m_x_offset (0)
849{
f0479000 850 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
851 {
852 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
853 Ignore any ranges that are awkward to handle. */
56d3d781 854 const location_range *loc_range = richloc->get_range (idx);
a2507e3d 855 maybe_add_location_range (loc_range, idx, false);
f0479000 856 }
857
70017c99 858 /* Populate m_fixit_hints, filtering to only those that are in the
859 same file. */
860 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
861 {
862 const fixit_hint *hint = richloc->get_fixit_hint (i);
863 if (validate_fixit_hint_p (hint))
864 m_fixit_hints.safe_push (hint);
865 }
866
f907f132 867 /* Sort m_fixit_hints. */
868 m_fixit_hints.qsort (fixit_cmp);
869
aec1f4bd 870 /* Populate m_line_spans. */
871 calculate_line_spans ();
872
ff7410b8 873 /* Determine m_linenum_width. */
874 gcc_assert (m_line_spans.length () > 0);
875 const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
876 int highest_line = last_span->m_last_line;
877 if (highest_line < 0)
878 highest_line = 0;
879 m_linenum_width = num_digits (highest_line);
dd9ed701 880 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
881 if (m_line_spans.length () > 1)
882 m_linenum_width = MAX (m_linenum_width, 3);
31087b7e 883 /* If there's a minimum margin width, apply it (subtracting 1 for the space
884 after the line number. */
885 m_linenum_width = MAX (m_linenum_width, context->min_margin_width - 1);
ff7410b8 886
f0479000 887 /* Adjust m_x_offset.
888 Center the primary caret to fit in max_width; all columns
889 will be adjusted accordingly. */
0bce23e1 890 size_t max_width = m_context->caret_max_width;
891 char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
892 if (line && (size_t)m_exploc.column <= line.length ())
f0479000 893 {
0bce23e1 894 size_t right_margin = CARET_LINE_MARGIN;
895 size_t column = m_exploc.column;
ff7410b8 896 if (m_show_line_numbers_p)
897 column += m_linenum_width + 2;
0bce23e1 898 right_margin = MIN (line.length () - column, right_margin);
f0479000 899 right_margin = max_width - right_margin;
0bce23e1 900 if (line.length () >= max_width && column > right_margin)
f0479000 901 m_x_offset = column - right_margin;
902 gcc_assert (m_x_offset >= 0);
903 }
abf93a25 904
905 if (context->show_ruler_p)
906 show_ruler (m_x_offset + max_width);
f0479000 907}
fee30e05 908
a8a31e3d 909/* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
910 those that we can sanely print.
911
a2507e3d 912 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
913 (for use as extrinsic state by label ranges FIXME).
914
a8a31e3d 915 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
916 filtered against this layout instance's current line spans: it
917 will only be added if the location is fully within the lines
918 already specified by other locations.
919
920 Return true iff LOC_RANGE was added. */
921
922bool
923layout::maybe_add_location_range (const location_range *loc_range,
a2507e3d 924 unsigned original_idx,
a8a31e3d 925 bool restrict_to_current_line_spans)
926{
927 gcc_assert (loc_range);
928
929 /* Split the "range" into caret and range information. */
930 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
931
932 /* Expand the various locations. */
933 expanded_location start
934 = linemap_client_expand_location_to_spelling_point
935 (src_range.m_start, LOCATION_ASPECT_START);
936 expanded_location finish
937 = linemap_client_expand_location_to_spelling_point
938 (src_range.m_finish, LOCATION_ASPECT_FINISH);
939 expanded_location caret
940 = linemap_client_expand_location_to_spelling_point
941 (loc_range->m_loc, LOCATION_ASPECT_CARET);
942
943 /* If any part of the range isn't in the same file as the primary
944 location of this diagnostic, ignore the range. */
945 if (start.file != m_exploc.file)
946 return false;
947 if (finish.file != m_exploc.file)
948 return false;
5fe20025 949 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
a8a31e3d 950 if (caret.file != m_exploc.file)
951 return false;
952
953 /* Sanitize the caret location for non-primary ranges. */
954 if (m_layout_ranges.length () > 0)
5fe20025 955 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
a8a31e3d 956 if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
957 /* Discard any non-primary ranges that can't be printed
958 sanely relative to the primary location. */
959 return false;
960
961 /* Everything is now known to be in the correct source file,
962 but it may require further sanitization. */
5fe20025 963 layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
a2507e3d 964 original_idx, loc_range->m_label);
a8a31e3d 965
966 /* If we have a range that finishes before it starts (perhaps
967 from something built via macro expansion), printing the
968 range is likely to be nonsensical. Also, attempting to do so
969 breaks assumptions within the printing code (PR c/68473).
970 Similarly, don't attempt to print ranges if one or both ends
971 of the range aren't sane to print relative to the
972 primary location (PR c++/70105). */
973 if (start.line > finish.line
974 || !compatible_locations_p (src_range.m_start, m_primary_loc)
975 || !compatible_locations_p (src_range.m_finish, m_primary_loc))
976 {
977 /* Is this the primary location? */
978 if (m_layout_ranges.length () == 0)
979 {
980 /* We want to print the caret for the primary location, but
981 we must sanitize away m_start and m_finish. */
982 ri.m_start = ri.m_caret;
983 ri.m_finish = ri.m_caret;
984 }
985 else
986 /* This is a non-primary range; ignore it. */
987 return false;
988 }
989
990 /* Potentially filter to just the lines already specified by other
991 locations. This is for use by gcc_rich_location::add_location_if_nearby.
992 The layout ctor doesn't use it, and can't because m_line_spans
993 hasn't been set up at that point. */
994 if (restrict_to_current_line_spans)
995 {
996 if (!will_show_line_p (start.line))
997 return false;
998 if (!will_show_line_p (finish.line))
999 return false;
5fe20025 1000 if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
a8a31e3d 1001 if (!will_show_line_p (caret.line))
1002 return false;
1003 }
1004
1005 /* Passed all the tests; add the range to m_layout_ranges so that
1006 it will be printed. */
1007 m_layout_ranges.safe_push (ri);
1008 return true;
1009}
1010
1011/* Return true iff ROW is within one of the line spans for this layout. */
1012
1013bool
d73881b0 1014layout::will_show_line_p (linenum_type row) const
a8a31e3d 1015{
1016 for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1017 line_span_idx++)
1018 {
1019 const line_span *line_span = get_line_span (line_span_idx);
1020 if (line_span->contains_line_p (row))
1021 return true;
1022 }
1023 return false;
1024}
1025
dd9ed701 1026/* Print a line showing a gap in the line numbers, for showing the boundary
1027 between two line spans. */
1028
1029void
1030layout::print_gap_in_line_numbering ()
1031{
1032 gcc_assert (m_show_line_numbers_p);
1033
1034 for (int i = 0; i < m_linenum_width + 1; i++)
1035 pp_character (m_pp, '.');
1036
1037 pp_newline (m_pp);
1038}
1039
aec1f4bd 1040/* Return true iff we should print a heading when starting the
1041 line span with the given index. */
1042
1043bool
1044layout::print_heading_for_line_span_index_p (int line_span_idx) const
1045{
1046 /* We print a heading for every change of line span, hence for every
1047 line span after the initial one. */
1048 if (line_span_idx > 0)
1049 return true;
1050
1051 /* We also do it for the initial span if the primary location of the
1052 diagnostic is in a different span. */
1053 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1054 return true;
1055
1056 return false;
1057}
1058
1059/* Get an expanded_location for the first location of interest within
1060 the given line_span.
1061 Used when printing a heading to indicate a new line span. */
1062
1063expanded_location
1064layout::get_expanded_location (const line_span *line_span) const
1065{
1066 /* Whenever possible, use the caret location. */
1067 if (line_span->contains_line_p (m_exploc.line))
1068 return m_exploc;
1069
1070 /* Otherwise, use the start of the first range that's present
1071 within the line_span. */
1072 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1073 {
1074 const layout_range *lr = &m_layout_ranges[i];
1075 if (line_span->contains_line_p (lr->m_start.m_line))
1076 {
1077 expanded_location exploc = m_exploc;
1078 exploc.line = lr->m_start.m_line;
1079 exploc.column = lr->m_start.m_column;
1080 return exploc;
1081 }
1082 }
1083
70017c99 1084 /* Otherwise, use the location of the first fixit-hint present within
1085 the line_span. */
1086 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1087 {
1088 const fixit_hint *hint = m_fixit_hints[i];
1089 location_t loc = hint->get_start_loc ();
1090 expanded_location exploc = expand_location (loc);
1091 if (line_span->contains_line_p (exploc.line))
1092 return exploc;
1093 }
1094
aec1f4bd 1095 /* It should not be possible to have a line span that didn't
70017c99 1096 contain any of the layout_range or fixit_hint instances. */
aec1f4bd 1097 gcc_unreachable ();
1098 return m_exploc;
1099}
1100
70017c99 1101/* Determine if HINT is meaningful to print within this layout. */
1102
1103bool
1104layout::validate_fixit_hint_p (const fixit_hint *hint)
1105{
be45049f 1106 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1107 return false;
1108 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1109 return false;
70017c99 1110
1111 return true;
1112}
1113
1114/* Determine the range of lines affected by HINT.
1115 This assumes that HINT has already been filtered by
1116 validate_fixit_hint_p, and so affects the correct source file. */
1117
1118static line_span
1119get_line_span_for_fixit_hint (const fixit_hint *hint)
1120{
1121 gcc_assert (hint);
e69492e4 1122
1123 int start_line = LOCATION_LINE (hint->get_start_loc ());
1124
1125 /* For line-insertion fix-it hints, add the previous line to the
1126 span, to give the user more context on the proposed change. */
1127 if (hint->ends_with_newline_p ())
1128 if (start_line > 1)
1129 start_line--;
1130
1131 return line_span (start_line,
be45049f 1132 LOCATION_LINE (hint->get_next_loc ()));
70017c99 1133}
1134
aec1f4bd 1135/* We want to print the pertinent source code at a diagnostic. The
1136 rich_location can contain multiple locations. This will have been
1137 filtered into m_exploc (the caret for the primary location) and
1138 m_layout_ranges, for those ranges within the same source file.
1139
1140 We will print a subset of the lines within the source file in question,
1141 as a collection of "spans" of lines.
1142
1143 This function populates m_line_spans with an ordered, disjoint list of
1144 the line spans of interest.
1145
dd9ed701 1146 Printing a gap between line spans takes one line, so, when printing
1147 line numbers, we allow a gap of up to one line between spans when
1148 merging, since it makes more sense to print the source line rather than a
1149 "gap-in-line-numbering" line. When not printing line numbers, it's
1150 better to be more explicit about what's going on, so keeping them as
1151 separate spans is preferred.
1152
1153 For example, if the primary range is on lines 8-10, with secondary ranges
1154 covering lines 5-6 and lines 13-15:
aec1f4bd 1155
1156 004
dd9ed701 1157 005 |RANGE 1
1158 006 |RANGE 1
1159 007
1160 008 |PRIMARY RANGE
1161 009 |PRIMARY CARET
1162 010 |PRIMARY RANGE
1163 011
1164 012
1165 013 |RANGE 2
1166 014 |RANGE 2
1167 015 |RANGE 2
1168 016
1169
1170 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1171
1172 With line numbering off (with span headers), we want three spans: lines 5-6,
1173 lines 8-10, and lines 13-15. */
aec1f4bd 1174
1175void
1176layout::calculate_line_spans ()
1177{
1178 /* This should only be called once, by the ctor. */
1179 gcc_assert (m_line_spans.length () == 0);
1180
1181 /* Populate tmp_spans with individual spans, for each of
1182 m_exploc, and for m_layout_ranges. */
d6dd1b60 1183 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
aec1f4bd 1184 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1185 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1186 {
1187 const layout_range *lr = &m_layout_ranges[i];
1188 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1189 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1190 lr->m_finish.m_line));
1191 }
1192
70017c99 1193 /* Also add spans for any fix-it hints, in case they cover other lines. */
1194 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1195 {
1196 const fixit_hint *hint = m_fixit_hints[i];
1197 gcc_assert (hint);
1198 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1199 }
1200
aec1f4bd 1201 /* Sort them. */
1202 tmp_spans.qsort(line_span::comparator);
1203
1204 /* Now iterate through tmp_spans, copying into m_line_spans, and
1205 combining where possible. */
1206 gcc_assert (tmp_spans.length () > 0);
1207 m_line_spans.safe_push (tmp_spans[0]);
1208 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1209 {
1210 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1211 const line_span *next = &tmp_spans[i];
1212 gcc_assert (next->m_first_line >= current->m_first_line);
dd9ed701 1213 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1214 if (next->m_first_line <= current->m_last_line + 1 + merger_distance)
aec1f4bd 1215 {
1216 /* We can merge them. */
1217 if (next->m_last_line > current->m_last_line)
1218 current->m_last_line = next->m_last_line;
1219 }
1220 else
1221 {
1222 /* No merger possible. */
1223 m_line_spans.safe_push (*next);
1224 }
1225 }
1226
1227 /* Verify the result, in m_line_spans. */
1228 gcc_assert (m_line_spans.length () > 0);
1229 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1230 {
1231 const line_span *prev = &m_line_spans[i - 1];
1232 const line_span *next = &m_line_spans[i];
1233 /* The individual spans must be sane. */
1234 gcc_assert (prev->m_first_line <= prev->m_last_line);
1235 gcc_assert (next->m_first_line <= next->m_last_line);
1236 /* The spans must be ordered. */
1237 gcc_assert (prev->m_first_line < next->m_first_line);
1238 /* There must be a gap of at least one line between separate spans. */
1239 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1240 }
1241}
1242
896d130e 1243/* Print line ROW of source code, potentially colorized at any ranges, and
1244 populate *LBOUNDS_OUT.
1245 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1246 is its width. */
f0479000 1247
896d130e 1248void
d73881b0 1249layout::print_source_line (linenum_type row, const char *line, int line_width,
896d130e 1250 line_bounds *lbounds_out)
f0479000 1251{
f0479000 1252 m_colorizer.set_normal_text ();
1253
1254 /* We will stop printing the source line at any trailing
1255 whitespace. */
1256 line_width = get_line_width_without_trailing_whitespace (line,
1257 line_width);
12634056 1258 line += m_x_offset;
f0479000 1259
ff7410b8 1260 if (m_show_line_numbers_p)
1261 {
1262 int width = num_digits (row);
1263 for (int i = 0; i < m_linenum_width - width; i++)
1264 pp_space (m_pp);
1265 pp_printf (m_pp, "%i | ", row);
1266 }
1267 else
1268 pp_space (m_pp);
f0479000 1269 int first_non_ws = INT_MAX;
1270 int last_non_ws = 0;
1271 int column;
1272 for (column = 1 + m_x_offset; column <= line_width; column++)
fee30e05 1273 {
f0479000 1274 /* Assuming colorization is enabled for the caret and underline
1275 characters, we may also colorize the associated characters
1276 within the source line.
1277
1278 For frontends that generate range information, we color the
1279 associated characters in the source line the same as the
1280 carets and underlines in the annotation line, to make it easier
1281 for the reader to see the pertinent code.
1282
1283 For frontends that only generate carets, we don't colorize the
1284 characters above them, since this would look strange (e.g.
1285 colorizing just the first character in a token). */
1286 if (m_colorize_source_p)
1287 {
1288 bool in_range_p;
1289 point_state state;
1290 in_range_p = get_state_at_point (row, column,
1291 0, INT_MAX,
1292 &state);
1293 if (in_range_p)
1294 m_colorizer.set_range (state.range_idx);
1295 else
1296 m_colorizer.set_normal_text ();
1297 }
28bd6e12 1298 char c = *line;
1299 if (c == '\0' || c == '\t' || c == '\r')
fee30e05 1300 c = ' ';
f0479000 1301 if (c != ' ')
1302 {
1303 last_non_ws = column;
1304 if (first_non_ws == INT_MAX)
1305 first_non_ws = column;
1306 }
1307 pp_character (m_pp, c);
fee30e05 1308 line++;
1309 }
3752e5b1 1310 print_newline ();
f0479000 1311
1312 lbounds_out->m_first_non_ws = first_non_ws;
1313 lbounds_out->m_last_non_ws = last_non_ws;
f0479000 1314}
1315
70017c99 1316/* Determine if we should print an annotation line for ROW.
1317 i.e. if any of m_layout_ranges contains ROW. */
1318
1319bool
d73881b0 1320layout::should_print_annotation_line_p (linenum_type row) const
70017c99 1321{
1322 layout_range *range;
1323 int i;
1324 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
5fe20025 1325 {
1326 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1327 return false;
1328 if (range->intersects_line_p (row))
1329 return true;
1330 }
70017c99 1331 return false;
1332}
1333
ff7410b8 1334/* Begin an annotation line. If m_show_line_numbers_p, print the left
1335 margin, which is empty for annotation lines. Otherwise, do nothing. */
1336
1337void
87c50f50 1338layout::start_annotation_line (char margin_char) const
ff7410b8 1339{
1340 if (m_show_line_numbers_p)
1341 {
31087b7e 1342 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1343 of it, right-aligned, padded with spaces. */
1344 int i;
1345 for (i = 0; i < m_linenum_width - 3; i++)
1346 pp_space (m_pp);
1347 for (; i < m_linenum_width; i++)
87c50f50 1348 pp_character (m_pp, margin_char);
ff7410b8 1349 pp_string (m_pp, " |");
1350 }
1351}
1352
f0479000 1353/* Print a line consisting of the caret/underlines for the given
1354 source line. */
fee30e05 1355
f0479000 1356void
d73881b0 1357layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
f0479000 1358{
1359 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1360 lbounds.m_last_non_ws);
1361
ff7410b8 1362 start_annotation_line ();
f0479000 1363 pp_space (m_pp);
ff7410b8 1364
f0479000 1365 for (int column = 1 + m_x_offset; column < x_bound; column++)
1366 {
1367 bool in_range_p;
1368 point_state state;
1369 in_range_p = get_state_at_point (row, column,
1370 lbounds.m_first_non_ws,
1371 lbounds.m_last_non_ws,
1372 &state);
1373 if (in_range_p)
1374 {
1375 /* Within a range. Draw either the caret or an underline. */
1376 m_colorizer.set_range (state.range_idx);
1377 if (state.draw_caret_p)
d6dd1b60 1378 {
1379 /* Draw the caret. */
1380 char caret_char;
1381 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1382 caret_char = m_context->caret_chars[state.range_idx];
1383 else
1384 caret_char = '^';
1385 pp_character (m_pp, caret_char);
1386 }
f0479000 1387 else
1388 pp_character (m_pp, '~');
1389 }
1390 else
1391 {
1392 /* Not in a range. */
1393 m_colorizer.set_normal_text ();
1394 pp_character (m_pp, ' ');
1395 }
1396 }
3752e5b1 1397 print_newline ();
f0479000 1398}
1399
b7bb5264 1400/* Implementation detail of layout::print_any_labels.
1401
1402 A label within the given row of source. */
1403
1404struct line_label
1405{
1406 line_label (int state_idx, int column, label_text text)
1407 : m_state_idx (state_idx), m_column (column),
1408 m_text (text), m_length (strlen (text.m_buffer)),
1409 m_label_line (0)
1410 {}
1411
1412 /* Sorting is primarily by column, then by state index. */
1413 static int comparator (const void *p1, const void *p2)
1414 {
1415 const line_label *ll1 = (const line_label *)p1;
1416 const line_label *ll2 = (const line_label *)p2;
1417 int column_cmp = compare (ll1->m_column, ll2->m_column);
1418 if (column_cmp)
1419 return column_cmp;
1420 return compare (ll1->m_state_idx, ll2->m_state_idx);
1421 }
1422
1423 int m_state_idx;
1424 int m_column;
1425 label_text m_text;
1426 size_t m_length;
1427 int m_label_line;
1428};
1429
1430/* Print any labels in this row. */
1431void
1432layout::print_any_labels (linenum_type row)
1433{
1434 int i;
1435 auto_vec<line_label> labels;
1436
1437 /* Gather the labels that are to be printed into "labels". */
1438 {
1439 layout_range *range;
1440 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1441 {
1442 /* Most ranges don't have labels, so reject this first. */
1443 if (range->m_label == NULL)
1444 continue;
1445
1446 /* The range's caret must be on this line. */
1447 if (range->m_caret.m_line != row)
1448 continue;
1449
1450 /* Reject labels that aren't fully visible due to clipping
1451 by m_x_offset. */
1452 if (range->m_caret.m_column <= m_x_offset)
1453 continue;
1454
1455 label_text text;
a2507e3d 1456 text = range->m_label->get_text (range->m_original_idx);
b7bb5264 1457
1458 /* Allow for labels that return NULL from their get_text
1459 implementation (so e.g. such labels can control their own
1460 visibility). */
1461 if (text.m_buffer == NULL)
1462 continue;
1463
1464 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1465 }
1466 }
1467
1468 /* Bail out if there are no labels on this row. */
1469 if (labels.length () == 0)
1470 return;
1471
1472 /* Sort them. */
1473 labels.qsort(line_label::comparator);
1474
1475 /* Figure out how many "label lines" we need, and which
1476 one each label is printed in.
1477
1478 For example, if the labels aren't too densely packed,
1479 we can fit them on the same line, giving two "label lines":
1480
1481 foo + bar
1482 ~~~ ~~~
1483 | | : label line 0
1484 l0 l1 : label line 1
1485
1486 If they would touch each other or overlap, then we need
1487 additional "label lines":
1488
1489 foo + bar
1490 ~~~ ~~~
1491 | | : label line 0
1492 | label 1 : label line 1
1493 label 0 : label line 2
1494
1495 Place the final label on label line 1, and work backwards, adding
1496 label lines as needed.
1497
1498 If multiple labels are at the same place, put them on separate
1499 label lines:
1500
1501 foo + bar
1502 ^ : label line 0
1503 | : label line 1
1504 label 1 : label line 2
1505 label 0 : label line 3. */
1506
1507 int max_label_line = 1;
1508 {
1509 int next_column = INT_MAX;
1510 line_label *label;
1511 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1512 {
1513 /* Would this label "touch" or overlap the next label? */
1514 if (label->m_column + label->m_length >= (size_t)next_column)
1515 max_label_line++;
1516
1517 label->m_label_line = max_label_line;
1518 next_column = label->m_column;
1519 }
1520 }
1521
1522 /* Print the "label lines". For each label within the line, print
1523 either a vertical bar ('|') for the labels that are lower down, or the
1524 labels themselves once we've reached their line. */
1525 {
1526 /* Keep track of in which column we last printed a vertical bar.
1527 This allows us to suppress duplicate vertical bars for the case
1528 where multiple labels are on one column. */
1529 int last_vbar = 0;
1530 for (int label_line = 0; label_line <= max_label_line; label_line++)
1531 {
1532 start_annotation_line ();
1533 pp_space (m_pp);
1534 int column = 1 + m_x_offset;
1535 line_label *label;
1536 FOR_EACH_VEC_ELT (labels, i, label)
1537 {
1538 if (label_line > label->m_label_line)
1539 /* We've printed all the labels for this label line. */
1540 break;
1541
1542 if (label_line == label->m_label_line)
1543 {
1544 gcc_assert (column <= label->m_column);
1545 move_to_column (&column, label->m_column, true);
1546 m_colorizer.set_range (label->m_state_idx);
1547 pp_string (m_pp, label->m_text.m_buffer);
1548 m_colorizer.set_normal_text ();
1549 column += label->m_length;
1550 }
1551 else if (label->m_column != last_vbar)
1552 {
1553 gcc_assert (column <= label->m_column);
1554 move_to_column (&column, label->m_column, true);
1555 m_colorizer.set_range (label->m_state_idx);
1556 pp_character (m_pp, '|');
1557 m_colorizer.set_normal_text ();
1558 last_vbar = column;
1559 column++;
1560 }
1561 }
1562 print_newline ();
1563 }
1564 }
1565
1566 /* Clean up. */
1567 {
1568 line_label *label;
1569 FOR_EACH_VEC_ELT (labels, i, label)
1570 label->m_text.maybe_free ();
1571 }
1572}
1573
896d130e 1574/* If there are any fixit hints inserting new lines before source line ROW,
1575 print them.
1576
1577 They are printed on lines of their own, before the source line
1578 itself, with a leading '+'. */
1579
1580void
d73881b0 1581layout::print_leading_fixits (linenum_type row)
896d130e 1582{
1583 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1584 {
1585 const fixit_hint *hint = m_fixit_hints[i];
1586
1587 if (!hint->ends_with_newline_p ())
1588 /* Not a newline fixit; print it in print_trailing_fixits. */
1589 continue;
1590
1591 gcc_assert (hint->insertion_p ());
1592
1593 if (hint->affects_line_p (m_exploc.file, row))
1594 {
1595 /* Printing the '+' with normal colorization
1596 and the inserted line with "insert" colorization
1597 helps them stand out from each other, and from
1598 the surrounding text. */
1599 m_colorizer.set_normal_text ();
87c50f50 1600 start_annotation_line ('+');
896d130e 1601 pp_character (m_pp, '+');
1602 m_colorizer.set_fixit_insert ();
1603 /* Print all but the trailing newline of the fix-it hint.
1604 We have to print the newline separately to avoid
1605 getting additional pp prefixes printed. */
1606 for (size_t i = 0; i < hint->get_length () - 1; i++)
1607 pp_character (m_pp, hint->get_string ()[i]);
1608 m_colorizer.set_normal_text ();
1609 pp_newline (m_pp);
1610 }
1611 }
1612}
1613
1614/* Subroutine of layout::print_trailing_fixits.
73ffb7be 1615
1616 Determine if the annotation line printed for LINE contained
1617 the exact range from START_COLUMN to FINISH_COLUMN. */
1618
1619bool
d73881b0 1620layout::annotation_line_showed_range_p (linenum_type line, int start_column,
73ffb7be 1621 int finish_column) const
1622{
1623 layout_range *range;
1624 int i;
1625 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1626 if (range->m_start.m_line == line
1627 && range->m_start.m_column == start_column
1628 && range->m_finish.m_line == line
1629 && range->m_finish.m_column == finish_column)
1630 return true;
1631 return false;
1632}
1633
f085b618 1634/* Classes for printing trailing fix-it hints i.e. those that
1635 don't add new lines.
1636
1637 For insertion, these can look like:
1638
1639 new_text
1640
1641 For replacement, these can look like:
1642
1643 ------------- : underline showing affected range
1644 new_text
1645
1646 For deletion, these can look like:
1647
1648 ------------- : underline showing affected range
1649
1650 This can become confusing if they overlap, and so we need
1651 to do some preprocessing to decide what to print.
1652 We use the list of fixit_hint instances affecting the line
1653 to build a list of "correction" instances, and print the
1654 latter.
1655
1656 For example, consider a set of fix-its for converting
1657 a C-style cast to a C++ const_cast.
1658
1659 Given:
1660
1661 ..000000000111111111122222222223333333333.
1662 ..123456789012345678901234567890123456789.
1663 foo *f = (foo *)ptr->field;
1664 ^~~~~
1665
1666 and the fix-it hints:
1667 - replace col 10 (the open paren) with "const_cast<"
1668 - replace col 16 (the close paren) with "> ("
1669 - insert ")" before col 27
1670
1671 then we would get odd-looking output:
1672
1673 foo *f = (foo *)ptr->field;
1674 ^~~~~
1675 -
1676 const_cast<
1677 -
1678 > ( )
1679
1680 It would be better to detect when fixit hints are going to
1681 overlap (those that require new lines), and to consolidate
1682 the printing of such fixits, giving something like:
1683
1684 foo *f = (foo *)ptr->field;
1685 ^~~~~
1686 -----------------
1687 const_cast<foo *> (ptr->field)
1688
1689 This works by detecting when the printing would overlap, and
1690 effectively injecting no-op replace hints into the gaps between
1691 such fix-its, so that the printing joins up.
1692
1693 In the above example, the overlap of:
1694 - replace col 10 (the open paren) with "const_cast<"
1695 and:
1696 - replace col 16 (the close paren) with "> ("
1697 is fixed by injecting a no-op:
1698 - replace cols 11-15 with themselves ("foo *")
1699 and consolidating these, making:
1700 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1701 i.e.:
1702 - replace cols 10-16 with "const_cast<foo *> ("
1703
1704 This overlaps with the final fix-it hint:
1705 - insert ")" before col 27
1706 and so we repeat the consolidation process, by injecting
1707 a no-op:
1708 - replace cols 17-26 with themselves ("ptr->field")
1709 giving:
1710 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1711 i.e.:
1712 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1713
1714 and is thus printed as desired. */
1715
1716/* A range of columns within a line. */
1717
1718struct column_range
1719{
f907f132 1720 column_range (int start_, int finish_) : start (start_), finish (finish_)
1721 {
1722 /* We must have either a range, or an insertion. */
1723 gcc_assert (start <= finish || finish == start - 1);
1724 }
f085b618 1725
1726 bool operator== (const column_range &other) const
1727 {
1728 return start == other.start && finish == other.finish;
1729 }
1730
1731 int start;
1732 int finish;
1733};
1734
1735/* Get the range of columns that HINT would affect. */
1736
1737static column_range
1738get_affected_columns (const fixit_hint *hint)
1739{
1740 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1741 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1742
1743 return column_range (start_column, finish_column);
1744}
1745
1746/* Get the range of columns that would be printed for HINT. */
1747
1748static column_range
1749get_printed_columns (const fixit_hint *hint)
1750{
1751 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1752 int final_hint_column = start_column + hint->get_length () - 1;
1753 if (hint->insertion_p ())
1754 {
1755 return column_range (start_column, final_hint_column);
1756 }
1757 else
1758 {
1759 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1760
1761 return column_range (start_column,
1762 MAX (finish_column, final_hint_column));
1763 }
1764}
1765
1766/* A correction on a particular line.
1767 This describes a plan for how to print one or more fixit_hint
1768 instances that affected the line, potentially consolidating hints
1769 into corrections to make the result easier for the user to read. */
1770
1771struct correction
1772{
1773 correction (column_range affected_columns,
1774 column_range printed_columns,
1775 const char *new_text, size_t new_text_len)
1776 : m_affected_columns (affected_columns),
1777 m_printed_columns (printed_columns),
1778 m_text (xstrdup (new_text)),
1779 m_len (new_text_len),
1780 m_alloc_sz (new_text_len + 1)
1781 {
1782 }
1783
1784 ~correction () { free (m_text); }
1785
1786 bool insertion_p () const
1787 {
1788 return m_affected_columns.start == m_affected_columns.finish + 1;
1789 }
1790
1791 void ensure_capacity (size_t len);
1792 void ensure_terminated ();
1793
f907f132 1794 void overwrite (int dst_offset, const char_span &src_span)
1795 {
1796 gcc_assert (dst_offset >= 0);
0bce23e1 1797 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1798 memcpy (m_text + dst_offset, src_span.get_buffer (),
1799 src_span.length ());
f907f132 1800 }
1801
f085b618 1802 /* If insert, then start: the column before which the text
1803 is to be inserted, and finish is offset by the length of
1804 the replacement.
1805 If replace, then the range of columns affected. */
1806 column_range m_affected_columns;
1807
1808 /* If insert, then start: the column before which the text
1809 is to be inserted, and finish is offset by the length of
1810 the replacement.
1811 If replace, then the range of columns affected. */
1812 column_range m_printed_columns;
1813
1814 /* The text to be inserted/used as replacement. */
1815 char *m_text;
1816 size_t m_len;
1817 size_t m_alloc_sz;
1818};
1819
1820/* Ensure that m_text can hold a string of length LEN
1821 (plus 1 for 0-termination). */
1822
1823void
1824correction::ensure_capacity (size_t len)
1825{
1826 /* Allow 1 extra byte for 0-termination. */
1827 if (m_alloc_sz < (len + 1))
1828 {
1829 size_t new_alloc_sz = (len + 1) * 2;
1830 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1831 m_alloc_sz = new_alloc_sz;
1832 }
1833}
1834
1835/* Ensure that m_text is 0-terminated. */
1836
1837void
1838correction::ensure_terminated ()
1839{
1840 /* 0-terminate the buffer. */
1841 gcc_assert (m_len < m_alloc_sz);
1842 m_text[m_len] = '\0';
1843}
1844
1845/* A list of corrections affecting a particular line.
1846 This is used by layout::print_trailing_fixits for planning
1847 how to print the fix-it hints affecting the line. */
1848
1849struct line_corrections
1850{
d73881b0 1851 line_corrections (const char *filename, linenum_type row)
f085b618 1852 : m_filename (filename), m_row (row)
1853 {}
1854 ~line_corrections ();
1855
1856 void add_hint (const fixit_hint *hint);
1857
1858 const char *m_filename;
d73881b0 1859 linenum_type m_row;
f085b618 1860 auto_vec <correction *> m_corrections;
1861};
1862
1863/* struct line_corrections. */
1864
1865line_corrections::~line_corrections ()
1866{
1867 unsigned i;
1868 correction *c;
1869 FOR_EACH_VEC_ELT (m_corrections, i, c)
1870 delete c;
1871}
1872
f907f132 1873/* A struct wrapping a particular source line, allowing
1874 run-time bounds-checking of accesses in a checked build. */
1875
1876struct source_line
1877{
1878 source_line (const char *filename, int line);
1879
1880 char_span as_span () { return char_span (chars, width); }
1881
1882 const char *chars;
1883 int width;
1884};
1885
1886/* source_line's ctor. */
1887
1888source_line::source_line (const char *filename, int line)
1889{
0bce23e1 1890 char_span span = location_get_source_line (filename, line);
1891 chars = span.get_buffer ();
1892 width = span.length ();
f907f132 1893}
1894
f085b618 1895/* Add HINT to the corrections for this line.
1896 Attempt to consolidate nearby hints so that they will not
1897 overlap with printed. */
1898
1899void
1900line_corrections::add_hint (const fixit_hint *hint)
1901{
1902 column_range affected_columns = get_affected_columns (hint);
1903 column_range printed_columns = get_printed_columns (hint);
1904
1905 /* Potentially consolidate. */
1906 if (!m_corrections.is_empty ())
1907 {
1908 correction *last_correction
1909 = m_corrections[m_corrections.length () - 1];
f907f132 1910
1911 /* The following consolidation code assumes that the fix-it hints
1912 have been sorted by start (done within layout's ctor). */
1913 gcc_assert (affected_columns.start
1914 >= last_correction->m_affected_columns.start);
1915 gcc_assert (printed_columns.start
1916 >= last_correction->m_printed_columns.start);
1917
f085b618 1918 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1919 {
1920 /* We have two hints for which the printed forms of the hints
1921 would touch or overlap, so we need to consolidate them to avoid
1922 confusing the user.
1923 Attempt to inject a "replace" correction from immediately
1924 after the end of the last hint to immediately before the start
1925 of the next hint. */
1926 column_range between (last_correction->m_affected_columns.finish + 1,
1927 printed_columns.start - 1);
1928
1929 /* Try to read the source. */
f907f132 1930 source_line line (m_filename, m_row);
1931 if (line.chars && between.finish < line.width)
f085b618 1932 {
1933 /* Consolidate into the last correction:
1934 add a no-op "replace" of the "between" text, and
1935 add the text from the new hint. */
f907f132 1936 int old_len = last_correction->m_len;
1937 gcc_assert (old_len >= 0);
1938 int between_len = between.finish + 1 - between.start;
1939 gcc_assert (between_len >= 0);
1940 int new_len = old_len + between_len + hint->get_length ();
1941 gcc_assert (new_len >= 0);
f085b618 1942 last_correction->ensure_capacity (new_len);
f907f132 1943 last_correction->overwrite
1944 (old_len,
1945 line.as_span ().subspan (between.start - 1,
1946 between.finish + 1 - between.start));
1947 last_correction->overwrite (old_len + between_len,
1948 char_span (hint->get_string (),
1949 hint->get_length ()));
f085b618 1950 last_correction->m_len = new_len;
1951 last_correction->ensure_terminated ();
1952 last_correction->m_affected_columns.finish
1953 = affected_columns.finish;
1954 last_correction->m_printed_columns.finish
1955 += between_len + hint->get_length ();
1956 return;
1957 }
1958 }
1959 }
1960
1961 /* If no consolidation happened, add a new correction instance. */
1962 m_corrections.safe_push (new correction (affected_columns,
1963 printed_columns,
1964 hint->get_string (),
1965 hint->get_length ()));
1966}
1967
70017c99 1968/* If there are any fixit hints on source line ROW, print them.
734caf84 1969 They are printed in order, attempting to combine them onto lines, but
896d130e 1970 starting new lines if necessary.
1971 Fix-it hints that insert new lines are handled separately,
1972 in layout::print_leading_fixits. */
734caf84 1973
1974void
d73881b0 1975layout::print_trailing_fixits (linenum_type row)
734caf84 1976{
f085b618 1977 /* Build a list of correction instances for the line,
1978 potentially consolidating hints (for the sake of readability). */
1979 line_corrections corrections (m_exploc.file, row);
70017c99 1980 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
734caf84 1981 {
70017c99 1982 const fixit_hint *hint = m_fixit_hints[i];
896d130e 1983
f085b618 1984 /* Newline fixits are handled by layout::print_leading_fixits. */
896d130e 1985 if (hint->ends_with_newline_p ())
1986 continue;
1987
734caf84 1988 if (hint->affects_line_p (m_exploc.file, row))
f085b618 1989 corrections.add_hint (hint);
1990 }
1991
1992 /* Now print the corrections. */
1993 unsigned i;
1994 correction *c;
4df8da84 1995 int column = m_x_offset;
f085b618 1996
ff7410b8 1997 if (!corrections.m_corrections.is_empty ())
1998 start_annotation_line ();
1999
f085b618 2000 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2001 {
2002 /* For now we assume each fixit hint can only touch one line. */
2003 if (c->insertion_p ())
2004 {
2005 /* This assumes the insertion just affects one line. */
2006 int start_column = c->m_printed_columns.start;
ff7410b8 2007 move_to_column (&column, start_column, true);
f085b618 2008 m_colorizer.set_fixit_insert ();
2009 pp_string (m_pp, c->m_text);
2010 m_colorizer.set_normal_text ();
2011 column += c->m_len;
2012 }
2013 else
734caf84 2014 {
f085b618 2015 /* If the range of the replacement wasn't printed in the
2016 annotation line, then print an extra underline to
2017 indicate exactly what is being replaced.
2018 Always show it for removals. */
2019 int start_column = c->m_affected_columns.start;
2020 int finish_column = c->m_affected_columns.finish;
2021 if (!annotation_line_showed_range_p (row, start_column,
2022 finish_column)
2023 || c->m_len == 0)
be45049f 2024 {
ff7410b8 2025 move_to_column (&column, start_column, true);
f085b618 2026 m_colorizer.set_fixit_delete ();
2027 for (; column <= finish_column; column++)
2028 pp_character (m_pp, '-');
be45049f 2029 m_colorizer.set_normal_text ();
be45049f 2030 }
f085b618 2031 /* Print the replacement text. REPLACE also covers
2032 removals, so only do this extra work (potentially starting
2033 a new line) if we have actual replacement text. */
2034 if (c->m_len > 0)
734caf84 2035 {
ff7410b8 2036 move_to_column (&column, start_column, true);
f085b618 2037 m_colorizer.set_fixit_insert ();
2038 pp_string (m_pp, c->m_text);
2039 m_colorizer.set_normal_text ();
2040 column += c->m_len;
734caf84 2041 }
2042 }
2043 }
3752e5b1 2044
2045 /* Add a trailing newline, if necessary. */
ff7410b8 2046 move_to_column (&column, 0, false);
3752e5b1 2047}
2048
2049/* Disable any colorization and emit a newline. */
2050
2051void
2052layout::print_newline ()
2053{
2054 m_colorizer.set_normal_text ();
2055 pp_newline (m_pp);
734caf84 2056}
2057
f0479000 2058/* Return true if (ROW/COLUMN) is within a range of the layout.
2059 If it returns true, OUT_STATE is written to, with the
2060 range index, and whether we should draw the caret at
2061 (ROW/COLUMN) (as opposed to an underline). */
2062
2063bool
2064layout::get_state_at_point (/* Inputs. */
d73881b0 2065 linenum_type row, int column,
f0479000 2066 int first_non_ws, int last_non_ws,
2067 /* Outputs. */
2068 point_state *out_state)
2069{
2070 layout_range *range;
fee30e05 2071 int i;
f0479000 2072 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2073 {
5fe20025 2074 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2075 /* Bail out early, so that such ranges don't affect underlining or
2076 source colorization. */
2077 continue;
2078
f0479000 2079 if (range->contains_point (row, column))
2080 {
2081 out_state->range_idx = i;
2082
2083 /* Are we at the range's caret? is it visible? */
2084 out_state->draw_caret_p = false;
5fe20025 2085 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
415d5641 2086 && row == range->m_caret.m_line
f0479000 2087 && column == range->m_caret.m_column)
415d5641 2088 out_state->draw_caret_p = true;
fee30e05 2089
f0479000 2090 /* Within a multiline range, don't display any underline
2091 in any leading or trailing whitespace on a line.
2092 We do display carets, however. */
2093 if (!out_state->draw_caret_p)
2094 if (column < first_non_ws || column > last_non_ws)
2095 return false;
2096
2097 /* We are within a range. */
2098 return true;
2099 }
2100 }
2101
2102 return false;
2103}
2104
2105/* Helper function for use by layout::print_line when printing the
2106 annotation line under the source line.
2107 Get the column beyond the rightmost one that could contain a caret or
2108 range marker, given that we stop rendering at trailing whitespace.
2109 ROW is the source line within the given file.
2110 CARET_COLUMN is the column of range 0's caret.
2111 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2112 character of source (as determined when printing the source line). */
2113
2114int
d73881b0 2115layout::get_x_bound_for_row (linenum_type row, int caret_column,
f0479000 2116 int last_non_ws_column)
2117{
2118 int result = caret_column + 1;
2119
2120 layout_range *range;
2121 int i;
2122 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
fee30e05 2123 {
f0479000 2124 if (row >= range->m_start.m_line)
2125 {
2126 if (range->m_finish.m_line == row)
2127 {
2128 /* On the final line within a range; ensure that
2129 we render up to the end of the range. */
2130 if (result <= range->m_finish.m_column)
2131 result = range->m_finish.m_column + 1;
2132 }
2133 else if (row < range->m_finish.m_line)
2134 {
2135 /* Within a multiline range; ensure that we render up to the
2136 last non-whitespace column. */
2137 if (result <= last_non_ws_column)
2138 result = last_non_ws_column + 1;
2139 }
2140 }
fee30e05 2141 }
f0479000 2142
2143 return result;
2144}
2145
734caf84 2146/* Given *COLUMN as an x-coordinate, print spaces to position
2147 successive output at DEST_COLUMN, printing a newline if necessary,
ff7410b8 2148 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2149 left margin after any newline. */
734caf84 2150
2151void
ff7410b8 2152layout::move_to_column (int *column, int dest_column, bool add_left_margin)
734caf84 2153{
2154 /* Start a new line if we need to. */
2155 if (*column > dest_column)
2156 {
3752e5b1 2157 print_newline ();
ff7410b8 2158 if (add_left_margin)
2159 start_annotation_line ();
4df8da84 2160 *column = m_x_offset;
734caf84 2161 }
2162
2163 while (*column < dest_column)
2164 {
2165 pp_space (m_pp);
2166 (*column)++;
2167 }
2168}
2169
abf93a25 2170/* For debugging layout issues, render a ruler giving column numbers
2171 (after the 1-column indent). */
2172
2173void
2174layout::show_ruler (int max_column) const
2175{
2176 /* Hundreds. */
2177 if (max_column > 99)
2178 {
ff7410b8 2179 start_annotation_line ();
abf93a25 2180 pp_space (m_pp);
2181 for (int column = 1 + m_x_offset; column <= max_column; column++)
c9281ef8 2182 if (column % 10 == 0)
abf93a25 2183 pp_character (m_pp, '0' + (column / 100) % 10);
2184 else
2185 pp_space (m_pp);
2186 pp_newline (m_pp);
2187 }
2188
2189 /* Tens. */
ff7410b8 2190 start_annotation_line ();
abf93a25 2191 pp_space (m_pp);
2192 for (int column = 1 + m_x_offset; column <= max_column; column++)
c9281ef8 2193 if (column % 10 == 0)
abf93a25 2194 pp_character (m_pp, '0' + (column / 10) % 10);
2195 else
2196 pp_space (m_pp);
2197 pp_newline (m_pp);
2198
2199 /* Units. */
ff7410b8 2200 start_annotation_line ();
abf93a25 2201 pp_space (m_pp);
2202 for (int column = 1 + m_x_offset; column <= max_column; column++)
2203 pp_character (m_pp, '0' + (column % 10));
2204 pp_newline (m_pp);
2205}
2206
896d130e 2207/* Print leading fix-its (for new lines inserted before the source line)
2208 then the source line, followed by an annotation line
2209 consisting of any caret/underlines, then any fixits.
2210 If the source line can't be read, print nothing. */
2211void
d73881b0 2212layout::print_line (linenum_type row)
896d130e 2213{
0bce23e1 2214 char_span line = location_get_source_line (m_exploc.file, row);
896d130e 2215 if (!line)
2216 return;
2217
2218 line_bounds lbounds;
2219 print_leading_fixits (row);
0bce23e1 2220 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
896d130e 2221 if (should_print_annotation_line_p (row))
2222 print_annotation_line (row, lbounds);
b7bb5264 2223 if (m_show_labels_p)
2224 print_any_labels (row);
896d130e 2225 print_trailing_fixits (row);
2226}
2227
f0479000 2228} /* End of anonymous namespace. */
2229
a8a31e3d 2230/* If LOC is within the spans of lines that will already be printed for
2231 this gcc_rich_location, then add it as a secondary location and return true.
2232
2233 Otherwise return false. */
2234
2235bool
2236gcc_rich_location::add_location_if_nearby (location_t loc)
2237{
2238 /* Use the layout location-handling logic to sanitize LOC,
2239 filtering it to the current line spans within a temporary
2240 layout instance. */
2241 layout layout (global_dc, this, DK_ERROR);
2242 location_range loc_range;
2243 loc_range.m_loc = loc;
5fe20025 2244 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
a2507e3d 2245 if (!layout.maybe_add_location_range (&loc_range, 0, true))
a8a31e3d 2246 return false;
2247
5fe20025 2248 add_range (loc);
a8a31e3d 2249 return true;
2250}
2251
f0479000 2252/* Print the physical source code corresponding to the location of
2253 this diagnostic, with additional annotations. */
2254
2255void
2256diagnostic_show_locus (diagnostic_context * context,
48a7392b 2257 rich_location *richloc,
2258 diagnostic_t diagnostic_kind)
f0479000 2259{
3752e5b1 2260 pp_newline (context->printer);
2261
48a7392b 2262 location_t loc = richloc->get_loc ();
d0f11d68 2263 /* Do nothing if source-printing has been disabled. */
2264 if (!context->show_caret)
2265 return;
2266
2267 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
48a7392b 2268 if (loc <= BUILTINS_LOCATION)
d0f11d68 2269 return;
2270
2271 /* Don't print the same source location twice in a row, unless we have
2272 fix-it hints. */
48a7392b 2273 if (loc == context->last_location
2274 && richloc->get_num_fixit_hints () == 0)
f0479000 2275 return;
2276
48a7392b 2277 context->last_location = loc;
f0479000 2278
cba058c7 2279 char *saved_prefix = pp_take_prefix (context->printer);
f0479000 2280 pp_set_prefix (context->printer, NULL);
2281
48a7392b 2282 layout layout (context, richloc, diagnostic_kind);
aec1f4bd 2283 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2284 line_span_idx++)
3752e5b1 2285 {
aec1f4bd 2286 const line_span *line_span = layout.get_line_span (line_span_idx);
dd9ed701 2287 if (context->show_line_numbers_p)
aec1f4bd 2288 {
dd9ed701 2289 /* With line numbers, we should show whenever the line-numbering
2290 "jumps". */
2291 if (line_span_idx > 0)
2292 layout.print_gap_in_line_numbering ();
2293 }
2294 else
2295 {
2296 /* Without line numbers, we print headings for some line spans. */
2297 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2298 {
2299 expanded_location exploc
2300 = layout.get_expanded_location (line_span);
2301 context->start_span (context, exploc);
2302 }
aec1f4bd 2303 }
d73881b0 2304 linenum_type last_line = line_span->get_last_line ();
2305 for (linenum_type row = line_span->get_first_line ();
2306 row <= last_line; row++)
896d130e 2307 layout.print_line (row);
3752e5b1 2308 }
f0479000 2309
fee30e05 2310 pp_set_prefix (context->printer, saved_prefix);
fee30e05 2311}
99b4f3a2 2312
2313#if CHECKING_P
2314
2315namespace selftest {
2316
48a7392b 2317/* Selftests for diagnostic_show_locus. */
2318
48a7392b 2319/* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2320
2321static void
2322test_diagnostic_show_locus_unknown_location ()
2323{
2324 test_diagnostic_context dc;
2325 rich_location richloc (line_table, UNKNOWN_LOCATION);
2326 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2327 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2328}
2329
2330/* Verify that diagnostic_show_locus works sanely for various
2331 single-line cases.
2332
2333 All of these work on the following 1-line source file:
2334 .0000000001111111
2335 .1234567890123456
2336 "foo = bar.field;\n"
2337 which is set up by test_diagnostic_show_locus_one_liner and calls
2338 them. */
2339
2340/* Just a caret. */
2341
2342static void
2343test_one_liner_simple_caret ()
2344{
2345 test_diagnostic_context dc;
2346 location_t caret = linemap_position_for_column (line_table, 10);
2347 rich_location richloc (line_table, caret);
2348 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2349 ASSERT_STREQ ("\n"
2350 " foo = bar.field;\n"
2351 " ^\n",
2352 pp_formatted_text (dc.printer));
2353}
2354
2355/* Caret and range. */
2356
2357static void
2358test_one_liner_caret_and_range ()
2359{
2360 test_diagnostic_context dc;
2361 location_t caret = linemap_position_for_column (line_table, 10);
2362 location_t start = linemap_position_for_column (line_table, 7);
2363 location_t finish = linemap_position_for_column (line_table, 15);
2364 location_t loc = make_location (caret, start, finish);
2365 rich_location richloc (line_table, loc);
2366 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2367 ASSERT_STREQ ("\n"
2368 " foo = bar.field;\n"
2369 " ~~~^~~~~~\n",
2370 pp_formatted_text (dc.printer));
2371}
2372
2373/* Multiple ranges and carets. */
2374
2375static void
2376test_one_liner_multiple_carets_and_ranges ()
2377{
2378 test_diagnostic_context dc;
2379 location_t foo
2380 = make_location (linemap_position_for_column (line_table, 2),
2381 linemap_position_for_column (line_table, 1),
2382 linemap_position_for_column (line_table, 3));
2383 dc.caret_chars[0] = 'A';
2384
2385 location_t bar
2386 = make_location (linemap_position_for_column (line_table, 8),
2387 linemap_position_for_column (line_table, 7),
2388 linemap_position_for_column (line_table, 9));
2389 dc.caret_chars[1] = 'B';
2390
2391 location_t field
2392 = make_location (linemap_position_for_column (line_table, 13),
2393 linemap_position_for_column (line_table, 11),
2394 linemap_position_for_column (line_table, 15));
2395 dc.caret_chars[2] = 'C';
2396
2397 rich_location richloc (line_table, foo);
5fe20025 2398 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2399 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
48a7392b 2400 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2401 ASSERT_STREQ ("\n"
2402 " foo = bar.field;\n"
2403 " ~A~ ~B~ ~~C~~\n",
2404 pp_formatted_text (dc.printer));
2405}
2406
2407/* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2408
2409static void
68ef907c 2410test_one_liner_fixit_insert_before ()
48a7392b 2411{
2412 test_diagnostic_context dc;
2413 location_t caret = linemap_position_for_column (line_table, 7);
2414 rich_location richloc (line_table, caret);
68ef907c 2415 richloc.add_fixit_insert_before ("&");
48a7392b 2416 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2417 ASSERT_STREQ ("\n"
2418 " foo = bar.field;\n"
2419 " ^\n"
2420 " &\n",
2421 pp_formatted_text (dc.printer));
2422}
2423
68ef907c 2424/* Insertion fix-it hint: adding a "[0]" after "foo". */
2425
2426static void
2427test_one_liner_fixit_insert_after ()
2428{
2429 test_diagnostic_context dc;
2430 location_t start = linemap_position_for_column (line_table, 1);
2431 location_t finish = linemap_position_for_column (line_table, 3);
2432 location_t foo = make_location (start, start, finish);
2433 rich_location richloc (line_table, foo);
2434 richloc.add_fixit_insert_after ("[0]");
2435 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2436 ASSERT_STREQ ("\n"
2437 " foo = bar.field;\n"
2438 " ^~~\n"
2439 " [0]\n",
2440 pp_formatted_text (dc.printer));
2441}
2442
48a7392b 2443/* Removal fix-it hint: removal of the ".field". */
2444
2445static void
2446test_one_liner_fixit_remove ()
2447{
2448 test_diagnostic_context dc;
2449 location_t start = linemap_position_for_column (line_table, 10);
2450 location_t finish = linemap_position_for_column (line_table, 15);
2451 location_t dot = make_location (start, start, finish);
2452 rich_location richloc (line_table, dot);
850c2009 2453 richloc.add_fixit_remove ();
48a7392b 2454 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2455 ASSERT_STREQ ("\n"
2456 " foo = bar.field;\n"
2457 " ^~~~~~\n"
2458 " ------\n",
2459 pp_formatted_text (dc.printer));
2460}
2461
2462/* Replace fix-it hint: replacing "field" with "m_field". */
2463
2464static void
2465test_one_liner_fixit_replace ()
2466{
2467 test_diagnostic_context dc;
2468 location_t start = linemap_position_for_column (line_table, 11);
2469 location_t finish = linemap_position_for_column (line_table, 15);
2470 location_t field = make_location (start, start, finish);
2471 rich_location richloc (line_table, field);
850c2009 2472 richloc.add_fixit_replace ("m_field");
48a7392b 2473 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2474 ASSERT_STREQ ("\n"
2475 " foo = bar.field;\n"
2476 " ^~~~~\n"
2477 " m_field\n",
2478 pp_formatted_text (dc.printer));
2479}
2480
73ffb7be 2481/* Replace fix-it hint: replacing "field" with "m_field",
2482 but where the caret was elsewhere. */
2483
2484static void
2485test_one_liner_fixit_replace_non_equal_range ()
2486{
2487 test_diagnostic_context dc;
2488 location_t equals = linemap_position_for_column (line_table, 5);
2489 location_t start = linemap_position_for_column (line_table, 11);
2490 location_t finish = linemap_position_for_column (line_table, 15);
2491 rich_location richloc (line_table, equals);
2492 source_range range;
2493 range.m_start = start;
2494 range.m_finish = finish;
2495 richloc.add_fixit_replace (range, "m_field");
2496 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2497 /* The replacement range is not indicated in the annotation line, so
2498 it should be indicated via an additional underline. */
2499 ASSERT_STREQ ("\n"
2500 " foo = bar.field;\n"
2501 " ^\n"
2502 " -----\n"
2503 " m_field\n",
2504 pp_formatted_text (dc.printer));
2505}
2506
2507/* Replace fix-it hint: replacing "field" with "m_field",
2508 where the caret was elsewhere, but where a secondary range
2509 exactly covers "field". */
2510
2511static void
2512test_one_liner_fixit_replace_equal_secondary_range ()
2513{
2514 test_diagnostic_context dc;
2515 location_t equals = linemap_position_for_column (line_table, 5);
2516 location_t start = linemap_position_for_column (line_table, 11);
2517 location_t finish = linemap_position_for_column (line_table, 15);
2518 rich_location richloc (line_table, equals);
2519 location_t field = make_location (start, start, finish);
5fe20025 2520 richloc.add_range (field);
850c2009 2521 richloc.add_fixit_replace (field, "m_field");
73ffb7be 2522 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2523 /* The replacement range is indicated in the annotation line,
2524 so it shouldn't be indicated via an additional underline. */
2525 ASSERT_STREQ ("\n"
2526 " foo = bar.field;\n"
2527 " ^ ~~~~~\n"
2528 " m_field\n",
2529 pp_formatted_text (dc.printer));
2530}
2531
a02b4eae 2532/* Verify that we can use ad-hoc locations when adding fixits to a
2533 rich_location. */
2534
2535static void
2536test_one_liner_fixit_validation_adhoc_locations ()
2537{
2538 /* Generate a range that's too long to be packed, so must
2539 be stored as an ad-hoc location (given the defaults
2540 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2541 const location_t c7 = linemap_position_for_column (line_table, 7);
2542 const location_t c47 = linemap_position_for_column (line_table, 47);
2543 const location_t loc = make_location (c7, c7, c47);
2544
2545 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2546 return;
2547
2548 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2549
2550 /* Insert. */
2551 {
2552 rich_location richloc (line_table, loc);
68ef907c 2553 richloc.add_fixit_insert_before (loc, "test");
a02b4eae 2554 /* It should not have been discarded by the validator. */
2555 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2556
2557 test_diagnostic_context dc;
2558 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2559 ASSERT_STREQ ("\n"
2560 " foo = bar.field;\n"
2561 " ^~~~~~~~~~ \n"
2562 " test\n",
2563 pp_formatted_text (dc.printer));
2564 }
2565
2566 /* Remove. */
2567 {
2568 rich_location richloc (line_table, loc);
2569 source_range range = source_range::from_locations (loc, c47);
2570 richloc.add_fixit_remove (range);
2571 /* It should not have been discarded by the validator. */
2572 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2573
2574 test_diagnostic_context dc;
2575 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2576 ASSERT_STREQ ("\n"
2577 " foo = bar.field;\n"
2578 " ^~~~~~~~~~ \n"
2579 " -----------------------------------------\n",
2580 pp_formatted_text (dc.printer));
2581 }
2582
2583 /* Replace. */
2584 {
2585 rich_location richloc (line_table, loc);
2586 source_range range = source_range::from_locations (loc, c47);
2587 richloc.add_fixit_replace (range, "test");
2588 /* It should not have been discarded by the validator. */
2589 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2590
2591 test_diagnostic_context dc;
2592 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2593 ASSERT_STREQ ("\n"
2594 " foo = bar.field;\n"
2595 " ^~~~~~~~~~ \n"
2596 " test\n",
2597 pp_formatted_text (dc.printer));
2598 }
2599}
2600
be45049f 2601/* Test of consolidating insertions at the same location. */
d6dd1b60 2602
2603static void
be45049f 2604test_one_liner_many_fixits_1 ()
d6dd1b60 2605{
2606 test_diagnostic_context dc;
2607 location_t equals = linemap_position_for_column (line_table, 5);
2608 rich_location richloc (line_table, equals);
2609 for (int i = 0; i < 19; i++)
68ef907c 2610 richloc.add_fixit_insert_before ("a");
be45049f 2611 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2612 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2613 ASSERT_STREQ ("\n"
2614 " foo = bar.field;\n"
2615 " ^\n"
2616 " aaaaaaaaaaaaaaaaaaa\n",
2617 pp_formatted_text (dc.printer));
2618}
2619
2620/* Ensure that we can add an arbitrary number of fix-it hints to a
2621 rich_location, even if they are not consolidated. */
2622
2623static void
2624test_one_liner_many_fixits_2 ()
2625{
2626 test_diagnostic_context dc;
2627 location_t equals = linemap_position_for_column (line_table, 5);
2628 rich_location richloc (line_table, equals);
2629 for (int i = 0; i < 19; i++)
2630 {
2631 location_t loc = linemap_position_for_column (line_table, i * 2);
2632 richloc.add_fixit_insert_before (loc, "a");
2633 }
d6dd1b60 2634 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2635 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2636 ASSERT_STREQ ("\n"
2637 " foo = bar.field;\n"
2638 " ^\n"
be45049f 2639 "a a a a a a a a a a a a a a a a a a a\n",
d6dd1b60 2640 pp_formatted_text (dc.printer));
2641}
2642
b7bb5264 2643/* Test of labeling the ranges within a rich_location. */
2644
2645static void
2646test_one_liner_labels ()
2647{
2648 location_t foo
2649 = make_location (linemap_position_for_column (line_table, 1),
2650 linemap_position_for_column (line_table, 1),
2651 linemap_position_for_column (line_table, 3));
2652 location_t bar
2653 = make_location (linemap_position_for_column (line_table, 7),
2654 linemap_position_for_column (line_table, 7),
2655 linemap_position_for_column (line_table, 9));
2656 location_t field
2657 = make_location (linemap_position_for_column (line_table, 11),
2658 linemap_position_for_column (line_table, 11),
2659 linemap_position_for_column (line_table, 15));
2660
2661 /* Example where all the labels fit on one line. */
2662 {
2663 text_range_label label0 ("0");
2664 text_range_label label1 ("1");
2665 text_range_label label2 ("2");
2666 gcc_rich_location richloc (foo, &label0);
5fe20025 2667 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2668 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2669
2670 {
2671 test_diagnostic_context dc;
2672 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2673 ASSERT_STREQ ("\n"
2674 " foo = bar.field;\n"
2675 " ^~~ ~~~ ~~~~~\n"
2676 " | | |\n"
2677 " 0 1 2\n",
2678 pp_formatted_text (dc.printer));
2679 }
2680
2681 /* Verify that we can disable label-printing. */
2682 {
2683 test_diagnostic_context dc;
2684 dc.show_labels_p = false;
2685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2686 ASSERT_STREQ ("\n"
2687 " foo = bar.field;\n"
2688 " ^~~ ~~~ ~~~~~\n",
2689 pp_formatted_text (dc.printer));
2690 }
2691 }
2692
2693 /* Example where the labels need extra lines. */
2694 {
2695 text_range_label label0 ("label 0");
2696 text_range_label label1 ("label 1");
2697 text_range_label label2 ("label 2");
2698 gcc_rich_location richloc (foo, &label0);
5fe20025 2699 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2700 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2701
2702 test_diagnostic_context dc;
2703 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2704 ASSERT_STREQ ("\n"
2705 " foo = bar.field;\n"
2706 " ^~~ ~~~ ~~~~~\n"
2707 " | | |\n"
2708 " | | label 2\n"
2709 " | label 1\n"
2710 " label 0\n",
2711 pp_formatted_text (dc.printer));
2712 }
2713
2714 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2715 but label 1 just touches label 2. */
2716 {
2717 text_range_label label0 ("aaaaa");
2718 text_range_label label1 ("bbbb");
2719 text_range_label label2 ("c");
2720 gcc_rich_location richloc (foo, &label0);
5fe20025 2721 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2722 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2723
2724 test_diagnostic_context dc;
2725 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2726 ASSERT_STREQ ("\n"
2727 " foo = bar.field;\n"
2728 " ^~~ ~~~ ~~~~~\n"
2729 " | | |\n"
2730 " | | c\n"
2731 " aaaaa bbbb\n",
2732 pp_formatted_text (dc.printer));
2733 }
2734
2735 /* Example of out-of-order ranges (thus requiring a sort). */
2736 {
2737 text_range_label label0 ("0");
2738 text_range_label label1 ("1");
2739 text_range_label label2 ("2");
2740 gcc_rich_location richloc (field, &label0);
5fe20025 2741 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2742 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2743
2744 test_diagnostic_context dc;
2745 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2746 ASSERT_STREQ ("\n"
2747 " foo = bar.field;\n"
2748 " ~~~ ~~~ ^~~~~\n"
2749 " | | |\n"
2750 " 2 1 0\n",
2751 pp_formatted_text (dc.printer));
2752 }
2753
2754 /* Ensure we don't ICE if multiple ranges with labels are on
2755 the same point. */
2756 {
2757 text_range_label label0 ("label 0");
2758 text_range_label label1 ("label 1");
2759 text_range_label label2 ("label 2");
2760 gcc_rich_location richloc (bar, &label0);
5fe20025 2761 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2762 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2763
2764 test_diagnostic_context dc;
2765 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2766 ASSERT_STREQ ("\n"
2767 " foo = bar.field;\n"
2768 " ^~~\n"
2769 " |\n"
2770 " label 2\n"
2771 " label 1\n"
2772 " label 0\n",
2773 pp_formatted_text (dc.printer));
2774 }
2775
2776 /* Verify that a NULL result from range_label::get_text is
2777 handled gracefully. */
2778 {
2779 text_range_label label (NULL);
2780 gcc_rich_location richloc (bar, &label);
2781
2782 test_diagnostic_context dc;
2783 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2784 ASSERT_STREQ ("\n"
2785 " foo = bar.field;\n"
2786 " ^~~\n",
2787 pp_formatted_text (dc.printer));
2788 }
2789
2790 /* TODO: example of formatted printing (needs to be in
2791 gcc-rich-location.c due to Makefile.in issues). */
2792}
2793
48a7392b 2794/* Run the various one-liner tests. */
2795
2796static void
2797test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2798{
2799 /* Create a tempfile and write some text to it.
2800 ....................0000000001111111.
2801 ....................1234567890123456. */
2802 const char *content = "foo = bar.field;\n";
2803 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2804 line_table_test ltt (case_);
2805
2806 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2807
2808 location_t line_end = linemap_position_for_column (line_table, 16);
2809
2810 /* Don't attempt to run the tests if column data might be unavailable. */
2811 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2812 return;
2813
2814 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2815 ASSERT_EQ (1, LOCATION_LINE (line_end));
2816 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2817
2818 test_one_liner_simple_caret ();
2819 test_one_liner_caret_and_range ();
2820 test_one_liner_multiple_carets_and_ranges ();
68ef907c 2821 test_one_liner_fixit_insert_before ();
2822 test_one_liner_fixit_insert_after ();
48a7392b 2823 test_one_liner_fixit_remove ();
2824 test_one_liner_fixit_replace ();
73ffb7be 2825 test_one_liner_fixit_replace_non_equal_range ();
2826 test_one_liner_fixit_replace_equal_secondary_range ();
a02b4eae 2827 test_one_liner_fixit_validation_adhoc_locations ();
be45049f 2828 test_one_liner_many_fixits_1 ();
2829 test_one_liner_many_fixits_2 ();
b7bb5264 2830 test_one_liner_labels ();
48a7392b 2831}
2832
a8a31e3d 2833/* Verify that gcc_rich_location::add_location_if_nearby works. */
2834
2835static void
2836test_add_location_if_nearby (const line_table_case &case_)
2837{
2838 /* Create a tempfile and write some text to it.
2839 ...000000000111111111122222222223333333333.
2840 ...123456789012345678901234567890123456789. */
2841 const char *content
2842 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2843 "struct different_line\n" /* line 2. */
2844 "{\n" /* line 3. */
2845 " double x;\n" /* line 4. */
2846 " double y;\n" /* line 5. */
2847 ";\n"); /* line 6. */
2848 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2849 line_table_test ltt (case_);
2850
2851 const line_map_ordinary *ord_map
2852 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2853 tmp.get_filename (), 0));
2854
2855 linemap_line_start (line_table, 1, 100);
2856
2857 const location_t final_line_end
2858 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2859
2860 /* Don't attempt to run the tests if column data might be unavailable. */
2861 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2862 return;
2863
2864 /* Test of add_location_if_nearby on the same line as the
2865 primary location. */
2866 {
2867 const location_t missing_close_brace_1_39
2868 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2869 const location_t matching_open_brace_1_18
2870 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2871 gcc_rich_location richloc (missing_close_brace_1_39);
2872 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2873 ASSERT_TRUE (added);
2874 ASSERT_EQ (2, richloc.get_num_locations ());
2875 test_diagnostic_context dc;
2876 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2877 ASSERT_STREQ ("\n"
2878 " struct same_line { double x; double y; ;\n"
2879 " ~ ^\n",
2880 pp_formatted_text (dc.printer));
2881 }
2882
2883 /* Test of add_location_if_nearby on a different line to the
2884 primary location. */
2885 {
2886 const location_t missing_close_brace_6_1
2887 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2888 const location_t matching_open_brace_3_1
2889 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2890 gcc_rich_location richloc (missing_close_brace_6_1);
2891 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2892 ASSERT_FALSE (added);
2893 ASSERT_EQ (1, richloc.get_num_locations ());
2894 }
2895}
2896
70017c99 2897/* Verify that we print fixits even if they only affect lines
2898 outside those covered by the ranges in the rich_location. */
2899
2900static void
2901test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2902{
2903 /* Create a tempfile and write some text to it.
2904 ...000000000111111111122222222223333333333.
2905 ...123456789012345678901234567890123456789. */
2906 const char *content
2907 = ("struct point { double x; double y; };\n" /* line 1. */
2908 "struct point origin = {x: 0.0,\n" /* line 2. */
2909 " y\n" /* line 3. */
2910 "\n" /* line 4. */
2911 "\n" /* line 5. */
2912 " : 0.0};\n"); /* line 6. */
2913 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2914 line_table_test ltt (case_);
2915
2916 const line_map_ordinary *ord_map
2917 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2918 tmp.get_filename (), 0));
2919
2920 linemap_line_start (line_table, 1, 100);
2921
2922 const location_t final_line_end
2923 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2924
2925 /* Don't attempt to run the tests if column data might be unavailable. */
2926 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2927 return;
2928
2929 /* A pair of tests for modernizing the initializers to C99-style. */
2930
2931 /* The one-liner case (line 2). */
2932 {
2933 test_diagnostic_context dc;
2934 const location_t x
2935 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2936 const location_t colon
2937 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2938 rich_location richloc (line_table, colon);
68ef907c 2939 richloc.add_fixit_insert_before (x, ".");
70017c99 2940 richloc.add_fixit_replace (colon, "=");
2941 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2942 ASSERT_STREQ ("\n"
2943 " struct point origin = {x: 0.0,\n"
2944 " ^\n"
2945 " .=\n",
2946 pp_formatted_text (dc.printer));
2947 }
2948
2949 /* The multiline case. The caret for the rich_location is on line 6;
2950 verify that insertion fixit on line 3 is still printed (and that
2951 span starts are printed due to the gap between the span at line 3
2952 and that at line 6). */
2953 {
2954 test_diagnostic_context dc;
2955 const location_t y
2956 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2957 const location_t colon
2958 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2959 rich_location richloc (line_table, colon);
68ef907c 2960 richloc.add_fixit_insert_before (y, ".");
70017c99 2961 richloc.add_fixit_replace (colon, "=");
2962 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2963 ASSERT_STREQ ("\n"
2964 "FILENAME:3:24:\n"
2965 " y\n"
2966 " .\n"
2967 "FILENAME:6:25:\n"
2968 " : 0.0};\n"
2969 " ^\n"
2970 " =\n",
2971 pp_formatted_text (dc.printer));
2972 }
dd9ed701 2973
2974 /* As above, but verify the behavior of multiple line spans
2975 with line-numbering enabled. */
2976 {
2977 const location_t y
2978 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2979 const location_t colon
2980 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2981 rich_location richloc (line_table, colon);
2982 richloc.add_fixit_insert_before (y, ".");
2983 richloc.add_fixit_replace (colon, "=");
2984 test_diagnostic_context dc;
2985 dc.show_line_numbers_p = true;
2986 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2987 ASSERT_STREQ ("\n"
31087b7e 2988 " 3 | y\n"
2989 " | .\n"
2990 "......\n"
2991 " 6 | : 0.0};\n"
2992 " | ^\n"
2993 " | =\n",
dd9ed701 2994 pp_formatted_text (dc.printer));
2995 }
70017c99 2996}
2997
2998
367964fa 2999/* Verify that fix-it hints are appropriately consolidated.
3000
3001 If any fix-it hints in a rich_location involve locations beyond
3002 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3003 the fix-it as a whole, so there should be none.
3004
3005 Otherwise, verify that consecutive "replace" and "remove" fix-its
3006 are merged, and that other fix-its remain separate. */
3007
3008static void
3009test_fixit_consolidation (const line_table_case &case_)
3010{
3011 line_table_test ltt (case_);
3012
3013 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3014
3015 const location_t c10 = linemap_position_for_column (line_table, 10);
3016 const location_t c15 = linemap_position_for_column (line_table, 15);
3017 const location_t c16 = linemap_position_for_column (line_table, 16);
3018 const location_t c17 = linemap_position_for_column (line_table, 17);
3019 const location_t c20 = linemap_position_for_column (line_table, 20);
be45049f 3020 const location_t c21 = linemap_position_for_column (line_table, 21);
367964fa 3021 const location_t caret = c10;
3022
3023 /* Insert + insert. */
3024 {
3025 rich_location richloc (line_table, caret);
68ef907c 3026 richloc.add_fixit_insert_before (c10, "foo");
3027 richloc.add_fixit_insert_before (c15, "bar");
367964fa 3028
3029 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3030 /* Bogus column info for 2nd fixit, so no fixits. */
3031 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3032 else
3033 /* They should not have been merged. */
3034 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3035 }
3036
3037 /* Insert + replace. */
3038 {
3039 rich_location richloc (line_table, caret);
68ef907c 3040 richloc.add_fixit_insert_before (c10, "foo");
367964fa 3041 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3042 "bar");
3043
3044 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3045 /* Bogus column info for 2nd fixit, so no fixits. */
3046 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3047 else
3048 /* They should not have been merged. */
3049 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3050 }
3051
3052 /* Replace + non-consecutive insert. */
3053 {
3054 rich_location richloc (line_table, caret);
3055 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3056 "bar");
68ef907c 3057 richloc.add_fixit_insert_before (c17, "foo");
367964fa 3058
3059 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3060 /* Bogus column info for 2nd fixit, so no fixits. */
3061 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3062 else
3063 /* They should not have been merged. */
3064 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3065 }
3066
3067 /* Replace + non-consecutive replace. */
3068 {
3069 rich_location richloc (line_table, caret);
3070 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3071 "foo");
3072 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3073 "bar");
3074
3075 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3076 /* Bogus column info for 2nd fixit, so no fixits. */
3077 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3078 else
3079 /* They should not have been merged. */
3080 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3081 }
3082
3083 /* Replace + consecutive replace. */
3084 {
3085 rich_location richloc (line_table, caret);
3086 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3087 "foo");
3088 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3089 "bar");
3090
3091 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3092 /* Bogus column info for 2nd fixit, so no fixits. */
3093 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3094 else
3095 {
3096 /* They should have been merged into a single "replace". */
3097 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3098 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3099 ASSERT_STREQ ("foobar", hint->get_string ());
3100 ASSERT_EQ (c10, hint->get_start_loc ());
3101 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3102 }
3103 }
3104
3105 /* Replace + consecutive removal. */
3106 {
3107 rich_location richloc (line_table, caret);
3108 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3109 "foo");
3110 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3111
3112 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3113 /* Bogus column info for 2nd fixit, so no fixits. */
3114 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3115 else
3116 {
3117 /* They should have been merged into a single replace, with the
3118 range extended to cover that of the removal. */
3119 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3120 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3121 ASSERT_STREQ ("foo", hint->get_string ());
3122 ASSERT_EQ (c10, hint->get_start_loc ());
3123 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3124 }
3125 }
3126
3127 /* Consecutive removals. */
3128 {
3129 rich_location richloc (line_table, caret);
3130 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3131 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3132
3133 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3134 /* Bogus column info for 2nd fixit, so no fixits. */
3135 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3136 else
3137 {
3138 /* They should have been merged into a single "replace-with-empty". */
3139 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3140 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3141 ASSERT_STREQ ("", hint->get_string ());
3142 ASSERT_EQ (c10, hint->get_start_loc ());
3143 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3144 }
3145 }
3146}
3147
f085b618 3148/* Verify that the line_corrections machinery correctly prints
3149 overlapping fixit-hints. */
3150
3151static void
3152test_overlapped_fixit_printing (const line_table_case &case_)
3153{
3154 /* Create a tempfile and write some text to it.
3155 ...000000000111111111122222222223333333333.
3156 ...123456789012345678901234567890123456789. */
3157 const char *content
3158 = (" foo *f = (foo *)ptr->field;\n");
3159 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3160 line_table_test ltt (case_);
3161
3162 const line_map_ordinary *ord_map
3163 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3164 tmp.get_filename (), 0));
3165
3166 linemap_line_start (line_table, 1, 100);
3167
3168 const location_t final_line_end
3169 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3170
3171 /* Don't attempt to run the tests if column data might be unavailable. */
3172 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3173 return;
3174
3175 /* A test for converting a C-style cast to a C++-style cast. */
3176 const location_t open_paren
3177 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3178 const location_t close_paren
3179 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3180 const location_t expr_start
3181 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3182 const location_t expr_finish
3183 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3184 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3185
3186 /* Various examples of fix-it hints that aren't themselves consolidated,
3187 but for which the *printing* may need consolidation. */
3188
3189 /* Example where 3 fix-it hints are printed as one. */
3190 {
3191 test_diagnostic_context dc;
3192 rich_location richloc (line_table, expr);
3193 richloc.add_fixit_replace (open_paren, "const_cast<");
3194 richloc.add_fixit_replace (close_paren, "> (");
3195 richloc.add_fixit_insert_after (")");
3196
3197 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3198 ASSERT_STREQ ("\n"
3199 " foo *f = (foo *)ptr->field;\n"
3200 " ^~~~~~~~~~\n"
3201 " -----------------\n"
3202 " const_cast<foo *> (ptr->field)\n",
3203 pp_formatted_text (dc.printer));
3204
3205 /* Unit-test the line_corrections machinery. */
3206 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3207 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3208 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3209 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3210 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3211 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3212 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3213 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3214 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3215 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3216
3217 /* Add each hint in turn to a line_corrections instance,
3218 and verify that they are consolidated into one correction instance
3219 as expected. */
3220 line_corrections lc (tmp.get_filename (), 1);
3221
3222 /* The first replace hint by itself. */
3223 lc.add_hint (hint_0);
3224 ASSERT_EQ (1, lc.m_corrections.length ());
3225 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3226 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3227 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3228
3229 /* After the second replacement hint, they are printed together
3230 as a replacement (along with the text between them). */
3231 lc.add_hint (hint_1);
3232 ASSERT_EQ (1, lc.m_corrections.length ());
3233 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3234 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3235 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3236
3237 /* After the final insertion hint, they are all printed together
3238 as a replacement (along with the text between them). */
3239 lc.add_hint (hint_2);
3240 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3241 lc.m_corrections[0]->m_text);
3242 ASSERT_EQ (1, lc.m_corrections.length ());
3243 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3244 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3245 }
3246
3247 /* Example where two are consolidated during printing. */
3248 {
3249 test_diagnostic_context dc;
3250 rich_location richloc (line_table, expr);
3251 richloc.add_fixit_replace (open_paren, "CAST (");
3252 richloc.add_fixit_replace (close_paren, ") (");
3253 richloc.add_fixit_insert_after (")");
3254
3255 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3256 ASSERT_STREQ ("\n"
3257 " foo *f = (foo *)ptr->field;\n"
3258 " ^~~~~~~~~~\n"
3259 " -\n"
3260 " CAST (-\n"
3261 " ) ( )\n",
3262 pp_formatted_text (dc.printer));
3263 }
3264
3265 /* Example where none are consolidated during printing. */
3266 {
3267 test_diagnostic_context dc;
3268 rich_location richloc (line_table, expr);
3269 richloc.add_fixit_replace (open_paren, "CST (");
3270 richloc.add_fixit_replace (close_paren, ") (");
3271 richloc.add_fixit_insert_after (")");
3272
3273 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3274 ASSERT_STREQ ("\n"
3275 " foo *f = (foo *)ptr->field;\n"
3276 " ^~~~~~~~~~\n"
3277 " -\n"
3278 " CST ( -\n"
3279 " ) ( )\n",
3280 pp_formatted_text (dc.printer));
3281 }
3282
3283 /* Example of deletion fix-it hints. */
3284 {
3285 test_diagnostic_context dc;
3286 rich_location richloc (line_table, expr);
3287 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3288 source_range victim = {open_paren, close_paren};
3289 richloc.add_fixit_remove (victim);
3290
3291 /* This case is actually handled by fixit-consolidation,
3292 rather than by line_corrections. */
3293 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3294
3295 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3296 ASSERT_STREQ ("\n"
3297 " foo *f = (foo *)ptr->field;\n"
3298 " ^~~~~~~~~~\n"
3299 " -------\n"
3300 " (bar *)\n",
3301 pp_formatted_text (dc.printer));
3302 }
3303
3304 /* Example of deletion fix-it hints that would overlap. */
3305 {
3306 test_diagnostic_context dc;
3307 rich_location richloc (line_table, expr);
3308 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3309 source_range victim = {expr_start, expr_finish};
3310 richloc.add_fixit_remove (victim);
3311
3312 /* These fixits are not consolidated. */
3313 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3314
3315 /* But the corrections are. */
3316 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3317 ASSERT_STREQ ("\n"
3318 " foo *f = (foo *)ptr->field;\n"
3319 " ^~~~~~~~~~\n"
3320 " -----------------\n"
3321 " (longer *)(foo *)\n",
3322 pp_formatted_text (dc.printer));
3323 }
3324
3325 /* Example of insertion fix-it hints that would overlap. */
3326 {
3327 test_diagnostic_context dc;
3328 rich_location richloc (line_table, expr);
3329 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3330 richloc.add_fixit_insert_after (close_paren, "TEST");
3331
3332 /* The first insertion is long enough that if printed naively,
3333 it would overlap with the second.
3334 Verify that they are printed as a single replacement. */
3335 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3336 ASSERT_STREQ ("\n"
3337 " foo *f = (foo *)ptr->field;\n"
3338 " ^~~~~~~~~~\n"
3339 " -------\n"
3340 " LONGER THAN THE CAST(foo *)TEST\n",
3341 pp_formatted_text (dc.printer));
3342 }
3343}
3344
f907f132 3345/* Verify that the line_corrections machinery correctly prints
3346 overlapping fixit-hints that have been added in the wrong
3347 order.
3348 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3349
3350static void
3351test_overlapped_fixit_printing_2 (const line_table_case &case_)
3352{
3353 /* Create a tempfile and write some text to it.
3354 ...000000000111111111122222222223333333333.
3355 ...123456789012345678901234567890123456789. */
3356 const char *content
3357 = ("int a5[][0][0] = { 1, 2 };\n");
3358 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3359 line_table_test ltt (case_);
3360
3361 const line_map_ordinary *ord_map
3362 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3363 tmp.get_filename (), 0));
3364
3365 linemap_line_start (line_table, 1, 100);
3366
3367 const location_t final_line_end
3368 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3369
3370 /* Don't attempt to run the tests if column data might be unavailable. */
3371 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3372 return;
3373
3374 const location_t col_1
3375 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3376 const location_t col_20
3377 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3378 const location_t col_21
3379 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3380 const location_t col_23
3381 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3382 const location_t col_25
3383 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3384
3385 /* Two insertions, in the wrong order. */
3386 {
3387 rich_location richloc (line_table, col_20);
3388 richloc.add_fixit_insert_before (col_23, "{");
3389 richloc.add_fixit_insert_before (col_21, "}");
3390
3391 /* These fixits should be accepted; they can't be consolidated. */
3392 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3393 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3394 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3395 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3396 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3397 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3398 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3399
3400 /* Verify that they're printed correctly. */
3401 test_diagnostic_context dc;
3402 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3403 ASSERT_STREQ ("\n"
3404 " int a5[][0][0] = { 1, 2 };\n"
3405 " ^\n"
3406 " } {\n",
3407 pp_formatted_text (dc.printer));
3408 }
3409
3410 /* Various overlapping insertions, some occurring "out of order"
3411 (reproducing the fix-it hints from PR c/81405). */
3412 {
3413 test_diagnostic_context dc;
3414 rich_location richloc (line_table, col_20);
3415
3416 richloc.add_fixit_insert_before (col_20, "{{");
3417 richloc.add_fixit_insert_before (col_21, "}}");
3418 richloc.add_fixit_insert_before (col_23, "{");
3419 richloc.add_fixit_insert_before (col_21, "}");
3420 richloc.add_fixit_insert_before (col_23, "{{");
3421 richloc.add_fixit_insert_before (col_25, "}");
3422 richloc.add_fixit_insert_before (col_21, "}");
3423 richloc.add_fixit_insert_before (col_1, "{");
3424 richloc.add_fixit_insert_before (col_25, "}");
3425 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3426 ASSERT_STREQ ("\n"
3427 " int a5[][0][0] = { 1, 2 };\n"
3428 " ^\n"
3429 " { -----\n"
3430 " {{1}}}}, {{{2 }}\n",
3431 pp_formatted_text (dc.printer));
3432 }
3433}
3434
896d130e 3435/* Insertion fix-it hint: adding a "break;" on a line by itself. */
d9020fe6 3436
3437static void
3438test_fixit_insert_containing_newline (const line_table_case &case_)
3439{
3440 /* Create a tempfile and write some text to it.
3441 .........................0000000001111111.
3442 .........................1234567890123456. */
3443 const char *old_content = (" case 'a':\n" /* line 1. */
3444 " x = a;\n" /* line 2. */
3445 " case 'b':\n" /* line 3. */
3446 " x = b;\n");/* line 4. */
3447
3448 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3449 line_table_test ltt (case_);
3450 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3451
d9020fe6 3452 location_t case_start = linemap_position_for_column (line_table, 5);
3453 location_t case_finish = linemap_position_for_column (line_table, 13);
3454 location_t case_loc = make_location (case_start, case_start, case_finish);
d9020fe6 3455 location_t line_start = linemap_position_for_column (line_table, 1);
d9020fe6 3456
3457 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3458 return;
3459
896d130e 3460 /* Add a "break;" on a line by itself before line 3 i.e. before
3461 column 1 of line 3. */
3462 {
3463 rich_location richloc (line_table, case_loc);
3464 richloc.add_fixit_insert_before (line_start, " break;\n");
e69492e4 3465
3466 /* Without line numbers. */
3467 {
3468 test_diagnostic_context dc;
3469 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3470 ASSERT_STREQ ("\n"
3471 " x = a;\n"
3472 "+ break;\n"
3473 " case 'b':\n"
3474 " ^~~~~~~~~\n",
3475 pp_formatted_text (dc.printer));
3476 }
3477
3478 /* With line numbers. */
3479 {
3480 test_diagnostic_context dc;
3481 dc.show_line_numbers_p = true;
3482 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3483 ASSERT_STREQ ("\n"
31087b7e 3484 " 2 | x = a;\n"
3485 " +++ |+ break;\n"
3486 " 3 | case 'b':\n"
3487 " | ^~~~~~~~~\n",
e69492e4 3488 pp_formatted_text (dc.printer));
3489 }
896d130e 3490 }
3491
3492 /* Verify that attempts to add text with a newline fail when the
3493 insertion point is *not* at the start of a line. */
3494 {
3495 rich_location richloc (line_table, case_loc);
3496 richloc.add_fixit_insert_before (case_start, "break;\n");
3497 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3498 test_diagnostic_context dc;
3499 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3500 ASSERT_STREQ ("\n"
3501 " case 'b':\n"
3502 " ^~~~~~~~~\n",
3503 pp_formatted_text (dc.printer));
3504 }
3505}
3506
3507/* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3508 of the file, where the fix-it is printed in a different line-span
3509 to the primary range of the diagnostic. */
3510
3511static void
3512test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3513{
3514 /* Create a tempfile and write some text to it.
3515 .........................0000000001111111.
3516 .........................1234567890123456. */
3517 const char *old_content = ("test (int ch)\n" /* line 1. */
3518 "{\n" /* line 2. */
3519 " putchar (ch);\n" /* line 3. */
3520 "}\n"); /* line 4. */
3521
3522 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3523 line_table_test ltt (case_);
3524
3525 const line_map_ordinary *ord_map = linemap_check_ordinary
3526 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3527 linemap_line_start (line_table, 1, 100);
3528
3529 /* The primary range is the "putchar" token. */
3530 location_t putchar_start
3531 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3532 location_t putchar_finish
3533 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3534 location_t putchar_loc
3535 = make_location (putchar_start, putchar_start, putchar_finish);
3536 rich_location richloc (line_table, putchar_loc);
3537
3538 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3539 location_t file_start
3540 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3541 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3542
3543 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3544 return;
3545
dd9ed701 3546 {
3547 test_diagnostic_context dc;
3548 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3549 ASSERT_STREQ ("\n"
3550 "FILENAME:1:1:\n"
3551 "+#include <stdio.h>\n"
3552 " test (int ch)\n"
3553 "FILENAME:3:2:\n"
3554 " putchar (ch);\n"
3555 " ^~~~~~~\n",
3556 pp_formatted_text (dc.printer));
3557 }
3558
3559 /* With line-numbering, the line spans are close enough to be
3560 consolidated, since it makes little sense to skip line 2. */
3561 {
3562 test_diagnostic_context dc;
3563 dc.show_line_numbers_p = true;
3564 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3565 ASSERT_STREQ ("\n"
31087b7e 3566 " +++ |+#include <stdio.h>\n"
3567 " 1 | test (int ch)\n"
3568 " 2 | {\n"
3569 " 3 | putchar (ch);\n"
3570 " | ^~~~~~~\n",
dd9ed701 3571 pp_formatted_text (dc.printer));
3572 }
d9020fe6 3573}
3574
3575/* Replacement fix-it hint containing a newline.
896d130e 3576 This will fail, as newlines are only supported when inserting at the
3577 beginning of a line. */
d9020fe6 3578
3579static void
3580test_fixit_replace_containing_newline (const line_table_case &case_)
3581{
3582 /* Create a tempfile and write some text to it.
3583 .........................0000000001111.
3584 .........................1234567890123. */
3585 const char *old_content = "foo = bar ();\n";
3586
3587 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3588 line_table_test ltt (case_);
3589 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3590
3591 /* Replace the " = " with "\n = ", as if we were reformatting an
3592 overly long line. */
3593 location_t start = linemap_position_for_column (line_table, 4);
3594 location_t finish = linemap_position_for_column (line_table, 6);
3595 location_t loc = linemap_position_for_column (line_table, 13);
3596 rich_location richloc (line_table, loc);
3597 source_range range = source_range::from_locations (start, finish);
3598 richloc.add_fixit_replace (range, "\n =");
3599
896d130e 3600 /* Arbitrary newlines are not yet supported within fix-it hints, so
d9020fe6 3601 the fix-it should not be displayed. */
3602 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3603
3604 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3605 return;
3606
3607 test_diagnostic_context dc;
3608 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3609 ASSERT_STREQ ("\n"
3610 " foo = bar ();\n"
3611 " ^\n",
3612 pp_formatted_text (dc.printer));
3613}
3614
c2403f36 3615/* Fix-it hint, attempting to delete a newline.
3616 This will fail, as we currently only support fix-it hints that
3617 affect one line at a time. */
3618
3619static void
3620test_fixit_deletion_affecting_newline (const line_table_case &case_)
3621{
3622 /* Create a tempfile and write some text to it.
3623 ..........................0000000001111.
3624 ..........................1234567890123. */
3625 const char *old_content = ("foo = bar (\n"
3626 " );\n");
3627
3628 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3629 line_table_test ltt (case_);
3630 const line_map_ordinary *ord_map = linemap_check_ordinary
3631 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3632 linemap_line_start (line_table, 1, 100);
3633
3634 /* Attempt to delete the " (\n...)". */
3635 location_t start
3636 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3637 location_t caret
3638 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3639 location_t finish
3640 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3641 location_t loc = make_location (caret, start, finish);
3642 rich_location richloc (line_table, loc);
3643 richloc. add_fixit_remove ();
3644
3645 /* Fix-it hints that affect more than one line are not yet supported, so
3646 the fix-it should not be displayed. */
3647 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3648
3649 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3650 return;
3651
3652 test_diagnostic_context dc;
3653 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3654 ASSERT_STREQ ("\n"
3655 " foo = bar (\n"
3656 " ~^\n"
3657 " );\n"
3658 " ~ \n",
3659 pp_formatted_text (dc.printer));
3660}
3661
ff7410b8 3662/* Verify that line numbers are correctly printed for the case of
3663 a multiline range in which the width of the line numbers changes
3664 (e.g. from "9" to "10"). */
3665
3666static void
3667test_line_numbers_multiline_range ()
3668{
3669 /* Create a tempfile and write some text to it. */
3670 pretty_printer pp;
3671 for (int i = 0; i < 20; i++)
3672 /* .........0000000001111111.
3673 .............1234567890123456. */
3674 pp_printf (&pp, "this is line %i\n", i + 1);
3675 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3676 line_table_test ltt;
3677
3678 const line_map_ordinary *ord_map = linemap_check_ordinary
3679 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3680 linemap_line_start (line_table, 1, 100);
3681
3682 /* Create a multi-line location, starting at the "line" of line 9, with
3683 a caret on the "is" of line 10, finishing on the "this" line 11. */
3684
3685 location_t start
3686 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3687 location_t caret
3688 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3689 location_t finish
3690 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3691 location_t loc = make_location (caret, start, finish);
3692
3693 test_diagnostic_context dc;
3694 dc.show_line_numbers_p = true;
31087b7e 3695 dc.min_margin_width = 0;
ff7410b8 3696 gcc_rich_location richloc (loc);
3697 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3698 ASSERT_STREQ ("\n"
3699 " 9 | this is line 9\n"
3700 " | ~~~~~~\n"
3701 "10 | this is line 10\n"
3702 " | ~~~~~^~~~~~~~~~\n"
3703 "11 | this is line 11\n"
3704 " | ~~~~ \n",
3705 pp_formatted_text (dc.printer));
3706}
3707
99b4f3a2 3708/* Run all of the selftests within this file. */
3709
3710void
3711diagnostic_show_locus_c_tests ()
3712{
d73881b0 3713 test_line_span ();
3714
70017c99 3715 test_layout_range_for_single_point ();
3716 test_layout_range_for_single_line ();
3717 test_layout_range_for_multiple_lines ();
99b4f3a2 3718
3719 test_get_line_width_without_trailing_whitespace ();
48a7392b 3720
3721 test_diagnostic_show_locus_unknown_location ();
3722
3723 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
a8a31e3d 3724 for_each_line_table_case (test_add_location_if_nearby);
70017c99 3725 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
367964fa 3726 for_each_line_table_case (test_fixit_consolidation);
f085b618 3727 for_each_line_table_case (test_overlapped_fixit_printing);
f907f132 3728 for_each_line_table_case (test_overlapped_fixit_printing_2);
d9020fe6 3729 for_each_line_table_case (test_fixit_insert_containing_newline);
896d130e 3730 for_each_line_table_case (test_fixit_insert_containing_newline_2);
d9020fe6 3731 for_each_line_table_case (test_fixit_replace_containing_newline);
c2403f36 3732 for_each_line_table_case (test_fixit_deletion_affecting_newline);
ff7410b8 3733
3734 test_line_numbers_multiline_range ();
99b4f3a2 3735}
3736
3737} // namespace selftest
3738
3739#endif /* #if CHECKING_P */