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