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