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