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