]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/diagnostic-show-locus.c
diagnostics: fix end-points of ranges within macros (PR c++/79300)
[thirdparty/gcc.git] / gcc / diagnostic-show-locus.c
1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2017 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "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
792 (src_range.m_start, LOCATION_ASPECT_START);
793 expanded_location finish
794 = linemap_client_expand_location_to_spelling_point
795 (src_range.m_finish, LOCATION_ASPECT_FINISH);
796 expanded_location caret
797 = linemap_client_expand_location_to_spelling_point
798 (loc_range->m_loc, LOCATION_ASPECT_CARET);
799
800 /* If any part of the range isn't in the same file as the primary
801 location of this diagnostic, ignore the range. */
802 if (start.file != m_exploc.file)
803 continue;
804 if (finish.file != m_exploc.file)
805 continue;
806 if (loc_range->m_show_caret_p)
807 if (caret.file != m_exploc.file)
808 continue;
809
810 /* Sanitize the caret location for non-primary ranges. */
811 if (m_layout_ranges.length () > 0)
812 if (loc_range->m_show_caret_p)
813 if (!compatible_locations_p (loc_range->m_loc, primary_loc))
814 /* Discard any non-primary ranges that can't be printed
815 sanely relative to the primary location. */
816 continue;
817
818 /* Everything is now known to be in the correct source file,
819 but it may require further sanitization. */
820 layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
821
822 /* If we have a range that finishes before it starts (perhaps
823 from something built via macro expansion), printing the
824 range is likely to be nonsensical. Also, attempting to do so
825 breaks assumptions within the printing code (PR c/68473).
826 Similarly, don't attempt to print ranges if one or both ends
827 of the range aren't sane to print relative to the
828 primary location (PR c++/70105). */
829 if (start.line > finish.line
830 || !compatible_locations_p (src_range.m_start, primary_loc)
831 || !compatible_locations_p (src_range.m_finish, primary_loc))
832 {
833 /* Is this the primary location? */
834 if (m_layout_ranges.length () == 0)
835 {
836 /* We want to print the caret for the primary location, but
837 we must sanitize away m_start and m_finish. */
838 ri.m_start = ri.m_caret;
839 ri.m_finish = ri.m_caret;
840 }
841 else
842 /* This is a non-primary range; ignore it. */
843 continue;
844 }
845
846 /* Passed all the tests; add the range to m_layout_ranges so that
847 it will be printed. */
848 m_layout_ranges.safe_push (ri);
849 }
850
851 /* Populate m_fixit_hints, filtering to only those that are in the
852 same file. */
853 for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
854 {
855 const fixit_hint *hint = richloc->get_fixit_hint (i);
856 if (validate_fixit_hint_p (hint))
857 m_fixit_hints.safe_push (hint);
858 }
859
860 /* Populate m_line_spans. */
861 calculate_line_spans ();
862
863 /* Adjust m_x_offset.
864 Center the primary caret to fit in max_width; all columns
865 will be adjusted accordingly. */
866 int max_width = m_context->caret_max_width;
867 int line_width;
868 const char *line = location_get_source_line (m_exploc.file, m_exploc.line,
869 &line_width);
870 if (line && m_exploc.column <= line_width)
871 {
872 int right_margin = CARET_LINE_MARGIN;
873 int column = m_exploc.column;
874 right_margin = MIN (line_width - column, right_margin);
875 right_margin = max_width - right_margin;
876 if (line_width >= max_width && column > right_margin)
877 m_x_offset = column - right_margin;
878 gcc_assert (m_x_offset >= 0);
879 }
880
881 if (context->show_ruler_p)
882 show_ruler (m_x_offset + max_width);
883 }
884
885 /* Return true iff we should print a heading when starting the
886 line span with the given index. */
887
888 bool
889 layout::print_heading_for_line_span_index_p (int line_span_idx) const
890 {
891 /* We print a heading for every change of line span, hence for every
892 line span after the initial one. */
893 if (line_span_idx > 0)
894 return true;
895
896 /* We also do it for the initial span if the primary location of the
897 diagnostic is in a different span. */
898 if (m_exploc.line > (int)get_line_span (0)->m_last_line)
899 return true;
900
901 return false;
902 }
903
904 /* Get an expanded_location for the first location of interest within
905 the given line_span.
906 Used when printing a heading to indicate a new line span. */
907
908 expanded_location
909 layout::get_expanded_location (const line_span *line_span) const
910 {
911 /* Whenever possible, use the caret location. */
912 if (line_span->contains_line_p (m_exploc.line))
913 return m_exploc;
914
915 /* Otherwise, use the start of the first range that's present
916 within the line_span. */
917 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
918 {
919 const layout_range *lr = &m_layout_ranges[i];
920 if (line_span->contains_line_p (lr->m_start.m_line))
921 {
922 expanded_location exploc = m_exploc;
923 exploc.line = lr->m_start.m_line;
924 exploc.column = lr->m_start.m_column;
925 return exploc;
926 }
927 }
928
929 /* Otherwise, use the location of the first fixit-hint present within
930 the line_span. */
931 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
932 {
933 const fixit_hint *hint = m_fixit_hints[i];
934 location_t loc = hint->get_start_loc ();
935 expanded_location exploc = expand_location (loc);
936 if (line_span->contains_line_p (exploc.line))
937 return exploc;
938 }
939
940 /* It should not be possible to have a line span that didn't
941 contain any of the layout_range or fixit_hint instances. */
942 gcc_unreachable ();
943 return m_exploc;
944 }
945
946 /* Determine if HINT is meaningful to print within this layout. */
947
948 bool
949 layout::validate_fixit_hint_p (const fixit_hint *hint)
950 {
951 if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
952 return false;
953 if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
954 return false;
955
956 return true;
957 }
958
959 /* Determine the range of lines affected by HINT.
960 This assumes that HINT has already been filtered by
961 validate_fixit_hint_p, and so affects the correct source file. */
962
963 static line_span
964 get_line_span_for_fixit_hint (const fixit_hint *hint)
965 {
966 gcc_assert (hint);
967 return line_span (LOCATION_LINE (hint->get_start_loc ()),
968 LOCATION_LINE (hint->get_next_loc ()));
969 }
970
971 /* We want to print the pertinent source code at a diagnostic. The
972 rich_location can contain multiple locations. This will have been
973 filtered into m_exploc (the caret for the primary location) and
974 m_layout_ranges, for those ranges within the same source file.
975
976 We will print a subset of the lines within the source file in question,
977 as a collection of "spans" of lines.
978
979 This function populates m_line_spans with an ordered, disjoint list of
980 the line spans of interest.
981
982 For example, if the primary caret location is on line 7, with ranges
983 covering lines 5-6 and lines 9-12:
984
985 004
986 005 |RANGE 0
987 006 |RANGE 0
988 007 |PRIMARY CARET
989 008
990 009 |RANGE 1
991 010 |RANGE 1
992 011 |RANGE 1
993 012 |RANGE 1
994 013
995
996 then we want two spans: lines 5-7 and lines 9-12. */
997
998 void
999 layout::calculate_line_spans ()
1000 {
1001 /* This should only be called once, by the ctor. */
1002 gcc_assert (m_line_spans.length () == 0);
1003
1004 /* Populate tmp_spans with individual spans, for each of
1005 m_exploc, and for m_layout_ranges. */
1006 auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1007 tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1008 for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1009 {
1010 const layout_range *lr = &m_layout_ranges[i];
1011 gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1012 tmp_spans.safe_push (line_span (lr->m_start.m_line,
1013 lr->m_finish.m_line));
1014 }
1015
1016 /* Also add spans for any fix-it hints, in case they cover other lines. */
1017 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1018 {
1019 const fixit_hint *hint = m_fixit_hints[i];
1020 gcc_assert (hint);
1021 tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1022 }
1023
1024 /* Sort them. */
1025 tmp_spans.qsort(line_span::comparator);
1026
1027 /* Now iterate through tmp_spans, copying into m_line_spans, and
1028 combining where possible. */
1029 gcc_assert (tmp_spans.length () > 0);
1030 m_line_spans.safe_push (tmp_spans[0]);
1031 for (unsigned int i = 1; i < tmp_spans.length (); i++)
1032 {
1033 line_span *current = &m_line_spans[m_line_spans.length () - 1];
1034 const line_span *next = &tmp_spans[i];
1035 gcc_assert (next->m_first_line >= current->m_first_line);
1036 if (next->m_first_line <= current->m_last_line + 1)
1037 {
1038 /* We can merge them. */
1039 if (next->m_last_line > current->m_last_line)
1040 current->m_last_line = next->m_last_line;
1041 }
1042 else
1043 {
1044 /* No merger possible. */
1045 m_line_spans.safe_push (*next);
1046 }
1047 }
1048
1049 /* Verify the result, in m_line_spans. */
1050 gcc_assert (m_line_spans.length () > 0);
1051 for (unsigned int i = 1; i < m_line_spans.length (); i++)
1052 {
1053 const line_span *prev = &m_line_spans[i - 1];
1054 const line_span *next = &m_line_spans[i];
1055 /* The individual spans must be sane. */
1056 gcc_assert (prev->m_first_line <= prev->m_last_line);
1057 gcc_assert (next->m_first_line <= next->m_last_line);
1058 /* The spans must be ordered. */
1059 gcc_assert (prev->m_first_line < next->m_first_line);
1060 /* There must be a gap of at least one line between separate spans. */
1061 gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1062 }
1063 }
1064
1065 /* Print line ROW of source code, potentially colorized at any ranges, and
1066 populate *LBOUNDS_OUT.
1067 LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1068 is its width. */
1069
1070 void
1071 layout::print_source_line (int row, const char *line, int line_width,
1072 line_bounds *lbounds_out)
1073 {
1074 m_colorizer.set_normal_text ();
1075
1076 /* We will stop printing the source line at any trailing
1077 whitespace. */
1078 line_width = get_line_width_without_trailing_whitespace (line,
1079 line_width);
1080 line += m_x_offset;
1081
1082 pp_space (m_pp);
1083 int first_non_ws = INT_MAX;
1084 int last_non_ws = 0;
1085 int column;
1086 for (column = 1 + m_x_offset; column <= line_width; column++)
1087 {
1088 /* Assuming colorization is enabled for the caret and underline
1089 characters, we may also colorize the associated characters
1090 within the source line.
1091
1092 For frontends that generate range information, we color the
1093 associated characters in the source line the same as the
1094 carets and underlines in the annotation line, to make it easier
1095 for the reader to see the pertinent code.
1096
1097 For frontends that only generate carets, we don't colorize the
1098 characters above them, since this would look strange (e.g.
1099 colorizing just the first character in a token). */
1100 if (m_colorize_source_p)
1101 {
1102 bool in_range_p;
1103 point_state state;
1104 in_range_p = get_state_at_point (row, column,
1105 0, INT_MAX,
1106 &state);
1107 if (in_range_p)
1108 m_colorizer.set_range (state.range_idx);
1109 else
1110 m_colorizer.set_normal_text ();
1111 }
1112 char c = *line == '\t' ? ' ' : *line;
1113 if (c == '\0')
1114 c = ' ';
1115 if (c != ' ')
1116 {
1117 last_non_ws = column;
1118 if (first_non_ws == INT_MAX)
1119 first_non_ws = column;
1120 }
1121 pp_character (m_pp, c);
1122 line++;
1123 }
1124 print_newline ();
1125
1126 lbounds_out->m_first_non_ws = first_non_ws;
1127 lbounds_out->m_last_non_ws = last_non_ws;
1128 }
1129
1130 /* Determine if we should print an annotation line for ROW.
1131 i.e. if any of m_layout_ranges contains ROW. */
1132
1133 bool
1134 layout::should_print_annotation_line_p (int row) const
1135 {
1136 layout_range *range;
1137 int i;
1138 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1139 if (range->intersects_line_p (row))
1140 return true;
1141 return false;
1142 }
1143
1144 /* Print a line consisting of the caret/underlines for the given
1145 source line. */
1146
1147 void
1148 layout::print_annotation_line (int row, const line_bounds lbounds)
1149 {
1150 int x_bound = get_x_bound_for_row (row, m_exploc.column,
1151 lbounds.m_last_non_ws);
1152
1153 pp_space (m_pp);
1154 for (int column = 1 + m_x_offset; column < x_bound; column++)
1155 {
1156 bool in_range_p;
1157 point_state state;
1158 in_range_p = get_state_at_point (row, column,
1159 lbounds.m_first_non_ws,
1160 lbounds.m_last_non_ws,
1161 &state);
1162 if (in_range_p)
1163 {
1164 /* Within a range. Draw either the caret or an underline. */
1165 m_colorizer.set_range (state.range_idx);
1166 if (state.draw_caret_p)
1167 {
1168 /* Draw the caret. */
1169 char caret_char;
1170 if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1171 caret_char = m_context->caret_chars[state.range_idx];
1172 else
1173 caret_char = '^';
1174 pp_character (m_pp, caret_char);
1175 }
1176 else
1177 pp_character (m_pp, '~');
1178 }
1179 else
1180 {
1181 /* Not in a range. */
1182 m_colorizer.set_normal_text ();
1183 pp_character (m_pp, ' ');
1184 }
1185 }
1186 print_newline ();
1187 }
1188
1189 /* If there are any fixit hints inserting new lines before source line ROW,
1190 print them.
1191
1192 They are printed on lines of their own, before the source line
1193 itself, with a leading '+'. */
1194
1195 void
1196 layout::print_leading_fixits (int row)
1197 {
1198 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1199 {
1200 const fixit_hint *hint = m_fixit_hints[i];
1201
1202 if (!hint->ends_with_newline_p ())
1203 /* Not a newline fixit; print it in print_trailing_fixits. */
1204 continue;
1205
1206 gcc_assert (hint->insertion_p ());
1207
1208 if (hint->affects_line_p (m_exploc.file, row))
1209 {
1210 /* Printing the '+' with normal colorization
1211 and the inserted line with "insert" colorization
1212 helps them stand out from each other, and from
1213 the surrounding text. */
1214 m_colorizer.set_normal_text ();
1215 pp_character (m_pp, '+');
1216 m_colorizer.set_fixit_insert ();
1217 /* Print all but the trailing newline of the fix-it hint.
1218 We have to print the newline separately to avoid
1219 getting additional pp prefixes printed. */
1220 for (size_t i = 0; i < hint->get_length () - 1; i++)
1221 pp_character (m_pp, hint->get_string ()[i]);
1222 m_colorizer.set_normal_text ();
1223 pp_newline (m_pp);
1224 }
1225 }
1226 }
1227
1228 /* Subroutine of layout::print_trailing_fixits.
1229
1230 Determine if the annotation line printed for LINE contained
1231 the exact range from START_COLUMN to FINISH_COLUMN. */
1232
1233 bool
1234 layout::annotation_line_showed_range_p (int line, int start_column,
1235 int finish_column) const
1236 {
1237 layout_range *range;
1238 int i;
1239 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1240 if (range->m_start.m_line == line
1241 && range->m_start.m_column == start_column
1242 && range->m_finish.m_line == line
1243 && range->m_finish.m_column == finish_column)
1244 return true;
1245 return false;
1246 }
1247
1248 /* Classes for printing trailing fix-it hints i.e. those that
1249 don't add new lines.
1250
1251 For insertion, these can look like:
1252
1253 new_text
1254
1255 For replacement, these can look like:
1256
1257 ------------- : underline showing affected range
1258 new_text
1259
1260 For deletion, these can look like:
1261
1262 ------------- : underline showing affected range
1263
1264 This can become confusing if they overlap, and so we need
1265 to do some preprocessing to decide what to print.
1266 We use the list of fixit_hint instances affecting the line
1267 to build a list of "correction" instances, and print the
1268 latter.
1269
1270 For example, consider a set of fix-its for converting
1271 a C-style cast to a C++ const_cast.
1272
1273 Given:
1274
1275 ..000000000111111111122222222223333333333.
1276 ..123456789012345678901234567890123456789.
1277 foo *f = (foo *)ptr->field;
1278 ^~~~~
1279
1280 and the fix-it hints:
1281 - replace col 10 (the open paren) with "const_cast<"
1282 - replace col 16 (the close paren) with "> ("
1283 - insert ")" before col 27
1284
1285 then we would get odd-looking output:
1286
1287 foo *f = (foo *)ptr->field;
1288 ^~~~~
1289 -
1290 const_cast<
1291 -
1292 > ( )
1293
1294 It would be better to detect when fixit hints are going to
1295 overlap (those that require new lines), and to consolidate
1296 the printing of such fixits, giving something like:
1297
1298 foo *f = (foo *)ptr->field;
1299 ^~~~~
1300 -----------------
1301 const_cast<foo *> (ptr->field)
1302
1303 This works by detecting when the printing would overlap, and
1304 effectively injecting no-op replace hints into the gaps between
1305 such fix-its, so that the printing joins up.
1306
1307 In the above example, the overlap of:
1308 - replace col 10 (the open paren) with "const_cast<"
1309 and:
1310 - replace col 16 (the close paren) with "> ("
1311 is fixed by injecting a no-op:
1312 - replace cols 11-15 with themselves ("foo *")
1313 and consolidating these, making:
1314 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1315 i.e.:
1316 - replace cols 10-16 with "const_cast<foo *> ("
1317
1318 This overlaps with the final fix-it hint:
1319 - insert ")" before col 27
1320 and so we repeat the consolidation process, by injecting
1321 a no-op:
1322 - replace cols 17-26 with themselves ("ptr->field")
1323 giving:
1324 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1325 i.e.:
1326 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1327
1328 and is thus printed as desired. */
1329
1330 /* A range of columns within a line. */
1331
1332 struct column_range
1333 {
1334 column_range (int start_, int finish_) : start (start_), finish (finish_) {}
1335
1336 bool operator== (const column_range &other) const
1337 {
1338 return start == other.start && finish == other.finish;
1339 }
1340
1341 int start;
1342 int finish;
1343 };
1344
1345 /* Get the range of columns that HINT would affect. */
1346
1347 static column_range
1348 get_affected_columns (const fixit_hint *hint)
1349 {
1350 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1351 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1352
1353 return column_range (start_column, finish_column);
1354 }
1355
1356 /* Get the range of columns that would be printed for HINT. */
1357
1358 static column_range
1359 get_printed_columns (const fixit_hint *hint)
1360 {
1361 int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1362 int final_hint_column = start_column + hint->get_length () - 1;
1363 if (hint->insertion_p ())
1364 {
1365 return column_range (start_column, final_hint_column);
1366 }
1367 else
1368 {
1369 int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1370
1371 return column_range (start_column,
1372 MAX (finish_column, final_hint_column));
1373 }
1374 }
1375
1376 /* A correction on a particular line.
1377 This describes a plan for how to print one or more fixit_hint
1378 instances that affected the line, potentially consolidating hints
1379 into corrections to make the result easier for the user to read. */
1380
1381 struct correction
1382 {
1383 correction (column_range affected_columns,
1384 column_range printed_columns,
1385 const char *new_text, size_t new_text_len)
1386 : m_affected_columns (affected_columns),
1387 m_printed_columns (printed_columns),
1388 m_text (xstrdup (new_text)),
1389 m_len (new_text_len),
1390 m_alloc_sz (new_text_len + 1)
1391 {
1392 }
1393
1394 ~correction () { free (m_text); }
1395
1396 bool insertion_p () const
1397 {
1398 return m_affected_columns.start == m_affected_columns.finish + 1;
1399 }
1400
1401 void ensure_capacity (size_t len);
1402 void ensure_terminated ();
1403
1404 /* If insert, then start: the column before which the text
1405 is to be inserted, and finish is offset by the length of
1406 the replacement.
1407 If replace, then the range of columns affected. */
1408 column_range m_affected_columns;
1409
1410 /* If insert, then start: the column before which the text
1411 is to be inserted, and finish is offset by the length of
1412 the replacement.
1413 If replace, then the range of columns affected. */
1414 column_range m_printed_columns;
1415
1416 /* The text to be inserted/used as replacement. */
1417 char *m_text;
1418 size_t m_len;
1419 size_t m_alloc_sz;
1420 };
1421
1422 /* Ensure that m_text can hold a string of length LEN
1423 (plus 1 for 0-termination). */
1424
1425 void
1426 correction::ensure_capacity (size_t len)
1427 {
1428 /* Allow 1 extra byte for 0-termination. */
1429 if (m_alloc_sz < (len + 1))
1430 {
1431 size_t new_alloc_sz = (len + 1) * 2;
1432 m_text = (char *)xrealloc (m_text, new_alloc_sz);
1433 m_alloc_sz = new_alloc_sz;
1434 }
1435 }
1436
1437 /* Ensure that m_text is 0-terminated. */
1438
1439 void
1440 correction::ensure_terminated ()
1441 {
1442 /* 0-terminate the buffer. */
1443 gcc_assert (m_len < m_alloc_sz);
1444 m_text[m_len] = '\0';
1445 }
1446
1447 /* A list of corrections affecting a particular line.
1448 This is used by layout::print_trailing_fixits for planning
1449 how to print the fix-it hints affecting the line. */
1450
1451 struct line_corrections
1452 {
1453 line_corrections (const char *filename, int row)
1454 : m_filename (filename), m_row (row)
1455 {}
1456 ~line_corrections ();
1457
1458 void add_hint (const fixit_hint *hint);
1459
1460 const char *m_filename;
1461 int m_row;
1462 auto_vec <correction *> m_corrections;
1463 };
1464
1465 /* struct line_corrections. */
1466
1467 line_corrections::~line_corrections ()
1468 {
1469 unsigned i;
1470 correction *c;
1471 FOR_EACH_VEC_ELT (m_corrections, i, c)
1472 delete c;
1473 }
1474
1475 /* Add HINT to the corrections for this line.
1476 Attempt to consolidate nearby hints so that they will not
1477 overlap with printed. */
1478
1479 void
1480 line_corrections::add_hint (const fixit_hint *hint)
1481 {
1482 column_range affected_columns = get_affected_columns (hint);
1483 column_range printed_columns = get_printed_columns (hint);
1484
1485 /* Potentially consolidate. */
1486 if (!m_corrections.is_empty ())
1487 {
1488 correction *last_correction
1489 = m_corrections[m_corrections.length () - 1];
1490 if (printed_columns.start <= last_correction->m_printed_columns.finish)
1491 {
1492 /* We have two hints for which the printed forms of the hints
1493 would touch or overlap, so we need to consolidate them to avoid
1494 confusing the user.
1495 Attempt to inject a "replace" correction from immediately
1496 after the end of the last hint to immediately before the start
1497 of the next hint. */
1498 column_range between (last_correction->m_affected_columns.finish + 1,
1499 printed_columns.start - 1);
1500
1501 /* Try to read the source. */
1502 int line_width;
1503 const char *line = location_get_source_line (m_filename, m_row,
1504 &line_width);
1505 if (line && between.finish < line_width)
1506 {
1507 /* Consolidate into the last correction:
1508 add a no-op "replace" of the "between" text, and
1509 add the text from the new hint. */
1510 size_t old_len = last_correction->m_len;
1511 size_t between_len = between.finish + 1 - between.start;
1512 size_t new_len = old_len + between_len + hint->get_length ();
1513 last_correction->ensure_capacity (new_len);
1514 memcpy (last_correction->m_text + old_len,
1515 line + between.start - 1,
1516 between.finish + 1 - between.start);
1517 memcpy (last_correction->m_text + old_len + between_len,
1518 hint->get_string (), hint->get_length ());
1519 last_correction->m_len = new_len;
1520 last_correction->ensure_terminated ();
1521 last_correction->m_affected_columns.finish
1522 = affected_columns.finish;
1523 last_correction->m_printed_columns.finish
1524 += between_len + hint->get_length ();
1525 return;
1526 }
1527 }
1528 }
1529
1530 /* If no consolidation happened, add a new correction instance. */
1531 m_corrections.safe_push (new correction (affected_columns,
1532 printed_columns,
1533 hint->get_string (),
1534 hint->get_length ()));
1535 }
1536
1537 /* If there are any fixit hints on source line ROW, print them.
1538 They are printed in order, attempting to combine them onto lines, but
1539 starting new lines if necessary.
1540 Fix-it hints that insert new lines are handled separately,
1541 in layout::print_leading_fixits. */
1542
1543 void
1544 layout::print_trailing_fixits (int row)
1545 {
1546 /* Build a list of correction instances for the line,
1547 potentially consolidating hints (for the sake of readability). */
1548 line_corrections corrections (m_exploc.file, row);
1549 for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1550 {
1551 const fixit_hint *hint = m_fixit_hints[i];
1552
1553 /* Newline fixits are handled by layout::print_leading_fixits. */
1554 if (hint->ends_with_newline_p ())
1555 continue;
1556
1557 if (hint->affects_line_p (m_exploc.file, row))
1558 corrections.add_hint (hint);
1559 }
1560
1561 /* Now print the corrections. */
1562 unsigned i;
1563 correction *c;
1564 int column = 0;
1565
1566 FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
1567 {
1568 /* For now we assume each fixit hint can only touch one line. */
1569 if (c->insertion_p ())
1570 {
1571 /* This assumes the insertion just affects one line. */
1572 int start_column = c->m_printed_columns.start;
1573 move_to_column (&column, start_column);
1574 m_colorizer.set_fixit_insert ();
1575 pp_string (m_pp, c->m_text);
1576 m_colorizer.set_normal_text ();
1577 column += c->m_len;
1578 }
1579 else
1580 {
1581 /* If the range of the replacement wasn't printed in the
1582 annotation line, then print an extra underline to
1583 indicate exactly what is being replaced.
1584 Always show it for removals. */
1585 int start_column = c->m_affected_columns.start;
1586 int finish_column = c->m_affected_columns.finish;
1587 if (!annotation_line_showed_range_p (row, start_column,
1588 finish_column)
1589 || c->m_len == 0)
1590 {
1591 move_to_column (&column, start_column);
1592 m_colorizer.set_fixit_delete ();
1593 for (; column <= finish_column; column++)
1594 pp_character (m_pp, '-');
1595 m_colorizer.set_normal_text ();
1596 }
1597 /* Print the replacement text. REPLACE also covers
1598 removals, so only do this extra work (potentially starting
1599 a new line) if we have actual replacement text. */
1600 if (c->m_len > 0)
1601 {
1602 move_to_column (&column, start_column);
1603 m_colorizer.set_fixit_insert ();
1604 pp_string (m_pp, c->m_text);
1605 m_colorizer.set_normal_text ();
1606 column += c->m_len;
1607 }
1608 }
1609 }
1610
1611 /* Add a trailing newline, if necessary. */
1612 move_to_column (&column, 0);
1613 }
1614
1615 /* Disable any colorization and emit a newline. */
1616
1617 void
1618 layout::print_newline ()
1619 {
1620 m_colorizer.set_normal_text ();
1621 pp_newline (m_pp);
1622 }
1623
1624 /* Return true if (ROW/COLUMN) is within a range of the layout.
1625 If it returns true, OUT_STATE is written to, with the
1626 range index, and whether we should draw the caret at
1627 (ROW/COLUMN) (as opposed to an underline). */
1628
1629 bool
1630 layout::get_state_at_point (/* Inputs. */
1631 int row, int column,
1632 int first_non_ws, int last_non_ws,
1633 /* Outputs. */
1634 point_state *out_state)
1635 {
1636 layout_range *range;
1637 int i;
1638 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1639 {
1640 if (range->contains_point (row, column))
1641 {
1642 out_state->range_idx = i;
1643
1644 /* Are we at the range's caret? is it visible? */
1645 out_state->draw_caret_p = false;
1646 if (range->m_show_caret_p
1647 && row == range->m_caret.m_line
1648 && column == range->m_caret.m_column)
1649 out_state->draw_caret_p = true;
1650
1651 /* Within a multiline range, don't display any underline
1652 in any leading or trailing whitespace on a line.
1653 We do display carets, however. */
1654 if (!out_state->draw_caret_p)
1655 if (column < first_non_ws || column > last_non_ws)
1656 return false;
1657
1658 /* We are within a range. */
1659 return true;
1660 }
1661 }
1662
1663 return false;
1664 }
1665
1666 /* Helper function for use by layout::print_line when printing the
1667 annotation line under the source line.
1668 Get the column beyond the rightmost one that could contain a caret or
1669 range marker, given that we stop rendering at trailing whitespace.
1670 ROW is the source line within the given file.
1671 CARET_COLUMN is the column of range 0's caret.
1672 LAST_NON_WS_COLUMN is the last column containing a non-whitespace
1673 character of source (as determined when printing the source line). */
1674
1675 int
1676 layout::get_x_bound_for_row (int row, int caret_column,
1677 int last_non_ws_column)
1678 {
1679 int result = caret_column + 1;
1680
1681 layout_range *range;
1682 int i;
1683 FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1684 {
1685 if (row >= range->m_start.m_line)
1686 {
1687 if (range->m_finish.m_line == row)
1688 {
1689 /* On the final line within a range; ensure that
1690 we render up to the end of the range. */
1691 if (result <= range->m_finish.m_column)
1692 result = range->m_finish.m_column + 1;
1693 }
1694 else if (row < range->m_finish.m_line)
1695 {
1696 /* Within a multiline range; ensure that we render up to the
1697 last non-whitespace column. */
1698 if (result <= last_non_ws_column)
1699 result = last_non_ws_column + 1;
1700 }
1701 }
1702 }
1703
1704 return result;
1705 }
1706
1707 /* Given *COLUMN as an x-coordinate, print spaces to position
1708 successive output at DEST_COLUMN, printing a newline if necessary,
1709 and updating *COLUMN. */
1710
1711 void
1712 layout::move_to_column (int *column, int dest_column)
1713 {
1714 /* Start a new line if we need to. */
1715 if (*column > dest_column)
1716 {
1717 print_newline ();
1718 *column = 0;
1719 }
1720
1721 while (*column < dest_column)
1722 {
1723 pp_space (m_pp);
1724 (*column)++;
1725 }
1726 }
1727
1728 /* For debugging layout issues, render a ruler giving column numbers
1729 (after the 1-column indent). */
1730
1731 void
1732 layout::show_ruler (int max_column) const
1733 {
1734 /* Hundreds. */
1735 if (max_column > 99)
1736 {
1737 pp_space (m_pp);
1738 for (int column = 1 + m_x_offset; column <= max_column; column++)
1739 if (0 == column % 10)
1740 pp_character (m_pp, '0' + (column / 100) % 10);
1741 else
1742 pp_space (m_pp);
1743 pp_newline (m_pp);
1744 }
1745
1746 /* Tens. */
1747 pp_space (m_pp);
1748 for (int column = 1 + m_x_offset; column <= max_column; column++)
1749 if (0 == column % 10)
1750 pp_character (m_pp, '0' + (column / 10) % 10);
1751 else
1752 pp_space (m_pp);
1753 pp_newline (m_pp);
1754
1755 /* Units. */
1756 pp_space (m_pp);
1757 for (int column = 1 + m_x_offset; column <= max_column; column++)
1758 pp_character (m_pp, '0' + (column % 10));
1759 pp_newline (m_pp);
1760 }
1761
1762 /* Print leading fix-its (for new lines inserted before the source line)
1763 then the source line, followed by an annotation line
1764 consisting of any caret/underlines, then any fixits.
1765 If the source line can't be read, print nothing. */
1766 void
1767 layout::print_line (int row)
1768 {
1769 int line_width;
1770 const char *line = location_get_source_line (m_exploc.file, row,
1771 &line_width);
1772 if (!line)
1773 return;
1774
1775 line_bounds lbounds;
1776 print_leading_fixits (row);
1777 print_source_line (row, line, line_width, &lbounds);
1778 if (should_print_annotation_line_p (row))
1779 print_annotation_line (row, lbounds);
1780 print_trailing_fixits (row);
1781 }
1782
1783 } /* End of anonymous namespace. */
1784
1785 /* Print the physical source code corresponding to the location of
1786 this diagnostic, with additional annotations. */
1787
1788 void
1789 diagnostic_show_locus (diagnostic_context * context,
1790 rich_location *richloc,
1791 diagnostic_t diagnostic_kind)
1792 {
1793 pp_newline (context->printer);
1794
1795 location_t loc = richloc->get_loc ();
1796 /* Do nothing if source-printing has been disabled. */
1797 if (!context->show_caret)
1798 return;
1799
1800 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
1801 if (loc <= BUILTINS_LOCATION)
1802 return;
1803
1804 /* Don't print the same source location twice in a row, unless we have
1805 fix-it hints. */
1806 if (loc == context->last_location
1807 && richloc->get_num_fixit_hints () == 0)
1808 return;
1809
1810 context->last_location = loc;
1811
1812 const char *saved_prefix = pp_get_prefix (context->printer);
1813 pp_set_prefix (context->printer, NULL);
1814
1815 layout layout (context, richloc, diagnostic_kind);
1816 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
1817 line_span_idx++)
1818 {
1819 const line_span *line_span = layout.get_line_span (line_span_idx);
1820 if (layout.print_heading_for_line_span_index_p (line_span_idx))
1821 {
1822 expanded_location exploc = layout.get_expanded_location (line_span);
1823 context->start_span (context, exploc);
1824 }
1825 int last_line = line_span->get_last_line ();
1826 for (int row = line_span->get_first_line (); row <= last_line; row++)
1827 layout.print_line (row);
1828 }
1829
1830 pp_set_prefix (context->printer, saved_prefix);
1831 }
1832
1833 #if CHECKING_P
1834
1835 namespace selftest {
1836
1837 /* Selftests for diagnostic_show_locus. */
1838
1839 /* Convenience subclass of diagnostic_context for testing
1840 diagnostic_show_locus. */
1841
1842 class test_diagnostic_context : public diagnostic_context
1843 {
1844 public:
1845 test_diagnostic_context ()
1846 {
1847 diagnostic_initialize (this, 0);
1848 show_caret = true;
1849 show_column = true;
1850 start_span = start_span_cb;
1851 }
1852 ~test_diagnostic_context ()
1853 {
1854 diagnostic_finish (this);
1855 }
1856
1857 /* Implementation of diagnostic_start_span_fn, hiding the
1858 real filename (to avoid printing the names of tempfiles). */
1859 static void
1860 start_span_cb (diagnostic_context *context, expanded_location exploc)
1861 {
1862 exploc.file = "FILENAME";
1863 default_diagnostic_start_span_fn (context, exploc);
1864 }
1865 };
1866
1867 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
1868
1869 static void
1870 test_diagnostic_show_locus_unknown_location ()
1871 {
1872 test_diagnostic_context dc;
1873 rich_location richloc (line_table, UNKNOWN_LOCATION);
1874 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1875 ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
1876 }
1877
1878 /* Verify that diagnostic_show_locus works sanely for various
1879 single-line cases.
1880
1881 All of these work on the following 1-line source file:
1882 .0000000001111111
1883 .1234567890123456
1884 "foo = bar.field;\n"
1885 which is set up by test_diagnostic_show_locus_one_liner and calls
1886 them. */
1887
1888 /* Just a caret. */
1889
1890 static void
1891 test_one_liner_simple_caret ()
1892 {
1893 test_diagnostic_context dc;
1894 location_t caret = linemap_position_for_column (line_table, 10);
1895 rich_location richloc (line_table, caret);
1896 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1897 ASSERT_STREQ ("\n"
1898 " foo = bar.field;\n"
1899 " ^\n",
1900 pp_formatted_text (dc.printer));
1901 }
1902
1903 /* Caret and range. */
1904
1905 static void
1906 test_one_liner_caret_and_range ()
1907 {
1908 test_diagnostic_context dc;
1909 location_t caret = linemap_position_for_column (line_table, 10);
1910 location_t start = linemap_position_for_column (line_table, 7);
1911 location_t finish = linemap_position_for_column (line_table, 15);
1912 location_t loc = make_location (caret, start, finish);
1913 rich_location richloc (line_table, loc);
1914 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1915 ASSERT_STREQ ("\n"
1916 " foo = bar.field;\n"
1917 " ~~~^~~~~~\n",
1918 pp_formatted_text (dc.printer));
1919 }
1920
1921 /* Multiple ranges and carets. */
1922
1923 static void
1924 test_one_liner_multiple_carets_and_ranges ()
1925 {
1926 test_diagnostic_context dc;
1927 location_t foo
1928 = make_location (linemap_position_for_column (line_table, 2),
1929 linemap_position_for_column (line_table, 1),
1930 linemap_position_for_column (line_table, 3));
1931 dc.caret_chars[0] = 'A';
1932
1933 location_t bar
1934 = make_location (linemap_position_for_column (line_table, 8),
1935 linemap_position_for_column (line_table, 7),
1936 linemap_position_for_column (line_table, 9));
1937 dc.caret_chars[1] = 'B';
1938
1939 location_t field
1940 = make_location (linemap_position_for_column (line_table, 13),
1941 linemap_position_for_column (line_table, 11),
1942 linemap_position_for_column (line_table, 15));
1943 dc.caret_chars[2] = 'C';
1944
1945 rich_location richloc (line_table, foo);
1946 richloc.add_range (bar, true);
1947 richloc.add_range (field, true);
1948 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1949 ASSERT_STREQ ("\n"
1950 " foo = bar.field;\n"
1951 " ~A~ ~B~ ~~C~~\n",
1952 pp_formatted_text (dc.printer));
1953 }
1954
1955 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
1956
1957 static void
1958 test_one_liner_fixit_insert_before ()
1959 {
1960 test_diagnostic_context dc;
1961 location_t caret = linemap_position_for_column (line_table, 7);
1962 rich_location richloc (line_table, caret);
1963 richloc.add_fixit_insert_before ("&");
1964 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1965 ASSERT_STREQ ("\n"
1966 " foo = bar.field;\n"
1967 " ^\n"
1968 " &\n",
1969 pp_formatted_text (dc.printer));
1970 }
1971
1972 /* Insertion fix-it hint: adding a "[0]" after "foo". */
1973
1974 static void
1975 test_one_liner_fixit_insert_after ()
1976 {
1977 test_diagnostic_context dc;
1978 location_t start = linemap_position_for_column (line_table, 1);
1979 location_t finish = linemap_position_for_column (line_table, 3);
1980 location_t foo = make_location (start, start, finish);
1981 rich_location richloc (line_table, foo);
1982 richloc.add_fixit_insert_after ("[0]");
1983 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
1984 ASSERT_STREQ ("\n"
1985 " foo = bar.field;\n"
1986 " ^~~\n"
1987 " [0]\n",
1988 pp_formatted_text (dc.printer));
1989 }
1990
1991 /* Removal fix-it hint: removal of the ".field". */
1992
1993 static void
1994 test_one_liner_fixit_remove ()
1995 {
1996 test_diagnostic_context dc;
1997 location_t start = linemap_position_for_column (line_table, 10);
1998 location_t finish = linemap_position_for_column (line_table, 15);
1999 location_t dot = make_location (start, start, finish);
2000 rich_location richloc (line_table, dot);
2001 richloc.add_fixit_remove ();
2002 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2003 ASSERT_STREQ ("\n"
2004 " foo = bar.field;\n"
2005 " ^~~~~~\n"
2006 " ------\n",
2007 pp_formatted_text (dc.printer));
2008 }
2009
2010 /* Replace fix-it hint: replacing "field" with "m_field". */
2011
2012 static void
2013 test_one_liner_fixit_replace ()
2014 {
2015 test_diagnostic_context dc;
2016 location_t start = linemap_position_for_column (line_table, 11);
2017 location_t finish = linemap_position_for_column (line_table, 15);
2018 location_t field = make_location (start, start, finish);
2019 rich_location richloc (line_table, field);
2020 richloc.add_fixit_replace ("m_field");
2021 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2022 ASSERT_STREQ ("\n"
2023 " foo = bar.field;\n"
2024 " ^~~~~\n"
2025 " m_field\n",
2026 pp_formatted_text (dc.printer));
2027 }
2028
2029 /* Replace fix-it hint: replacing "field" with "m_field",
2030 but where the caret was elsewhere. */
2031
2032 static void
2033 test_one_liner_fixit_replace_non_equal_range ()
2034 {
2035 test_diagnostic_context dc;
2036 location_t equals = linemap_position_for_column (line_table, 5);
2037 location_t start = linemap_position_for_column (line_table, 11);
2038 location_t finish = linemap_position_for_column (line_table, 15);
2039 rich_location richloc (line_table, equals);
2040 source_range range;
2041 range.m_start = start;
2042 range.m_finish = finish;
2043 richloc.add_fixit_replace (range, "m_field");
2044 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2045 /* The replacement range is not indicated in the annotation line, so
2046 it should be indicated via an additional underline. */
2047 ASSERT_STREQ ("\n"
2048 " foo = bar.field;\n"
2049 " ^\n"
2050 " -----\n"
2051 " m_field\n",
2052 pp_formatted_text (dc.printer));
2053 }
2054
2055 /* Replace fix-it hint: replacing "field" with "m_field",
2056 where the caret was elsewhere, but where a secondary range
2057 exactly covers "field". */
2058
2059 static void
2060 test_one_liner_fixit_replace_equal_secondary_range ()
2061 {
2062 test_diagnostic_context dc;
2063 location_t equals = linemap_position_for_column (line_table, 5);
2064 location_t start = linemap_position_for_column (line_table, 11);
2065 location_t finish = linemap_position_for_column (line_table, 15);
2066 rich_location richloc (line_table, equals);
2067 location_t field = make_location (start, start, finish);
2068 richloc.add_range (field, false);
2069 richloc.add_fixit_replace (field, "m_field");
2070 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2071 /* The replacement range is indicated in the annotation line,
2072 so it shouldn't be indicated via an additional underline. */
2073 ASSERT_STREQ ("\n"
2074 " foo = bar.field;\n"
2075 " ^ ~~~~~\n"
2076 " m_field\n",
2077 pp_formatted_text (dc.printer));
2078 }
2079
2080 /* Verify that we can use ad-hoc locations when adding fixits to a
2081 rich_location. */
2082
2083 static void
2084 test_one_liner_fixit_validation_adhoc_locations ()
2085 {
2086 /* Generate a range that's too long to be packed, so must
2087 be stored as an ad-hoc location (given the defaults
2088 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
2089 const location_t c7 = linemap_position_for_column (line_table, 7);
2090 const location_t c47 = linemap_position_for_column (line_table, 47);
2091 const location_t loc = make_location (c7, c7, c47);
2092
2093 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2094 return;
2095
2096 ASSERT_TRUE (IS_ADHOC_LOC (loc));
2097
2098 /* Insert. */
2099 {
2100 rich_location richloc (line_table, loc);
2101 richloc.add_fixit_insert_before (loc, "test");
2102 /* It should not have been discarded by the validator. */
2103 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2104
2105 test_diagnostic_context dc;
2106 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2107 ASSERT_STREQ ("\n"
2108 " foo = bar.field;\n"
2109 " ^~~~~~~~~~ \n"
2110 " test\n",
2111 pp_formatted_text (dc.printer));
2112 }
2113
2114 /* Remove. */
2115 {
2116 rich_location richloc (line_table, loc);
2117 source_range range = source_range::from_locations (loc, c47);
2118 richloc.add_fixit_remove (range);
2119 /* It should not have been discarded by the validator. */
2120 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2121
2122 test_diagnostic_context dc;
2123 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2124 ASSERT_STREQ ("\n"
2125 " foo = bar.field;\n"
2126 " ^~~~~~~~~~ \n"
2127 " -----------------------------------------\n",
2128 pp_formatted_text (dc.printer));
2129 }
2130
2131 /* Replace. */
2132 {
2133 rich_location richloc (line_table, loc);
2134 source_range range = source_range::from_locations (loc, c47);
2135 richloc.add_fixit_replace (range, "test");
2136 /* It should not have been discarded by the validator. */
2137 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2138
2139 test_diagnostic_context dc;
2140 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2141 ASSERT_STREQ ("\n"
2142 " foo = bar.field;\n"
2143 " ^~~~~~~~~~ \n"
2144 " test\n",
2145 pp_formatted_text (dc.printer));
2146 }
2147 }
2148
2149 /* Test of consolidating insertions at the same location. */
2150
2151 static void
2152 test_one_liner_many_fixits_1 ()
2153 {
2154 test_diagnostic_context dc;
2155 location_t equals = linemap_position_for_column (line_table, 5);
2156 rich_location richloc (line_table, equals);
2157 for (int i = 0; i < 19; i++)
2158 richloc.add_fixit_insert_before ("a");
2159 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2160 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2161 ASSERT_STREQ ("\n"
2162 " foo = bar.field;\n"
2163 " ^\n"
2164 " aaaaaaaaaaaaaaaaaaa\n",
2165 pp_formatted_text (dc.printer));
2166 }
2167
2168 /* Ensure that we can add an arbitrary number of fix-it hints to a
2169 rich_location, even if they are not consolidated. */
2170
2171 static void
2172 test_one_liner_many_fixits_2 ()
2173 {
2174 test_diagnostic_context dc;
2175 location_t equals = linemap_position_for_column (line_table, 5);
2176 rich_location richloc (line_table, equals);
2177 for (int i = 0; i < 19; i++)
2178 {
2179 location_t loc = linemap_position_for_column (line_table, i * 2);
2180 richloc.add_fixit_insert_before (loc, "a");
2181 }
2182 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2183 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2184 ASSERT_STREQ ("\n"
2185 " foo = bar.field;\n"
2186 " ^\n"
2187 "a a a a a a a a a a a a a a a a a a a\n",
2188 pp_formatted_text (dc.printer));
2189 }
2190
2191 /* Run the various one-liner tests. */
2192
2193 static void
2194 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2195 {
2196 /* Create a tempfile and write some text to it.
2197 ....................0000000001111111.
2198 ....................1234567890123456. */
2199 const char *content = "foo = bar.field;\n";
2200 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2201 line_table_test ltt (case_);
2202
2203 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2204
2205 location_t line_end = linemap_position_for_column (line_table, 16);
2206
2207 /* Don't attempt to run the tests if column data might be unavailable. */
2208 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2209 return;
2210
2211 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2212 ASSERT_EQ (1, LOCATION_LINE (line_end));
2213 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2214
2215 test_one_liner_simple_caret ();
2216 test_one_liner_caret_and_range ();
2217 test_one_liner_multiple_carets_and_ranges ();
2218 test_one_liner_fixit_insert_before ();
2219 test_one_liner_fixit_insert_after ();
2220 test_one_liner_fixit_remove ();
2221 test_one_liner_fixit_replace ();
2222 test_one_liner_fixit_replace_non_equal_range ();
2223 test_one_liner_fixit_replace_equal_secondary_range ();
2224 test_one_liner_fixit_validation_adhoc_locations ();
2225 test_one_liner_many_fixits_1 ();
2226 test_one_liner_many_fixits_2 ();
2227 }
2228
2229 /* Verify that we print fixits even if they only affect lines
2230 outside those covered by the ranges in the rich_location. */
2231
2232 static void
2233 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2234 {
2235 /* Create a tempfile and write some text to it.
2236 ...000000000111111111122222222223333333333.
2237 ...123456789012345678901234567890123456789. */
2238 const char *content
2239 = ("struct point { double x; double y; };\n" /* line 1. */
2240 "struct point origin = {x: 0.0,\n" /* line 2. */
2241 " y\n" /* line 3. */
2242 "\n" /* line 4. */
2243 "\n" /* line 5. */
2244 " : 0.0};\n"); /* line 6. */
2245 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2246 line_table_test ltt (case_);
2247
2248 const line_map_ordinary *ord_map
2249 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2250 tmp.get_filename (), 0));
2251
2252 linemap_line_start (line_table, 1, 100);
2253
2254 const location_t final_line_end
2255 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2256
2257 /* Don't attempt to run the tests if column data might be unavailable. */
2258 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2259 return;
2260
2261 /* A pair of tests for modernizing the initializers to C99-style. */
2262
2263 /* The one-liner case (line 2). */
2264 {
2265 test_diagnostic_context dc;
2266 const location_t x
2267 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2268 const location_t colon
2269 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2270 rich_location richloc (line_table, colon);
2271 richloc.add_fixit_insert_before (x, ".");
2272 richloc.add_fixit_replace (colon, "=");
2273 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2274 ASSERT_STREQ ("\n"
2275 " struct point origin = {x: 0.0,\n"
2276 " ^\n"
2277 " .=\n",
2278 pp_formatted_text (dc.printer));
2279 }
2280
2281 /* The multiline case. The caret for the rich_location is on line 6;
2282 verify that insertion fixit on line 3 is still printed (and that
2283 span starts are printed due to the gap between the span at line 3
2284 and that at line 6). */
2285 {
2286 test_diagnostic_context dc;
2287 const location_t y
2288 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2289 const location_t colon
2290 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2291 rich_location richloc (line_table, colon);
2292 richloc.add_fixit_insert_before (y, ".");
2293 richloc.add_fixit_replace (colon, "=");
2294 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2295 ASSERT_STREQ ("\n"
2296 "FILENAME:3:24:\n"
2297 " y\n"
2298 " .\n"
2299 "FILENAME:6:25:\n"
2300 " : 0.0};\n"
2301 " ^\n"
2302 " =\n",
2303 pp_formatted_text (dc.printer));
2304 }
2305 }
2306
2307
2308 /* Verify that fix-it hints are appropriately consolidated.
2309
2310 If any fix-it hints in a rich_location involve locations beyond
2311 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
2312 the fix-it as a whole, so there should be none.
2313
2314 Otherwise, verify that consecutive "replace" and "remove" fix-its
2315 are merged, and that other fix-its remain separate. */
2316
2317 static void
2318 test_fixit_consolidation (const line_table_case &case_)
2319 {
2320 line_table_test ltt (case_);
2321
2322 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
2323
2324 const location_t c10 = linemap_position_for_column (line_table, 10);
2325 const location_t c15 = linemap_position_for_column (line_table, 15);
2326 const location_t c16 = linemap_position_for_column (line_table, 16);
2327 const location_t c17 = linemap_position_for_column (line_table, 17);
2328 const location_t c20 = linemap_position_for_column (line_table, 20);
2329 const location_t c21 = linemap_position_for_column (line_table, 21);
2330 const location_t caret = c10;
2331
2332 /* Insert + insert. */
2333 {
2334 rich_location richloc (line_table, caret);
2335 richloc.add_fixit_insert_before (c10, "foo");
2336 richloc.add_fixit_insert_before (c15, "bar");
2337
2338 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2339 /* Bogus column info for 2nd fixit, so no fixits. */
2340 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2341 else
2342 /* They should not have been merged. */
2343 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2344 }
2345
2346 /* Insert + replace. */
2347 {
2348 rich_location richloc (line_table, caret);
2349 richloc.add_fixit_insert_before (c10, "foo");
2350 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
2351 "bar");
2352
2353 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2354 /* Bogus column info for 2nd fixit, so no fixits. */
2355 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2356 else
2357 /* They should not have been merged. */
2358 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2359 }
2360
2361 /* Replace + non-consecutive insert. */
2362 {
2363 rich_location richloc (line_table, caret);
2364 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2365 "bar");
2366 richloc.add_fixit_insert_before (c17, "foo");
2367
2368 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2369 /* Bogus column info for 2nd fixit, so no fixits. */
2370 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2371 else
2372 /* They should not have been merged. */
2373 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2374 }
2375
2376 /* Replace + non-consecutive replace. */
2377 {
2378 rich_location richloc (line_table, caret);
2379 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2380 "foo");
2381 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
2382 "bar");
2383
2384 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2385 /* Bogus column info for 2nd fixit, so no fixits. */
2386 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2387 else
2388 /* They should not have been merged. */
2389 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2390 }
2391
2392 /* Replace + consecutive replace. */
2393 {
2394 rich_location richloc (line_table, caret);
2395 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2396 "foo");
2397 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
2398 "bar");
2399
2400 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2401 /* Bogus column info for 2nd fixit, so no fixits. */
2402 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2403 else
2404 {
2405 /* They should have been merged into a single "replace". */
2406 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2407 const fixit_hint *hint = richloc.get_fixit_hint (0);
2408 ASSERT_STREQ ("foobar", hint->get_string ());
2409 ASSERT_EQ (c10, hint->get_start_loc ());
2410 ASSERT_EQ (c21, hint->get_next_loc ());
2411 }
2412 }
2413
2414 /* Replace + consecutive removal. */
2415 {
2416 rich_location richloc (line_table, caret);
2417 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
2418 "foo");
2419 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2420
2421 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2422 /* Bogus column info for 2nd fixit, so no fixits. */
2423 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2424 else
2425 {
2426 /* They should have been merged into a single replace, with the
2427 range extended to cover that of the removal. */
2428 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2429 const fixit_hint *hint = richloc.get_fixit_hint (0);
2430 ASSERT_STREQ ("foo", hint->get_string ());
2431 ASSERT_EQ (c10, hint->get_start_loc ());
2432 ASSERT_EQ (c21, hint->get_next_loc ());
2433 }
2434 }
2435
2436 /* Consecutive removals. */
2437 {
2438 rich_location richloc (line_table, caret);
2439 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
2440 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
2441
2442 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2443 /* Bogus column info for 2nd fixit, so no fixits. */
2444 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
2445 else
2446 {
2447 /* They should have been merged into a single "replace-with-empty". */
2448 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2449 const fixit_hint *hint = richloc.get_fixit_hint (0);
2450 ASSERT_STREQ ("", hint->get_string ());
2451 ASSERT_EQ (c10, hint->get_start_loc ());
2452 ASSERT_EQ (c21, hint->get_next_loc ());
2453 }
2454 }
2455 }
2456
2457 /* Verify that the line_corrections machinery correctly prints
2458 overlapping fixit-hints. */
2459
2460 static void
2461 test_overlapped_fixit_printing (const line_table_case &case_)
2462 {
2463 /* Create a tempfile and write some text to it.
2464 ...000000000111111111122222222223333333333.
2465 ...123456789012345678901234567890123456789. */
2466 const char *content
2467 = (" foo *f = (foo *)ptr->field;\n");
2468 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
2469 line_table_test ltt (case_);
2470
2471 const line_map_ordinary *ord_map
2472 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2473 tmp.get_filename (), 0));
2474
2475 linemap_line_start (line_table, 1, 100);
2476
2477 const location_t final_line_end
2478 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2479
2480 /* Don't attempt to run the tests if column data might be unavailable. */
2481 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2482 return;
2483
2484 /* A test for converting a C-style cast to a C++-style cast. */
2485 const location_t open_paren
2486 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
2487 const location_t close_paren
2488 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2489 const location_t expr_start
2490 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
2491 const location_t expr_finish
2492 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
2493 const location_t expr = make_location (expr_start, expr_start, expr_finish);
2494
2495 /* Various examples of fix-it hints that aren't themselves consolidated,
2496 but for which the *printing* may need consolidation. */
2497
2498 /* Example where 3 fix-it hints are printed as one. */
2499 {
2500 test_diagnostic_context dc;
2501 rich_location richloc (line_table, expr);
2502 richloc.add_fixit_replace (open_paren, "const_cast<");
2503 richloc.add_fixit_replace (close_paren, "> (");
2504 richloc.add_fixit_insert_after (")");
2505
2506 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2507 ASSERT_STREQ ("\n"
2508 " foo *f = (foo *)ptr->field;\n"
2509 " ^~~~~~~~~~\n"
2510 " -----------------\n"
2511 " const_cast<foo *> (ptr->field)\n",
2512 pp_formatted_text (dc.printer));
2513
2514 /* Unit-test the line_corrections machinery. */
2515 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
2516 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
2517 ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
2518 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
2519 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
2520 ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
2521 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
2522 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
2523 ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
2524 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
2525
2526 /* Add each hint in turn to a line_corrections instance,
2527 and verify that they are consolidated into one correction instance
2528 as expected. */
2529 line_corrections lc (tmp.get_filename (), 1);
2530
2531 /* The first replace hint by itself. */
2532 lc.add_hint (hint_0);
2533 ASSERT_EQ (1, lc.m_corrections.length ());
2534 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
2535 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
2536 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
2537
2538 /* After the second replacement hint, they are printed together
2539 as a replacement (along with the text between them). */
2540 lc.add_hint (hint_1);
2541 ASSERT_EQ (1, lc.m_corrections.length ());
2542 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
2543 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
2544 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
2545
2546 /* After the final insertion hint, they are all printed together
2547 as a replacement (along with the text between them). */
2548 lc.add_hint (hint_2);
2549 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
2550 lc.m_corrections[0]->m_text);
2551 ASSERT_EQ (1, lc.m_corrections.length ());
2552 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
2553 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
2554 }
2555
2556 /* Example where two are consolidated during printing. */
2557 {
2558 test_diagnostic_context dc;
2559 rich_location richloc (line_table, expr);
2560 richloc.add_fixit_replace (open_paren, "CAST (");
2561 richloc.add_fixit_replace (close_paren, ") (");
2562 richloc.add_fixit_insert_after (")");
2563
2564 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2565 ASSERT_STREQ ("\n"
2566 " foo *f = (foo *)ptr->field;\n"
2567 " ^~~~~~~~~~\n"
2568 " -\n"
2569 " CAST (-\n"
2570 " ) ( )\n",
2571 pp_formatted_text (dc.printer));
2572 }
2573
2574 /* Example where none are consolidated during printing. */
2575 {
2576 test_diagnostic_context dc;
2577 rich_location richloc (line_table, expr);
2578 richloc.add_fixit_replace (open_paren, "CST (");
2579 richloc.add_fixit_replace (close_paren, ") (");
2580 richloc.add_fixit_insert_after (")");
2581
2582 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2583 ASSERT_STREQ ("\n"
2584 " foo *f = (foo *)ptr->field;\n"
2585 " ^~~~~~~~~~\n"
2586 " -\n"
2587 " CST ( -\n"
2588 " ) ( )\n",
2589 pp_formatted_text (dc.printer));
2590 }
2591
2592 /* Example of deletion fix-it hints. */
2593 {
2594 test_diagnostic_context dc;
2595 rich_location richloc (line_table, expr);
2596 richloc.add_fixit_insert_before (open_paren, "(bar *)");
2597 source_range victim = {open_paren, close_paren};
2598 richloc.add_fixit_remove (victim);
2599
2600 /* This case is actually handled by fixit-consolidation,
2601 rather than by line_corrections. */
2602 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2603
2604 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2605 ASSERT_STREQ ("\n"
2606 " foo *f = (foo *)ptr->field;\n"
2607 " ^~~~~~~~~~\n"
2608 " -------\n"
2609 " (bar *)\n",
2610 pp_formatted_text (dc.printer));
2611 }
2612
2613 /* Example of deletion fix-it hints that would overlap. */
2614 {
2615 test_diagnostic_context dc;
2616 rich_location richloc (line_table, expr);
2617 richloc.add_fixit_insert_before (open_paren, "(longer *)");
2618 source_range victim = {expr_start, expr_finish};
2619 richloc.add_fixit_remove (victim);
2620
2621 /* These fixits are not consolidated. */
2622 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
2623
2624 /* But the corrections are. */
2625 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2626 ASSERT_STREQ ("\n"
2627 " foo *f = (foo *)ptr->field;\n"
2628 " ^~~~~~~~~~\n"
2629 " -----------------\n"
2630 " (longer *)(foo *)\n",
2631 pp_formatted_text (dc.printer));
2632 }
2633
2634 /* Example of insertion fix-it hints that would overlap. */
2635 {
2636 test_diagnostic_context dc;
2637 rich_location richloc (line_table, expr);
2638 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
2639 richloc.add_fixit_insert_after (close_paren, "TEST");
2640
2641 /* The first insertion is long enough that if printed naively,
2642 it would overlap with the second.
2643 Verify that they are printed as a single replacement. */
2644 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2645 ASSERT_STREQ ("\n"
2646 " foo *f = (foo *)ptr->field;\n"
2647 " ^~~~~~~~~~\n"
2648 " -------\n"
2649 " LONGER THAN THE CAST(foo *)TEST\n",
2650 pp_formatted_text (dc.printer));
2651 }
2652 }
2653
2654 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
2655
2656 static void
2657 test_fixit_insert_containing_newline (const line_table_case &case_)
2658 {
2659 /* Create a tempfile and write some text to it.
2660 .........................0000000001111111.
2661 .........................1234567890123456. */
2662 const char *old_content = (" case 'a':\n" /* line 1. */
2663 " x = a;\n" /* line 2. */
2664 " case 'b':\n" /* line 3. */
2665 " x = b;\n");/* line 4. */
2666
2667 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2668 line_table_test ltt (case_);
2669 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
2670
2671 location_t case_start = linemap_position_for_column (line_table, 5);
2672 location_t case_finish = linemap_position_for_column (line_table, 13);
2673 location_t case_loc = make_location (case_start, case_start, case_finish);
2674 location_t line_start = linemap_position_for_column (line_table, 1);
2675
2676 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2677 return;
2678
2679 /* Add a "break;" on a line by itself before line 3 i.e. before
2680 column 1 of line 3. */
2681 {
2682 rich_location richloc (line_table, case_loc);
2683 richloc.add_fixit_insert_before (line_start, " break;\n");
2684 test_diagnostic_context dc;
2685 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2686 ASSERT_STREQ ("\n"
2687 "+ break;\n"
2688 " case 'b':\n"
2689 " ^~~~~~~~~\n",
2690 pp_formatted_text (dc.printer));
2691 }
2692
2693 /* Verify that attempts to add text with a newline fail when the
2694 insertion point is *not* at the start of a line. */
2695 {
2696 rich_location richloc (line_table, case_loc);
2697 richloc.add_fixit_insert_before (case_start, "break;\n");
2698 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2699 test_diagnostic_context dc;
2700 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2701 ASSERT_STREQ ("\n"
2702 " case 'b':\n"
2703 " ^~~~~~~~~\n",
2704 pp_formatted_text (dc.printer));
2705 }
2706 }
2707
2708 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
2709 of the file, where the fix-it is printed in a different line-span
2710 to the primary range of the diagnostic. */
2711
2712 static void
2713 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
2714 {
2715 /* Create a tempfile and write some text to it.
2716 .........................0000000001111111.
2717 .........................1234567890123456. */
2718 const char *old_content = ("test (int ch)\n" /* line 1. */
2719 "{\n" /* line 2. */
2720 " putchar (ch);\n" /* line 3. */
2721 "}\n"); /* line 4. */
2722
2723 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2724 line_table_test ltt (case_);
2725
2726 const line_map_ordinary *ord_map = linemap_check_ordinary
2727 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
2728 linemap_line_start (line_table, 1, 100);
2729
2730 /* The primary range is the "putchar" token. */
2731 location_t putchar_start
2732 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
2733 location_t putchar_finish
2734 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
2735 location_t putchar_loc
2736 = make_location (putchar_start, putchar_start, putchar_finish);
2737 rich_location richloc (line_table, putchar_loc);
2738
2739 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
2740 location_t file_start
2741 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
2742 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
2743
2744 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2745 return;
2746
2747 test_diagnostic_context dc;
2748 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2749 ASSERT_STREQ ("\n"
2750 "FILENAME:1:1:\n"
2751 "+#include <stdio.h>\n"
2752 " test (int ch)\n"
2753 "FILENAME:3:2:\n"
2754 " putchar (ch);\n"
2755 " ^~~~~~~\n",
2756 pp_formatted_text (dc.printer));
2757 }
2758
2759 /* Replacement fix-it hint containing a newline.
2760 This will fail, as newlines are only supported when inserting at the
2761 beginning of a line. */
2762
2763 static void
2764 test_fixit_replace_containing_newline (const line_table_case &case_)
2765 {
2766 /* Create a tempfile and write some text to it.
2767 .........................0000000001111.
2768 .........................1234567890123. */
2769 const char *old_content = "foo = bar ();\n";
2770
2771 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2772 line_table_test ltt (case_);
2773 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2774
2775 /* Replace the " = " with "\n = ", as if we were reformatting an
2776 overly long line. */
2777 location_t start = linemap_position_for_column (line_table, 4);
2778 location_t finish = linemap_position_for_column (line_table, 6);
2779 location_t loc = linemap_position_for_column (line_table, 13);
2780 rich_location richloc (line_table, loc);
2781 source_range range = source_range::from_locations (start, finish);
2782 richloc.add_fixit_replace (range, "\n =");
2783
2784 /* Arbitrary newlines are not yet supported within fix-it hints, so
2785 the fix-it should not be displayed. */
2786 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2787
2788 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2789 return;
2790
2791 test_diagnostic_context dc;
2792 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2793 ASSERT_STREQ ("\n"
2794 " foo = bar ();\n"
2795 " ^\n",
2796 pp_formatted_text (dc.printer));
2797 }
2798
2799 /* Fix-it hint, attempting to delete a newline.
2800 This will fail, as we currently only support fix-it hints that
2801 affect one line at a time. */
2802
2803 static void
2804 test_fixit_deletion_affecting_newline (const line_table_case &case_)
2805 {
2806 /* Create a tempfile and write some text to it.
2807 ..........................0000000001111.
2808 ..........................1234567890123. */
2809 const char *old_content = ("foo = bar (\n"
2810 " );\n");
2811
2812 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
2813 line_table_test ltt (case_);
2814 const line_map_ordinary *ord_map = linemap_check_ordinary
2815 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
2816 linemap_line_start (line_table, 1, 100);
2817
2818 /* Attempt to delete the " (\n...)". */
2819 location_t start
2820 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
2821 location_t caret
2822 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
2823 location_t finish
2824 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
2825 location_t loc = make_location (caret, start, finish);
2826 rich_location richloc (line_table, loc);
2827 richloc. add_fixit_remove ();
2828
2829 /* Fix-it hints that affect more than one line are not yet supported, so
2830 the fix-it should not be displayed. */
2831 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
2832
2833 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
2834 return;
2835
2836 test_diagnostic_context dc;
2837 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2838 ASSERT_STREQ ("\n"
2839 " foo = bar (\n"
2840 " ~^\n"
2841 " );\n"
2842 " ~ \n",
2843 pp_formatted_text (dc.printer));
2844 }
2845
2846 /* Run all of the selftests within this file. */
2847
2848 void
2849 diagnostic_show_locus_c_tests ()
2850 {
2851 test_layout_range_for_single_point ();
2852 test_layout_range_for_single_line ();
2853 test_layout_range_for_multiple_lines ();
2854
2855 test_get_line_width_without_trailing_whitespace ();
2856
2857 test_diagnostic_show_locus_unknown_location ();
2858
2859 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
2860 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
2861 for_each_line_table_case (test_fixit_consolidation);
2862 for_each_line_table_case (test_overlapped_fixit_printing);
2863 for_each_line_table_case (test_fixit_insert_containing_newline);
2864 for_each_line_table_case (test_fixit_insert_containing_newline_2);
2865 for_each_line_table_case (test_fixit_replace_containing_newline);
2866 for_each_line_table_case (test_fixit_deletion_affecting_newline);
2867 }
2868
2869 } // namespace selftest
2870
2871 #endif /* #if CHECKING_P */