1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
5 This file is part of GCC.
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
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
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/>. */
23 #include "coretypes.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
32 #include "selftest-diagnostic.h"
39 #ifdef GWINSZ_IN_SYS_IOCTL
40 # include <sys/ioctl.h>
43 /* Disable warnings about quoting issues in the pp_xxx calls below
44 that (intentionally) don't follow GCC diagnostic conventions. */
46 # pragma GCC diagnostic push
47 # pragma GCC diagnostic ignored "-Wformat-diag"
50 /* Classes for rendering source code and diagnostics, within an
52 The work is done by "class layout", which embeds and uses
53 "class colorizer" and "class layout_range" to get things done. */
57 /* The state at a given point of the source code, assuming that we're
58 in a range: which range are we in, and whether we should draw a caret at
67 /* A class to inject colorization codes when printing the diagnostic locus.
69 It has one kind of colorization for each of:
71 - range 0 (the "primary location")
75 The class caches the lookup of the color codes for the above.
77 The class also has responsibility for tracking which of the above is
78 active, filtering out unnecessary changes. This allows
79 layout::print_source_line and layout::print_annotation_line
80 to simply request a colorization code for *every* character they print,
81 via this class, and have the filtering be done for them here. */
86 colorizer (diagnostic_context
*context
,
87 diagnostic_t diagnostic_kind
);
90 void set_range (int range_idx
)
92 /* Normally we emphasize the primary location, then alternate between
93 two colors for the secondary locations.
94 But if we're printing a run of events in a diagnostic path, that
95 makes no sense, so print all of them with the same colorization. */
96 if (m_diagnostic_kind
== DK_DIAGNOSTIC_PATH
)
99 set_state (range_idx
);
101 void set_normal_text () { set_state (STATE_NORMAL_TEXT
); }
102 void set_fixit_insert () { set_state (STATE_FIXIT_INSERT
); }
103 void set_fixit_delete () { set_state (STATE_FIXIT_DELETE
); }
106 void set_state (int state
);
107 void begin_state (int state
);
108 void finish_state (int state
);
109 const char *get_color_by_name (const char *);
112 static const int STATE_NORMAL_TEXT
= -1;
113 static const int STATE_FIXIT_INSERT
= -2;
114 static const int STATE_FIXIT_DELETE
= -3;
116 diagnostic_context
*m_context
;
117 diagnostic_t m_diagnostic_kind
;
119 const char *m_range1
;
120 const char *m_range2
;
121 const char *m_fixit_insert
;
122 const char *m_fixit_delete
;
123 const char *m_stop_color
;
126 /* In order to handle multibyte sources properly, all of this logic needs to be
127 aware of the distinction between the number of bytes and the number of
128 display columns occupied by a character, which are not the same for non-ASCII
129 characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
130 as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
131 display column when it is output. A typical emoji, such as U+1F602 (in
132 UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
134 The below example line, which is also used for selftests below, shows how the
135 display column and byte column are related:
137 0000000001111111111222222 display
138 1234567890123456789012345 columns
139 SS_foo = P_bar.SS_fieldP;
140 0000000111111111222222223 byte
141 1356789012456789134567891 columns
143 Here SS represents the two display columns for the U+1F602 emoji, and P
144 represents the one display column for the U+03C0 pi symbol. As an example, a
145 diagnostic pointing to the final P on this line is at byte column 29 and
146 display column 24. This reflects the fact that the three extended characters
147 before the final P occupy cumulatively 5 more bytes than they do display
148 columns (a difference of 2 for each of the two SSs, and one for the other P).
150 One or the other of the two column units is more useful depending on the
151 context. For instance, in order to output the caret at the correct location,
152 we need to count display columns; in order to colorize a source line, we need
153 to count the bytes. All locations are provided to us as byte counts, which
154 we augment with the display column on demand so that it can be used when
155 needed. This is not the most efficient way to do things since it requires
156 looping over the whole line each time, but it should be fine for the purpose
157 of outputting diagnostics.
159 In order to keep straight which units (byte or display) are in use at a
160 given time, the following enum lets us specify that explicitly. */
163 /* Measured in raw bytes. */
166 /* Measured in display units. */
169 /* For arrays indexed by column_unit. */
173 /* Utility class to augment an exploc with the corresponding display column. */
175 class exploc_with_display_col
: public expanded_location
178 exploc_with_display_col (const expanded_location
&exploc
, int tabstop
)
179 : expanded_location (exploc
),
180 m_display_col (location_compute_display_column (exploc
, tabstop
))
187 /* A point within a layout_range; similar to an exploc_with_display_col,
188 but after filtering on file. */
193 layout_point (const exploc_with_display_col
&exploc
)
194 : m_line (exploc
.line
)
196 m_columns
[CU_BYTES
] = exploc
.column
;
197 m_columns
[CU_DISPLAY_COLS
] = exploc
.m_display_col
;
201 int m_columns
[CU_NUM_UNITS
];
204 /* A class for use by "class layout" below: a filtered location_range. */
209 layout_range (const exploc_with_display_col
&start_exploc
,
210 const exploc_with_display_col
&finish_exploc
,
211 enum range_display_kind range_display_kind
,
212 const exploc_with_display_col
&caret_exploc
,
213 unsigned original_idx
,
214 const range_label
*label
);
216 bool contains_point (linenum_type row
, int column
,
217 enum column_unit col_unit
) const;
218 bool intersects_line_p (linenum_type row
) const;
220 layout_point m_start
;
221 layout_point m_finish
;
222 enum range_display_kind m_range_display_kind
;
223 layout_point m_caret
;
224 unsigned m_original_idx
;
225 const range_label
*m_label
;
228 /* A struct for use by layout::print_source_line for telling
229 layout::print_annotation_line the extents of the source line that
230 it printed, so that underlines can be clipped appropriately. Units
231 are 1-based display columns. */
235 int m_first_non_ws_disp_col
;
236 int m_last_non_ws_disp_col
;
240 m_first_non_ws_disp_col
= INT_MAX
;
241 m_last_non_ws_disp_col
= 0;
245 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
246 or "line 23"). During the layout ctor, layout::calculate_line_spans
247 splits the pertinent source lines into a list of disjoint line_span
248 instances (e.g. lines 5-10, lines 15-20, line 23). */
253 line_span (linenum_type first_line
, linenum_type last_line
)
254 : m_first_line (first_line
), m_last_line (last_line
)
256 gcc_assert (first_line
<= last_line
);
258 linenum_type
get_first_line () const { return m_first_line
; }
259 linenum_type
get_last_line () const { return m_last_line
; }
261 bool contains_line_p (linenum_type line
) const
263 return line
>= m_first_line
&& line
<= m_last_line
;
266 static int comparator (const void *p1
, const void *p2
)
268 const line_span
*ls1
= (const line_span
*)p1
;
269 const line_span
*ls2
= (const line_span
*)p2
;
270 int first_line_cmp
= compare (ls1
->m_first_line
, ls2
->m_first_line
);
272 return first_line_cmp
;
273 return compare (ls1
->m_last_line
, ls2
->m_last_line
);
276 linenum_type m_first_line
;
277 linenum_type m_last_line
;
282 /* Selftests for line_span. */
287 line_span
line_one (1, 1);
288 ASSERT_EQ (1, line_one
.get_first_line ());
289 ASSERT_EQ (1, line_one
.get_last_line ());
290 ASSERT_FALSE (line_one
.contains_line_p (0));
291 ASSERT_TRUE (line_one
.contains_line_p (1));
292 ASSERT_FALSE (line_one
.contains_line_p (2));
294 line_span
lines_1_to_3 (1, 3);
295 ASSERT_EQ (1, lines_1_to_3
.get_first_line ());
296 ASSERT_EQ (3, lines_1_to_3
.get_last_line ());
297 ASSERT_TRUE (lines_1_to_3
.contains_line_p (1));
298 ASSERT_TRUE (lines_1_to_3
.contains_line_p (3));
300 ASSERT_EQ (0, line_span::comparator (&line_one
, &line_one
));
301 ASSERT_GT (line_span::comparator (&lines_1_to_3
, &line_one
), 0);
302 ASSERT_LT (line_span::comparator (&line_one
, &lines_1_to_3
), 0);
304 /* A linenum > 2^31. */
305 const linenum_type LARGEST_LINE
= 0xffffffff;
306 line_span
largest_line (LARGEST_LINE
, LARGEST_LINE
);
307 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_first_line ());
308 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_last_line ());
310 ASSERT_GT (line_span::comparator (&largest_line
, &line_one
), 0);
311 ASSERT_LT (line_span::comparator (&line_one
, &largest_line
), 0);
314 #endif /* #if CHECKING_P */
316 /* A class to control the overall layout when printing a diagnostic.
318 The layout is determined within the constructor.
319 It is then printed by repeatedly calling the "print_source_line",
320 "print_annotation_line" and "print_any_fixits" methods.
322 We assume we have disjoint ranges. */
327 layout (diagnostic_context
*context
,
328 rich_location
*richloc
,
329 diagnostic_t diagnostic_kind
);
331 bool maybe_add_location_range (const location_range
*loc_range
,
332 unsigned original_idx
,
333 bool restrict_to_current_line_spans
);
335 int get_num_line_spans () const { return m_line_spans
.length (); }
336 const line_span
*get_line_span (int idx
) const { return &m_line_spans
[idx
]; }
338 int get_linenum_width () const { return m_linenum_width
; }
339 int get_x_offset_display () const { return m_x_offset_display
; }
341 void print_gap_in_line_numbering ();
342 bool print_heading_for_line_span_index_p (int line_span_idx
) const;
344 expanded_location
get_expanded_location (const line_span
*) const;
346 void print_line (linenum_type row
);
349 bool will_show_line_p (linenum_type row
) const;
350 void print_leading_fixits (linenum_type row
);
351 line_bounds
print_source_line (linenum_type row
, const char *line
,
353 bool should_print_annotation_line_p (linenum_type row
) const;
354 void start_annotation_line (char margin_char
= ' ') const;
355 void print_annotation_line (linenum_type row
, const line_bounds lbounds
);
356 void print_any_labels (linenum_type row
);
357 void print_trailing_fixits (linenum_type row
);
359 bool annotation_line_showed_range_p (linenum_type line
, int start_column
,
360 int finish_column
) const;
361 void show_ruler (int max_column
) const;
363 bool validate_fixit_hint_p (const fixit_hint
*hint
);
365 void calculate_line_spans ();
366 void calculate_linenum_width ();
367 void calculate_x_offset_display ();
369 void print_newline ();
372 get_state_at_point (/* Inputs. */
373 linenum_type row
, int column
,
374 int first_non_ws
, int last_non_ws
,
375 enum column_unit col_unit
,
377 point_state
*out_state
);
380 get_x_bound_for_row (linenum_type row
, int caret_column
,
384 move_to_column (int *column
, int dest_column
, bool add_left_margin
);
387 diagnostic_context
*m_context
;
388 pretty_printer
*m_pp
;
389 location_t m_primary_loc
;
390 exploc_with_display_col m_exploc
;
391 colorizer m_colorizer
;
392 bool m_colorize_source_p
;
393 bool m_show_labels_p
;
394 bool m_show_line_numbers_p
;
395 bool m_diagnostic_path_p
;
396 auto_vec
<layout_range
> m_layout_ranges
;
397 auto_vec
<const fixit_hint
*> m_fixit_hints
;
398 auto_vec
<line_span
> m_line_spans
;
400 int m_x_offset_display
;
403 /* Implementation of "class colorizer". */
405 /* The constructor for "colorizer". Lookup and store color codes for the
406 different kinds of things we might need to print. */
408 colorizer::colorizer (diagnostic_context
*context
,
409 diagnostic_t diagnostic_kind
) :
411 m_diagnostic_kind (diagnostic_kind
),
412 m_current_state (STATE_NORMAL_TEXT
)
414 m_range1
= get_color_by_name ("range1");
415 m_range2
= get_color_by_name ("range2");
416 m_fixit_insert
= get_color_by_name ("fixit-insert");
417 m_fixit_delete
= get_color_by_name ("fixit-delete");
418 m_stop_color
= colorize_stop (pp_show_color (context
->printer
));
421 /* The destructor for "colorize". If colorization is on, print a code to
424 colorizer::~colorizer ()
426 finish_state (m_current_state
);
429 /* Update state, printing color codes if necessary if there's a state
433 colorizer::set_state (int new_state
)
435 if (m_current_state
!= new_state
)
437 finish_state (m_current_state
);
438 m_current_state
= new_state
;
439 begin_state (new_state
);
443 /* Turn on any colorization for STATE. */
446 colorizer::begin_state (int state
)
450 case STATE_NORMAL_TEXT
:
453 case STATE_FIXIT_INSERT
:
454 pp_string (m_context
->printer
, m_fixit_insert
);
457 case STATE_FIXIT_DELETE
:
458 pp_string (m_context
->printer
, m_fixit_delete
);
462 /* Make range 0 be the same color as the "kind" text
463 (error vs warning vs note). */
466 colorize_start (pp_show_color (m_context
->printer
),
467 diagnostic_get_color_for_kind (m_diagnostic_kind
)));
471 pp_string (m_context
->printer
, m_range1
);
475 pp_string (m_context
->printer
, m_range2
);
479 /* For ranges beyond 2, alternate between color 1 and color 2. */
481 gcc_assert (state
> 2);
482 pp_string (m_context
->printer
,
483 state
% 2 ? m_range1
: m_range2
);
489 /* Turn off any colorization for STATE. */
492 colorizer::finish_state (int state
)
494 if (state
!= STATE_NORMAL_TEXT
)
495 pp_string (m_context
->printer
, m_stop_color
);
498 /* Get the color code for NAME (or the empty string if
499 colorization is disabled). */
502 colorizer::get_color_by_name (const char *name
)
504 return colorize_start (pp_show_color (m_context
->printer
), name
);
507 /* Implementation of class layout_range. */
509 /* The constructor for class layout_range.
510 Initialize various layout_point fields from expanded_location
511 equivalents; we've already filtered on file. */
513 layout_range::layout_range (const exploc_with_display_col
&start_exploc
,
514 const exploc_with_display_col
&finish_exploc
,
515 enum range_display_kind range_display_kind
,
516 const exploc_with_display_col
&caret_exploc
,
517 unsigned original_idx
,
518 const range_label
*label
)
519 : m_start (start_exploc
),
520 m_finish (finish_exploc
),
521 m_range_display_kind (range_display_kind
),
522 m_caret (caret_exploc
),
523 m_original_idx (original_idx
),
528 /* Is (column, row) within the given range?
529 We've already filtered on the file.
531 Ranges are closed (both limits are within the range).
533 Example A: a single-line range:
534 start: (col=22, line=2)
535 finish: (col=38, line=2)
537 |00000011111111112222222222333333333344444444444
538 |34567890123456789012345678901234567890123456789
539 --+-----------------------------------------------
540 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
541 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
542 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
544 Example B: a multiline range with
545 start: (col=14, line=3)
546 finish: (col=08, line=5)
548 |00000011111111112222222222333333333344444444444
549 |34567890123456789012345678901234567890123456789
550 --+-----------------------------------------------
551 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
552 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
553 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
554 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
555 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
556 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
557 --+-----------------------------------------------
560 - 'b' indicates a point *before* the range
561 - 'S' indicates the start of the range
562 - 'w' indicates a point within the range
563 - 'F' indicates the finish of the range (which is
565 - 'a' indicates a subsequent point *after* the range.
567 COL_UNIT controls whether we check the byte column or
568 the display column; one or the other is more convenient
569 depending on the context. */
572 layout_range::contains_point (linenum_type row
, int column
,
573 enum column_unit col_unit
) const
575 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
576 /* ...but the equivalent isn't true for the columns;
577 consider example B in the comment above. */
579 if (row
< m_start
.m_line
)
580 /* Points before the first line of the range are
581 outside it (corresponding to line 01 in example A
582 and lines 01 and 02 in example B above). */
585 if (row
== m_start
.m_line
)
586 /* On same line as start of range (corresponding
587 to line 02 in example A and line 03 in example B). */
589 if (column
< m_start
.m_columns
[col_unit
])
590 /* Points on the starting line of the range, but
591 before the column in which it begins. */
594 if (row
< m_finish
.m_line
)
595 /* This is a multiline range; the point
596 is within it (corresponds to line 03 in example B
597 from column 14 onwards) */
601 /* This is a single-line range. */
602 gcc_assert (row
== m_finish
.m_line
);
603 return column
<= m_finish
.m_columns
[col_unit
];
607 /* The point is in a line beyond that containing the
608 start of the range: lines 03 onwards in example A,
609 and lines 04 onwards in example B. */
610 gcc_assert (row
> m_start
.m_line
);
612 if (row
> m_finish
.m_line
)
613 /* The point is beyond the final line of the range
614 (lines 03 onwards in example A, and lines 06 onwards
618 if (row
< m_finish
.m_line
)
620 /* The point is in a line that's fully within a multiline
621 range (e.g. line 04 in example B). */
622 gcc_assert (m_start
.m_line
< m_finish
.m_line
);
626 gcc_assert (row
== m_finish
.m_line
);
628 return column
<= m_finish
.m_columns
[col_unit
];
631 /* Does this layout_range contain any part of line ROW? */
634 layout_range::intersects_line_p (linenum_type row
) const
636 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
637 if (row
< m_start
.m_line
)
639 if (row
> m_finish
.m_line
)
646 /* Default for when we don't care what the tab expansion is set to. */
647 static const int def_tabstop
= 8;
649 /* Create some expanded locations for testing layout_range. The filename
650 member of the explocs is set to the empty string. This member will only be
651 inspected by the calls to location_compute_display_column() made from the
652 layout_point constructors. That function will check for an empty filename
653 argument and not attempt to open it, rather treating the non-existent data
654 as if the display width were the same as the byte count. Tests exercising a
655 real difference between byte count and display width are performed later,
656 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
659 make_range (int start_line
, int start_col
, int end_line
, int end_col
)
661 const expanded_location start_exploc
662 = {"", start_line
, start_col
, NULL
, false};
663 const expanded_location finish_exploc
664 = {"", end_line
, end_col
, NULL
, false};
665 return layout_range (exploc_with_display_col (start_exploc
, def_tabstop
),
666 exploc_with_display_col (finish_exploc
, def_tabstop
),
667 SHOW_RANGE_WITHOUT_CARET
,
668 exploc_with_display_col (start_exploc
, def_tabstop
),
672 /* Selftests for layout_range::contains_point and
673 layout_range::intersects_line_p. */
675 /* Selftest for layout_range, where the layout_range
676 is a range with start==end i.e. a single point. */
679 test_layout_range_for_single_point ()
681 layout_range point
= make_range (7, 10, 7, 10);
683 /* Tests for layout_range::contains_point. */
685 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
687 const enum column_unit col_unit
= (enum column_unit
) i
;
689 /* Before the line. */
690 ASSERT_FALSE (point
.contains_point (6, 1, col_unit
));
692 /* On the line, but before start. */
693 ASSERT_FALSE (point
.contains_point (7, 9, col_unit
));
696 ASSERT_TRUE (point
.contains_point (7, 10, col_unit
));
698 /* On the line, after the point. */
699 ASSERT_FALSE (point
.contains_point (7, 11, col_unit
));
701 /* After the line. */
702 ASSERT_FALSE (point
.contains_point (8, 1, col_unit
));
705 /* Tests for layout_range::intersects_line_p. */
706 ASSERT_FALSE (point
.intersects_line_p (6));
707 ASSERT_TRUE (point
.intersects_line_p (7));
708 ASSERT_FALSE (point
.intersects_line_p (8));
711 /* Selftest for layout_range, where the layout_range
712 is the single-line range shown as "Example A" above. */
715 test_layout_range_for_single_line ()
717 layout_range example_a
= make_range (2, 22, 2, 38);
719 /* Tests for layout_range::contains_point. */
721 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
723 const enum column_unit col_unit
= (enum column_unit
) i
;
725 /* Before the line. */
726 ASSERT_FALSE (example_a
.contains_point (1, 1, col_unit
));
728 /* On the line, but before start. */
729 ASSERT_FALSE (example_a
.contains_point (2, 21, col_unit
));
731 /* On the line, at the start. */
732 ASSERT_TRUE (example_a
.contains_point (2, 22, col_unit
));
734 /* On the line, within the range. */
735 ASSERT_TRUE (example_a
.contains_point (2, 23, col_unit
));
737 /* On the line, at the end. */
738 ASSERT_TRUE (example_a
.contains_point (2, 38, col_unit
));
740 /* On the line, after the end. */
741 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
743 /* After the line. */
744 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
747 /* Tests for layout_range::intersects_line_p. */
748 ASSERT_FALSE (example_a
.intersects_line_p (1));
749 ASSERT_TRUE (example_a
.intersects_line_p (2));
750 ASSERT_FALSE (example_a
.intersects_line_p (3));
753 /* Selftest for layout_range, where the layout_range
754 is the multi-line range shown as "Example B" above. */
757 test_layout_range_for_multiple_lines ()
759 layout_range example_b
= make_range (3, 14, 5, 8);
761 /* Tests for layout_range::contains_point. */
763 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
765 const enum column_unit col_unit
= (enum column_unit
) i
;
767 /* Before first line. */
768 ASSERT_FALSE (example_b
.contains_point (1, 1, col_unit
));
770 /* On the first line, but before start. */
771 ASSERT_FALSE (example_b
.contains_point (3, 13, col_unit
));
774 ASSERT_TRUE (example_b
.contains_point (3, 14, col_unit
));
776 /* On the first line, within the range. */
777 ASSERT_TRUE (example_b
.contains_point (3, 15, col_unit
));
779 /* On an interior line.
780 The column number should not matter; try various boundary
782 ASSERT_TRUE (example_b
.contains_point (4, 1, col_unit
));
783 ASSERT_TRUE (example_b
.contains_point (4, 7, col_unit
));
784 ASSERT_TRUE (example_b
.contains_point (4, 8, col_unit
));
785 ASSERT_TRUE (example_b
.contains_point (4, 9, col_unit
));
786 ASSERT_TRUE (example_b
.contains_point (4, 13, col_unit
));
787 ASSERT_TRUE (example_b
.contains_point (4, 14, col_unit
));
788 ASSERT_TRUE (example_b
.contains_point (4, 15, col_unit
));
790 /* On the final line, before the end. */
791 ASSERT_TRUE (example_b
.contains_point (5, 7, col_unit
));
793 /* On the final line, at the end. */
794 ASSERT_TRUE (example_b
.contains_point (5, 8, col_unit
));
796 /* On the final line, after the end. */
797 ASSERT_FALSE (example_b
.contains_point (5, 9, col_unit
));
799 /* After the line. */
800 ASSERT_FALSE (example_b
.contains_point (6, 1, col_unit
));
803 /* Tests for layout_range::intersects_line_p. */
804 ASSERT_FALSE (example_b
.intersects_line_p (2));
805 ASSERT_TRUE (example_b
.intersects_line_p (3));
806 ASSERT_TRUE (example_b
.intersects_line_p (4));
807 ASSERT_TRUE (example_b
.intersects_line_p (5));
808 ASSERT_FALSE (example_b
.intersects_line_p (6));
811 #endif /* #if CHECKING_P */
813 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
814 (still in bytes, not display cols) without any trailing whitespace. */
817 get_line_bytes_without_trailing_whitespace (const char *line
, int line_bytes
)
819 int result
= line_bytes
;
822 char ch
= line
[result
- 1];
823 if (ch
== ' ' || ch
== '\t' || ch
== '\r')
828 gcc_assert (result
>= 0);
829 gcc_assert (result
<= line_bytes
);
830 gcc_assert (result
== 0 ||
831 (line
[result
- 1] != ' '
832 && line
[result
-1] != '\t'
833 && line
[result
-1] != '\r'));
839 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
842 assert_eq (const char *line
, int expected_bytes
)
845 = get_line_bytes_without_trailing_whitespace (line
, strlen (line
));
846 ASSERT_EQ (actual_value
, expected_bytes
);
849 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
850 various inputs. It is not required to handle newlines. */
853 test_get_line_bytes_without_trailing_whitespace ()
859 assert_eq ("hello world", 11);
860 assert_eq ("hello world ", 11);
861 assert_eq ("hello world \t\t ", 11);
862 assert_eq ("hello world\r", 11);
865 #endif /* #if CHECKING_P */
867 /* Helper function for layout's ctor, for sanitizing locations relative
868 to the primary location within a diagnostic.
870 Compare LOC_A and LOC_B to see if it makes sense to print underlines
871 connecting their expanded locations. Doing so is only guaranteed to
872 make sense if the locations share the same macro expansion "history"
873 i.e. they can be traced through the same macro expansions, eventually
874 reaching an ordinary map.
876 This may be too strong a condition, but it effectively sanitizes
877 PR c++/70105, which has an example of printing an expression where the
878 final location of the expression is in a different macro, which
879 erroneously was leading to hundreds of lines of irrelevant source
883 compatible_locations_p (location_t loc_a
, location_t loc_b
)
885 if (IS_ADHOC_LOC (loc_a
))
886 loc_a
= get_location_from_adhoc_loc (line_table
, loc_a
);
887 if (IS_ADHOC_LOC (loc_b
))
888 loc_b
= get_location_from_adhoc_loc (line_table
, loc_b
);
890 /* If either location is one of the special locations outside of a
891 linemap, they are only compatible if they are equal. */
892 if (loc_a
< RESERVED_LOCATION_COUNT
893 || loc_b
< RESERVED_LOCATION_COUNT
)
894 return loc_a
== loc_b
;
896 const line_map
*map_a
= linemap_lookup (line_table
, loc_a
);
897 linemap_assert (map_a
);
899 const line_map
*map_b
= linemap_lookup (line_table
, loc_b
);
900 linemap_assert (map_b
);
902 /* Are they within the same map? */
905 /* Are both within the same macro expansion? */
906 if (linemap_macro_expansion_map_p (map_a
))
908 /* If so, then they're only compatible if either both are
909 from the macro definition, or both from the macro arguments. */
911 = linemap_location_from_macro_definition_p (line_table
, loc_a
);
913 = linemap_location_from_macro_definition_p (line_table
, loc_b
);
914 if (loc_a_from_defn
!= loc_b_from_defn
)
917 /* Expand each location towards the spelling location, and
919 const line_map_macro
*macro_map
= linemap_check_macro (map_a
);
920 location_t loc_a_toward_spelling
921 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
924 location_t loc_b_toward_spelling
925 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
928 return compatible_locations_p (loc_a_toward_spelling
,
929 loc_b_toward_spelling
);
932 /* Otherwise they are within the same ordinary map. */
937 /* Within different maps. */
939 /* If either is within a macro expansion, they are incompatible. */
940 if (linemap_macro_expansion_map_p (map_a
)
941 || linemap_macro_expansion_map_p (map_b
))
944 /* Within two different ordinary maps; they are compatible iff they
945 are in the same file. */
946 const line_map_ordinary
*ord_map_a
= linemap_check_ordinary (map_a
);
947 const line_map_ordinary
*ord_map_b
= linemap_check_ordinary (map_b
);
948 return ord_map_a
->to_file
== ord_map_b
->to_file
;
952 /* Comparator for sorting fix-it hints. */
955 fixit_cmp (const void *p_a
, const void *p_b
)
957 const fixit_hint
* hint_a
= *static_cast<const fixit_hint
* const *> (p_a
);
958 const fixit_hint
* hint_b
= *static_cast<const fixit_hint
* const *> (p_b
);
959 return hint_a
->get_start_loc () - hint_b
->get_start_loc ();
962 /* Implementation of class layout. */
964 /* Constructor for class layout.
966 Filter the ranges from the rich_location to those that we can
967 sanely print, populating m_layout_ranges and m_fixit_hints.
968 Determine the range of lines that we will print, splitting them
969 up into an ordered list of disjoint spans of contiguous line numbers.
970 Determine m_x_offset_display, to ensure that the primary caret
971 will fit within the max_width provided by the diagnostic_context. */
973 layout::layout (diagnostic_context
* context
,
974 rich_location
*richloc
,
975 diagnostic_t diagnostic_kind
)
976 : m_context (context
),
977 m_pp (context
->printer
),
978 m_primary_loc (richloc
->get_range (0)->m_loc
),
979 m_exploc (richloc
->get_expanded_location (0), context
->tabstop
),
980 m_colorizer (context
, diagnostic_kind
),
981 m_colorize_source_p (context
->colorize_source_p
),
982 m_show_labels_p (context
->show_labels_p
),
983 m_show_line_numbers_p (context
->show_line_numbers_p
),
984 m_diagnostic_path_p (diagnostic_kind
== DK_DIAGNOSTIC_PATH
),
985 m_layout_ranges (richloc
->get_num_locations ()),
986 m_fixit_hints (richloc
->get_num_fixit_hints ()),
987 m_line_spans (1 + richloc
->get_num_locations ()),
989 m_x_offset_display (0)
991 for (unsigned int idx
= 0; idx
< richloc
->get_num_locations (); idx
++)
993 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
994 Ignore any ranges that are awkward to handle. */
995 const location_range
*loc_range
= richloc
->get_range (idx
);
996 maybe_add_location_range (loc_range
, idx
, false);
999 /* Populate m_fixit_hints, filtering to only those that are in the
1001 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
1003 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
1004 if (validate_fixit_hint_p (hint
))
1005 m_fixit_hints
.safe_push (hint
);
1008 /* Sort m_fixit_hints. */
1009 m_fixit_hints
.qsort (fixit_cmp
);
1011 /* Populate the indicated members. */
1012 calculate_line_spans ();
1013 calculate_linenum_width ();
1014 calculate_x_offset_display ();
1016 if (context
->show_ruler_p
)
1017 show_ruler (m_x_offset_display
+ m_context
->caret_max_width
);
1021 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1022 those that we can sanely print.
1024 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1025 (for use as extrinsic state by label ranges FIXME).
1027 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1028 filtered against this layout instance's current line spans: it
1029 will only be added if the location is fully within the lines
1030 already specified by other locations.
1032 Return true iff LOC_RANGE was added. */
1035 layout::maybe_add_location_range (const location_range
*loc_range
,
1036 unsigned original_idx
,
1037 bool restrict_to_current_line_spans
)
1039 gcc_assert (loc_range
);
1041 /* Split the "range" into caret and range information. */
1042 source_range src_range
= get_range_from_loc (line_table
, loc_range
->m_loc
);
1044 /* Expand the various locations. */
1045 expanded_location start
1046 = linemap_client_expand_location_to_spelling_point
1047 (src_range
.m_start
, LOCATION_ASPECT_START
);
1048 expanded_location finish
1049 = linemap_client_expand_location_to_spelling_point
1050 (src_range
.m_finish
, LOCATION_ASPECT_FINISH
);
1051 expanded_location caret
1052 = linemap_client_expand_location_to_spelling_point
1053 (loc_range
->m_loc
, LOCATION_ASPECT_CARET
);
1055 /* If any part of the range isn't in the same file as the primary
1056 location of this diagnostic, ignore the range. */
1057 if (start
.file
!= m_exploc
.file
)
1059 if (finish
.file
!= m_exploc
.file
)
1061 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1062 if (caret
.file
!= m_exploc
.file
)
1065 /* Sanitize the caret location for non-primary ranges. */
1066 if (m_layout_ranges
.length () > 0)
1067 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1068 if (!compatible_locations_p (loc_range
->m_loc
, m_primary_loc
))
1069 /* Discard any non-primary ranges that can't be printed
1070 sanely relative to the primary location. */
1073 /* Everything is now known to be in the correct source file,
1074 but it may require further sanitization. */
1075 layout_range
ri (exploc_with_display_col (start
, m_context
->tabstop
),
1076 exploc_with_display_col (finish
, m_context
->tabstop
),
1077 loc_range
->m_range_display_kind
,
1078 exploc_with_display_col (caret
, m_context
->tabstop
),
1079 original_idx
, loc_range
->m_label
);
1081 /* If we have a range that finishes before it starts (perhaps
1082 from something built via macro expansion), printing the
1083 range is likely to be nonsensical. Also, attempting to do so
1084 breaks assumptions within the printing code (PR c/68473).
1085 Similarly, don't attempt to print ranges if one or both ends
1086 of the range aren't sane to print relative to the
1087 primary location (PR c++/70105). */
1088 if (start
.line
> finish
.line
1089 || !compatible_locations_p (src_range
.m_start
, m_primary_loc
)
1090 || !compatible_locations_p (src_range
.m_finish
, m_primary_loc
))
1092 /* Is this the primary location? */
1093 if (m_layout_ranges
.length () == 0)
1095 /* We want to print the caret for the primary location, but
1096 we must sanitize away m_start and m_finish. */
1097 ri
.m_start
= ri
.m_caret
;
1098 ri
.m_finish
= ri
.m_caret
;
1101 /* This is a non-primary range; ignore it. */
1105 /* Potentially filter to just the lines already specified by other
1106 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1107 The layout ctor doesn't use it, and can't because m_line_spans
1108 hasn't been set up at that point. */
1109 if (restrict_to_current_line_spans
)
1111 if (!will_show_line_p (start
.line
))
1113 if (!will_show_line_p (finish
.line
))
1115 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1116 if (!will_show_line_p (caret
.line
))
1120 /* Passed all the tests; add the range to m_layout_ranges so that
1121 it will be printed. */
1122 m_layout_ranges
.safe_push (ri
);
1126 /* Return true iff ROW is within one of the line spans for this layout. */
1129 layout::will_show_line_p (linenum_type row
) const
1131 for (int line_span_idx
= 0; line_span_idx
< get_num_line_spans ();
1134 const line_span
*line_span
= get_line_span (line_span_idx
);
1135 if (line_span
->contains_line_p (row
))
1141 /* Print a line showing a gap in the line numbers, for showing the boundary
1142 between two line spans. */
1145 layout::print_gap_in_line_numbering ()
1147 gcc_assert (m_show_line_numbers_p
);
1149 pp_emit_prefix (m_pp
);
1151 for (int i
= 0; i
< m_linenum_width
+ 1; i
++)
1152 pp_character (m_pp
, '.');
1157 /* Return true iff we should print a heading when starting the
1158 line span with the given index. */
1161 layout::print_heading_for_line_span_index_p (int line_span_idx
) const
1163 /* We print a heading for every change of line span, hence for every
1164 line span after the initial one. */
1165 if (line_span_idx
> 0)
1168 /* We also do it for the initial span if the primary location of the
1169 diagnostic is in a different span. */
1170 if (m_exploc
.line
> (int)get_line_span (0)->m_last_line
)
1176 /* Get an expanded_location for the first location of interest within
1177 the given line_span.
1178 Used when printing a heading to indicate a new line span. */
1181 layout::get_expanded_location (const line_span
*line_span
) const
1183 /* Whenever possible, use the caret location. */
1184 if (line_span
->contains_line_p (m_exploc
.line
))
1187 /* Otherwise, use the start of the first range that's present
1188 within the line_span. */
1189 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1191 const layout_range
*lr
= &m_layout_ranges
[i
];
1192 if (line_span
->contains_line_p (lr
->m_start
.m_line
))
1194 expanded_location exploc
= m_exploc
;
1195 exploc
.line
= lr
->m_start
.m_line
;
1196 exploc
.column
= lr
->m_start
.m_columns
[CU_BYTES
];
1201 /* Otherwise, use the location of the first fixit-hint present within
1203 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1205 const fixit_hint
*hint
= m_fixit_hints
[i
];
1206 location_t loc
= hint
->get_start_loc ();
1207 expanded_location exploc
= expand_location (loc
);
1208 if (line_span
->contains_line_p (exploc
.line
))
1212 /* It should not be possible to have a line span that didn't
1213 contain any of the layout_range or fixit_hint instances. */
1218 /* Determine if HINT is meaningful to print within this layout. */
1221 layout::validate_fixit_hint_p (const fixit_hint
*hint
)
1223 if (LOCATION_FILE (hint
->get_start_loc ()) != m_exploc
.file
)
1225 if (LOCATION_FILE (hint
->get_next_loc ()) != m_exploc
.file
)
1231 /* Determine the range of lines affected by HINT.
1232 This assumes that HINT has already been filtered by
1233 validate_fixit_hint_p, and so affects the correct source file. */
1236 get_line_span_for_fixit_hint (const fixit_hint
*hint
)
1240 int start_line
= LOCATION_LINE (hint
->get_start_loc ());
1242 /* For line-insertion fix-it hints, add the previous line to the
1243 span, to give the user more context on the proposed change. */
1244 if (hint
->ends_with_newline_p ())
1248 return line_span (start_line
,
1249 LOCATION_LINE (hint
->get_next_loc ()));
1252 /* We want to print the pertinent source code at a diagnostic. The
1253 rich_location can contain multiple locations. This will have been
1254 filtered into m_exploc (the caret for the primary location) and
1255 m_layout_ranges, for those ranges within the same source file.
1257 We will print a subset of the lines within the source file in question,
1258 as a collection of "spans" of lines.
1260 This function populates m_line_spans with an ordered, disjoint list of
1261 the line spans of interest.
1263 Printing a gap between line spans takes one line, so, when printing
1264 line numbers, we allow a gap of up to one line between spans when
1265 merging, since it makes more sense to print the source line rather than a
1266 "gap-in-line-numbering" line. When not printing line numbers, it's
1267 better to be more explicit about what's going on, so keeping them as
1268 separate spans is preferred.
1270 For example, if the primary range is on lines 8-10, with secondary ranges
1271 covering lines 5-6 and lines 13-15:
1287 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1289 With line numbering off (with span headers), we want three spans: lines 5-6,
1290 lines 8-10, and lines 13-15. */
1293 layout::calculate_line_spans ()
1295 /* This should only be called once, by the ctor. */
1296 gcc_assert (m_line_spans
.length () == 0);
1298 /* Populate tmp_spans with individual spans, for each of
1299 m_exploc, and for m_layout_ranges. */
1300 auto_vec
<line_span
> tmp_spans (1 + m_layout_ranges
.length ());
1301 tmp_spans
.safe_push (line_span (m_exploc
.line
, m_exploc
.line
));
1302 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1304 const layout_range
*lr
= &m_layout_ranges
[i
];
1305 gcc_assert (lr
->m_start
.m_line
<= lr
->m_finish
.m_line
);
1306 tmp_spans
.safe_push (line_span (lr
->m_start
.m_line
,
1307 lr
->m_finish
.m_line
));
1310 /* Also add spans for any fix-it hints, in case they cover other lines. */
1311 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1313 const fixit_hint
*hint
= m_fixit_hints
[i
];
1315 tmp_spans
.safe_push (get_line_span_for_fixit_hint (hint
));
1319 tmp_spans
.qsort(line_span::comparator
);
1321 /* Now iterate through tmp_spans, copying into m_line_spans, and
1322 combining where possible. */
1323 gcc_assert (tmp_spans
.length () > 0);
1324 m_line_spans
.safe_push (tmp_spans
[0]);
1325 for (unsigned int i
= 1; i
< tmp_spans
.length (); i
++)
1327 line_span
*current
= &m_line_spans
[m_line_spans
.length () - 1];
1328 const line_span
*next
= &tmp_spans
[i
];
1329 gcc_assert (next
->m_first_line
>= current
->m_first_line
);
1330 const int merger_distance
= m_show_line_numbers_p
? 1 : 0;
1331 if ((linenum_arith_t
)next
->m_first_line
1332 <= (linenum_arith_t
)current
->m_last_line
+ 1 + merger_distance
)
1334 /* We can merge them. */
1335 if (next
->m_last_line
> current
->m_last_line
)
1336 current
->m_last_line
= next
->m_last_line
;
1340 /* No merger possible. */
1341 m_line_spans
.safe_push (*next
);
1345 /* Verify the result, in m_line_spans. */
1346 gcc_assert (m_line_spans
.length () > 0);
1347 for (unsigned int i
= 1; i
< m_line_spans
.length (); i
++)
1349 const line_span
*prev
= &m_line_spans
[i
- 1];
1350 const line_span
*next
= &m_line_spans
[i
];
1351 /* The individual spans must be sane. */
1352 gcc_assert (prev
->m_first_line
<= prev
->m_last_line
);
1353 gcc_assert (next
->m_first_line
<= next
->m_last_line
);
1354 /* The spans must be ordered. */
1355 gcc_assert (prev
->m_first_line
< next
->m_first_line
);
1356 /* There must be a gap of at least one line between separate spans. */
1357 gcc_assert ((prev
->m_last_line
+ 1) < next
->m_first_line
);
1361 /* Determine how many display columns need to be reserved for line numbers,
1362 based on the largest line number that will be needed, and populate
1366 layout::calculate_linenum_width ()
1368 gcc_assert (m_line_spans
.length () > 0);
1369 const line_span
*last_span
= &m_line_spans
[m_line_spans
.length () - 1];
1370 int highest_line
= last_span
->m_last_line
;
1371 if (highest_line
< 0)
1373 m_linenum_width
= num_digits (highest_line
);
1374 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1375 if (m_line_spans
.length () > 1)
1376 m_linenum_width
= MAX (m_linenum_width
, 3);
1377 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1378 after the line number. */
1379 m_linenum_width
= MAX (m_linenum_width
, m_context
->min_margin_width
- 1);
1382 /* Calculate m_x_offset_display, which improves readability in case the source
1383 line of interest is longer than the user's display. All lines output will be
1384 shifted to the left (so that their beginning is no longer displayed) by
1385 m_x_offset_display display columns, so that the caret is in a reasonable
1389 layout::calculate_x_offset_display ()
1391 m_x_offset_display
= 0;
1393 const int max_width
= m_context
->caret_max_width
;
1396 /* Nothing to do, the width is not capped. */
1400 const char_span line
= location_get_source_line (m_exploc
.file
,
1404 /* Nothing to do, we couldn't find the source line. */
1407 int caret_display_column
= m_exploc
.m_display_col
;
1408 const int line_bytes
1409 = get_line_bytes_without_trailing_whitespace (line
.get_buffer (),
1411 int eol_display_column
1412 = cpp_display_width (line
.get_buffer (), line_bytes
, m_context
->tabstop
);
1413 if (caret_display_column
> eol_display_column
1414 || !caret_display_column
)
1416 /* This does not make sense, so don't try to do anything in this case. */
1420 /* Adjust caret and eol positions to include the left margin. If we are
1421 outputting line numbers, then the left margin is equal to m_linenum_width
1422 plus three for the " | " which follows it. Otherwise the left margin width
1423 is equal to 1, because layout::print_source_line() will prefix each line
1425 const int source_display_cols
= eol_display_column
;
1426 int left_margin_size
= 1;
1427 if (m_show_line_numbers_p
)
1428 left_margin_size
= m_linenum_width
+ 3;
1429 caret_display_column
+= left_margin_size
;
1430 eol_display_column
+= left_margin_size
;
1432 if (eol_display_column
<= max_width
)
1434 /* Nothing to do, everything fits in the display. */
1438 /* The line is too long for the display. Calculate an offset such that the
1439 caret is not too close to the right edge of the screen. It will be
1440 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1441 than that to the end of the source line anyway. */
1442 int right_margin_size
= CARET_LINE_MARGIN
;
1443 right_margin_size
= MIN (eol_display_column
- caret_display_column
,
1445 if (right_margin_size
+ left_margin_size
>= max_width
)
1447 /* The max_width is very small, so anything we try to do will not be very
1448 effective; just punt in this case and output with no offset. */
1451 const int max_caret_display_column
= max_width
- right_margin_size
;
1452 if (caret_display_column
> max_caret_display_column
)
1454 m_x_offset_display
= caret_display_column
- max_caret_display_column
;
1455 /* Make sure we don't offset the line into oblivion. */
1456 static const int min_cols_visible
= 2;
1457 if (source_display_cols
- m_x_offset_display
< min_cols_visible
)
1458 m_x_offset_display
= 0;
1462 /* Print line ROW of source code, potentially colorized at any ranges, and
1463 return the line bounds. LINE is the source line (not necessarily
1464 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1465 colorization and tab expansion, this function tracks the line position in
1466 both byte and display column units. */
1469 layout::print_source_line (linenum_type row
, const char *line
, int line_bytes
)
1471 m_colorizer
.set_normal_text ();
1473 pp_emit_prefix (m_pp
);
1474 if (m_show_line_numbers_p
)
1476 int width
= num_digits (row
);
1477 for (int i
= 0; i
< m_linenum_width
- width
; i
++)
1479 pp_printf (m_pp
, "%i | ", row
);
1484 /* We will stop printing the source line at any trailing whitespace. */
1485 line_bytes
= get_line_bytes_without_trailing_whitespace (line
,
1488 /* This object helps to keep track of which display column we are at, which is
1489 necessary for computing the line bounds in display units, for doing
1490 tab expansion, and for implementing m_x_offset_display. */
1491 cpp_display_width_computation
dw (line
, line_bytes
, m_context
->tabstop
);
1493 /* Skip the first m_x_offset_display display columns. In case the leading
1494 portion that will be skipped ends with a character with wcwidth > 1, then
1495 it is possible we skipped too much, so account for that by padding with
1496 spaces. Note that this does the right thing too in case a tab was the last
1497 character to be skipped over; the tab is effectively replaced by the
1498 correct number of trailing spaces needed to offset by the desired number of
1500 for (int skipped_display_cols
= dw
.advance_display_cols (m_x_offset_display
);
1501 skipped_display_cols
> m_x_offset_display
; --skipped_display_cols
)
1504 /* Print the line and compute the line_bounds. */
1505 line_bounds lbounds
;
1508 /* Assuming colorization is enabled for the caret and underline
1509 characters, we may also colorize the associated characters
1510 within the source line.
1512 For frontends that generate range information, we color the
1513 associated characters in the source line the same as the
1514 carets and underlines in the annotation line, to make it easier
1515 for the reader to see the pertinent code.
1517 For frontends that only generate carets, we don't colorize the
1518 characters above them, since this would look strange (e.g.
1519 colorizing just the first character in a token). */
1520 if (m_colorize_source_p
)
1524 const int start_byte_col
= dw
.bytes_processed () + 1;
1525 in_range_p
= get_state_at_point (row
, start_byte_col
,
1530 m_colorizer
.set_range (state
.range_idx
);
1532 m_colorizer
.set_normal_text ();
1535 /* Get the display width of the next character to be output, expanding
1536 tabs and replacing some control bytes with spaces as necessary. */
1537 const char *c
= dw
.next_byte ();
1538 const int start_disp_col
= dw
.display_cols_processed () + 1;
1539 const int this_display_width
= dw
.process_next_codepoint ();
1542 /* The returned display width is the number of spaces into which the
1543 tab should be expanded. */
1544 for (int i
= 0; i
!= this_display_width
; ++i
)
1548 if (*c
== '\0' || *c
== '\r')
1550 /* cpp_wcwidth() promises to return 1 for all control bytes, and we
1551 want to output these as a single space too, so this case is
1552 actually the same as the '\t' case. */
1553 gcc_assert (this_display_width
== 1);
1558 /* We have a (possibly multibyte) character to output; update the line
1559 bounds if it is not whitespace. */
1562 lbounds
.m_last_non_ws_disp_col
= dw
.display_cols_processed ();
1563 if (lbounds
.m_first_non_ws_disp_col
== INT_MAX
)
1564 lbounds
.m_first_non_ws_disp_col
= start_disp_col
;
1567 /* Output the character. */
1568 while (c
!= dw
.next_byte ()) pp_character (m_pp
, *c
++);
1574 /* Determine if we should print an annotation line for ROW.
1575 i.e. if any of m_layout_ranges contains ROW. */
1578 layout::should_print_annotation_line_p (linenum_type row
) const
1580 layout_range
*range
;
1582 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1584 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
1586 if (range
->intersects_line_p (row
))
1592 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1593 margin, which is empty for annotation lines. Otherwise, do nothing. */
1596 layout::start_annotation_line (char margin_char
) const
1598 pp_emit_prefix (m_pp
);
1599 if (m_show_line_numbers_p
)
1601 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1602 of it, right-aligned, padded with spaces. */
1604 for (i
= 0; i
< m_linenum_width
- 3; i
++)
1606 for (; i
< m_linenum_width
; i
++)
1607 pp_character (m_pp
, margin_char
);
1608 pp_string (m_pp
, " |");
1612 /* Print a line consisting of the caret/underlines for the given
1616 layout::print_annotation_line (linenum_type row
, const line_bounds lbounds
)
1618 int x_bound
= get_x_bound_for_row (row
, m_exploc
.m_display_col
,
1619 lbounds
.m_last_non_ws_disp_col
);
1621 start_annotation_line ();
1624 for (int column
= 1 + m_x_offset_display
; column
< x_bound
; column
++)
1628 in_range_p
= get_state_at_point (row
, column
,
1629 lbounds
.m_first_non_ws_disp_col
,
1630 lbounds
.m_last_non_ws_disp_col
,
1635 /* Within a range. Draw either the caret or an underline. */
1636 m_colorizer
.set_range (state
.range_idx
);
1637 if (state
.draw_caret_p
)
1639 /* Draw the caret. */
1641 if (state
.range_idx
< rich_location::STATICALLY_ALLOCATED_RANGES
)
1642 caret_char
= m_context
->caret_chars
[state
.range_idx
];
1645 pp_character (m_pp
, caret_char
);
1648 pp_character (m_pp
, '~');
1652 /* Not in a range. */
1653 m_colorizer
.set_normal_text ();
1654 pp_character (m_pp
, ' ');
1660 /* Implementation detail of layout::print_any_labels.
1662 A label within the given row of source. */
1667 line_label (diagnostic_context
*context
, int state_idx
, int column
,
1669 : m_state_idx (state_idx
), m_column (column
),
1670 m_text (text
), m_label_line (0), m_has_vbar (true)
1672 const int bytes
= strlen (text
.m_buffer
);
1674 = cpp_display_width (text
.m_buffer
, bytes
, context
->tabstop
);
1677 /* Sorting is primarily by column, then by state index. */
1678 static int comparator (const void *p1
, const void *p2
)
1680 const line_label
*ll1
= (const line_label
*)p1
;
1681 const line_label
*ll2
= (const line_label
*)p2
;
1682 int column_cmp
= compare (ll1
->m_column
, ll2
->m_column
);
1685 /* Order by reverse state index, so that labels are printed
1686 in order of insertion into the rich_location when the
1687 sorted list is walked backwards. */
1688 return -compare (ll1
->m_state_idx
, ll2
->m_state_idx
);
1694 size_t m_display_width
;
1699 /* Print any labels in this row. */
1701 layout::print_any_labels (linenum_type row
)
1704 auto_vec
<line_label
> labels
;
1706 /* Gather the labels that are to be printed into "labels". */
1708 layout_range
*range
;
1709 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1711 /* Most ranges don't have labels, so reject this first. */
1712 if (range
->m_label
== NULL
)
1715 /* The range's caret must be on this line. */
1716 if (range
->m_caret
.m_line
!= row
)
1719 /* Reject labels that aren't fully visible due to clipping
1720 by m_x_offset_display. */
1721 const int disp_col
= range
->m_caret
.m_columns
[CU_DISPLAY_COLS
];
1722 if (disp_col
<= m_x_offset_display
)
1726 text
= range
->m_label
->get_text (range
->m_original_idx
);
1728 /* Allow for labels that return NULL from their get_text
1729 implementation (so e.g. such labels can control their own
1731 if (text
.m_buffer
== NULL
)
1734 labels
.safe_push (line_label (m_context
, i
, disp_col
, text
));
1738 /* Bail out if there are no labels on this row. */
1739 if (labels
.length () == 0)
1743 labels
.qsort(line_label::comparator
);
1745 /* Figure out how many "label lines" we need, and which
1746 one each label is printed in.
1748 For example, if the labels aren't too densely packed,
1749 we can fit them on the same line, giving two "label lines":
1754 l0 l1 : label line 1
1756 If they would touch each other or overlap, then we need
1757 additional "label lines":
1762 | label 1 : label line 1
1763 label 0 : label line 2
1765 Place the final label on label line 1, and work backwards, adding
1766 label lines as needed.
1768 If multiple labels are at the same place, put them on separate
1774 label 0 : label line 2
1775 label 1 : label line 3. */
1777 int max_label_line
= 1;
1779 int next_column
= INT_MAX
;
1781 FOR_EACH_VEC_ELT_REVERSE (labels
, i
, label
)
1783 /* Would this label "touch" or overlap the next label? */
1784 if (label
->m_column
+ label
->m_display_width
>= (size_t)next_column
)
1788 /* If we've already seen labels with the same column, suppress the
1789 vertical bar for subsequent ones in this backwards iteration;
1790 hence only the one with the highest label_line has m_has_vbar set. */
1791 if (label
->m_column
== next_column
)
1792 label
->m_has_vbar
= false;
1795 label
->m_label_line
= max_label_line
;
1796 next_column
= label
->m_column
;
1800 /* Print the "label lines". For each label within the line, print
1801 either a vertical bar ('|') for the labels that are lower down, or the
1802 labels themselves once we've reached their line. */
1804 for (int label_line
= 0; label_line
<= max_label_line
; label_line
++)
1806 start_annotation_line ();
1808 int column
= 1 + m_x_offset_display
;
1810 FOR_EACH_VEC_ELT (labels
, i
, label
)
1812 if (label_line
> label
->m_label_line
)
1813 /* We've printed all the labels for this label line. */
1816 if (label_line
== label
->m_label_line
)
1818 gcc_assert (column
<= label
->m_column
);
1819 move_to_column (&column
, label
->m_column
, true);
1820 /* Colorize the text, unless it's for events in a
1822 if (!m_diagnostic_path_p
)
1823 m_colorizer
.set_range (label
->m_state_idx
);
1824 pp_string (m_pp
, label
->m_text
.m_buffer
);
1825 m_colorizer
.set_normal_text ();
1826 column
+= label
->m_display_width
;
1828 else if (label
->m_has_vbar
)
1830 gcc_assert (column
<= label
->m_column
);
1831 move_to_column (&column
, label
->m_column
, true);
1832 m_colorizer
.set_range (label
->m_state_idx
);
1833 pp_character (m_pp
, '|');
1834 m_colorizer
.set_normal_text ();
1845 FOR_EACH_VEC_ELT (labels
, i
, label
)
1846 label
->m_text
.maybe_free ();
1850 /* If there are any fixit hints inserting new lines before source line ROW,
1853 They are printed on lines of their own, before the source line
1854 itself, with a leading '+'. */
1857 layout::print_leading_fixits (linenum_type row
)
1859 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1861 const fixit_hint
*hint
= m_fixit_hints
[i
];
1863 if (!hint
->ends_with_newline_p ())
1864 /* Not a newline fixit; print it in print_trailing_fixits. */
1867 gcc_assert (hint
->insertion_p ());
1869 if (hint
->affects_line_p (m_exploc
.file
, row
))
1871 /* Printing the '+' with normal colorization
1872 and the inserted line with "insert" colorization
1873 helps them stand out from each other, and from
1874 the surrounding text. */
1875 m_colorizer
.set_normal_text ();
1876 start_annotation_line ('+');
1877 pp_character (m_pp
, '+');
1878 m_colorizer
.set_fixit_insert ();
1879 /* Print all but the trailing newline of the fix-it hint.
1880 We have to print the newline separately to avoid
1881 getting additional pp prefixes printed. */
1882 for (size_t i
= 0; i
< hint
->get_length () - 1; i
++)
1883 pp_character (m_pp
, hint
->get_string ()[i
]);
1884 m_colorizer
.set_normal_text ();
1890 /* Subroutine of layout::print_trailing_fixits.
1892 Determine if the annotation line printed for LINE contained
1893 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
1896 layout::annotation_line_showed_range_p (linenum_type line
, int start_column
,
1897 int finish_column
) const
1899 layout_range
*range
;
1901 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1902 if (range
->m_start
.m_line
== line
1903 && range
->m_start
.m_columns
[CU_DISPLAY_COLS
] == start_column
1904 && range
->m_finish
.m_line
== line
1905 && range
->m_finish
.m_columns
[CU_DISPLAY_COLS
] == finish_column
)
1910 /* Classes for printing trailing fix-it hints i.e. those that
1911 don't add new lines.
1913 For insertion, these can look like:
1917 For replacement, these can look like:
1919 ------------- : underline showing affected range
1922 For deletion, these can look like:
1924 ------------- : underline showing affected range
1926 This can become confusing if they overlap, and so we need
1927 to do some preprocessing to decide what to print.
1928 We use the list of fixit_hint instances affecting the line
1929 to build a list of "correction" instances, and print the
1932 For example, consider a set of fix-its for converting
1933 a C-style cast to a C++ const_cast.
1937 ..000000000111111111122222222223333333333.
1938 ..123456789012345678901234567890123456789.
1939 foo *f = (foo *)ptr->field;
1942 and the fix-it hints:
1943 - replace col 10 (the open paren) with "const_cast<"
1944 - replace col 16 (the close paren) with "> ("
1945 - insert ")" before col 27
1947 then we would get odd-looking output:
1949 foo *f = (foo *)ptr->field;
1956 It would be better to detect when fixit hints are going to
1957 overlap (those that require new lines), and to consolidate
1958 the printing of such fixits, giving something like:
1960 foo *f = (foo *)ptr->field;
1963 const_cast<foo *> (ptr->field)
1965 This works by detecting when the printing would overlap, and
1966 effectively injecting no-op replace hints into the gaps between
1967 such fix-its, so that the printing joins up.
1969 In the above example, the overlap of:
1970 - replace col 10 (the open paren) with "const_cast<"
1972 - replace col 16 (the close paren) with "> ("
1973 is fixed by injecting a no-op:
1974 - replace cols 11-15 with themselves ("foo *")
1975 and consolidating these, making:
1976 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1978 - replace cols 10-16 with "const_cast<foo *> ("
1980 This overlaps with the final fix-it hint:
1981 - insert ")" before col 27
1982 and so we repeat the consolidation process, by injecting
1984 - replace cols 17-26 with themselves ("ptr->field")
1986 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1988 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1990 and is thus printed as desired. */
1992 /* A range of (byte or display) columns within a line. */
1997 column_range (int start_
, int finish_
) : start (start_
), finish (finish_
)
1999 /* We must have either a range, or an insertion. */
2000 gcc_assert (start
<= finish
|| finish
== start
- 1);
2003 bool operator== (const column_range
&other
) const
2005 return start
== other
.start
&& finish
== other
.finish
;
2012 /* Get the range of bytes or display columns that HINT would affect. */
2014 get_affected_range (diagnostic_context
*context
,
2015 const fixit_hint
*hint
, enum column_unit col_unit
)
2017 expanded_location exploc_start
= expand_location (hint
->get_start_loc ());
2018 expanded_location exploc_finish
= expand_location (hint
->get_next_loc ());
2019 --exploc_finish
.column
;
2023 if (col_unit
== CU_DISPLAY_COLS
)
2026 = location_compute_display_column (exploc_start
, context
->tabstop
);
2027 if (hint
->insertion_p ())
2028 finish_column
= start_column
- 1;
2031 = location_compute_display_column (exploc_finish
, context
->tabstop
);
2035 start_column
= exploc_start
.column
;
2036 finish_column
= exploc_finish
.column
;
2038 return column_range (start_column
, finish_column
);
2041 /* Get the range of display columns that would be printed for HINT. */
2044 get_printed_columns (diagnostic_context
*context
, const fixit_hint
*hint
)
2046 expanded_location exploc
= expand_location (hint
->get_start_loc ());
2047 int start_column
= location_compute_display_column (exploc
, context
->tabstop
);
2048 int hint_width
= cpp_display_width (hint
->get_string (), hint
->get_length (),
2050 int final_hint_column
= start_column
+ hint_width
- 1;
2051 if (hint
->insertion_p ())
2053 return column_range (start_column
, final_hint_column
);
2057 exploc
= expand_location (hint
->get_next_loc ());
2060 = location_compute_display_column (exploc
, context
->tabstop
);
2061 return column_range (start_column
,
2062 MAX (finish_column
, final_hint_column
));
2066 /* A correction on a particular line.
2067 This describes a plan for how to print one or more fixit_hint
2068 instances that affected the line, potentially consolidating hints
2069 into corrections to make the result easier for the user to read. */
2074 correction (column_range affected_bytes
,
2075 column_range affected_columns
,
2076 column_range printed_columns
,
2077 const char *new_text
, size_t new_text_len
,
2079 : m_affected_bytes (affected_bytes
),
2080 m_affected_columns (affected_columns
),
2081 m_printed_columns (printed_columns
),
2082 m_text (xstrdup (new_text
)),
2083 m_byte_length (new_text_len
),
2084 m_tabstop (tabstop
),
2085 m_alloc_sz (new_text_len
+ 1)
2087 compute_display_cols ();
2090 ~correction () { free (m_text
); }
2092 bool insertion_p () const
2094 return m_affected_bytes
.start
== m_affected_bytes
.finish
+ 1;
2097 void ensure_capacity (size_t len
);
2098 void ensure_terminated ();
2100 void compute_display_cols ()
2102 m_display_cols
= cpp_display_width (m_text
, m_byte_length
, m_tabstop
);
2105 void overwrite (int dst_offset
, const char_span
&src_span
)
2107 gcc_assert (dst_offset
>= 0);
2108 gcc_assert (dst_offset
+ src_span
.length () < m_alloc_sz
);
2109 memcpy (m_text
+ dst_offset
, src_span
.get_buffer (),
2110 src_span
.length ());
2113 /* If insert, then start: the column before which the text
2114 is to be inserted, and finish is offset by the length of
2116 If replace, then the range of columns affected. */
2117 column_range m_affected_bytes
;
2118 column_range m_affected_columns
;
2120 /* If insert, then start: the column before which the text
2121 is to be inserted, and finish is offset by the length of
2123 If replace, then the range of columns affected. */
2124 column_range m_printed_columns
;
2126 /* The text to be inserted/used as replacement. */
2128 size_t m_byte_length
; /* Not including null-terminator. */
2134 /* Ensure that m_text can hold a string of length LEN
2135 (plus 1 for 0-termination). */
2138 correction::ensure_capacity (size_t len
)
2140 /* Allow 1 extra byte for 0-termination. */
2141 if (m_alloc_sz
< (len
+ 1))
2143 size_t new_alloc_sz
= (len
+ 1) * 2;
2144 m_text
= (char *)xrealloc (m_text
, new_alloc_sz
);
2145 m_alloc_sz
= new_alloc_sz
;
2149 /* Ensure that m_text is 0-terminated. */
2152 correction::ensure_terminated ()
2154 /* 0-terminate the buffer. */
2155 gcc_assert (m_byte_length
< m_alloc_sz
);
2156 m_text
[m_byte_length
] = '\0';
2159 /* A list of corrections affecting a particular line.
2160 This is used by layout::print_trailing_fixits for planning
2161 how to print the fix-it hints affecting the line. */
2163 class line_corrections
2166 line_corrections (diagnostic_context
*context
, const char *filename
,
2168 : m_context (context
), m_filename (filename
), m_row (row
)
2170 ~line_corrections ();
2172 void add_hint (const fixit_hint
*hint
);
2174 diagnostic_context
*m_context
;
2175 const char *m_filename
;
2177 auto_vec
<correction
*> m_corrections
;
2180 /* struct line_corrections. */
2182 line_corrections::~line_corrections ()
2186 FOR_EACH_VEC_ELT (m_corrections
, i
, c
)
2190 /* A struct wrapping a particular source line, allowing
2191 run-time bounds-checking of accesses in a checked build. */
2196 source_line (const char *filename
, int line
);
2198 char_span
as_span () { return char_span (chars
, width
); }
2204 /* source_line's ctor. */
2206 source_line::source_line (const char *filename
, int line
)
2208 char_span span
= location_get_source_line (filename
, line
);
2209 chars
= span
.get_buffer ();
2210 width
= span
.length ();
2213 /* Add HINT to the corrections for this line.
2214 Attempt to consolidate nearby hints so that they will not
2215 overlap with printed. */
2218 line_corrections::add_hint (const fixit_hint
*hint
)
2220 column_range affected_bytes
= get_affected_range (m_context
, hint
, CU_BYTES
);
2221 column_range affected_columns
= get_affected_range (m_context
, hint
,
2223 column_range printed_columns
= get_printed_columns (m_context
, hint
);
2225 /* Potentially consolidate. */
2226 if (!m_corrections
.is_empty ())
2228 correction
*last_correction
2229 = m_corrections
[m_corrections
.length () - 1];
2231 /* The following consolidation code assumes that the fix-it hints
2232 have been sorted by start (done within layout's ctor). */
2233 gcc_assert (affected_bytes
.start
2234 >= last_correction
->m_affected_bytes
.start
);
2235 gcc_assert (printed_columns
.start
2236 >= last_correction
->m_printed_columns
.start
);
2238 if (printed_columns
.start
<= last_correction
->m_printed_columns
.finish
)
2240 /* We have two hints for which the printed forms of the hints
2241 would touch or overlap, so we need to consolidate them to avoid
2243 Attempt to inject a "replace" correction from immediately
2244 after the end of the last hint to immediately before the start
2245 of the next hint. */
2246 column_range
between (last_correction
->m_affected_bytes
.finish
+ 1,
2247 affected_bytes
.start
- 1);
2249 /* Try to read the source. */
2250 source_line
line (m_filename
, m_row
);
2251 if (line
.chars
&& between
.finish
< line
.width
)
2253 /* Consolidate into the last correction:
2254 add a no-op "replace" of the "between" text, and
2255 add the text from the new hint. */
2256 int old_byte_len
= last_correction
->m_byte_length
;
2257 gcc_assert (old_byte_len
>= 0);
2258 int between_byte_len
= between
.finish
+ 1 - between
.start
;
2259 gcc_assert (between_byte_len
>= 0);
2261 = old_byte_len
+ between_byte_len
+ hint
->get_length ();
2262 gcc_assert (new_byte_len
>= 0);
2263 last_correction
->ensure_capacity (new_byte_len
);
2264 last_correction
->overwrite
2266 line
.as_span ().subspan (between
.start
- 1,
2267 between
.finish
+ 1 - between
.start
));
2268 last_correction
->overwrite (old_byte_len
+ between_byte_len
,
2269 char_span (hint
->get_string (),
2270 hint
->get_length ()));
2271 last_correction
->m_byte_length
= new_byte_len
;
2272 last_correction
->ensure_terminated ();
2273 last_correction
->m_affected_bytes
.finish
2274 = affected_bytes
.finish
;
2275 last_correction
->m_affected_columns
.finish
2276 = affected_columns
.finish
;
2277 int prev_display_cols
= last_correction
->m_display_cols
;
2278 last_correction
->compute_display_cols ();
2279 last_correction
->m_printed_columns
.finish
2280 += last_correction
->m_display_cols
- prev_display_cols
;
2286 /* If no consolidation happened, add a new correction instance. */
2287 m_corrections
.safe_push (new correction (affected_bytes
,
2290 hint
->get_string (),
2291 hint
->get_length (),
2292 m_context
->tabstop
));
2295 /* If there are any fixit hints on source line ROW, print them.
2296 They are printed in order, attempting to combine them onto lines, but
2297 starting new lines if necessary.
2298 Fix-it hints that insert new lines are handled separately,
2299 in layout::print_leading_fixits. */
2302 layout::print_trailing_fixits (linenum_type row
)
2304 /* Build a list of correction instances for the line,
2305 potentially consolidating hints (for the sake of readability). */
2306 line_corrections
corrections (m_context
, m_exploc
.file
, row
);
2307 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
2309 const fixit_hint
*hint
= m_fixit_hints
[i
];
2311 /* Newline fixits are handled by layout::print_leading_fixits. */
2312 if (hint
->ends_with_newline_p ())
2315 if (hint
->affects_line_p (m_exploc
.file
, row
))
2316 corrections
.add_hint (hint
);
2319 /* Now print the corrections. */
2322 int column
= m_x_offset_display
;
2324 if (!corrections
.m_corrections
.is_empty ())
2325 start_annotation_line ();
2327 FOR_EACH_VEC_ELT (corrections
.m_corrections
, i
, c
)
2329 /* For now we assume each fixit hint can only touch one line. */
2330 if (c
->insertion_p ())
2332 /* This assumes the insertion just affects one line. */
2333 int start_column
= c
->m_printed_columns
.start
;
2334 move_to_column (&column
, start_column
, true);
2335 m_colorizer
.set_fixit_insert ();
2336 pp_string (m_pp
, c
->m_text
);
2337 m_colorizer
.set_normal_text ();
2338 column
+= c
->m_display_cols
;
2342 /* If the range of the replacement wasn't printed in the
2343 annotation line, then print an extra underline to
2344 indicate exactly what is being replaced.
2345 Always show it for removals. */
2346 int start_column
= c
->m_affected_columns
.start
;
2347 int finish_column
= c
->m_affected_columns
.finish
;
2348 if (!annotation_line_showed_range_p (row
, start_column
,
2350 || c
->m_byte_length
== 0)
2352 move_to_column (&column
, start_column
, true);
2353 m_colorizer
.set_fixit_delete ();
2354 for (; column
<= finish_column
; column
++)
2355 pp_character (m_pp
, '-');
2356 m_colorizer
.set_normal_text ();
2358 /* Print the replacement text. REPLACE also covers
2359 removals, so only do this extra work (potentially starting
2360 a new line) if we have actual replacement text. */
2361 if (c
->m_byte_length
> 0)
2363 move_to_column (&column
, start_column
, true);
2364 m_colorizer
.set_fixit_insert ();
2365 pp_string (m_pp
, c
->m_text
);
2366 m_colorizer
.set_normal_text ();
2367 column
+= c
->m_display_cols
;
2372 /* Add a trailing newline, if necessary. */
2373 move_to_column (&column
, 0, false);
2376 /* Disable any colorization and emit a newline. */
2379 layout::print_newline ()
2381 m_colorizer
.set_normal_text ();
2385 /* Return true if (ROW/COLUMN) is within a range of the layout.
2386 If it returns true, OUT_STATE is written to, with the
2387 range index, and whether we should draw the caret at
2388 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2389 whether all inputs and outputs are in bytes or display column units. */
2392 layout::get_state_at_point (/* Inputs. */
2393 linenum_type row
, int column
,
2394 int first_non_ws
, int last_non_ws
,
2395 enum column_unit col_unit
,
2397 point_state
*out_state
)
2399 layout_range
*range
;
2401 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2403 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
2404 /* Bail out early, so that such ranges don't affect underlining or
2405 source colorization. */
2408 if (range
->contains_point (row
, column
, col_unit
))
2410 out_state
->range_idx
= i
;
2412 /* Are we at the range's caret? is it visible? */
2413 out_state
->draw_caret_p
= false;
2414 if (range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
2415 && row
== range
->m_caret
.m_line
2416 && column
== range
->m_caret
.m_columns
[col_unit
])
2417 out_state
->draw_caret_p
= true;
2419 /* Within a multiline range, don't display any underline
2420 in any leading or trailing whitespace on a line.
2421 We do display carets, however. */
2422 if (!out_state
->draw_caret_p
)
2423 if (column
< first_non_ws
|| column
> last_non_ws
)
2426 /* We are within a range. */
2434 /* Helper function for use by layout::print_line when printing the
2435 annotation line under the source line.
2436 Get the display column beyond the rightmost one that could contain a caret
2437 or range marker, given that we stop rendering at trailing whitespace.
2438 ROW is the source line within the given file.
2439 CARET_COLUMN is the display column of range 0's caret.
2440 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2441 character of source (as determined when printing the source line). */
2444 layout::get_x_bound_for_row (linenum_type row
, int caret_column
,
2445 int last_non_ws_column
)
2447 int result
= caret_column
+ 1;
2449 layout_range
*range
;
2451 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2453 if (row
>= range
->m_start
.m_line
)
2455 if (range
->m_finish
.m_line
== row
)
2457 /* On the final line within a range; ensure that
2458 we render up to the end of the range. */
2459 const int disp_col
= range
->m_finish
.m_columns
[CU_DISPLAY_COLS
];
2460 if (result
<= disp_col
)
2461 result
= disp_col
+ 1;
2463 else if (row
< range
->m_finish
.m_line
)
2465 /* Within a multiline range; ensure that we render up to the
2466 last non-whitespace column. */
2467 if (result
<= last_non_ws_column
)
2468 result
= last_non_ws_column
+ 1;
2476 /* Given *COLUMN as an x-coordinate, print spaces to position
2477 successive output at DEST_COLUMN, printing a newline if necessary,
2478 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2479 left margin after any newline. */
2482 layout::move_to_column (int *column
, int dest_column
, bool add_left_margin
)
2484 /* Start a new line if we need to. */
2485 if (*column
> dest_column
)
2488 if (add_left_margin
)
2489 start_annotation_line ();
2490 *column
= m_x_offset_display
;
2493 while (*column
< dest_column
)
2500 /* For debugging layout issues, render a ruler giving column numbers
2501 (after the 1-column indent). */
2504 layout::show_ruler (int max_column
) const
2507 if (max_column
> 99)
2509 start_annotation_line ();
2511 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2512 if (column
% 10 == 0)
2513 pp_character (m_pp
, '0' + (column
/ 100) % 10);
2520 start_annotation_line ();
2522 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2523 if (column
% 10 == 0)
2524 pp_character (m_pp
, '0' + (column
/ 10) % 10);
2530 start_annotation_line ();
2532 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2533 pp_character (m_pp
, '0' + (column
% 10));
2537 /* Print leading fix-its (for new lines inserted before the source line)
2538 then the source line, followed by an annotation line
2539 consisting of any caret/underlines, then any fixits.
2540 If the source line can't be read, print nothing. */
2542 layout::print_line (linenum_type row
)
2544 char_span line
= location_get_source_line (m_exploc
.file
, row
);
2548 print_leading_fixits (row
);
2549 const line_bounds lbounds
2550 = print_source_line (row
, line
.get_buffer (), line
.length ());
2551 if (should_print_annotation_line_p (row
))
2552 print_annotation_line (row
, lbounds
);
2553 if (m_show_labels_p
)
2554 print_any_labels (row
);
2555 print_trailing_fixits (row
);
2558 } /* End of anonymous namespace. */
2560 /* If LOC is within the spans of lines that will already be printed for
2561 this gcc_rich_location, then add it as a secondary location and return true.
2563 Otherwise return false. */
2566 gcc_rich_location::add_location_if_nearby (location_t loc
,
2567 bool restrict_to_current_line_spans
,
2568 const range_label
*label
)
2570 /* Use the layout location-handling logic to sanitize LOC,
2571 filtering it to the current line spans within a temporary
2573 layout
layout (global_dc
, this, DK_ERROR
);
2574 location_range loc_range
;
2575 loc_range
.m_loc
= loc
;
2576 loc_range
.m_range_display_kind
= SHOW_RANGE_WITHOUT_CARET
;
2577 if (!layout
.maybe_add_location_range (&loc_range
, 0,
2578 restrict_to_current_line_spans
))
2581 add_range (loc
, SHOW_RANGE_WITHOUT_CARET
, label
);
2585 /* Print the physical source code corresponding to the location of
2586 this diagnostic, with additional annotations. */
2589 diagnostic_show_locus (diagnostic_context
* context
,
2590 rich_location
*richloc
,
2591 diagnostic_t diagnostic_kind
)
2593 location_t loc
= richloc
->get_loc ();
2594 /* Do nothing if source-printing has been disabled. */
2595 if (!context
->show_caret
)
2598 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2599 if (loc
<= BUILTINS_LOCATION
)
2602 /* Don't print the same source location twice in a row, unless we have
2603 fix-it hints, or multiple locations, or a label. */
2604 if (loc
== context
->last_location
2605 && richloc
->get_num_fixit_hints () == 0
2606 && richloc
->get_num_locations () == 1
2607 && richloc
->get_range (0)->m_label
== NULL
)
2610 context
->last_location
= loc
;
2612 layout
layout (context
, richloc
, diagnostic_kind
);
2613 for (int line_span_idx
= 0; line_span_idx
< layout
.get_num_line_spans ();
2616 const line_span
*line_span
= layout
.get_line_span (line_span_idx
);
2617 if (context
->show_line_numbers_p
)
2619 /* With line numbers, we should show whenever the line-numbering
2621 if (line_span_idx
> 0)
2622 layout
.print_gap_in_line_numbering ();
2626 /* Without line numbers, we print headings for some line spans. */
2627 if (layout
.print_heading_for_line_span_index_p (line_span_idx
))
2629 expanded_location exploc
2630 = layout
.get_expanded_location (line_span
);
2631 context
->start_span (context
, exploc
);
2634 /* Iterate over the lines within this span (using linenum_arith_t to
2635 avoid overflow with 0xffffffff causing an infinite loop). */
2636 linenum_arith_t last_line
= line_span
->get_last_line ();
2637 for (linenum_arith_t row
= line_span
->get_first_line ();
2638 row
<= last_line
; row
++)
2639 layout
.print_line (row
);
2645 namespace selftest
{
2647 /* Selftests for diagnostic_show_locus. */
2649 /* For precise tests of the layout, make clear where the source line will
2650 start. test_left_margin sets the total byte count from the left side of the
2651 screen to the start of source lines, after the line number and the separator,
2652 which consists of the three characters " | ". */
2653 static const int test_linenum_sep
= 3;
2654 static const int test_left_margin
= 7;
2656 /* Helper function for test_layout_x_offset_display_utf8(). */
2658 test_offset_impl (int caret_byte_col
, int max_width
,
2659 int expected_x_offset_display
,
2660 int left_margin
= test_left_margin
)
2662 test_diagnostic_context dc
;
2663 dc
.caret_max_width
= max_width
;
2664 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2665 the line number plus one space after. */
2666 dc
.min_margin_width
= left_margin
- test_linenum_sep
+ 1;
2667 dc
.show_line_numbers_p
= true;
2668 rich_location
richloc (line_table
,
2669 linemap_position_for_column (line_table
,
2671 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2672 ASSERT_EQ (left_margin
- test_linenum_sep
,
2673 test_layout
.get_linenum_width ());
2674 ASSERT_EQ (expected_x_offset_display
,
2675 test_layout
.get_x_offset_display ());
2678 /* Test that layout::calculate_x_offset_display() works. */
2680 test_layout_x_offset_display_utf8 (const line_table_case
&case_
)
2684 = "This line is very long, so that we can use it to test the logic for "
2685 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2686 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2689 /* Number of bytes in the line, subtracting one to remove the newline. */
2690 const int line_bytes
= strlen (content
) - 1;
2692 /* Number of display columns occupied by the line; each of the 2 emojis
2693 takes up 2 fewer display columns than it does bytes. */
2694 const int line_display_cols
= line_bytes
- 2*2;
2696 /* The column of the first emoji. Byte or display is the same as there are
2697 no multibyte characters earlier on the line. */
2698 const int emoji_col
= 102;
2700 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2701 line_table_test
ltt (case_
);
2703 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2705 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
2707 /* Don't attempt to run the tests if column data might be unavailable. */
2708 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2711 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
2712 ASSERT_EQ (1, LOCATION_LINE (line_end
));
2713 ASSERT_EQ (line_bytes
, LOCATION_COLUMN (line_end
));
2715 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
2716 ASSERT_EQ (line_display_cols
,
2717 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
2719 ASSERT_EQ (line_display_cols
,
2720 location_compute_display_column (expand_location (line_end
),
2722 ASSERT_EQ (0, memcmp (lspan
.get_buffer () + (emoji_col
- 1),
2723 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2725 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2727 /* No constraint on the width -> no offset. */
2728 test_offset_impl (emoji_col
, 0, 0);
2730 /* Caret is before the beginning -> no offset. */
2731 test_offset_impl (0, 100, 0);
2733 /* Caret is past the end of the line -> no offset. */
2734 test_offset_impl (line_bytes
+1, 100, 0);
2736 /* Line fits in the display -> no offset. */
2737 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
, 0);
2738 test_offset_impl (emoji_col
, line_display_cols
+ test_left_margin
, 0);
2740 /* Line is too long for the display but caret location is OK
2741 anyway -> no offset. */
2742 static const int small_width
= 24;
2743 test_offset_impl (1, small_width
, 0);
2745 /* Width constraint is very small -> no offset. */
2746 test_offset_impl (emoji_col
, CARET_LINE_MARGIN
, 0);
2748 /* Line would be offset, but due to large line numbers, offsetting
2749 would remove the whole line -> no offset. */
2750 static const int huge_left_margin
= 100;
2751 test_offset_impl (emoji_col
, huge_left_margin
, 0, huge_left_margin
);
2753 /* Line is the same length as the display, but the line number makes it too
2754 long, so offset is required. Caret is at the end so padding on the right
2755 is not in effect. */
2756 for (int excess
= 1; excess
<= 3; ++excess
)
2757 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
- excess
,
2760 /* Line is much too long for the display, caret is near the end ->
2761 offset should be such that the line fits in the display and caret
2762 remains the same distance from the end that it was. */
2763 for (int caret_offset
= 0, max_offset
= MIN (CARET_LINE_MARGIN
, 10);
2764 caret_offset
<= max_offset
; ++caret_offset
)
2765 test_offset_impl (line_bytes
- caret_offset
, small_width
,
2766 line_display_cols
+ test_left_margin
- small_width
);
2768 /* As previous case but caret is closer to the middle; now we want it to end
2769 up CARET_LINE_MARGIN bytes from the end. */
2770 ASSERT_GT (line_display_cols
- emoji_col
, CARET_LINE_MARGIN
);
2771 test_offset_impl (emoji_col
, small_width
,
2772 emoji_col
+ test_left_margin
2773 - (small_width
- CARET_LINE_MARGIN
));
2775 /* Test that the source line is offset as expected when printed. */
2777 test_diagnostic_context dc
;
2778 dc
.caret_max_width
= small_width
- 6;
2779 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2780 dc
.show_line_numbers_p
= true;
2781 dc
.show_ruler_p
= true;
2782 rich_location
richloc (line_table
,
2783 linemap_position_for_column (line_table
,
2785 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2786 test_layout
.print_line (1);
2787 ASSERT_STREQ (" | 1 \n"
2789 " | 234567890123456789\n"
2790 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2791 "that occupies 8 bytes and 4 display columns, starting at "
2794 pp_formatted_text (dc
.printer
));
2797 /* Similar to the previous example, but now the offset called for would split
2798 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2799 it with a padding space in this case. */
2801 test_diagnostic_context dc
;
2802 dc
.caret_max_width
= small_width
- 5;
2803 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2804 dc
.show_line_numbers_p
= true;
2805 dc
.show_ruler_p
= true;
2806 rich_location
richloc (line_table
,
2807 linemap_position_for_column (line_table
,
2809 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2810 test_layout
.print_line (1);
2811 ASSERT_STREQ (" | 1 1 \n"
2813 " | 3456789012345678901\n"
2814 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2815 "that occupies 8 bytes and 4 display columns, starting at "
2818 pp_formatted_text (dc
.printer
));
2824 test_layout_x_offset_display_tab (const line_table_case
&case_
)
2827 = "This line is very long, so that we can use it to test the logic for "
2828 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
2829 "a variable number of display columns, starting at column #103.\n";
2831 /* Number of bytes in the line, subtracting one to remove the newline. */
2832 const int line_bytes
= strlen (content
) - 1;
2834 /* The column where the tab begins. Byte or display is the same as there are
2835 no multibyte characters earlier on the line. */
2836 const int tab_col
= 103;
2838 /* Effective extra size of the tab beyond what a single space would have taken
2839 up, indexed by tabstop. */
2840 static const int num_tabstops
= 11;
2841 int extra_width
[num_tabstops
];
2842 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2844 const int this_tab_size
= tabstop
- (tab_col
- 1) % tabstop
;
2845 extra_width
[tabstop
] = this_tab_size
- 1;
2847 /* Example of this calculation: if tabstop is 10, the tab starting at column
2848 #103 has to expand into 8 spaces, covering columns 103-110, so that the
2849 next character is at column #111. So it takes up 7 more columns than
2850 a space would have taken up. */
2851 ASSERT_EQ (7, extra_width
[10]);
2853 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2854 line_table_test
ltt (case_
);
2856 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2858 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
2860 /* Don't attempt to run the tests if column data might be unavailable. */
2861 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2864 /* Check that cpp_display_width handles the tabs as expected. */
2865 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
2866 ASSERT_EQ ('\t', *(lspan
.get_buffer () + (tab_col
- 1)));
2867 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2869 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
2870 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
2872 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
2873 location_compute_display_column (expand_location (line_end
),
2877 /* Check that the tab is expanded to the expected number of spaces. */
2878 rich_location
richloc (line_table
,
2879 linemap_position_for_column (line_table
,
2881 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2883 test_diagnostic_context dc
;
2884 dc
.tabstop
= tabstop
;
2885 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2886 test_layout
.print_line (1);
2887 const char *out
= pp_formatted_text (dc
.printer
);
2888 ASSERT_EQ (NULL
, strchr (out
, '\t'));
2889 const char *left_quote
= strchr (out
, '`');
2890 const char *right_quote
= strchr (out
, '\'');
2891 ASSERT_NE (NULL
, left_quote
);
2892 ASSERT_NE (NULL
, right_quote
);
2893 ASSERT_EQ (right_quote
- left_quote
, extra_width
[tabstop
] + 2);
2896 /* Check that the line is offset properly and that the tab is broken up
2897 into the expected number of spaces when it is the last character skipped
2899 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2901 test_diagnostic_context dc
;
2902 dc
.tabstop
= tabstop
;
2903 static const int small_width
= 24;
2904 dc
.caret_max_width
= small_width
- 4;
2905 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2906 dc
.show_line_numbers_p
= true;
2907 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2908 test_layout
.print_line (1);
2910 /* We have arranged things so that two columns will be printed before
2911 the caret. If the tab results in more than one space, this should
2912 produce two spaces in the output; otherwise, it will be a single space
2913 preceded by the opening quote before the tab character. */
2915 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
2916 "display columns, starting at column #103.\n"
2919 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
2920 "display columns, starting at column #103.\n"
2922 const char *expected_output
= (extra_width
[tabstop
] ? output1
: output2
);
2923 ASSERT_STREQ (expected_output
, pp_formatted_text (dc
.printer
));
2928 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2931 test_diagnostic_show_locus_unknown_location ()
2933 test_diagnostic_context dc
;
2934 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
2935 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2936 ASSERT_STREQ ("", pp_formatted_text (dc
.printer
));
2939 /* Verify that diagnostic_show_locus works sanely for various
2942 All of these work on the following 1-line source file:
2945 "foo = bar.field;\n"
2946 which is set up by test_diagnostic_show_locus_one_liner and calls
2952 test_one_liner_simple_caret ()
2954 test_diagnostic_context dc
;
2955 location_t caret
= linemap_position_for_column (line_table
, 10);
2956 rich_location
richloc (line_table
, caret
);
2957 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2958 ASSERT_STREQ (" foo = bar.field;\n"
2960 pp_formatted_text (dc
.printer
));
2963 /* Caret and range. */
2966 test_one_liner_caret_and_range ()
2968 test_diagnostic_context dc
;
2969 location_t caret
= linemap_position_for_column (line_table
, 10);
2970 location_t start
= linemap_position_for_column (line_table
, 7);
2971 location_t finish
= linemap_position_for_column (line_table
, 15);
2972 location_t loc
= make_location (caret
, start
, finish
);
2973 rich_location
richloc (line_table
, loc
);
2974 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2975 ASSERT_STREQ (" foo = bar.field;\n"
2977 pp_formatted_text (dc
.printer
));
2980 /* Multiple ranges and carets. */
2983 test_one_liner_multiple_carets_and_ranges ()
2985 test_diagnostic_context dc
;
2987 = make_location (linemap_position_for_column (line_table
, 2),
2988 linemap_position_for_column (line_table
, 1),
2989 linemap_position_for_column (line_table
, 3));
2990 dc
.caret_chars
[0] = 'A';
2993 = make_location (linemap_position_for_column (line_table
, 8),
2994 linemap_position_for_column (line_table
, 7),
2995 linemap_position_for_column (line_table
, 9));
2996 dc
.caret_chars
[1] = 'B';
2999 = make_location (linemap_position_for_column (line_table
, 13),
3000 linemap_position_for_column (line_table
, 11),
3001 linemap_position_for_column (line_table
, 15));
3002 dc
.caret_chars
[2] = 'C';
3004 rich_location
richloc (line_table
, foo
);
3005 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3006 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3007 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3008 ASSERT_STREQ (" foo = bar.field;\n"
3010 pp_formatted_text (dc
.printer
));
3013 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3016 test_one_liner_fixit_insert_before ()
3018 test_diagnostic_context dc
;
3019 location_t caret
= linemap_position_for_column (line_table
, 7);
3020 rich_location
richloc (line_table
, caret
);
3021 richloc
.add_fixit_insert_before ("&");
3022 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3023 ASSERT_STREQ (" foo = bar.field;\n"
3026 pp_formatted_text (dc
.printer
));
3029 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3032 test_one_liner_fixit_insert_after ()
3034 test_diagnostic_context dc
;
3035 location_t start
= linemap_position_for_column (line_table
, 1);
3036 location_t finish
= linemap_position_for_column (line_table
, 3);
3037 location_t foo
= make_location (start
, start
, finish
);
3038 rich_location
richloc (line_table
, foo
);
3039 richloc
.add_fixit_insert_after ("[0]");
3040 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3041 ASSERT_STREQ (" foo = bar.field;\n"
3044 pp_formatted_text (dc
.printer
));
3047 /* Removal fix-it hint: removal of the ".field".
3048 Also verify the interaction of pp_set_prefix with rulers and
3052 test_one_liner_fixit_remove ()
3054 location_t start
= linemap_position_for_column (line_table
, 10);
3055 location_t finish
= linemap_position_for_column (line_table
, 15);
3056 location_t dot
= make_location (start
, start
, finish
);
3057 rich_location
richloc (line_table
, dot
);
3058 richloc
.add_fixit_remove ();
3062 test_diagnostic_context dc
;
3063 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3064 ASSERT_STREQ (" foo = bar.field;\n"
3067 pp_formatted_text (dc
.printer
));
3070 /* Test of adding a prefix. */
3072 test_diagnostic_context dc
;
3073 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3074 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3075 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3076 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3077 "TEST PREFIX: ^~~~~~\n"
3078 "TEST PREFIX: ------\n",
3079 pp_formatted_text (dc
.printer
));
3082 /* Normal, with ruler. */
3084 test_diagnostic_context dc
;
3085 dc
.show_ruler_p
= true;
3086 dc
.caret_max_width
= 104;
3087 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3088 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3089 " 1 2 3 4 5 6 7 8 9 0 \n"
3090 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3091 " foo = bar.field;\n"
3094 pp_formatted_text (dc
.printer
));
3097 /* Test of adding a prefix, with ruler. */
3099 test_diagnostic_context dc
;
3100 dc
.show_ruler_p
= true;
3101 dc
.caret_max_width
= 50;
3102 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3103 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3104 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3105 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3106 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3107 "TEST PREFIX: foo = bar.field;\n"
3108 "TEST PREFIX: ^~~~~~\n"
3109 "TEST PREFIX: ------\n",
3110 pp_formatted_text (dc
.printer
));
3113 /* Test of adding a prefix, with ruler and line numbers. */
3115 test_diagnostic_context dc
;
3116 dc
.show_ruler_p
= true;
3117 dc
.caret_max_width
= 50;
3118 dc
.show_line_numbers_p
= true;
3119 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3120 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3121 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3122 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3123 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3124 "TEST PREFIX: 1 | foo = bar.field;\n"
3125 "TEST PREFIX: | ^~~~~~\n"
3126 "TEST PREFIX: | ------\n",
3127 pp_formatted_text (dc
.printer
));
3131 /* Replace fix-it hint: replacing "field" with "m_field". */
3134 test_one_liner_fixit_replace ()
3136 test_diagnostic_context dc
;
3137 location_t start
= linemap_position_for_column (line_table
, 11);
3138 location_t finish
= linemap_position_for_column (line_table
, 15);
3139 location_t field
= make_location (start
, start
, finish
);
3140 rich_location
richloc (line_table
, field
);
3141 richloc
.add_fixit_replace ("m_field");
3142 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3143 ASSERT_STREQ (" foo = bar.field;\n"
3146 pp_formatted_text (dc
.printer
));
3149 /* Replace fix-it hint: replacing "field" with "m_field",
3150 but where the caret was elsewhere. */
3153 test_one_liner_fixit_replace_non_equal_range ()
3155 test_diagnostic_context dc
;
3156 location_t equals
= linemap_position_for_column (line_table
, 5);
3157 location_t start
= linemap_position_for_column (line_table
, 11);
3158 location_t finish
= linemap_position_for_column (line_table
, 15);
3159 rich_location
richloc (line_table
, equals
);
3161 range
.m_start
= start
;
3162 range
.m_finish
= finish
;
3163 richloc
.add_fixit_replace (range
, "m_field");
3164 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3165 /* The replacement range is not indicated in the annotation line, so
3166 it should be indicated via an additional underline. */
3167 ASSERT_STREQ (" foo = bar.field;\n"
3171 pp_formatted_text (dc
.printer
));
3174 /* Replace fix-it hint: replacing "field" with "m_field",
3175 where the caret was elsewhere, but where a secondary range
3176 exactly covers "field". */
3179 test_one_liner_fixit_replace_equal_secondary_range ()
3181 test_diagnostic_context dc
;
3182 location_t equals
= linemap_position_for_column (line_table
, 5);
3183 location_t start
= linemap_position_for_column (line_table
, 11);
3184 location_t finish
= linemap_position_for_column (line_table
, 15);
3185 rich_location
richloc (line_table
, equals
);
3186 location_t field
= make_location (start
, start
, finish
);
3187 richloc
.add_range (field
);
3188 richloc
.add_fixit_replace (field
, "m_field");
3189 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3190 /* The replacement range is indicated in the annotation line,
3191 so it shouldn't be indicated via an additional underline. */
3192 ASSERT_STREQ (" foo = bar.field;\n"
3195 pp_formatted_text (dc
.printer
));
3198 /* Verify that we can use ad-hoc locations when adding fixits to a
3202 test_one_liner_fixit_validation_adhoc_locations ()
3204 /* Generate a range that's too long to be packed, so must
3205 be stored as an ad-hoc location (given the defaults
3206 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3207 const location_t c7
= linemap_position_for_column (line_table
, 7);
3208 const location_t c47
= linemap_position_for_column (line_table
, 47);
3209 const location_t loc
= make_location (c7
, c7
, c47
);
3211 if (c47
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3214 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
3218 rich_location
richloc (line_table
, loc
);
3219 richloc
.add_fixit_insert_before (loc
, "test");
3220 /* It should not have been discarded by the validator. */
3221 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3223 test_diagnostic_context dc
;
3224 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3225 ASSERT_STREQ (" foo = bar.field;\n"
3228 pp_formatted_text (dc
.printer
));
3233 rich_location
richloc (line_table
, loc
);
3234 source_range range
= source_range::from_locations (loc
, c47
);
3235 richloc
.add_fixit_remove (range
);
3236 /* It should not have been discarded by the validator. */
3237 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3239 test_diagnostic_context dc
;
3240 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3241 ASSERT_STREQ (" foo = bar.field;\n"
3243 " -----------------------------------------\n",
3244 pp_formatted_text (dc
.printer
));
3249 rich_location
richloc (line_table
, loc
);
3250 source_range range
= source_range::from_locations (loc
, c47
);
3251 richloc
.add_fixit_replace (range
, "test");
3252 /* It should not have been discarded by the validator. */
3253 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3255 test_diagnostic_context dc
;
3256 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3257 ASSERT_STREQ (" foo = bar.field;\n"
3260 pp_formatted_text (dc
.printer
));
3264 /* Test of consolidating insertions at the same location. */
3267 test_one_liner_many_fixits_1 ()
3269 test_diagnostic_context dc
;
3270 location_t equals
= linemap_position_for_column (line_table
, 5);
3271 rich_location
richloc (line_table
, equals
);
3272 for (int i
= 0; i
< 19; i
++)
3273 richloc
.add_fixit_insert_before ("a");
3274 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3275 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3276 ASSERT_STREQ (" foo = bar.field;\n"
3278 " aaaaaaaaaaaaaaaaaaa\n",
3279 pp_formatted_text (dc
.printer
));
3282 /* Ensure that we can add an arbitrary number of fix-it hints to a
3283 rich_location, even if they are not consolidated. */
3286 test_one_liner_many_fixits_2 ()
3288 test_diagnostic_context dc
;
3289 location_t equals
= linemap_position_for_column (line_table
, 5);
3290 rich_location
richloc (line_table
, equals
);
3291 for (int i
= 0; i
< 19; i
++)
3293 location_t loc
= linemap_position_for_column (line_table
, (i
* 2) + 1);
3294 richloc
.add_fixit_insert_before (loc
, "a");
3296 ASSERT_EQ (19, richloc
.get_num_fixit_hints ());
3297 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3298 ASSERT_STREQ (" foo = bar.field;\n"
3300 " a a a a a a a a a a a a a a a a a a a\n",
3301 pp_formatted_text (dc
.printer
));
3304 /* Test of labeling the ranges within a rich_location. */
3307 test_one_liner_labels ()
3310 = make_location (linemap_position_for_column (line_table
, 1),
3311 linemap_position_for_column (line_table
, 1),
3312 linemap_position_for_column (line_table
, 3));
3314 = make_location (linemap_position_for_column (line_table
, 7),
3315 linemap_position_for_column (line_table
, 7),
3316 linemap_position_for_column (line_table
, 9));
3318 = make_location (linemap_position_for_column (line_table
, 11),
3319 linemap_position_for_column (line_table
, 11),
3320 linemap_position_for_column (line_table
, 15));
3322 /* Example where all the labels fit on one line. */
3324 text_range_label
label0 ("0");
3325 text_range_label
label1 ("1");
3326 text_range_label
label2 ("2");
3327 gcc_rich_location
richloc (foo
, &label0
);
3328 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3329 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3332 test_diagnostic_context dc
;
3333 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3334 ASSERT_STREQ (" foo = bar.field;\n"
3338 pp_formatted_text (dc
.printer
));
3341 /* Verify that we can disable label-printing. */
3343 test_diagnostic_context dc
;
3344 dc
.show_labels_p
= false;
3345 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3346 ASSERT_STREQ (" foo = bar.field;\n"
3348 pp_formatted_text (dc
.printer
));
3352 /* Example where the labels need extra lines. */
3354 text_range_label
label0 ("label 0");
3355 text_range_label
label1 ("label 1");
3356 text_range_label
label2 ("label 2");
3357 gcc_rich_location
richloc (foo
, &label0
);
3358 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3359 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3361 test_diagnostic_context dc
;
3362 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3363 ASSERT_STREQ (" foo = bar.field;\n"
3369 pp_formatted_text (dc
.printer
));
3372 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3373 but label 1 just touches label 2. */
3375 text_range_label
label0 ("aaaaa");
3376 text_range_label
label1 ("bbbb");
3377 text_range_label
label2 ("c");
3378 gcc_rich_location
richloc (foo
, &label0
);
3379 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3380 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3382 test_diagnostic_context dc
;
3383 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3384 ASSERT_STREQ (" foo = bar.field;\n"
3389 pp_formatted_text (dc
.printer
));
3392 /* Example of out-of-order ranges (thus requiring a sort). */
3394 text_range_label
label0 ("0");
3395 text_range_label
label1 ("1");
3396 text_range_label
label2 ("2");
3397 gcc_rich_location
richloc (field
, &label0
);
3398 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3399 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3401 test_diagnostic_context dc
;
3402 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3403 ASSERT_STREQ (" foo = bar.field;\n"
3407 pp_formatted_text (dc
.printer
));
3410 /* Ensure we don't ICE if multiple ranges with labels are on
3413 text_range_label
label0 ("label 0");
3414 text_range_label
label1 ("label 1");
3415 text_range_label
label2 ("label 2");
3416 gcc_rich_location
richloc (bar
, &label0
);
3417 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3418 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3420 test_diagnostic_context dc
;
3421 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3422 ASSERT_STREQ (" foo = bar.field;\n"
3428 pp_formatted_text (dc
.printer
));
3431 /* Example of out-of-order ranges (thus requiring a sort), where
3432 they overlap, and there are multiple ranges on the same point. */
3434 text_range_label
label_0a ("label 0a");
3435 text_range_label
label_1a ("label 1a");
3436 text_range_label
label_2a ("label 2a");
3437 text_range_label
label_0b ("label 0b");
3438 text_range_label
label_1b ("label 1b");
3439 text_range_label
label_2b ("label 2b");
3440 text_range_label
label_0c ("label 0c");
3441 text_range_label
label_1c ("label 1c");
3442 text_range_label
label_2c ("label 2c");
3443 gcc_rich_location
richloc (field
, &label_0a
);
3444 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1a
);
3445 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2a
);
3447 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0b
);
3448 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1b
);
3449 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2b
);
3451 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0c
);
3452 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1c
);
3453 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2c
);
3455 test_diagnostic_context dc
;
3456 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3457 ASSERT_STREQ (" foo = bar.field;\n"
3469 pp_formatted_text (dc
.printer
));
3472 /* Verify that a NULL result from range_label::get_text is
3473 handled gracefully. */
3475 text_range_label
label (NULL
);
3476 gcc_rich_location
richloc (bar
, &label
);
3478 test_diagnostic_context dc
;
3479 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3480 ASSERT_STREQ (" foo = bar.field;\n"
3482 pp_formatted_text (dc
.printer
));
3485 /* TODO: example of formatted printing (needs to be in
3486 gcc-rich-location.c due to Makefile.in issues). */
3489 /* Run the various one-liner tests. */
3492 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
3494 /* Create a tempfile and write some text to it.
3495 ....................0000000001111111.
3496 ....................1234567890123456. */
3497 const char *content
= "foo = bar.field;\n";
3498 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3499 line_table_test
ltt (case_
);
3501 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3503 location_t line_end
= linemap_position_for_column (line_table
, 16);
3505 /* Don't attempt to run the tests if column data might be unavailable. */
3506 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3509 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
3510 ASSERT_EQ (1, LOCATION_LINE (line_end
));
3511 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
3513 test_one_liner_simple_caret ();
3514 test_one_liner_caret_and_range ();
3515 test_one_liner_multiple_carets_and_ranges ();
3516 test_one_liner_fixit_insert_before ();
3517 test_one_liner_fixit_insert_after ();
3518 test_one_liner_fixit_remove ();
3519 test_one_liner_fixit_replace ();
3520 test_one_liner_fixit_replace_non_equal_range ();
3521 test_one_liner_fixit_replace_equal_secondary_range ();
3522 test_one_liner_fixit_validation_adhoc_locations ();
3523 test_one_liner_many_fixits_1 ();
3524 test_one_liner_many_fixits_2 ();
3525 test_one_liner_labels ();
3528 /* Version of all one-liner tests exercising multibyte awareness. For
3529 simplicity we stick to using two multibyte characters in the test, U+1F602
3530 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3531 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3532 below asserts would be easier to read if we used UTF-8 directly in the
3533 string constants, but it seems better not to demand the host compiler
3534 support this, when it isn't otherwise necessary. Instead, whenever an
3535 extended character appears in a string, we put a line break after it so that
3536 all succeeding characters can appear visually at the correct display column.
3538 All of these work on the following 1-line source file:
3540 .0000000001111111111222222 display
3541 .1234567890123456789012345 columns
3542 "SS_foo = P_bar.SS_fieldP;\n"
3543 .0000000111111111222222223 byte
3544 .1356789012456789134567891 columns
3546 which is set up by test_diagnostic_show_locus_one_liner and calls
3547 them. Here SS represents the two display columns for the U+1F602 emoji and
3548 P represents the one display column for the U+03C0 pi symbol. */
3553 test_one_liner_simple_caret_utf8 ()
3555 test_diagnostic_context dc
;
3556 location_t caret
= linemap_position_for_column (line_table
, 18);
3557 rich_location
richloc (line_table
, caret
);
3558 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3559 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3561 "_bar.\xf0\x9f\x98\x82"
3565 pp_formatted_text (dc
.printer
));
3568 /* Caret and range. */
3570 test_one_liner_caret_and_range_utf8 ()
3572 test_diagnostic_context dc
;
3573 location_t caret
= linemap_position_for_column (line_table
, 18);
3574 location_t start
= linemap_position_for_column (line_table
, 12);
3575 location_t finish
= linemap_position_for_column (line_table
, 30);
3576 location_t loc
= make_location (caret
, start
, finish
);
3577 rich_location
richloc (line_table
, loc
);
3578 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3579 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3581 "_bar.\xf0\x9f\x98\x82"
3584 " ~~~~~^~~~~~~~~~\n",
3585 pp_formatted_text (dc
.printer
));
3588 /* Multiple ranges and carets. */
3591 test_one_liner_multiple_carets_and_ranges_utf8 ()
3593 test_diagnostic_context dc
;
3595 = make_location (linemap_position_for_column (line_table
, 7),
3596 linemap_position_for_column (line_table
, 1),
3597 linemap_position_for_column (line_table
, 8));
3598 dc
.caret_chars
[0] = 'A';
3601 = make_location (linemap_position_for_column (line_table
, 16),
3602 linemap_position_for_column (line_table
, 12),
3603 linemap_position_for_column (line_table
, 17));
3604 dc
.caret_chars
[1] = 'B';
3607 = make_location (linemap_position_for_column (line_table
, 26),
3608 linemap_position_for_column (line_table
, 19),
3609 linemap_position_for_column (line_table
, 30));
3610 dc
.caret_chars
[2] = 'C';
3611 rich_location
richloc (line_table
, foo
);
3612 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3613 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3614 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3615 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3617 "_bar.\xf0\x9f\x98\x82"
3620 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3621 pp_formatted_text (dc
.printer
));
3624 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3627 test_one_liner_fixit_insert_before_utf8 ()
3629 test_diagnostic_context dc
;
3630 location_t caret
= linemap_position_for_column (line_table
, 12);
3631 rich_location
richloc (line_table
, caret
);
3632 richloc
.add_fixit_insert_before ("&");
3633 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3634 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3636 "_bar.\xf0\x9f\x98\x82"
3641 pp_formatted_text (dc
.printer
));
3644 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3647 test_one_liner_fixit_insert_after_utf8 ()
3649 test_diagnostic_context dc
;
3650 location_t start
= linemap_position_for_column (line_table
, 1);
3651 location_t finish
= linemap_position_for_column (line_table
, 8);
3652 location_t foo
= make_location (start
, start
, finish
);
3653 rich_location
richloc (line_table
, foo
);
3654 richloc
.add_fixit_insert_after ("[0]");
3655 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3656 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3658 "_bar.\xf0\x9f\x98\x82"
3663 pp_formatted_text (dc
.printer
));
3666 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3669 test_one_liner_fixit_remove_utf8 ()
3671 test_diagnostic_context dc
;
3672 location_t start
= linemap_position_for_column (line_table
, 18);
3673 location_t finish
= linemap_position_for_column (line_table
, 30);
3674 location_t dot
= make_location (start
, start
, finish
);
3675 rich_location
richloc (line_table
, dot
);
3676 richloc
.add_fixit_remove ();
3677 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3678 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3680 "_bar.\xf0\x9f\x98\x82"
3685 pp_formatted_text (dc
.printer
));
3688 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3691 test_one_liner_fixit_replace_utf8 ()
3693 test_diagnostic_context dc
;
3694 location_t start
= linemap_position_for_column (line_table
, 19);
3695 location_t finish
= linemap_position_for_column (line_table
, 30);
3696 location_t field
= make_location (start
, start
, finish
);
3697 rich_location
richloc (line_table
, field
);
3698 richloc
.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3699 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3700 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3702 "_bar.\xf0\x9f\x98\x82"
3706 " m_\xf0\x9f\x98\x82"
3708 pp_formatted_text (dc
.printer
));
3711 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3712 but where the caret was elsewhere. */
3715 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3717 test_diagnostic_context dc
;
3718 location_t equals
= linemap_position_for_column (line_table
, 10);
3719 location_t start
= linemap_position_for_column (line_table
, 19);
3720 location_t finish
= linemap_position_for_column (line_table
, 30);
3721 rich_location
richloc (line_table
, equals
);
3723 range
.m_start
= start
;
3724 range
.m_finish
= finish
;
3725 richloc
.add_fixit_replace (range
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3726 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3727 /* The replacement range is not indicated in the annotation line, so
3728 it should be indicated via an additional underline. */
3729 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3731 "_bar.\xf0\x9f\x98\x82"
3736 " m_\xf0\x9f\x98\x82"
3738 pp_formatted_text (dc
.printer
));
3741 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3742 where the caret was elsewhere, but where a secondary range
3743 exactly covers "field". */
3746 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3748 test_diagnostic_context dc
;
3749 location_t equals
= linemap_position_for_column (line_table
, 10);
3750 location_t start
= linemap_position_for_column (line_table
, 19);
3751 location_t finish
= linemap_position_for_column (line_table
, 30);
3752 rich_location
richloc (line_table
, equals
);
3753 location_t field
= make_location (start
, start
, finish
);
3754 richloc
.add_range (field
);
3755 richloc
.add_fixit_replace (field
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3756 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3757 /* The replacement range is indicated in the annotation line,
3758 so it shouldn't be indicated via an additional underline. */
3759 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3761 "_bar.\xf0\x9f\x98\x82"
3765 " m_\xf0\x9f\x98\x82"
3767 pp_formatted_text (dc
.printer
));
3770 /* Verify that we can use ad-hoc locations when adding fixits to a
3774 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3776 /* Generate a range that's too long to be packed, so must
3777 be stored as an ad-hoc location (given the defaults
3778 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3779 const location_t c12
= linemap_position_for_column (line_table
, 12);
3780 const location_t c52
= linemap_position_for_column (line_table
, 52);
3781 const location_t loc
= make_location (c12
, c12
, c52
);
3783 if (c52
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3786 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
3790 rich_location
richloc (line_table
, loc
);
3791 richloc
.add_fixit_insert_before (loc
, "test");
3792 /* It should not have been discarded by the validator. */
3793 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3795 test_diagnostic_context dc
;
3796 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3797 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3799 "_bar.\xf0\x9f\x98\x82"
3802 " ^~~~~~~~~~~~~~~~ \n"
3804 pp_formatted_text (dc
.printer
));
3809 rich_location
richloc (line_table
, loc
);
3810 source_range range
= source_range::from_locations (loc
, c52
);
3811 richloc
.add_fixit_remove (range
);
3812 /* It should not have been discarded by the validator. */
3813 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3815 test_diagnostic_context dc
;
3816 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3817 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3819 "_bar.\xf0\x9f\x98\x82"
3822 " ^~~~~~~~~~~~~~~~ \n"
3823 " -------------------------------------\n",
3824 pp_formatted_text (dc
.printer
));
3829 rich_location
richloc (line_table
, loc
);
3830 source_range range
= source_range::from_locations (loc
, c52
);
3831 richloc
.add_fixit_replace (range
, "test");
3832 /* It should not have been discarded by the validator. */
3833 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3835 test_diagnostic_context dc
;
3836 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3837 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3839 "_bar.\xf0\x9f\x98\x82"
3842 " ^~~~~~~~~~~~~~~~ \n"
3844 pp_formatted_text (dc
.printer
));
3848 /* Test of consolidating insertions at the same location. */
3851 test_one_liner_many_fixits_1_utf8 ()
3853 test_diagnostic_context dc
;
3854 location_t equals
= linemap_position_for_column (line_table
, 10);
3855 rich_location
richloc (line_table
, equals
);
3856 for (int i
= 0; i
< 19; i
++)
3857 richloc
.add_fixit_insert_before (i
& 1 ? "@" : "\xcf\x80");
3858 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3859 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3860 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3862 "_bar.\xf0\x9f\x98\x82"
3866 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3867 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3868 pp_formatted_text (dc
.printer
));
3871 /* Ensure that we can add an arbitrary number of fix-it hints to a
3872 rich_location, even if they are not consolidated. */
3875 test_one_liner_many_fixits_2_utf8 ()
3877 test_diagnostic_context dc
;
3878 location_t equals
= linemap_position_for_column (line_table
, 10);
3879 rich_location
richloc (line_table
, equals
);
3880 const int nlocs
= 19;
3881 int locs
[nlocs
] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3882 34, 36, 38, 40, 42, 44};
3883 for (int i
= 0; i
!= nlocs
; ++i
)
3885 location_t loc
= linemap_position_for_column (line_table
, locs
[i
]);
3886 richloc
.add_fixit_insert_before (loc
, i
& 1 ? "@" : "\xcf\x80");
3889 ASSERT_EQ (nlocs
, richloc
.get_num_fixit_hints ());
3890 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3891 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3893 "_bar.\xf0\x9f\x98\x82"
3897 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3898 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3899 pp_formatted_text (dc
.printer
));
3902 /* Test of labeling the ranges within a rich_location. */
3905 test_one_liner_labels_utf8 ()
3908 = make_location (linemap_position_for_column (line_table
, 1),
3909 linemap_position_for_column (line_table
, 1),
3910 linemap_position_for_column (line_table
, 8));
3912 = make_location (linemap_position_for_column (line_table
, 12),
3913 linemap_position_for_column (line_table
, 12),
3914 linemap_position_for_column (line_table
, 17));
3916 = make_location (linemap_position_for_column (line_table
, 19),
3917 linemap_position_for_column (line_table
, 19),
3918 linemap_position_for_column (line_table
, 30));
3920 /* Example where all the labels fit on one line. */
3922 /* These three labels contain multibyte characters such that their byte
3923 lengths are respectively (12, 10, 18), but their display widths are only
3924 (6, 5, 9). All three fit on the line when considering the display
3925 widths, but not when considering the byte widths, so verify that we do
3926 indeed put them all on one line. */
3927 text_range_label label0
3928 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3929 text_range_label label1
3930 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3931 text_range_label label2
3932 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3934 gcc_rich_location
richloc (foo
, &label0
);
3935 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3936 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3939 test_diagnostic_context dc
;
3940 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3941 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3943 "_bar.\xf0\x9f\x98\x82"
3946 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3948 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3949 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3950 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3951 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3952 pp_formatted_text (dc
.printer
));
3957 /* Example where the labels need extra lines. */
3959 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
3960 text_range_label
label1 ("label 1\xcf\x80");
3961 text_range_label
label2 ("label 2\xcf\x80");
3962 gcc_rich_location
richloc (foo
, &label0
);
3963 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3964 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3966 test_diagnostic_context dc
;
3967 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3969 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3971 "_bar.\xf0\x9f\x98\x82"
3974 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3976 " | | label 2\xcf\x80\n"
3977 " | label 1\xcf\x80\n"
3978 " label 0\xf0\x9f\x98\x82\n",
3979 pp_formatted_text (dc
.printer
));
3982 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3983 but label 1 just touches label 2. */
3985 text_range_label
label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3986 text_range_label
label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3987 text_range_label
label2 ("c");
3988 gcc_rich_location
richloc (foo
, &label0
);
3989 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3990 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3992 test_diagnostic_context dc
;
3993 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3994 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3996 "_bar.\xf0\x9f\x98\x82"
3999 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4002 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4003 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4004 pp_formatted_text (dc
.printer
));
4008 /* Make sure that colorization codes don't interrupt a multibyte
4009 sequence, which would corrupt it. */
4011 test_one_liner_colorized_utf8 ()
4013 test_diagnostic_context dc
;
4014 dc
.colorize_source_p
= true;
4015 diagnostic_color_init (&dc
, DIAGNOSTICS_COLOR_YES
);
4016 const location_t pi
= linemap_position_for_column (line_table
, 12);
4017 rich_location
richloc (line_table
, pi
);
4018 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4020 /* In order to avoid having the test depend on exactly how the colorization
4021 was effected, just confirm there are two pi characters in the output. */
4022 const char *result
= pp_formatted_text (dc
.printer
);
4023 const char *null_term
= result
+ strlen (result
);
4024 const char *first_pi
= strstr (result
, "\xcf\x80");
4025 ASSERT_TRUE (first_pi
&& first_pi
<= null_term
- 2);
4026 ASSERT_STR_CONTAINS (first_pi
+ 2, "\xcf\x80");
4029 /* Run the various one-liner tests. */
4032 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case
&case_
)
4034 /* Create a tempfile and write some text to it. */
4037 0000000000000000000000011111111111111111111111111111112222222222222
4038 1111111122222222345678900000000123456666666677777777890123444444445 */
4039 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4040 /* 0000000000000000000001111111111111111111222222222222222222222233333
4041 1111222233334444567890122223333456789999000011112222345678999900001
4043 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4044 line_table_test
ltt (case_
);
4046 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
4048 location_t line_end
= linemap_position_for_column (line_table
, 31);
4050 /* Don't attempt to run the tests if column data might be unavailable. */
4051 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4054 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
4055 ASSERT_EQ (1, LOCATION_LINE (line_end
));
4056 ASSERT_EQ (31, LOCATION_COLUMN (line_end
));
4058 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
4059 ASSERT_EQ (25, cpp_display_width (lspan
.get_buffer (), lspan
.length (),
4061 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end
),
4064 test_one_liner_simple_caret_utf8 ();
4065 test_one_liner_caret_and_range_utf8 ();
4066 test_one_liner_multiple_carets_and_ranges_utf8 ();
4067 test_one_liner_fixit_insert_before_utf8 ();
4068 test_one_liner_fixit_insert_after_utf8 ();
4069 test_one_liner_fixit_remove_utf8 ();
4070 test_one_liner_fixit_replace_utf8 ();
4071 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4072 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4073 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4074 test_one_liner_many_fixits_1_utf8 ();
4075 test_one_liner_many_fixits_2_utf8 ();
4076 test_one_liner_labels_utf8 ();
4077 test_one_liner_colorized_utf8 ();
4080 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4083 test_add_location_if_nearby (const line_table_case
&case_
)
4085 /* Create a tempfile and write some text to it.
4086 ...000000000111111111122222222223333333333.
4087 ...123456789012345678901234567890123456789. */
4089 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4090 "struct different_line\n" /* line 2. */
4092 " double x;\n" /* line 4. */
4093 " double y;\n" /* line 5. */
4094 ";\n"); /* line 6. */
4095 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4096 line_table_test
ltt (case_
);
4098 const line_map_ordinary
*ord_map
4099 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4100 tmp
.get_filename (), 0));
4102 linemap_line_start (line_table
, 1, 100);
4104 const location_t final_line_end
4105 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 7);
4107 /* Don't attempt to run the tests if column data might be unavailable. */
4108 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4111 /* Test of add_location_if_nearby on the same line as the
4112 primary location. */
4114 const location_t missing_close_brace_1_39
4115 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 39);
4116 const location_t matching_open_brace_1_18
4117 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4118 gcc_rich_location
richloc (missing_close_brace_1_39
);
4119 bool added
= richloc
.add_location_if_nearby (matching_open_brace_1_18
);
4120 ASSERT_TRUE (added
);
4121 ASSERT_EQ (2, richloc
.get_num_locations ());
4122 test_diagnostic_context dc
;
4123 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4124 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4126 pp_formatted_text (dc
.printer
));
4129 /* Test of add_location_if_nearby on a different line to the
4130 primary location. */
4132 const location_t missing_close_brace_6_1
4133 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 1);
4134 const location_t matching_open_brace_3_1
4135 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 1);
4136 gcc_rich_location
richloc (missing_close_brace_6_1
);
4137 bool added
= richloc
.add_location_if_nearby (matching_open_brace_3_1
);
4138 ASSERT_FALSE (added
);
4139 ASSERT_EQ (1, richloc
.get_num_locations ());
4143 /* Verify that we print fixits even if they only affect lines
4144 outside those covered by the ranges in the rich_location. */
4147 test_diagnostic_show_locus_fixit_lines (const line_table_case
&case_
)
4149 /* Create a tempfile and write some text to it.
4150 ...000000000111111111122222222223333333333.
4151 ...123456789012345678901234567890123456789. */
4153 = ("struct point { double x; double y; };\n" /* line 1. */
4154 "struct point origin = {x: 0.0,\n" /* line 2. */
4155 " y\n" /* line 3. */
4158 " : 0.0};\n"); /* line 6. */
4159 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4160 line_table_test
ltt (case_
);
4162 const line_map_ordinary
*ord_map
4163 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4164 tmp
.get_filename (), 0));
4166 linemap_line_start (line_table
, 1, 100);
4168 const location_t final_line_end
4169 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4171 /* Don't attempt to run the tests if column data might be unavailable. */
4172 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4175 /* A pair of tests for modernizing the initializers to C99-style. */
4177 /* The one-liner case (line 2). */
4179 test_diagnostic_context dc
;
4181 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 24);
4182 const location_t colon
4183 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 25);
4184 rich_location
richloc (line_table
, colon
);
4185 richloc
.add_fixit_insert_before (x
, ".");
4186 richloc
.add_fixit_replace (colon
, "=");
4187 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4188 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4191 pp_formatted_text (dc
.printer
));
4194 /* The multiline case. The caret for the rich_location is on line 6;
4195 verify that insertion fixit on line 3 is still printed (and that
4196 span starts are printed due to the gap between the span at line 3
4197 and that at line 6). */
4199 test_diagnostic_context dc
;
4201 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4202 const location_t colon
4203 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4204 rich_location
richloc (line_table
, colon
);
4205 richloc
.add_fixit_insert_before (y
, ".");
4206 richloc
.add_fixit_replace (colon
, "=");
4207 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4208 ASSERT_STREQ ("FILENAME:3:24:\n"
4215 pp_formatted_text (dc
.printer
));
4218 /* As above, but verify the behavior of multiple line spans
4219 with line-numbering enabled. */
4222 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4223 const location_t colon
4224 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4225 rich_location
richloc (line_table
, colon
);
4226 richloc
.add_fixit_insert_before (y
, ".");
4227 richloc
.add_fixit_replace (colon
, "=");
4228 test_diagnostic_context dc
;
4229 dc
.show_line_numbers_p
= true;
4230 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4231 ASSERT_STREQ (" 3 | y\n"
4237 pp_formatted_text (dc
.printer
));
4242 /* Verify that fix-it hints are appropriately consolidated.
4244 If any fix-it hints in a rich_location involve locations beyond
4245 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4246 the fix-it as a whole, so there should be none.
4248 Otherwise, verify that consecutive "replace" and "remove" fix-its
4249 are merged, and that other fix-its remain separate. */
4252 test_fixit_consolidation (const line_table_case
&case_
)
4254 line_table_test
ltt (case_
);
4256 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
4258 const location_t c10
= linemap_position_for_column (line_table
, 10);
4259 const location_t c15
= linemap_position_for_column (line_table
, 15);
4260 const location_t c16
= linemap_position_for_column (line_table
, 16);
4261 const location_t c17
= linemap_position_for_column (line_table
, 17);
4262 const location_t c20
= linemap_position_for_column (line_table
, 20);
4263 const location_t c21
= linemap_position_for_column (line_table
, 21);
4264 const location_t caret
= c10
;
4266 /* Insert + insert. */
4268 rich_location
richloc (line_table
, caret
);
4269 richloc
.add_fixit_insert_before (c10
, "foo");
4270 richloc
.add_fixit_insert_before (c15
, "bar");
4272 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4273 /* Bogus column info for 2nd fixit, so no fixits. */
4274 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4276 /* They should not have been merged. */
4277 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4280 /* Insert + replace. */
4282 rich_location
richloc (line_table
, caret
);
4283 richloc
.add_fixit_insert_before (c10
, "foo");
4284 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
4287 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4288 /* Bogus column info for 2nd fixit, so no fixits. */
4289 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4291 /* They should not have been merged. */
4292 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4295 /* Replace + non-consecutive insert. */
4297 rich_location
richloc (line_table
, caret
);
4298 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4300 richloc
.add_fixit_insert_before (c17
, "foo");
4302 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4303 /* Bogus column info for 2nd fixit, so no fixits. */
4304 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4306 /* They should not have been merged. */
4307 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4310 /* Replace + non-consecutive replace. */
4312 rich_location
richloc (line_table
, caret
);
4313 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4315 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
4318 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4319 /* Bogus column info for 2nd fixit, so no fixits. */
4320 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4322 /* They should not have been merged. */
4323 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4326 /* Replace + consecutive replace. */
4328 rich_location
richloc (line_table
, caret
);
4329 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4331 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
4334 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4335 /* Bogus column info for 2nd fixit, so no fixits. */
4336 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4339 /* They should have been merged into a single "replace". */
4340 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4341 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4342 ASSERT_STREQ ("foobar", hint
->get_string ());
4343 ASSERT_EQ (c10
, hint
->get_start_loc ());
4344 ASSERT_EQ (c21
, hint
->get_next_loc ());
4348 /* Replace + consecutive removal. */
4350 rich_location
richloc (line_table
, caret
);
4351 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4353 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4355 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4356 /* Bogus column info for 2nd fixit, so no fixits. */
4357 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4360 /* They should have been merged into a single replace, with the
4361 range extended to cover that of the removal. */
4362 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4363 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4364 ASSERT_STREQ ("foo", hint
->get_string ());
4365 ASSERT_EQ (c10
, hint
->get_start_loc ());
4366 ASSERT_EQ (c21
, hint
->get_next_loc ());
4370 /* Consecutive removals. */
4372 rich_location
richloc (line_table
, caret
);
4373 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
4374 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4376 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4377 /* Bogus column info for 2nd fixit, so no fixits. */
4378 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4381 /* They should have been merged into a single "replace-with-empty". */
4382 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4383 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4384 ASSERT_STREQ ("", hint
->get_string ());
4385 ASSERT_EQ (c10
, hint
->get_start_loc ());
4386 ASSERT_EQ (c21
, hint
->get_next_loc ());
4391 /* Verify that the line_corrections machinery correctly prints
4392 overlapping fixit-hints. */
4395 test_overlapped_fixit_printing (const line_table_case
&case_
)
4397 /* Create a tempfile and write some text to it.
4398 ...000000000111111111122222222223333333333.
4399 ...123456789012345678901234567890123456789. */
4401 = (" foo *f = (foo *)ptr->field;\n");
4402 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4403 line_table_test
ltt (case_
);
4405 const line_map_ordinary
*ord_map
4406 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4407 tmp
.get_filename (), 0));
4409 linemap_line_start (line_table
, 1, 100);
4411 const location_t final_line_end
4412 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4414 /* Don't attempt to run the tests if column data might be unavailable. */
4415 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4418 /* A test for converting a C-style cast to a C++-style cast. */
4419 const location_t open_paren
4420 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 12);
4421 const location_t close_paren
4422 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4423 const location_t expr_start
4424 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 19);
4425 const location_t expr_finish
4426 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 28);
4427 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4429 /* Various examples of fix-it hints that aren't themselves consolidated,
4430 but for which the *printing* may need consolidation. */
4432 /* Example where 3 fix-it hints are printed as one. */
4434 test_diagnostic_context dc
;
4435 rich_location
richloc (line_table
, expr
);
4436 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4437 richloc
.add_fixit_replace (close_paren
, "> (");
4438 richloc
.add_fixit_insert_after (")");
4440 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4441 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4443 " -----------------\n"
4444 " const_cast<foo *> (ptr->field)\n",
4445 pp_formatted_text (dc
.printer
));
4447 /* Unit-test the line_corrections machinery. */
4448 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
4449 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4450 ASSERT_EQ (column_range (12, 12),
4451 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4452 ASSERT_EQ (column_range (12, 12),
4453 get_affected_range (&dc
, hint_0
, CU_DISPLAY_COLS
));
4454 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc
, hint_0
));
4455 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4456 ASSERT_EQ (column_range (18, 18),
4457 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4458 ASSERT_EQ (column_range (18, 18),
4459 get_affected_range (&dc
, hint_1
, CU_DISPLAY_COLS
));
4460 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc
, hint_1
));
4461 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
4462 ASSERT_EQ (column_range (29, 28),
4463 get_affected_range (&dc
, hint_2
, CU_BYTES
));
4464 ASSERT_EQ (column_range (29, 28),
4465 get_affected_range (&dc
, hint_2
, CU_DISPLAY_COLS
));
4466 ASSERT_EQ (column_range (29, 29), get_printed_columns (&dc
, hint_2
));
4468 /* Add each hint in turn to a line_corrections instance,
4469 and verify that they are consolidated into one correction instance
4471 line_corrections
lc (&dc
, tmp
.get_filename (), 1);
4473 /* The first replace hint by itself. */
4474 lc
.add_hint (hint_0
);
4475 ASSERT_EQ (1, lc
.m_corrections
.length ());
4476 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_bytes
);
4477 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
4478 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
4479 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
4481 /* After the second replacement hint, they are printed together
4482 as a replacement (along with the text between them). */
4483 lc
.add_hint (hint_1
);
4484 ASSERT_EQ (1, lc
.m_corrections
.length ());
4485 ASSERT_STREQ ("const_cast<foo *> (", lc
.m_corrections
[0]->m_text
);
4486 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_bytes
);
4487 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
4488 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
4490 /* After the final insertion hint, they are all printed together
4491 as a replacement (along with the text between them). */
4492 lc
.add_hint (hint_2
);
4493 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4494 lc
.m_corrections
[0]->m_text
);
4495 ASSERT_EQ (1, lc
.m_corrections
.length ());
4496 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_bytes
);
4497 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_columns
);
4498 ASSERT_EQ (column_range (12, 41), lc
.m_corrections
[0]->m_printed_columns
);
4501 /* Example where two are consolidated during printing. */
4503 test_diagnostic_context dc
;
4504 rich_location
richloc (line_table
, expr
);
4505 richloc
.add_fixit_replace (open_paren
, "CAST (");
4506 richloc
.add_fixit_replace (close_paren
, ") (");
4507 richloc
.add_fixit_insert_after (")");
4509 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4510 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4515 pp_formatted_text (dc
.printer
));
4518 /* Example where none are consolidated during printing. */
4520 test_diagnostic_context dc
;
4521 rich_location
richloc (line_table
, expr
);
4522 richloc
.add_fixit_replace (open_paren
, "CST (");
4523 richloc
.add_fixit_replace (close_paren
, ") (");
4524 richloc
.add_fixit_insert_after (")");
4526 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4527 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4532 pp_formatted_text (dc
.printer
));
4535 /* Example of deletion fix-it hints. */
4537 test_diagnostic_context dc
;
4538 rich_location
richloc (line_table
, expr
);
4539 richloc
.add_fixit_insert_before (open_paren
, "(bar *)");
4540 source_range victim
= {open_paren
, close_paren
};
4541 richloc
.add_fixit_remove (victim
);
4543 /* This case is actually handled by fixit-consolidation,
4544 rather than by line_corrections. */
4545 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4547 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4548 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4552 pp_formatted_text (dc
.printer
));
4555 /* Example of deletion fix-it hints that would overlap. */
4557 test_diagnostic_context dc
;
4558 rich_location
richloc (line_table
, expr
);
4559 richloc
.add_fixit_insert_before (open_paren
, "(longer *)");
4560 source_range victim
= {expr_start
, expr_finish
};
4561 richloc
.add_fixit_remove (victim
);
4563 /* These fixits are not consolidated. */
4564 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4566 /* But the corrections are. */
4567 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4568 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4570 " -----------------\n"
4571 " (longer *)(foo *)\n",
4572 pp_formatted_text (dc
.printer
));
4575 /* Example of insertion fix-it hints that would overlap. */
4577 test_diagnostic_context dc
;
4578 rich_location
richloc (line_table
, expr
);
4579 richloc
.add_fixit_insert_before (open_paren
, "LONGER THAN THE CAST");
4580 richloc
.add_fixit_insert_after (close_paren
, "TEST");
4582 /* The first insertion is long enough that if printed naively,
4583 it would overlap with the second.
4584 Verify that they are printed as a single replacement. */
4585 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4586 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4589 " LONGER THAN THE CAST(foo *)TEST\n",
4590 pp_formatted_text (dc
.printer
));
4594 /* Multibyte-aware version of preceding tests. See comments above
4595 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4599 test_overlapped_fixit_printing_utf8 (const line_table_case
&case_
)
4601 /* Create a tempfile and write some text to it. */
4605 00000000000000000000000111111111111111111111111222222222222222223
4606 12344444444555555556789012344444444555555556789012345678999999990 */
4607 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4608 /* 00000000000000000000011111111111111111111112222222222333333333333
4609 12344445555666677778901234566667777888899990123456789012333344445
4612 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4613 line_table_test
ltt (case_
);
4615 const line_map_ordinary
*ord_map
4616 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4617 tmp
.get_filename (), 0));
4619 linemap_line_start (line_table
, 1, 100);
4621 const location_t final_line_end
4622 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 50);
4624 /* Don't attempt to run the tests if column data might be unavailable. */
4625 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4628 /* A test for converting a C-style cast to a C++-style cast. */
4629 const location_t open_paren
4630 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 14);
4631 const location_t close_paren
4632 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 22);
4633 const location_t expr_start
4634 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
4635 const location_t expr_finish
4636 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 34);
4637 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4639 /* Various examples of fix-it hints that aren't themselves consolidated,
4640 but for which the *printing* may need consolidation. */
4642 /* Example where 3 fix-it hints are printed as one. */
4644 test_diagnostic_context dc
;
4645 rich_location
richloc (line_table
, expr
);
4646 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4647 richloc
.add_fixit_replace (close_paren
, "> (");
4648 richloc
.add_fixit_insert_after (")");
4650 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4651 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4652 " *f = (f\xf0\x9f\x98\x82"
4653 " *)ptr->field\xcf\x80"
4656 " ------------------\n"
4657 " const_cast<f\xf0\x9f\x98\x82"
4658 " *> (ptr->field\xcf\x80"
4660 pp_formatted_text (dc
.printer
));
4662 /* Unit-test the line_corrections machinery. */
4663 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
4664 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4665 ASSERT_EQ (column_range (14, 14),
4666 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4667 ASSERT_EQ (column_range (12, 12),
4668 get_affected_range (&dc
, hint_0
, CU_DISPLAY_COLS
));
4669 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc
, hint_0
));
4670 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4671 ASSERT_EQ (column_range (22, 22),
4672 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4673 ASSERT_EQ (column_range (18, 18),
4674 get_affected_range (&dc
, hint_1
, CU_DISPLAY_COLS
));
4675 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc
, hint_1
));
4676 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
4677 ASSERT_EQ (column_range (35, 34),
4678 get_affected_range (&dc
, hint_2
, CU_BYTES
));
4679 ASSERT_EQ (column_range (30, 29),
4680 get_affected_range (&dc
, hint_2
, CU_DISPLAY_COLS
));
4681 ASSERT_EQ (column_range (30, 30), get_printed_columns (&dc
, hint_2
));
4683 /* Add each hint in turn to a line_corrections instance,
4684 and verify that they are consolidated into one correction instance
4686 line_corrections
lc (&dc
, tmp
.get_filename (), 1);
4688 /* The first replace hint by itself. */
4689 lc
.add_hint (hint_0
);
4690 ASSERT_EQ (1, lc
.m_corrections
.length ());
4691 ASSERT_EQ (column_range (14, 14), lc
.m_corrections
[0]->m_affected_bytes
);
4692 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
4693 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
4694 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
4696 /* After the second replacement hint, they are printed together
4697 as a replacement (along with the text between them). */
4698 lc
.add_hint (hint_1
);
4699 ASSERT_EQ (1, lc
.m_corrections
.length ());
4700 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4701 lc
.m_corrections
[0]->m_text
);
4702 ASSERT_EQ (column_range (14, 22), lc
.m_corrections
[0]->m_affected_bytes
);
4703 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
4704 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
4706 /* After the final insertion hint, they are all printed together
4707 as a replacement (along with the text between them). */
4708 lc
.add_hint (hint_2
);
4709 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4710 lc
.m_corrections
[0]->m_text
);
4711 ASSERT_EQ (1, lc
.m_corrections
.length ());
4712 ASSERT_EQ (column_range (14, 34), lc
.m_corrections
[0]->m_affected_bytes
);
4713 ASSERT_EQ (column_range (12, 29), lc
.m_corrections
[0]->m_affected_columns
);
4714 ASSERT_EQ (column_range (12, 42), lc
.m_corrections
[0]->m_printed_columns
);
4717 /* Example where two are consolidated during printing. */
4719 test_diagnostic_context dc
;
4720 rich_location
richloc (line_table
, expr
);
4721 richloc
.add_fixit_replace (open_paren
, "CAST (");
4722 richloc
.add_fixit_replace (close_paren
, ") (");
4723 richloc
.add_fixit_insert_after (")");
4725 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4726 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4727 " *f = (f\xf0\x9f\x98\x82"
4728 " *)ptr->field\xcf\x80"
4734 pp_formatted_text (dc
.printer
));
4737 /* Example where none are consolidated during printing. */
4739 test_diagnostic_context dc
;
4740 rich_location
richloc (line_table
, expr
);
4741 richloc
.add_fixit_replace (open_paren
, "CST (");
4742 richloc
.add_fixit_replace (close_paren
, ") (");
4743 richloc
.add_fixit_insert_after (")");
4745 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4746 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4747 " *f = (f\xf0\x9f\x98\x82"
4748 " *)ptr->field\xcf\x80"
4754 pp_formatted_text (dc
.printer
));
4757 /* Example of deletion fix-it hints. */
4759 test_diagnostic_context dc
;
4760 rich_location
richloc (line_table
, expr
);
4761 richloc
.add_fixit_insert_before (open_paren
, "(bar\xf0\x9f\x98\x82 *)");
4762 source_range victim
= {open_paren
, close_paren
};
4763 richloc
.add_fixit_remove (victim
);
4765 /* This case is actually handled by fixit-consolidation,
4766 rather than by line_corrections. */
4767 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4769 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4770 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4771 " *f = (f\xf0\x9f\x98\x82"
4772 " *)ptr->field\xcf\x80"
4776 " (bar\xf0\x9f\x98\x82"
4778 pp_formatted_text (dc
.printer
));
4781 /* Example of deletion fix-it hints that would overlap. */
4783 test_diagnostic_context dc
;
4784 rich_location
richloc (line_table
, expr
);
4785 richloc
.add_fixit_insert_before (open_paren
, "(long\xf0\x9f\x98\x82 *)");
4786 source_range victim
= {expr_start
, expr_finish
};
4787 richloc
.add_fixit_remove (victim
);
4789 /* These fixits are not consolidated. */
4790 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4792 /* But the corrections are. */
4793 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4794 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4795 " *f = (f\xf0\x9f\x98\x82"
4796 " *)ptr->field\xcf\x80"
4799 " ------------------\n"
4800 " (long\xf0\x9f\x98\x82"
4801 " *)(f\xf0\x9f\x98\x82"
4803 pp_formatted_text (dc
.printer
));
4806 /* Example of insertion fix-it hints that would overlap. */
4808 test_diagnostic_context dc
;
4809 rich_location
richloc (line_table
, expr
);
4810 richloc
.add_fixit_insert_before
4811 (open_paren
, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4812 richloc
.add_fixit_insert_after (close_paren
, "TEST");
4814 /* The first insertion is long enough that if printed naively,
4815 it would overlap with the second.
4816 Verify that they are printed as a single replacement. */
4817 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4818 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4819 " *f = (f\xf0\x9f\x98\x82"
4820 " *)ptr->field\xcf\x80"
4824 " L\xf0\x9f\x98\x82"
4825 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4827 pp_formatted_text (dc
.printer
));
4831 /* Verify that the line_corrections machinery correctly prints
4832 overlapping fixit-hints that have been added in the wrong
4834 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4837 test_overlapped_fixit_printing_2 (const line_table_case
&case_
)
4839 /* Create a tempfile and write some text to it.
4840 ...000000000111111111122222222223333333333.
4841 ...123456789012345678901234567890123456789. */
4843 = ("int a5[][0][0] = { 1, 2 };\n");
4844 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4845 line_table_test
ltt (case_
);
4847 const line_map_ordinary
*ord_map
4848 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4849 tmp
.get_filename (), 0));
4851 linemap_line_start (line_table
, 1, 100);
4853 const location_t final_line_end
4854 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 100);
4856 /* Don't attempt to run the tests if column data might be unavailable. */
4857 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4860 const location_t col_1
4861 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
4862 const location_t col_20
4863 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 20);
4864 const location_t col_21
4865 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 21);
4866 const location_t col_23
4867 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
4868 const location_t col_25
4869 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 25);
4871 /* Two insertions, in the wrong order. */
4873 test_diagnostic_context dc
;
4875 rich_location
richloc (line_table
, col_20
);
4876 richloc
.add_fixit_insert_before (col_23
, "{");
4877 richloc
.add_fixit_insert_before (col_21
, "}");
4879 /* These fixits should be accepted; they can't be consolidated. */
4880 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4881 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4882 ASSERT_EQ (column_range (23, 22),
4883 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4884 ASSERT_EQ (column_range (23, 23), get_printed_columns (&dc
, hint_0
));
4885 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4886 ASSERT_EQ (column_range (21, 20),
4887 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4888 ASSERT_EQ (column_range (21, 21), get_printed_columns (&dc
, hint_1
));
4890 /* Verify that they're printed correctly. */
4891 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4892 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4895 pp_formatted_text (dc
.printer
));
4898 /* Various overlapping insertions, some occurring "out of order"
4899 (reproducing the fix-it hints from PR c/81405). */
4901 test_diagnostic_context dc
;
4902 rich_location
richloc (line_table
, col_20
);
4904 richloc
.add_fixit_insert_before (col_20
, "{{");
4905 richloc
.add_fixit_insert_before (col_21
, "}}");
4906 richloc
.add_fixit_insert_before (col_23
, "{");
4907 richloc
.add_fixit_insert_before (col_21
, "}");
4908 richloc
.add_fixit_insert_before (col_23
, "{{");
4909 richloc
.add_fixit_insert_before (col_25
, "}");
4910 richloc
.add_fixit_insert_before (col_21
, "}");
4911 richloc
.add_fixit_insert_before (col_1
, "{");
4912 richloc
.add_fixit_insert_before (col_25
, "}");
4913 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4914 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4917 " {{1}}}}, {{{2 }}\n",
4918 pp_formatted_text (dc
.printer
));
4922 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4925 test_fixit_insert_containing_newline (const line_table_case
&case_
)
4927 /* Create a tempfile and write some text to it.
4928 .........................0000000001111111.
4929 .........................1234567890123456. */
4930 const char *old_content
= (" case 'a':\n" /* line 1. */
4931 " x = a;\n" /* line 2. */
4932 " case 'b':\n" /* line 3. */
4933 " x = b;\n");/* line 4. */
4935 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
4936 line_table_test
ltt (case_
);
4937 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
4939 location_t case_start
= linemap_position_for_column (line_table
, 5);
4940 location_t case_finish
= linemap_position_for_column (line_table
, 13);
4941 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
4942 location_t line_start
= linemap_position_for_column (line_table
, 1);
4944 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4947 /* Add a "break;" on a line by itself before line 3 i.e. before
4948 column 1 of line 3. */
4950 rich_location
richloc (line_table
, case_loc
);
4951 richloc
.add_fixit_insert_before (line_start
, " break;\n");
4953 /* Without line numbers. */
4955 test_diagnostic_context dc
;
4956 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4957 ASSERT_STREQ (" x = a;\n"
4961 pp_formatted_text (dc
.printer
));
4964 /* With line numbers. */
4966 test_diagnostic_context dc
;
4967 dc
.show_line_numbers_p
= true;
4968 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4969 ASSERT_STREQ (" 2 | x = a;\n"
4973 pp_formatted_text (dc
.printer
));
4977 /* Verify that attempts to add text with a newline fail when the
4978 insertion point is *not* at the start of a line. */
4980 rich_location
richloc (line_table
, case_loc
);
4981 richloc
.add_fixit_insert_before (case_start
, "break;\n");
4982 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
4983 test_diagnostic_context dc
;
4984 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4985 ASSERT_STREQ (" case 'b':\n"
4987 pp_formatted_text (dc
.printer
));
4991 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4992 of the file, where the fix-it is printed in a different line-span
4993 to the primary range of the diagnostic. */
4996 test_fixit_insert_containing_newline_2 (const line_table_case
&case_
)
4998 /* Create a tempfile and write some text to it.
4999 .........................0000000001111111.
5000 .........................1234567890123456. */
5001 const char *old_content
= ("test (int ch)\n" /* line 1. */
5003 " putchar (ch);\n" /* line 3. */
5004 "}\n"); /* line 4. */
5006 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5007 line_table_test
ltt (case_
);
5009 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5010 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5011 linemap_line_start (line_table
, 1, 100);
5013 /* The primary range is the "putchar" token. */
5014 location_t putchar_start
5015 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 2);
5016 location_t putchar_finish
5017 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 8);
5018 location_t putchar_loc
5019 = make_location (putchar_start
, putchar_start
, putchar_finish
);
5020 rich_location
richloc (line_table
, putchar_loc
);
5022 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5023 location_t file_start
5024 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5025 richloc
.add_fixit_insert_before (file_start
, "#include <stdio.h>\n");
5027 if (putchar_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5031 test_diagnostic_context dc
;
5032 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5033 ASSERT_STREQ ("FILENAME:1:1:\n"
5034 "+#include <stdio.h>\n"
5039 pp_formatted_text (dc
.printer
));
5042 /* With line-numbering, the line spans are close enough to be
5043 consolidated, since it makes little sense to skip line 2. */
5045 test_diagnostic_context dc
;
5046 dc
.show_line_numbers_p
= true;
5047 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5048 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5049 " 1 | test (int ch)\n"
5051 " 3 | putchar (ch);\n"
5053 pp_formatted_text (dc
.printer
));
5057 /* Replacement fix-it hint containing a newline.
5058 This will fail, as newlines are only supported when inserting at the
5059 beginning of a line. */
5062 test_fixit_replace_containing_newline (const line_table_case
&case_
)
5064 /* Create a tempfile and write some text to it.
5065 .........................0000000001111.
5066 .........................1234567890123. */
5067 const char *old_content
= "foo = bar ();\n";
5069 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5070 line_table_test
ltt (case_
);
5071 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5073 /* Replace the " = " with "\n = ", as if we were reformatting an
5074 overly long line. */
5075 location_t start
= linemap_position_for_column (line_table
, 4);
5076 location_t finish
= linemap_position_for_column (line_table
, 6);
5077 location_t loc
= linemap_position_for_column (line_table
, 13);
5078 rich_location
richloc (line_table
, loc
);
5079 source_range range
= source_range::from_locations (start
, finish
);
5080 richloc
.add_fixit_replace (range
, "\n =");
5082 /* Arbitrary newlines are not yet supported within fix-it hints, so
5083 the fix-it should not be displayed. */
5084 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5086 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5089 test_diagnostic_context dc
;
5090 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5091 ASSERT_STREQ (" foo = bar ();\n"
5093 pp_formatted_text (dc
.printer
));
5096 /* Fix-it hint, attempting to delete a newline.
5097 This will fail, as we currently only support fix-it hints that
5098 affect one line at a time. */
5101 test_fixit_deletion_affecting_newline (const line_table_case
&case_
)
5103 /* Create a tempfile and write some text to it.
5104 ..........................0000000001111.
5105 ..........................1234567890123. */
5106 const char *old_content
= ("foo = bar (\n"
5109 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5110 line_table_test
ltt (case_
);
5111 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5112 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5113 linemap_line_start (line_table
, 1, 100);
5115 /* Attempt to delete the " (\n...)". */
5117 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 10);
5119 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
5121 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 7);
5122 location_t loc
= make_location (caret
, start
, finish
);
5123 rich_location
richloc (line_table
, loc
);
5124 richloc
. add_fixit_remove ();
5126 /* Fix-it hints that affect more than one line are not yet supported, so
5127 the fix-it should not be displayed. */
5128 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5130 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5133 test_diagnostic_context dc
;
5134 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5135 ASSERT_STREQ (" foo = bar (\n"
5139 pp_formatted_text (dc
.printer
));
5143 test_tab_expansion (const line_table_case
&case_
)
5145 /* Create a tempfile and write some text to it. This example uses a tabstop
5146 of 8, as the column numbers attempt to indicate:
5148 .....................000.01111111111.22222333333 display
5149 .....................123.90123456789.56789012345 columns */
5150 const char *content
= " \t This: `\t' is a tab.\n";
5151 /* ....................000 00000011111 11111222222 byte
5152 ....................123 45678901234 56789012345 columns */
5154 const int tabstop
= 8;
5155 const int first_non_ws_byte_col
= 7;
5156 const int right_quote_byte_col
= 15;
5157 const int last_byte_col
= 25;
5158 ASSERT_EQ (35, cpp_display_width (content
, last_byte_col
, tabstop
));
5160 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5161 line_table_test
ltt (case_
);
5162 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5164 /* Don't attempt to run the tests if column data might be unavailable. */
5165 location_t line_end
= linemap_position_for_column (line_table
, last_byte_col
);
5166 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5169 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5170 into 11 spaces. Recall that print_line() also puts one space before
5173 test_diagnostic_context dc
;
5174 dc
.tabstop
= tabstop
;
5175 rich_location
richloc (line_table
,
5176 linemap_position_for_column (line_table
,
5177 first_non_ws_byte_col
));
5178 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5179 test_layout
.print_line (1);
5180 ASSERT_STREQ (" This: ` ' is a tab.\n"
5182 pp_formatted_text (dc
.printer
));
5185 /* Confirm the display width was tracked correctly across the internal tab
5188 test_diagnostic_context dc
;
5189 dc
.tabstop
= tabstop
;
5190 rich_location
richloc (line_table
,
5191 linemap_position_for_column (line_table
,
5192 right_quote_byte_col
));
5193 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5194 test_layout
.print_line (1);
5195 ASSERT_STREQ (" This: ` ' is a tab.\n"
5197 pp_formatted_text (dc
.printer
));
5201 /* Verify that line numbers are correctly printed for the case of
5202 a multiline range in which the width of the line numbers changes
5203 (e.g. from "9" to "10"). */
5206 test_line_numbers_multiline_range ()
5208 /* Create a tempfile and write some text to it. */
5210 for (int i
= 0; i
< 20; i
++)
5211 /* .........0000000001111111.
5212 .............1234567890123456. */
5213 pp_printf (&pp
, "this is line %i\n", i
+ 1);
5214 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", pp_formatted_text (&pp
));
5215 line_table_test ltt
;
5217 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5218 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5219 linemap_line_start (line_table
, 1, 100);
5221 /* Create a multi-line location, starting at the "line" of line 9, with
5222 a caret on the "is" of line 10, finishing on the "this" line 11. */
5225 = linemap_position_for_line_and_column (line_table
, ord_map
, 9, 9);
5227 = linemap_position_for_line_and_column (line_table
, ord_map
, 10, 6);
5229 = linemap_position_for_line_and_column (line_table
, ord_map
, 11, 4);
5230 location_t loc
= make_location (caret
, start
, finish
);
5232 test_diagnostic_context dc
;
5233 dc
.show_line_numbers_p
= true;
5234 dc
.min_margin_width
= 0;
5235 gcc_rich_location
richloc (loc
);
5236 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5237 ASSERT_STREQ (" 9 | this is line 9\n"
5239 "10 | this is line 10\n"
5240 " | ~~~~~^~~~~~~~~~\n"
5241 "11 | this is line 11\n"
5243 pp_formatted_text (dc
.printer
));
5246 /* Run all of the selftests within this file. */
5249 diagnostic_show_locus_c_tests ()
5253 test_layout_range_for_single_point ();
5254 test_layout_range_for_single_line ();
5255 test_layout_range_for_multiple_lines ();
5257 for_each_line_table_case (test_layout_x_offset_display_utf8
);
5258 for_each_line_table_case (test_layout_x_offset_display_tab
);
5260 test_get_line_bytes_without_trailing_whitespace ();
5262 test_diagnostic_show_locus_unknown_location ();
5264 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
5265 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8
);
5266 for_each_line_table_case (test_add_location_if_nearby
);
5267 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines
);
5268 for_each_line_table_case (test_fixit_consolidation
);
5269 for_each_line_table_case (test_overlapped_fixit_printing
);
5270 for_each_line_table_case (test_overlapped_fixit_printing_utf8
);
5271 for_each_line_table_case (test_overlapped_fixit_printing_2
);
5272 for_each_line_table_case (test_fixit_insert_containing_newline
);
5273 for_each_line_table_case (test_fixit_insert_containing_newline_2
);
5274 for_each_line_table_case (test_fixit_replace_containing_newline
);
5275 for_each_line_table_case (test_fixit_deletion_affecting_newline
);
5276 for_each_line_table_case (test_tab_expansion
);
5278 test_line_numbers_multiline_range ();
5281 } // namespace selftest
5283 #endif /* #if CHECKING_P */
5286 # pragma GCC diagnostic pop