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