Add validation and consolidation of fix-it hints
[gcc.git] / gcc / diagnostic-show-locus.c
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2016 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 "selftest.h"
31
32 #ifdef HAVE_TERMIOS_H
33 # include <termios.h>
34 #endif
35
36 #ifdef GWINSZ_IN_SYS_IOCTL
37 # include <sys/ioctl.h>
38 #endif
39
40 /* Classes for rendering source code and diagnostics, within an
41 anonymous namespace.
42 The work is done by "class layout", which embeds and uses
43 "class colorizer" and "class layout_range" to get things done. */
44
45 namespace {
46
47 /* The state at a given point of the source code, assuming that we're
48 in a range: which range are we in, and whether we should draw a caret at
49 this point. */
50
51 struct point_state
52 {
53 int range_idx;
54 bool draw_caret_p;
55 };
56
57 /* A class to inject colorization codes when printing the diagnostic locus.
58
59 It has one kind of colorization for each of:
60 - normal text
61 - range 0 (the "primary location")
62 - range 1
63 - range 2
64
65 The class caches the lookup of the color codes for the above.
66
67 The class also has responsibility for tracking which of the above is
68 active, filtering out unnecessary changes. This allows
69 layout::print_source_line and layout::print_annotation_line
70 to simply request a colorization code for *every* character they print,
71 via this class, and have the filtering be done for them here. */
72
73 class colorizer
74 {
75 public:
76 colorizer (diagnostic_context *context,
77 diagnostic_t diagnostic_kind);
78 ~colorizer ();
79
80 void set_range (int range_idx) { set_state (range_idx); }
81 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
82 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
83 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
84
85 private:
86 void set_state (int state);
87 void begin_state (int state);
88 void finish_state (int state);
89 const char *get_color_by_name (const char *);
90
91 private:
92 static const int STATE_NORMAL_TEXT = -1;
93 static const int STATE_FIXIT_INSERT = -2;
94 static const int STATE_FIXIT_DELETE = -3;
95
96 diagnostic_context *m_context;
97 diagnostic_t m_diagnostic_kind;
98 int m_current_state;
99 const char *m_caret;
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
133 layout_point m_start;
134 layout_point m_finish;
135 bool m_show_caret_p;
136 layout_point m_caret;
137 };
138
139 /* A struct for use by layout::print_source_line for telling
140 layout::print_annotation_line the extents of the source line that
141 it printed, so that underlines can be clipped appropriately. */
142
143 struct line_bounds
144 {
145 int m_first_non_ws;
146 int m_last_non_ws;
147 };
148
149 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
150 or "line 23"). During the layout ctor, layout::calculate_line_spans
151 splits the pertinent source lines into a list of disjoint line_span
152 instances (e.g. lines 5-10, lines 15-20, line 23). */
153
154 struct line_span
155 {
156 line_span (linenum_type first_line, linenum_type last_line)
157 : m_first_line (first_line), m_last_line (last_line)
158 {
159 gcc_assert (first_line <= last_line);
160 }
161 linenum_type get_first_line () const { return m_first_line; }
162 linenum_type get_last_line () const { return m_last_line; }
163
164 bool contains_line_p (linenum_type line) const
165 {
166 return line >= m_first_line && line <= m_last_line;
167 }
168
169 static int comparator (const void *p1, const void *p2)
170 {
171 const line_span *ls1 = (const line_span *)p1;
172 const line_span *ls2 = (const line_span *)p2;
173 int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
174 if (first_line_diff)
175 return first_line_diff;
176 return (int)ls1->m_last_line - (int)ls2->m_last_line;
177 }
178
179 linenum_type m_first_line;
180 linenum_type m_last_line;
181 };
182
183 /* A class to control the overall layout when printing a diagnostic.
184
185 The layout is determined within the constructor.
186 It is then printed by repeatedly calling the "print_source_line",
187 "print_annotation_line" and "print_any_fixits" methods.
188
189 We assume we have disjoint ranges. */
190
191 class layout
192 {
193 public:
194 layout (diagnostic_context *context,
195 rich_location *richloc,
196 diagnostic_t diagnostic_kind);
197
198 int get_num_line_spans () const { return m_line_spans.length (); }
199 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
200
201 bool print_heading_for_line_span_index_p (int line_span_idx) const;
202
203 expanded_location get_expanded_location (const line_span *) const;
204
205 bool print_source_line (int row, line_bounds *lbounds_out);
206 void print_annotation_line (int row, const line_bounds lbounds);
207 bool annotation_line_showed_range_p (int line, int start_column,
208 int finish_column) const;
209 void print_any_fixits (int row, const rich_location *richloc);
210
211 void show_ruler (int max_column) const;
212
213 private:
214 void calculate_line_spans ();
215
216 void print_newline ();
217
218 bool
219 get_state_at_point (/* Inputs. */
220 int row, int column,
221 int first_non_ws, int last_non_ws,
222 /* Outputs. */
223 point_state *out_state);
224
225 int
226 get_x_bound_for_row (int row, int caret_column,
227 int last_non_ws);
228
229 void
230 move_to_column (int *column, int dest_column);
231
232 private:
233 diagnostic_context *m_context;
234 pretty_printer *m_pp;
235 diagnostic_t m_diagnostic_kind;
236 expanded_location m_exploc;
237 colorizer m_colorizer;
238 bool m_colorize_source_p;
239 auto_vec <layout_range> m_layout_ranges;
240 auto_vec <line_span> m_line_spans;
241 int m_x_offset;
242 };
243
244 /* Implementation of "class colorizer". */
245
246 /* The constructor for "colorizer". Lookup and store color codes for the
247 different kinds of things we might need to print. */
248
249 colorizer::colorizer (diagnostic_context *context,
250 diagnostic_t diagnostic_kind) :
251 m_context (context),
252 m_diagnostic_kind (diagnostic_kind),
253 m_current_state (STATE_NORMAL_TEXT)
254 {
255 m_range1 = get_color_by_name ("range1");
256 m_range2 = get_color_by_name ("range2");
257 m_fixit_insert = get_color_by_name ("fixit-insert");
258 m_fixit_delete = get_color_by_name ("fixit-delete");
259 m_stop_color = colorize_stop (pp_show_color (context->printer));
260 }
261
262 /* The destructor for "colorize". If colorization is on, print a code to
263 turn it off. */
264
265 colorizer::~colorizer ()
266 {
267 finish_state (m_current_state);
268 }
269
270 /* Update state, printing color codes if necessary if there's a state
271 change. */
272
273 void
274 colorizer::set_state (int new_state)
275 {
276 if (m_current_state != new_state)
277 {
278 finish_state (m_current_state);
279 m_current_state = new_state;
280 begin_state (new_state);
281 }
282 }
283
284 /* Turn on any colorization for STATE. */
285
286 void
287 colorizer::begin_state (int state)
288 {
289 switch (state)
290 {
291 case STATE_NORMAL_TEXT:
292 break;
293
294 case STATE_FIXIT_INSERT:
295 pp_string (m_context->printer, m_fixit_insert);
296 break;
297
298 case STATE_FIXIT_DELETE:
299 pp_string (m_context->printer, m_fixit_delete);
300 break;
301
302 case 0:
303 /* Make range 0 be the same color as the "kind" text
304 (error vs warning vs note). */
305 pp_string
306 (m_context->printer,
307 colorize_start (pp_show_color (m_context->printer),
308 diagnostic_get_color_for_kind (m_diagnostic_kind)));
309 break;
310
311 case 1:
312 pp_string (m_context->printer, m_range1);
313 break;
314
315 case 2:
316 pp_string (m_context->printer, m_range2);
317 break;
318
319 default:
320 /* We don't expect more than 3 ranges per diagnostic. */
321 gcc_unreachable ();
322 break;
323 }
324 }
325
326 /* Turn off any colorization for STATE. */
327
328 void
329 colorizer::finish_state (int state)
330 {
331 if (state != STATE_NORMAL_TEXT)
332 pp_string (m_context->printer, m_stop_color);
333 }
334
335 /* Get the color code for NAME (or the empty string if
336 colorization is disabled). */
337
338 const char *
339 colorizer::get_color_by_name (const char *name)
340 {
341 return colorize_start (pp_show_color (m_context->printer), name);
342 }
343
344 /* Implementation of class layout_range. */
345
346 /* The constructor for class layout_range.
347 Initialize various layout_point fields from expanded_location
348 equivalents; we've already filtered on file. */
349
350 layout_range::layout_range (const expanded_location *start_exploc,
351 const expanded_location *finish_exploc,
352 bool show_caret_p,
353 const expanded_location *caret_exploc)
354 : m_start (*start_exploc),
355 m_finish (*finish_exploc),
356 m_show_caret_p (show_caret_p),
357 m_caret (*caret_exploc)
358 {
359 }
360
361 /* Is (column, row) within the given range?
362 We've already filtered on the file.
363
364 Ranges are closed (both limits are within the range).
365
366 Example A: a single-line range:
367 start: (col=22, line=2)
368 finish: (col=38, line=2)
369
370 |00000011111111112222222222333333333344444444444
371 |34567890123456789012345678901234567890123456789
372 --+-----------------------------------------------
373 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
374 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
375 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
376
377 Example B: a multiline range with
378 start: (col=14, line=3)
379 finish: (col=08, line=5)
380
381 |00000011111111112222222222333333333344444444444
382 |34567890123456789012345678901234567890123456789
383 --+-----------------------------------------------
384 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
385 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
386 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
387 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
388 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
389 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
390 --+-----------------------------------------------
391
392 Legend:
393 - 'b' indicates a point *before* the range
394 - 'S' indicates the start of the range
395 - 'w' indicates a point within the range
396 - 'F' indicates the finish of the range (which is
397 within it).
398 - 'a' indicates a subsequent point *after* the range. */
399
400 bool
401 layout_range::contains_point (int row, int column) const
402 {
403 gcc_assert (m_start.m_line <= m_finish.m_line);
404 /* ...but the equivalent isn't true for the columns;
405 consider example B in the comment above. */
406
407 if (row < m_start.m_line)
408 /* Points before the first line of the range are
409 outside it (corresponding to line 01 in example A
410 and lines 01 and 02 in example B above). */
411 return false;
412
413 if (row == m_start.m_line)
414 /* On same line as start of range (corresponding
415 to line 02 in example A and line 03 in example B). */
416 {
417 if (column < m_start.m_column)
418 /* Points on the starting line of the range, but
419 before the column in which it begins. */
420 return false;
421
422 if (row < m_finish.m_line)
423 /* This is a multiline range; the point
424 is within it (corresponds to line 03 in example B
425 from column 14 onwards) */
426 return true;
427 else
428 {
429 /* This is a single-line range. */
430 gcc_assert (row == m_finish.m_line);
431 return column <= m_finish.m_column;
432 }
433 }
434
435 /* The point is in a line beyond that containing the
436 start of the range: lines 03 onwards in example A,
437 and lines 04 onwards in example B. */
438 gcc_assert (row > m_start.m_line);
439
440 if (row > m_finish.m_line)
441 /* The point is beyond the final line of the range
442 (lines 03 onwards in example A, and lines 06 onwards
443 in example B). */
444 return false;
445
446 if (row < m_finish.m_line)
447 {
448 /* The point is in a line that's fully within a multiline
449 range (e.g. line 04 in example B). */
450 gcc_assert (m_start.m_line < m_finish.m_line);
451 return true;
452 }
453
454 gcc_assert (row == m_finish.m_line);
455
456 return column <= m_finish.m_column;
457 }
458
459 #if CHECKING_P
460
461 /* A helper function for testing layout_range::contains_point. */
462
463 static layout_range
464 make_range (int start_line, int start_col, int end_line, int end_col)
465 {
466 const expanded_location start_exploc
467 = {"test.c", start_line, start_col, NULL, false};
468 const expanded_location finish_exploc
469 = {"test.c", end_line, end_col, NULL, false};
470 return layout_range (&start_exploc, &finish_exploc, false,
471 &start_exploc);
472 }
473
474 /* Selftests for layout_range::contains_point. */
475
476 /* Selftest for layout_range::contains_point where the layout_range
477 is a range with start==end i.e. a single point. */
478
479 static void
480 test_range_contains_point_for_single_point ()
481 {
482 layout_range point = make_range (7, 10, 7, 10);
483
484 /* Before the line. */
485 ASSERT_FALSE (point.contains_point (6, 1));
486
487 /* On the line, but before start. */
488 ASSERT_FALSE (point.contains_point (7, 9));
489
490 /* At the point. */
491 ASSERT_TRUE (point.contains_point (7, 10));
492
493 /* On the line, after the point. */
494 ASSERT_FALSE (point.contains_point (7, 11));
495
496 /* After the line. */
497 ASSERT_FALSE (point.contains_point (8, 1));
498 }
499
500 /* Selftest for layout_range::contains_point where the layout_range
501 is the single-line range shown as "Example A" above. */
502
503 static void
504 test_range_contains_point_for_single_line ()
505 {
506 layout_range example_a = make_range (2, 22, 2, 38);
507
508 /* Before the line. */
509 ASSERT_FALSE (example_a.contains_point (1, 1));
510
511 /* On the line, but before start. */
512 ASSERT_FALSE (example_a.contains_point (2, 21));
513
514 /* On the line, at the start. */
515 ASSERT_TRUE (example_a.contains_point (2, 22));
516
517 /* On the line, within the range. */
518 ASSERT_TRUE (example_a.contains_point (2, 23));
519
520 /* On the line, at the end. */
521 ASSERT_TRUE (example_a.contains_point (2, 38));
522
523 /* On the line, after the end. */
524 ASSERT_FALSE (example_a.contains_point (2, 39));
525
526 /* After the line. */
527 ASSERT_FALSE (example_a.contains_point (2, 39));
528 }
529
530 /* Selftest for layout_range::contains_point where the layout_range
531 is the multi-line range shown as "Example B" above. */
532
533 static void
534 test_range_contains_point_for_multiple_lines ()
535 {
536 layout_range example_b = make_range (3, 14, 5, 8);
537
538 /* Before first line. */
539 ASSERT_FALSE (example_b.contains_point (1, 1));
540
541 /* On the first line, but before start. */
542 ASSERT_FALSE (example_b.contains_point (3, 13));
543
544 /* At the start. */
545 ASSERT_TRUE (example_b.contains_point (3, 14));
546
547 /* On the first line, within the range. */
548 ASSERT_TRUE (example_b.contains_point (3, 15));
549
550 /* On an interior line.
551 The column number should not matter; try various boundary
552 values. */
553 ASSERT_TRUE (example_b.contains_point (4, 1));
554 ASSERT_TRUE (example_b.contains_point (4, 7));
555 ASSERT_TRUE (example_b.contains_point (4, 8));
556 ASSERT_TRUE (example_b.contains_point (4, 9));
557 ASSERT_TRUE (example_b.contains_point (4, 13));
558 ASSERT_TRUE (example_b.contains_point (4, 14));
559 ASSERT_TRUE (example_b.contains_point (4, 15));
560
561 /* On the final line, before the end. */
562 ASSERT_TRUE (example_b.contains_point (5, 7));
563
564 /* On the final line, at the end. */
565 ASSERT_TRUE (example_b.contains_point (5, 8));
566
567 /* On the final line, after the end. */
568 ASSERT_FALSE (example_b.contains_point (5, 9));
569
570 /* After the line. */
571 ASSERT_FALSE (example_b.contains_point (6, 1));
572 }
573
574 #endif /* #if CHECKING_P */
575
576 /* Given a source line LINE of length LINE_WIDTH, determine the width
577 without any trailing whitespace. */
578
579 static int
580 get_line_width_without_trailing_whitespace (const char *line, int line_width)
581 {
582 int result = line_width;
583 while (result > 0)
584 {
585 char ch = line[result - 1];
586 if (ch == ' ' || ch == '\t')
587 result--;
588 else
589 break;
590 }
591 gcc_assert (result >= 0);
592 gcc_assert (result <= line_width);
593 gcc_assert (result == 0 ||
594 (line[result - 1] != ' '
595 && line[result -1] != '\t'));
596 return result;
597 }
598
599 #if CHECKING_P
600
601 /* A helper function for testing get_line_width_without_trailing_whitespace. */
602
603 static void
604 assert_eq (const char *line, int expected_width)
605 {
606 int actual_value
607 = get_line_width_without_trailing_whitespace (line, strlen (line));
608 ASSERT_EQ (actual_value, expected_width);
609 }
610
611 /* Verify that get_line_width_without_trailing_whitespace is sane for
612 various inputs. It is not required to handle newlines. */
613
614 static void
615 test_get_line_width_without_trailing_whitespace ()
616 {
617 assert_eq ("", 0);
618 assert_eq (" ", 0);
619 assert_eq ("\t", 0);
620 assert_eq ("hello world", 11);
621 assert_eq ("hello world ", 11);
622 assert_eq ("hello world \t\t ", 11);
623 }
624
625 #endif /* #if CHECKING_P */
626
627 /* Helper function for layout's ctor, for sanitizing locations relative
628 to the primary location within a diagnostic.
629
630 Compare LOC_A and LOC_B to see if it makes sense to print underlines
631 connecting their expanded locations. Doing so is only guaranteed to
632 make sense if the locations share the same macro expansion "history"
633 i.e. they can be traced through the same macro expansions, eventually
634 reaching an ordinary map.
635
636 This may be too strong a condition, but it effectively sanitizes
637 PR c++/70105, which has an example of printing an expression where the
638 final location of the expression is in a different macro, which
639 erroneously was leading to hundreds of lines of irrelevant source
640 being printed. */
641
642 static bool
643 compatible_locations_p (location_t loc_a, location_t loc_b)
644 {
645 if (IS_ADHOC_LOC (loc_a))
646 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
647 if (IS_ADHOC_LOC (loc_b))
648 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
649
650 /* If either location is one of the special locations outside of a
651 linemap, they are only compatible if they are equal. */
652 if (loc_a < RESERVED_LOCATION_COUNT
653 || loc_b < RESERVED_LOCATION_COUNT)
654 return loc_a == loc_b;
655
656 const line_map *map_a = linemap_lookup (line_table, loc_a);
657 linemap_assert (map_a);
658
659 const line_map *map_b = linemap_lookup (line_table, loc_b);
660 linemap_assert (map_b);
661
662 /* Are they within the same map? */
663 if (map_a == map_b)
664 {
665 /* Are both within the same macro expansion? */
666 if (linemap_macro_expansion_map_p (map_a))
667 {
668 /* Expand each location towards the spelling location, and
669 recurse. */
670 const line_map_macro *macro_map = linemap_check_macro (map_a);
671 source_location loc_a_toward_spelling
672 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
673 macro_map,
674 loc_a);
675 source_location loc_b_toward_spelling
676 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
677 macro_map,
678 loc_b);
679 return compatible_locations_p (loc_a_toward_spelling,
680 loc_b_toward_spelling);
681 }
682
683 /* Otherwise they are within the same ordinary map. */
684 return true;
685 }
686 else
687 {
688 /* Within different maps. */
689
690 /* If either is within a macro expansion, they are incompatible. */
691 if (linemap_macro_expansion_map_p (map_a)
692 || linemap_macro_expansion_map_p (map_b))
693 return false;
694
695 /* Within two different ordinary maps; they are compatible iff they
696 are in the same file. */
697 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
698 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
699 return ord_map_a->to_file == ord_map_b->to_file;
700 }
701 }
702
703 /* Implementation of class layout. */
704
705 /* Constructor for class layout.
706
707 Filter the ranges from the rich_location to those that we can
708 sanely print, populating m_layout_ranges.
709 Determine the range of lines that we will print, splitting them
710 up into an ordered list of disjoint spans of contiguous line numbers.
711 Determine m_x_offset, to ensure that the primary caret
712 will fit within the max_width provided by the diagnostic_context. */
713
714 layout::layout (diagnostic_context * context,
715 rich_location *richloc,
716 diagnostic_t diagnostic_kind)
717 : m_context (context),
718 m_pp (context->printer),
719 m_diagnostic_kind (diagnostic_kind),
720 m_exploc (richloc->get_expanded_location (0)),
721 m_colorizer (context, diagnostic_kind),
722 m_colorize_source_p (context->colorize_source_p),
723 m_layout_ranges (rich_location::MAX_RANGES),
724 m_line_spans (1 + rich_location::MAX_RANGES),
725 m_x_offset (0)
726 {
727 source_location primary_loc = richloc->get_range (0)->m_loc;
728
729 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
730 {
731 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
732 Ignore any ranges that are awkward to handle. */
733 const location_range *loc_range = richloc->get_range (idx);
734
735 /* Split the "range" into caret and range information. */
736 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
737
738 /* Expand the various locations. */
739 expanded_location start
740 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
741 expanded_location finish
742 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
743 expanded_location caret
744 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
745
746 /* If any part of the range isn't in the same file as the primary
747 location of this diagnostic, ignore the range. */
748 if (start.file != m_exploc.file)
749 continue;
750 if (finish.file != m_exploc.file)
751 continue;
752 if (loc_range->m_show_caret_p)
753 if (caret.file != m_exploc.file)
754 continue;
755
756 /* Sanitize the caret location for non-primary ranges. */
757 if (m_layout_ranges.length () > 0)
758 if (loc_range->m_show_caret_p)
759 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
760 /* Discard any non-primary ranges that can't be printed
761 sanely relative to the primary location. */
762 continue;
763
764 /* Everything is now known to be in the correct source file,
765 but it may require further sanitization. */
766 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
767
768 /* If we have a range that finishes before it starts (perhaps
769 from something built via macro expansion), printing the
770 range is likely to be nonsensical. Also, attempting to do so
771 breaks assumptions within the printing code (PR c/68473).
772 Similarly, don't attempt to print ranges if one or both ends
773 of the range aren't sane to print relative to the
774 primary location (PR c++/70105). */
775 if (start.line > finish.line
776 || !compatible_locations_p (src_range.m_start, primary_loc)
777 || !compatible_locations_p (src_range.m_finish, primary_loc))
778 {
779 /* Is this the primary location? */
780 if (m_layout_ranges.length () == 0)
781 {
782 /* We want to print the caret for the primary location, but
783 we must sanitize away m_start and m_finish. */
784 ri.m_start = ri.m_caret;
785 ri.m_finish = ri.m_caret;
786 }
787 else
788 /* This is a non-primary range; ignore it. */
789 continue;
790 }
791
792 /* Passed all the tests; add the range to m_layout_ranges so that
793 it will be printed. */
794 m_layout_ranges.safe_push (ri);
795 }
796
797 /* Populate m_line_spans. */
798 calculate_line_spans ();
799
800 /* Adjust m_x_offset.
801 Center the primary caret to fit in max_width; all columns
802 will be adjusted accordingly. */
803 int max_width = m_context->caret_max_width;
804 int line_width;
805 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
806 &line_width);
807 if (line && m_exploc.column <= line_width)
808 {
809 int right_margin = CARET_LINE_MARGIN;
810 int column = m_exploc.column;
811 right_margin = MIN (line_width - column, right_margin);
812 right_margin = max_width - right_margin;
813 if (line_width >= max_width && column > right_margin)
814 m_x_offset = column - right_margin;
815 gcc_assert (m_x_offset >= 0);
816 }
817
818 if (context->show_ruler_p)
819 show_ruler (m_x_offset + max_width);
820 }
821
822 /* Return true iff we should print a heading when starting the
823 line span with the given index. */
824
825 bool
826 layout::print_heading_for_line_span_index_p (int line_span_idx) const
827 {
828 /* We print a heading for every change of line span, hence for every
829 line span after the initial one. */
830 if (line_span_idx > 0)
831 return true;
832
833 /* We also do it for the initial span if the primary location of the
834 diagnostic is in a different span. */
835 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
836 return true;
837
838 return false;
839 }
840
841 /* Get an expanded_location for the first location of interest within
842 the given line_span.
843 Used when printing a heading to indicate a new line span. */
844
845 expanded_location
846 layout::get_expanded_location (const line_span *line_span) const
847 {
848 /* Whenever possible, use the caret location. */
849 if (line_span->contains_line_p (m_exploc.line))
850 return m_exploc;
851
852 /* Otherwise, use the start of the first range that's present
853 within the line_span. */
854 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
855 {
856 const layout_range *lr = &m_layout_ranges[i];
857 if (line_span->contains_line_p (lr->m_start.m_line))
858 {
859 expanded_location exploc = m_exploc;
860 exploc.line = lr->m_start.m_line;
861 exploc.column = lr->m_start.m_column;
862 return exploc;
863 }
864 }
865
866 /* It should not be possible to have a line span that didn't
867 contain any of the layout_range instances. */
868 gcc_unreachable ();
869 return m_exploc;
870 }
871
872 /* We want to print the pertinent source code at a diagnostic. The
873 rich_location can contain multiple locations. This will have been
874 filtered into m_exploc (the caret for the primary location) and
875 m_layout_ranges, for those ranges within the same source file.
876
877 We will print a subset of the lines within the source file in question,
878 as a collection of "spans" of lines.
879
880 This function populates m_line_spans with an ordered, disjoint list of
881 the line spans of interest.
882
883 For example, if the primary caret location is on line 7, with ranges
884 covering lines 5-6 and lines 9-12:
885
886 004
887 005 |RANGE 0
888 006 |RANGE 0
889 007 |PRIMARY CARET
890 008
891 009 |RANGE 1
892 010 |RANGE 1
893 011 |RANGE 1
894 012 |RANGE 1
895 013
896
897 then we want two spans: lines 5-7 and lines 9-12. */
898
899 void
900 layout::calculate_line_spans ()
901 {
902 /* This should only be called once, by the ctor. */
903 gcc_assert (m_line_spans.length () == 0);
904
905 /* Populate tmp_spans with individual spans, for each of
906 m_exploc, and for m_layout_ranges. */
907 auto_vec<line_span> tmp_spans (1 + rich_location::MAX_RANGES);
908 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
909 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
910 {
911 const layout_range *lr = &m_layout_ranges[i];
912 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
913 tmp_spans.safe_push (line_span (lr->m_start.m_line,
914 lr->m_finish.m_line));
915 }
916
917 /* Sort them. */
918 tmp_spans.qsort(line_span::comparator);
919
920 /* Now iterate through tmp_spans, copying into m_line_spans, and
921 combining where possible. */
922 gcc_assert (tmp_spans.length () > 0);
923 m_line_spans.safe_push (tmp_spans[0]);
924 for (unsigned int i = 1; i < tmp_spans.length (); i++)
925 {
926 line_span *current = &m_line_spans[m_line_spans.length () - 1];
927 const line_span *next = &tmp_spans[i];
928 gcc_assert (next->m_first_line >= current->m_first_line);
929 if (next->m_first_line <= current->m_last_line + 1)
930 {
931 /* We can merge them. */
932 if (next->m_last_line > current->m_last_line)
933 current->m_last_line = next->m_last_line;
934 }
935 else
936 {
937 /* No merger possible. */
938 m_line_spans.safe_push (*next);
939 }
940 }
941
942 /* Verify the result, in m_line_spans. */
943 gcc_assert (m_line_spans.length () > 0);
944 for (unsigned int i = 1; i < m_line_spans.length (); i++)
945 {
946 const line_span *prev = &m_line_spans[i - 1];
947 const line_span *next = &m_line_spans[i];
948 /* The individual spans must be sane. */
949 gcc_assert (prev->m_first_line <= prev->m_last_line);
950 gcc_assert (next->m_first_line <= next->m_last_line);
951 /* The spans must be ordered. */
952 gcc_assert (prev->m_first_line < next->m_first_line);
953 /* There must be a gap of at least one line between separate spans. */
954 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
955 }
956 }
957
958 /* Attempt to print line ROW of source code, potentially colorized at any
959 ranges.
960 Return true if the line was printed, populating *LBOUNDS_OUT.
961 Return false if the source line could not be read, leaving *LBOUNDS_OUT
962 untouched. */
963
964 bool
965 layout::print_source_line (int row, line_bounds *lbounds_out)
966 {
967 int line_width;
968 const char *line = location_get_source_line (m_exploc.file, row,
969 &line_width);
970 if (!line)
971 return false;
972
973 m_colorizer.set_normal_text ();
974
975 /* We will stop printing the source line at any trailing
976 whitespace. */
977 line_width = get_line_width_without_trailing_whitespace (line,
978 line_width);
979 line += m_x_offset;
980
981 pp_space (m_pp);
982 int first_non_ws = INT_MAX;
983 int last_non_ws = 0;
984 int column;
985 for (column = 1 + m_x_offset; column <= line_width; column++)
986 {
987 /* Assuming colorization is enabled for the caret and underline
988 characters, we may also colorize the associated characters
989 within the source line.
990
991 For frontends that generate range information, we color the
992 associated characters in the source line the same as the
993 carets and underlines in the annotation line, to make it easier
994 for the reader to see the pertinent code.
995
996 For frontends that only generate carets, we don't colorize the
997 characters above them, since this would look strange (e.g.
998 colorizing just the first character in a token). */
999 if (m_colorize_source_p)
1000 {
1001 bool in_range_p;
1002 point_state state;
1003 in_range_p = get_state_at_point (row, column,
1004 0, INT_MAX,
1005 &state);
1006 if (in_range_p)
1007 m_colorizer.set_range (state.range_idx);
1008 else
1009 m_colorizer.set_normal_text ();
1010 }
1011 char c = *line == '\t' ? ' ' : *line;
1012 if (c == '\0')
1013 c = ' ';
1014 if (c != ' ')
1015 {
1016 last_non_ws = column;
1017 if (first_non_ws == INT_MAX)
1018 first_non_ws = column;
1019 }
1020 pp_character (m_pp, c);
1021 line++;
1022 }
1023 print_newline ();
1024
1025 lbounds_out->m_first_non_ws = first_non_ws;
1026 lbounds_out->m_last_non_ws = last_non_ws;
1027 return true;
1028 }
1029
1030 /* Print a line consisting of the caret/underlines for the given
1031 source line. */
1032
1033 void
1034 layout::print_annotation_line (int row, const line_bounds lbounds)
1035 {
1036 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1037 lbounds.m_last_non_ws);
1038
1039 pp_space (m_pp);
1040 for (int column = 1 + m_x_offset; column < x_bound; column++)
1041 {
1042 bool in_range_p;
1043 point_state state;
1044 in_range_p = get_state_at_point (row, column,
1045 lbounds.m_first_non_ws,
1046 lbounds.m_last_non_ws,
1047 &state);
1048 if (in_range_p)
1049 {
1050 /* Within a range. Draw either the caret or an underline. */
1051 m_colorizer.set_range (state.range_idx);
1052 if (state.draw_caret_p)
1053 /* Draw the caret. */
1054 pp_character (m_pp, m_context->caret_chars[state.range_idx]);
1055 else
1056 pp_character (m_pp, '~');
1057 }
1058 else
1059 {
1060 /* Not in a range. */
1061 m_colorizer.set_normal_text ();
1062 pp_character (m_pp, ' ');
1063 }
1064 }
1065 print_newline ();
1066 }
1067
1068 /* Subroutine of layout::print_any_fixits.
1069
1070 Determine if the annotation line printed for LINE contained
1071 the exact range from START_COLUMN to FINISH_COLUMN. */
1072
1073 bool
1074 layout::annotation_line_showed_range_p (int line, int start_column,
1075 int finish_column) const
1076 {
1077 layout_range *range;
1078 int i;
1079 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1080 if (range->m_start.m_line == line
1081 && range->m_start.m_column == start_column
1082 && range->m_finish.m_line == line
1083 && range->m_finish.m_column == finish_column)
1084 return true;
1085 return false;
1086 }
1087
1088 /* If there are any fixit hints on source line ROW within RICHLOC, print them.
1089 They are printed in order, attempting to combine them onto lines, but
1090 starting new lines if necessary. */
1091
1092 void
1093 layout::print_any_fixits (int row, const rich_location *richloc)
1094 {
1095 int column = 0;
1096 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
1097 {
1098 fixit_hint *hint = richloc->get_fixit_hint (i);
1099 if (hint->affects_line_p (m_exploc.file, row))
1100 {
1101 /* For now we assume each fixit hint can only touch one line. */
1102 switch (hint->get_kind ())
1103 {
1104 case fixit_hint::INSERT:
1105 {
1106 fixit_insert *insert = static_cast <fixit_insert *> (hint);
1107 /* This assumes the insertion just affects one line. */
1108 int start_column
1109 = LOCATION_COLUMN (insert->get_location ());
1110 move_to_column (&column, start_column);
1111 m_colorizer.set_fixit_insert ();
1112 pp_string (m_pp, insert->get_string ());
1113 m_colorizer.set_normal_text ();
1114 column += insert->get_length ();
1115 }
1116 break;
1117
1118 case fixit_hint::REPLACE:
1119 {
1120 fixit_replace *replace = static_cast <fixit_replace *> (hint);
1121 source_range src_range = replace->get_range ();
1122 int line = LOCATION_LINE (src_range.m_start);
1123 int start_column = LOCATION_COLUMN (src_range.m_start);
1124 int finish_column = LOCATION_COLUMN (src_range.m_finish);
1125
1126 /* If the range of the replacement wasn't printed in the
1127 annotation line, then print an extra underline to
1128 indicate exactly what is being replaced.
1129 Always show it for removals. */
1130 if (!annotation_line_showed_range_p (line, start_column,
1131 finish_column)
1132 || replace->get_length () == 0)
1133 {
1134 move_to_column (&column, start_column);
1135 m_colorizer.set_fixit_delete ();
1136 for (; column <= finish_column; column++)
1137 pp_character (m_pp, '-');
1138 m_colorizer.set_normal_text ();
1139 }
1140 /* Print the replacement text. REPLACE also covers
1141 removals, so only do this extra work (potentially starting
1142 a new line) if we have actual replacement text. */
1143 if (replace->get_length () > 0)
1144 {
1145 move_to_column (&column, start_column);
1146 m_colorizer.set_fixit_insert ();
1147 pp_string (m_pp, replace->get_string ());
1148 m_colorizer.set_normal_text ();
1149 column += replace->get_length ();
1150 }
1151 }
1152 break;
1153
1154 default:
1155 gcc_unreachable ();
1156 }
1157 }
1158 }
1159
1160 /* Add a trailing newline, if necessary. */
1161 move_to_column (&column, 0);
1162 }
1163
1164 /* Disable any colorization and emit a newline. */
1165
1166 void
1167 layout::print_newline ()
1168 {
1169 m_colorizer.set_normal_text ();
1170 pp_newline (m_pp);
1171 }
1172
1173 /* Return true if (ROW/COLUMN) is within a range of the layout.
1174 If it returns true, OUT_STATE is written to, with the
1175 range index, and whether we should draw the caret at
1176 (ROW/COLUMN) (as opposed to an underline). */
1177
1178 bool
1179 layout::get_state_at_point (/* Inputs. */
1180 int row, int column,
1181 int first_non_ws, int last_non_ws,
1182 /* Outputs. */
1183 point_state *out_state)
1184 {
1185 layout_range *range;
1186 int i;
1187 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1188 {
1189 if (range->contains_point (row, column))
1190 {
1191 out_state->range_idx = i;
1192
1193 /* Are we at the range's caret? is it visible? */
1194 out_state->draw_caret_p = false;
1195 if (range->m_show_caret_p
1196 && row == range->m_caret.m_line
1197 && column == range->m_caret.m_column)
1198 out_state->draw_caret_p = true;
1199
1200 /* Within a multiline range, don't display any underline
1201 in any leading or trailing whitespace on a line.
1202 We do display carets, however. */
1203 if (!out_state->draw_caret_p)
1204 if (column < first_non_ws || column > last_non_ws)
1205 return false;
1206
1207 /* We are within a range. */
1208 return true;
1209 }
1210 }
1211
1212 return false;
1213 }
1214
1215 /* Helper function for use by layout::print_line when printing the
1216 annotation line under the source line.
1217 Get the column beyond the rightmost one that could contain a caret or
1218 range marker, given that we stop rendering at trailing whitespace.
1219 ROW is the source line within the given file.
1220 CARET_COLUMN is the column of range 0's caret.
1221 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1222 character of source (as determined when printing the source line). */
1223
1224 int
1225 layout::get_x_bound_for_row (int row, int caret_column,
1226 int last_non_ws_column)
1227 {
1228 int result = caret_column + 1;
1229
1230 layout_range *range;
1231 int i;
1232 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1233 {
1234 if (row >= range->m_start.m_line)
1235 {
1236 if (range->m_finish.m_line == row)
1237 {
1238 /* On the final line within a range; ensure that
1239 we render up to the end of the range. */
1240 if (result <= range->m_finish.m_column)
1241 result = range->m_finish.m_column + 1;
1242 }
1243 else if (row < range->m_finish.m_line)
1244 {
1245 /* Within a multiline range; ensure that we render up to the
1246 last non-whitespace column. */
1247 if (result <= last_non_ws_column)
1248 result = last_non_ws_column + 1;
1249 }
1250 }
1251 }
1252
1253 return result;
1254 }
1255
1256 /* Given *COLUMN as an x-coordinate, print spaces to position
1257 successive output at DEST_COLUMN, printing a newline if necessary,
1258 and updating *COLUMN. */
1259
1260 void
1261 layout::move_to_column (int *column, int dest_column)
1262 {
1263 /* Start a new line if we need to. */
1264 if (*column > dest_column)
1265 {
1266 print_newline ();
1267 *column = 0;
1268 }
1269
1270 while (*column < dest_column)
1271 {
1272 pp_space (m_pp);
1273 (*column)++;
1274 }
1275 }
1276
1277 /* For debugging layout issues, render a ruler giving column numbers
1278 (after the 1-column indent). */
1279
1280 void
1281 layout::show_ruler (int max_column) const
1282 {
1283 /* Hundreds. */
1284 if (max_column > 99)
1285 {
1286 pp_space (m_pp);
1287 for (int column = 1 + m_x_offset; column <= max_column; column++)
1288 if (0 == column % 10)
1289 pp_character (m_pp, '0' + (column / 100) % 10);
1290 else
1291 pp_space (m_pp);
1292 pp_newline (m_pp);
1293 }
1294
1295 /* Tens. */
1296 pp_space (m_pp);
1297 for (int column = 1 + m_x_offset; column <= max_column; column++)
1298 if (0 == column % 10)
1299 pp_character (m_pp, '0' + (column / 10) % 10);
1300 else
1301 pp_space (m_pp);
1302 pp_newline (m_pp);
1303
1304 /* Units. */
1305 pp_space (m_pp);
1306 for (int column = 1 + m_x_offset; column <= max_column; column++)
1307 pp_character (m_pp, '0' + (column % 10));
1308 pp_newline (m_pp);
1309 }
1310
1311 } /* End of anonymous namespace. */
1312
1313 /* Print the physical source code corresponding to the location of
1314 this diagnostic, with additional annotations. */
1315
1316 void
1317 diagnostic_show_locus (diagnostic_context * context,
1318 rich_location *richloc,
1319 diagnostic_t diagnostic_kind)
1320 {
1321 pp_newline (context->printer);
1322
1323 location_t loc = richloc->get_loc ();
1324 /* Do nothing if source-printing has been disabled. */
1325 if (!context->show_caret)
1326 return;
1327
1328 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1329 if (loc <= BUILTINS_LOCATION)
1330 return;
1331
1332 /* Don't print the same source location twice in a row, unless we have
1333 fix-it hints. */
1334 if (loc == context->last_location
1335 && richloc->get_num_fixit_hints () == 0)
1336 return;
1337
1338 context->last_location = loc;
1339
1340 const char *saved_prefix = pp_get_prefix (context->printer);
1341 pp_set_prefix (context->printer, NULL);
1342
1343 layout layout (context, richloc, diagnostic_kind);
1344 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1345 line_span_idx++)
1346 {
1347 const line_span *line_span = layout.get_line_span (line_span_idx);
1348 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1349 {
1350 expanded_location exploc = layout.get_expanded_location (line_span);
1351 context->start_span (context, exploc);
1352 }
1353 int last_line = line_span->get_last_line ();
1354 for (int row = line_span->get_first_line (); row <= last_line; row++)
1355 {
1356 /* Print the source line, followed by an annotation line
1357 consisting of any caret/underlines, then any fixits.
1358 If the source line can't be read, print nothing. */
1359 line_bounds lbounds;
1360 if (layout.print_source_line (row, &lbounds))
1361 {
1362 layout.print_annotation_line (row, lbounds);
1363 layout.print_any_fixits (row, richloc);
1364 }
1365 }
1366 }
1367
1368 pp_set_prefix (context->printer, saved_prefix);
1369 }
1370
1371 #if CHECKING_P
1372
1373 namespace selftest {
1374
1375 /* Selftests for diagnostic_show_locus. */
1376
1377 /* Convenience subclass of diagnostic_context for testing
1378 diagnostic_show_locus. */
1379
1380 class test_diagnostic_context : public diagnostic_context
1381 {
1382 public:
1383 test_diagnostic_context ()
1384 {
1385 diagnostic_initialize (this, 0);
1386 show_caret = true;
1387 }
1388 ~test_diagnostic_context ()
1389 {
1390 diagnostic_finish (this);
1391 }
1392 };
1393
1394 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1395
1396 static void
1397 test_diagnostic_show_locus_unknown_location ()
1398 {
1399 test_diagnostic_context dc;
1400 rich_location richloc (line_table, UNKNOWN_LOCATION);
1401 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1402 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1403 }
1404
1405 /* Verify that diagnostic_show_locus works sanely for various
1406 single-line cases.
1407
1408 All of these work on the following 1-line source file:
1409 .0000000001111111
1410 .1234567890123456
1411 "foo = bar.field;\n"
1412 which is set up by test_diagnostic_show_locus_one_liner and calls
1413 them. */
1414
1415 /* Just a caret. */
1416
1417 static void
1418 test_one_liner_simple_caret ()
1419 {
1420 test_diagnostic_context dc;
1421 location_t caret = linemap_position_for_column (line_table, 10);
1422 rich_location richloc (line_table, caret);
1423 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1424 ASSERT_STREQ ("\n"
1425 " foo = bar.field;\n"
1426 " ^\n",
1427 pp_formatted_text (dc.printer));
1428 }
1429
1430 /* Caret and range. */
1431
1432 static void
1433 test_one_liner_caret_and_range ()
1434 {
1435 test_diagnostic_context dc;
1436 location_t caret = linemap_position_for_column (line_table, 10);
1437 location_t start = linemap_position_for_column (line_table, 7);
1438 location_t finish = linemap_position_for_column (line_table, 15);
1439 location_t loc = make_location (caret, start, finish);
1440 rich_location richloc (line_table, loc);
1441 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1442 ASSERT_STREQ ("\n"
1443 " foo = bar.field;\n"
1444 " ~~~^~~~~~\n",
1445 pp_formatted_text (dc.printer));
1446 }
1447
1448 /* Multiple ranges and carets. */
1449
1450 static void
1451 test_one_liner_multiple_carets_and_ranges ()
1452 {
1453 test_diagnostic_context dc;
1454 location_t foo
1455 = make_location (linemap_position_for_column (line_table, 2),
1456 linemap_position_for_column (line_table, 1),
1457 linemap_position_for_column (line_table, 3));
1458 dc.caret_chars[0] = 'A';
1459
1460 location_t bar
1461 = make_location (linemap_position_for_column (line_table, 8),
1462 linemap_position_for_column (line_table, 7),
1463 linemap_position_for_column (line_table, 9));
1464 dc.caret_chars[1] = 'B';
1465
1466 location_t field
1467 = make_location (linemap_position_for_column (line_table, 13),
1468 linemap_position_for_column (line_table, 11),
1469 linemap_position_for_column (line_table, 15));
1470 dc.caret_chars[2] = 'C';
1471
1472 rich_location richloc (line_table, foo);
1473 richloc.add_range (bar, true);
1474 richloc.add_range (field, true);
1475 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1476 ASSERT_STREQ ("\n"
1477 " foo = bar.field;\n"
1478 " ~A~ ~B~ ~~C~~\n",
1479 pp_formatted_text (dc.printer));
1480 }
1481
1482 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1483
1484 static void
1485 test_one_liner_fixit_insert ()
1486 {
1487 test_diagnostic_context dc;
1488 location_t caret = linemap_position_for_column (line_table, 7);
1489 rich_location richloc (line_table, caret);
1490 richloc.add_fixit_insert (caret, "&");
1491 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1492 ASSERT_STREQ ("\n"
1493 " foo = bar.field;\n"
1494 " ^\n"
1495 " &\n",
1496 pp_formatted_text (dc.printer));
1497 }
1498
1499 /* Removal fix-it hint: removal of the ".field". */
1500
1501 static void
1502 test_one_liner_fixit_remove ()
1503 {
1504 test_diagnostic_context dc;
1505 location_t start = linemap_position_for_column (line_table, 10);
1506 location_t finish = linemap_position_for_column (line_table, 15);
1507 location_t dot = make_location (start, start, finish);
1508 rich_location richloc (line_table, dot);
1509 source_range range;
1510 range.m_start = start;
1511 range.m_finish = finish;
1512 richloc.add_fixit_remove (range);
1513 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1514 ASSERT_STREQ ("\n"
1515 " foo = bar.field;\n"
1516 " ^~~~~~\n"
1517 " ------\n",
1518 pp_formatted_text (dc.printer));
1519 }
1520
1521 /* Replace fix-it hint: replacing "field" with "m_field". */
1522
1523 static void
1524 test_one_liner_fixit_replace ()
1525 {
1526 test_diagnostic_context dc;
1527 location_t start = linemap_position_for_column (line_table, 11);
1528 location_t finish = linemap_position_for_column (line_table, 15);
1529 location_t field = make_location (start, start, finish);
1530 rich_location richloc (line_table, field);
1531 source_range range;
1532 range.m_start = start;
1533 range.m_finish = finish;
1534 richloc.add_fixit_replace (range, "m_field");
1535 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1536 ASSERT_STREQ ("\n"
1537 " foo = bar.field;\n"
1538 " ^~~~~\n"
1539 " m_field\n",
1540 pp_formatted_text (dc.printer));
1541 }
1542
1543 /* Replace fix-it hint: replacing "field" with "m_field",
1544 but where the caret was elsewhere. */
1545
1546 static void
1547 test_one_liner_fixit_replace_non_equal_range ()
1548 {
1549 test_diagnostic_context dc;
1550 location_t equals = linemap_position_for_column (line_table, 5);
1551 location_t start = linemap_position_for_column (line_table, 11);
1552 location_t finish = linemap_position_for_column (line_table, 15);
1553 rich_location richloc (line_table, equals);
1554 source_range range;
1555 range.m_start = start;
1556 range.m_finish = finish;
1557 richloc.add_fixit_replace (range, "m_field");
1558 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1559 /* The replacement range is not indicated in the annotation line, so
1560 it should be indicated via an additional underline. */
1561 ASSERT_STREQ ("\n"
1562 " foo = bar.field;\n"
1563 " ^\n"
1564 " -----\n"
1565 " m_field\n",
1566 pp_formatted_text (dc.printer));
1567 }
1568
1569 /* Replace fix-it hint: replacing "field" with "m_field",
1570 where the caret was elsewhere, but where a secondary range
1571 exactly covers "field". */
1572
1573 static void
1574 test_one_liner_fixit_replace_equal_secondary_range ()
1575 {
1576 test_diagnostic_context dc;
1577 location_t equals = linemap_position_for_column (line_table, 5);
1578 location_t start = linemap_position_for_column (line_table, 11);
1579 location_t finish = linemap_position_for_column (line_table, 15);
1580 rich_location richloc (line_table, equals);
1581 location_t field = make_location (start, start, finish);
1582 richloc.add_range (field, false);
1583 source_range range;
1584 range.m_start = start;
1585 range.m_finish = finish;
1586 richloc.add_fixit_replace (range, "m_field");
1587 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1588 /* The replacement range is indicated in the annotation line,
1589 so it shouldn't be indicated via an additional underline. */
1590 ASSERT_STREQ ("\n"
1591 " foo = bar.field;\n"
1592 " ^ ~~~~~\n"
1593 " m_field\n",
1594 pp_formatted_text (dc.printer));
1595 }
1596
1597 /* Run the various one-liner tests. */
1598
1599 static void
1600 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1601 {
1602 /* Create a tempfile and write some text to it.
1603 ....................0000000001111111.
1604 ....................1234567890123456. */
1605 const char *content = "foo = bar.field;\n";
1606 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1607 line_table_test ltt (case_);
1608
1609 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1610
1611 location_t line_end = linemap_position_for_column (line_table, 16);
1612
1613 /* Don't attempt to run the tests if column data might be unavailable. */
1614 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1615 return;
1616
1617 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1618 ASSERT_EQ (1, LOCATION_LINE (line_end));
1619 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1620
1621 test_one_liner_simple_caret ();
1622 test_one_liner_caret_and_range ();
1623 test_one_liner_multiple_carets_and_ranges ();
1624 test_one_liner_fixit_insert ();
1625 test_one_liner_fixit_remove ();
1626 test_one_liner_fixit_replace ();
1627 test_one_liner_fixit_replace_non_equal_range ();
1628 test_one_liner_fixit_replace_equal_secondary_range ();
1629 }
1630
1631 /* Verify that fix-it hints are appropriately consolidated.
1632
1633 If any fix-it hints in a rich_location involve locations beyond
1634 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
1635 the fix-it as a whole, so there should be none.
1636
1637 Otherwise, verify that consecutive "replace" and "remove" fix-its
1638 are merged, and that other fix-its remain separate. */
1639
1640 static void
1641 test_fixit_consolidation (const line_table_case &case_)
1642 {
1643 line_table_test ltt (case_);
1644
1645 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
1646
1647 const location_t c10 = linemap_position_for_column (line_table, 10);
1648 const location_t c15 = linemap_position_for_column (line_table, 15);
1649 const location_t c16 = linemap_position_for_column (line_table, 16);
1650 const location_t c17 = linemap_position_for_column (line_table, 17);
1651 const location_t c20 = linemap_position_for_column (line_table, 20);
1652 const location_t caret = c10;
1653
1654 /* Insert + insert. */
1655 {
1656 rich_location richloc (line_table, caret);
1657 richloc.add_fixit_insert (c10, "foo");
1658 richloc.add_fixit_insert (c15, "bar");
1659
1660 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1661 /* Bogus column info for 2nd fixit, so no fixits. */
1662 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1663 else
1664 /* They should not have been merged. */
1665 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1666 }
1667
1668 /* Insert + replace. */
1669 {
1670 rich_location richloc (line_table, caret);
1671 richloc.add_fixit_insert (c10, "foo");
1672 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
1673 "bar");
1674
1675 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1676 /* Bogus column info for 2nd fixit, so no fixits. */
1677 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1678 else
1679 /* They should not have been merged. */
1680 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1681 }
1682
1683 /* Replace + non-consecutive insert. */
1684 {
1685 rich_location richloc (line_table, caret);
1686 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1687 "bar");
1688 richloc.add_fixit_insert (c17, "foo");
1689
1690 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1691 /* Bogus column info for 2nd fixit, so no fixits. */
1692 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1693 else
1694 /* They should not have been merged. */
1695 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1696 }
1697
1698 /* Replace + non-consecutive replace. */
1699 {
1700 rich_location richloc (line_table, caret);
1701 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1702 "foo");
1703 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
1704 "bar");
1705
1706 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1707 /* Bogus column info for 2nd fixit, so no fixits. */
1708 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1709 else
1710 /* They should not have been merged. */
1711 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1712 }
1713
1714 /* Replace + consecutive replace. */
1715 {
1716 rich_location richloc (line_table, caret);
1717 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1718 "foo");
1719 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
1720 "bar");
1721
1722 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1723 /* Bogus column info for 2nd fixit, so no fixits. */
1724 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1725 else
1726 {
1727 /* They should have been merged into a single "replace". */
1728 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1729 const fixit_hint *hint = richloc.get_fixit_hint (0);
1730 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1731 const fixit_replace *replace = (const fixit_replace *)hint;
1732 ASSERT_STREQ ("foobar", replace->get_string ());
1733 ASSERT_EQ (c10, replace->get_range ().m_start);
1734 ASSERT_EQ (c20, replace->get_range ().m_finish);
1735 }
1736 }
1737
1738 /* Replace + consecutive removal. */
1739 {
1740 rich_location richloc (line_table, caret);
1741 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1742 "foo");
1743 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
1744
1745 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1746 /* Bogus column info for 2nd fixit, so no fixits. */
1747 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1748 else
1749 {
1750 /* They should have been merged into a single replace, with the
1751 range extended to cover that of the removal. */
1752 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1753 const fixit_hint *hint = richloc.get_fixit_hint (0);
1754 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1755 const fixit_replace *replace = (const fixit_replace *)hint;
1756 ASSERT_STREQ ("foo", replace->get_string ());
1757 ASSERT_EQ (c10, replace->get_range ().m_start);
1758 ASSERT_EQ (c20, replace->get_range ().m_finish);
1759 }
1760 }
1761
1762 /* Consecutive removals. */
1763 {
1764 rich_location richloc (line_table, caret);
1765 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
1766 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
1767
1768 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1769 /* Bogus column info for 2nd fixit, so no fixits. */
1770 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1771 else
1772 {
1773 /* They should have been merged into a single "replace-with-empty". */
1774 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1775 const fixit_hint *hint = richloc.get_fixit_hint (0);
1776 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1777 const fixit_replace *replace = (const fixit_replace *)hint;
1778 ASSERT_STREQ ("", replace->get_string ());
1779 ASSERT_EQ (c10, replace->get_range ().m_start);
1780 ASSERT_EQ (c20, replace->get_range ().m_finish);
1781 }
1782 }
1783 }
1784
1785 /* Run all of the selftests within this file. */
1786
1787 void
1788 diagnostic_show_locus_c_tests ()
1789 {
1790 test_range_contains_point_for_single_point ();
1791 test_range_contains_point_for_single_line ();
1792 test_range_contains_point_for_multiple_lines ();
1793
1794 test_get_line_width_without_trailing_whitespace ();
1795
1796 test_diagnostic_show_locus_unknown_location ();
1797
1798 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
1799 for_each_line_table_case (test_fixit_consolidation);
1800 }
1801
1802 } // namespace selftest
1803
1804 #endif /* #if CHECKING_P */