]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/diagnostic-show-locus.c
In PR70010, a function is marked with target(no-vsx) to disable VSX code
[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
b2902434 1042 pp_emit_prefix (m_pp);
1043
dd9ed701 1044 for (int i = 0; i < m_linenum_width + 1; i++)
1045 pp_character (m_pp, '.');
1046
1047 pp_newline (m_pp);
1048}
1049
aec1f4bd 1050/* Return true iff we should print a heading when starting the
1051 line span with the given index. */
1052
1053bool
1054layout::print_heading_for_line_span_index_p (int line_span_idx) const
1055{
1056 /* We print a heading for every change of line span, hence for every
1057 line span after the initial one. */
1058 if (line_span_idx > 0)
1059 return true;
1060
1061 /* We also do it for the initial span if the primary location of the
1062 diagnostic is in a different span. */
1063 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1064 return true;
1065
1066 return false;
1067}
1068
1069/* Get an expanded_location for the first location of interest within
1070 the given line_span.
1071 Used when printing a heading to indicate a new line span. */
1072
1073expanded_location
1074layout::get_expanded_location (const line_span *line_span) const
1075{
1076 /* Whenever possible, use the caret location. */
1077 if (line_span->contains_line_p (m_exploc.line))
1078 return m_exploc;
1079
1080 /* Otherwise, use the start of the first range that's present
1081 within the line_span. */
1082 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1083 {
1084 const layout_range *lr = &m_layout_ranges[i];
1085 if (line_span->contains_line_p (lr->m_start.m_line))
1086 {
1087 expanded_location exploc = m_exploc;
1088 exploc.line = lr->m_start.m_line;
1089 exploc.column = lr->m_start.m_column;
1090 return exploc;
1091 }
1092 }
1093
70017c99 1094 /* Otherwise, use the location of the first fixit-hint present within
1095 the line_span. */
1096 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1097 {
1098 const fixit_hint *hint = m_fixit_hints[i];
1099 location_t loc = hint->get_start_loc ();
1100 expanded_location exploc = expand_location (loc);
1101 if (line_span->contains_line_p (exploc.line))
1102 return exploc;
1103 }
1104
aec1f4bd 1105 /* It should not be possible to have a line span that didn't
70017c99 1106 contain any of the layout_range or fixit_hint instances. */
aec1f4bd 1107 gcc_unreachable ();
1108 return m_exploc;
1109}
1110
70017c99 1111/* Determine if HINT is meaningful to print within this layout. */
1112
1113bool
1114layout::validate_fixit_hint_p (const fixit_hint *hint)
1115{
be45049f 1116 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1117 return false;
1118 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1119 return false;
70017c99 1120
1121 return true;
1122}
1123
1124/* Determine the range of lines affected by HINT.
1125 This assumes that HINT has already been filtered by
1126 validate_fixit_hint_p, and so affects the correct source file. */
1127
1128static line_span
1129get_line_span_for_fixit_hint (const fixit_hint *hint)
1130{
1131 gcc_assert (hint);
e69492e4 1132
1133 int start_line = LOCATION_LINE (hint->get_start_loc ());
1134
1135 /* For line-insertion fix-it hints, add the previous line to the
1136 span, to give the user more context on the proposed change. */
1137 if (hint->ends_with_newline_p ())
1138 if (start_line > 1)
1139 start_line--;
1140
1141 return line_span (start_line,
be45049f 1142 LOCATION_LINE (hint->get_next_loc ()));
70017c99 1143}
1144
aec1f4bd 1145/* We want to print the pertinent source code at a diagnostic. The
1146 rich_location can contain multiple locations. This will have been
1147 filtered into m_exploc (the caret for the primary location) and
1148 m_layout_ranges, for those ranges within the same source file.
1149
1150 We will print a subset of the lines within the source file in question,
1151 as a collection of "spans" of lines.
1152
1153 This function populates m_line_spans with an ordered, disjoint list of
1154 the line spans of interest.
1155
dd9ed701 1156 Printing a gap between line spans takes one line, so, when printing
1157 line numbers, we allow a gap of up to one line between spans when
1158 merging, since it makes more sense to print the source line rather than a
1159 "gap-in-line-numbering" line. When not printing line numbers, it's
1160 better to be more explicit about what's going on, so keeping them as
1161 separate spans is preferred.
1162
1163 For example, if the primary range is on lines 8-10, with secondary ranges
1164 covering lines 5-6 and lines 13-15:
aec1f4bd 1165
1166 004
dd9ed701 1167 005 |RANGE 1
1168 006 |RANGE 1
1169 007
1170 008 |PRIMARY RANGE
1171 009 |PRIMARY CARET
1172 010 |PRIMARY RANGE
1173 011
1174 012
1175 013 |RANGE 2
1176 014 |RANGE 2
1177 015 |RANGE 2
1178 016
1179
1180 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1181
1182 With line numbering off (with span headers), we want three spans: lines 5-6,
1183 lines 8-10, and lines 13-15. */
aec1f4bd 1184
1185void
1186layout::calculate_line_spans ()
1187{
1188 /* This should only be called once, by the ctor. */
1189 gcc_assert (m_line_spans.length () == 0);
1190
1191 /* Populate tmp_spans with individual spans, for each of
1192 m_exploc, and for m_layout_ranges. */
d6dd1b60 1193 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
aec1f4bd 1194 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1195 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1196 {
1197 const layout_range *lr = &m_layout_ranges[i];
1198 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1199 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1200 lr->m_finish.m_line));
1201 }
1202
70017c99 1203 /* Also add spans for any fix-it hints, in case they cover other lines. */
1204 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1205 {
1206 const fixit_hint *hint = m_fixit_hints[i];
1207 gcc_assert (hint);
1208 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1209 }
1210
aec1f4bd 1211 /* Sort them. */
1212 tmp_spans.qsort(line_span::comparator);
1213
1214 /* Now iterate through tmp_spans, copying into m_line_spans, and
1215 combining where possible. */
1216 gcc_assert (tmp_spans.length () > 0);
1217 m_line_spans.safe_push (tmp_spans[0]);
1218 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1219 {
1220 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1221 const line_span *next = &tmp_spans[i];
1222 gcc_assert (next->m_first_line >= current->m_first_line);
dd9ed701 1223 const int merger_distance = m_show_line_numbers_p ? 1 : 0;
a268d555 1224 if ((linenum_arith_t)next->m_first_line
1225 <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
aec1f4bd 1226 {
1227 /* We can merge them. */
1228 if (next->m_last_line > current->m_last_line)
1229 current->m_last_line = next->m_last_line;
1230 }
1231 else
1232 {
1233 /* No merger possible. */
1234 m_line_spans.safe_push (*next);
1235 }
1236 }
1237
1238 /* Verify the result, in m_line_spans. */
1239 gcc_assert (m_line_spans.length () > 0);
1240 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1241 {
1242 const line_span *prev = &m_line_spans[i - 1];
1243 const line_span *next = &m_line_spans[i];
1244 /* The individual spans must be sane. */
1245 gcc_assert (prev->m_first_line <= prev->m_last_line);
1246 gcc_assert (next->m_first_line <= next->m_last_line);
1247 /* The spans must be ordered. */
1248 gcc_assert (prev->m_first_line < next->m_first_line);
1249 /* There must be a gap of at least one line between separate spans. */
1250 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1251 }
1252}
1253
896d130e 1254/* Print line ROW of source code, potentially colorized at any ranges, and
1255 populate *LBOUNDS_OUT.
1256 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1257 is its width. */
f0479000 1258
896d130e 1259void
d73881b0 1260layout::print_source_line (linenum_type row, const char *line, int line_width,
896d130e 1261 line_bounds *lbounds_out)
f0479000 1262{
f0479000 1263 m_colorizer.set_normal_text ();
1264
1265 /* We will stop printing the source line at any trailing
1266 whitespace. */
1267 line_width = get_line_width_without_trailing_whitespace (line,
1268 line_width);
12634056 1269 line += m_x_offset;
f0479000 1270
b2902434 1271 pp_emit_prefix (m_pp);
1272
ff7410b8 1273 if (m_show_line_numbers_p)
1274 {
1275 int width = num_digits (row);
1276 for (int i = 0; i < m_linenum_width - width; i++)
1277 pp_space (m_pp);
1278 pp_printf (m_pp, "%i | ", row);
1279 }
1280 else
1281 pp_space (m_pp);
f0479000 1282 int first_non_ws = INT_MAX;
1283 int last_non_ws = 0;
1284 int column;
1285 for (column = 1 + m_x_offset; column <= line_width; column++)
fee30e05 1286 {
f0479000 1287 /* Assuming colorization is enabled for the caret and underline
1288 characters, we may also colorize the associated characters
1289 within the source line.
1290
1291 For frontends that generate range information, we color the
1292 associated characters in the source line the same as the
1293 carets and underlines in the annotation line, to make it easier
1294 for the reader to see the pertinent code.
1295
1296 For frontends that only generate carets, we don't colorize the
1297 characters above them, since this would look strange (e.g.
1298 colorizing just the first character in a token). */
1299 if (m_colorize_source_p)
1300 {
1301 bool in_range_p;
1302 point_state state;
1303 in_range_p = get_state_at_point (row, column,
1304 0, INT_MAX,
1305 &state);
1306 if (in_range_p)
1307 m_colorizer.set_range (state.range_idx);
1308 else
1309 m_colorizer.set_normal_text ();
1310 }
28bd6e12 1311 char c = *line;
1312 if (c == '\0' || c == '\t' || c == '\r')
fee30e05 1313 c = ' ';
f0479000 1314 if (c != ' ')
1315 {
1316 last_non_ws = column;
1317 if (first_non_ws == INT_MAX)
1318 first_non_ws = column;
1319 }
1320 pp_character (m_pp, c);
fee30e05 1321 line++;
1322 }
3752e5b1 1323 print_newline ();
f0479000 1324
1325 lbounds_out->m_first_non_ws = first_non_ws;
1326 lbounds_out->m_last_non_ws = last_non_ws;
f0479000 1327}
1328
70017c99 1329/* Determine if we should print an annotation line for ROW.
1330 i.e. if any of m_layout_ranges contains ROW. */
1331
1332bool
d73881b0 1333layout::should_print_annotation_line_p (linenum_type row) const
70017c99 1334{
1335 layout_range *range;
1336 int i;
1337 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
5fe20025 1338 {
1339 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1340 return false;
1341 if (range->intersects_line_p (row))
1342 return true;
1343 }
70017c99 1344 return false;
1345}
1346
ff7410b8 1347/* Begin an annotation line. If m_show_line_numbers_p, print the left
1348 margin, which is empty for annotation lines. Otherwise, do nothing. */
1349
1350void
87c50f50 1351layout::start_annotation_line (char margin_char) const
ff7410b8 1352{
b2902434 1353 pp_emit_prefix (m_pp);
ff7410b8 1354 if (m_show_line_numbers_p)
1355 {
31087b7e 1356 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1357 of it, right-aligned, padded with spaces. */
1358 int i;
1359 for (i = 0; i < m_linenum_width - 3; i++)
1360 pp_space (m_pp);
1361 for (; i < m_linenum_width; i++)
87c50f50 1362 pp_character (m_pp, margin_char);
ff7410b8 1363 pp_string (m_pp, " |");
1364 }
1365}
1366
f0479000 1367/* Print a line consisting of the caret/underlines for the given
1368 source line. */
fee30e05 1369
f0479000 1370void
d73881b0 1371layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
f0479000 1372{
1373 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1374 lbounds.m_last_non_ws);
1375
ff7410b8 1376 start_annotation_line ();
f0479000 1377 pp_space (m_pp);
ff7410b8 1378
f0479000 1379 for (int column = 1 + m_x_offset; column < x_bound; column++)
1380 {
1381 bool in_range_p;
1382 point_state state;
1383 in_range_p = get_state_at_point (row, column,
1384 lbounds.m_first_non_ws,
1385 lbounds.m_last_non_ws,
1386 &state);
1387 if (in_range_p)
1388 {
1389 /* Within a range. Draw either the caret or an underline. */
1390 m_colorizer.set_range (state.range_idx);
1391 if (state.draw_caret_p)
d6dd1b60 1392 {
1393 /* Draw the caret. */
1394 char caret_char;
1395 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1396 caret_char = m_context->caret_chars[state.range_idx];
1397 else
1398 caret_char = '^';
1399 pp_character (m_pp, caret_char);
1400 }
f0479000 1401 else
1402 pp_character (m_pp, '~');
1403 }
1404 else
1405 {
1406 /* Not in a range. */
1407 m_colorizer.set_normal_text ();
1408 pp_character (m_pp, ' ');
1409 }
1410 }
3752e5b1 1411 print_newline ();
f0479000 1412}
1413
b7bb5264 1414/* Implementation detail of layout::print_any_labels.
1415
1416 A label within the given row of source. */
1417
251317e4 1418class line_label
b7bb5264 1419{
251317e4 1420public:
b7bb5264 1421 line_label (int state_idx, int column, label_text text)
1422 : m_state_idx (state_idx), m_column (column),
1423 m_text (text), m_length (strlen (text.m_buffer)),
9e2f4c7e 1424 m_label_line (0), m_has_vbar (true)
b7bb5264 1425 {}
1426
1427 /* Sorting is primarily by column, then by state index. */
1428 static int comparator (const void *p1, const void *p2)
1429 {
1430 const line_label *ll1 = (const line_label *)p1;
1431 const line_label *ll2 = (const line_label *)p2;
1432 int column_cmp = compare (ll1->m_column, ll2->m_column);
1433 if (column_cmp)
1434 return column_cmp;
9e2f4c7e 1435 /* Order by reverse state index, so that labels are printed
1436 in order of insertion into the rich_location when the
1437 sorted list is walked backwards. */
1438 return -compare (ll1->m_state_idx, ll2->m_state_idx);
b7bb5264 1439 }
1440
1441 int m_state_idx;
1442 int m_column;
1443 label_text m_text;
1444 size_t m_length;
1445 int m_label_line;
9e2f4c7e 1446 bool m_has_vbar;
b7bb5264 1447};
1448
1449/* Print any labels in this row. */
1450void
1451layout::print_any_labels (linenum_type row)
1452{
1453 int i;
1454 auto_vec<line_label> labels;
1455
1456 /* Gather the labels that are to be printed into "labels". */
1457 {
1458 layout_range *range;
1459 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1460 {
1461 /* Most ranges don't have labels, so reject this first. */
1462 if (range->m_label == NULL)
1463 continue;
1464
1465 /* The range's caret must be on this line. */
1466 if (range->m_caret.m_line != row)
1467 continue;
1468
1469 /* Reject labels that aren't fully visible due to clipping
1470 by m_x_offset. */
1471 if (range->m_caret.m_column <= m_x_offset)
1472 continue;
1473
1474 label_text text;
a2507e3d 1475 text = range->m_label->get_text (range->m_original_idx);
b7bb5264 1476
1477 /* Allow for labels that return NULL from their get_text
1478 implementation (so e.g. such labels can control their own
1479 visibility). */
1480 if (text.m_buffer == NULL)
1481 continue;
1482
1483 labels.safe_push (line_label (i, range->m_caret.m_column, text));
1484 }
1485 }
1486
1487 /* Bail out if there are no labels on this row. */
1488 if (labels.length () == 0)
1489 return;
1490
1491 /* Sort them. */
1492 labels.qsort(line_label::comparator);
1493
1494 /* Figure out how many "label lines" we need, and which
1495 one each label is printed in.
1496
1497 For example, if the labels aren't too densely packed,
1498 we can fit them on the same line, giving two "label lines":
1499
1500 foo + bar
1501 ~~~ ~~~
1502 | | : label line 0
1503 l0 l1 : label line 1
1504
1505 If they would touch each other or overlap, then we need
1506 additional "label lines":
1507
1508 foo + bar
1509 ~~~ ~~~
1510 | | : label line 0
1511 | label 1 : label line 1
1512 label 0 : label line 2
1513
1514 Place the final label on label line 1, and work backwards, adding
1515 label lines as needed.
1516
1517 If multiple labels are at the same place, put them on separate
1518 label lines:
1519
1520 foo + bar
1521 ^ : label line 0
1522 | : label line 1
9e2f4c7e 1523 label 0 : label line 2
1524 label 1 : label line 3. */
b7bb5264 1525
1526 int max_label_line = 1;
1527 {
1528 int next_column = INT_MAX;
1529 line_label *label;
1530 FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1531 {
1532 /* Would this label "touch" or overlap the next label? */
1533 if (label->m_column + label->m_length >= (size_t)next_column)
9e2f4c7e 1534 {
1535 max_label_line++;
1536
1537 /* If we've already seen labels with the same column, suppress the
1538 vertical bar for subsequent ones in this backwards iteration;
1539 hence only the one with the highest label_line has m_has_vbar set. */
1540 if (label->m_column == next_column)
1541 label->m_has_vbar = false;
1542 }
b7bb5264 1543
1544 label->m_label_line = max_label_line;
1545 next_column = label->m_column;
1546 }
1547 }
1548
1549 /* Print the "label lines". For each label within the line, print
1550 either a vertical bar ('|') for the labels that are lower down, or the
1551 labels themselves once we've reached their line. */
1552 {
b7bb5264 1553 for (int label_line = 0; label_line <= max_label_line; label_line++)
1554 {
1555 start_annotation_line ();
1556 pp_space (m_pp);
1557 int column = 1 + m_x_offset;
1558 line_label *label;
1559 FOR_EACH_VEC_ELT (labels, i, label)
1560 {
1561 if (label_line > label->m_label_line)
1562 /* We've printed all the labels for this label line. */
1563 break;
1564
1565 if (label_line == label->m_label_line)
1566 {
1567 gcc_assert (column <= label->m_column);
1568 move_to_column (&column, label->m_column, true);
1569 m_colorizer.set_range (label->m_state_idx);
1570 pp_string (m_pp, label->m_text.m_buffer);
1571 m_colorizer.set_normal_text ();
1572 column += label->m_length;
1573 }
9e2f4c7e 1574 else if (label->m_has_vbar)
b7bb5264 1575 {
1576 gcc_assert (column <= label->m_column);
1577 move_to_column (&column, label->m_column, true);
1578 m_colorizer.set_range (label->m_state_idx);
1579 pp_character (m_pp, '|');
1580 m_colorizer.set_normal_text ();
b7bb5264 1581 column++;
1582 }
1583 }
1584 print_newline ();
1585 }
1586 }
1587
1588 /* Clean up. */
1589 {
1590 line_label *label;
1591 FOR_EACH_VEC_ELT (labels, i, label)
1592 label->m_text.maybe_free ();
1593 }
1594}
1595
896d130e 1596/* If there are any fixit hints inserting new lines before source line ROW,
1597 print them.
1598
1599 They are printed on lines of their own, before the source line
1600 itself, with a leading '+'. */
1601
1602void
d73881b0 1603layout::print_leading_fixits (linenum_type row)
896d130e 1604{
1605 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1606 {
1607 const fixit_hint *hint = m_fixit_hints[i];
1608
1609 if (!hint->ends_with_newline_p ())
1610 /* Not a newline fixit; print it in print_trailing_fixits. */
1611 continue;
1612
1613 gcc_assert (hint->insertion_p ());
1614
1615 if (hint->affects_line_p (m_exploc.file, row))
1616 {
1617 /* Printing the '+' with normal colorization
1618 and the inserted line with "insert" colorization
1619 helps them stand out from each other, and from
1620 the surrounding text. */
1621 m_colorizer.set_normal_text ();
87c50f50 1622 start_annotation_line ('+');
896d130e 1623 pp_character (m_pp, '+');
1624 m_colorizer.set_fixit_insert ();
1625 /* Print all but the trailing newline of the fix-it hint.
1626 We have to print the newline separately to avoid
1627 getting additional pp prefixes printed. */
1628 for (size_t i = 0; i < hint->get_length () - 1; i++)
1629 pp_character (m_pp, hint->get_string ()[i]);
1630 m_colorizer.set_normal_text ();
1631 pp_newline (m_pp);
1632 }
1633 }
1634}
1635
1636/* Subroutine of layout::print_trailing_fixits.
73ffb7be 1637
1638 Determine if the annotation line printed for LINE contained
1639 the exact range from START_COLUMN to FINISH_COLUMN. */
1640
1641bool
d73881b0 1642layout::annotation_line_showed_range_p (linenum_type line, int start_column,
73ffb7be 1643 int finish_column) const
1644{
1645 layout_range *range;
1646 int i;
1647 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1648 if (range->m_start.m_line == line
1649 && range->m_start.m_column == start_column
1650 && range->m_finish.m_line == line
1651 && range->m_finish.m_column == finish_column)
1652 return true;
1653 return false;
1654}
1655
f085b618 1656/* Classes for printing trailing fix-it hints i.e. those that
1657 don't add new lines.
1658
1659 For insertion, these can look like:
1660
1661 new_text
1662
1663 For replacement, these can look like:
1664
1665 ------------- : underline showing affected range
1666 new_text
1667
1668 For deletion, these can look like:
1669
1670 ------------- : underline showing affected range
1671
1672 This can become confusing if they overlap, and so we need
1673 to do some preprocessing to decide what to print.
1674 We use the list of fixit_hint instances affecting the line
1675 to build a list of "correction" instances, and print the
1676 latter.
1677
1678 For example, consider a set of fix-its for converting
1679 a C-style cast to a C++ const_cast.
1680
1681 Given:
1682
1683 ..000000000111111111122222222223333333333.
1684 ..123456789012345678901234567890123456789.
1685 foo *f = (foo *)ptr->field;
1686 ^~~~~
1687
1688 and the fix-it hints:
1689 - replace col 10 (the open paren) with "const_cast<"
1690 - replace col 16 (the close paren) with "> ("
1691 - insert ")" before col 27
1692
1693 then we would get odd-looking output:
1694
1695 foo *f = (foo *)ptr->field;
1696 ^~~~~
1697 -
1698 const_cast<
1699 -
1700 > ( )
1701
1702 It would be better to detect when fixit hints are going to
1703 overlap (those that require new lines), and to consolidate
1704 the printing of such fixits, giving something like:
1705
1706 foo *f = (foo *)ptr->field;
1707 ^~~~~
1708 -----------------
1709 const_cast<foo *> (ptr->field)
1710
1711 This works by detecting when the printing would overlap, and
1712 effectively injecting no-op replace hints into the gaps between
1713 such fix-its, so that the printing joins up.
1714
1715 In the above example, the overlap of:
1716 - replace col 10 (the open paren) with "const_cast<"
1717 and:
1718 - replace col 16 (the close paren) with "> ("
1719 is fixed by injecting a no-op:
1720 - replace cols 11-15 with themselves ("foo *")
1721 and consolidating these, making:
1722 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1723 i.e.:
1724 - replace cols 10-16 with "const_cast<foo *> ("
1725
1726 This overlaps with the final fix-it hint:
1727 - insert ")" before col 27
1728 and so we repeat the consolidation process, by injecting
1729 a no-op:
1730 - replace cols 17-26 with themselves ("ptr->field")
1731 giving:
1732 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1733 i.e.:
1734 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1735
1736 and is thus printed as desired. */
1737
1738/* A range of columns within a line. */
1739
251317e4 1740class column_range
f085b618 1741{
251317e4 1742public:
f907f132 1743 column_range (int start_, int finish_) : start (start_), finish (finish_)
1744 {
1745 /* We must have either a range, or an insertion. */
1746 gcc_assert (start <= finish || finish == start - 1);
1747 }
f085b618 1748
1749 bool operator== (const column_range &other) const
1750 {
1751 return start == other.start && finish == other.finish;
1752 }
1753
1754 int start;
1755 int finish;
1756};
1757
1758/* Get the range of columns that HINT would affect. */
1759
1760static column_range
1761get_affected_columns (const fixit_hint *hint)
1762{
1763 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1764 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1765
1766 return column_range (start_column, finish_column);
1767}
1768
1769/* Get the range of columns that would be printed for HINT. */
1770
1771static column_range
1772get_printed_columns (const fixit_hint *hint)
1773{
1774 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1775 int final_hint_column = start_column + hint->get_length () - 1;
1776 if (hint->insertion_p ())
1777 {
1778 return column_range (start_column, final_hint_column);
1779 }
1780 else
1781 {
1782 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1783
1784 return column_range (start_column,
1785 MAX (finish_column, final_hint_column));
1786 }
1787}
1788
1789/* A correction on a particular line.
1790 This describes a plan for how to print one or more fixit_hint
1791 instances that affected the line, potentially consolidating hints
1792 into corrections to make the result easier for the user to read. */
1793
251317e4 1794class correction
f085b618 1795{
251317e4 1796public:
f085b618 1797 correction (column_range affected_columns,
1798 column_range printed_columns,
1799 const char *new_text, size_t new_text_len)
1800 : m_affected_columns (affected_columns),
1801 m_printed_columns (printed_columns),
1802 m_text (xstrdup (new_text)),
1803 m_len (new_text_len),
1804 m_alloc_sz (new_text_len + 1)
1805 {
1806 }
1807
1808 ~correction () { free (m_text); }
1809
1810 bool insertion_p () const
1811 {
1812 return m_affected_columns.start == m_affected_columns.finish + 1;
1813 }
1814
1815 void ensure_capacity (size_t len);
1816 void ensure_terminated ();
1817
f907f132 1818 void overwrite (int dst_offset, const char_span &src_span)
1819 {
1820 gcc_assert (dst_offset >= 0);
0bce23e1 1821 gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1822 memcpy (m_text + dst_offset, src_span.get_buffer (),
1823 src_span.length ());
f907f132 1824 }
1825
f085b618 1826 /* If insert, then start: the column before which the text
1827 is to be inserted, and finish is offset by the length of
1828 the replacement.
1829 If replace, then the range of columns affected. */
1830 column_range m_affected_columns;
1831
1832 /* If insert, then start: the column before which the text
1833 is to be inserted, and finish is offset by the length of
1834 the replacement.
1835 If replace, then the range of columns affected. */
1836 column_range m_printed_columns;
1837
1838 /* The text to be inserted/used as replacement. */
1839 char *m_text;
1840 size_t m_len;
1841 size_t m_alloc_sz;
1842};
1843
1844/* Ensure that m_text can hold a string of length LEN
1845 (plus 1 for 0-termination). */
1846
1847void
1848correction::ensure_capacity (size_t len)
1849{
1850 /* Allow 1 extra byte for 0-termination. */
1851 if (m_alloc_sz < (len + 1))
1852 {
1853 size_t new_alloc_sz = (len + 1) * 2;
1854 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1855 m_alloc_sz = new_alloc_sz;
1856 }
1857}
1858
1859/* Ensure that m_text is 0-terminated. */
1860
1861void
1862correction::ensure_terminated ()
1863{
1864 /* 0-terminate the buffer. */
1865 gcc_assert (m_len < m_alloc_sz);
1866 m_text[m_len] = '\0';
1867}
1868
1869/* A list of corrections affecting a particular line.
1870 This is used by layout::print_trailing_fixits for planning
1871 how to print the fix-it hints affecting the line. */
1872
251317e4 1873class line_corrections
f085b618 1874{
251317e4 1875public:
d73881b0 1876 line_corrections (const char *filename, linenum_type row)
f085b618 1877 : m_filename (filename), m_row (row)
1878 {}
1879 ~line_corrections ();
1880
1881 void add_hint (const fixit_hint *hint);
1882
1883 const char *m_filename;
d73881b0 1884 linenum_type m_row;
f085b618 1885 auto_vec <correction *> m_corrections;
1886};
1887
1888/* struct line_corrections. */
1889
1890line_corrections::~line_corrections ()
1891{
1892 unsigned i;
1893 correction *c;
1894 FOR_EACH_VEC_ELT (m_corrections, i, c)
1895 delete c;
1896}
1897
f907f132 1898/* A struct wrapping a particular source line, allowing
1899 run-time bounds-checking of accesses in a checked build. */
1900
251317e4 1901class source_line
f907f132 1902{
251317e4 1903public:
f907f132 1904 source_line (const char *filename, int line);
1905
1906 char_span as_span () { return char_span (chars, width); }
1907
1908 const char *chars;
1909 int width;
1910};
1911
1912/* source_line's ctor. */
1913
1914source_line::source_line (const char *filename, int line)
1915{
0bce23e1 1916 char_span span = location_get_source_line (filename, line);
1917 chars = span.get_buffer ();
1918 width = span.length ();
f907f132 1919}
1920
f085b618 1921/* Add HINT to the corrections for this line.
1922 Attempt to consolidate nearby hints so that they will not
1923 overlap with printed. */
1924
1925void
1926line_corrections::add_hint (const fixit_hint *hint)
1927{
1928 column_range affected_columns = get_affected_columns (hint);
1929 column_range printed_columns = get_printed_columns (hint);
1930
1931 /* Potentially consolidate. */
1932 if (!m_corrections.is_empty ())
1933 {
1934 correction *last_correction
1935 = m_corrections[m_corrections.length () - 1];
f907f132 1936
1937 /* The following consolidation code assumes that the fix-it hints
1938 have been sorted by start (done within layout's ctor). */
1939 gcc_assert (affected_columns.start
1940 >= last_correction->m_affected_columns.start);
1941 gcc_assert (printed_columns.start
1942 >= last_correction->m_printed_columns.start);
1943
f085b618 1944 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1945 {
1946 /* We have two hints for which the printed forms of the hints
1947 would touch or overlap, so we need to consolidate them to avoid
1948 confusing the user.
1949 Attempt to inject a "replace" correction from immediately
1950 after the end of the last hint to immediately before the start
1951 of the next hint. */
1952 column_range between (last_correction->m_affected_columns.finish + 1,
1953 printed_columns.start - 1);
1954
1955 /* Try to read the source. */
f907f132 1956 source_line line (m_filename, m_row);
1957 if (line.chars && between.finish < line.width)
f085b618 1958 {
1959 /* Consolidate into the last correction:
1960 add a no-op "replace" of the "between" text, and
1961 add the text from the new hint. */
f907f132 1962 int old_len = last_correction->m_len;
1963 gcc_assert (old_len >= 0);
1964 int between_len = between.finish + 1 - between.start;
1965 gcc_assert (between_len >= 0);
1966 int new_len = old_len + between_len + hint->get_length ();
1967 gcc_assert (new_len >= 0);
f085b618 1968 last_correction->ensure_capacity (new_len);
f907f132 1969 last_correction->overwrite
1970 (old_len,
1971 line.as_span ().subspan (between.start - 1,
1972 between.finish + 1 - between.start));
1973 last_correction->overwrite (old_len + between_len,
1974 char_span (hint->get_string (),
1975 hint->get_length ()));
f085b618 1976 last_correction->m_len = new_len;
1977 last_correction->ensure_terminated ();
1978 last_correction->m_affected_columns.finish
1979 = affected_columns.finish;
1980 last_correction->m_printed_columns.finish
1981 += between_len + hint->get_length ();
1982 return;
1983 }
1984 }
1985 }
1986
1987 /* If no consolidation happened, add a new correction instance. */
1988 m_corrections.safe_push (new correction (affected_columns,
1989 printed_columns,
1990 hint->get_string (),
1991 hint->get_length ()));
1992}
1993
70017c99 1994/* If there are any fixit hints on source line ROW, print them.
734caf84 1995 They are printed in order, attempting to combine them onto lines, but
896d130e 1996 starting new lines if necessary.
1997 Fix-it hints that insert new lines are handled separately,
1998 in layout::print_leading_fixits. */
734caf84 1999
2000void
d73881b0 2001layout::print_trailing_fixits (linenum_type row)
734caf84 2002{
f085b618 2003 /* Build a list of correction instances for the line,
2004 potentially consolidating hints (for the sake of readability). */
2005 line_corrections corrections (m_exploc.file, row);
70017c99 2006 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
734caf84 2007 {
70017c99 2008 const fixit_hint *hint = m_fixit_hints[i];
896d130e 2009
f085b618 2010 /* Newline fixits are handled by layout::print_leading_fixits. */
896d130e 2011 if (hint->ends_with_newline_p ())
2012 continue;
2013
734caf84 2014 if (hint->affects_line_p (m_exploc.file, row))
f085b618 2015 corrections.add_hint (hint);
2016 }
2017
2018 /* Now print the corrections. */
2019 unsigned i;
2020 correction *c;
4df8da84 2021 int column = m_x_offset;
f085b618 2022
ff7410b8 2023 if (!corrections.m_corrections.is_empty ())
2024 start_annotation_line ();
2025
f085b618 2026 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2027 {
2028 /* For now we assume each fixit hint can only touch one line. */
2029 if (c->insertion_p ())
2030 {
2031 /* This assumes the insertion just affects one line. */
2032 int start_column = c->m_printed_columns.start;
ff7410b8 2033 move_to_column (&column, start_column, true);
f085b618 2034 m_colorizer.set_fixit_insert ();
2035 pp_string (m_pp, c->m_text);
2036 m_colorizer.set_normal_text ();
2037 column += c->m_len;
2038 }
2039 else
734caf84 2040 {
f085b618 2041 /* If the range of the replacement wasn't printed in the
2042 annotation line, then print an extra underline to
2043 indicate exactly what is being replaced.
2044 Always show it for removals. */
2045 int start_column = c->m_affected_columns.start;
2046 int finish_column = c->m_affected_columns.finish;
2047 if (!annotation_line_showed_range_p (row, start_column,
2048 finish_column)
2049 || c->m_len == 0)
be45049f 2050 {
ff7410b8 2051 move_to_column (&column, start_column, true);
f085b618 2052 m_colorizer.set_fixit_delete ();
2053 for (; column <= finish_column; column++)
2054 pp_character (m_pp, '-');
be45049f 2055 m_colorizer.set_normal_text ();
be45049f 2056 }
f085b618 2057 /* Print the replacement text. REPLACE also covers
2058 removals, so only do this extra work (potentially starting
2059 a new line) if we have actual replacement text. */
2060 if (c->m_len > 0)
734caf84 2061 {
ff7410b8 2062 move_to_column (&column, start_column, true);
f085b618 2063 m_colorizer.set_fixit_insert ();
2064 pp_string (m_pp, c->m_text);
2065 m_colorizer.set_normal_text ();
2066 column += c->m_len;
734caf84 2067 }
2068 }
2069 }
3752e5b1 2070
2071 /* Add a trailing newline, if necessary. */
ff7410b8 2072 move_to_column (&column, 0, false);
3752e5b1 2073}
2074
2075/* Disable any colorization and emit a newline. */
2076
2077void
2078layout::print_newline ()
2079{
2080 m_colorizer.set_normal_text ();
2081 pp_newline (m_pp);
734caf84 2082}
2083
f0479000 2084/* Return true if (ROW/COLUMN) is within a range of the layout.
2085 If it returns true, OUT_STATE is written to, with the
2086 range index, and whether we should draw the caret at
2087 (ROW/COLUMN) (as opposed to an underline). */
2088
2089bool
2090layout::get_state_at_point (/* Inputs. */
d73881b0 2091 linenum_type row, int column,
f0479000 2092 int first_non_ws, int last_non_ws,
2093 /* Outputs. */
2094 point_state *out_state)
2095{
2096 layout_range *range;
fee30e05 2097 int i;
f0479000 2098 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2099 {
5fe20025 2100 if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2101 /* Bail out early, so that such ranges don't affect underlining or
2102 source colorization. */
2103 continue;
2104
f0479000 2105 if (range->contains_point (row, column))
2106 {
2107 out_state->range_idx = i;
2108
2109 /* Are we at the range's caret? is it visible? */
2110 out_state->draw_caret_p = false;
5fe20025 2111 if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
415d5641 2112 && row == range->m_caret.m_line
f0479000 2113 && column == range->m_caret.m_column)
415d5641 2114 out_state->draw_caret_p = true;
fee30e05 2115
f0479000 2116 /* Within a multiline range, don't display any underline
2117 in any leading or trailing whitespace on a line.
2118 We do display carets, however. */
2119 if (!out_state->draw_caret_p)
2120 if (column < first_non_ws || column > last_non_ws)
2121 return false;
2122
2123 /* We are within a range. */
2124 return true;
2125 }
2126 }
2127
2128 return false;
2129}
2130
2131/* Helper function for use by layout::print_line when printing the
2132 annotation line under the source line.
2133 Get the column beyond the rightmost one that could contain a caret or
2134 range marker, given that we stop rendering at trailing whitespace.
2135 ROW is the source line within the given file.
2136 CARET_COLUMN is the column of range 0's caret.
2137 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2138 character of source (as determined when printing the source line). */
2139
2140int
d73881b0 2141layout::get_x_bound_for_row (linenum_type row, int caret_column,
f0479000 2142 int last_non_ws_column)
2143{
2144 int result = caret_column + 1;
2145
2146 layout_range *range;
2147 int i;
2148 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
fee30e05 2149 {
f0479000 2150 if (row >= range->m_start.m_line)
2151 {
2152 if (range->m_finish.m_line == row)
2153 {
2154 /* On the final line within a range; ensure that
2155 we render up to the end of the range. */
2156 if (result <= range->m_finish.m_column)
2157 result = range->m_finish.m_column + 1;
2158 }
2159 else if (row < range->m_finish.m_line)
2160 {
2161 /* Within a multiline range; ensure that we render up to the
2162 last non-whitespace column. */
2163 if (result <= last_non_ws_column)
2164 result = last_non_ws_column + 1;
2165 }
2166 }
fee30e05 2167 }
f0479000 2168
2169 return result;
2170}
2171
734caf84 2172/* Given *COLUMN as an x-coordinate, print spaces to position
2173 successive output at DEST_COLUMN, printing a newline if necessary,
ff7410b8 2174 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2175 left margin after any newline. */
734caf84 2176
2177void
ff7410b8 2178layout::move_to_column (int *column, int dest_column, bool add_left_margin)
734caf84 2179{
2180 /* Start a new line if we need to. */
2181 if (*column > dest_column)
2182 {
3752e5b1 2183 print_newline ();
ff7410b8 2184 if (add_left_margin)
2185 start_annotation_line ();
4df8da84 2186 *column = m_x_offset;
734caf84 2187 }
2188
2189 while (*column < dest_column)
2190 {
2191 pp_space (m_pp);
2192 (*column)++;
2193 }
2194}
2195
abf93a25 2196/* For debugging layout issues, render a ruler giving column numbers
2197 (after the 1-column indent). */
2198
2199void
2200layout::show_ruler (int max_column) const
2201{
2202 /* Hundreds. */
2203 if (max_column > 99)
2204 {
ff7410b8 2205 start_annotation_line ();
abf93a25 2206 pp_space (m_pp);
2207 for (int column = 1 + m_x_offset; column <= max_column; column++)
c9281ef8 2208 if (column % 10 == 0)
abf93a25 2209 pp_character (m_pp, '0' + (column / 100) % 10);
2210 else
2211 pp_space (m_pp);
2212 pp_newline (m_pp);
2213 }
2214
2215 /* Tens. */
ff7410b8 2216 start_annotation_line ();
abf93a25 2217 pp_space (m_pp);
2218 for (int column = 1 + m_x_offset; column <= max_column; column++)
c9281ef8 2219 if (column % 10 == 0)
abf93a25 2220 pp_character (m_pp, '0' + (column / 10) % 10);
2221 else
2222 pp_space (m_pp);
2223 pp_newline (m_pp);
2224
2225 /* Units. */
ff7410b8 2226 start_annotation_line ();
abf93a25 2227 pp_space (m_pp);
2228 for (int column = 1 + m_x_offset; column <= max_column; column++)
2229 pp_character (m_pp, '0' + (column % 10));
2230 pp_newline (m_pp);
2231}
2232
896d130e 2233/* Print leading fix-its (for new lines inserted before the source line)
2234 then the source line, followed by an annotation line
2235 consisting of any caret/underlines, then any fixits.
2236 If the source line can't be read, print nothing. */
2237void
d73881b0 2238layout::print_line (linenum_type row)
896d130e 2239{
0bce23e1 2240 char_span line = location_get_source_line (m_exploc.file, row);
896d130e 2241 if (!line)
2242 return;
2243
2244 line_bounds lbounds;
2245 print_leading_fixits (row);
0bce23e1 2246 print_source_line (row, line.get_buffer (), line.length (), &lbounds);
896d130e 2247 if (should_print_annotation_line_p (row))
2248 print_annotation_line (row, lbounds);
b7bb5264 2249 if (m_show_labels_p)
2250 print_any_labels (row);
896d130e 2251 print_trailing_fixits (row);
2252}
2253
f0479000 2254} /* End of anonymous namespace. */
2255
a8a31e3d 2256/* If LOC is within the spans of lines that will already be printed for
2257 this gcc_rich_location, then add it as a secondary location and return true.
2258
2259 Otherwise return false. */
2260
2261bool
2262gcc_rich_location::add_location_if_nearby (location_t loc)
2263{
2264 /* Use the layout location-handling logic to sanitize LOC,
2265 filtering it to the current line spans within a temporary
2266 layout instance. */
2267 layout layout (global_dc, this, DK_ERROR);
2268 location_range loc_range;
2269 loc_range.m_loc = loc;
5fe20025 2270 loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
a2507e3d 2271 if (!layout.maybe_add_location_range (&loc_range, 0, true))
a8a31e3d 2272 return false;
2273
5fe20025 2274 add_range (loc);
a8a31e3d 2275 return true;
2276}
2277
f0479000 2278/* Print the physical source code corresponding to the location of
2279 this diagnostic, with additional annotations. */
2280
2281void
2282diagnostic_show_locus (diagnostic_context * context,
48a7392b 2283 rich_location *richloc,
2284 diagnostic_t diagnostic_kind)
f0479000 2285{
3752e5b1 2286 pp_newline (context->printer);
2287
48a7392b 2288 location_t loc = richloc->get_loc ();
d0f11d68 2289 /* Do nothing if source-printing has been disabled. */
2290 if (!context->show_caret)
2291 return;
2292
2293 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
48a7392b 2294 if (loc <= BUILTINS_LOCATION)
d0f11d68 2295 return;
2296
2297 /* Don't print the same source location twice in a row, unless we have
2298 fix-it hints. */
48a7392b 2299 if (loc == context->last_location
2300 && richloc->get_num_fixit_hints () == 0)
f0479000 2301 return;
2302
48a7392b 2303 context->last_location = loc;
f0479000 2304
48a7392b 2305 layout layout (context, richloc, diagnostic_kind);
aec1f4bd 2306 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2307 line_span_idx++)
3752e5b1 2308 {
aec1f4bd 2309 const line_span *line_span = layout.get_line_span (line_span_idx);
dd9ed701 2310 if (context->show_line_numbers_p)
aec1f4bd 2311 {
dd9ed701 2312 /* With line numbers, we should show whenever the line-numbering
2313 "jumps". */
2314 if (line_span_idx > 0)
2315 layout.print_gap_in_line_numbering ();
2316 }
2317 else
2318 {
2319 /* Without line numbers, we print headings for some line spans. */
2320 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2321 {
2322 expanded_location exploc
2323 = layout.get_expanded_location (line_span);
2324 context->start_span (context, exploc);
2325 }
aec1f4bd 2326 }
a268d555 2327 /* Iterate over the lines within this span (using linenum_arith_t to
2328 avoid overflow with 0xffffffff causing an infinite loop). */
2329 linenum_arith_t last_line = line_span->get_last_line ();
2330 for (linenum_arith_t row = line_span->get_first_line ();
d73881b0 2331 row <= last_line; row++)
896d130e 2332 layout.print_line (row);
3752e5b1 2333 }
fee30e05 2334}
99b4f3a2 2335
2336#if CHECKING_P
2337
2338namespace selftest {
2339
48a7392b 2340/* Selftests for diagnostic_show_locus. */
2341
48a7392b 2342/* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2343
2344static void
2345test_diagnostic_show_locus_unknown_location ()
2346{
2347 test_diagnostic_context dc;
2348 rich_location richloc (line_table, UNKNOWN_LOCATION);
2349 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2350 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2351}
2352
2353/* Verify that diagnostic_show_locus works sanely for various
2354 single-line cases.
2355
2356 All of these work on the following 1-line source file:
2357 .0000000001111111
2358 .1234567890123456
2359 "foo = bar.field;\n"
2360 which is set up by test_diagnostic_show_locus_one_liner and calls
2361 them. */
2362
2363/* Just a caret. */
2364
2365static void
2366test_one_liner_simple_caret ()
2367{
2368 test_diagnostic_context dc;
2369 location_t caret = linemap_position_for_column (line_table, 10);
2370 rich_location richloc (line_table, caret);
2371 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2372 ASSERT_STREQ ("\n"
2373 " foo = bar.field;\n"
2374 " ^\n",
2375 pp_formatted_text (dc.printer));
2376}
2377
2378/* Caret and range. */
2379
2380static void
2381test_one_liner_caret_and_range ()
2382{
2383 test_diagnostic_context dc;
2384 location_t caret = linemap_position_for_column (line_table, 10);
2385 location_t start = linemap_position_for_column (line_table, 7);
2386 location_t finish = linemap_position_for_column (line_table, 15);
2387 location_t loc = make_location (caret, start, finish);
2388 rich_location richloc (line_table, loc);
2389 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2390 ASSERT_STREQ ("\n"
2391 " foo = bar.field;\n"
2392 " ~~~^~~~~~\n",
2393 pp_formatted_text (dc.printer));
2394}
2395
2396/* Multiple ranges and carets. */
2397
2398static void
2399test_one_liner_multiple_carets_and_ranges ()
2400{
2401 test_diagnostic_context dc;
2402 location_t foo
2403 = make_location (linemap_position_for_column (line_table, 2),
2404 linemap_position_for_column (line_table, 1),
2405 linemap_position_for_column (line_table, 3));
2406 dc.caret_chars[0] = 'A';
2407
2408 location_t bar
2409 = make_location (linemap_position_for_column (line_table, 8),
2410 linemap_position_for_column (line_table, 7),
2411 linemap_position_for_column (line_table, 9));
2412 dc.caret_chars[1] = 'B';
2413
2414 location_t field
2415 = make_location (linemap_position_for_column (line_table, 13),
2416 linemap_position_for_column (line_table, 11),
2417 linemap_position_for_column (line_table, 15));
2418 dc.caret_chars[2] = 'C';
2419
2420 rich_location richloc (line_table, foo);
5fe20025 2421 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2422 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
48a7392b 2423 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2424 ASSERT_STREQ ("\n"
2425 " foo = bar.field;\n"
2426 " ~A~ ~B~ ~~C~~\n",
2427 pp_formatted_text (dc.printer));
2428}
2429
2430/* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2431
2432static void
68ef907c 2433test_one_liner_fixit_insert_before ()
48a7392b 2434{
2435 test_diagnostic_context dc;
2436 location_t caret = linemap_position_for_column (line_table, 7);
2437 rich_location richloc (line_table, caret);
68ef907c 2438 richloc.add_fixit_insert_before ("&");
48a7392b 2439 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2440 ASSERT_STREQ ("\n"
2441 " foo = bar.field;\n"
2442 " ^\n"
2443 " &\n",
2444 pp_formatted_text (dc.printer));
2445}
2446
68ef907c 2447/* Insertion fix-it hint: adding a "[0]" after "foo". */
2448
2449static void
2450test_one_liner_fixit_insert_after ()
2451{
2452 test_diagnostic_context dc;
2453 location_t start = linemap_position_for_column (line_table, 1);
2454 location_t finish = linemap_position_for_column (line_table, 3);
2455 location_t foo = make_location (start, start, finish);
2456 rich_location richloc (line_table, foo);
2457 richloc.add_fixit_insert_after ("[0]");
2458 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2459 ASSERT_STREQ ("\n"
2460 " foo = bar.field;\n"
2461 " ^~~\n"
2462 " [0]\n",
2463 pp_formatted_text (dc.printer));
2464}
2465
b2902434 2466/* Removal fix-it hint: removal of the ".field".
2467 Also verify the interaction of pp_set_prefix with rulers and
2468 fix-it hints. */
48a7392b 2469
2470static void
2471test_one_liner_fixit_remove ()
2472{
48a7392b 2473 location_t start = linemap_position_for_column (line_table, 10);
2474 location_t finish = linemap_position_for_column (line_table, 15);
2475 location_t dot = make_location (start, start, finish);
2476 rich_location richloc (line_table, dot);
850c2009 2477 richloc.add_fixit_remove ();
b2902434 2478
2479 /* Normal. */
2480 {
2481 test_diagnostic_context dc;
2482 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2483 ASSERT_STREQ ("\n"
2484 " foo = bar.field;\n"
2485 " ^~~~~~\n"
2486 " ------\n",
2487 pp_formatted_text (dc.printer));
2488 }
2489
2490 /* Test of adding a prefix. */
2491 {
2492 test_diagnostic_context dc;
2493 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2494 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2495 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2496 ASSERT_STREQ ("\n"
2497 "TEST PREFIX: foo = bar.field;\n"
2498 "TEST PREFIX: ^~~~~~\n"
2499 "TEST PREFIX: ------\n",
2500 pp_formatted_text (dc.printer));
2501 }
2502
2503 /* Normal, with ruler. */
2504 {
2505 test_diagnostic_context dc;
2506 dc.show_ruler_p = true;
2507 dc.caret_max_width = 104;
2508 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2509 ASSERT_STREQ ("\n"
2510 " 0 0 0 0 0 0 0 0 0 1 \n"
2511 " 1 2 3 4 5 6 7 8 9 0 \n"
2512 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
2513 " foo = bar.field;\n"
2514 " ^~~~~~\n"
2515 " ------\n",
2516 pp_formatted_text (dc.printer));
2517 }
2518
2519 /* Test of adding a prefix, with ruler. */
2520 {
2521 test_diagnostic_context dc;
2522 dc.show_ruler_p = true;
2523 dc.caret_max_width = 50;
2524 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2525 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2527 ASSERT_STREQ ("\n"
2528 "TEST PREFIX: 1 2 3 4 5\n"
2529 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
2530 "TEST PREFIX: foo = bar.field;\n"
2531 "TEST PREFIX: ^~~~~~\n"
2532 "TEST PREFIX: ------\n",
2533 pp_formatted_text (dc.printer));
2534 }
2535
2536 /* Test of adding a prefix, with ruler and line numbers. */
2537 {
2538 test_diagnostic_context dc;
2539 dc.show_ruler_p = true;
2540 dc.caret_max_width = 50;
2541 dc.show_line_numbers_p = true;
2542 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2543 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2544 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2545 ASSERT_STREQ ("\n"
2546 "TEST PREFIX: | 1 2 3 4 5\n"
2547 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
2548 "TEST PREFIX: 1 | foo = bar.field;\n"
2549 "TEST PREFIX: | ^~~~~~\n"
2550 "TEST PREFIX: | ------\n",
2551 pp_formatted_text (dc.printer));
2552 }
48a7392b 2553}
2554
2555/* Replace fix-it hint: replacing "field" with "m_field". */
2556
2557static void
2558test_one_liner_fixit_replace ()
2559{
2560 test_diagnostic_context dc;
2561 location_t start = linemap_position_for_column (line_table, 11);
2562 location_t finish = linemap_position_for_column (line_table, 15);
2563 location_t field = make_location (start, start, finish);
2564 rich_location richloc (line_table, field);
850c2009 2565 richloc.add_fixit_replace ("m_field");
48a7392b 2566 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2567 ASSERT_STREQ ("\n"
2568 " foo = bar.field;\n"
2569 " ^~~~~\n"
2570 " m_field\n",
2571 pp_formatted_text (dc.printer));
2572}
2573
73ffb7be 2574/* Replace fix-it hint: replacing "field" with "m_field",
2575 but where the caret was elsewhere. */
2576
2577static void
2578test_one_liner_fixit_replace_non_equal_range ()
2579{
2580 test_diagnostic_context dc;
2581 location_t equals = linemap_position_for_column (line_table, 5);
2582 location_t start = linemap_position_for_column (line_table, 11);
2583 location_t finish = linemap_position_for_column (line_table, 15);
2584 rich_location richloc (line_table, equals);
2585 source_range range;
2586 range.m_start = start;
2587 range.m_finish = finish;
2588 richloc.add_fixit_replace (range, "m_field");
2589 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2590 /* The replacement range is not indicated in the annotation line, so
2591 it should be indicated via an additional underline. */
2592 ASSERT_STREQ ("\n"
2593 " foo = bar.field;\n"
2594 " ^\n"
2595 " -----\n"
2596 " m_field\n",
2597 pp_formatted_text (dc.printer));
2598}
2599
2600/* Replace fix-it hint: replacing "field" with "m_field",
2601 where the caret was elsewhere, but where a secondary range
2602 exactly covers "field". */
2603
2604static void
2605test_one_liner_fixit_replace_equal_secondary_range ()
2606{
2607 test_diagnostic_context dc;
2608 location_t equals = linemap_position_for_column (line_table, 5);
2609 location_t start = linemap_position_for_column (line_table, 11);
2610 location_t finish = linemap_position_for_column (line_table, 15);
2611 rich_location richloc (line_table, equals);
2612 location_t field = make_location (start, start, finish);
5fe20025 2613 richloc.add_range (field);
850c2009 2614 richloc.add_fixit_replace (field, "m_field");
73ffb7be 2615 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2616 /* The replacement range is indicated in the annotation line,
2617 so it shouldn't be indicated via an additional underline. */
2618 ASSERT_STREQ ("\n"
2619 " foo = bar.field;\n"
2620 " ^ ~~~~~\n"
2621 " m_field\n",
2622 pp_formatted_text (dc.printer));
2623}
2624
a02b4eae 2625/* Verify that we can use ad-hoc locations when adding fixits to a
2626 rich_location. */
2627
2628static void
2629test_one_liner_fixit_validation_adhoc_locations ()
2630{
2631 /* Generate a range that's too long to be packed, so must
2632 be stored as an ad-hoc location (given the defaults
2633 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2634 const location_t c7 = linemap_position_for_column (line_table, 7);
2635 const location_t c47 = linemap_position_for_column (line_table, 47);
2636 const location_t loc = make_location (c7, c7, c47);
2637
2638 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2639 return;
2640
2641 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2642
2643 /* Insert. */
2644 {
2645 rich_location richloc (line_table, loc);
68ef907c 2646 richloc.add_fixit_insert_before (loc, "test");
a02b4eae 2647 /* It should not have been discarded by the validator. */
2648 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2649
2650 test_diagnostic_context dc;
2651 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2652 ASSERT_STREQ ("\n"
2653 " foo = bar.field;\n"
2654 " ^~~~~~~~~~ \n"
2655 " test\n",
2656 pp_formatted_text (dc.printer));
2657 }
2658
2659 /* Remove. */
2660 {
2661 rich_location richloc (line_table, loc);
2662 source_range range = source_range::from_locations (loc, c47);
2663 richloc.add_fixit_remove (range);
2664 /* It should not have been discarded by the validator. */
2665 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2666
2667 test_diagnostic_context dc;
2668 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2669 ASSERT_STREQ ("\n"
2670 " foo = bar.field;\n"
2671 " ^~~~~~~~~~ \n"
2672 " -----------------------------------------\n",
2673 pp_formatted_text (dc.printer));
2674 }
2675
2676 /* Replace. */
2677 {
2678 rich_location richloc (line_table, loc);
2679 source_range range = source_range::from_locations (loc, c47);
2680 richloc.add_fixit_replace (range, "test");
2681 /* It should not have been discarded by the validator. */
2682 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2683
2684 test_diagnostic_context dc;
2685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2686 ASSERT_STREQ ("\n"
2687 " foo = bar.field;\n"
2688 " ^~~~~~~~~~ \n"
2689 " test\n",
2690 pp_formatted_text (dc.printer));
2691 }
2692}
2693
be45049f 2694/* Test of consolidating insertions at the same location. */
d6dd1b60 2695
2696static void
be45049f 2697test_one_liner_many_fixits_1 ()
d6dd1b60 2698{
2699 test_diagnostic_context dc;
2700 location_t equals = linemap_position_for_column (line_table, 5);
2701 rich_location richloc (line_table, equals);
2702 for (int i = 0; i < 19; i++)
68ef907c 2703 richloc.add_fixit_insert_before ("a");
be45049f 2704 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2705 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2706 ASSERT_STREQ ("\n"
2707 " foo = bar.field;\n"
2708 " ^\n"
2709 " aaaaaaaaaaaaaaaaaaa\n",
2710 pp_formatted_text (dc.printer));
2711}
2712
2713/* Ensure that we can add an arbitrary number of fix-it hints to a
2714 rich_location, even if they are not consolidated. */
2715
2716static void
2717test_one_liner_many_fixits_2 ()
2718{
2719 test_diagnostic_context dc;
2720 location_t equals = linemap_position_for_column (line_table, 5);
2721 rich_location richloc (line_table, equals);
2722 for (int i = 0; i < 19; i++)
2723 {
2724 location_t loc = linemap_position_for_column (line_table, i * 2);
2725 richloc.add_fixit_insert_before (loc, "a");
2726 }
d6dd1b60 2727 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2728 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2729 ASSERT_STREQ ("\n"
2730 " foo = bar.field;\n"
2731 " ^\n"
be45049f 2732 "a a a a a a a a a a a a a a a a a a a\n",
d6dd1b60 2733 pp_formatted_text (dc.printer));
2734}
2735
b7bb5264 2736/* Test of labeling the ranges within a rich_location. */
2737
2738static void
2739test_one_liner_labels ()
2740{
2741 location_t foo
2742 = make_location (linemap_position_for_column (line_table, 1),
2743 linemap_position_for_column (line_table, 1),
2744 linemap_position_for_column (line_table, 3));
2745 location_t bar
2746 = make_location (linemap_position_for_column (line_table, 7),
2747 linemap_position_for_column (line_table, 7),
2748 linemap_position_for_column (line_table, 9));
2749 location_t field
2750 = make_location (linemap_position_for_column (line_table, 11),
2751 linemap_position_for_column (line_table, 11),
2752 linemap_position_for_column (line_table, 15));
2753
2754 /* Example where all the labels fit on one line. */
2755 {
2756 text_range_label label0 ("0");
2757 text_range_label label1 ("1");
2758 text_range_label label2 ("2");
2759 gcc_rich_location richloc (foo, &label0);
5fe20025 2760 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2761 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2762
2763 {
2764 test_diagnostic_context dc;
2765 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2766 ASSERT_STREQ ("\n"
2767 " foo = bar.field;\n"
2768 " ^~~ ~~~ ~~~~~\n"
2769 " | | |\n"
2770 " 0 1 2\n",
2771 pp_formatted_text (dc.printer));
2772 }
2773
2774 /* Verify that we can disable label-printing. */
2775 {
2776 test_diagnostic_context dc;
2777 dc.show_labels_p = false;
2778 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2779 ASSERT_STREQ ("\n"
2780 " foo = bar.field;\n"
2781 " ^~~ ~~~ ~~~~~\n",
2782 pp_formatted_text (dc.printer));
2783 }
2784 }
2785
2786 /* Example where the labels need extra lines. */
2787 {
2788 text_range_label label0 ("label 0");
2789 text_range_label label1 ("label 1");
2790 text_range_label label2 ("label 2");
2791 gcc_rich_location richloc (foo, &label0);
5fe20025 2792 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2793 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2794
2795 test_diagnostic_context dc;
2796 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2797 ASSERT_STREQ ("\n"
2798 " foo = bar.field;\n"
2799 " ^~~ ~~~ ~~~~~\n"
2800 " | | |\n"
2801 " | | label 2\n"
2802 " | label 1\n"
2803 " label 0\n",
2804 pp_formatted_text (dc.printer));
2805 }
2806
2807 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2808 but label 1 just touches label 2. */
2809 {
2810 text_range_label label0 ("aaaaa");
2811 text_range_label label1 ("bbbb");
2812 text_range_label label2 ("c");
2813 gcc_rich_location richloc (foo, &label0);
5fe20025 2814 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2815 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2816
2817 test_diagnostic_context dc;
2818 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2819 ASSERT_STREQ ("\n"
2820 " foo = bar.field;\n"
2821 " ^~~ ~~~ ~~~~~\n"
2822 " | | |\n"
2823 " | | c\n"
2824 " aaaaa bbbb\n",
2825 pp_formatted_text (dc.printer));
2826 }
2827
2828 /* Example of out-of-order ranges (thus requiring a sort). */
2829 {
2830 text_range_label label0 ("0");
2831 text_range_label label1 ("1");
2832 text_range_label label2 ("2");
2833 gcc_rich_location richloc (field, &label0);
5fe20025 2834 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2835 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2836
2837 test_diagnostic_context dc;
2838 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2839 ASSERT_STREQ ("\n"
2840 " foo = bar.field;\n"
2841 " ~~~ ~~~ ^~~~~\n"
2842 " | | |\n"
2843 " 2 1 0\n",
2844 pp_formatted_text (dc.printer));
2845 }
2846
2847 /* Ensure we don't ICE if multiple ranges with labels are on
2848 the same point. */
2849 {
2850 text_range_label label0 ("label 0");
2851 text_range_label label1 ("label 1");
2852 text_range_label label2 ("label 2");
2853 gcc_rich_location richloc (bar, &label0);
5fe20025 2854 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2855 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
b7bb5264 2856
2857 test_diagnostic_context dc;
2858 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2859 ASSERT_STREQ ("\n"
2860 " foo = bar.field;\n"
2861 " ^~~\n"
2862 " |\n"
9e2f4c7e 2863 " label 0\n"
b7bb5264 2864 " label 1\n"
9e2f4c7e 2865 " label 2\n",
2866 pp_formatted_text (dc.printer));
2867 }
2868
2869 /* Example of out-of-order ranges (thus requiring a sort), where
2870 they overlap, and there are multiple ranges on the same point. */
2871 {
2872 text_range_label label_0a ("label 0a");
2873 text_range_label label_1a ("label 1a");
2874 text_range_label label_2a ("label 2a");
2875 text_range_label label_0b ("label 0b");
2876 text_range_label label_1b ("label 1b");
2877 text_range_label label_2b ("label 2b");
2878 text_range_label label_0c ("label 0c");
2879 text_range_label label_1c ("label 1c");
2880 text_range_label label_2c ("label 2c");
2881 gcc_rich_location richloc (field, &label_0a);
2882 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
2883 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
2884
2885 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
2886 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
2887 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
2888
2889 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
2890 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
2891 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
2892
2893 test_diagnostic_context dc;
2894 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2895 ASSERT_STREQ ("\n"
2896 " foo = bar.field;\n"
2897 " ~~~ ~~~ ^~~~~\n"
2898 " | | |\n"
2899 " | | label 0a\n"
2900 " | | label 0b\n"
2901 " | | label 0c\n"
2902 " | label 1a\n"
2903 " | label 1b\n"
2904 " | label 1c\n"
2905 " label 2a\n"
2906 " label 2b\n"
2907 " label 2c\n",
b7bb5264 2908 pp_formatted_text (dc.printer));
2909 }
2910
2911 /* Verify that a NULL result from range_label::get_text is
2912 handled gracefully. */
2913 {
2914 text_range_label label (NULL);
2915 gcc_rich_location richloc (bar, &label);
2916
2917 test_diagnostic_context dc;
2918 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2919 ASSERT_STREQ ("\n"
2920 " foo = bar.field;\n"
2921 " ^~~\n",
2922 pp_formatted_text (dc.printer));
2923 }
2924
2925 /* TODO: example of formatted printing (needs to be in
2926 gcc-rich-location.c due to Makefile.in issues). */
2927}
2928
48a7392b 2929/* Run the various one-liner tests. */
2930
2931static void
2932test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2933{
2934 /* Create a tempfile and write some text to it.
2935 ....................0000000001111111.
2936 ....................1234567890123456. */
2937 const char *content = "foo = bar.field;\n";
2938 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2939 line_table_test ltt (case_);
2940
2941 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2942
2943 location_t line_end = linemap_position_for_column (line_table, 16);
2944
2945 /* Don't attempt to run the tests if column data might be unavailable. */
2946 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2947 return;
2948
2949 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2950 ASSERT_EQ (1, LOCATION_LINE (line_end));
2951 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2952
2953 test_one_liner_simple_caret ();
2954 test_one_liner_caret_and_range ();
2955 test_one_liner_multiple_carets_and_ranges ();
68ef907c 2956 test_one_liner_fixit_insert_before ();
2957 test_one_liner_fixit_insert_after ();
48a7392b 2958 test_one_liner_fixit_remove ();
2959 test_one_liner_fixit_replace ();
73ffb7be 2960 test_one_liner_fixit_replace_non_equal_range ();
2961 test_one_liner_fixit_replace_equal_secondary_range ();
a02b4eae 2962 test_one_liner_fixit_validation_adhoc_locations ();
be45049f 2963 test_one_liner_many_fixits_1 ();
2964 test_one_liner_many_fixits_2 ();
b7bb5264 2965 test_one_liner_labels ();
48a7392b 2966}
2967
a8a31e3d 2968/* Verify that gcc_rich_location::add_location_if_nearby works. */
2969
2970static void
2971test_add_location_if_nearby (const line_table_case &case_)
2972{
2973 /* Create a tempfile and write some text to it.
2974 ...000000000111111111122222222223333333333.
2975 ...123456789012345678901234567890123456789. */
2976 const char *content
2977 = ("struct same_line { double x; double y; ;\n" /* line 1. */
2978 "struct different_line\n" /* line 2. */
2979 "{\n" /* line 3. */
2980 " double x;\n" /* line 4. */
2981 " double y;\n" /* line 5. */
2982 ";\n"); /* line 6. */
2983 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2984 line_table_test ltt (case_);
2985
2986 const line_map_ordinary *ord_map
2987 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2988 tmp.get_filename (), 0));
2989
2990 linemap_line_start (line_table, 1, 100);
2991
2992 const location_t final_line_end
2993 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2994
2995 /* Don't attempt to run the tests if column data might be unavailable. */
2996 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2997 return;
2998
2999 /* Test of add_location_if_nearby on the same line as the
3000 primary location. */
3001 {
3002 const location_t missing_close_brace_1_39
3003 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
3004 const location_t matching_open_brace_1_18
3005 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3006 gcc_rich_location richloc (missing_close_brace_1_39);
3007 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
3008 ASSERT_TRUE (added);
3009 ASSERT_EQ (2, richloc.get_num_locations ());
3010 test_diagnostic_context dc;
3011 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3012 ASSERT_STREQ ("\n"
3013 " struct same_line { double x; double y; ;\n"
3014 " ~ ^\n",
3015 pp_formatted_text (dc.printer));
3016 }
3017
3018 /* Test of add_location_if_nearby on a different line to the
3019 primary location. */
3020 {
3021 const location_t missing_close_brace_6_1
3022 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
3023 const location_t matching_open_brace_3_1
3024 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
3025 gcc_rich_location richloc (missing_close_brace_6_1);
3026 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
3027 ASSERT_FALSE (added);
3028 ASSERT_EQ (1, richloc.get_num_locations ());
3029 }
3030}
3031
70017c99 3032/* Verify that we print fixits even if they only affect lines
3033 outside those covered by the ranges in the rich_location. */
3034
3035static void
3036test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
3037{
3038 /* Create a tempfile and write some text to it.
3039 ...000000000111111111122222222223333333333.
3040 ...123456789012345678901234567890123456789. */
3041 const char *content
3042 = ("struct point { double x; double y; };\n" /* line 1. */
3043 "struct point origin = {x: 0.0,\n" /* line 2. */
3044 " y\n" /* line 3. */
3045 "\n" /* line 4. */
3046 "\n" /* line 5. */
3047 " : 0.0};\n"); /* line 6. */
3048 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3049 line_table_test ltt (case_);
3050
3051 const line_map_ordinary *ord_map
3052 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3053 tmp.get_filename (), 0));
3054
3055 linemap_line_start (line_table, 1, 100);
3056
3057 const location_t final_line_end
3058 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3059
3060 /* Don't attempt to run the tests if column data might be unavailable. */
3061 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3062 return;
3063
3064 /* A pair of tests for modernizing the initializers to C99-style. */
3065
3066 /* The one-liner case (line 2). */
3067 {
3068 test_diagnostic_context dc;
3069 const location_t x
3070 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
3071 const location_t colon
3072 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
3073 rich_location richloc (line_table, colon);
68ef907c 3074 richloc.add_fixit_insert_before (x, ".");
70017c99 3075 richloc.add_fixit_replace (colon, "=");
3076 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3077 ASSERT_STREQ ("\n"
3078 " struct point origin = {x: 0.0,\n"
3079 " ^\n"
3080 " .=\n",
3081 pp_formatted_text (dc.printer));
3082 }
3083
3084 /* The multiline case. The caret for the rich_location is on line 6;
3085 verify that insertion fixit on line 3 is still printed (and that
3086 span starts are printed due to the gap between the span at line 3
3087 and that at line 6). */
3088 {
3089 test_diagnostic_context dc;
3090 const location_t y
3091 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3092 const location_t colon
3093 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3094 rich_location richloc (line_table, colon);
68ef907c 3095 richloc.add_fixit_insert_before (y, ".");
70017c99 3096 richloc.add_fixit_replace (colon, "=");
3097 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3098 ASSERT_STREQ ("\n"
3099 "FILENAME:3:24:\n"
3100 " y\n"
3101 " .\n"
3102 "FILENAME:6:25:\n"
3103 " : 0.0};\n"
3104 " ^\n"
3105 " =\n",
3106 pp_formatted_text (dc.printer));
3107 }
dd9ed701 3108
3109 /* As above, but verify the behavior of multiple line spans
3110 with line-numbering enabled. */
3111 {
3112 const location_t y
3113 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
3114 const location_t colon
3115 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
3116 rich_location richloc (line_table, colon);
3117 richloc.add_fixit_insert_before (y, ".");
3118 richloc.add_fixit_replace (colon, "=");
3119 test_diagnostic_context dc;
3120 dc.show_line_numbers_p = true;
3121 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3122 ASSERT_STREQ ("\n"
31087b7e 3123 " 3 | y\n"
3124 " | .\n"
3125 "......\n"
3126 " 6 | : 0.0};\n"
3127 " | ^\n"
3128 " | =\n",
dd9ed701 3129 pp_formatted_text (dc.printer));
3130 }
70017c99 3131}
3132
3133
367964fa 3134/* Verify that fix-it hints are appropriately consolidated.
3135
3136 If any fix-it hints in a rich_location involve locations beyond
3137 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3138 the fix-it as a whole, so there should be none.
3139
3140 Otherwise, verify that consecutive "replace" and "remove" fix-its
3141 are merged, and that other fix-its remain separate. */
3142
3143static void
3144test_fixit_consolidation (const line_table_case &case_)
3145{
3146 line_table_test ltt (case_);
3147
3148 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3149
3150 const location_t c10 = linemap_position_for_column (line_table, 10);
3151 const location_t c15 = linemap_position_for_column (line_table, 15);
3152 const location_t c16 = linemap_position_for_column (line_table, 16);
3153 const location_t c17 = linemap_position_for_column (line_table, 17);
3154 const location_t c20 = linemap_position_for_column (line_table, 20);
be45049f 3155 const location_t c21 = linemap_position_for_column (line_table, 21);
367964fa 3156 const location_t caret = c10;
3157
3158 /* Insert + insert. */
3159 {
3160 rich_location richloc (line_table, caret);
68ef907c 3161 richloc.add_fixit_insert_before (c10, "foo");
3162 richloc.add_fixit_insert_before (c15, "bar");
367964fa 3163
3164 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3165 /* Bogus column info for 2nd fixit, so no fixits. */
3166 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3167 else
3168 /* They should not have been merged. */
3169 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3170 }
3171
3172 /* Insert + replace. */
3173 {
3174 rich_location richloc (line_table, caret);
68ef907c 3175 richloc.add_fixit_insert_before (c10, "foo");
367964fa 3176 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3177 "bar");
3178
3179 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3180 /* Bogus column info for 2nd fixit, so no fixits. */
3181 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3182 else
3183 /* They should not have been merged. */
3184 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3185 }
3186
3187 /* Replace + non-consecutive insert. */
3188 {
3189 rich_location richloc (line_table, caret);
3190 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3191 "bar");
68ef907c 3192 richloc.add_fixit_insert_before (c17, "foo");
367964fa 3193
3194 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3195 /* Bogus column info for 2nd fixit, so no fixits. */
3196 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3197 else
3198 /* They should not have been merged. */
3199 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3200 }
3201
3202 /* Replace + non-consecutive replace. */
3203 {
3204 rich_location richloc (line_table, caret);
3205 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3206 "foo");
3207 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3208 "bar");
3209
3210 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3211 /* Bogus column info for 2nd fixit, so no fixits. */
3212 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3213 else
3214 /* They should not have been merged. */
3215 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3216 }
3217
3218 /* Replace + consecutive replace. */
3219 {
3220 rich_location richloc (line_table, caret);
3221 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3222 "foo");
3223 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3224 "bar");
3225
3226 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3227 /* Bogus column info for 2nd fixit, so no fixits. */
3228 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3229 else
3230 {
3231 /* They should have been merged into a single "replace". */
3232 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3233 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3234 ASSERT_STREQ ("foobar", hint->get_string ());
3235 ASSERT_EQ (c10, hint->get_start_loc ());
3236 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3237 }
3238 }
3239
3240 /* Replace + consecutive removal. */
3241 {
3242 rich_location richloc (line_table, caret);
3243 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3244 "foo");
3245 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3246
3247 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3248 /* Bogus column info for 2nd fixit, so no fixits. */
3249 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3250 else
3251 {
3252 /* They should have been merged into a single replace, with the
3253 range extended to cover that of the removal. */
3254 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3255 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3256 ASSERT_STREQ ("foo", hint->get_string ());
3257 ASSERT_EQ (c10, hint->get_start_loc ());
3258 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3259 }
3260 }
3261
3262 /* Consecutive removals. */
3263 {
3264 rich_location richloc (line_table, caret);
3265 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3266 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3267
3268 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3269 /* Bogus column info for 2nd fixit, so no fixits. */
3270 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3271 else
3272 {
3273 /* They should have been merged into a single "replace-with-empty". */
3274 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3275 const fixit_hint *hint = richloc.get_fixit_hint (0);
be45049f 3276 ASSERT_STREQ ("", hint->get_string ());
3277 ASSERT_EQ (c10, hint->get_start_loc ());
3278 ASSERT_EQ (c21, hint->get_next_loc ());
367964fa 3279 }
3280 }
3281}
3282
f085b618 3283/* Verify that the line_corrections machinery correctly prints
3284 overlapping fixit-hints. */
3285
3286static void
3287test_overlapped_fixit_printing (const line_table_case &case_)
3288{
3289 /* Create a tempfile and write some text to it.
3290 ...000000000111111111122222222223333333333.
3291 ...123456789012345678901234567890123456789. */
3292 const char *content
3293 = (" foo *f = (foo *)ptr->field;\n");
3294 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3295 line_table_test ltt (case_);
3296
3297 const line_map_ordinary *ord_map
3298 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3299 tmp.get_filename (), 0));
3300
3301 linemap_line_start (line_table, 1, 100);
3302
3303 const location_t final_line_end
3304 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3305
3306 /* Don't attempt to run the tests if column data might be unavailable. */
3307 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3308 return;
3309
3310 /* A test for converting a C-style cast to a C++-style cast. */
3311 const location_t open_paren
3312 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3313 const location_t close_paren
3314 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3315 const location_t expr_start
3316 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3317 const location_t expr_finish
3318 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3319 const location_t expr = make_location (expr_start, expr_start, expr_finish);
3320
3321 /* Various examples of fix-it hints that aren't themselves consolidated,
3322 but for which the *printing* may need consolidation. */
3323
3324 /* Example where 3 fix-it hints are printed as one. */
3325 {
3326 test_diagnostic_context dc;
3327 rich_location richloc (line_table, expr);
3328 richloc.add_fixit_replace (open_paren, "const_cast<");
3329 richloc.add_fixit_replace (close_paren, "> (");
3330 richloc.add_fixit_insert_after (")");
3331
3332 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3333 ASSERT_STREQ ("\n"
3334 " foo *f = (foo *)ptr->field;\n"
3335 " ^~~~~~~~~~\n"
3336 " -----------------\n"
3337 " const_cast<foo *> (ptr->field)\n",
3338 pp_formatted_text (dc.printer));
3339
3340 /* Unit-test the line_corrections machinery. */
3341 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3342 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3343 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3344 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3345 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3346 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3347 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3348 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3349 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3350 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3351
3352 /* Add each hint in turn to a line_corrections instance,
3353 and verify that they are consolidated into one correction instance
3354 as expected. */
3355 line_corrections lc (tmp.get_filename (), 1);
3356
3357 /* The first replace hint by itself. */
3358 lc.add_hint (hint_0);
3359 ASSERT_EQ (1, lc.m_corrections.length ());
3360 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3361 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3362 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3363
3364 /* After the second replacement hint, they are printed together
3365 as a replacement (along with the text between them). */
3366 lc.add_hint (hint_1);
3367 ASSERT_EQ (1, lc.m_corrections.length ());
3368 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3369 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3370 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3371
3372 /* After the final insertion hint, they are all printed together
3373 as a replacement (along with the text between them). */
3374 lc.add_hint (hint_2);
3375 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3376 lc.m_corrections[0]->m_text);
3377 ASSERT_EQ (1, lc.m_corrections.length ());
3378 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3379 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3380 }
3381
3382 /* Example where two are consolidated during printing. */
3383 {
3384 test_diagnostic_context dc;
3385 rich_location richloc (line_table, expr);
3386 richloc.add_fixit_replace (open_paren, "CAST (");
3387 richloc.add_fixit_replace (close_paren, ") (");
3388 richloc.add_fixit_insert_after (")");
3389
3390 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3391 ASSERT_STREQ ("\n"
3392 " foo *f = (foo *)ptr->field;\n"
3393 " ^~~~~~~~~~\n"
3394 " -\n"
3395 " CAST (-\n"
3396 " ) ( )\n",
3397 pp_formatted_text (dc.printer));
3398 }
3399
3400 /* Example where none are consolidated during printing. */
3401 {
3402 test_diagnostic_context dc;
3403 rich_location richloc (line_table, expr);
3404 richloc.add_fixit_replace (open_paren, "CST (");
3405 richloc.add_fixit_replace (close_paren, ") (");
3406 richloc.add_fixit_insert_after (")");
3407
3408 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3409 ASSERT_STREQ ("\n"
3410 " foo *f = (foo *)ptr->field;\n"
3411 " ^~~~~~~~~~\n"
3412 " -\n"
3413 " CST ( -\n"
3414 " ) ( )\n",
3415 pp_formatted_text (dc.printer));
3416 }
3417
3418 /* Example of deletion fix-it hints. */
3419 {
3420 test_diagnostic_context dc;
3421 rich_location richloc (line_table, expr);
3422 richloc.add_fixit_insert_before (open_paren, "(bar *)");
3423 source_range victim = {open_paren, close_paren};
3424 richloc.add_fixit_remove (victim);
3425
3426 /* This case is actually handled by fixit-consolidation,
3427 rather than by line_corrections. */
3428 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3429
3430 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3431 ASSERT_STREQ ("\n"
3432 " foo *f = (foo *)ptr->field;\n"
3433 " ^~~~~~~~~~\n"
3434 " -------\n"
3435 " (bar *)\n",
3436 pp_formatted_text (dc.printer));
3437 }
3438
3439 /* Example of deletion fix-it hints that would overlap. */
3440 {
3441 test_diagnostic_context dc;
3442 rich_location richloc (line_table, expr);
3443 richloc.add_fixit_insert_before (open_paren, "(longer *)");
3444 source_range victim = {expr_start, expr_finish};
3445 richloc.add_fixit_remove (victim);
3446
3447 /* These fixits are not consolidated. */
3448 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3449
3450 /* But the corrections are. */
3451 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3452 ASSERT_STREQ ("\n"
3453 " foo *f = (foo *)ptr->field;\n"
3454 " ^~~~~~~~~~\n"
3455 " -----------------\n"
3456 " (longer *)(foo *)\n",
3457 pp_formatted_text (dc.printer));
3458 }
3459
3460 /* Example of insertion fix-it hints that would overlap. */
3461 {
3462 test_diagnostic_context dc;
3463 rich_location richloc (line_table, expr);
3464 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3465 richloc.add_fixit_insert_after (close_paren, "TEST");
3466
3467 /* The first insertion is long enough that if printed naively,
3468 it would overlap with the second.
3469 Verify that they are printed as a single replacement. */
3470 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3471 ASSERT_STREQ ("\n"
3472 " foo *f = (foo *)ptr->field;\n"
3473 " ^~~~~~~~~~\n"
3474 " -------\n"
3475 " LONGER THAN THE CAST(foo *)TEST\n",
3476 pp_formatted_text (dc.printer));
3477 }
3478}
3479
f907f132 3480/* Verify that the line_corrections machinery correctly prints
3481 overlapping fixit-hints that have been added in the wrong
3482 order.
3483 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3484
3485static void
3486test_overlapped_fixit_printing_2 (const line_table_case &case_)
3487{
3488 /* Create a tempfile and write some text to it.
3489 ...000000000111111111122222222223333333333.
3490 ...123456789012345678901234567890123456789. */
3491 const char *content
3492 = ("int a5[][0][0] = { 1, 2 };\n");
3493 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3494 line_table_test ltt (case_);
3495
3496 const line_map_ordinary *ord_map
3497 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3498 tmp.get_filename (), 0));
3499
3500 linemap_line_start (line_table, 1, 100);
3501
3502 const location_t final_line_end
3503 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3504
3505 /* Don't attempt to run the tests if column data might be unavailable. */
3506 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3507 return;
3508
3509 const location_t col_1
3510 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3511 const location_t col_20
3512 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3513 const location_t col_21
3514 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3515 const location_t col_23
3516 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3517 const location_t col_25
3518 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3519
3520 /* Two insertions, in the wrong order. */
3521 {
3522 rich_location richloc (line_table, col_20);
3523 richloc.add_fixit_insert_before (col_23, "{");
3524 richloc.add_fixit_insert_before (col_21, "}");
3525
3526 /* These fixits should be accepted; they can't be consolidated. */
3527 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3528 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3529 ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3530 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3531 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3532 ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3533 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3534
3535 /* Verify that they're printed correctly. */
3536 test_diagnostic_context dc;
3537 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3538 ASSERT_STREQ ("\n"
3539 " int a5[][0][0] = { 1, 2 };\n"
3540 " ^\n"
3541 " } {\n",
3542 pp_formatted_text (dc.printer));
3543 }
3544
3545 /* Various overlapping insertions, some occurring "out of order"
3546 (reproducing the fix-it hints from PR c/81405). */
3547 {
3548 test_diagnostic_context dc;
3549 rich_location richloc (line_table, col_20);
3550
3551 richloc.add_fixit_insert_before (col_20, "{{");
3552 richloc.add_fixit_insert_before (col_21, "}}");
3553 richloc.add_fixit_insert_before (col_23, "{");
3554 richloc.add_fixit_insert_before (col_21, "}");
3555 richloc.add_fixit_insert_before (col_23, "{{");
3556 richloc.add_fixit_insert_before (col_25, "}");
3557 richloc.add_fixit_insert_before (col_21, "}");
3558 richloc.add_fixit_insert_before (col_1, "{");
3559 richloc.add_fixit_insert_before (col_25, "}");
3560 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3561 ASSERT_STREQ ("\n"
3562 " int a5[][0][0] = { 1, 2 };\n"
3563 " ^\n"
3564 " { -----\n"
3565 " {{1}}}}, {{{2 }}\n",
3566 pp_formatted_text (dc.printer));
3567 }
3568}
3569
896d130e 3570/* Insertion fix-it hint: adding a "break;" on a line by itself. */
d9020fe6 3571
3572static void
3573test_fixit_insert_containing_newline (const line_table_case &case_)
3574{
3575 /* Create a tempfile and write some text to it.
3576 .........................0000000001111111.
3577 .........................1234567890123456. */
3578 const char *old_content = (" case 'a':\n" /* line 1. */
3579 " x = a;\n" /* line 2. */
3580 " case 'b':\n" /* line 3. */
3581 " x = b;\n");/* line 4. */
3582
3583 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3584 line_table_test ltt (case_);
3585 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3586
d9020fe6 3587 location_t case_start = linemap_position_for_column (line_table, 5);
3588 location_t case_finish = linemap_position_for_column (line_table, 13);
3589 location_t case_loc = make_location (case_start, case_start, case_finish);
d9020fe6 3590 location_t line_start = linemap_position_for_column (line_table, 1);
d9020fe6 3591
3592 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3593 return;
3594
896d130e 3595 /* Add a "break;" on a line by itself before line 3 i.e. before
3596 column 1 of line 3. */
3597 {
3598 rich_location richloc (line_table, case_loc);
3599 richloc.add_fixit_insert_before (line_start, " break;\n");
e69492e4 3600
3601 /* Without line numbers. */
3602 {
3603 test_diagnostic_context dc;
3604 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3605 ASSERT_STREQ ("\n"
3606 " x = a;\n"
3607 "+ break;\n"
3608 " case 'b':\n"
3609 " ^~~~~~~~~\n",
3610 pp_formatted_text (dc.printer));
3611 }
3612
3613 /* With line numbers. */
3614 {
3615 test_diagnostic_context dc;
3616 dc.show_line_numbers_p = true;
3617 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3618 ASSERT_STREQ ("\n"
31087b7e 3619 " 2 | x = a;\n"
3620 " +++ |+ break;\n"
3621 " 3 | case 'b':\n"
3622 " | ^~~~~~~~~\n",
e69492e4 3623 pp_formatted_text (dc.printer));
3624 }
896d130e 3625 }
3626
3627 /* Verify that attempts to add text with a newline fail when the
3628 insertion point is *not* at the start of a line. */
3629 {
3630 rich_location richloc (line_table, case_loc);
3631 richloc.add_fixit_insert_before (case_start, "break;\n");
3632 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3633 test_diagnostic_context dc;
3634 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3635 ASSERT_STREQ ("\n"
3636 " case 'b':\n"
3637 " ^~~~~~~~~\n",
3638 pp_formatted_text (dc.printer));
3639 }
3640}
3641
3642/* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3643 of the file, where the fix-it is printed in a different line-span
3644 to the primary range of the diagnostic. */
3645
3646static void
3647test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3648{
3649 /* Create a tempfile and write some text to it.
3650 .........................0000000001111111.
3651 .........................1234567890123456. */
3652 const char *old_content = ("test (int ch)\n" /* line 1. */
3653 "{\n" /* line 2. */
3654 " putchar (ch);\n" /* line 3. */
3655 "}\n"); /* line 4. */
3656
3657 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3658 line_table_test ltt (case_);
3659
3660 const line_map_ordinary *ord_map = linemap_check_ordinary
3661 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3662 linemap_line_start (line_table, 1, 100);
3663
3664 /* The primary range is the "putchar" token. */
3665 location_t putchar_start
3666 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3667 location_t putchar_finish
3668 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3669 location_t putchar_loc
3670 = make_location (putchar_start, putchar_start, putchar_finish);
3671 rich_location richloc (line_table, putchar_loc);
3672
3673 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
3674 location_t file_start
3675 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3676 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3677
3678 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3679 return;
3680
dd9ed701 3681 {
3682 test_diagnostic_context dc;
3683 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3684 ASSERT_STREQ ("\n"
3685 "FILENAME:1:1:\n"
3686 "+#include <stdio.h>\n"
3687 " test (int ch)\n"
3688 "FILENAME:3:2:\n"
3689 " putchar (ch);\n"
3690 " ^~~~~~~\n",
3691 pp_formatted_text (dc.printer));
3692 }
3693
3694 /* With line-numbering, the line spans are close enough to be
3695 consolidated, since it makes little sense to skip line 2. */
3696 {
3697 test_diagnostic_context dc;
3698 dc.show_line_numbers_p = true;
3699 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3700 ASSERT_STREQ ("\n"
31087b7e 3701 " +++ |+#include <stdio.h>\n"
3702 " 1 | test (int ch)\n"
3703 " 2 | {\n"
3704 " 3 | putchar (ch);\n"
3705 " | ^~~~~~~\n",
dd9ed701 3706 pp_formatted_text (dc.printer));
3707 }
d9020fe6 3708}
3709
3710/* Replacement fix-it hint containing a newline.
896d130e 3711 This will fail, as newlines are only supported when inserting at the
3712 beginning of a line. */
d9020fe6 3713
3714static void
3715test_fixit_replace_containing_newline (const line_table_case &case_)
3716{
3717 /* Create a tempfile and write some text to it.
3718 .........................0000000001111.
3719 .........................1234567890123. */
3720 const char *old_content = "foo = bar ();\n";
3721
3722 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3723 line_table_test ltt (case_);
3724 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3725
3726 /* Replace the " = " with "\n = ", as if we were reformatting an
3727 overly long line. */
3728 location_t start = linemap_position_for_column (line_table, 4);
3729 location_t finish = linemap_position_for_column (line_table, 6);
3730 location_t loc = linemap_position_for_column (line_table, 13);
3731 rich_location richloc (line_table, loc);
3732 source_range range = source_range::from_locations (start, finish);
3733 richloc.add_fixit_replace (range, "\n =");
3734
896d130e 3735 /* Arbitrary newlines are not yet supported within fix-it hints, so
d9020fe6 3736 the fix-it should not be displayed. */
3737 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3738
3739 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3740 return;
3741
3742 test_diagnostic_context dc;
3743 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3744 ASSERT_STREQ ("\n"
3745 " foo = bar ();\n"
3746 " ^\n",
3747 pp_formatted_text (dc.printer));
3748}
3749
c2403f36 3750/* Fix-it hint, attempting to delete a newline.
3751 This will fail, as we currently only support fix-it hints that
3752 affect one line at a time. */
3753
3754static void
3755test_fixit_deletion_affecting_newline (const line_table_case &case_)
3756{
3757 /* Create a tempfile and write some text to it.
3758 ..........................0000000001111.
3759 ..........................1234567890123. */
3760 const char *old_content = ("foo = bar (\n"
3761 " );\n");
3762
3763 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3764 line_table_test ltt (case_);
3765 const line_map_ordinary *ord_map = linemap_check_ordinary
3766 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3767 linemap_line_start (line_table, 1, 100);
3768
3769 /* Attempt to delete the " (\n...)". */
3770 location_t start
3771 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3772 location_t caret
3773 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3774 location_t finish
3775 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3776 location_t loc = make_location (caret, start, finish);
3777 rich_location richloc (line_table, loc);
3778 richloc. add_fixit_remove ();
3779
3780 /* Fix-it hints that affect more than one line are not yet supported, so
3781 the fix-it should not be displayed. */
3782 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3783
3784 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3785 return;
3786
3787 test_diagnostic_context dc;
3788 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3789 ASSERT_STREQ ("\n"
3790 " foo = bar (\n"
3791 " ~^\n"
3792 " );\n"
3793 " ~ \n",
3794 pp_formatted_text (dc.printer));
3795}
3796
ff7410b8 3797/* Verify that line numbers are correctly printed for the case of
3798 a multiline range in which the width of the line numbers changes
3799 (e.g. from "9" to "10"). */
3800
3801static void
3802test_line_numbers_multiline_range ()
3803{
3804 /* Create a tempfile and write some text to it. */
3805 pretty_printer pp;
3806 for (int i = 0; i < 20; i++)
3807 /* .........0000000001111111.
3808 .............1234567890123456. */
3809 pp_printf (&pp, "this is line %i\n", i + 1);
3810 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3811 line_table_test ltt;
3812
3813 const line_map_ordinary *ord_map = linemap_check_ordinary
3814 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3815 linemap_line_start (line_table, 1, 100);
3816
3817 /* Create a multi-line location, starting at the "line" of line 9, with
3818 a caret on the "is" of line 10, finishing on the "this" line 11. */
3819
3820 location_t start
3821 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3822 location_t caret
3823 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3824 location_t finish
3825 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3826 location_t loc = make_location (caret, start, finish);
3827
3828 test_diagnostic_context dc;
3829 dc.show_line_numbers_p = true;
31087b7e 3830 dc.min_margin_width = 0;
ff7410b8 3831 gcc_rich_location richloc (loc);
3832 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3833 ASSERT_STREQ ("\n"
3834 " 9 | this is line 9\n"
3835 " | ~~~~~~\n"
3836 "10 | this is line 10\n"
3837 " | ~~~~~^~~~~~~~~~\n"
3838 "11 | this is line 11\n"
3839 " | ~~~~ \n",
3840 pp_formatted_text (dc.printer));
3841}
3842
99b4f3a2 3843/* Run all of the selftests within this file. */
3844
3845void
3846diagnostic_show_locus_c_tests ()
3847{
d73881b0 3848 test_line_span ();
3849
70017c99 3850 test_layout_range_for_single_point ();
3851 test_layout_range_for_single_line ();
3852 test_layout_range_for_multiple_lines ();
99b4f3a2 3853
3854 test_get_line_width_without_trailing_whitespace ();
48a7392b 3855
3856 test_diagnostic_show_locus_unknown_location ();
3857
3858 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
a8a31e3d 3859 for_each_line_table_case (test_add_location_if_nearby);
70017c99 3860 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
367964fa 3861 for_each_line_table_case (test_fixit_consolidation);
f085b618 3862 for_each_line_table_case (test_overlapped_fixit_printing);
f907f132 3863 for_each_line_table_case (test_overlapped_fixit_printing_2);
d9020fe6 3864 for_each_line_table_case (test_fixit_insert_containing_newline);
896d130e 3865 for_each_line_table_case (test_fixit_insert_containing_newline_2);
d9020fe6 3866 for_each_line_table_case (test_fixit_replace_containing_newline);
c2403f36 3867 for_each_line_table_case (test_fixit_deletion_affecting_newline);
ff7410b8 3868
3869 test_line_numbers_multiline_range ();
99b4f3a2 3870}
3871
3872} // namespace selftest
3873
3874#endif /* #if CHECKING_P */
62c34df8 3875
3876#if __GNUC__ >= 10
3877# pragma GCC diagnostic pop
3878#endif