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
2604 if (loc
== context
->last_location
2605 && richloc
->get_num_fixit_hints () == 0)
2608 context
->last_location
= loc
;
2610 layout
layout (context
, richloc
, diagnostic_kind
);
2611 for (int line_span_idx
= 0; line_span_idx
< layout
.get_num_line_spans ();
2614 const line_span
*line_span
= layout
.get_line_span (line_span_idx
);
2615 if (context
->show_line_numbers_p
)
2617 /* With line numbers, we should show whenever the line-numbering
2619 if (line_span_idx
> 0)
2620 layout
.print_gap_in_line_numbering ();
2624 /* Without line numbers, we print headings for some line spans. */
2625 if (layout
.print_heading_for_line_span_index_p (line_span_idx
))
2627 expanded_location exploc
2628 = layout
.get_expanded_location (line_span
);
2629 context
->start_span (context
, exploc
);
2632 /* Iterate over the lines within this span (using linenum_arith_t to
2633 avoid overflow with 0xffffffff causing an infinite loop). */
2634 linenum_arith_t last_line
= line_span
->get_last_line ();
2635 for (linenum_arith_t row
= line_span
->get_first_line ();
2636 row
<= last_line
; row
++)
2637 layout
.print_line (row
);
2643 namespace selftest
{
2645 /* Selftests for diagnostic_show_locus. */
2647 /* For precise tests of the layout, make clear where the source line will
2648 start. test_left_margin sets the total byte count from the left side of the
2649 screen to the start of source lines, after the line number and the separator,
2650 which consists of the three characters " | ". */
2651 static const int test_linenum_sep
= 3;
2652 static const int test_left_margin
= 7;
2654 /* Helper function for test_layout_x_offset_display_utf8(). */
2656 test_offset_impl (int caret_byte_col
, int max_width
,
2657 int expected_x_offset_display
,
2658 int left_margin
= test_left_margin
)
2660 test_diagnostic_context dc
;
2661 dc
.caret_max_width
= max_width
;
2662 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2663 the line number plus one space after. */
2664 dc
.min_margin_width
= left_margin
- test_linenum_sep
+ 1;
2665 dc
.show_line_numbers_p
= true;
2666 rich_location
richloc (line_table
,
2667 linemap_position_for_column (line_table
,
2669 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2670 ASSERT_EQ (left_margin
- test_linenum_sep
,
2671 test_layout
.get_linenum_width ());
2672 ASSERT_EQ (expected_x_offset_display
,
2673 test_layout
.get_x_offset_display ());
2676 /* Test that layout::calculate_x_offset_display() works. */
2678 test_layout_x_offset_display_utf8 (const line_table_case
&case_
)
2682 = "This line is very long, so that we can use it to test the logic for "
2683 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2684 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2687 /* Number of bytes in the line, subtracting one to remove the newline. */
2688 const int line_bytes
= strlen (content
) - 1;
2690 /* Number of display columns occupied by the line; each of the 2 emojis
2691 takes up 2 fewer display columns than it does bytes. */
2692 const int line_display_cols
= line_bytes
- 2*2;
2694 /* The column of the first emoji. Byte or display is the same as there are
2695 no multibyte characters earlier on the line. */
2696 const int emoji_col
= 102;
2698 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2699 line_table_test
ltt (case_
);
2701 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2703 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
2705 /* Don't attempt to run the tests if column data might be unavailable. */
2706 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2709 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
2710 ASSERT_EQ (1, LOCATION_LINE (line_end
));
2711 ASSERT_EQ (line_bytes
, LOCATION_COLUMN (line_end
));
2713 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
2714 ASSERT_EQ (line_display_cols
,
2715 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
2717 ASSERT_EQ (line_display_cols
,
2718 location_compute_display_column (expand_location (line_end
),
2720 ASSERT_EQ (0, memcmp (lspan
.get_buffer () + (emoji_col
- 1),
2721 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2723 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2725 /* No constraint on the width -> no offset. */
2726 test_offset_impl (emoji_col
, 0, 0);
2728 /* Caret is before the beginning -> no offset. */
2729 test_offset_impl (0, 100, 0);
2731 /* Caret is past the end of the line -> no offset. */
2732 test_offset_impl (line_bytes
+1, 100, 0);
2734 /* Line fits in the display -> no offset. */
2735 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
, 0);
2736 test_offset_impl (emoji_col
, line_display_cols
+ test_left_margin
, 0);
2738 /* Line is too long for the display but caret location is OK
2739 anyway -> no offset. */
2740 static const int small_width
= 24;
2741 test_offset_impl (1, small_width
, 0);
2743 /* Width constraint is very small -> no offset. */
2744 test_offset_impl (emoji_col
, CARET_LINE_MARGIN
, 0);
2746 /* Line would be offset, but due to large line numbers, offsetting
2747 would remove the whole line -> no offset. */
2748 static const int huge_left_margin
= 100;
2749 test_offset_impl (emoji_col
, huge_left_margin
, 0, huge_left_margin
);
2751 /* Line is the same length as the display, but the line number makes it too
2752 long, so offset is required. Caret is at the end so padding on the right
2753 is not in effect. */
2754 for (int excess
= 1; excess
<= 3; ++excess
)
2755 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
- excess
,
2758 /* Line is much too long for the display, caret is near the end ->
2759 offset should be such that the line fits in the display and caret
2760 remains the same distance from the end that it was. */
2761 for (int caret_offset
= 0, max_offset
= MIN (CARET_LINE_MARGIN
, 10);
2762 caret_offset
<= max_offset
; ++caret_offset
)
2763 test_offset_impl (line_bytes
- caret_offset
, small_width
,
2764 line_display_cols
+ test_left_margin
- small_width
);
2766 /* As previous case but caret is closer to the middle; now we want it to end
2767 up CARET_LINE_MARGIN bytes from the end. */
2768 ASSERT_GT (line_display_cols
- emoji_col
, CARET_LINE_MARGIN
);
2769 test_offset_impl (emoji_col
, small_width
,
2770 emoji_col
+ test_left_margin
2771 - (small_width
- CARET_LINE_MARGIN
));
2773 /* Test that the source line is offset as expected when printed. */
2775 test_diagnostic_context dc
;
2776 dc
.caret_max_width
= small_width
- 6;
2777 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2778 dc
.show_line_numbers_p
= true;
2779 dc
.show_ruler_p
= true;
2780 rich_location
richloc (line_table
,
2781 linemap_position_for_column (line_table
,
2783 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2784 test_layout
.print_line (1);
2785 ASSERT_STREQ (" | 1 \n"
2787 " | 234567890123456789\n"
2788 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2789 "that occupies 8 bytes and 4 display columns, starting at "
2792 pp_formatted_text (dc
.printer
));
2795 /* Similar to the previous example, but now the offset called for would split
2796 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2797 it with a padding space in this case. */
2799 test_diagnostic_context dc
;
2800 dc
.caret_max_width
= small_width
- 5;
2801 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2802 dc
.show_line_numbers_p
= true;
2803 dc
.show_ruler_p
= true;
2804 rich_location
richloc (line_table
,
2805 linemap_position_for_column (line_table
,
2807 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2808 test_layout
.print_line (1);
2809 ASSERT_STREQ (" | 1 1 \n"
2811 " | 3456789012345678901\n"
2812 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2813 "that occupies 8 bytes and 4 display columns, starting at "
2816 pp_formatted_text (dc
.printer
));
2822 test_layout_x_offset_display_tab (const line_table_case
&case_
)
2825 = "This line is very long, so that we can use it to test the logic for "
2826 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
2827 "a variable number of display columns, starting at column #103.\n";
2829 /* Number of bytes in the line, subtracting one to remove the newline. */
2830 const int line_bytes
= strlen (content
) - 1;
2832 /* The column where the tab begins. Byte or display is the same as there are
2833 no multibyte characters earlier on the line. */
2834 const int tab_col
= 103;
2836 /* Effective extra size of the tab beyond what a single space would have taken
2837 up, indexed by tabstop. */
2838 static const int num_tabstops
= 11;
2839 int extra_width
[num_tabstops
];
2840 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2842 const int this_tab_size
= tabstop
- (tab_col
- 1) % tabstop
;
2843 extra_width
[tabstop
] = this_tab_size
- 1;
2845 /* Example of this calculation: if tabstop is 10, the tab starting at column
2846 #103 has to expand into 8 spaces, covering columns 103-110, so that the
2847 next character is at column #111. So it takes up 7 more columns than
2848 a space would have taken up. */
2849 ASSERT_EQ (7, extra_width
[10]);
2851 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
2852 line_table_test
ltt (case_
);
2854 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
2856 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
2858 /* Don't attempt to run the tests if column data might be unavailable. */
2859 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
2862 /* Check that cpp_display_width handles the tabs as expected. */
2863 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
2864 ASSERT_EQ ('\t', *(lspan
.get_buffer () + (tab_col
- 1)));
2865 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2867 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
2868 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
2870 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
2871 location_compute_display_column (expand_location (line_end
),
2875 /* Check that the tab is expanded to the expected number of spaces. */
2876 rich_location
richloc (line_table
,
2877 linemap_position_for_column (line_table
,
2879 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2881 test_diagnostic_context dc
;
2882 dc
.tabstop
= tabstop
;
2883 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2884 test_layout
.print_line (1);
2885 const char *out
= pp_formatted_text (dc
.printer
);
2886 ASSERT_EQ (NULL
, strchr (out
, '\t'));
2887 const char *left_quote
= strchr (out
, '`');
2888 const char *right_quote
= strchr (out
, '\'');
2889 ASSERT_NE (NULL
, left_quote
);
2890 ASSERT_NE (NULL
, right_quote
);
2891 ASSERT_EQ (right_quote
- left_quote
, extra_width
[tabstop
] + 2);
2894 /* Check that the line is offset properly and that the tab is broken up
2895 into the expected number of spaces when it is the last character skipped
2897 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
2899 test_diagnostic_context dc
;
2900 dc
.tabstop
= tabstop
;
2901 static const int small_width
= 24;
2902 dc
.caret_max_width
= small_width
- 4;
2903 dc
.min_margin_width
= test_left_margin
- test_linenum_sep
+ 1;
2904 dc
.show_line_numbers_p
= true;
2905 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2906 test_layout
.print_line (1);
2908 /* We have arranged things so that two columns will be printed before
2909 the caret. If the tab results in more than one space, this should
2910 produce two spaces in the output; otherwise, it will be a single space
2911 preceded by the opening quote before the tab character. */
2913 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
2914 "display columns, starting at column #103.\n"
2917 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
2918 "display columns, starting at column #103.\n"
2920 const char *expected_output
= (extra_width
[tabstop
] ? output1
: output2
);
2921 ASSERT_STREQ (expected_output
, pp_formatted_text (dc
.printer
));
2926 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2929 test_diagnostic_show_locus_unknown_location ()
2931 test_diagnostic_context dc
;
2932 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
2933 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2934 ASSERT_STREQ ("", pp_formatted_text (dc
.printer
));
2937 /* Verify that diagnostic_show_locus works sanely for various
2940 All of these work on the following 1-line source file:
2943 "foo = bar.field;\n"
2944 which is set up by test_diagnostic_show_locus_one_liner and calls
2950 test_one_liner_simple_caret ()
2952 test_diagnostic_context dc
;
2953 location_t caret
= linemap_position_for_column (line_table
, 10);
2954 rich_location
richloc (line_table
, caret
);
2955 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2956 ASSERT_STREQ (" foo = bar.field;\n"
2958 pp_formatted_text (dc
.printer
));
2961 /* Caret and range. */
2964 test_one_liner_caret_and_range ()
2966 test_diagnostic_context dc
;
2967 location_t caret
= linemap_position_for_column (line_table
, 10);
2968 location_t start
= linemap_position_for_column (line_table
, 7);
2969 location_t finish
= linemap_position_for_column (line_table
, 15);
2970 location_t loc
= make_location (caret
, start
, finish
);
2971 rich_location
richloc (line_table
, loc
);
2972 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
2973 ASSERT_STREQ (" foo = bar.field;\n"
2975 pp_formatted_text (dc
.printer
));
2978 /* Multiple ranges and carets. */
2981 test_one_liner_multiple_carets_and_ranges ()
2983 test_diagnostic_context dc
;
2985 = make_location (linemap_position_for_column (line_table
, 2),
2986 linemap_position_for_column (line_table
, 1),
2987 linemap_position_for_column (line_table
, 3));
2988 dc
.caret_chars
[0] = 'A';
2991 = make_location (linemap_position_for_column (line_table
, 8),
2992 linemap_position_for_column (line_table
, 7),
2993 linemap_position_for_column (line_table
, 9));
2994 dc
.caret_chars
[1] = 'B';
2997 = make_location (linemap_position_for_column (line_table
, 13),
2998 linemap_position_for_column (line_table
, 11),
2999 linemap_position_for_column (line_table
, 15));
3000 dc
.caret_chars
[2] = 'C';
3002 rich_location
richloc (line_table
, foo
);
3003 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3004 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3005 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3006 ASSERT_STREQ (" foo = bar.field;\n"
3008 pp_formatted_text (dc
.printer
));
3011 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3014 test_one_liner_fixit_insert_before ()
3016 test_diagnostic_context dc
;
3017 location_t caret
= linemap_position_for_column (line_table
, 7);
3018 rich_location
richloc (line_table
, caret
);
3019 richloc
.add_fixit_insert_before ("&");
3020 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3021 ASSERT_STREQ (" foo = bar.field;\n"
3024 pp_formatted_text (dc
.printer
));
3027 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3030 test_one_liner_fixit_insert_after ()
3032 test_diagnostic_context dc
;
3033 location_t start
= linemap_position_for_column (line_table
, 1);
3034 location_t finish
= linemap_position_for_column (line_table
, 3);
3035 location_t foo
= make_location (start
, start
, finish
);
3036 rich_location
richloc (line_table
, foo
);
3037 richloc
.add_fixit_insert_after ("[0]");
3038 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3039 ASSERT_STREQ (" foo = bar.field;\n"
3042 pp_formatted_text (dc
.printer
));
3045 /* Removal fix-it hint: removal of the ".field".
3046 Also verify the interaction of pp_set_prefix with rulers and
3050 test_one_liner_fixit_remove ()
3052 location_t start
= linemap_position_for_column (line_table
, 10);
3053 location_t finish
= linemap_position_for_column (line_table
, 15);
3054 location_t dot
= make_location (start
, start
, finish
);
3055 rich_location
richloc (line_table
, dot
);
3056 richloc
.add_fixit_remove ();
3060 test_diagnostic_context dc
;
3061 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3062 ASSERT_STREQ (" foo = bar.field;\n"
3065 pp_formatted_text (dc
.printer
));
3068 /* Test of adding a prefix. */
3070 test_diagnostic_context dc
;
3071 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3072 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3073 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3074 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3075 "TEST PREFIX: ^~~~~~\n"
3076 "TEST PREFIX: ------\n",
3077 pp_formatted_text (dc
.printer
));
3080 /* Normal, with ruler. */
3082 test_diagnostic_context dc
;
3083 dc
.show_ruler_p
= true;
3084 dc
.caret_max_width
= 104;
3085 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3086 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3087 " 1 2 3 4 5 6 7 8 9 0 \n"
3088 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3089 " foo = bar.field;\n"
3092 pp_formatted_text (dc
.printer
));
3095 /* Test of adding a prefix, with ruler. */
3097 test_diagnostic_context dc
;
3098 dc
.show_ruler_p
= true;
3099 dc
.caret_max_width
= 50;
3100 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3101 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3102 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3103 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3104 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3105 "TEST PREFIX: foo = bar.field;\n"
3106 "TEST PREFIX: ^~~~~~\n"
3107 "TEST PREFIX: ------\n",
3108 pp_formatted_text (dc
.printer
));
3111 /* Test of adding a prefix, with ruler and line numbers. */
3113 test_diagnostic_context dc
;
3114 dc
.show_ruler_p
= true;
3115 dc
.caret_max_width
= 50;
3116 dc
.show_line_numbers_p
= true;
3117 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3118 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3119 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3120 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3121 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3122 "TEST PREFIX: 1 | foo = bar.field;\n"
3123 "TEST PREFIX: | ^~~~~~\n"
3124 "TEST PREFIX: | ------\n",
3125 pp_formatted_text (dc
.printer
));
3129 /* Replace fix-it hint: replacing "field" with "m_field". */
3132 test_one_liner_fixit_replace ()
3134 test_diagnostic_context dc
;
3135 location_t start
= linemap_position_for_column (line_table
, 11);
3136 location_t finish
= linemap_position_for_column (line_table
, 15);
3137 location_t field
= make_location (start
, start
, finish
);
3138 rich_location
richloc (line_table
, field
);
3139 richloc
.add_fixit_replace ("m_field");
3140 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3141 ASSERT_STREQ (" foo = bar.field;\n"
3144 pp_formatted_text (dc
.printer
));
3147 /* Replace fix-it hint: replacing "field" with "m_field",
3148 but where the caret was elsewhere. */
3151 test_one_liner_fixit_replace_non_equal_range ()
3153 test_diagnostic_context dc
;
3154 location_t equals
= linemap_position_for_column (line_table
, 5);
3155 location_t start
= linemap_position_for_column (line_table
, 11);
3156 location_t finish
= linemap_position_for_column (line_table
, 15);
3157 rich_location
richloc (line_table
, equals
);
3159 range
.m_start
= start
;
3160 range
.m_finish
= finish
;
3161 richloc
.add_fixit_replace (range
, "m_field");
3162 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3163 /* The replacement range is not indicated in the annotation line, so
3164 it should be indicated via an additional underline. */
3165 ASSERT_STREQ (" foo = bar.field;\n"
3169 pp_formatted_text (dc
.printer
));
3172 /* Replace fix-it hint: replacing "field" with "m_field",
3173 where the caret was elsewhere, but where a secondary range
3174 exactly covers "field". */
3177 test_one_liner_fixit_replace_equal_secondary_range ()
3179 test_diagnostic_context dc
;
3180 location_t equals
= linemap_position_for_column (line_table
, 5);
3181 location_t start
= linemap_position_for_column (line_table
, 11);
3182 location_t finish
= linemap_position_for_column (line_table
, 15);
3183 rich_location
richloc (line_table
, equals
);
3184 location_t field
= make_location (start
, start
, finish
);
3185 richloc
.add_range (field
);
3186 richloc
.add_fixit_replace (field
, "m_field");
3187 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3188 /* The replacement range is indicated in the annotation line,
3189 so it shouldn't be indicated via an additional underline. */
3190 ASSERT_STREQ (" foo = bar.field;\n"
3193 pp_formatted_text (dc
.printer
));
3196 /* Verify that we can use ad-hoc locations when adding fixits to a
3200 test_one_liner_fixit_validation_adhoc_locations ()
3202 /* Generate a range that's too long to be packed, so must
3203 be stored as an ad-hoc location (given the defaults
3204 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3205 const location_t c7
= linemap_position_for_column (line_table
, 7);
3206 const location_t c47
= linemap_position_for_column (line_table
, 47);
3207 const location_t loc
= make_location (c7
, c7
, c47
);
3209 if (c47
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3212 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
3216 rich_location
richloc (line_table
, loc
);
3217 richloc
.add_fixit_insert_before (loc
, "test");
3218 /* It should not have been discarded by the validator. */
3219 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3221 test_diagnostic_context dc
;
3222 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3223 ASSERT_STREQ (" foo = bar.field;\n"
3226 pp_formatted_text (dc
.printer
));
3231 rich_location
richloc (line_table
, loc
);
3232 source_range range
= source_range::from_locations (loc
, c47
);
3233 richloc
.add_fixit_remove (range
);
3234 /* It should not have been discarded by the validator. */
3235 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3237 test_diagnostic_context dc
;
3238 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3239 ASSERT_STREQ (" foo = bar.field;\n"
3241 " -----------------------------------------\n",
3242 pp_formatted_text (dc
.printer
));
3247 rich_location
richloc (line_table
, loc
);
3248 source_range range
= source_range::from_locations (loc
, c47
);
3249 richloc
.add_fixit_replace (range
, "test");
3250 /* It should not have been discarded by the validator. */
3251 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3253 test_diagnostic_context dc
;
3254 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3255 ASSERT_STREQ (" foo = bar.field;\n"
3258 pp_formatted_text (dc
.printer
));
3262 /* Test of consolidating insertions at the same location. */
3265 test_one_liner_many_fixits_1 ()
3267 test_diagnostic_context dc
;
3268 location_t equals
= linemap_position_for_column (line_table
, 5);
3269 rich_location
richloc (line_table
, equals
);
3270 for (int i
= 0; i
< 19; i
++)
3271 richloc
.add_fixit_insert_before ("a");
3272 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3273 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3274 ASSERT_STREQ (" foo = bar.field;\n"
3276 " aaaaaaaaaaaaaaaaaaa\n",
3277 pp_formatted_text (dc
.printer
));
3280 /* Ensure that we can add an arbitrary number of fix-it hints to a
3281 rich_location, even if they are not consolidated. */
3284 test_one_liner_many_fixits_2 ()
3286 test_diagnostic_context dc
;
3287 location_t equals
= linemap_position_for_column (line_table
, 5);
3288 rich_location
richloc (line_table
, equals
);
3289 for (int i
= 0; i
< 19; i
++)
3291 location_t loc
= linemap_position_for_column (line_table
, (i
* 2) + 1);
3292 richloc
.add_fixit_insert_before (loc
, "a");
3294 ASSERT_EQ (19, richloc
.get_num_fixit_hints ());
3295 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3296 ASSERT_STREQ (" foo = bar.field;\n"
3298 " a a a a a a a a a a a a a a a a a a a\n",
3299 pp_formatted_text (dc
.printer
));
3302 /* Test of labeling the ranges within a rich_location. */
3305 test_one_liner_labels ()
3308 = make_location (linemap_position_for_column (line_table
, 1),
3309 linemap_position_for_column (line_table
, 1),
3310 linemap_position_for_column (line_table
, 3));
3312 = make_location (linemap_position_for_column (line_table
, 7),
3313 linemap_position_for_column (line_table
, 7),
3314 linemap_position_for_column (line_table
, 9));
3316 = make_location (linemap_position_for_column (line_table
, 11),
3317 linemap_position_for_column (line_table
, 11),
3318 linemap_position_for_column (line_table
, 15));
3320 /* Example where all the labels fit on one line. */
3322 text_range_label
label0 ("0");
3323 text_range_label
label1 ("1");
3324 text_range_label
label2 ("2");
3325 gcc_rich_location
richloc (foo
, &label0
);
3326 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3327 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3330 test_diagnostic_context dc
;
3331 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3332 ASSERT_STREQ (" foo = bar.field;\n"
3336 pp_formatted_text (dc
.printer
));
3339 /* Verify that we can disable label-printing. */
3341 test_diagnostic_context dc
;
3342 dc
.show_labels_p
= false;
3343 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3344 ASSERT_STREQ (" foo = bar.field;\n"
3346 pp_formatted_text (dc
.printer
));
3350 /* Example where the labels need extra lines. */
3352 text_range_label
label0 ("label 0");
3353 text_range_label
label1 ("label 1");
3354 text_range_label
label2 ("label 2");
3355 gcc_rich_location
richloc (foo
, &label0
);
3356 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3357 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3359 test_diagnostic_context dc
;
3360 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3361 ASSERT_STREQ (" foo = bar.field;\n"
3367 pp_formatted_text (dc
.printer
));
3370 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3371 but label 1 just touches label 2. */
3373 text_range_label
label0 ("aaaaa");
3374 text_range_label
label1 ("bbbb");
3375 text_range_label
label2 ("c");
3376 gcc_rich_location
richloc (foo
, &label0
);
3377 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3378 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3380 test_diagnostic_context dc
;
3381 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3382 ASSERT_STREQ (" foo = bar.field;\n"
3387 pp_formatted_text (dc
.printer
));
3390 /* Example of out-of-order ranges (thus requiring a sort). */
3392 text_range_label
label0 ("0");
3393 text_range_label
label1 ("1");
3394 text_range_label
label2 ("2");
3395 gcc_rich_location
richloc (field
, &label0
);
3396 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3397 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3399 test_diagnostic_context dc
;
3400 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3401 ASSERT_STREQ (" foo = bar.field;\n"
3405 pp_formatted_text (dc
.printer
));
3408 /* Ensure we don't ICE if multiple ranges with labels are on
3411 text_range_label
label0 ("label 0");
3412 text_range_label
label1 ("label 1");
3413 text_range_label
label2 ("label 2");
3414 gcc_rich_location
richloc (bar
, &label0
);
3415 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3416 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3418 test_diagnostic_context dc
;
3419 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3420 ASSERT_STREQ (" foo = bar.field;\n"
3426 pp_formatted_text (dc
.printer
));
3429 /* Example of out-of-order ranges (thus requiring a sort), where
3430 they overlap, and there are multiple ranges on the same point. */
3432 text_range_label
label_0a ("label 0a");
3433 text_range_label
label_1a ("label 1a");
3434 text_range_label
label_2a ("label 2a");
3435 text_range_label
label_0b ("label 0b");
3436 text_range_label
label_1b ("label 1b");
3437 text_range_label
label_2b ("label 2b");
3438 text_range_label
label_0c ("label 0c");
3439 text_range_label
label_1c ("label 1c");
3440 text_range_label
label_2c ("label 2c");
3441 gcc_rich_location
richloc (field
, &label_0a
);
3442 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1a
);
3443 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2a
);
3445 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0b
);
3446 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1b
);
3447 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2b
);
3449 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0c
);
3450 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1c
);
3451 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2c
);
3453 test_diagnostic_context dc
;
3454 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3455 ASSERT_STREQ (" foo = bar.field;\n"
3467 pp_formatted_text (dc
.printer
));
3470 /* Verify that a NULL result from range_label::get_text is
3471 handled gracefully. */
3473 text_range_label
label (NULL
);
3474 gcc_rich_location
richloc (bar
, &label
);
3476 test_diagnostic_context dc
;
3477 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3478 ASSERT_STREQ (" foo = bar.field;\n"
3480 pp_formatted_text (dc
.printer
));
3483 /* TODO: example of formatted printing (needs to be in
3484 gcc-rich-location.c due to Makefile.in issues). */
3487 /* Run the various one-liner tests. */
3490 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
3492 /* Create a tempfile and write some text to it.
3493 ....................0000000001111111.
3494 ....................1234567890123456. */
3495 const char *content
= "foo = bar.field;\n";
3496 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3497 line_table_test
ltt (case_
);
3499 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3501 location_t line_end
= linemap_position_for_column (line_table
, 16);
3503 /* Don't attempt to run the tests if column data might be unavailable. */
3504 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3507 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
3508 ASSERT_EQ (1, LOCATION_LINE (line_end
));
3509 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
3511 test_one_liner_simple_caret ();
3512 test_one_liner_caret_and_range ();
3513 test_one_liner_multiple_carets_and_ranges ();
3514 test_one_liner_fixit_insert_before ();
3515 test_one_liner_fixit_insert_after ();
3516 test_one_liner_fixit_remove ();
3517 test_one_liner_fixit_replace ();
3518 test_one_liner_fixit_replace_non_equal_range ();
3519 test_one_liner_fixit_replace_equal_secondary_range ();
3520 test_one_liner_fixit_validation_adhoc_locations ();
3521 test_one_liner_many_fixits_1 ();
3522 test_one_liner_many_fixits_2 ();
3523 test_one_liner_labels ();
3526 /* Version of all one-liner tests exercising multibyte awareness. For
3527 simplicity we stick to using two multibyte characters in the test, U+1F602
3528 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3529 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3530 below asserts would be easier to read if we used UTF-8 directly in the
3531 string constants, but it seems better not to demand the host compiler
3532 support this, when it isn't otherwise necessary. Instead, whenever an
3533 extended character appears in a string, we put a line break after it so that
3534 all succeeding characters can appear visually at the correct display column.
3536 All of these work on the following 1-line source file:
3538 .0000000001111111111222222 display
3539 .1234567890123456789012345 columns
3540 "SS_foo = P_bar.SS_fieldP;\n"
3541 .0000000111111111222222223 byte
3542 .1356789012456789134567891 columns
3544 which is set up by test_diagnostic_show_locus_one_liner and calls
3545 them. Here SS represents the two display columns for the U+1F602 emoji and
3546 P represents the one display column for the U+03C0 pi symbol. */
3551 test_one_liner_simple_caret_utf8 ()
3553 test_diagnostic_context dc
;
3554 location_t caret
= linemap_position_for_column (line_table
, 18);
3555 rich_location
richloc (line_table
, caret
);
3556 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3557 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3559 "_bar.\xf0\x9f\x98\x82"
3563 pp_formatted_text (dc
.printer
));
3566 /* Caret and range. */
3568 test_one_liner_caret_and_range_utf8 ()
3570 test_diagnostic_context dc
;
3571 location_t caret
= linemap_position_for_column (line_table
, 18);
3572 location_t start
= linemap_position_for_column (line_table
, 12);
3573 location_t finish
= linemap_position_for_column (line_table
, 30);
3574 location_t loc
= make_location (caret
, start
, finish
);
3575 rich_location
richloc (line_table
, loc
);
3576 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3577 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3579 "_bar.\xf0\x9f\x98\x82"
3582 " ~~~~~^~~~~~~~~~\n",
3583 pp_formatted_text (dc
.printer
));
3586 /* Multiple ranges and carets. */
3589 test_one_liner_multiple_carets_and_ranges_utf8 ()
3591 test_diagnostic_context dc
;
3593 = make_location (linemap_position_for_column (line_table
, 7),
3594 linemap_position_for_column (line_table
, 1),
3595 linemap_position_for_column (line_table
, 8));
3596 dc
.caret_chars
[0] = 'A';
3599 = make_location (linemap_position_for_column (line_table
, 16),
3600 linemap_position_for_column (line_table
, 12),
3601 linemap_position_for_column (line_table
, 17));
3602 dc
.caret_chars
[1] = 'B';
3605 = make_location (linemap_position_for_column (line_table
, 26),
3606 linemap_position_for_column (line_table
, 19),
3607 linemap_position_for_column (line_table
, 30));
3608 dc
.caret_chars
[2] = 'C';
3609 rich_location
richloc (line_table
, foo
);
3610 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3611 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3612 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3613 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3615 "_bar.\xf0\x9f\x98\x82"
3618 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3619 pp_formatted_text (dc
.printer
));
3622 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3625 test_one_liner_fixit_insert_before_utf8 ()
3627 test_diagnostic_context dc
;
3628 location_t caret
= linemap_position_for_column (line_table
, 12);
3629 rich_location
richloc (line_table
, caret
);
3630 richloc
.add_fixit_insert_before ("&");
3631 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3632 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3634 "_bar.\xf0\x9f\x98\x82"
3639 pp_formatted_text (dc
.printer
));
3642 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3645 test_one_liner_fixit_insert_after_utf8 ()
3647 test_diagnostic_context dc
;
3648 location_t start
= linemap_position_for_column (line_table
, 1);
3649 location_t finish
= linemap_position_for_column (line_table
, 8);
3650 location_t foo
= make_location (start
, start
, finish
);
3651 rich_location
richloc (line_table
, foo
);
3652 richloc
.add_fixit_insert_after ("[0]");
3653 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3654 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3656 "_bar.\xf0\x9f\x98\x82"
3661 pp_formatted_text (dc
.printer
));
3664 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3667 test_one_liner_fixit_remove_utf8 ()
3669 test_diagnostic_context dc
;
3670 location_t start
= linemap_position_for_column (line_table
, 18);
3671 location_t finish
= linemap_position_for_column (line_table
, 30);
3672 location_t dot
= make_location (start
, start
, finish
);
3673 rich_location
richloc (line_table
, dot
);
3674 richloc
.add_fixit_remove ();
3675 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3676 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3678 "_bar.\xf0\x9f\x98\x82"
3683 pp_formatted_text (dc
.printer
));
3686 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3689 test_one_liner_fixit_replace_utf8 ()
3691 test_diagnostic_context dc
;
3692 location_t start
= linemap_position_for_column (line_table
, 19);
3693 location_t finish
= linemap_position_for_column (line_table
, 30);
3694 location_t field
= make_location (start
, start
, finish
);
3695 rich_location
richloc (line_table
, field
);
3696 richloc
.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3697 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3698 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3700 "_bar.\xf0\x9f\x98\x82"
3704 " m_\xf0\x9f\x98\x82"
3706 pp_formatted_text (dc
.printer
));
3709 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3710 but where the caret was elsewhere. */
3713 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3715 test_diagnostic_context dc
;
3716 location_t equals
= linemap_position_for_column (line_table
, 10);
3717 location_t start
= linemap_position_for_column (line_table
, 19);
3718 location_t finish
= linemap_position_for_column (line_table
, 30);
3719 rich_location
richloc (line_table
, equals
);
3721 range
.m_start
= start
;
3722 range
.m_finish
= finish
;
3723 richloc
.add_fixit_replace (range
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3724 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3725 /* The replacement range is not indicated in the annotation line, so
3726 it should be indicated via an additional underline. */
3727 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3729 "_bar.\xf0\x9f\x98\x82"
3734 " m_\xf0\x9f\x98\x82"
3736 pp_formatted_text (dc
.printer
));
3739 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3740 where the caret was elsewhere, but where a secondary range
3741 exactly covers "field". */
3744 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3746 test_diagnostic_context dc
;
3747 location_t equals
= linemap_position_for_column (line_table
, 10);
3748 location_t start
= linemap_position_for_column (line_table
, 19);
3749 location_t finish
= linemap_position_for_column (line_table
, 30);
3750 rich_location
richloc (line_table
, equals
);
3751 location_t field
= make_location (start
, start
, finish
);
3752 richloc
.add_range (field
);
3753 richloc
.add_fixit_replace (field
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3754 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3755 /* The replacement range is indicated in the annotation line,
3756 so it shouldn't be indicated via an additional underline. */
3757 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3759 "_bar.\xf0\x9f\x98\x82"
3763 " m_\xf0\x9f\x98\x82"
3765 pp_formatted_text (dc
.printer
));
3768 /* Verify that we can use ad-hoc locations when adding fixits to a
3772 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3774 /* Generate a range that's too long to be packed, so must
3775 be stored as an ad-hoc location (given the defaults
3776 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3777 const location_t c12
= linemap_position_for_column (line_table
, 12);
3778 const location_t c52
= linemap_position_for_column (line_table
, 52);
3779 const location_t loc
= make_location (c12
, c12
, c52
);
3781 if (c52
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3784 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
3788 rich_location
richloc (line_table
, loc
);
3789 richloc
.add_fixit_insert_before (loc
, "test");
3790 /* It should not have been discarded by the validator. */
3791 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3793 test_diagnostic_context dc
;
3794 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3795 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3797 "_bar.\xf0\x9f\x98\x82"
3800 " ^~~~~~~~~~~~~~~~ \n"
3802 pp_formatted_text (dc
.printer
));
3807 rich_location
richloc (line_table
, loc
);
3808 source_range range
= source_range::from_locations (loc
, c52
);
3809 richloc
.add_fixit_remove (range
);
3810 /* It should not have been discarded by the validator. */
3811 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3813 test_diagnostic_context dc
;
3814 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3815 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3817 "_bar.\xf0\x9f\x98\x82"
3820 " ^~~~~~~~~~~~~~~~ \n"
3821 " -------------------------------------\n",
3822 pp_formatted_text (dc
.printer
));
3827 rich_location
richloc (line_table
, loc
);
3828 source_range range
= source_range::from_locations (loc
, c52
);
3829 richloc
.add_fixit_replace (range
, "test");
3830 /* It should not have been discarded by the validator. */
3831 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3833 test_diagnostic_context dc
;
3834 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3835 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3837 "_bar.\xf0\x9f\x98\x82"
3840 " ^~~~~~~~~~~~~~~~ \n"
3842 pp_formatted_text (dc
.printer
));
3846 /* Test of consolidating insertions at the same location. */
3849 test_one_liner_many_fixits_1_utf8 ()
3851 test_diagnostic_context dc
;
3852 location_t equals
= linemap_position_for_column (line_table
, 10);
3853 rich_location
richloc (line_table
, equals
);
3854 for (int i
= 0; i
< 19; i
++)
3855 richloc
.add_fixit_insert_before (i
& 1 ? "@" : "\xcf\x80");
3856 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3857 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3858 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3860 "_bar.\xf0\x9f\x98\x82"
3864 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3865 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3866 pp_formatted_text (dc
.printer
));
3869 /* Ensure that we can add an arbitrary number of fix-it hints to a
3870 rich_location, even if they are not consolidated. */
3873 test_one_liner_many_fixits_2_utf8 ()
3875 test_diagnostic_context dc
;
3876 location_t equals
= linemap_position_for_column (line_table
, 10);
3877 rich_location
richloc (line_table
, equals
);
3878 const int nlocs
= 19;
3879 int locs
[nlocs
] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3880 34, 36, 38, 40, 42, 44};
3881 for (int i
= 0; i
!= nlocs
; ++i
)
3883 location_t loc
= linemap_position_for_column (line_table
, locs
[i
]);
3884 richloc
.add_fixit_insert_before (loc
, i
& 1 ? "@" : "\xcf\x80");
3887 ASSERT_EQ (nlocs
, richloc
.get_num_fixit_hints ());
3888 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3889 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3891 "_bar.\xf0\x9f\x98\x82"
3895 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3896 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3897 pp_formatted_text (dc
.printer
));
3900 /* Test of labeling the ranges within a rich_location. */
3903 test_one_liner_labels_utf8 ()
3906 = make_location (linemap_position_for_column (line_table
, 1),
3907 linemap_position_for_column (line_table
, 1),
3908 linemap_position_for_column (line_table
, 8));
3910 = make_location (linemap_position_for_column (line_table
, 12),
3911 linemap_position_for_column (line_table
, 12),
3912 linemap_position_for_column (line_table
, 17));
3914 = make_location (linemap_position_for_column (line_table
, 19),
3915 linemap_position_for_column (line_table
, 19),
3916 linemap_position_for_column (line_table
, 30));
3918 /* Example where all the labels fit on one line. */
3920 /* These three labels contain multibyte characters such that their byte
3921 lengths are respectively (12, 10, 18), but their display widths are only
3922 (6, 5, 9). All three fit on the line when considering the display
3923 widths, but not when considering the byte widths, so verify that we do
3924 indeed put them all on one line. */
3925 text_range_label label0
3926 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3927 text_range_label label1
3928 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3929 text_range_label label2
3930 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3932 gcc_rich_location
richloc (foo
, &label0
);
3933 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3934 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3937 test_diagnostic_context dc
;
3938 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3939 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3941 "_bar.\xf0\x9f\x98\x82"
3944 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3946 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3947 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3948 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3949 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3950 pp_formatted_text (dc
.printer
));
3955 /* Example where the labels need extra lines. */
3957 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
3958 text_range_label
label1 ("label 1\xcf\x80");
3959 text_range_label
label2 ("label 2\xcf\x80");
3960 gcc_rich_location
richloc (foo
, &label0
);
3961 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3962 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3964 test_diagnostic_context dc
;
3965 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3967 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3969 "_bar.\xf0\x9f\x98\x82"
3972 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3974 " | | label 2\xcf\x80\n"
3975 " | label 1\xcf\x80\n"
3976 " label 0\xf0\x9f\x98\x82\n",
3977 pp_formatted_text (dc
.printer
));
3980 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3981 but label 1 just touches label 2. */
3983 text_range_label
label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3984 text_range_label
label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3985 text_range_label
label2 ("c");
3986 gcc_rich_location
richloc (foo
, &label0
);
3987 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3988 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3990 test_diagnostic_context dc
;
3991 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3992 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3994 "_bar.\xf0\x9f\x98\x82"
3997 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4000 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4001 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4002 pp_formatted_text (dc
.printer
));
4006 /* Make sure that colorization codes don't interrupt a multibyte
4007 sequence, which would corrupt it. */
4009 test_one_liner_colorized_utf8 ()
4011 test_diagnostic_context dc
;
4012 dc
.colorize_source_p
= true;
4013 diagnostic_color_init (&dc
, DIAGNOSTICS_COLOR_YES
);
4014 const location_t pi
= linemap_position_for_column (line_table
, 12);
4015 rich_location
richloc (line_table
, pi
);
4016 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4018 /* In order to avoid having the test depend on exactly how the colorization
4019 was effected, just confirm there are two pi characters in the output. */
4020 const char *result
= pp_formatted_text (dc
.printer
);
4021 const char *null_term
= result
+ strlen (result
);
4022 const char *first_pi
= strstr (result
, "\xcf\x80");
4023 ASSERT_TRUE (first_pi
&& first_pi
<= null_term
- 2);
4024 ASSERT_STR_CONTAINS (first_pi
+ 2, "\xcf\x80");
4027 /* Run the various one-liner tests. */
4030 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case
&case_
)
4032 /* Create a tempfile and write some text to it. */
4035 0000000000000000000000011111111111111111111111111111112222222222222
4036 1111111122222222345678900000000123456666666677777777890123444444445 */
4037 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4038 /* 0000000000000000000001111111111111111111222222222222222222222233333
4039 1111222233334444567890122223333456789999000011112222345678999900001
4041 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4042 line_table_test
ltt (case_
);
4044 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
4046 location_t line_end
= linemap_position_for_column (line_table
, 31);
4048 /* Don't attempt to run the tests if column data might be unavailable. */
4049 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4052 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
4053 ASSERT_EQ (1, LOCATION_LINE (line_end
));
4054 ASSERT_EQ (31, LOCATION_COLUMN (line_end
));
4056 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
4057 ASSERT_EQ (25, cpp_display_width (lspan
.get_buffer (), lspan
.length (),
4059 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end
),
4062 test_one_liner_simple_caret_utf8 ();
4063 test_one_liner_caret_and_range_utf8 ();
4064 test_one_liner_multiple_carets_and_ranges_utf8 ();
4065 test_one_liner_fixit_insert_before_utf8 ();
4066 test_one_liner_fixit_insert_after_utf8 ();
4067 test_one_liner_fixit_remove_utf8 ();
4068 test_one_liner_fixit_replace_utf8 ();
4069 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4070 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4071 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4072 test_one_liner_many_fixits_1_utf8 ();
4073 test_one_liner_many_fixits_2_utf8 ();
4074 test_one_liner_labels_utf8 ();
4075 test_one_liner_colorized_utf8 ();
4078 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4081 test_add_location_if_nearby (const line_table_case
&case_
)
4083 /* Create a tempfile and write some text to it.
4084 ...000000000111111111122222222223333333333.
4085 ...123456789012345678901234567890123456789. */
4087 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4088 "struct different_line\n" /* line 2. */
4090 " double x;\n" /* line 4. */
4091 " double y;\n" /* line 5. */
4092 ";\n"); /* line 6. */
4093 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4094 line_table_test
ltt (case_
);
4096 const line_map_ordinary
*ord_map
4097 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4098 tmp
.get_filename (), 0));
4100 linemap_line_start (line_table
, 1, 100);
4102 const location_t final_line_end
4103 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 7);
4105 /* Don't attempt to run the tests if column data might be unavailable. */
4106 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4109 /* Test of add_location_if_nearby on the same line as the
4110 primary location. */
4112 const location_t missing_close_brace_1_39
4113 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 39);
4114 const location_t matching_open_brace_1_18
4115 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4116 gcc_rich_location
richloc (missing_close_brace_1_39
);
4117 bool added
= richloc
.add_location_if_nearby (matching_open_brace_1_18
);
4118 ASSERT_TRUE (added
);
4119 ASSERT_EQ (2, richloc
.get_num_locations ());
4120 test_diagnostic_context dc
;
4121 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4122 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4124 pp_formatted_text (dc
.printer
));
4127 /* Test of add_location_if_nearby on a different line to the
4128 primary location. */
4130 const location_t missing_close_brace_6_1
4131 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 1);
4132 const location_t matching_open_brace_3_1
4133 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 1);
4134 gcc_rich_location
richloc (missing_close_brace_6_1
);
4135 bool added
= richloc
.add_location_if_nearby (matching_open_brace_3_1
);
4136 ASSERT_FALSE (added
);
4137 ASSERT_EQ (1, richloc
.get_num_locations ());
4141 /* Verify that we print fixits even if they only affect lines
4142 outside those covered by the ranges in the rich_location. */
4145 test_diagnostic_show_locus_fixit_lines (const line_table_case
&case_
)
4147 /* Create a tempfile and write some text to it.
4148 ...000000000111111111122222222223333333333.
4149 ...123456789012345678901234567890123456789. */
4151 = ("struct point { double x; double y; };\n" /* line 1. */
4152 "struct point origin = {x: 0.0,\n" /* line 2. */
4153 " y\n" /* line 3. */
4156 " : 0.0};\n"); /* line 6. */
4157 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4158 line_table_test
ltt (case_
);
4160 const line_map_ordinary
*ord_map
4161 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4162 tmp
.get_filename (), 0));
4164 linemap_line_start (line_table
, 1, 100);
4166 const location_t final_line_end
4167 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4169 /* Don't attempt to run the tests if column data might be unavailable. */
4170 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4173 /* A pair of tests for modernizing the initializers to C99-style. */
4175 /* The one-liner case (line 2). */
4177 test_diagnostic_context dc
;
4179 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 24);
4180 const location_t colon
4181 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 25);
4182 rich_location
richloc (line_table
, colon
);
4183 richloc
.add_fixit_insert_before (x
, ".");
4184 richloc
.add_fixit_replace (colon
, "=");
4185 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4186 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4189 pp_formatted_text (dc
.printer
));
4192 /* The multiline case. The caret for the rich_location is on line 6;
4193 verify that insertion fixit on line 3 is still printed (and that
4194 span starts are printed due to the gap between the span at line 3
4195 and that at line 6). */
4197 test_diagnostic_context dc
;
4199 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4200 const location_t colon
4201 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4202 rich_location
richloc (line_table
, colon
);
4203 richloc
.add_fixit_insert_before (y
, ".");
4204 richloc
.add_fixit_replace (colon
, "=");
4205 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4206 ASSERT_STREQ ("FILENAME:3:24:\n"
4213 pp_formatted_text (dc
.printer
));
4216 /* As above, but verify the behavior of multiple line spans
4217 with line-numbering enabled. */
4220 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4221 const location_t colon
4222 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4223 rich_location
richloc (line_table
, colon
);
4224 richloc
.add_fixit_insert_before (y
, ".");
4225 richloc
.add_fixit_replace (colon
, "=");
4226 test_diagnostic_context dc
;
4227 dc
.show_line_numbers_p
= true;
4228 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4229 ASSERT_STREQ (" 3 | y\n"
4235 pp_formatted_text (dc
.printer
));
4240 /* Verify that fix-it hints are appropriately consolidated.
4242 If any fix-it hints in a rich_location involve locations beyond
4243 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4244 the fix-it as a whole, so there should be none.
4246 Otherwise, verify that consecutive "replace" and "remove" fix-its
4247 are merged, and that other fix-its remain separate. */
4250 test_fixit_consolidation (const line_table_case
&case_
)
4252 line_table_test
ltt (case_
);
4254 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
4256 const location_t c10
= linemap_position_for_column (line_table
, 10);
4257 const location_t c15
= linemap_position_for_column (line_table
, 15);
4258 const location_t c16
= linemap_position_for_column (line_table
, 16);
4259 const location_t c17
= linemap_position_for_column (line_table
, 17);
4260 const location_t c20
= linemap_position_for_column (line_table
, 20);
4261 const location_t c21
= linemap_position_for_column (line_table
, 21);
4262 const location_t caret
= c10
;
4264 /* Insert + insert. */
4266 rich_location
richloc (line_table
, caret
);
4267 richloc
.add_fixit_insert_before (c10
, "foo");
4268 richloc
.add_fixit_insert_before (c15
, "bar");
4270 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4271 /* Bogus column info for 2nd fixit, so no fixits. */
4272 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4274 /* They should not have been merged. */
4275 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4278 /* Insert + replace. */
4280 rich_location
richloc (line_table
, caret
);
4281 richloc
.add_fixit_insert_before (c10
, "foo");
4282 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
4285 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4286 /* Bogus column info for 2nd fixit, so no fixits. */
4287 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4289 /* They should not have been merged. */
4290 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4293 /* Replace + non-consecutive insert. */
4295 rich_location
richloc (line_table
, caret
);
4296 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4298 richloc
.add_fixit_insert_before (c17
, "foo");
4300 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4301 /* Bogus column info for 2nd fixit, so no fixits. */
4302 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4304 /* They should not have been merged. */
4305 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4308 /* Replace + non-consecutive replace. */
4310 rich_location
richloc (line_table
, caret
);
4311 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4313 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
4316 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4317 /* Bogus column info for 2nd fixit, so no fixits. */
4318 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4320 /* They should not have been merged. */
4321 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4324 /* Replace + consecutive replace. */
4326 rich_location
richloc (line_table
, caret
);
4327 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4329 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
4332 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4333 /* Bogus column info for 2nd fixit, so no fixits. */
4334 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4337 /* They should have been merged into a single "replace". */
4338 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4339 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4340 ASSERT_STREQ ("foobar", hint
->get_string ());
4341 ASSERT_EQ (c10
, hint
->get_start_loc ());
4342 ASSERT_EQ (c21
, hint
->get_next_loc ());
4346 /* Replace + consecutive removal. */
4348 rich_location
richloc (line_table
, caret
);
4349 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4351 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4353 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4354 /* Bogus column info for 2nd fixit, so no fixits. */
4355 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4358 /* They should have been merged into a single replace, with the
4359 range extended to cover that of the removal. */
4360 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4361 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4362 ASSERT_STREQ ("foo", hint
->get_string ());
4363 ASSERT_EQ (c10
, hint
->get_start_loc ());
4364 ASSERT_EQ (c21
, hint
->get_next_loc ());
4368 /* Consecutive removals. */
4370 rich_location
richloc (line_table
, caret
);
4371 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
4372 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4374 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4375 /* Bogus column info for 2nd fixit, so no fixits. */
4376 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4379 /* They should have been merged into a single "replace-with-empty". */
4380 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4381 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4382 ASSERT_STREQ ("", hint
->get_string ());
4383 ASSERT_EQ (c10
, hint
->get_start_loc ());
4384 ASSERT_EQ (c21
, hint
->get_next_loc ());
4389 /* Verify that the line_corrections machinery correctly prints
4390 overlapping fixit-hints. */
4393 test_overlapped_fixit_printing (const line_table_case
&case_
)
4395 /* Create a tempfile and write some text to it.
4396 ...000000000111111111122222222223333333333.
4397 ...123456789012345678901234567890123456789. */
4399 = (" foo *f = (foo *)ptr->field;\n");
4400 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4401 line_table_test
ltt (case_
);
4403 const line_map_ordinary
*ord_map
4404 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4405 tmp
.get_filename (), 0));
4407 linemap_line_start (line_table
, 1, 100);
4409 const location_t final_line_end
4410 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4412 /* Don't attempt to run the tests if column data might be unavailable. */
4413 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4416 /* A test for converting a C-style cast to a C++-style cast. */
4417 const location_t open_paren
4418 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 12);
4419 const location_t close_paren
4420 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4421 const location_t expr_start
4422 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 19);
4423 const location_t expr_finish
4424 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 28);
4425 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4427 /* Various examples of fix-it hints that aren't themselves consolidated,
4428 but for which the *printing* may need consolidation. */
4430 /* Example where 3 fix-it hints are printed as one. */
4432 test_diagnostic_context dc
;
4433 rich_location
richloc (line_table
, expr
);
4434 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4435 richloc
.add_fixit_replace (close_paren
, "> (");
4436 richloc
.add_fixit_insert_after (")");
4438 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4439 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4441 " -----------------\n"
4442 " const_cast<foo *> (ptr->field)\n",
4443 pp_formatted_text (dc
.printer
));
4445 /* Unit-test the line_corrections machinery. */
4446 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
4447 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4448 ASSERT_EQ (column_range (12, 12),
4449 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4450 ASSERT_EQ (column_range (12, 12),
4451 get_affected_range (&dc
, hint_0
, CU_DISPLAY_COLS
));
4452 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc
, hint_0
));
4453 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4454 ASSERT_EQ (column_range (18, 18),
4455 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4456 ASSERT_EQ (column_range (18, 18),
4457 get_affected_range (&dc
, hint_1
, CU_DISPLAY_COLS
));
4458 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc
, hint_1
));
4459 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
4460 ASSERT_EQ (column_range (29, 28),
4461 get_affected_range (&dc
, hint_2
, CU_BYTES
));
4462 ASSERT_EQ (column_range (29, 28),
4463 get_affected_range (&dc
, hint_2
, CU_DISPLAY_COLS
));
4464 ASSERT_EQ (column_range (29, 29), get_printed_columns (&dc
, hint_2
));
4466 /* Add each hint in turn to a line_corrections instance,
4467 and verify that they are consolidated into one correction instance
4469 line_corrections
lc (&dc
, tmp
.get_filename (), 1);
4471 /* The first replace hint by itself. */
4472 lc
.add_hint (hint_0
);
4473 ASSERT_EQ (1, lc
.m_corrections
.length ());
4474 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_bytes
);
4475 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
4476 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
4477 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
4479 /* After the second replacement hint, they are printed together
4480 as a replacement (along with the text between them). */
4481 lc
.add_hint (hint_1
);
4482 ASSERT_EQ (1, lc
.m_corrections
.length ());
4483 ASSERT_STREQ ("const_cast<foo *> (", lc
.m_corrections
[0]->m_text
);
4484 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_bytes
);
4485 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
4486 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
4488 /* After the final insertion hint, they are all printed together
4489 as a replacement (along with the text between them). */
4490 lc
.add_hint (hint_2
);
4491 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4492 lc
.m_corrections
[0]->m_text
);
4493 ASSERT_EQ (1, lc
.m_corrections
.length ());
4494 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_bytes
);
4495 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_columns
);
4496 ASSERT_EQ (column_range (12, 41), lc
.m_corrections
[0]->m_printed_columns
);
4499 /* Example where two are consolidated during printing. */
4501 test_diagnostic_context dc
;
4502 rich_location
richloc (line_table
, expr
);
4503 richloc
.add_fixit_replace (open_paren
, "CAST (");
4504 richloc
.add_fixit_replace (close_paren
, ") (");
4505 richloc
.add_fixit_insert_after (")");
4507 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4508 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4513 pp_formatted_text (dc
.printer
));
4516 /* Example where none are consolidated during printing. */
4518 test_diagnostic_context dc
;
4519 rich_location
richloc (line_table
, expr
);
4520 richloc
.add_fixit_replace (open_paren
, "CST (");
4521 richloc
.add_fixit_replace (close_paren
, ") (");
4522 richloc
.add_fixit_insert_after (")");
4524 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4525 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4530 pp_formatted_text (dc
.printer
));
4533 /* Example of deletion fix-it hints. */
4535 test_diagnostic_context dc
;
4536 rich_location
richloc (line_table
, expr
);
4537 richloc
.add_fixit_insert_before (open_paren
, "(bar *)");
4538 source_range victim
= {open_paren
, close_paren
};
4539 richloc
.add_fixit_remove (victim
);
4541 /* This case is actually handled by fixit-consolidation,
4542 rather than by line_corrections. */
4543 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4545 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4546 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4550 pp_formatted_text (dc
.printer
));
4553 /* Example of deletion fix-it hints that would overlap. */
4555 test_diagnostic_context dc
;
4556 rich_location
richloc (line_table
, expr
);
4557 richloc
.add_fixit_insert_before (open_paren
, "(longer *)");
4558 source_range victim
= {expr_start
, expr_finish
};
4559 richloc
.add_fixit_remove (victim
);
4561 /* These fixits are not consolidated. */
4562 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4564 /* But the corrections are. */
4565 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4566 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4568 " -----------------\n"
4569 " (longer *)(foo *)\n",
4570 pp_formatted_text (dc
.printer
));
4573 /* Example of insertion fix-it hints that would overlap. */
4575 test_diagnostic_context dc
;
4576 rich_location
richloc (line_table
, expr
);
4577 richloc
.add_fixit_insert_before (open_paren
, "LONGER THAN THE CAST");
4578 richloc
.add_fixit_insert_after (close_paren
, "TEST");
4580 /* The first insertion is long enough that if printed naively,
4581 it would overlap with the second.
4582 Verify that they are printed as a single replacement. */
4583 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4584 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4587 " LONGER THAN THE CAST(foo *)TEST\n",
4588 pp_formatted_text (dc
.printer
));
4592 /* Multibyte-aware version of preceding tests. See comments above
4593 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4597 test_overlapped_fixit_printing_utf8 (const line_table_case
&case_
)
4599 /* Create a tempfile and write some text to it. */
4603 00000000000000000000000111111111111111111111111222222222222222223
4604 12344444444555555556789012344444444555555556789012345678999999990 */
4605 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4606 /* 00000000000000000000011111111111111111111112222222222333333333333
4607 12344445555666677778901234566667777888899990123456789012333344445
4610 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4611 line_table_test
ltt (case_
);
4613 const line_map_ordinary
*ord_map
4614 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4615 tmp
.get_filename (), 0));
4617 linemap_line_start (line_table
, 1, 100);
4619 const location_t final_line_end
4620 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 50);
4622 /* Don't attempt to run the tests if column data might be unavailable. */
4623 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4626 /* A test for converting a C-style cast to a C++-style cast. */
4627 const location_t open_paren
4628 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 14);
4629 const location_t close_paren
4630 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 22);
4631 const location_t expr_start
4632 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
4633 const location_t expr_finish
4634 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 34);
4635 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4637 /* Various examples of fix-it hints that aren't themselves consolidated,
4638 but for which the *printing* may need consolidation. */
4640 /* Example where 3 fix-it hints are printed as one. */
4642 test_diagnostic_context dc
;
4643 rich_location
richloc (line_table
, expr
);
4644 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4645 richloc
.add_fixit_replace (close_paren
, "> (");
4646 richloc
.add_fixit_insert_after (")");
4648 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4649 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4650 " *f = (f\xf0\x9f\x98\x82"
4651 " *)ptr->field\xcf\x80"
4654 " ------------------\n"
4655 " const_cast<f\xf0\x9f\x98\x82"
4656 " *> (ptr->field\xcf\x80"
4658 pp_formatted_text (dc
.printer
));
4660 /* Unit-test the line_corrections machinery. */
4661 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
4662 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4663 ASSERT_EQ (column_range (14, 14),
4664 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4665 ASSERT_EQ (column_range (12, 12),
4666 get_affected_range (&dc
, hint_0
, CU_DISPLAY_COLS
));
4667 ASSERT_EQ (column_range (12, 22), get_printed_columns (&dc
, hint_0
));
4668 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4669 ASSERT_EQ (column_range (22, 22),
4670 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4671 ASSERT_EQ (column_range (18, 18),
4672 get_affected_range (&dc
, hint_1
, CU_DISPLAY_COLS
));
4673 ASSERT_EQ (column_range (18, 20), get_printed_columns (&dc
, hint_1
));
4674 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
4675 ASSERT_EQ (column_range (35, 34),
4676 get_affected_range (&dc
, hint_2
, CU_BYTES
));
4677 ASSERT_EQ (column_range (30, 29),
4678 get_affected_range (&dc
, hint_2
, CU_DISPLAY_COLS
));
4679 ASSERT_EQ (column_range (30, 30), get_printed_columns (&dc
, hint_2
));
4681 /* Add each hint in turn to a line_corrections instance,
4682 and verify that they are consolidated into one correction instance
4684 line_corrections
lc (&dc
, tmp
.get_filename (), 1);
4686 /* The first replace hint by itself. */
4687 lc
.add_hint (hint_0
);
4688 ASSERT_EQ (1, lc
.m_corrections
.length ());
4689 ASSERT_EQ (column_range (14, 14), lc
.m_corrections
[0]->m_affected_bytes
);
4690 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
4691 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
4692 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
4694 /* After the second replacement hint, they are printed together
4695 as a replacement (along with the text between them). */
4696 lc
.add_hint (hint_1
);
4697 ASSERT_EQ (1, lc
.m_corrections
.length ());
4698 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4699 lc
.m_corrections
[0]->m_text
);
4700 ASSERT_EQ (column_range (14, 22), lc
.m_corrections
[0]->m_affected_bytes
);
4701 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
4702 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
4704 /* After the final insertion hint, they are all printed together
4705 as a replacement (along with the text between them). */
4706 lc
.add_hint (hint_2
);
4707 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4708 lc
.m_corrections
[0]->m_text
);
4709 ASSERT_EQ (1, lc
.m_corrections
.length ());
4710 ASSERT_EQ (column_range (14, 34), lc
.m_corrections
[0]->m_affected_bytes
);
4711 ASSERT_EQ (column_range (12, 29), lc
.m_corrections
[0]->m_affected_columns
);
4712 ASSERT_EQ (column_range (12, 42), lc
.m_corrections
[0]->m_printed_columns
);
4715 /* Example where two are consolidated during printing. */
4717 test_diagnostic_context dc
;
4718 rich_location
richloc (line_table
, expr
);
4719 richloc
.add_fixit_replace (open_paren
, "CAST (");
4720 richloc
.add_fixit_replace (close_paren
, ") (");
4721 richloc
.add_fixit_insert_after (")");
4723 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4724 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4725 " *f = (f\xf0\x9f\x98\x82"
4726 " *)ptr->field\xcf\x80"
4732 pp_formatted_text (dc
.printer
));
4735 /* Example where none are consolidated during printing. */
4737 test_diagnostic_context dc
;
4738 rich_location
richloc (line_table
, expr
);
4739 richloc
.add_fixit_replace (open_paren
, "CST (");
4740 richloc
.add_fixit_replace (close_paren
, ") (");
4741 richloc
.add_fixit_insert_after (")");
4743 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4744 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4745 " *f = (f\xf0\x9f\x98\x82"
4746 " *)ptr->field\xcf\x80"
4752 pp_formatted_text (dc
.printer
));
4755 /* Example of deletion fix-it hints. */
4757 test_diagnostic_context dc
;
4758 rich_location
richloc (line_table
, expr
);
4759 richloc
.add_fixit_insert_before (open_paren
, "(bar\xf0\x9f\x98\x82 *)");
4760 source_range victim
= {open_paren
, close_paren
};
4761 richloc
.add_fixit_remove (victim
);
4763 /* This case is actually handled by fixit-consolidation,
4764 rather than by line_corrections. */
4765 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4767 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4768 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4769 " *f = (f\xf0\x9f\x98\x82"
4770 " *)ptr->field\xcf\x80"
4774 " (bar\xf0\x9f\x98\x82"
4776 pp_formatted_text (dc
.printer
));
4779 /* Example of deletion fix-it hints that would overlap. */
4781 test_diagnostic_context dc
;
4782 rich_location
richloc (line_table
, expr
);
4783 richloc
.add_fixit_insert_before (open_paren
, "(long\xf0\x9f\x98\x82 *)");
4784 source_range victim
= {expr_start
, expr_finish
};
4785 richloc
.add_fixit_remove (victim
);
4787 /* These fixits are not consolidated. */
4788 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4790 /* But the corrections are. */
4791 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4792 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4793 " *f = (f\xf0\x9f\x98\x82"
4794 " *)ptr->field\xcf\x80"
4797 " ------------------\n"
4798 " (long\xf0\x9f\x98\x82"
4799 " *)(f\xf0\x9f\x98\x82"
4801 pp_formatted_text (dc
.printer
));
4804 /* Example of insertion fix-it hints that would overlap. */
4806 test_diagnostic_context dc
;
4807 rich_location
richloc (line_table
, expr
);
4808 richloc
.add_fixit_insert_before
4809 (open_paren
, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4810 richloc
.add_fixit_insert_after (close_paren
, "TEST");
4812 /* The first insertion is long enough that if printed naively,
4813 it would overlap with the second.
4814 Verify that they are printed as a single replacement. */
4815 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4816 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4817 " *f = (f\xf0\x9f\x98\x82"
4818 " *)ptr->field\xcf\x80"
4822 " L\xf0\x9f\x98\x82"
4823 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4825 pp_formatted_text (dc
.printer
));
4829 /* Verify that the line_corrections machinery correctly prints
4830 overlapping fixit-hints that have been added in the wrong
4832 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4835 test_overlapped_fixit_printing_2 (const line_table_case
&case_
)
4837 /* Create a tempfile and write some text to it.
4838 ...000000000111111111122222222223333333333.
4839 ...123456789012345678901234567890123456789. */
4841 = ("int a5[][0][0] = { 1, 2 };\n");
4842 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4843 line_table_test
ltt (case_
);
4845 const line_map_ordinary
*ord_map
4846 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4847 tmp
.get_filename (), 0));
4849 linemap_line_start (line_table
, 1, 100);
4851 const location_t final_line_end
4852 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 100);
4854 /* Don't attempt to run the tests if column data might be unavailable. */
4855 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4858 const location_t col_1
4859 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
4860 const location_t col_20
4861 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 20);
4862 const location_t col_21
4863 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 21);
4864 const location_t col_23
4865 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
4866 const location_t col_25
4867 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 25);
4869 /* Two insertions, in the wrong order. */
4871 test_diagnostic_context dc
;
4873 rich_location
richloc (line_table
, col_20
);
4874 richloc
.add_fixit_insert_before (col_23
, "{");
4875 richloc
.add_fixit_insert_before (col_21
, "}");
4877 /* These fixits should be accepted; they can't be consolidated. */
4878 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4879 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4880 ASSERT_EQ (column_range (23, 22),
4881 get_affected_range (&dc
, hint_0
, CU_BYTES
));
4882 ASSERT_EQ (column_range (23, 23), get_printed_columns (&dc
, hint_0
));
4883 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4884 ASSERT_EQ (column_range (21, 20),
4885 get_affected_range (&dc
, hint_1
, CU_BYTES
));
4886 ASSERT_EQ (column_range (21, 21), get_printed_columns (&dc
, hint_1
));
4888 /* Verify that they're printed correctly. */
4889 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4890 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4893 pp_formatted_text (dc
.printer
));
4896 /* Various overlapping insertions, some occurring "out of order"
4897 (reproducing the fix-it hints from PR c/81405). */
4899 test_diagnostic_context dc
;
4900 rich_location
richloc (line_table
, col_20
);
4902 richloc
.add_fixit_insert_before (col_20
, "{{");
4903 richloc
.add_fixit_insert_before (col_21
, "}}");
4904 richloc
.add_fixit_insert_before (col_23
, "{");
4905 richloc
.add_fixit_insert_before (col_21
, "}");
4906 richloc
.add_fixit_insert_before (col_23
, "{{");
4907 richloc
.add_fixit_insert_before (col_25
, "}");
4908 richloc
.add_fixit_insert_before (col_21
, "}");
4909 richloc
.add_fixit_insert_before (col_1
, "{");
4910 richloc
.add_fixit_insert_before (col_25
, "}");
4911 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4912 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4915 " {{1}}}}, {{{2 }}\n",
4916 pp_formatted_text (dc
.printer
));
4920 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4923 test_fixit_insert_containing_newline (const line_table_case
&case_
)
4925 /* Create a tempfile and write some text to it.
4926 .........................0000000001111111.
4927 .........................1234567890123456. */
4928 const char *old_content
= (" case 'a':\n" /* line 1. */
4929 " x = a;\n" /* line 2. */
4930 " case 'b':\n" /* line 3. */
4931 " x = b;\n");/* line 4. */
4933 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
4934 line_table_test
ltt (case_
);
4935 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
4937 location_t case_start
= linemap_position_for_column (line_table
, 5);
4938 location_t case_finish
= linemap_position_for_column (line_table
, 13);
4939 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
4940 location_t line_start
= linemap_position_for_column (line_table
, 1);
4942 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4945 /* Add a "break;" on a line by itself before line 3 i.e. before
4946 column 1 of line 3. */
4948 rich_location
richloc (line_table
, case_loc
);
4949 richloc
.add_fixit_insert_before (line_start
, " break;\n");
4951 /* Without line numbers. */
4953 test_diagnostic_context dc
;
4954 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4955 ASSERT_STREQ (" x = a;\n"
4959 pp_formatted_text (dc
.printer
));
4962 /* With line numbers. */
4964 test_diagnostic_context dc
;
4965 dc
.show_line_numbers_p
= true;
4966 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4967 ASSERT_STREQ (" 2 | x = a;\n"
4971 pp_formatted_text (dc
.printer
));
4975 /* Verify that attempts to add text with a newline fail when the
4976 insertion point is *not* at the start of a line. */
4978 rich_location
richloc (line_table
, case_loc
);
4979 richloc
.add_fixit_insert_before (case_start
, "break;\n");
4980 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
4981 test_diagnostic_context dc
;
4982 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4983 ASSERT_STREQ (" case 'b':\n"
4985 pp_formatted_text (dc
.printer
));
4989 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4990 of the file, where the fix-it is printed in a different line-span
4991 to the primary range of the diagnostic. */
4994 test_fixit_insert_containing_newline_2 (const line_table_case
&case_
)
4996 /* Create a tempfile and write some text to it.
4997 .........................0000000001111111.
4998 .........................1234567890123456. */
4999 const char *old_content
= ("test (int ch)\n" /* line 1. */
5001 " putchar (ch);\n" /* line 3. */
5002 "}\n"); /* line 4. */
5004 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5005 line_table_test
ltt (case_
);
5007 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5008 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5009 linemap_line_start (line_table
, 1, 100);
5011 /* The primary range is the "putchar" token. */
5012 location_t putchar_start
5013 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 2);
5014 location_t putchar_finish
5015 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 8);
5016 location_t putchar_loc
5017 = make_location (putchar_start
, putchar_start
, putchar_finish
);
5018 rich_location
richloc (line_table
, putchar_loc
);
5020 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5021 location_t file_start
5022 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5023 richloc
.add_fixit_insert_before (file_start
, "#include <stdio.h>\n");
5025 if (putchar_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5029 test_diagnostic_context dc
;
5030 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5031 ASSERT_STREQ ("FILENAME:1:1:\n"
5032 "+#include <stdio.h>\n"
5037 pp_formatted_text (dc
.printer
));
5040 /* With line-numbering, the line spans are close enough to be
5041 consolidated, since it makes little sense to skip line 2. */
5043 test_diagnostic_context dc
;
5044 dc
.show_line_numbers_p
= true;
5045 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5046 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5047 " 1 | test (int ch)\n"
5049 " 3 | putchar (ch);\n"
5051 pp_formatted_text (dc
.printer
));
5055 /* Replacement fix-it hint containing a newline.
5056 This will fail, as newlines are only supported when inserting at the
5057 beginning of a line. */
5060 test_fixit_replace_containing_newline (const line_table_case
&case_
)
5062 /* Create a tempfile and write some text to it.
5063 .........................0000000001111.
5064 .........................1234567890123. */
5065 const char *old_content
= "foo = bar ();\n";
5067 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5068 line_table_test
ltt (case_
);
5069 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5071 /* Replace the " = " with "\n = ", as if we were reformatting an
5072 overly long line. */
5073 location_t start
= linemap_position_for_column (line_table
, 4);
5074 location_t finish
= linemap_position_for_column (line_table
, 6);
5075 location_t loc
= linemap_position_for_column (line_table
, 13);
5076 rich_location
richloc (line_table
, loc
);
5077 source_range range
= source_range::from_locations (start
, finish
);
5078 richloc
.add_fixit_replace (range
, "\n =");
5080 /* Arbitrary newlines are not yet supported within fix-it hints, so
5081 the fix-it should not be displayed. */
5082 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5084 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5087 test_diagnostic_context dc
;
5088 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5089 ASSERT_STREQ (" foo = bar ();\n"
5091 pp_formatted_text (dc
.printer
));
5094 /* Fix-it hint, attempting to delete a newline.
5095 This will fail, as we currently only support fix-it hints that
5096 affect one line at a time. */
5099 test_fixit_deletion_affecting_newline (const line_table_case
&case_
)
5101 /* Create a tempfile and write some text to it.
5102 ..........................0000000001111.
5103 ..........................1234567890123. */
5104 const char *old_content
= ("foo = bar (\n"
5107 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5108 line_table_test
ltt (case_
);
5109 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5110 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5111 linemap_line_start (line_table
, 1, 100);
5113 /* Attempt to delete the " (\n...)". */
5115 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 10);
5117 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
5119 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 7);
5120 location_t loc
= make_location (caret
, start
, finish
);
5121 rich_location
richloc (line_table
, loc
);
5122 richloc
. add_fixit_remove ();
5124 /* Fix-it hints that affect more than one line are not yet supported, so
5125 the fix-it should not be displayed. */
5126 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5128 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5131 test_diagnostic_context dc
;
5132 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5133 ASSERT_STREQ (" foo = bar (\n"
5137 pp_formatted_text (dc
.printer
));
5141 test_tab_expansion (const line_table_case
&case_
)
5143 /* Create a tempfile and write some text to it. This example uses a tabstop
5144 of 8, as the column numbers attempt to indicate:
5146 .....................000.01111111111.22222333333 display
5147 .....................123.90123456789.56789012345 columns */
5148 const char *content
= " \t This: `\t' is a tab.\n";
5149 /* ....................000 00000011111 11111222222 byte
5150 ....................123 45678901234 56789012345 columns */
5152 const int tabstop
= 8;
5153 const int first_non_ws_byte_col
= 7;
5154 const int right_quote_byte_col
= 15;
5155 const int last_byte_col
= 25;
5156 ASSERT_EQ (35, cpp_display_width (content
, last_byte_col
, tabstop
));
5158 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5159 line_table_test
ltt (case_
);
5160 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5162 /* Don't attempt to run the tests if column data might be unavailable. */
5163 location_t line_end
= linemap_position_for_column (line_table
, last_byte_col
);
5164 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5167 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5168 into 11 spaces. Recall that print_line() also puts one space before
5171 test_diagnostic_context dc
;
5172 dc
.tabstop
= tabstop
;
5173 rich_location
richloc (line_table
,
5174 linemap_position_for_column (line_table
,
5175 first_non_ws_byte_col
));
5176 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5177 test_layout
.print_line (1);
5178 ASSERT_STREQ (" This: ` ' is a tab.\n"
5180 pp_formatted_text (dc
.printer
));
5183 /* Confirm the display width was tracked correctly across the internal tab
5186 test_diagnostic_context dc
;
5187 dc
.tabstop
= tabstop
;
5188 rich_location
richloc (line_table
,
5189 linemap_position_for_column (line_table
,
5190 right_quote_byte_col
));
5191 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5192 test_layout
.print_line (1);
5193 ASSERT_STREQ (" This: ` ' is a tab.\n"
5195 pp_formatted_text (dc
.printer
));
5199 /* Verify that line numbers are correctly printed for the case of
5200 a multiline range in which the width of the line numbers changes
5201 (e.g. from "9" to "10"). */
5204 test_line_numbers_multiline_range ()
5206 /* Create a tempfile and write some text to it. */
5208 for (int i
= 0; i
< 20; i
++)
5209 /* .........0000000001111111.
5210 .............1234567890123456. */
5211 pp_printf (&pp
, "this is line %i\n", i
+ 1);
5212 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", pp_formatted_text (&pp
));
5213 line_table_test ltt
;
5215 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5216 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5217 linemap_line_start (line_table
, 1, 100);
5219 /* Create a multi-line location, starting at the "line" of line 9, with
5220 a caret on the "is" of line 10, finishing on the "this" line 11. */
5223 = linemap_position_for_line_and_column (line_table
, ord_map
, 9, 9);
5225 = linemap_position_for_line_and_column (line_table
, ord_map
, 10, 6);
5227 = linemap_position_for_line_and_column (line_table
, ord_map
, 11, 4);
5228 location_t loc
= make_location (caret
, start
, finish
);
5230 test_diagnostic_context dc
;
5231 dc
.show_line_numbers_p
= true;
5232 dc
.min_margin_width
= 0;
5233 gcc_rich_location
richloc (loc
);
5234 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5235 ASSERT_STREQ (" 9 | this is line 9\n"
5237 "10 | this is line 10\n"
5238 " | ~~~~~^~~~~~~~~~\n"
5239 "11 | this is line 11\n"
5241 pp_formatted_text (dc
.printer
));
5244 /* Run all of the selftests within this file. */
5247 diagnostic_show_locus_c_tests ()
5251 test_layout_range_for_single_point ();
5252 test_layout_range_for_single_line ();
5253 test_layout_range_for_multiple_lines ();
5255 for_each_line_table_case (test_layout_x_offset_display_utf8
);
5256 for_each_line_table_case (test_layout_x_offset_display_tab
);
5258 test_get_line_bytes_without_trailing_whitespace ();
5260 test_diagnostic_show_locus_unknown_location ();
5262 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
5263 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8
);
5264 for_each_line_table_case (test_add_location_if_nearby
);
5265 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines
);
5266 for_each_line_table_case (test_fixit_consolidation
);
5267 for_each_line_table_case (test_overlapped_fixit_printing
);
5268 for_each_line_table_case (test_overlapped_fixit_printing_utf8
);
5269 for_each_line_table_case (test_overlapped_fixit_printing_2
);
5270 for_each_line_table_case (test_fixit_insert_containing_newline
);
5271 for_each_line_table_case (test_fixit_insert_containing_newline_2
);
5272 for_each_line_table_case (test_fixit_replace_containing_newline
);
5273 for_each_line_table_case (test_fixit_deletion_affecting_newline
);
5274 for_each_line_table_case (test_tab_expansion
);
5276 test_line_numbers_multiline_range ();
5279 } // namespace selftest
5281 #endif /* #if CHECKING_P */
5284 # pragma GCC diagnostic pop