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