comparison gcc/diagnostic-show-locus.c @ 16:04ced10e8804

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