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