5c386ae256d9606d47d4d100e7d0b6e0622cfaf1
[gcc.git] / gcc / diagnostic-show-locus.c
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2017 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "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 bool intersects_line_p (int row) const;
133
134 layout_point m_start;
135 layout_point m_finish;
136 bool m_show_caret_p;
137 layout_point m_caret;
138 };
139
140 /* A struct for use by layout::print_source_line for telling
141 layout::print_annotation_line the extents of the source line that
142 it printed, so that underlines can be clipped appropriately. */
143
144 struct line_bounds
145 {
146 int m_first_non_ws;
147 int m_last_non_ws;
148 };
149
150 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
151 or "line 23"). During the layout ctor, layout::calculate_line_spans
152 splits the pertinent source lines into a list of disjoint line_span
153 instances (e.g. lines 5-10, lines 15-20, line 23). */
154
155 struct line_span
156 {
157 line_span (linenum_type first_line, linenum_type last_line)
158 : m_first_line (first_line), m_last_line (last_line)
159 {
160 gcc_assert (first_line <= last_line);
161 }
162 linenum_type get_first_line () const { return m_first_line; }
163 linenum_type get_last_line () const { return m_last_line; }
164
165 bool contains_line_p (linenum_type line) const
166 {
167 return line >= m_first_line && line <= m_last_line;
168 }
169
170 static int comparator (const void *p1, const void *p2)
171 {
172 const line_span *ls1 = (const line_span *)p1;
173 const line_span *ls2 = (const line_span *)p2;
174 int first_line_diff = (int)ls1->m_first_line - (int)ls2->m_first_line;
175 if (first_line_diff)
176 return first_line_diff;
177 return (int)ls1->m_last_line - (int)ls2->m_last_line;
178 }
179
180 linenum_type m_first_line;
181 linenum_type m_last_line;
182 };
183
184 /* A class to control the overall layout when printing a diagnostic.
185
186 The layout is determined within the constructor.
187 It is then printed by repeatedly calling the "print_source_line",
188 "print_annotation_line" and "print_any_fixits" methods.
189
190 We assume we have disjoint ranges. */
191
192 class layout
193 {
194 public:
195 layout (diagnostic_context *context,
196 rich_location *richloc,
197 diagnostic_t diagnostic_kind);
198
199 int get_num_line_spans () const { return m_line_spans.length (); }
200 const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
201
202 bool print_heading_for_line_span_index_p (int line_span_idx) const;
203
204 expanded_location get_expanded_location (const line_span *) const;
205
206 bool print_source_line (int row, line_bounds *lbounds_out);
207 bool should_print_annotation_line_p (int row) const;
208 void print_annotation_line (int row, const line_bounds lbounds);
209 bool annotation_line_showed_range_p (int line, int start_column,
210 int finish_column) const;
211 void print_any_fixits (int row);
212
213 void show_ruler (int max_column) const;
214
215 private:
216 bool validate_fixit_hint_p (const fixit_hint *hint);
217
218 void calculate_line_spans ();
219
220 void print_newline ();
221
222 bool
223 get_state_at_point (/* Inputs. */
224 int row, int column,
225 int first_non_ws, int last_non_ws,
226 /* Outputs. */
227 point_state *out_state);
228
229 int
230 get_x_bound_for_row (int row, int caret_column,
231 int last_non_ws);
232
233 void
234 move_to_column (int *column, int dest_column);
235
236 private:
237 diagnostic_context *m_context;
238 pretty_printer *m_pp;
239 diagnostic_t m_diagnostic_kind;
240 expanded_location m_exploc;
241 colorizer m_colorizer;
242 bool m_colorize_source_p;
243 auto_vec <layout_range> m_layout_ranges;
244 auto_vec <const fixit_hint *> m_fixit_hints;
245 auto_vec <line_span> m_line_spans;
246 int m_x_offset;
247 };
248
249 /* Implementation of "class colorizer". */
250
251 /* The constructor for "colorizer". Lookup and store color codes for the
252 different kinds of things we might need to print. */
253
254 colorizer::colorizer (diagnostic_context *context,
255 diagnostic_t diagnostic_kind) :
256 m_context (context),
257 m_diagnostic_kind (diagnostic_kind),
258 m_current_state (STATE_NORMAL_TEXT)
259 {
260 m_range1 = get_color_by_name ("range1");
261 m_range2 = get_color_by_name ("range2");
262 m_fixit_insert = get_color_by_name ("fixit-insert");
263 m_fixit_delete = get_color_by_name ("fixit-delete");
264 m_stop_color = colorize_stop (pp_show_color (context->printer));
265 }
266
267 /* The destructor for "colorize". If colorization is on, print a code to
268 turn it off. */
269
270 colorizer::~colorizer ()
271 {
272 finish_state (m_current_state);
273 }
274
275 /* Update state, printing color codes if necessary if there's a state
276 change. */
277
278 void
279 colorizer::set_state (int new_state)
280 {
281 if (m_current_state != new_state)
282 {
283 finish_state (m_current_state);
284 m_current_state = new_state;
285 begin_state (new_state);
286 }
287 }
288
289 /* Turn on any colorization for STATE. */
290
291 void
292 colorizer::begin_state (int state)
293 {
294 switch (state)
295 {
296 case STATE_NORMAL_TEXT:
297 break;
298
299 case STATE_FIXIT_INSERT:
300 pp_string (m_context->printer, m_fixit_insert);
301 break;
302
303 case STATE_FIXIT_DELETE:
304 pp_string (m_context->printer, m_fixit_delete);
305 break;
306
307 case 0:
308 /* Make range 0 be the same color as the "kind" text
309 (error vs warning vs note). */
310 pp_string
311 (m_context->printer,
312 colorize_start (pp_show_color (m_context->printer),
313 diagnostic_get_color_for_kind (m_diagnostic_kind)));
314 break;
315
316 case 1:
317 pp_string (m_context->printer, m_range1);
318 break;
319
320 case 2:
321 pp_string (m_context->printer, m_range2);
322 break;
323
324 default:
325 /* For ranges beyond 2, alternate between color 1 and color 2. */
326 {
327 gcc_assert (state > 2);
328 pp_string (m_context->printer,
329 state % 2 ? m_range1 : m_range2);
330 }
331 break;
332 }
333 }
334
335 /* Turn off any colorization for STATE. */
336
337 void
338 colorizer::finish_state (int state)
339 {
340 if (state != STATE_NORMAL_TEXT)
341 pp_string (m_context->printer, m_stop_color);
342 }
343
344 /* Get the color code for NAME (or the empty string if
345 colorization is disabled). */
346
347 const char *
348 colorizer::get_color_by_name (const char *name)
349 {
350 return colorize_start (pp_show_color (m_context->printer), name);
351 }
352
353 /* Implementation of class layout_range. */
354
355 /* The constructor for class layout_range.
356 Initialize various layout_point fields from expanded_location
357 equivalents; we've already filtered on file. */
358
359 layout_range::layout_range (const expanded_location *start_exploc,
360 const expanded_location *finish_exploc,
361 bool show_caret_p,
362 const expanded_location *caret_exploc)
363 : m_start (*start_exploc),
364 m_finish (*finish_exploc),
365 m_show_caret_p (show_caret_p),
366 m_caret (*caret_exploc)
367 {
368 }
369
370 /* Is (column, row) within the given range?
371 We've already filtered on the file.
372
373 Ranges are closed (both limits are within the range).
374
375 Example A: a single-line range:
376 start: (col=22, line=2)
377 finish: (col=38, line=2)
378
379 |00000011111111112222222222333333333344444444444
380 |34567890123456789012345678901234567890123456789
381 --+-----------------------------------------------
382 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
383 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
384 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
385
386 Example B: a multiline range with
387 start: (col=14, line=3)
388 finish: (col=08, line=5)
389
390 |00000011111111112222222222333333333344444444444
391 |34567890123456789012345678901234567890123456789
392 --+-----------------------------------------------
393 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
394 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
395 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
396 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
397 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
398 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
399 --+-----------------------------------------------
400
401 Legend:
402 - 'b' indicates a point *before* the range
403 - 'S' indicates the start of the range
404 - 'w' indicates a point within the range
405 - 'F' indicates the finish of the range (which is
406 within it).
407 - 'a' indicates a subsequent point *after* the range. */
408
409 bool
410 layout_range::contains_point (int row, int column) const
411 {
412 gcc_assert (m_start.m_line <= m_finish.m_line);
413 /* ...but the equivalent isn't true for the columns;
414 consider example B in the comment above. */
415
416 if (row < m_start.m_line)
417 /* Points before the first line of the range are
418 outside it (corresponding to line 01 in example A
419 and lines 01 and 02 in example B above). */
420 return false;
421
422 if (row == m_start.m_line)
423 /* On same line as start of range (corresponding
424 to line 02 in example A and line 03 in example B). */
425 {
426 if (column < m_start.m_column)
427 /* Points on the starting line of the range, but
428 before the column in which it begins. */
429 return false;
430
431 if (row < m_finish.m_line)
432 /* This is a multiline range; the point
433 is within it (corresponds to line 03 in example B
434 from column 14 onwards) */
435 return true;
436 else
437 {
438 /* This is a single-line range. */
439 gcc_assert (row == m_finish.m_line);
440 return column <= m_finish.m_column;
441 }
442 }
443
444 /* The point is in a line beyond that containing the
445 start of the range: lines 03 onwards in example A,
446 and lines 04 onwards in example B. */
447 gcc_assert (row > m_start.m_line);
448
449 if (row > m_finish.m_line)
450 /* The point is beyond the final line of the range
451 (lines 03 onwards in example A, and lines 06 onwards
452 in example B). */
453 return false;
454
455 if (row < m_finish.m_line)
456 {
457 /* The point is in a line that's fully within a multiline
458 range (e.g. line 04 in example B). */
459 gcc_assert (m_start.m_line < m_finish.m_line);
460 return true;
461 }
462
463 gcc_assert (row == m_finish.m_line);
464
465 return column <= m_finish.m_column;
466 }
467
468 /* Does this layout_range contain any part of line ROW? */
469
470 bool
471 layout_range::intersects_line_p (int row) const
472 {
473 gcc_assert (m_start.m_line <= m_finish.m_line);
474 if (row < m_start.m_line)
475 return false;
476 if (row > m_finish.m_line)
477 return false;
478 return true;
479 }
480
481 #if CHECKING_P
482
483 /* A helper function for testing layout_range. */
484
485 static layout_range
486 make_range (int start_line, int start_col, int end_line, int end_col)
487 {
488 const expanded_location start_exploc
489 = {"test.c", start_line, start_col, NULL, false};
490 const expanded_location finish_exploc
491 = {"test.c", end_line, end_col, NULL, false};
492 return layout_range (&start_exploc, &finish_exploc, false,
493 &start_exploc);
494 }
495
496 /* Selftests for layout_range::contains_point and
497 layout_range::intersects_line_p. */
498
499 /* Selftest for layout_range, where the layout_range
500 is a range with start==end i.e. a single point. */
501
502 static void
503 test_layout_range_for_single_point ()
504 {
505 layout_range point = make_range (7, 10, 7, 10);
506
507 /* Tests for layout_range::contains_point. */
508
509 /* Before the line. */
510 ASSERT_FALSE (point.contains_point (6, 1));
511
512 /* On the line, but before start. */
513 ASSERT_FALSE (point.contains_point (7, 9));
514
515 /* At the point. */
516 ASSERT_TRUE (point.contains_point (7, 10));
517
518 /* On the line, after the point. */
519 ASSERT_FALSE (point.contains_point (7, 11));
520
521 /* After the line. */
522 ASSERT_FALSE (point.contains_point (8, 1));
523
524 /* Tests for layout_range::intersects_line_p. */
525 ASSERT_FALSE (point.intersects_line_p (6));
526 ASSERT_TRUE (point.intersects_line_p (7));
527 ASSERT_FALSE (point.intersects_line_p (8));
528 }
529
530 /* Selftest for layout_range, where the layout_range
531 is the single-line range shown as "Example A" above. */
532
533 static void
534 test_layout_range_for_single_line ()
535 {
536 layout_range example_a = make_range (2, 22, 2, 38);
537
538 /* Tests for layout_range::contains_point. */
539
540 /* Before the line. */
541 ASSERT_FALSE (example_a.contains_point (1, 1));
542
543 /* On the line, but before start. */
544 ASSERT_FALSE (example_a.contains_point (2, 21));
545
546 /* On the line, at the start. */
547 ASSERT_TRUE (example_a.contains_point (2, 22));
548
549 /* On the line, within the range. */
550 ASSERT_TRUE (example_a.contains_point (2, 23));
551
552 /* On the line, at the end. */
553 ASSERT_TRUE (example_a.contains_point (2, 38));
554
555 /* On the line, after the end. */
556 ASSERT_FALSE (example_a.contains_point (2, 39));
557
558 /* After the line. */
559 ASSERT_FALSE (example_a.contains_point (2, 39));
560
561 /* Tests for layout_range::intersects_line_p. */
562 ASSERT_FALSE (example_a.intersects_line_p (1));
563 ASSERT_TRUE (example_a.intersects_line_p (2));
564 ASSERT_FALSE (example_a.intersects_line_p (3));
565 }
566
567 /* Selftest for layout_range, where the layout_range
568 is the multi-line range shown as "Example B" above. */
569
570 static void
571 test_layout_range_for_multiple_lines ()
572 {
573 layout_range example_b = make_range (3, 14, 5, 8);
574
575 /* Tests for layout_range::contains_point. */
576
577 /* Before first line. */
578 ASSERT_FALSE (example_b.contains_point (1, 1));
579
580 /* On the first line, but before start. */
581 ASSERT_FALSE (example_b.contains_point (3, 13));
582
583 /* At the start. */
584 ASSERT_TRUE (example_b.contains_point (3, 14));
585
586 /* On the first line, within the range. */
587 ASSERT_TRUE (example_b.contains_point (3, 15));
588
589 /* On an interior line.
590 The column number should not matter; try various boundary
591 values. */
592 ASSERT_TRUE (example_b.contains_point (4, 1));
593 ASSERT_TRUE (example_b.contains_point (4, 7));
594 ASSERT_TRUE (example_b.contains_point (4, 8));
595 ASSERT_TRUE (example_b.contains_point (4, 9));
596 ASSERT_TRUE (example_b.contains_point (4, 13));
597 ASSERT_TRUE (example_b.contains_point (4, 14));
598 ASSERT_TRUE (example_b.contains_point (4, 15));
599
600 /* On the final line, before the end. */
601 ASSERT_TRUE (example_b.contains_point (5, 7));
602
603 /* On the final line, at the end. */
604 ASSERT_TRUE (example_b.contains_point (5, 8));
605
606 /* On the final line, after the end. */
607 ASSERT_FALSE (example_b.contains_point (5, 9));
608
609 /* After the line. */
610 ASSERT_FALSE (example_b.contains_point (6, 1));
611
612 /* Tests for layout_range::intersects_line_p. */
613 ASSERT_FALSE (example_b.intersects_line_p (2));
614 ASSERT_TRUE (example_b.intersects_line_p (3));
615 ASSERT_TRUE (example_b.intersects_line_p (4));
616 ASSERT_TRUE (example_b.intersects_line_p (5));
617 ASSERT_FALSE (example_b.intersects_line_p (6));
618 }
619
620 #endif /* #if CHECKING_P */
621
622 /* Given a source line LINE of length LINE_WIDTH, determine the width
623 without any trailing whitespace. */
624
625 static int
626 get_line_width_without_trailing_whitespace (const char *line, int line_width)
627 {
628 int result = line_width;
629 while (result > 0)
630 {
631 char ch = line[result - 1];
632 if (ch == ' ' || ch == '\t')
633 result--;
634 else
635 break;
636 }
637 gcc_assert (result >= 0);
638 gcc_assert (result <= line_width);
639 gcc_assert (result == 0 ||
640 (line[result - 1] != ' '
641 && line[result -1] != '\t'));
642 return result;
643 }
644
645 #if CHECKING_P
646
647 /* A helper function for testing get_line_width_without_trailing_whitespace. */
648
649 static void
650 assert_eq (const char *line, int expected_width)
651 {
652 int actual_value
653 = get_line_width_without_trailing_whitespace (line, strlen (line));
654 ASSERT_EQ (actual_value, expected_width);
655 }
656
657 /* Verify that get_line_width_without_trailing_whitespace is sane for
658 various inputs. It is not required to handle newlines. */
659
660 static void
661 test_get_line_width_without_trailing_whitespace ()
662 {
663 assert_eq ("", 0);
664 assert_eq (" ", 0);
665 assert_eq ("\t", 0);
666 assert_eq ("hello world", 11);
667 assert_eq ("hello world ", 11);
668 assert_eq ("hello world \t\t ", 11);
669 }
670
671 #endif /* #if CHECKING_P */
672
673 /* Helper function for layout's ctor, for sanitizing locations relative
674 to the primary location within a diagnostic.
675
676 Compare LOC_A and LOC_B to see if it makes sense to print underlines
677 connecting their expanded locations. Doing so is only guaranteed to
678 make sense if the locations share the same macro expansion "history"
679 i.e. they can be traced through the same macro expansions, eventually
680 reaching an ordinary map.
681
682 This may be too strong a condition, but it effectively sanitizes
683 PR c++/70105, which has an example of printing an expression where the
684 final location of the expression is in a different macro, which
685 erroneously was leading to hundreds of lines of irrelevant source
686 being printed. */
687
688 static bool
689 compatible_locations_p (location_t loc_a, location_t loc_b)
690 {
691 if (IS_ADHOC_LOC (loc_a))
692 loc_a = get_location_from_adhoc_loc (line_table, loc_a);
693 if (IS_ADHOC_LOC (loc_b))
694 loc_b = get_location_from_adhoc_loc (line_table, loc_b);
695
696 /* If either location is one of the special locations outside of a
697 linemap, they are only compatible if they are equal. */
698 if (loc_a < RESERVED_LOCATION_COUNT
699 || loc_b < RESERVED_LOCATION_COUNT)
700 return loc_a == loc_b;
701
702 const line_map *map_a = linemap_lookup (line_table, loc_a);
703 linemap_assert (map_a);
704
705 const line_map *map_b = linemap_lookup (line_table, loc_b);
706 linemap_assert (map_b);
707
708 /* Are they within the same map? */
709 if (map_a == map_b)
710 {
711 /* Are both within the same macro expansion? */
712 if (linemap_macro_expansion_map_p (map_a))
713 {
714 /* Expand each location towards the spelling location, and
715 recurse. */
716 const line_map_macro *macro_map = linemap_check_macro (map_a);
717 source_location loc_a_toward_spelling
718 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
719 macro_map,
720 loc_a);
721 source_location loc_b_toward_spelling
722 = linemap_macro_map_loc_unwind_toward_spelling (line_table,
723 macro_map,
724 loc_b);
725 return compatible_locations_p (loc_a_toward_spelling,
726 loc_b_toward_spelling);
727 }
728
729 /* Otherwise they are within the same ordinary map. */
730 return true;
731 }
732 else
733 {
734 /* Within different maps. */
735
736 /* If either is within a macro expansion, they are incompatible. */
737 if (linemap_macro_expansion_map_p (map_a)
738 || linemap_macro_expansion_map_p (map_b))
739 return false;
740
741 /* Within two different ordinary maps; they are compatible iff they
742 are in the same file. */
743 const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
744 const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
745 return ord_map_a->to_file == ord_map_b->to_file;
746 }
747 }
748
749 /* Implementation of class layout. */
750
751 /* Constructor for class layout.
752
753 Filter the ranges from the rich_location to those that we can
754 sanely print, populating m_layout_ranges and m_fixit_hints.
755 Determine the range of lines that we will print, splitting them
756 up into an ordered list of disjoint spans of contiguous line numbers.
757 Determine m_x_offset, to ensure that the primary caret
758 will fit within the max_width provided by the diagnostic_context. */
759
760 layout::layout (diagnostic_context * context,
761 rich_location *richloc,
762 diagnostic_t diagnostic_kind)
763 : m_context (context),
764 m_pp (context->printer),
765 m_diagnostic_kind (diagnostic_kind),
766 m_exploc (richloc->get_expanded_location (0)),
767 m_colorizer (context, diagnostic_kind),
768 m_colorize_source_p (context->colorize_source_p),
769 m_layout_ranges (richloc->get_num_locations ()),
770 m_fixit_hints (richloc->get_num_fixit_hints ()),
771 m_line_spans (1 + richloc->get_num_locations ()),
772 m_x_offset (0)
773 {
774 source_location primary_loc = richloc->get_range (0)->m_loc;
775
776 for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
777 {
778 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
779 Ignore any ranges that are awkward to handle. */
780 const location_range *loc_range = richloc->get_range (idx);
781
782 /* Split the "range" into caret and range information. */
783 source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
784
785 /* Expand the various locations. */
786 expanded_location start
787 = linemap_client_expand_location_to_spelling_point (src_range.m_start);
788 expanded_location finish
789 = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
790 expanded_location caret
791 = linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
792
793 /* If any part of the range isn't in the same file as the primary
794 location of this diagnostic, ignore the range. */
795 if (start.file != m_exploc.file)
796 continue;
797 if (finish.file != m_exploc.file)
798 continue;
799 if (loc_range->m_show_caret_p)
800 if (caret.file != m_exploc.file)
801 continue;
802
803 /* Sanitize the caret location for non-primary ranges. */
804 if (m_layout_ranges.length () > 0)
805 if (loc_range->m_show_caret_p)
806 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
807 /* Discard any non-primary ranges that can't be printed
808 sanely relative to the primary location. */
809 continue;
810
811 /* Everything is now known to be in the correct source file,
812 but it may require further sanitization. */
813 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
814
815 /* If we have a range that finishes before it starts (perhaps
816 from something built via macro expansion), printing the
817 range is likely to be nonsensical. Also, attempting to do so
818 breaks assumptions within the printing code (PR c/68473).
819 Similarly, don't attempt to print ranges if one or both ends
820 of the range aren't sane to print relative to the
821 primary location (PR c++/70105). */
822 if (start.line > finish.line
823 || !compatible_locations_p (src_range.m_start, primary_loc)
824 || !compatible_locations_p (src_range.m_finish, primary_loc))
825 {
826 /* Is this the primary location? */
827 if (m_layout_ranges.length () == 0)
828 {
829 /* We want to print the caret for the primary location, but
830 we must sanitize away m_start and m_finish. */
831 ri.m_start = ri.m_caret;
832 ri.m_finish = ri.m_caret;
833 }
834 else
835 /* This is a non-primary range; ignore it. */
836 continue;
837 }
838
839 /* Passed all the tests; add the range to m_layout_ranges so that
840 it will be printed. */
841 m_layout_ranges.safe_push (ri);
842 }
843
844 /* Populate m_fixit_hints, filtering to only those that are in the
845 same file. */
846 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
847 {
848 const fixit_hint *hint = richloc->get_fixit_hint (i);
849 if (validate_fixit_hint_p (hint))
850 m_fixit_hints.safe_push (hint);
851 }
852
853 /* Populate m_line_spans. */
854 calculate_line_spans ();
855
856 /* Adjust m_x_offset.
857 Center the primary caret to fit in max_width; all columns
858 will be adjusted accordingly. */
859 int max_width = m_context->caret_max_width;
860 int line_width;
861 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
862 &line_width);
863 if (line && m_exploc.column <= line_width)
864 {
865 int right_margin = CARET_LINE_MARGIN;
866 int column = m_exploc.column;
867 right_margin = MIN (line_width - column, right_margin);
868 right_margin = max_width - right_margin;
869 if (line_width >= max_width && column > right_margin)
870 m_x_offset = column - right_margin;
871 gcc_assert (m_x_offset >= 0);
872 }
873
874 if (context->show_ruler_p)
875 show_ruler (m_x_offset + max_width);
876 }
877
878 /* Return true iff we should print a heading when starting the
879 line span with the given index. */
880
881 bool
882 layout::print_heading_for_line_span_index_p (int line_span_idx) const
883 {
884 /* We print a heading for every change of line span, hence for every
885 line span after the initial one. */
886 if (line_span_idx > 0)
887 return true;
888
889 /* We also do it for the initial span if the primary location of the
890 diagnostic is in a different span. */
891 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
892 return true;
893
894 return false;
895 }
896
897 /* Get an expanded_location for the first location of interest within
898 the given line_span.
899 Used when printing a heading to indicate a new line span. */
900
901 expanded_location
902 layout::get_expanded_location (const line_span *line_span) const
903 {
904 /* Whenever possible, use the caret location. */
905 if (line_span->contains_line_p (m_exploc.line))
906 return m_exploc;
907
908 /* Otherwise, use the start of the first range that's present
909 within the line_span. */
910 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
911 {
912 const layout_range *lr = &m_layout_ranges[i];
913 if (line_span->contains_line_p (lr->m_start.m_line))
914 {
915 expanded_location exploc = m_exploc;
916 exploc.line = lr->m_start.m_line;
917 exploc.column = lr->m_start.m_column;
918 return exploc;
919 }
920 }
921
922 /* Otherwise, use the location of the first fixit-hint present within
923 the line_span. */
924 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
925 {
926 const fixit_hint *hint = m_fixit_hints[i];
927 location_t loc = hint->get_start_loc ();
928 expanded_location exploc = expand_location (loc);
929 if (line_span->contains_line_p (exploc.line))
930 return exploc;
931 }
932
933 /* It should not be possible to have a line span that didn't
934 contain any of the layout_range or fixit_hint instances. */
935 gcc_unreachable ();
936 return m_exploc;
937 }
938
939 /* Determine if HINT is meaningful to print within this layout. */
940
941 bool
942 layout::validate_fixit_hint_p (const fixit_hint *hint)
943 {
944 switch (hint->get_kind ())
945 {
946 case fixit_hint::INSERT:
947 {
948 const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
949 location_t loc = insert->get_location ();
950 if (LOCATION_FILE (loc) != m_exploc.file)
951 return false;
952 }
953 break;
954
955 case fixit_hint::REPLACE:
956 {
957 const fixit_replace *replace
958 = static_cast <const fixit_replace *> (hint);
959 source_range src_range = replace->get_range ();
960 if (LOCATION_FILE (src_range.m_start) != m_exploc.file)
961 return false;
962 if (LOCATION_FILE (src_range.m_finish) != m_exploc.file)
963 return false;
964 }
965 break;
966
967 default:
968 gcc_unreachable ();
969 }
970
971 return true;
972 }
973
974 /* Determine the range of lines affected by HINT.
975 This assumes that HINT has already been filtered by
976 validate_fixit_hint_p, and so affects the correct source file. */
977
978 static line_span
979 get_line_span_for_fixit_hint (const fixit_hint *hint)
980 {
981 gcc_assert (hint);
982 switch (hint->get_kind ())
983 {
984 case fixit_hint::INSERT:
985 {
986 const fixit_insert *insert = static_cast <const fixit_insert *> (hint);
987 location_t loc = insert->get_location ();
988 int line = LOCATION_LINE (loc);
989 return line_span (line, line);
990 }
991 break;
992
993 case fixit_hint::REPLACE:
994 {
995 const fixit_replace *replace
996 = static_cast <const fixit_replace *> (hint);
997 source_range src_range = replace->get_range ();
998 return line_span (LOCATION_LINE (src_range.m_start),
999 LOCATION_LINE (src_range.m_finish));
1000 }
1001 break;
1002
1003 default:
1004 gcc_unreachable ();
1005 }
1006 }
1007
1008 /* We want to print the pertinent source code at a diagnostic. The
1009 rich_location can contain multiple locations. This will have been
1010 filtered into m_exploc (the caret for the primary location) and
1011 m_layout_ranges, for those ranges within the same source file.
1012
1013 We will print a subset of the lines within the source file in question,
1014 as a collection of "spans" of lines.
1015
1016 This function populates m_line_spans with an ordered, disjoint list of
1017 the line spans of interest.
1018
1019 For example, if the primary caret location is on line 7, with ranges
1020 covering lines 5-6 and lines 9-12:
1021
1022 004
1023 005 |RANGE 0
1024 006 |RANGE 0
1025 007 |PRIMARY CARET
1026 008
1027 009 |RANGE 1
1028 010 |RANGE 1
1029 011 |RANGE 1
1030 012 |RANGE 1
1031 013
1032
1033 then we want two spans: lines 5-7 and lines 9-12. */
1034
1035 void
1036 layout::calculate_line_spans ()
1037 {
1038 /* This should only be called once, by the ctor. */
1039 gcc_assert (m_line_spans.length () == 0);
1040
1041 /* Populate tmp_spans with individual spans, for each of
1042 m_exploc, and for m_layout_ranges. */
1043 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1044 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1045 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1046 {
1047 const layout_range *lr = &m_layout_ranges[i];
1048 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1049 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1050 lr->m_finish.m_line));
1051 }
1052
1053 /* Also add spans for any fix-it hints, in case they cover other lines. */
1054 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1055 {
1056 const fixit_hint *hint = m_fixit_hints[i];
1057 gcc_assert (hint);
1058 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1059 }
1060
1061 /* Sort them. */
1062 tmp_spans.qsort(line_span::comparator);
1063
1064 /* Now iterate through tmp_spans, copying into m_line_spans, and
1065 combining where possible. */
1066 gcc_assert (tmp_spans.length () > 0);
1067 m_line_spans.safe_push (tmp_spans[0]);
1068 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1069 {
1070 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1071 const line_span *next = &tmp_spans[i];
1072 gcc_assert (next->m_first_line >= current->m_first_line);
1073 if (next->m_first_line <= current->m_last_line + 1)
1074 {
1075 /* We can merge them. */
1076 if (next->m_last_line > current->m_last_line)
1077 current->m_last_line = next->m_last_line;
1078 }
1079 else
1080 {
1081 /* No merger possible. */
1082 m_line_spans.safe_push (*next);
1083 }
1084 }
1085
1086 /* Verify the result, in m_line_spans. */
1087 gcc_assert (m_line_spans.length () > 0);
1088 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1089 {
1090 const line_span *prev = &m_line_spans[i - 1];
1091 const line_span *next = &m_line_spans[i];
1092 /* The individual spans must be sane. */
1093 gcc_assert (prev->m_first_line <= prev->m_last_line);
1094 gcc_assert (next->m_first_line <= next->m_last_line);
1095 /* The spans must be ordered. */
1096 gcc_assert (prev->m_first_line < next->m_first_line);
1097 /* There must be a gap of at least one line between separate spans. */
1098 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1099 }
1100 }
1101
1102 /* Attempt to print line ROW of source code, potentially colorized at any
1103 ranges.
1104 Return true if the line was printed, populating *LBOUNDS_OUT.
1105 Return false if the source line could not be read, leaving *LBOUNDS_OUT
1106 untouched. */
1107
1108 bool
1109 layout::print_source_line (int row, line_bounds *lbounds_out)
1110 {
1111 int line_width;
1112 const char *line = location_get_source_line (m_exploc.file, row,
1113 &line_width);
1114 if (!line)
1115 return false;
1116
1117 m_colorizer.set_normal_text ();
1118
1119 /* We will stop printing the source line at any trailing
1120 whitespace. */
1121 line_width = get_line_width_without_trailing_whitespace (line,
1122 line_width);
1123 line += m_x_offset;
1124
1125 pp_space (m_pp);
1126 int first_non_ws = INT_MAX;
1127 int last_non_ws = 0;
1128 int column;
1129 for (column = 1 + m_x_offset; column <= line_width; column++)
1130 {
1131 /* Assuming colorization is enabled for the caret and underline
1132 characters, we may also colorize the associated characters
1133 within the source line.
1134
1135 For frontends that generate range information, we color the
1136 associated characters in the source line the same as the
1137 carets and underlines in the annotation line, to make it easier
1138 for the reader to see the pertinent code.
1139
1140 For frontends that only generate carets, we don't colorize the
1141 characters above them, since this would look strange (e.g.
1142 colorizing just the first character in a token). */
1143 if (m_colorize_source_p)
1144 {
1145 bool in_range_p;
1146 point_state state;
1147 in_range_p = get_state_at_point (row, column,
1148 0, INT_MAX,
1149 &state);
1150 if (in_range_p)
1151 m_colorizer.set_range (state.range_idx);
1152 else
1153 m_colorizer.set_normal_text ();
1154 }
1155 char c = *line == '\t' ? ' ' : *line;
1156 if (c == '\0')
1157 c = ' ';
1158 if (c != ' ')
1159 {
1160 last_non_ws = column;
1161 if (first_non_ws == INT_MAX)
1162 first_non_ws = column;
1163 }
1164 pp_character (m_pp, c);
1165 line++;
1166 }
1167 print_newline ();
1168
1169 lbounds_out->m_first_non_ws = first_non_ws;
1170 lbounds_out->m_last_non_ws = last_non_ws;
1171 return true;
1172 }
1173
1174 /* Determine if we should print an annotation line for ROW.
1175 i.e. if any of m_layout_ranges contains ROW. */
1176
1177 bool
1178 layout::should_print_annotation_line_p (int row) const
1179 {
1180 layout_range *range;
1181 int i;
1182 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1183 if (range->intersects_line_p (row))
1184 return true;
1185 return false;
1186 }
1187
1188 /* Print a line consisting of the caret/underlines for the given
1189 source line. */
1190
1191 void
1192 layout::print_annotation_line (int row, const line_bounds lbounds)
1193 {
1194 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1195 lbounds.m_last_non_ws);
1196
1197 pp_space (m_pp);
1198 for (int column = 1 + m_x_offset; column < x_bound; column++)
1199 {
1200 bool in_range_p;
1201 point_state state;
1202 in_range_p = get_state_at_point (row, column,
1203 lbounds.m_first_non_ws,
1204 lbounds.m_last_non_ws,
1205 &state);
1206 if (in_range_p)
1207 {
1208 /* Within a range. Draw either the caret or an underline. */
1209 m_colorizer.set_range (state.range_idx);
1210 if (state.draw_caret_p)
1211 {
1212 /* Draw the caret. */
1213 char caret_char;
1214 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1215 caret_char = m_context->caret_chars[state.range_idx];
1216 else
1217 caret_char = '^';
1218 pp_character (m_pp, caret_char);
1219 }
1220 else
1221 pp_character (m_pp, '~');
1222 }
1223 else
1224 {
1225 /* Not in a range. */
1226 m_colorizer.set_normal_text ();
1227 pp_character (m_pp, ' ');
1228 }
1229 }
1230 print_newline ();
1231 }
1232
1233 /* Subroutine of layout::print_any_fixits.
1234
1235 Determine if the annotation line printed for LINE contained
1236 the exact range from START_COLUMN to FINISH_COLUMN. */
1237
1238 bool
1239 layout::annotation_line_showed_range_p (int line, int start_column,
1240 int finish_column) const
1241 {
1242 layout_range *range;
1243 int i;
1244 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1245 if (range->m_start.m_line == line
1246 && range->m_start.m_column == start_column
1247 && range->m_finish.m_line == line
1248 && range->m_finish.m_column == finish_column)
1249 return true;
1250 return false;
1251 }
1252
1253 /* If there are any fixit hints on source line ROW, print them.
1254 They are printed in order, attempting to combine them onto lines, but
1255 starting new lines if necessary. */
1256
1257 void
1258 layout::print_any_fixits (int row)
1259 {
1260 int column = 0;
1261 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1262 {
1263 const fixit_hint *hint = m_fixit_hints[i];
1264 if (hint->affects_line_p (m_exploc.file, row))
1265 {
1266 /* For now we assume each fixit hint can only touch one line. */
1267 switch (hint->get_kind ())
1268 {
1269 case fixit_hint::INSERT:
1270 {
1271 const fixit_insert *insert
1272 = static_cast <const fixit_insert *> (hint);
1273 /* This assumes the insertion just affects one line. */
1274 int start_column
1275 = LOCATION_COLUMN (insert->get_location ());
1276 move_to_column (&column, start_column);
1277 m_colorizer.set_fixit_insert ();
1278 pp_string (m_pp, insert->get_string ());
1279 m_colorizer.set_normal_text ();
1280 column += insert->get_length ();
1281 }
1282 break;
1283
1284 case fixit_hint::REPLACE:
1285 {
1286 const fixit_replace *replace
1287 = static_cast <const fixit_replace *> (hint);
1288 source_range src_range = replace->get_range ();
1289 int line = LOCATION_LINE (src_range.m_start);
1290 int start_column = LOCATION_COLUMN (src_range.m_start);
1291 int finish_column = LOCATION_COLUMN (src_range.m_finish);
1292
1293 /* If the range of the replacement wasn't printed in the
1294 annotation line, then print an extra underline to
1295 indicate exactly what is being replaced.
1296 Always show it for removals. */
1297 if (!annotation_line_showed_range_p (line, start_column,
1298 finish_column)
1299 || replace->get_length () == 0)
1300 {
1301 move_to_column (&column, start_column);
1302 m_colorizer.set_fixit_delete ();
1303 for (; column <= finish_column; column++)
1304 pp_character (m_pp, '-');
1305 m_colorizer.set_normal_text ();
1306 }
1307 /* Print the replacement text. REPLACE also covers
1308 removals, so only do this extra work (potentially starting
1309 a new line) if we have actual replacement text. */
1310 if (replace->get_length () > 0)
1311 {
1312 move_to_column (&column, start_column);
1313 m_colorizer.set_fixit_insert ();
1314 pp_string (m_pp, replace->get_string ());
1315 m_colorizer.set_normal_text ();
1316 column += replace->get_length ();
1317 }
1318 }
1319 break;
1320
1321 default:
1322 gcc_unreachable ();
1323 }
1324 }
1325 }
1326
1327 /* Add a trailing newline, if necessary. */
1328 move_to_column (&column, 0);
1329 }
1330
1331 /* Disable any colorization and emit a newline. */
1332
1333 void
1334 layout::print_newline ()
1335 {
1336 m_colorizer.set_normal_text ();
1337 pp_newline (m_pp);
1338 }
1339
1340 /* Return true if (ROW/COLUMN) is within a range of the layout.
1341 If it returns true, OUT_STATE is written to, with the
1342 range index, and whether we should draw the caret at
1343 (ROW/COLUMN) (as opposed to an underline). */
1344
1345 bool
1346 layout::get_state_at_point (/* Inputs. */
1347 int row, int column,
1348 int first_non_ws, int last_non_ws,
1349 /* Outputs. */
1350 point_state *out_state)
1351 {
1352 layout_range *range;
1353 int i;
1354 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1355 {
1356 if (range->contains_point (row, column))
1357 {
1358 out_state->range_idx = i;
1359
1360 /* Are we at the range's caret? is it visible? */
1361 out_state->draw_caret_p = false;
1362 if (range->m_show_caret_p
1363 && row == range->m_caret.m_line
1364 && column == range->m_caret.m_column)
1365 out_state->draw_caret_p = true;
1366
1367 /* Within a multiline range, don't display any underline
1368 in any leading or trailing whitespace on a line.
1369 We do display carets, however. */
1370 if (!out_state->draw_caret_p)
1371 if (column < first_non_ws || column > last_non_ws)
1372 return false;
1373
1374 /* We are within a range. */
1375 return true;
1376 }
1377 }
1378
1379 return false;
1380 }
1381
1382 /* Helper function for use by layout::print_line when printing the
1383 annotation line under the source line.
1384 Get the column beyond the rightmost one that could contain a caret or
1385 range marker, given that we stop rendering at trailing whitespace.
1386 ROW is the source line within the given file.
1387 CARET_COLUMN is the column of range 0's caret.
1388 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1389 character of source (as determined when printing the source line). */
1390
1391 int
1392 layout::get_x_bound_for_row (int row, int caret_column,
1393 int last_non_ws_column)
1394 {
1395 int result = caret_column + 1;
1396
1397 layout_range *range;
1398 int i;
1399 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1400 {
1401 if (row >= range->m_start.m_line)
1402 {
1403 if (range->m_finish.m_line == row)
1404 {
1405 /* On the final line within a range; ensure that
1406 we render up to the end of the range. */
1407 if (result <= range->m_finish.m_column)
1408 result = range->m_finish.m_column + 1;
1409 }
1410 else if (row < range->m_finish.m_line)
1411 {
1412 /* Within a multiline range; ensure that we render up to the
1413 last non-whitespace column. */
1414 if (result <= last_non_ws_column)
1415 result = last_non_ws_column + 1;
1416 }
1417 }
1418 }
1419
1420 return result;
1421 }
1422
1423 /* Given *COLUMN as an x-coordinate, print spaces to position
1424 successive output at DEST_COLUMN, printing a newline if necessary,
1425 and updating *COLUMN. */
1426
1427 void
1428 layout::move_to_column (int *column, int dest_column)
1429 {
1430 /* Start a new line if we need to. */
1431 if (*column > dest_column)
1432 {
1433 print_newline ();
1434 *column = 0;
1435 }
1436
1437 while (*column < dest_column)
1438 {
1439 pp_space (m_pp);
1440 (*column)++;
1441 }
1442 }
1443
1444 /* For debugging layout issues, render a ruler giving column numbers
1445 (after the 1-column indent). */
1446
1447 void
1448 layout::show_ruler (int max_column) const
1449 {
1450 /* Hundreds. */
1451 if (max_column > 99)
1452 {
1453 pp_space (m_pp);
1454 for (int column = 1 + m_x_offset; column <= max_column; column++)
1455 if (0 == column % 10)
1456 pp_character (m_pp, '0' + (column / 100) % 10);
1457 else
1458 pp_space (m_pp);
1459 pp_newline (m_pp);
1460 }
1461
1462 /* Tens. */
1463 pp_space (m_pp);
1464 for (int column = 1 + m_x_offset; column <= max_column; column++)
1465 if (0 == column % 10)
1466 pp_character (m_pp, '0' + (column / 10) % 10);
1467 else
1468 pp_space (m_pp);
1469 pp_newline (m_pp);
1470
1471 /* Units. */
1472 pp_space (m_pp);
1473 for (int column = 1 + m_x_offset; column <= max_column; column++)
1474 pp_character (m_pp, '0' + (column % 10));
1475 pp_newline (m_pp);
1476 }
1477
1478 } /* End of anonymous namespace. */
1479
1480 /* Print the physical source code corresponding to the location of
1481 this diagnostic, with additional annotations. */
1482
1483 void
1484 diagnostic_show_locus (diagnostic_context * context,
1485 rich_location *richloc,
1486 diagnostic_t diagnostic_kind)
1487 {
1488 pp_newline (context->printer);
1489
1490 location_t loc = richloc->get_loc ();
1491 /* Do nothing if source-printing has been disabled. */
1492 if (!context->show_caret)
1493 return;
1494
1495 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1496 if (loc <= BUILTINS_LOCATION)
1497 return;
1498
1499 /* Don't print the same source location twice in a row, unless we have
1500 fix-it hints. */
1501 if (loc == context->last_location
1502 && richloc->get_num_fixit_hints () == 0)
1503 return;
1504
1505 context->last_location = loc;
1506
1507 const char *saved_prefix = pp_get_prefix (context->printer);
1508 pp_set_prefix (context->printer, NULL);
1509
1510 layout layout (context, richloc, diagnostic_kind);
1511 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1512 line_span_idx++)
1513 {
1514 const line_span *line_span = layout.get_line_span (line_span_idx);
1515 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1516 {
1517 expanded_location exploc = layout.get_expanded_location (line_span);
1518 context->start_span (context, exploc);
1519 }
1520 int last_line = line_span->get_last_line ();
1521 for (int row = line_span->get_first_line (); row <= last_line; row++)
1522 {
1523 /* Print the source line, followed by an annotation line
1524 consisting of any caret/underlines, then any fixits.
1525 If the source line can't be read, print nothing. */
1526 line_bounds lbounds;
1527 if (layout.print_source_line (row, &lbounds))
1528 {
1529 if (layout.should_print_annotation_line_p (row))
1530 layout.print_annotation_line (row, lbounds);
1531 layout.print_any_fixits (row);
1532 }
1533 }
1534 }
1535
1536 pp_set_prefix (context->printer, saved_prefix);
1537 }
1538
1539 #if CHECKING_P
1540
1541 namespace selftest {
1542
1543 /* Selftests for diagnostic_show_locus. */
1544
1545 /* Convenience subclass of diagnostic_context for testing
1546 diagnostic_show_locus. */
1547
1548 class test_diagnostic_context : public diagnostic_context
1549 {
1550 public:
1551 test_diagnostic_context ()
1552 {
1553 diagnostic_initialize (this, 0);
1554 show_caret = true;
1555 show_column = true;
1556 start_span = start_span_cb;
1557 }
1558 ~test_diagnostic_context ()
1559 {
1560 diagnostic_finish (this);
1561 }
1562
1563 /* Implementation of diagnostic_start_span_fn, hiding the
1564 real filename (to avoid printing the names of tempfiles). */
1565 static void
1566 start_span_cb (diagnostic_context *context, expanded_location exploc)
1567 {
1568 exploc.file = "FILENAME";
1569 default_diagnostic_start_span_fn (context, exploc);
1570 }
1571 };
1572
1573 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1574
1575 static void
1576 test_diagnostic_show_locus_unknown_location ()
1577 {
1578 test_diagnostic_context dc;
1579 rich_location richloc (line_table, UNKNOWN_LOCATION);
1580 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1581 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1582 }
1583
1584 /* Verify that diagnostic_show_locus works sanely for various
1585 single-line cases.
1586
1587 All of these work on the following 1-line source file:
1588 .0000000001111111
1589 .1234567890123456
1590 "foo = bar.field;\n"
1591 which is set up by test_diagnostic_show_locus_one_liner and calls
1592 them. */
1593
1594 /* Just a caret. */
1595
1596 static void
1597 test_one_liner_simple_caret ()
1598 {
1599 test_diagnostic_context dc;
1600 location_t caret = linemap_position_for_column (line_table, 10);
1601 rich_location richloc (line_table, caret);
1602 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1603 ASSERT_STREQ ("\n"
1604 " foo = bar.field;\n"
1605 " ^\n",
1606 pp_formatted_text (dc.printer));
1607 }
1608
1609 /* Caret and range. */
1610
1611 static void
1612 test_one_liner_caret_and_range ()
1613 {
1614 test_diagnostic_context dc;
1615 location_t caret = linemap_position_for_column (line_table, 10);
1616 location_t start = linemap_position_for_column (line_table, 7);
1617 location_t finish = linemap_position_for_column (line_table, 15);
1618 location_t loc = make_location (caret, start, finish);
1619 rich_location richloc (line_table, loc);
1620 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1621 ASSERT_STREQ ("\n"
1622 " foo = bar.field;\n"
1623 " ~~~^~~~~~\n",
1624 pp_formatted_text (dc.printer));
1625 }
1626
1627 /* Multiple ranges and carets. */
1628
1629 static void
1630 test_one_liner_multiple_carets_and_ranges ()
1631 {
1632 test_diagnostic_context dc;
1633 location_t foo
1634 = make_location (linemap_position_for_column (line_table, 2),
1635 linemap_position_for_column (line_table, 1),
1636 linemap_position_for_column (line_table, 3));
1637 dc.caret_chars[0] = 'A';
1638
1639 location_t bar
1640 = make_location (linemap_position_for_column (line_table, 8),
1641 linemap_position_for_column (line_table, 7),
1642 linemap_position_for_column (line_table, 9));
1643 dc.caret_chars[1] = 'B';
1644
1645 location_t field
1646 = make_location (linemap_position_for_column (line_table, 13),
1647 linemap_position_for_column (line_table, 11),
1648 linemap_position_for_column (line_table, 15));
1649 dc.caret_chars[2] = 'C';
1650
1651 rich_location richloc (line_table, foo);
1652 richloc.add_range (bar, true);
1653 richloc.add_range (field, true);
1654 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1655 ASSERT_STREQ ("\n"
1656 " foo = bar.field;\n"
1657 " ~A~ ~B~ ~~C~~\n",
1658 pp_formatted_text (dc.printer));
1659 }
1660
1661 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1662
1663 static void
1664 test_one_liner_fixit_insert_before ()
1665 {
1666 test_diagnostic_context dc;
1667 location_t caret = linemap_position_for_column (line_table, 7);
1668 rich_location richloc (line_table, caret);
1669 richloc.add_fixit_insert_before ("&");
1670 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1671 ASSERT_STREQ ("\n"
1672 " foo = bar.field;\n"
1673 " ^\n"
1674 " &\n",
1675 pp_formatted_text (dc.printer));
1676 }
1677
1678 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1679
1680 static void
1681 test_one_liner_fixit_insert_after ()
1682 {
1683 test_diagnostic_context dc;
1684 location_t start = linemap_position_for_column (line_table, 1);
1685 location_t finish = linemap_position_for_column (line_table, 3);
1686 location_t foo = make_location (start, start, finish);
1687 rich_location richloc (line_table, foo);
1688 richloc.add_fixit_insert_after ("[0]");
1689 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1690 ASSERT_STREQ ("\n"
1691 " foo = bar.field;\n"
1692 " ^~~\n"
1693 " [0]\n",
1694 pp_formatted_text (dc.printer));
1695 }
1696
1697 /* Removal fix-it hint: removal of the ".field". */
1698
1699 static void
1700 test_one_liner_fixit_remove ()
1701 {
1702 test_diagnostic_context dc;
1703 location_t start = linemap_position_for_column (line_table, 10);
1704 location_t finish = linemap_position_for_column (line_table, 15);
1705 location_t dot = make_location (start, start, finish);
1706 rich_location richloc (line_table, dot);
1707 richloc.add_fixit_remove ();
1708 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1709 ASSERT_STREQ ("\n"
1710 " foo = bar.field;\n"
1711 " ^~~~~~\n"
1712 " ------\n",
1713 pp_formatted_text (dc.printer));
1714 }
1715
1716 /* Replace fix-it hint: replacing "field" with "m_field". */
1717
1718 static void
1719 test_one_liner_fixit_replace ()
1720 {
1721 test_diagnostic_context dc;
1722 location_t start = linemap_position_for_column (line_table, 11);
1723 location_t finish = linemap_position_for_column (line_table, 15);
1724 location_t field = make_location (start, start, finish);
1725 rich_location richloc (line_table, field);
1726 richloc.add_fixit_replace ("m_field");
1727 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1728 ASSERT_STREQ ("\n"
1729 " foo = bar.field;\n"
1730 " ^~~~~\n"
1731 " m_field\n",
1732 pp_formatted_text (dc.printer));
1733 }
1734
1735 /* Replace fix-it hint: replacing "field" with "m_field",
1736 but where the caret was elsewhere. */
1737
1738 static void
1739 test_one_liner_fixit_replace_non_equal_range ()
1740 {
1741 test_diagnostic_context dc;
1742 location_t equals = linemap_position_for_column (line_table, 5);
1743 location_t start = linemap_position_for_column (line_table, 11);
1744 location_t finish = linemap_position_for_column (line_table, 15);
1745 rich_location richloc (line_table, equals);
1746 source_range range;
1747 range.m_start = start;
1748 range.m_finish = finish;
1749 richloc.add_fixit_replace (range, "m_field");
1750 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1751 /* The replacement range is not indicated in the annotation line, so
1752 it should be indicated via an additional underline. */
1753 ASSERT_STREQ ("\n"
1754 " foo = bar.field;\n"
1755 " ^\n"
1756 " -----\n"
1757 " m_field\n",
1758 pp_formatted_text (dc.printer));
1759 }
1760
1761 /* Replace fix-it hint: replacing "field" with "m_field",
1762 where the caret was elsewhere, but where a secondary range
1763 exactly covers "field". */
1764
1765 static void
1766 test_one_liner_fixit_replace_equal_secondary_range ()
1767 {
1768 test_diagnostic_context dc;
1769 location_t equals = linemap_position_for_column (line_table, 5);
1770 location_t start = linemap_position_for_column (line_table, 11);
1771 location_t finish = linemap_position_for_column (line_table, 15);
1772 rich_location richloc (line_table, equals);
1773 location_t field = make_location (start, start, finish);
1774 richloc.add_range (field, false);
1775 richloc.add_fixit_replace (field, "m_field");
1776 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1777 /* The replacement range is indicated in the annotation line,
1778 so it shouldn't be indicated via an additional underline. */
1779 ASSERT_STREQ ("\n"
1780 " foo = bar.field;\n"
1781 " ^ ~~~~~\n"
1782 " m_field\n",
1783 pp_formatted_text (dc.printer));
1784 }
1785
1786 /* Verify that we can use ad-hoc locations when adding fixits to a
1787 rich_location. */
1788
1789 static void
1790 test_one_liner_fixit_validation_adhoc_locations ()
1791 {
1792 /* Generate a range that's too long to be packed, so must
1793 be stored as an ad-hoc location (given the defaults
1794 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
1795 const location_t c7 = linemap_position_for_column (line_table, 7);
1796 const location_t c47 = linemap_position_for_column (line_table, 47);
1797 const location_t loc = make_location (c7, c7, c47);
1798
1799 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
1800 return;
1801
1802 ASSERT_TRUE (IS_ADHOC_LOC (loc));
1803
1804 /* Insert. */
1805 {
1806 rich_location richloc (line_table, loc);
1807 richloc.add_fixit_insert_before (loc, "test");
1808 /* It should not have been discarded by the validator. */
1809 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1810
1811 test_diagnostic_context dc;
1812 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1813 ASSERT_STREQ ("\n"
1814 " foo = bar.field;\n"
1815 " ^~~~~~~~~~ \n"
1816 " test\n",
1817 pp_formatted_text (dc.printer));
1818 }
1819
1820 /* Remove. */
1821 {
1822 rich_location richloc (line_table, loc);
1823 source_range range = source_range::from_locations (loc, c47);
1824 richloc.add_fixit_remove (range);
1825 /* It should not have been discarded by the validator. */
1826 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1827
1828 test_diagnostic_context dc;
1829 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1830 ASSERT_STREQ ("\n"
1831 " foo = bar.field;\n"
1832 " ^~~~~~~~~~ \n"
1833 " -----------------------------------------\n",
1834 pp_formatted_text (dc.printer));
1835 }
1836
1837 /* Replace. */
1838 {
1839 rich_location richloc (line_table, loc);
1840 source_range range = source_range::from_locations (loc, c47);
1841 richloc.add_fixit_replace (range, "test");
1842 /* It should not have been discarded by the validator. */
1843 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
1844
1845 test_diagnostic_context dc;
1846 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1847 ASSERT_STREQ ("\n"
1848 " foo = bar.field;\n"
1849 " ^~~~~~~~~~ \n"
1850 " test\n",
1851 pp_formatted_text (dc.printer));
1852 }
1853 }
1854
1855 /* Ensure that we can add an arbitrary number of fix-it hints to a
1856 rich_location. */
1857
1858 static void
1859 test_one_liner_many_fixits ()
1860 {
1861 test_diagnostic_context dc;
1862 location_t equals = linemap_position_for_column (line_table, 5);
1863 rich_location richloc (line_table, equals);
1864 for (int i = 0; i < 19; i++)
1865 richloc.add_fixit_insert_before ("a");
1866 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
1867 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1868 ASSERT_STREQ ("\n"
1869 " foo = bar.field;\n"
1870 " ^\n"
1871 " a\n"
1872 " a\n"
1873 " a\n"
1874 " a\n"
1875 " a\n"
1876 " a\n"
1877 " a\n"
1878 " a\n"
1879 " a\n"
1880 " a\n"
1881 " a\n"
1882 " a\n"
1883 " a\n"
1884 " a\n"
1885 " a\n"
1886 " a\n"
1887 " a\n"
1888 " a\n"
1889 " a\n",
1890 pp_formatted_text (dc.printer));
1891 }
1892
1893 /* Run the various one-liner tests. */
1894
1895 static void
1896 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
1897 {
1898 /* Create a tempfile and write some text to it.
1899 ....................0000000001111111.
1900 ....................1234567890123456. */
1901 const char *content = "foo = bar.field;\n";
1902 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1903 line_table_test ltt (case_);
1904
1905 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
1906
1907 location_t line_end = linemap_position_for_column (line_table, 16);
1908
1909 /* Don't attempt to run the tests if column data might be unavailable. */
1910 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1911 return;
1912
1913 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
1914 ASSERT_EQ (1, LOCATION_LINE (line_end));
1915 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
1916
1917 test_one_liner_simple_caret ();
1918 test_one_liner_caret_and_range ();
1919 test_one_liner_multiple_carets_and_ranges ();
1920 test_one_liner_fixit_insert_before ();
1921 test_one_liner_fixit_insert_after ();
1922 test_one_liner_fixit_remove ();
1923 test_one_liner_fixit_replace ();
1924 test_one_liner_fixit_replace_non_equal_range ();
1925 test_one_liner_fixit_replace_equal_secondary_range ();
1926 test_one_liner_fixit_validation_adhoc_locations ();
1927 test_one_liner_many_fixits ();
1928 }
1929
1930 /* Verify that we print fixits even if they only affect lines
1931 outside those covered by the ranges in the rich_location. */
1932
1933 static void
1934 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
1935 {
1936 /* Create a tempfile and write some text to it.
1937 ...000000000111111111122222222223333333333.
1938 ...123456789012345678901234567890123456789. */
1939 const char *content
1940 = ("struct point { double x; double y; };\n" /* line 1. */
1941 "struct point origin = {x: 0.0,\n" /* line 2. */
1942 " y\n" /* line 3. */
1943 "\n" /* line 4. */
1944 "\n" /* line 5. */
1945 " : 0.0};\n"); /* line 6. */
1946 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
1947 line_table_test ltt (case_);
1948
1949 const line_map_ordinary *ord_map
1950 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
1951 tmp.get_filename (), 0));
1952
1953 linemap_line_start (line_table, 1, 100);
1954
1955 const location_t final_line_end
1956 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
1957
1958 /* Don't attempt to run the tests if column data might be unavailable. */
1959 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
1960 return;
1961
1962 /* A pair of tests for modernizing the initializers to C99-style. */
1963
1964 /* The one-liner case (line 2). */
1965 {
1966 test_diagnostic_context dc;
1967 const location_t x
1968 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
1969 const location_t colon
1970 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
1971 rich_location richloc (line_table, colon);
1972 richloc.add_fixit_insert_before (x, ".");
1973 richloc.add_fixit_replace (colon, "=");
1974 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1975 ASSERT_STREQ ("\n"
1976 " struct point origin = {x: 0.0,\n"
1977 " ^\n"
1978 " .=\n",
1979 pp_formatted_text (dc.printer));
1980 }
1981
1982 /* The multiline case. The caret for the rich_location is on line 6;
1983 verify that insertion fixit on line 3 is still printed (and that
1984 span starts are printed due to the gap between the span at line 3
1985 and that at line 6). */
1986 {
1987 test_diagnostic_context dc;
1988 const location_t y
1989 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
1990 const location_t colon
1991 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
1992 rich_location richloc (line_table, colon);
1993 richloc.add_fixit_insert_before (y, ".");
1994 richloc.add_fixit_replace (colon, "=");
1995 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1996 ASSERT_STREQ ("\n"
1997 "FILENAME:3:24:\n"
1998 " y\n"
1999 " .\n"
2000 "FILENAME:6:25:\n"
2001 " : 0.0};\n"
2002 " ^\n"
2003 " =\n",
2004 pp_formatted_text (dc.printer));
2005 }
2006 }
2007
2008
2009 /* Verify that fix-it hints are appropriately consolidated.
2010
2011 If any fix-it hints in a rich_location involve locations beyond
2012 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2013 the fix-it as a whole, so there should be none.
2014
2015 Otherwise, verify that consecutive "replace" and "remove" fix-its
2016 are merged, and that other fix-its remain separate. */
2017
2018 static void
2019 test_fixit_consolidation (const line_table_case &case_)
2020 {
2021 line_table_test ltt (case_);
2022
2023 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2024
2025 const location_t c10 = linemap_position_for_column (line_table, 10);
2026 const location_t c15 = linemap_position_for_column (line_table, 15);
2027 const location_t c16 = linemap_position_for_column (line_table, 16);
2028 const location_t c17 = linemap_position_for_column (line_table, 17);
2029 const location_t c20 = linemap_position_for_column (line_table, 20);
2030 const location_t caret = c10;
2031
2032 /* Insert + insert. */
2033 {
2034 rich_location richloc (line_table, caret);
2035 richloc.add_fixit_insert_before (c10, "foo");
2036 richloc.add_fixit_insert_before (c15, "bar");
2037
2038 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2039 /* Bogus column info for 2nd fixit, so no fixits. */
2040 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2041 else
2042 /* They should not have been merged. */
2043 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2044 }
2045
2046 /* Insert + replace. */
2047 {
2048 rich_location richloc (line_table, caret);
2049 richloc.add_fixit_insert_before (c10, "foo");
2050 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2051 "bar");
2052
2053 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2054 /* Bogus column info for 2nd fixit, so no fixits. */
2055 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2056 else
2057 /* They should not have been merged. */
2058 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2059 }
2060
2061 /* Replace + non-consecutive insert. */
2062 {
2063 rich_location richloc (line_table, caret);
2064 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2065 "bar");
2066 richloc.add_fixit_insert_before (c17, "foo");
2067
2068 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2069 /* Bogus column info for 2nd fixit, so no fixits. */
2070 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2071 else
2072 /* They should not have been merged. */
2073 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2074 }
2075
2076 /* Replace + non-consecutive replace. */
2077 {
2078 rich_location richloc (line_table, caret);
2079 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2080 "foo");
2081 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2082 "bar");
2083
2084 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2085 /* Bogus column info for 2nd fixit, so no fixits. */
2086 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2087 else
2088 /* They should not have been merged. */
2089 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2090 }
2091
2092 /* Replace + consecutive replace. */
2093 {
2094 rich_location richloc (line_table, caret);
2095 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2096 "foo");
2097 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2098 "bar");
2099
2100 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2101 /* Bogus column info for 2nd fixit, so no fixits. */
2102 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2103 else
2104 {
2105 /* They should have been merged into a single "replace". */
2106 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2107 const fixit_hint *hint = richloc.get_fixit_hint (0);
2108 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2109 const fixit_replace *replace = (const fixit_replace *)hint;
2110 ASSERT_STREQ ("foobar", replace->get_string ());
2111 ASSERT_EQ (c10, replace->get_range ().m_start);
2112 ASSERT_EQ (c20, replace->get_range ().m_finish);
2113 }
2114 }
2115
2116 /* Replace + consecutive removal. */
2117 {
2118 rich_location richloc (line_table, caret);
2119 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2120 "foo");
2121 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2122
2123 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2124 /* Bogus column info for 2nd fixit, so no fixits. */
2125 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2126 else
2127 {
2128 /* They should have been merged into a single replace, with the
2129 range extended to cover that of the removal. */
2130 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2131 const fixit_hint *hint = richloc.get_fixit_hint (0);
2132 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2133 const fixit_replace *replace = (const fixit_replace *)hint;
2134 ASSERT_STREQ ("foo", replace->get_string ());
2135 ASSERT_EQ (c10, replace->get_range ().m_start);
2136 ASSERT_EQ (c20, replace->get_range ().m_finish);
2137 }
2138 }
2139
2140 /* Consecutive removals. */
2141 {
2142 rich_location richloc (line_table, caret);
2143 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2144 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2145
2146 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2147 /* Bogus column info for 2nd fixit, so no fixits. */
2148 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2149 else
2150 {
2151 /* They should have been merged into a single "replace-with-empty". */
2152 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2153 const fixit_hint *hint = richloc.get_fixit_hint (0);
2154 ASSERT_EQ (fixit_hint::REPLACE, hint->get_kind ());
2155 const fixit_replace *replace = (const fixit_replace *)hint;
2156 ASSERT_STREQ ("", replace->get_string ());
2157 ASSERT_EQ (c10, replace->get_range ().m_start);
2158 ASSERT_EQ (c20, replace->get_range ().m_finish);
2159 }
2160 }
2161 }
2162
2163 /* Insertion fix-it hint: adding a "break;" on a line by itself.
2164 This will fail, as newlines aren't yet supported. */
2165
2166 static void
2167 test_fixit_insert_containing_newline (const line_table_case &case_)
2168 {
2169 /* Create a tempfile and write some text to it.
2170 .........................0000000001111111.
2171 .........................1234567890123456. */
2172 const char *old_content = (" case 'a':\n" /* line 1. */
2173 " x = a;\n" /* line 2. */
2174 " case 'b':\n" /* line 3. */
2175 " x = b;\n");/* line 4. */
2176
2177 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2178 line_table_test ltt (case_);
2179 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2180
2181 /* Add a "break;" on a line by itself before line 3 i.e. before
2182 column 1 of line 3. */
2183 location_t case_start = linemap_position_for_column (line_table, 5);
2184 location_t case_finish = linemap_position_for_column (line_table, 13);
2185 location_t case_loc = make_location (case_start, case_start, case_finish);
2186 rich_location richloc (line_table, case_loc);
2187 location_t line_start = linemap_position_for_column (line_table, 1);
2188 richloc.add_fixit_insert_before (line_start, " break;\n");
2189
2190 /* Newlines are not yet supported within fix-it hints, so
2191 the fix-it should not be displayed. */
2192 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2193
2194 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2195 return;
2196
2197 test_diagnostic_context dc;
2198 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2199 ASSERT_STREQ ("\n"
2200 " case 'b':\n"
2201 " ^~~~~~~~~\n",
2202 pp_formatted_text (dc.printer));
2203 }
2204
2205 /* Replacement fix-it hint containing a newline.
2206 This will fail, as newlines aren't yet supported. */
2207
2208 static void
2209 test_fixit_replace_containing_newline (const line_table_case &case_)
2210 {
2211 /* Create a tempfile and write some text to it.
2212 .........................0000000001111.
2213 .........................1234567890123. */
2214 const char *old_content = "foo = bar ();\n";
2215
2216 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2217 line_table_test ltt (case_);
2218 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2219
2220 /* Replace the " = " with "\n = ", as if we were reformatting an
2221 overly long line. */
2222 location_t start = linemap_position_for_column (line_table, 4);
2223 location_t finish = linemap_position_for_column (line_table, 6);
2224 location_t loc = linemap_position_for_column (line_table, 13);
2225 rich_location richloc (line_table, loc);
2226 source_range range = source_range::from_locations (start, finish);
2227 richloc.add_fixit_replace (range, "\n =");
2228
2229 /* Newlines are not yet supported within fix-it hints, so
2230 the fix-it should not be displayed. */
2231 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2232
2233 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2234 return;
2235
2236 test_diagnostic_context dc;
2237 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2238 ASSERT_STREQ ("\n"
2239 " foo = bar ();\n"
2240 " ^\n",
2241 pp_formatted_text (dc.printer));
2242 }
2243
2244 /* Run all of the selftests within this file. */
2245
2246 void
2247 diagnostic_show_locus_c_tests ()
2248 {
2249 test_layout_range_for_single_point ();
2250 test_layout_range_for_single_line ();
2251 test_layout_range_for_multiple_lines ();
2252
2253 test_get_line_width_without_trailing_whitespace ();
2254
2255 test_diagnostic_show_locus_unknown_location ();
2256
2257 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
2258 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
2259 for_each_line_table_case (test_fixit_consolidation);
2260 for_each_line_table_case (test_fixit_insert_containing_newline);
2261 for_each_line_table_case (test_fixit_replace_containing_newline);
2262 }
2263
2264 } // namespace selftest
2265
2266 #endif /* #if CHECKING_P */