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