]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/diagnostic-show-locus.c
Daily bump.
[thirdparty/gcc.git] / gcc / diagnostic-show-locus.c
CommitLineData
57eb2d70 1/* Diagnostic subroutines for printing source-code
818ab71a 2 Copyright (C) 1999-2016 Free Software Foundation, Inc.
57eb2d70
DM
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
22#include "system.h"
23#include "coretypes.h"
24#include "version.h"
25#include "demangle.h"
26#include "intl.h"
27#include "backtrace.h"
28#include "diagnostic.h"
29#include "diagnostic-color.h"
d9b950dd 30#include "selftest.h"
57eb2d70
DM
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
8a645150
DM
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
45namespace {
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
51struct 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
73class colorizer
74{
75 public:
76 colorizer (diagnostic_context *context,
cc015f3a 77 diagnostic_t diagnostic_kind);
8a645150
DM
78 ~colorizer ();
79
80 void set_range (int range_idx) { set_state (range_idx); }
81 void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
d41e76cf
DM
82 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
83 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
8a645150
DM
84
85 private:
86 void set_state (int state);
87 void begin_state (int state);
88 void finish_state (int state);
d41e76cf 89 const char *get_color_by_name (const char *);
8a645150
DM
90
91 private:
92 static const int STATE_NORMAL_TEXT = -1;
d41e76cf
DM
93 static const int STATE_FIXIT_INSERT = -2;
94 static const int STATE_FIXIT_DELETE = -3;
8a645150
DM
95
96 diagnostic_context *m_context;
cc015f3a 97 diagnostic_t m_diagnostic_kind;
8a645150 98 int m_current_state;
d41e76cf
DM
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;
8a645150
DM
105};
106
107/* A point within a layout_range; similar to an expanded_location,
108 but after filtering on file. */
109
110class 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
123class layout_range
124{
125 public:
40499f81
DM
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);
8a645150
DM
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
143struct line_bounds
144{
145 int m_first_non_ws;
146 int m_last_non_ws;
147};
148
876217ae
DM
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
154struct 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
8a645150
DM
183/* A class to control the overall layout when printing a diagnostic.
184
185 The layout is determined within the constructor.
a87a86e1
DM
186 It is then printed by repeatedly calling the "print_source_line",
187 "print_annotation_line" and "print_any_fixits" methods.
8a645150
DM
188
189 We assume we have disjoint ranges. */
190
191class layout
192{
193 public:
194 layout (diagnostic_context *context,
cc015f3a
DM
195 rich_location *richloc,
196 diagnostic_t diagnostic_kind);
8a645150 197
876217ae
DM
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;
8a645150
DM
204
205 bool print_source_line (int row, line_bounds *lbounds_out);
206 void print_annotation_line (int row, const line_bounds lbounds);
2ffe0809
DM
207 bool annotation_line_showed_range_p (int line, int start_column,
208 int finish_column) const;
a87a86e1 209 void print_any_fixits (int row, const rich_location *richloc);
8a645150 210
f3352cab
DM
211 void show_ruler (int max_column) const;
212
8a645150 213 private:
876217ae
DM
214 void calculate_line_spans ();
215
01e1dea3
DM
216 void print_newline ();
217
8a645150
DM
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
a87a86e1
DM
229 void
230 move_to_column (int *column, int dest_column);
231
8a645150
DM
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;
876217ae 240 auto_vec <line_span> m_line_spans;
8a645150
DM
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
249colorizer::colorizer (diagnostic_context *context,
cc015f3a 250 diagnostic_t diagnostic_kind) :
8a645150 251 m_context (context),
cc015f3a 252 m_diagnostic_kind (diagnostic_kind),
8a645150
DM
253 m_current_state (STATE_NORMAL_TEXT)
254{
d41e76cf
DM
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));
8a645150
DM
260}
261
262/* The destructor for "colorize". If colorization is on, print a code to
263 turn it off. */
264
265colorizer::~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
273void
274colorizer::set_state (int new_state)
275{
276 if (m_current_state != new_state)
57eb2d70 277 {
8a645150
DM
278 finish_state (m_current_state);
279 m_current_state = new_state;
280 begin_state (new_state);
57eb2d70 281 }
57eb2d70
DM
282}
283
8a645150
DM
284/* Turn on any colorization for STATE. */
285
57eb2d70 286void
8a645150 287colorizer::begin_state (int state)
57eb2d70 288{
8a645150
DM
289 switch (state)
290 {
291 case STATE_NORMAL_TEXT:
292 break;
57eb2d70 293
d41e76cf
DM
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
8a645150
DM
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),
cc015f3a 308 diagnostic_get_color_for_kind (m_diagnostic_kind)));
8a645150 309 break;
57eb2d70 310
8a645150 311 case 1:
d41e76cf 312 pp_string (m_context->printer, m_range1);
8a645150 313 break;
57eb2d70 314
8a645150 315 case 2:
d41e76cf 316 pp_string (m_context->printer, m_range2);
8a645150
DM
317 break;
318
319 default:
320 /* We don't expect more than 3 ranges per diagnostic. */
321 gcc_unreachable ();
322 break;
323 }
57eb2d70
DM
324}
325
8a645150
DM
326/* Turn off any colorization for STATE. */
327
57eb2d70 328void
8a645150
DM
329colorizer::finish_state (int state)
330{
d41e76cf
DM
331 if (state != STATE_NORMAL_TEXT)
332 pp_string (m_context->printer, m_stop_color);
333}
8a645150 334
d41e76cf
DM
335/* Get the color code for NAME (or the empty string if
336 colorization is disabled). */
8a645150 337
d41e76cf
DM
338const char *
339colorizer::get_color_by_name (const char *name)
340{
341 return colorize_start (pp_show_color (m_context->printer), name);
8a645150
DM
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
40499f81
DM
350layout_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)
8a645150
DM
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--+-----------------------------------------------
37301|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
37402|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
37503|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--+-----------------------------------------------
38401|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
38502|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
38603|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
38704|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
38805|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
38906|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
400bool
401layout_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
d9b950dd
DM
459#if CHECKING_P
460
461/* A helper function for testing layout_range::contains_point. */
462
463static layout_range
464make_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
479static void
480test_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
503static void
504test_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
533static void
534test_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
8a645150
DM
576/* Given a source line LINE of length LINE_WIDTH, determine the width
577 without any trailing whitespace. */
578
579static int
580get_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
d9b950dd
DM
599#if CHECKING_P
600
601/* A helper function for testing get_line_width_without_trailing_whitespace. */
602
603static void
604assert_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
614static void
615test_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
b4f3232d
DM
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
642static bool
643compatible_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
ded60913
DM
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
b4f3232d
DM
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
8a645150
DM
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.
876217ae
DM
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.
8a645150
DM
711 Determine m_x_offset, to ensure that the primary caret
712 will fit within the max_width provided by the diagnostic_context. */
713
714layout::layout (diagnostic_context * context,
cc015f3a
DM
715 rich_location *richloc,
716 diagnostic_t diagnostic_kind)
8a645150
DM
717: m_context (context),
718 m_pp (context->printer),
cc015f3a
DM
719 m_diagnostic_kind (diagnostic_kind),
720 m_exploc (richloc->get_expanded_location (0)),
721 m_colorizer (context, diagnostic_kind),
8a645150
DM
722 m_colorize_source_p (context->colorize_source_p),
723 m_layout_ranges (rich_location::MAX_RANGES),
876217ae 724 m_line_spans (1 + rich_location::MAX_RANGES),
8a645150
DM
725 m_x_offset (0)
726{
b4f3232d
DM
727 source_location primary_loc = richloc->get_range (0)->m_loc;
728
8a645150
DM
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. */
070856cc 733 const location_range *loc_range = richloc->get_range (idx);
8a645150 734
40499f81
DM
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
8a645150
DM
746 /* If any part of the range isn't in the same file as the primary
747 location of this diagnostic, ignore the range. */
40499f81 748 if (start.file != m_exploc.file)
8a645150 749 continue;
40499f81 750 if (finish.file != m_exploc.file)
8a645150
DM
751 continue;
752 if (loc_range->m_show_caret_p)
40499f81 753 if (caret.file != m_exploc.file)
8a645150
DM
754 continue;
755
b4f3232d
DM
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
070856cc
DM
764 /* Everything is now known to be in the correct source file,
765 but it may require further sanitization. */
40499f81 766 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
070856cc
DM
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
b4f3232d
DM
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))
070856cc
DM
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
8a645150
DM
792 /* Passed all the tests; add the range to m_layout_ranges so that
793 it will be printed. */
8a645150 794 m_layout_ranges.safe_push (ri);
8a645150
DM
795 }
796
876217ae
DM
797 /* Populate m_line_spans. */
798 calculate_line_spans ();
799
8a645150
DM
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;
57eb2d70 804 int line_width;
8a645150 805 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
57eb2d70 806 &line_width);
8a645150
DM
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 }
f3352cab
DM
817
818 if (context->show_ruler_p)
819 show_ruler (m_x_offset + max_width);
8a645150 820}
57eb2d70 821
876217ae
DM
822/* Return true iff we should print a heading when starting the
823 line span with the given index. */
824
825bool
826layout::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
845expanded_location
846layout::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
899void
900layout::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
8a645150
DM
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
964bool
965layout::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
8a645150
DM
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);
83eb5a03 979 line += m_x_offset;
8a645150
DM
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++)
57eb2d70 986 {
8a645150
DM
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 }
57eb2d70
DM
1011 char c = *line == '\t' ? ' ' : *line;
1012 if (c == '\0')
1013 c = ' ';
8a645150
DM
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);
57eb2d70
DM
1021 line++;
1022 }
01e1dea3 1023 print_newline ();
8a645150
DM
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. */
57eb2d70 1032
8a645150
DM
1033void
1034layout::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 }
01e1dea3 1065 print_newline ();
8a645150
DM
1066}
1067
2ffe0809
DM
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
1073bool
1074layout::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
a87a86e1
DM
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
1092void
1093layout::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);
d41e76cf 1111 m_colorizer.set_fixit_insert ();
a87a86e1
DM
1112 pp_string (m_pp, insert->get_string ());
1113 m_colorizer.set_normal_text ();
1114 column += insert->get_length ();
1115 }
1116 break;
1117
2ffe0809 1118 case fixit_hint::REPLACE:
a87a86e1 1119 {
2ffe0809
DM
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);
a87a86e1
DM
1123 int start_column = LOCATION_COLUMN (src_range.m_start);
1124 int finish_column = LOCATION_COLUMN (src_range.m_finish);
2ffe0809
DM
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)
a87a86e1 1133 {
2ffe0809 1134 move_to_column (&column, start_column);
d41e76cf 1135 m_colorizer.set_fixit_delete ();
2ffe0809
DM
1136 for (; column <= finish_column; column++)
1137 pp_character (m_pp, '-');
a87a86e1
DM
1138 m_colorizer.set_normal_text ();
1139 }
2ffe0809
DM
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);
d41e76cf 1146 m_colorizer.set_fixit_insert ();
2ffe0809
DM
1147 pp_string (m_pp, replace->get_string ());
1148 m_colorizer.set_normal_text ();
1149 column += replace->get_length ();
1150 }
a87a86e1
DM
1151 }
1152 break;
1153
1154 default:
1155 gcc_unreachable ();
1156 }
1157 }
1158 }
01e1dea3
DM
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
1166void
1167layout::print_newline ()
1168{
1169 m_colorizer.set_normal_text ();
1170 pp_newline (m_pp);
a87a86e1
DM
1171}
1172
8a645150
DM
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
1178bool
1179layout::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;
57eb2d70 1186 int i;
8a645150
DM
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;
0afbb81b
JJ
1195 if (range->m_show_caret_p
1196 && row == range->m_caret.m_line
8a645150 1197 && column == range->m_caret.m_column)
0afbb81b 1198 out_state->draw_caret_p = true;
57eb2d70 1199
8a645150
DM
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
1224int
1225layout::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)
57eb2d70 1233 {
8a645150
DM
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 }
57eb2d70 1251 }
8a645150
DM
1252
1253 return result;
1254}
1255
a87a86e1
DM
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
1260void
1261layout::move_to_column (int *column, int dest_column)
1262{
1263 /* Start a new line if we need to. */
1264 if (*column > dest_column)
1265 {
01e1dea3 1266 print_newline ();
a87a86e1
DM
1267 *column = 0;
1268 }
1269
1270 while (*column < dest_column)
1271 {
1272 pp_space (m_pp);
1273 (*column)++;
1274 }
1275}
1276
f3352cab
DM
1277/* For debugging layout issues, render a ruler giving column numbers
1278 (after the 1-column indent). */
1279
1280void
1281layout::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
8a645150
DM
1311} /* End of anonymous namespace. */
1312
1313/* Print the physical source code corresponding to the location of
1314 this diagnostic, with additional annotations. */
1315
1316void
1317diagnostic_show_locus (diagnostic_context * context,
cc015f3a
DM
1318 rich_location *richloc,
1319 diagnostic_t diagnostic_kind)
8a645150 1320{
01e1dea3
DM
1321 pp_newline (context->printer);
1322
cc015f3a 1323 location_t loc = richloc->get_loc ();
7c8f7eaa
DM
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. */
cc015f3a 1329 if (loc <= BUILTINS_LOCATION)
7c8f7eaa
DM
1330 return;
1331
1332 /* Don't print the same source location twice in a row, unless we have
1333 fix-it hints. */
cc015f3a
DM
1334 if (loc == context->last_location
1335 && richloc->get_num_fixit_hints () == 0)
8a645150
DM
1336 return;
1337
cc015f3a 1338 context->last_location = loc;
8a645150 1339
8a645150
DM
1340 const char *saved_prefix = pp_get_prefix (context->printer);
1341 pp_set_prefix (context->printer, NULL);
1342
cc015f3a 1343 layout layout (context, richloc, diagnostic_kind);
876217ae
DM
1344 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1345 line_span_idx++)
01e1dea3 1346 {
876217ae
DM
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++)
01e1dea3 1355 {
876217ae
DM
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);
cc015f3a 1363 layout.print_any_fixits (row, richloc);
876217ae 1364 }
01e1dea3
DM
1365 }
1366 }
8a645150 1367
57eb2d70 1368 pp_set_prefix (context->printer, saved_prefix);
57eb2d70 1369}
d9b950dd
DM
1370
1371#if CHECKING_P
1372
1373namespace selftest {
1374
cc015f3a
DM
1375/* Selftests for diagnostic_show_locus. */
1376
1377/* Convenience subclass of diagnostic_context for testing
1378 diagnostic_show_locus. */
1379
1380class 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
1396static void
1397test_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
1417static void
1418test_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
1432static void
1433test_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
1450static void
1451test_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
1484static void
1485test_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);
f9087798 1490 richloc.add_fixit_insert ("&");
cc015f3a
DM
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
1501static void
1502test_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);
f9087798 1509 richloc.add_fixit_remove ();
cc015f3a
DM
1510 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1511 ASSERT_STREQ ("\n"
1512 " foo = bar.field;\n"
1513 " ^~~~~~\n"
1514 " ------\n",
1515 pp_formatted_text (dc.printer));
1516}
1517
1518/* Replace fix-it hint: replacing "field" with "m_field". */
1519
1520static void
1521test_one_liner_fixit_replace ()
1522{
1523 test_diagnostic_context dc;
1524 location_t start = linemap_position_for_column (line_table, 11);
1525 location_t finish = linemap_position_for_column (line_table, 15);
1526 location_t field = make_location (start, start, finish);
1527 rich_location richloc (line_table, field);
f9087798 1528 richloc.add_fixit_replace ("m_field");
cc015f3a
DM
1529 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1530 ASSERT_STREQ ("\n"
1531 " foo = bar.field;\n"
1532 " ^~~~~\n"
1533 " m_field\n",
1534 pp_formatted_text (dc.printer));
1535}
1536
2ffe0809
DM
1537/* Replace fix-it hint: replacing "field" with "m_field",
1538 but where the caret was elsewhere. */
1539
1540static void
1541test_one_liner_fixit_replace_non_equal_range ()
1542{
1543 test_diagnostic_context dc;
1544 location_t equals = linemap_position_for_column (line_table, 5);
1545 location_t start = linemap_position_for_column (line_table, 11);
1546 location_t finish = linemap_position_for_column (line_table, 15);
1547 rich_location richloc (line_table, equals);
1548 source_range range;
1549 range.m_start = start;
1550 range.m_finish = finish;
1551 richloc.add_fixit_replace (range, "m_field");
1552 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1553 /* The replacement range is not indicated in the annotation line, so
1554 it should be indicated via an additional underline. */
1555 ASSERT_STREQ ("\n"
1556 " foo = bar.field;\n"
1557 " ^\n"
1558 " -----\n"
1559 " m_field\n",
1560 pp_formatted_text (dc.printer));
1561}
1562
1563/* Replace fix-it hint: replacing "field" with "m_field",
1564 where the caret was elsewhere, but where a secondary range
1565 exactly covers "field". */
1566
1567static void
1568test_one_liner_fixit_replace_equal_secondary_range ()
1569{
1570 test_diagnostic_context dc;
1571 location_t equals = linemap_position_for_column (line_table, 5);
1572 location_t start = linemap_position_for_column (line_table, 11);
1573 location_t finish = linemap_position_for_column (line_table, 15);
1574 rich_location richloc (line_table, equals);
1575 location_t field = make_location (start, start, finish);
1576 richloc.add_range (field, false);
f9087798 1577 richloc.add_fixit_replace (field, "m_field");
2ffe0809
DM
1578 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1579 /* The replacement range is indicated in the annotation line,
1580 so it shouldn't be indicated via an additional underline. */
1581 ASSERT_STREQ ("\n"
1582 " foo = bar.field;\n"
1583 " ^ ~~~~~\n"
1584 " m_field\n",
1585 pp_formatted_text (dc.printer));
1586}
1587
2aa51413
DM
1588/* Verify that we can use ad-hoc locations when adding fixits to a
1589 rich_location. */
1590
1591static void
1592test_one_liner_fixit_validation_adhoc_locations ()
1593{
1594 /* Generate a range that's too long to be packed, so must
1595 be stored as an ad-hoc location (given the defaults
1596 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
1597 const location_t c7 = linemap_position_for_column (line_table, 7);
1598 const location_t c47 = linemap_position_for_column (line_table, 47);
1599 const location_t loc = make_location (c7, c7, c47);
1600
1601 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1602 return;
1603
1604 ASSERT_TRUE (IS_ADHOC_LOC (loc));
1605
1606 /* Insert. */
1607 {
1608 rich_location richloc (line_table, loc);
1609 richloc.add_fixit_insert (loc, "test");
1610 /* It should not have been discarded by the validator. */
1611 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1612
1613 test_diagnostic_context dc;
1614 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1615 ASSERT_STREQ ("\n"
1616 " foo = bar.field;\n"
1617 " ^~~~~~~~~~ \n"
1618 " test\n",
1619 pp_formatted_text (dc.printer));
1620 }
1621
1622 /* Remove. */
1623 {
1624 rich_location richloc (line_table, loc);
1625 source_range range = source_range::from_locations (loc, c47);
1626 richloc.add_fixit_remove (range);
1627 /* It should not have been discarded by the validator. */
1628 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1629
1630 test_diagnostic_context dc;
1631 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1632 ASSERT_STREQ ("\n"
1633 " foo = bar.field;\n"
1634 " ^~~~~~~~~~ \n"
1635 " -----------------------------------------\n",
1636 pp_formatted_text (dc.printer));
1637 }
1638
1639 /* Replace. */
1640 {
1641 rich_location richloc (line_table, loc);
1642 source_range range = source_range::from_locations (loc, c47);
1643 richloc.add_fixit_replace (range, "test");
1644 /* It should not have been discarded by the validator. */
1645 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1646
1647 test_diagnostic_context dc;
1648 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1649 ASSERT_STREQ ("\n"
1650 " foo = bar.field;\n"
1651 " ^~~~~~~~~~ \n"
1652 " test\n",
1653 pp_formatted_text (dc.printer));
1654 }
1655}
1656
cc015f3a
DM
1657/* Run the various one-liner tests. */
1658
1659static void
1660test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1661{
1662 /* Create a tempfile and write some text to it.
1663 ....................0000000001111111.
1664 ....................1234567890123456. */
1665 const char *content = "foo = bar.field;\n";
1666 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1667 line_table_test ltt (case_);
1668
1669 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1670
1671 location_t line_end = linemap_position_for_column (line_table, 16);
1672
1673 /* Don't attempt to run the tests if column data might be unavailable. */
1674 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1675 return;
1676
1677 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1678 ASSERT_EQ (1, LOCATION_LINE (line_end));
1679 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1680
1681 test_one_liner_simple_caret ();
1682 test_one_liner_caret_and_range ();
1683 test_one_liner_multiple_carets_and_ranges ();
1684 test_one_liner_fixit_insert ();
1685 test_one_liner_fixit_remove ();
1686 test_one_liner_fixit_replace ();
2ffe0809
DM
1687 test_one_liner_fixit_replace_non_equal_range ();
1688 test_one_liner_fixit_replace_equal_secondary_range ();
2aa51413 1689 test_one_liner_fixit_validation_adhoc_locations ();
cc015f3a
DM
1690}
1691
ee908516
DM
1692/* Verify that fix-it hints are appropriately consolidated.
1693
1694 If any fix-it hints in a rich_location involve locations beyond
1695 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
1696 the fix-it as a whole, so there should be none.
1697
1698 Otherwise, verify that consecutive "replace" and "remove" fix-its
1699 are merged, and that other fix-its remain separate. */
1700
1701static void
1702test_fixit_consolidation (const line_table_case &case_)
1703{
1704 line_table_test ltt (case_);
1705
1706 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
1707
1708 const location_t c10 = linemap_position_for_column (line_table, 10);
1709 const location_t c15 = linemap_position_for_column (line_table, 15);
1710 const location_t c16 = linemap_position_for_column (line_table, 16);
1711 const location_t c17 = linemap_position_for_column (line_table, 17);
1712 const location_t c20 = linemap_position_for_column (line_table, 20);
1713 const location_t caret = c10;
1714
1715 /* Insert + insert. */
1716 {
1717 rich_location richloc (line_table, caret);
1718 richloc.add_fixit_insert (c10, "foo");
1719 richloc.add_fixit_insert (c15, "bar");
1720
1721 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1722 /* Bogus column info for 2nd fixit, so no fixits. */
1723 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1724 else
1725 /* They should not have been merged. */
1726 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1727 }
1728
1729 /* Insert + replace. */
1730 {
1731 rich_location richloc (line_table, caret);
1732 richloc.add_fixit_insert (c10, "foo");
1733 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
1734 "bar");
1735
1736 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1737 /* Bogus column info for 2nd fixit, so no fixits. */
1738 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1739 else
1740 /* They should not have been merged. */
1741 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1742 }
1743
1744 /* Replace + non-consecutive insert. */
1745 {
1746 rich_location richloc (line_table, caret);
1747 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1748 "bar");
1749 richloc.add_fixit_insert (c17, "foo");
1750
1751 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1752 /* Bogus column info for 2nd fixit, so no fixits. */
1753 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1754 else
1755 /* They should not have been merged. */
1756 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1757 }
1758
1759 /* Replace + non-consecutive replace. */
1760 {
1761 rich_location richloc (line_table, caret);
1762 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1763 "foo");
1764 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
1765 "bar");
1766
1767 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1768 /* Bogus column info for 2nd fixit, so no fixits. */
1769 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1770 else
1771 /* They should not have been merged. */
1772 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
1773 }
1774
1775 /* Replace + consecutive replace. */
1776 {
1777 rich_location richloc (line_table, caret);
1778 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1779 "foo");
1780 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
1781 "bar");
1782
1783 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1784 /* Bogus column info for 2nd fixit, so no fixits. */
1785 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1786 else
1787 {
1788 /* They should have been merged into a single "replace". */
1789 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1790 const fixit_hint *hint = richloc.get_fixit_hint (0);
1791 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1792 const fixit_replace *replace = (const fixit_replace *)hint;
1793 ASSERT_STREQ ("foobar", replace->get_string ());
1794 ASSERT_EQ (c10, replace->get_range ().m_start);
1795 ASSERT_EQ (c20, replace->get_range ().m_finish);
1796 }
1797 }
1798
1799 /* Replace + consecutive removal. */
1800 {
1801 rich_location richloc (line_table, caret);
1802 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
1803 "foo");
1804 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
1805
1806 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1807 /* Bogus column info for 2nd fixit, so no fixits. */
1808 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1809 else
1810 {
1811 /* They should have been merged into a single replace, with the
1812 range extended to cover that of the removal. */
1813 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1814 const fixit_hint *hint = richloc.get_fixit_hint (0);
1815 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1816 const fixit_replace *replace = (const fixit_replace *)hint;
1817 ASSERT_STREQ ("foo", replace->get_string ());
1818 ASSERT_EQ (c10, replace->get_range ().m_start);
1819 ASSERT_EQ (c20, replace->get_range ().m_finish);
1820 }
1821 }
1822
1823 /* Consecutive removals. */
1824 {
1825 rich_location richloc (line_table, caret);
1826 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
1827 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
1828
1829 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1830 /* Bogus column info for 2nd fixit, so no fixits. */
1831 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
1832 else
1833 {
1834 /* They should have been merged into a single "replace-with-empty". */
1835 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1836 const fixit_hint *hint = richloc.get_fixit_hint (0);
1837 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
1838 const fixit_replace *replace = (const fixit_replace *)hint;
1839 ASSERT_STREQ ("", replace->get_string ());
1840 ASSERT_EQ (c10, replace->get_range ().m_start);
1841 ASSERT_EQ (c20, replace->get_range ().m_finish);
1842 }
1843 }
1844}
1845
d9b950dd
DM
1846/* Run all of the selftests within this file. */
1847
1848void
1849diagnostic_show_locus_c_tests ()
1850{
1851 test_range_contains_point_for_single_point ();
1852 test_range_contains_point_for_single_line ();
1853 test_range_contains_point_for_multiple_lines ();
1854
1855 test_get_line_width_without_trailing_whitespace ();
cc015f3a
DM
1856
1857 test_diagnostic_show_locus_unknown_location ();
1858
1859 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
ee908516 1860 for_each_line_table_case (test_fixit_consolidation);
d9b950dd
DM
1861}
1862
1863} // namespace selftest
1864
1865#endif /* #if CHECKING_P */