1 /* Diagnostic subroutines for printing source-code
2 Copyright (C) 1999-2023 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
,
179 const cpp_char_column_policy
&policy
,
180 enum location_aspect aspect
)
181 : expanded_location (exploc
),
182 m_display_col (location_compute_display_column (exploc
, policy
))
184 if (exploc
.column
> 0)
186 /* m_display_col is now the final column of the byte.
187 If escaping has happened, we may want the first column instead. */
188 if (aspect
!= LOCATION_ASPECT_FINISH
)
190 expanded_location
prev_exploc (exploc
);
191 prev_exploc
.column
--;
193 = (location_compute_display_column (prev_exploc
, policy
));
194 m_display_col
= prev_display_col
+ 1;
203 /* A point within a layout_range; similar to an exploc_with_display_col,
204 but after filtering on file. */
209 layout_point (const exploc_with_display_col
&exploc
)
210 : m_line (exploc
.line
)
212 m_columns
[CU_BYTES
] = exploc
.column
;
213 m_columns
[CU_DISPLAY_COLS
] = exploc
.m_display_col
;
217 int m_columns
[CU_NUM_UNITS
];
220 /* A class for use by "class layout" below: a filtered location_range. */
225 layout_range (const exploc_with_display_col
&start_exploc
,
226 const exploc_with_display_col
&finish_exploc
,
227 enum range_display_kind range_display_kind
,
228 const exploc_with_display_col
&caret_exploc
,
229 unsigned original_idx
,
230 const range_label
*label
);
232 bool contains_point (linenum_type row
, int column
,
233 enum column_unit col_unit
) const;
234 bool intersects_line_p (linenum_type row
) const;
236 layout_point m_start
;
237 layout_point m_finish
;
238 enum range_display_kind m_range_display_kind
;
239 layout_point m_caret
;
240 unsigned m_original_idx
;
241 const range_label
*m_label
;
244 /* A struct for use by layout::print_source_line for telling
245 layout::print_annotation_line the extents of the source line that
246 it printed, so that underlines can be clipped appropriately. Units
247 are 1-based display columns. */
251 int m_first_non_ws_disp_col
;
252 int m_last_non_ws_disp_col
;
256 m_first_non_ws_disp_col
= INT_MAX
;
257 m_last_non_ws_disp_col
= 0;
261 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
262 or "line 23"). During the layout ctor, layout::calculate_line_spans
263 splits the pertinent source lines into a list of disjoint line_span
264 instances (e.g. lines 5-10, lines 15-20, line 23). */
269 line_span (linenum_type first_line
, linenum_type last_line
)
270 : m_first_line (first_line
), m_last_line (last_line
)
272 gcc_assert (first_line
<= last_line
);
274 linenum_type
get_first_line () const { return m_first_line
; }
275 linenum_type
get_last_line () const { return m_last_line
; }
277 bool contains_line_p (linenum_type line
) const
279 return line
>= m_first_line
&& line
<= m_last_line
;
282 static int comparator (const void *p1
, const void *p2
)
284 const line_span
*ls1
= (const line_span
*)p1
;
285 const line_span
*ls2
= (const line_span
*)p2
;
286 int first_line_cmp
= compare (ls1
->m_first_line
, ls2
->m_first_line
);
288 return first_line_cmp
;
289 return compare (ls1
->m_last_line
, ls2
->m_last_line
);
292 linenum_type m_first_line
;
293 linenum_type m_last_line
;
298 /* Selftests for line_span. */
303 line_span
line_one (1, 1);
304 ASSERT_EQ (1, line_one
.get_first_line ());
305 ASSERT_EQ (1, line_one
.get_last_line ());
306 ASSERT_FALSE (line_one
.contains_line_p (0));
307 ASSERT_TRUE (line_one
.contains_line_p (1));
308 ASSERT_FALSE (line_one
.contains_line_p (2));
310 line_span
lines_1_to_3 (1, 3);
311 ASSERT_EQ (1, lines_1_to_3
.get_first_line ());
312 ASSERT_EQ (3, lines_1_to_3
.get_last_line ());
313 ASSERT_TRUE (lines_1_to_3
.contains_line_p (1));
314 ASSERT_TRUE (lines_1_to_3
.contains_line_p (3));
316 ASSERT_EQ (0, line_span::comparator (&line_one
, &line_one
));
317 ASSERT_GT (line_span::comparator (&lines_1_to_3
, &line_one
), 0);
318 ASSERT_LT (line_span::comparator (&line_one
, &lines_1_to_3
), 0);
320 /* A linenum > 2^31. */
321 const linenum_type LARGEST_LINE
= 0xffffffff;
322 line_span
largest_line (LARGEST_LINE
, LARGEST_LINE
);
323 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_first_line ());
324 ASSERT_EQ (LARGEST_LINE
, largest_line
.get_last_line ());
326 ASSERT_GT (line_span::comparator (&largest_line
, &line_one
), 0);
327 ASSERT_LT (line_span::comparator (&line_one
, &largest_line
), 0);
330 #endif /* #if CHECKING_P */
332 /* A bundle of information containing how to print unicode
333 characters and bytes when quoting source code.
335 Provides a unified place to support escaping some subset
336 of characters to some format.
338 Extends char_column_policy; printing is split out to avoid
339 libcpp having to know about pretty_printer. */
341 struct char_display_policy
: public cpp_char_column_policy
344 char_display_policy (int tabstop
,
345 int (*width_cb
) (cppchar_t c
),
346 void (*print_cb
) (pretty_printer
*pp
,
347 const cpp_decoded_char
&cp
))
348 : cpp_char_column_policy (tabstop
, width_cb
),
349 m_print_cb (print_cb
)
353 void (*m_print_cb
) (pretty_printer
*pp
,
354 const cpp_decoded_char
&cp
);
357 /* A class to control the overall layout when printing a diagnostic.
359 The layout is determined within the constructor.
360 It is then printed by repeatedly calling the "print_source_line",
361 "print_annotation_line" and "print_any_fixits" methods.
363 We assume we have disjoint ranges. */
368 layout (diagnostic_context
*context
,
369 rich_location
*richloc
,
370 diagnostic_t diagnostic_kind
,
371 pretty_printer
*pp
= nullptr);
373 bool maybe_add_location_range (const location_range
*loc_range
,
374 unsigned original_idx
,
375 bool restrict_to_current_line_spans
);
377 int get_num_line_spans () const { return m_line_spans
.length (); }
378 const line_span
*get_line_span (int idx
) const { return &m_line_spans
[idx
]; }
380 int get_linenum_width () const { return m_linenum_width
; }
381 int get_x_offset_display () const { return m_x_offset_display
; }
383 void print_gap_in_line_numbering ();
384 bool print_heading_for_line_span_index_p (int line_span_idx
) const;
386 expanded_location
get_expanded_location (const line_span
*) const;
388 void print_line (linenum_type row
);
390 void on_bad_codepoint (const char *ptr
, cppchar_t ch
, size_t ch_sz
);
393 bool will_show_line_p (linenum_type row
) const;
394 void print_leading_fixits (linenum_type row
);
395 line_bounds
print_source_line (linenum_type row
, const char *line
,
397 bool should_print_annotation_line_p (linenum_type row
) const;
398 void start_annotation_line (char margin_char
= ' ') const;
399 void print_annotation_line (linenum_type row
, const line_bounds lbounds
);
400 void print_any_labels (linenum_type row
);
401 void print_trailing_fixits (linenum_type row
);
403 bool annotation_line_showed_range_p (linenum_type line
, int start_column
,
404 int finish_column
) const;
405 void show_ruler (int max_column
) const;
407 bool validate_fixit_hint_p (const fixit_hint
*hint
);
409 void calculate_line_spans ();
410 void calculate_linenum_width ();
411 void calculate_x_offset_display ();
413 void print_newline ();
416 get_state_at_point (/* Inputs. */
417 linenum_type row
, int column
,
418 int first_non_ws
, int last_non_ws
,
419 enum column_unit col_unit
,
421 point_state
*out_state
);
424 get_x_bound_for_row (linenum_type row
, int caret_column
,
428 move_to_column (int *column
, int dest_column
, bool add_left_margin
);
431 diagnostic_context
*m_context
;
432 pretty_printer
*m_pp
;
433 char_display_policy m_policy
;
434 location_t m_primary_loc
;
435 exploc_with_display_col m_exploc
;
436 colorizer m_colorizer
;
437 bool m_colorize_source_p
;
438 bool m_show_labels_p
;
439 bool m_show_line_numbers_p
;
440 bool m_diagnostic_path_p
;
441 auto_vec
<layout_range
> m_layout_ranges
;
442 auto_vec
<const fixit_hint
*> m_fixit_hints
;
443 auto_vec
<line_span
> m_line_spans
;
445 int m_x_offset_display
;
446 bool m_escape_on_output
;
449 /* Implementation of "class colorizer". */
451 /* The constructor for "colorizer". Lookup and store color codes for the
452 different kinds of things we might need to print. */
454 colorizer::colorizer (diagnostic_context
*context
,
455 diagnostic_t diagnostic_kind
) :
457 m_diagnostic_kind (diagnostic_kind
),
458 m_current_state (STATE_NORMAL_TEXT
)
460 m_range1
= get_color_by_name ("range1");
461 m_range2
= get_color_by_name ("range2");
462 m_fixit_insert
= get_color_by_name ("fixit-insert");
463 m_fixit_delete
= get_color_by_name ("fixit-delete");
464 m_stop_color
= colorize_stop (pp_show_color (context
->printer
));
467 /* The destructor for "colorize". If colorization is on, print a code to
470 colorizer::~colorizer ()
472 finish_state (m_current_state
);
475 /* Update state, printing color codes if necessary if there's a state
479 colorizer::set_state (int new_state
)
481 if (m_current_state
!= new_state
)
483 finish_state (m_current_state
);
484 m_current_state
= new_state
;
485 begin_state (new_state
);
489 /* Turn on any colorization for STATE. */
492 colorizer::begin_state (int state
)
496 case STATE_NORMAL_TEXT
:
499 case STATE_FIXIT_INSERT
:
500 pp_string (m_context
->printer
, m_fixit_insert
);
503 case STATE_FIXIT_DELETE
:
504 pp_string (m_context
->printer
, m_fixit_delete
);
508 /* Make range 0 be the same color as the "kind" text
509 (error vs warning vs note). */
512 colorize_start (pp_show_color (m_context
->printer
),
513 diagnostic_get_color_for_kind (m_diagnostic_kind
)));
517 pp_string (m_context
->printer
, m_range1
);
521 pp_string (m_context
->printer
, m_range2
);
525 /* For ranges beyond 2, alternate between color 1 and color 2. */
527 gcc_assert (state
> 2);
528 pp_string (m_context
->printer
,
529 state
% 2 ? m_range1
: m_range2
);
535 /* Turn off any colorization for STATE. */
538 colorizer::finish_state (int state
)
540 if (state
!= STATE_NORMAL_TEXT
)
541 pp_string (m_context
->printer
, m_stop_color
);
544 /* Get the color code for NAME (or the empty string if
545 colorization is disabled). */
548 colorizer::get_color_by_name (const char *name
)
550 return colorize_start (pp_show_color (m_context
->printer
), name
);
553 /* Implementation of class layout_range. */
555 /* The constructor for class layout_range.
556 Initialize various layout_point fields from expanded_location
557 equivalents; we've already filtered on file. */
559 layout_range::layout_range (const exploc_with_display_col
&start_exploc
,
560 const exploc_with_display_col
&finish_exploc
,
561 enum range_display_kind range_display_kind
,
562 const exploc_with_display_col
&caret_exploc
,
563 unsigned original_idx
,
564 const range_label
*label
)
565 : m_start (start_exploc
),
566 m_finish (finish_exploc
),
567 m_range_display_kind (range_display_kind
),
568 m_caret (caret_exploc
),
569 m_original_idx (original_idx
),
574 /* Is (column, row) within the given range?
575 We've already filtered on the file.
577 Ranges are closed (both limits are within the range).
579 Example A: a single-line range:
580 start: (col=22, line=2)
581 finish: (col=38, line=2)
583 |00000011111111112222222222333333333344444444444
584 |34567890123456789012345678901234567890123456789
585 --+-----------------------------------------------
586 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
587 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
588 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
590 Example B: a multiline range with
591 start: (col=14, line=3)
592 finish: (col=08, line=5)
594 |00000011111111112222222222333333333344444444444
595 |34567890123456789012345678901234567890123456789
596 --+-----------------------------------------------
597 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
598 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
599 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
600 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
601 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
602 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
603 --+-----------------------------------------------
606 - 'b' indicates a point *before* the range
607 - 'S' indicates the start of the range
608 - 'w' indicates a point within the range
609 - 'F' indicates the finish of the range (which is
611 - 'a' indicates a subsequent point *after* the range.
613 COL_UNIT controls whether we check the byte column or
614 the display column; one or the other is more convenient
615 depending on the context. */
618 layout_range::contains_point (linenum_type row
, int column
,
619 enum column_unit col_unit
) const
621 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
622 /* ...but the equivalent isn't true for the columns;
623 consider example B in the comment above. */
625 if (row
< m_start
.m_line
)
626 /* Points before the first line of the range are
627 outside it (corresponding to line 01 in example A
628 and lines 01 and 02 in example B above). */
631 if (row
== m_start
.m_line
)
632 /* On same line as start of range (corresponding
633 to line 02 in example A and line 03 in example B). */
635 if (column
< m_start
.m_columns
[col_unit
])
636 /* Points on the starting line of the range, but
637 before the column in which it begins. */
640 if (row
< m_finish
.m_line
)
641 /* This is a multiline range; the point
642 is within it (corresponds to line 03 in example B
643 from column 14 onwards) */
647 /* This is a single-line range. */
648 gcc_assert (row
== m_finish
.m_line
);
649 return column
<= m_finish
.m_columns
[col_unit
];
653 /* The point is in a line beyond that containing the
654 start of the range: lines 03 onwards in example A,
655 and lines 04 onwards in example B. */
656 gcc_assert (row
> m_start
.m_line
);
658 if (row
> m_finish
.m_line
)
659 /* The point is beyond the final line of the range
660 (lines 03 onwards in example A, and lines 06 onwards
664 if (row
< m_finish
.m_line
)
666 /* The point is in a line that's fully within a multiline
667 range (e.g. line 04 in example B). */
668 gcc_assert (m_start
.m_line
< m_finish
.m_line
);
672 gcc_assert (row
== m_finish
.m_line
);
674 return column
<= m_finish
.m_columns
[col_unit
];
677 /* Does this layout_range contain any part of line ROW? */
680 layout_range::intersects_line_p (linenum_type row
) const
682 gcc_assert (m_start
.m_line
<= m_finish
.m_line
);
683 if (row
< m_start
.m_line
)
685 if (row
> m_finish
.m_line
)
692 /* Default for when we don't care what the tab expansion is set to. */
693 static const int def_tabstop
= 8;
695 static cpp_char_column_policy
def_policy ()
697 return cpp_char_column_policy (def_tabstop
, cpp_wcwidth
);
700 /* Create some expanded locations for testing layout_range. The filename
701 member of the explocs is set to the empty string. This member will only be
702 inspected by the calls to location_compute_display_column() made from the
703 layout_point constructors. That function will check for an empty filename
704 argument and not attempt to open it, rather treating the non-existent data
705 as if the display width were the same as the byte count. Tests exercising a
706 real difference between byte count and display width are performed later,
707 e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
710 make_range (int start_line
, int start_col
, int end_line
, int end_col
)
712 const expanded_location start_exploc
713 = {"", start_line
, start_col
, NULL
, false};
714 const expanded_location finish_exploc
715 = {"", end_line
, end_col
, NULL
, false};
716 return layout_range (exploc_with_display_col (start_exploc
, def_policy (),
717 LOCATION_ASPECT_START
),
718 exploc_with_display_col (finish_exploc
, def_policy (),
719 LOCATION_ASPECT_FINISH
),
720 SHOW_RANGE_WITHOUT_CARET
,
721 exploc_with_display_col (start_exploc
, def_policy (),
722 LOCATION_ASPECT_CARET
),
726 /* Selftests for layout_range::contains_point and
727 layout_range::intersects_line_p. */
729 /* Selftest for layout_range, where the layout_range
730 is a range with start==end i.e. a single point. */
733 test_layout_range_for_single_point ()
735 layout_range point
= make_range (7, 10, 7, 10);
737 /* Tests for layout_range::contains_point. */
739 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
741 const enum column_unit col_unit
= (enum column_unit
) i
;
743 /* Before the line. */
744 ASSERT_FALSE (point
.contains_point (6, 1, col_unit
));
746 /* On the line, but before start. */
747 ASSERT_FALSE (point
.contains_point (7, 9, col_unit
));
750 ASSERT_TRUE (point
.contains_point (7, 10, col_unit
));
752 /* On the line, after the point. */
753 ASSERT_FALSE (point
.contains_point (7, 11, col_unit
));
755 /* After the line. */
756 ASSERT_FALSE (point
.contains_point (8, 1, col_unit
));
759 /* Tests for layout_range::intersects_line_p. */
760 ASSERT_FALSE (point
.intersects_line_p (6));
761 ASSERT_TRUE (point
.intersects_line_p (7));
762 ASSERT_FALSE (point
.intersects_line_p (8));
765 /* Selftest for layout_range, where the layout_range
766 is the single-line range shown as "Example A" above. */
769 test_layout_range_for_single_line ()
771 layout_range example_a
= make_range (2, 22, 2, 38);
773 /* Tests for layout_range::contains_point. */
775 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
777 const enum column_unit col_unit
= (enum column_unit
) i
;
779 /* Before the line. */
780 ASSERT_FALSE (example_a
.contains_point (1, 1, col_unit
));
782 /* On the line, but before start. */
783 ASSERT_FALSE (example_a
.contains_point (2, 21, col_unit
));
785 /* On the line, at the start. */
786 ASSERT_TRUE (example_a
.contains_point (2, 22, col_unit
));
788 /* On the line, within the range. */
789 ASSERT_TRUE (example_a
.contains_point (2, 23, col_unit
));
791 /* On the line, at the end. */
792 ASSERT_TRUE (example_a
.contains_point (2, 38, col_unit
));
794 /* On the line, after the end. */
795 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
797 /* After the line. */
798 ASSERT_FALSE (example_a
.contains_point (2, 39, col_unit
));
801 /* Tests for layout_range::intersects_line_p. */
802 ASSERT_FALSE (example_a
.intersects_line_p (1));
803 ASSERT_TRUE (example_a
.intersects_line_p (2));
804 ASSERT_FALSE (example_a
.intersects_line_p (3));
807 /* Selftest for layout_range, where the layout_range
808 is the multi-line range shown as "Example B" above. */
811 test_layout_range_for_multiple_lines ()
813 layout_range example_b
= make_range (3, 14, 5, 8);
815 /* Tests for layout_range::contains_point. */
817 for (int i
= 0; i
!= CU_NUM_UNITS
; ++i
)
819 const enum column_unit col_unit
= (enum column_unit
) i
;
821 /* Before first line. */
822 ASSERT_FALSE (example_b
.contains_point (1, 1, col_unit
));
824 /* On the first line, but before start. */
825 ASSERT_FALSE (example_b
.contains_point (3, 13, col_unit
));
828 ASSERT_TRUE (example_b
.contains_point (3, 14, col_unit
));
830 /* On the first line, within the range. */
831 ASSERT_TRUE (example_b
.contains_point (3, 15, col_unit
));
833 /* On an interior line.
834 The column number should not matter; try various boundary
836 ASSERT_TRUE (example_b
.contains_point (4, 1, col_unit
));
837 ASSERT_TRUE (example_b
.contains_point (4, 7, col_unit
));
838 ASSERT_TRUE (example_b
.contains_point (4, 8, col_unit
));
839 ASSERT_TRUE (example_b
.contains_point (4, 9, col_unit
));
840 ASSERT_TRUE (example_b
.contains_point (4, 13, col_unit
));
841 ASSERT_TRUE (example_b
.contains_point (4, 14, col_unit
));
842 ASSERT_TRUE (example_b
.contains_point (4, 15, col_unit
));
844 /* On the final line, before the end. */
845 ASSERT_TRUE (example_b
.contains_point (5, 7, col_unit
));
847 /* On the final line, at the end. */
848 ASSERT_TRUE (example_b
.contains_point (5, 8, col_unit
));
850 /* On the final line, after the end. */
851 ASSERT_FALSE (example_b
.contains_point (5, 9, col_unit
));
853 /* After the line. */
854 ASSERT_FALSE (example_b
.contains_point (6, 1, col_unit
));
857 /* Tests for layout_range::intersects_line_p. */
858 ASSERT_FALSE (example_b
.intersects_line_p (2));
859 ASSERT_TRUE (example_b
.intersects_line_p (3));
860 ASSERT_TRUE (example_b
.intersects_line_p (4));
861 ASSERT_TRUE (example_b
.intersects_line_p (5));
862 ASSERT_FALSE (example_b
.intersects_line_p (6));
865 #endif /* #if CHECKING_P */
867 /* Given a source line LINE of length LINE_BYTES bytes, determine the length
868 (still in bytes, not display cols) without any trailing whitespace. */
871 get_line_bytes_without_trailing_whitespace (const char *line
, int line_bytes
)
873 int result
= line_bytes
;
876 char ch
= line
[result
- 1];
877 if (ch
== ' ' || ch
== '\t' || ch
== '\r')
882 gcc_assert (result
>= 0);
883 gcc_assert (result
<= line_bytes
);
884 gcc_assert (result
== 0 ||
885 (line
[result
- 1] != ' '
886 && line
[result
-1] != '\t'
887 && line
[result
-1] != '\r'));
893 /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
896 assert_eq (const char *line
, int expected_bytes
)
899 = get_line_bytes_without_trailing_whitespace (line
, strlen (line
));
900 ASSERT_EQ (actual_value
, expected_bytes
);
903 /* Verify that get_line_bytes_without_trailing_whitespace is sane for
904 various inputs. It is not required to handle newlines. */
907 test_get_line_bytes_without_trailing_whitespace ()
913 assert_eq ("hello world", 11);
914 assert_eq ("hello world ", 11);
915 assert_eq ("hello world \t\t ", 11);
916 assert_eq ("hello world\r", 11);
919 #endif /* #if CHECKING_P */
921 /* Helper function for layout's ctor, for sanitizing locations relative
922 to the primary location within a diagnostic.
924 Compare LOC_A and LOC_B to see if it makes sense to print underlines
925 connecting their expanded locations. Doing so is only guaranteed to
926 make sense if the locations share the same macro expansion "history"
927 i.e. they can be traced through the same macro expansions, eventually
928 reaching an ordinary map.
930 This may be too strong a condition, but it effectively sanitizes
931 PR c++/70105, which has an example of printing an expression where the
932 final location of the expression is in a different macro, which
933 erroneously was leading to hundreds of lines of irrelevant source
937 compatible_locations_p (location_t loc_a
, location_t loc_b
)
939 if (IS_ADHOC_LOC (loc_a
))
940 loc_a
= get_location_from_adhoc_loc (line_table
, loc_a
);
941 if (IS_ADHOC_LOC (loc_b
))
942 loc_b
= get_location_from_adhoc_loc (line_table
, loc_b
);
944 /* If either location is one of the special locations outside of a
945 linemap, they are only compatible if they are equal. */
946 if (loc_a
< RESERVED_LOCATION_COUNT
947 || loc_b
< RESERVED_LOCATION_COUNT
)
948 return loc_a
== loc_b
;
950 const line_map
*map_a
= linemap_lookup (line_table
, loc_a
);
951 linemap_assert (map_a
);
953 const line_map
*map_b
= linemap_lookup (line_table
, loc_b
);
954 linemap_assert (map_b
);
956 /* Are they within the same map? */
959 /* Are both within the same macro expansion? */
960 if (linemap_macro_expansion_map_p (map_a
))
962 /* If so, then they're only compatible if either both are
963 from the macro definition, or both from the macro arguments. */
965 = linemap_location_from_macro_definition_p (line_table
, loc_a
);
967 = linemap_location_from_macro_definition_p (line_table
, loc_b
);
968 if (loc_a_from_defn
!= loc_b_from_defn
)
971 /* Expand each location towards the spelling location, and
973 const line_map_macro
*macro_map
= linemap_check_macro (map_a
);
974 location_t loc_a_toward_spelling
975 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
978 location_t loc_b_toward_spelling
979 = linemap_macro_map_loc_unwind_toward_spelling (line_table
,
982 return compatible_locations_p (loc_a_toward_spelling
,
983 loc_b_toward_spelling
);
986 /* Otherwise they are within the same ordinary map. */
991 /* Within different maps. */
993 /* If either is within a macro expansion, they are incompatible. */
994 if (linemap_macro_expansion_map_p (map_a
)
995 || linemap_macro_expansion_map_p (map_b
))
998 /* Within two different ordinary maps; they are compatible iff they
999 are in the same file. */
1000 const line_map_ordinary
*ord_map_a
= linemap_check_ordinary (map_a
);
1001 const line_map_ordinary
*ord_map_b
= linemap_check_ordinary (map_b
);
1002 return ord_map_a
->to_file
== ord_map_b
->to_file
;
1006 /* Comparator for sorting fix-it hints. */
1009 fixit_cmp (const void *p_a
, const void *p_b
)
1011 const fixit_hint
* hint_a
= *static_cast<const fixit_hint
* const *> (p_a
);
1012 const fixit_hint
* hint_b
= *static_cast<const fixit_hint
* const *> (p_b
);
1013 return hint_a
->get_start_loc () - hint_b
->get_start_loc ();
1016 /* Callbacks for use when not escaping the source. */
1018 /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1020 /* Callback for char_display_policy::m_print_cb for printing source chars
1021 when not escaping the source. */
1024 default_print_decoded_ch (pretty_printer
*pp
,
1025 const cpp_decoded_char
&decoded_ch
)
1027 for (const char *ptr
= decoded_ch
.m_start_byte
;
1028 ptr
!= decoded_ch
.m_next_byte
; ptr
++)
1030 if (*ptr
== '\0' || *ptr
== '\r')
1036 pp_character (pp
, *ptr
);
1040 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1042 static const int width_per_escaped_byte
= 4;
1044 /* Callback for char_column_policy::m_width_cb for determining the
1045 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1048 escape_as_bytes_width (cppchar_t ch
)
1050 if (ch
< 0x80 && ISPRINT (ch
))
1051 return cpp_wcwidth (ch
);
1054 if (ch
<= 0x7F) return 1 * width_per_escaped_byte
;
1055 if (ch
<= 0x7FF) return 2 * width_per_escaped_byte
;
1056 if (ch
<= 0xFFFF) return 3 * width_per_escaped_byte
;
1057 return 4 * width_per_escaped_byte
;
1061 /* Callback for char_display_policy::m_print_cb for printing source chars
1062 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1065 escape_as_bytes_print (pretty_printer
*pp
,
1066 const cpp_decoded_char
&decoded_ch
)
1068 if (!decoded_ch
.m_valid_ch
)
1070 for (const char *iter
= decoded_ch
.m_start_byte
;
1071 iter
!= decoded_ch
.m_next_byte
; ++iter
)
1074 sprintf (buf
, "<%02x>", (unsigned char)*iter
);
1075 pp_string (pp
, buf
);
1080 cppchar_t ch
= decoded_ch
.m_ch
;
1081 if (ch
< 0x80 && ISPRINT (ch
))
1082 pp_character (pp
, ch
);
1085 for (const char *iter
= decoded_ch
.m_start_byte
;
1086 iter
< decoded_ch
.m_next_byte
; ++iter
)
1089 sprintf (buf
, "<%02x>", (unsigned char)*iter
);
1090 pp_string (pp
, buf
);
1095 /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1097 /* Callback for char_column_policy::m_width_cb for determining the
1098 display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1101 escape_as_unicode_width (cppchar_t ch
)
1103 if (ch
< 0x80 && ISPRINT (ch
))
1104 return cpp_wcwidth (ch
);
1107 // Width of "<U+%04x>"
1110 else if (ch
> 0xffff)
1117 /* Callback for char_display_policy::m_print_cb for printing source chars
1118 when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1121 escape_as_unicode_print (pretty_printer
*pp
,
1122 const cpp_decoded_char
&decoded_ch
)
1124 if (!decoded_ch
.m_valid_ch
)
1126 escape_as_bytes_print (pp
, decoded_ch
);
1130 cppchar_t ch
= decoded_ch
.m_ch
;
1131 if (ch
< 0x80 && ISPRINT (ch
))
1132 pp_character (pp
, ch
);
1136 sprintf (buf
, "<U+%04X>", ch
);
1137 pp_string (pp
, buf
);
1141 /* Populate a char_display_policy based on DC and RICHLOC. */
1143 static char_display_policy
1144 make_policy (const diagnostic_context
&dc
,
1145 const rich_location
&richloc
)
1147 /* The default is to not escape non-ASCII bytes. */
1148 char_display_policy result
1149 (dc
.tabstop
, cpp_wcwidth
, default_print_decoded_ch
);
1151 /* If the diagnostic suggests escaping non-ASCII bytes, then
1152 use policy from user-supplied options. */
1153 if (richloc
.escape_on_output_p ())
1155 result
.m_undecoded_byte_width
= width_per_escaped_byte
;
1156 switch (dc
.escape_format
)
1160 case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
:
1161 result
.m_width_cb
= escape_as_unicode_width
;
1162 result
.m_print_cb
= escape_as_unicode_print
;
1164 case DIAGNOSTICS_ESCAPE_FORMAT_BYTES
:
1165 result
.m_width_cb
= escape_as_bytes_width
;
1166 result
.m_print_cb
= escape_as_bytes_print
;
1174 /* Implementation of class layout. */
1176 /* Constructor for class layout.
1178 Filter the ranges from the rich_location to those that we can
1179 sanely print, populating m_layout_ranges and m_fixit_hints.
1180 Determine the range of lines that we will print, splitting them
1181 up into an ordered list of disjoint spans of contiguous line numbers.
1182 Determine m_x_offset_display, to ensure that the primary caret
1183 will fit within the max_width provided by the diagnostic_context. */
1185 layout::layout (diagnostic_context
* context
,
1186 rich_location
*richloc
,
1187 diagnostic_t diagnostic_kind
,
1189 : m_context (context
),
1190 m_pp (pp
? pp
: context
->printer
),
1191 m_policy (make_policy (*context
, *richloc
)),
1192 m_primary_loc (richloc
->get_range (0)->m_loc
),
1193 m_exploc (richloc
->get_expanded_location (0), m_policy
,
1194 LOCATION_ASPECT_CARET
),
1195 m_colorizer (context
, diagnostic_kind
),
1196 m_colorize_source_p (context
->m_source_printing
.colorize_source_p
),
1197 m_show_labels_p (context
->m_source_printing
.show_labels_p
),
1198 m_show_line_numbers_p (context
->m_source_printing
.show_line_numbers_p
),
1199 m_diagnostic_path_p (diagnostic_kind
== DK_DIAGNOSTIC_PATH
),
1200 m_layout_ranges (richloc
->get_num_locations ()),
1201 m_fixit_hints (richloc
->get_num_fixit_hints ()),
1202 m_line_spans (1 + richloc
->get_num_locations ()),
1203 m_linenum_width (0),
1204 m_x_offset_display (0),
1205 m_escape_on_output (richloc
->escape_on_output_p ())
1207 for (unsigned int idx
= 0; idx
< richloc
->get_num_locations (); idx
++)
1209 /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1210 Ignore any ranges that are awkward to handle. */
1211 const location_range
*loc_range
= richloc
->get_range (idx
);
1212 maybe_add_location_range (loc_range
, idx
, false);
1215 /* Populate m_fixit_hints, filtering to only those that are in the
1217 for (unsigned int i
= 0; i
< richloc
->get_num_fixit_hints (); i
++)
1219 const fixit_hint
*hint
= richloc
->get_fixit_hint (i
);
1220 if (validate_fixit_hint_p (hint
))
1221 m_fixit_hints
.safe_push (hint
);
1224 /* Sort m_fixit_hints. */
1225 m_fixit_hints
.qsort (fixit_cmp
);
1227 /* Populate the indicated members. */
1228 calculate_line_spans ();
1229 calculate_linenum_width ();
1230 calculate_x_offset_display ();
1232 if (context
->m_source_printing
.show_ruler_p
)
1233 show_ruler (m_x_offset_display
+ m_context
->m_source_printing
.max_width
);
1237 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1238 those that we can sanely print.
1240 ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1241 (for use as extrinsic state by label ranges FIXME).
1243 If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1244 filtered against this layout instance's current line spans: it
1245 will only be added if the location is fully within the lines
1246 already specified by other locations.
1248 Return true iff LOC_RANGE was added. */
1251 layout::maybe_add_location_range (const location_range
*loc_range
,
1252 unsigned original_idx
,
1253 bool restrict_to_current_line_spans
)
1255 gcc_assert (loc_range
);
1257 /* Split the "range" into caret and range information. */
1258 source_range src_range
= get_range_from_loc (line_table
, loc_range
->m_loc
);
1260 /* Expand the various locations. */
1261 expanded_location start
1262 = linemap_client_expand_location_to_spelling_point
1263 (src_range
.m_start
, LOCATION_ASPECT_START
);
1264 expanded_location finish
1265 = linemap_client_expand_location_to_spelling_point
1266 (src_range
.m_finish
, LOCATION_ASPECT_FINISH
);
1267 expanded_location caret
1268 = linemap_client_expand_location_to_spelling_point
1269 (loc_range
->m_loc
, LOCATION_ASPECT_CARET
);
1271 /* If any part of the range isn't in the same file as the primary
1272 location of this diagnostic, ignore the range. */
1273 if (start
.file
!= m_exploc
.file
)
1275 if (finish
.file
!= m_exploc
.file
)
1277 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1278 if (caret
.file
!= m_exploc
.file
)
1281 /* Sanitize the caret location for non-primary ranges. */
1282 if (m_layout_ranges
.length () > 0)
1283 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1284 if (!compatible_locations_p (loc_range
->m_loc
, m_primary_loc
))
1285 /* Discard any non-primary ranges that can't be printed
1286 sanely relative to the primary location. */
1289 /* Everything is now known to be in the correct source file,
1290 but it may require further sanitization. */
1291 layout_range
ri (exploc_with_display_col (start
, m_policy
,
1292 LOCATION_ASPECT_START
),
1293 exploc_with_display_col (finish
, m_policy
,
1294 LOCATION_ASPECT_FINISH
),
1295 loc_range
->m_range_display_kind
,
1296 exploc_with_display_col (caret
, m_policy
,
1297 LOCATION_ASPECT_CARET
),
1298 original_idx
, loc_range
->m_label
);
1300 /* If we have a range that finishes before it starts (perhaps
1301 from something built via macro expansion), printing the
1302 range is likely to be nonsensical. Also, attempting to do so
1303 breaks assumptions within the printing code (PR c/68473).
1304 Similarly, don't attempt to print ranges if one or both ends
1305 of the range aren't sane to print relative to the
1306 primary location (PR c++/70105). */
1307 if (start
.line
> finish
.line
1308 || !compatible_locations_p (src_range
.m_start
, m_primary_loc
)
1309 || !compatible_locations_p (src_range
.m_finish
, m_primary_loc
))
1311 /* Is this the primary location? */
1312 if (m_layout_ranges
.length () == 0)
1314 /* We want to print the caret for the primary location, but
1315 we must sanitize away m_start and m_finish. */
1316 ri
.m_start
= ri
.m_caret
;
1317 ri
.m_finish
= ri
.m_caret
;
1320 /* This is a non-primary range; ignore it. */
1324 /* Potentially filter to just the lines already specified by other
1325 locations. This is for use by gcc_rich_location::add_location_if_nearby.
1326 The layout ctor doesn't use it, and can't because m_line_spans
1327 hasn't been set up at that point. */
1328 if (restrict_to_current_line_spans
)
1330 if (!will_show_line_p (start
.line
))
1332 if (!will_show_line_p (finish
.line
))
1334 if (loc_range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
)
1335 if (!will_show_line_p (caret
.line
))
1339 /* Passed all the tests; add the range to m_layout_ranges so that
1340 it will be printed. */
1341 m_layout_ranges
.safe_push (ri
);
1345 /* Return true iff ROW is within one of the line spans for this layout. */
1348 layout::will_show_line_p (linenum_type row
) const
1350 for (int line_span_idx
= 0; line_span_idx
< get_num_line_spans ();
1353 const line_span
*line_span
= get_line_span (line_span_idx
);
1354 if (line_span
->contains_line_p (row
))
1360 /* Print a line showing a gap in the line numbers, for showing the boundary
1361 between two line spans. */
1364 layout::print_gap_in_line_numbering ()
1366 gcc_assert (m_show_line_numbers_p
);
1368 pp_emit_prefix (m_pp
);
1370 for (int i
= 0; i
< m_linenum_width
+ 1; i
++)
1371 pp_character (m_pp
, '.');
1376 /* Return true iff we should print a heading when starting the
1377 line span with the given index. */
1380 layout::print_heading_for_line_span_index_p (int line_span_idx
) const
1382 /* We print a heading for every change of line span, hence for every
1383 line span after the initial one. */
1384 if (line_span_idx
> 0)
1387 /* We also do it for the initial span if the primary location of the
1388 diagnostic is in a different span. */
1389 if (m_exploc
.line
> (int)get_line_span (0)->m_last_line
)
1395 /* Get an expanded_location for the first location of interest within
1396 the given line_span.
1397 Used when printing a heading to indicate a new line span. */
1400 layout::get_expanded_location (const line_span
*line_span
) const
1402 /* Whenever possible, use the caret location. */
1403 if (line_span
->contains_line_p (m_exploc
.line
))
1406 /* Otherwise, use the start of the first range that's present
1407 within the line_span. */
1408 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1410 const layout_range
*lr
= &m_layout_ranges
[i
];
1411 if (line_span
->contains_line_p (lr
->m_start
.m_line
))
1413 expanded_location exploc
= m_exploc
;
1414 exploc
.line
= lr
->m_start
.m_line
;
1415 exploc
.column
= lr
->m_start
.m_columns
[CU_BYTES
];
1420 /* Otherwise, use the location of the first fixit-hint present within
1422 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1424 const fixit_hint
*hint
= m_fixit_hints
[i
];
1425 location_t loc
= hint
->get_start_loc ();
1426 expanded_location exploc
= expand_location (loc
);
1427 if (line_span
->contains_line_p (exploc
.line
))
1431 /* It should not be possible to have a line span that didn't
1432 contain any of the layout_range or fixit_hint instances. */
1437 /* Determine if HINT is meaningful to print within this layout. */
1440 layout::validate_fixit_hint_p (const fixit_hint
*hint
)
1442 if (LOCATION_FILE (hint
->get_start_loc ()) != m_exploc
.file
)
1444 if (LOCATION_FILE (hint
->get_next_loc ()) != m_exploc
.file
)
1450 /* Determine the range of lines affected by HINT.
1451 This assumes that HINT has already been filtered by
1452 validate_fixit_hint_p, and so affects the correct source file. */
1455 get_line_span_for_fixit_hint (const fixit_hint
*hint
)
1459 int start_line
= LOCATION_LINE (hint
->get_start_loc ());
1461 /* For line-insertion fix-it hints, add the previous line to the
1462 span, to give the user more context on the proposed change. */
1463 if (hint
->ends_with_newline_p ())
1467 return line_span (start_line
,
1468 LOCATION_LINE (hint
->get_next_loc ()));
1471 /* We want to print the pertinent source code at a diagnostic. The
1472 rich_location can contain multiple locations. This will have been
1473 filtered into m_exploc (the caret for the primary location) and
1474 m_layout_ranges, for those ranges within the same source file.
1476 We will print a subset of the lines within the source file in question,
1477 as a collection of "spans" of lines.
1479 This function populates m_line_spans with an ordered, disjoint list of
1480 the line spans of interest.
1482 Printing a gap between line spans takes one line, so, when printing
1483 line numbers, we allow a gap of up to one line between spans when
1484 merging, since it makes more sense to print the source line rather than a
1485 "gap-in-line-numbering" line. When not printing line numbers, it's
1486 better to be more explicit about what's going on, so keeping them as
1487 separate spans is preferred.
1489 For example, if the primary range is on lines 8-10, with secondary ranges
1490 covering lines 5-6 and lines 13-15:
1506 With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1508 With line numbering off (with span headers), we want three spans: lines 5-6,
1509 lines 8-10, and lines 13-15. */
1512 layout::calculate_line_spans ()
1514 /* This should only be called once, by the ctor. */
1515 gcc_assert (m_line_spans
.length () == 0);
1517 /* Populate tmp_spans with individual spans, for each of
1518 m_exploc, and for m_layout_ranges. */
1519 auto_vec
<line_span
> tmp_spans (1 + m_layout_ranges
.length ());
1520 tmp_spans
.safe_push (line_span (m_exploc
.line
, m_exploc
.line
));
1521 for (unsigned int i
= 0; i
< m_layout_ranges
.length (); i
++)
1523 const layout_range
*lr
= &m_layout_ranges
[i
];
1524 gcc_assert (lr
->m_start
.m_line
<= lr
->m_finish
.m_line
);
1525 tmp_spans
.safe_push (line_span (lr
->m_start
.m_line
,
1526 lr
->m_finish
.m_line
));
1529 /* Also add spans for any fix-it hints, in case they cover other lines. */
1530 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
1532 const fixit_hint
*hint
= m_fixit_hints
[i
];
1534 tmp_spans
.safe_push (get_line_span_for_fixit_hint (hint
));
1538 tmp_spans
.qsort(line_span::comparator
);
1540 /* Now iterate through tmp_spans, copying into m_line_spans, and
1541 combining where possible. */
1542 gcc_assert (tmp_spans
.length () > 0);
1543 m_line_spans
.safe_push (tmp_spans
[0]);
1544 for (unsigned int i
= 1; i
< tmp_spans
.length (); i
++)
1546 line_span
*current
= &m_line_spans
[m_line_spans
.length () - 1];
1547 const line_span
*next
= &tmp_spans
[i
];
1548 gcc_assert (next
->m_first_line
>= current
->m_first_line
);
1549 const int merger_distance
= m_show_line_numbers_p
? 1 : 0;
1550 if ((linenum_arith_t
)next
->m_first_line
1551 <= (linenum_arith_t
)current
->m_last_line
+ 1 + merger_distance
)
1553 /* We can merge them. */
1554 if (next
->m_last_line
> current
->m_last_line
)
1555 current
->m_last_line
= next
->m_last_line
;
1559 /* No merger possible. */
1560 m_line_spans
.safe_push (*next
);
1564 /* Verify the result, in m_line_spans. */
1565 gcc_assert (m_line_spans
.length () > 0);
1566 for (unsigned int i
= 1; i
< m_line_spans
.length (); i
++)
1568 const line_span
*prev
= &m_line_spans
[i
- 1];
1569 const line_span
*next
= &m_line_spans
[i
];
1570 /* The individual spans must be sane. */
1571 gcc_assert (prev
->m_first_line
<= prev
->m_last_line
);
1572 gcc_assert (next
->m_first_line
<= next
->m_last_line
);
1573 /* The spans must be ordered. */
1574 gcc_assert (prev
->m_first_line
< next
->m_first_line
);
1575 /* There must be a gap of at least one line between separate spans. */
1576 gcc_assert ((prev
->m_last_line
+ 1) < next
->m_first_line
);
1580 /* Determine how many display columns need to be reserved for line numbers,
1581 based on the largest line number that will be needed, and populate
1585 layout::calculate_linenum_width ()
1587 gcc_assert (m_line_spans
.length () > 0);
1588 const line_span
*last_span
= &m_line_spans
[m_line_spans
.length () - 1];
1589 int highest_line
= last_span
->m_last_line
;
1590 if (highest_line
< 0)
1592 m_linenum_width
= num_digits (highest_line
);
1593 /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1594 if (m_line_spans
.length () > 1)
1595 m_linenum_width
= MAX (m_linenum_width
, 3);
1596 /* If there's a minimum margin width, apply it (subtracting 1 for the space
1597 after the line number. */
1598 m_linenum_width
= MAX (m_linenum_width
,
1599 m_context
->m_source_printing
.min_margin_width
- 1);
1602 /* Calculate m_x_offset_display, which improves readability in case the source
1603 line of interest is longer than the user's display. All lines output will be
1604 shifted to the left (so that their beginning is no longer displayed) by
1605 m_x_offset_display display columns, so that the caret is in a reasonable
1609 layout::calculate_x_offset_display ()
1611 m_x_offset_display
= 0;
1613 const int max_width
= m_context
->m_source_printing
.max_width
;
1616 /* Nothing to do, the width is not capped. */
1620 const char_span line
= location_get_source_line (m_exploc
.file
,
1624 /* Nothing to do, we couldn't find the source line. */
1627 int caret_display_column
= m_exploc
.m_display_col
;
1628 const int line_bytes
1629 = get_line_bytes_without_trailing_whitespace (line
.get_buffer (),
1631 int eol_display_column
1632 = cpp_display_width (line
.get_buffer (), line_bytes
, m_policy
);
1633 if (caret_display_column
> eol_display_column
1634 || !caret_display_column
)
1636 /* This does not make sense, so don't try to do anything in this case. */
1640 /* Adjust caret and eol positions to include the left margin. If we are
1641 outputting line numbers, then the left margin is equal to m_linenum_width
1642 plus three for the " | " which follows it. Otherwise the left margin width
1643 is equal to 1, because layout::print_source_line() will prefix each line
1645 const int source_display_cols
= eol_display_column
;
1646 int left_margin_size
= 1;
1647 if (m_show_line_numbers_p
)
1648 left_margin_size
= m_linenum_width
+ 3;
1649 caret_display_column
+= left_margin_size
;
1650 eol_display_column
+= left_margin_size
;
1652 if (eol_display_column
<= max_width
)
1654 /* Nothing to do, everything fits in the display. */
1658 /* The line is too long for the display. Calculate an offset such that the
1659 caret is not too close to the right edge of the screen. It will be
1660 CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1661 than that to the end of the source line anyway. */
1662 int right_margin_size
= CARET_LINE_MARGIN
;
1663 right_margin_size
= MIN (eol_display_column
- caret_display_column
,
1665 if (right_margin_size
+ left_margin_size
>= max_width
)
1667 /* The max_width is very small, so anything we try to do will not be very
1668 effective; just punt in this case and output with no offset. */
1671 const int max_caret_display_column
= max_width
- right_margin_size
;
1672 if (caret_display_column
> max_caret_display_column
)
1674 m_x_offset_display
= caret_display_column
- max_caret_display_column
;
1675 /* Make sure we don't offset the line into oblivion. */
1676 static const int min_cols_visible
= 2;
1677 if (source_display_cols
- m_x_offset_display
< min_cols_visible
)
1678 m_x_offset_display
= 0;
1682 /* Print line ROW of source code, potentially colorized at any ranges, and
1683 return the line bounds. LINE is the source line (not necessarily
1684 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1685 colorization and tab expansion, this function tracks the line position in
1686 both byte and display column units. */
1689 layout::print_source_line (linenum_type row
, const char *line
, int line_bytes
)
1691 m_colorizer
.set_normal_text ();
1693 pp_emit_prefix (m_pp
);
1694 if (m_show_line_numbers_p
)
1696 int width
= num_digits (row
);
1697 for (int i
= 0; i
< m_linenum_width
- width
; i
++)
1699 pp_printf (m_pp
, "%i | ", row
);
1704 /* We will stop printing the source line at any trailing whitespace. */
1705 line_bytes
= get_line_bytes_without_trailing_whitespace (line
,
1708 /* This object helps to keep track of which display column we are at, which is
1709 necessary for computing the line bounds in display units, for doing
1710 tab expansion, and for implementing m_x_offset_display. */
1711 cpp_display_width_computation
dw (line
, line_bytes
, m_policy
);
1713 /* Skip the first m_x_offset_display display columns. In case the leading
1714 portion that will be skipped ends with a character with wcwidth > 1, then
1715 it is possible we skipped too much, so account for that by padding with
1716 spaces. Note that this does the right thing too in case a tab was the last
1717 character to be skipped over; the tab is effectively replaced by the
1718 correct number of trailing spaces needed to offset by the desired number of
1720 for (int skipped_display_cols
= dw
.advance_display_cols (m_x_offset_display
);
1721 skipped_display_cols
> m_x_offset_display
; --skipped_display_cols
)
1724 /* Print the line and compute the line_bounds. */
1725 line_bounds lbounds
;
1728 /* Assuming colorization is enabled for the caret and underline
1729 characters, we may also colorize the associated characters
1730 within the source line.
1732 For frontends that generate range information, we color the
1733 associated characters in the source line the same as the
1734 carets and underlines in the annotation line, to make it easier
1735 for the reader to see the pertinent code.
1737 For frontends that only generate carets, we don't colorize the
1738 characters above them, since this would look strange (e.g.
1739 colorizing just the first character in a token). */
1740 if (m_colorize_source_p
)
1744 const int start_byte_col
= dw
.bytes_processed () + 1;
1745 in_range_p
= get_state_at_point (row
, start_byte_col
,
1750 m_colorizer
.set_range (state
.range_idx
);
1752 m_colorizer
.set_normal_text ();
1755 /* Get the display width of the next character to be output, expanding
1756 tabs and replacing some control bytes with spaces as necessary. */
1757 const char *c
= dw
.next_byte ();
1758 const int start_disp_col
= dw
.display_cols_processed () + 1;
1759 cpp_decoded_char cp
;
1760 const int this_display_width
= dw
.process_next_codepoint (&cp
);
1763 /* The returned display width is the number of spaces into which the
1764 tab should be expanded. */
1765 for (int i
= 0; i
!= this_display_width
; ++i
)
1770 /* We have a (possibly multibyte) character to output; update the line
1771 bounds if it is not whitespace. */
1774 lbounds
.m_last_non_ws_disp_col
= dw
.display_cols_processed ();
1775 if (lbounds
.m_first_non_ws_disp_col
== INT_MAX
)
1776 lbounds
.m_first_non_ws_disp_col
= start_disp_col
;
1779 /* Output the character. */
1780 m_policy
.m_print_cb (m_pp
, cp
);
1781 c
= dw
.next_byte ();
1787 /* Determine if we should print an annotation line for ROW.
1788 i.e. if any of m_layout_ranges contains ROW. */
1791 layout::should_print_annotation_line_p (linenum_type row
) const
1793 layout_range
*range
;
1795 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1797 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
1799 if (range
->intersects_line_p (row
))
1805 /* Begin an annotation line. If m_show_line_numbers_p, print the left
1806 margin, which is empty for annotation lines. Otherwise, do nothing. */
1809 layout::start_annotation_line (char margin_char
) const
1811 pp_emit_prefix (m_pp
);
1812 if (m_show_line_numbers_p
)
1814 /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
1815 of it, right-aligned, padded with spaces. */
1817 for (i
= 0; i
< m_linenum_width
- 3; i
++)
1819 for (; i
< m_linenum_width
; i
++)
1820 pp_character (m_pp
, margin_char
);
1821 pp_string (m_pp
, " |");
1825 /* Print a line consisting of the caret/underlines for the given
1829 layout::print_annotation_line (linenum_type row
, const line_bounds lbounds
)
1831 int x_bound
= get_x_bound_for_row (row
, m_exploc
.m_display_col
,
1832 lbounds
.m_last_non_ws_disp_col
);
1834 start_annotation_line ();
1837 for (int column
= 1 + m_x_offset_display
; column
< x_bound
; column
++)
1841 in_range_p
= get_state_at_point (row
, column
,
1842 lbounds
.m_first_non_ws_disp_col
,
1843 lbounds
.m_last_non_ws_disp_col
,
1848 /* Within a range. Draw either the caret or an underline. */
1849 m_colorizer
.set_range (state
.range_idx
);
1850 if (state
.draw_caret_p
)
1852 /* Draw the caret. */
1854 if (state
.range_idx
< rich_location::STATICALLY_ALLOCATED_RANGES
)
1856 = m_context
->m_source_printing
.caret_chars
[state
.range_idx
];
1859 pp_character (m_pp
, caret_char
);
1862 pp_character (m_pp
, '~');
1866 /* Not in a range. */
1867 m_colorizer
.set_normal_text ();
1868 pp_character (m_pp
, ' ');
1874 /* A version of label_text that can live inside a vec.
1875 Requires manual cleanup via maybe_free. */
1877 struct pod_label_text
1880 : m_buffer (NULL
), m_caller_owned (false)
1883 pod_label_text (label_text
&&other
)
1884 : m_buffer (const_cast<char*> (other
.get ())),
1885 m_caller_owned (other
.is_owner ())
1897 bool m_caller_owned
;
1900 /* Implementation detail of layout::print_any_labels.
1902 A label within the given row of source. */
1907 line_label (const cpp_char_column_policy
&policy
,
1908 int state_idx
, int column
,
1910 : m_state_idx (state_idx
), m_column (column
),
1911 m_text (std::move (text
)), m_label_line (0), m_has_vbar (true)
1913 const int bytes
= strlen (m_text
.m_buffer
);
1914 m_display_width
= cpp_display_width (m_text
.m_buffer
, bytes
, policy
);
1917 /* Sorting is primarily by column, then by state index. */
1918 static int comparator (const void *p1
, const void *p2
)
1920 const line_label
*ll1
= (const line_label
*)p1
;
1921 const line_label
*ll2
= (const line_label
*)p2
;
1922 int column_cmp
= compare (ll1
->m_column
, ll2
->m_column
);
1925 /* Order by reverse state index, so that labels are printed
1926 in order of insertion into the rich_location when the
1927 sorted list is walked backwards. */
1928 return -compare (ll1
->m_state_idx
, ll2
->m_state_idx
);
1933 pod_label_text m_text
;
1934 size_t m_display_width
;
1939 /* Print any labels in this row. */
1941 layout::print_any_labels (linenum_type row
)
1944 auto_vec
<line_label
> labels
;
1946 /* Gather the labels that are to be printed into "labels". */
1948 layout_range
*range
;
1949 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
1951 /* Most ranges don't have labels, so reject this first. */
1952 if (range
->m_label
== NULL
)
1955 /* The range's caret must be on this line. */
1956 if (range
->m_caret
.m_line
!= row
)
1959 /* Reject labels that aren't fully visible due to clipping
1960 by m_x_offset_display. */
1961 const int disp_col
= range
->m_caret
.m_columns
[CU_DISPLAY_COLS
];
1962 if (disp_col
<= m_x_offset_display
)
1966 text
= range
->m_label
->get_text (range
->m_original_idx
);
1968 /* Allow for labels that return NULL from their get_text
1969 implementation (so e.g. such labels can control their own
1971 if (text
.get () == NULL
)
1974 labels
.safe_push (line_label (m_policy
, i
, disp_col
, std::move (text
)));
1978 /* Bail out if there are no labels on this row. */
1979 if (labels
.length () == 0)
1983 labels
.qsort(line_label::comparator
);
1985 /* Figure out how many "label lines" we need, and which
1986 one each label is printed in.
1988 For example, if the labels aren't too densely packed,
1989 we can fit them on the same line, giving two "label lines":
1994 l0 l1 : label line 1
1996 If they would touch each other or overlap, then we need
1997 additional "label lines":
2002 | label 1 : label line 1
2003 label 0 : label line 2
2005 Place the final label on label line 1, and work backwards, adding
2006 label lines as needed.
2008 If multiple labels are at the same place, put them on separate
2014 label 0 : label line 2
2015 label 1 : label line 3. */
2017 int max_label_line
= 1;
2019 int next_column
= INT_MAX
;
2021 FOR_EACH_VEC_ELT_REVERSE (labels
, i
, label
)
2023 /* Would this label "touch" or overlap the next label? */
2024 if (label
->m_column
+ label
->m_display_width
>= (size_t)next_column
)
2028 /* If we've already seen labels with the same column, suppress the
2029 vertical bar for subsequent ones in this backwards iteration;
2030 hence only the one with the highest label_line has m_has_vbar set. */
2031 if (label
->m_column
== next_column
)
2032 label
->m_has_vbar
= false;
2035 label
->m_label_line
= max_label_line
;
2036 next_column
= label
->m_column
;
2040 /* Print the "label lines". For each label within the line, print
2041 either a vertical bar ('|') for the labels that are lower down, or the
2042 labels themselves once we've reached their line. */
2044 for (int label_line
= 0; label_line
<= max_label_line
; label_line
++)
2046 start_annotation_line ();
2048 int column
= 1 + m_x_offset_display
;
2050 FOR_EACH_VEC_ELT (labels
, i
, label
)
2052 if (label_line
> label
->m_label_line
)
2053 /* We've printed all the labels for this label line. */
2056 if (label_line
== label
->m_label_line
)
2058 gcc_assert (column
<= label
->m_column
);
2059 move_to_column (&column
, label
->m_column
, true);
2060 /* Colorize the text, unless it's for events in a
2062 if (!m_diagnostic_path_p
)
2063 m_colorizer
.set_range (label
->m_state_idx
);
2064 pp_string (m_pp
, label
->m_text
.m_buffer
);
2065 m_colorizer
.set_normal_text ();
2066 column
+= label
->m_display_width
;
2068 else if (label
->m_has_vbar
)
2070 gcc_assert (column
<= label
->m_column
);
2071 move_to_column (&column
, label
->m_column
, true);
2072 m_colorizer
.set_range (label
->m_state_idx
);
2073 pp_character (m_pp
, '|');
2074 m_colorizer
.set_normal_text ();
2085 FOR_EACH_VEC_ELT (labels
, i
, label
)
2086 label
->m_text
.maybe_free ();
2090 /* If there are any fixit hints inserting new lines before source line ROW,
2093 They are printed on lines of their own, before the source line
2094 itself, with a leading '+'. */
2097 layout::print_leading_fixits (linenum_type row
)
2099 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
2101 const fixit_hint
*hint
= m_fixit_hints
[i
];
2103 if (!hint
->ends_with_newline_p ())
2104 /* Not a newline fixit; print it in print_trailing_fixits. */
2107 gcc_assert (hint
->insertion_p ());
2109 if (hint
->affects_line_p (m_exploc
.file
, row
))
2111 /* Printing the '+' with normal colorization
2112 and the inserted line with "insert" colorization
2113 helps them stand out from each other, and from
2114 the surrounding text. */
2115 m_colorizer
.set_normal_text ();
2116 start_annotation_line ('+');
2117 pp_character (m_pp
, '+');
2118 m_colorizer
.set_fixit_insert ();
2119 /* Print all but the trailing newline of the fix-it hint.
2120 We have to print the newline separately to avoid
2121 getting additional pp prefixes printed. */
2122 for (size_t i
= 0; i
< hint
->get_length () - 1; i
++)
2123 pp_character (m_pp
, hint
->get_string ()[i
]);
2124 m_colorizer
.set_normal_text ();
2130 /* Subroutine of layout::print_trailing_fixits.
2132 Determine if the annotation line printed for LINE contained
2133 the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2136 layout::annotation_line_showed_range_p (linenum_type line
, int start_column
,
2137 int finish_column
) const
2139 layout_range
*range
;
2141 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2142 if (range
->m_start
.m_line
== line
2143 && range
->m_start
.m_columns
[CU_DISPLAY_COLS
] == start_column
2144 && range
->m_finish
.m_line
== line
2145 && range
->m_finish
.m_columns
[CU_DISPLAY_COLS
] == finish_column
)
2150 /* Classes for printing trailing fix-it hints i.e. those that
2151 don't add new lines.
2153 For insertion, these can look like:
2157 For replacement, these can look like:
2159 ------------- : underline showing affected range
2162 For deletion, these can look like:
2164 ------------- : underline showing affected range
2166 This can become confusing if they overlap, and so we need
2167 to do some preprocessing to decide what to print.
2168 We use the list of fixit_hint instances affecting the line
2169 to build a list of "correction" instances, and print the
2172 For example, consider a set of fix-its for converting
2173 a C-style cast to a C++ const_cast.
2177 ..000000000111111111122222222223333333333.
2178 ..123456789012345678901234567890123456789.
2179 foo *f = (foo *)ptr->field;
2182 and the fix-it hints:
2183 - replace col 10 (the open paren) with "const_cast<"
2184 - replace col 16 (the close paren) with "> ("
2185 - insert ")" before col 27
2187 then we would get odd-looking output:
2189 foo *f = (foo *)ptr->field;
2196 It would be better to detect when fixit hints are going to
2197 overlap (those that require new lines), and to consolidate
2198 the printing of such fixits, giving something like:
2200 foo *f = (foo *)ptr->field;
2203 const_cast<foo *> (ptr->field)
2205 This works by detecting when the printing would overlap, and
2206 effectively injecting no-op replace hints into the gaps between
2207 such fix-its, so that the printing joins up.
2209 In the above example, the overlap of:
2210 - replace col 10 (the open paren) with "const_cast<"
2212 - replace col 16 (the close paren) with "> ("
2213 is fixed by injecting a no-op:
2214 - replace cols 11-15 with themselves ("foo *")
2215 and consolidating these, making:
2216 - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2218 - replace cols 10-16 with "const_cast<foo *> ("
2220 This overlaps with the final fix-it hint:
2221 - insert ")" before col 27
2222 and so we repeat the consolidation process, by injecting
2224 - replace cols 17-26 with themselves ("ptr->field")
2226 - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2228 - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2230 and is thus printed as desired. */
2232 /* A range of (byte or display) columns within a line. */
2237 column_range (int start_
, int finish_
) : start (start_
), finish (finish_
)
2239 gcc_assert (valid_p (start
, finish
));
2242 bool operator== (const column_range
&other
) const
2244 return start
== other
.start
&& finish
== other
.finish
;
2247 static bool valid_p (int start
, int finish
)
2249 /* We must have either a range, or an insertion. */
2250 return (start
<= finish
|| finish
== start
- 1);
2257 /* Get the range of bytes or display columns that HINT would affect. */
2259 get_affected_range (const cpp_char_column_policy
&policy
,
2260 const fixit_hint
*hint
, enum column_unit col_unit
)
2262 expanded_location exploc_start
= expand_location (hint
->get_start_loc ());
2263 expanded_location exploc_finish
= expand_location (hint
->get_next_loc ());
2264 --exploc_finish
.column
;
2268 if (col_unit
== CU_DISPLAY_COLS
)
2270 start_column
= location_compute_display_column (exploc_start
, policy
);
2271 if (hint
->insertion_p ())
2272 finish_column
= start_column
- 1;
2274 finish_column
= location_compute_display_column (exploc_finish
, policy
);
2278 start_column
= exploc_start
.column
;
2279 finish_column
= exploc_finish
.column
;
2281 return column_range (start_column
, finish_column
);
2284 /* Get the range of display columns that would be printed for HINT. */
2287 get_printed_columns (const cpp_char_column_policy
&policy
,
2288 const fixit_hint
*hint
)
2290 expanded_location exploc
= expand_location (hint
->get_start_loc ());
2291 int start_column
= location_compute_display_column (exploc
, policy
);
2292 int hint_width
= cpp_display_width (hint
->get_string (), hint
->get_length (),
2294 int final_hint_column
= start_column
+ hint_width
- 1;
2295 if (hint
->insertion_p ())
2297 return column_range (start_column
, final_hint_column
);
2301 exploc
= expand_location (hint
->get_next_loc ());
2303 int finish_column
= location_compute_display_column (exploc
, policy
);
2304 return column_range (start_column
,
2305 MAX (finish_column
, final_hint_column
));
2309 /* A correction on a particular line.
2310 This describes a plan for how to print one or more fixit_hint
2311 instances that affected the line, potentially consolidating hints
2312 into corrections to make the result easier for the user to read. */
2317 correction (column_range affected_bytes
,
2318 column_range affected_columns
,
2319 column_range printed_columns
,
2320 const char *new_text
, size_t new_text_len
,
2321 const cpp_char_column_policy
&policy
)
2322 : m_affected_bytes (affected_bytes
),
2323 m_affected_columns (affected_columns
),
2324 m_printed_columns (printed_columns
),
2325 m_text (xstrdup (new_text
)),
2326 m_byte_length (new_text_len
),
2328 m_alloc_sz (new_text_len
+ 1)
2330 compute_display_cols ();
2333 ~correction () { free (m_text
); }
2335 bool insertion_p () const
2337 return m_affected_bytes
.start
== m_affected_bytes
.finish
+ 1;
2340 void ensure_capacity (size_t len
);
2341 void ensure_terminated ();
2343 void compute_display_cols ()
2345 m_display_cols
= cpp_display_width (m_text
, m_byte_length
, m_policy
);
2348 void overwrite (int dst_offset
, const char_span
&src_span
)
2350 gcc_assert (dst_offset
>= 0);
2351 gcc_assert (dst_offset
+ src_span
.length () < m_alloc_sz
);
2352 memcpy (m_text
+ dst_offset
, src_span
.get_buffer (),
2353 src_span
.length ());
2356 /* If insert, then start: the column before which the text
2357 is to be inserted, and finish is offset by the length of
2359 If replace, then the range of columns affected. */
2360 column_range m_affected_bytes
;
2361 column_range m_affected_columns
;
2363 /* If insert, then start: the column before which the text
2364 is to be inserted, and finish is offset by the length of
2366 If replace, then the range of columns affected. */
2367 column_range m_printed_columns
;
2369 /* The text to be inserted/used as replacement. */
2371 size_t m_byte_length
; /* Not including null-terminator. */
2373 const cpp_char_column_policy
&m_policy
;
2377 /* Ensure that m_text can hold a string of length LEN
2378 (plus 1 for 0-termination). */
2381 correction::ensure_capacity (size_t len
)
2383 /* Allow 1 extra byte for 0-termination. */
2384 if (m_alloc_sz
< (len
+ 1))
2386 size_t new_alloc_sz
= (len
+ 1) * 2;
2387 m_text
= (char *)xrealloc (m_text
, new_alloc_sz
);
2388 m_alloc_sz
= new_alloc_sz
;
2392 /* Ensure that m_text is 0-terminated. */
2395 correction::ensure_terminated ()
2397 /* 0-terminate the buffer. */
2398 gcc_assert (m_byte_length
< m_alloc_sz
);
2399 m_text
[m_byte_length
] = '\0';
2402 /* A list of corrections affecting a particular line.
2403 This is used by layout::print_trailing_fixits for planning
2404 how to print the fix-it hints affecting the line. */
2406 class line_corrections
2409 line_corrections (const char_display_policy
&policy
,
2410 const char *filename
,
2412 : m_policy (policy
), m_filename (filename
), m_row (row
)
2414 ~line_corrections ();
2416 void add_hint (const fixit_hint
*hint
);
2418 const char_display_policy
&m_policy
;
2419 const char *m_filename
;
2421 auto_vec
<correction
*> m_corrections
;
2424 /* struct line_corrections. */
2426 line_corrections::~line_corrections ()
2430 FOR_EACH_VEC_ELT (m_corrections
, i
, c
)
2434 /* A struct wrapping a particular source line, allowing
2435 run-time bounds-checking of accesses in a checked build. */
2440 source_line (const char *filename
, int line
);
2442 char_span
as_span () { return char_span (chars
, width
); }
2448 /* source_line's ctor. */
2450 source_line::source_line (const char *filename
, int line
)
2452 char_span span
= location_get_source_line (filename
, line
);
2453 chars
= span
.get_buffer ();
2454 width
= span
.length ();
2457 /* Add HINT to the corrections for this line.
2458 Attempt to consolidate nearby hints so that they will not
2459 overlap with printed. */
2462 line_corrections::add_hint (const fixit_hint
*hint
)
2464 column_range affected_bytes
= get_affected_range (m_policy
, hint
, CU_BYTES
);
2465 column_range affected_columns
= get_affected_range (m_policy
, hint
,
2467 column_range printed_columns
= get_printed_columns (m_policy
, hint
);
2469 /* Potentially consolidate. */
2470 if (!m_corrections
.is_empty ())
2472 correction
*last_correction
2473 = m_corrections
[m_corrections
.length () - 1];
2475 /* The following consolidation code assumes that the fix-it hints
2476 have been sorted by start (done within layout's ctor). */
2477 gcc_assert (affected_bytes
.start
2478 >= last_correction
->m_affected_bytes
.start
);
2479 gcc_assert (printed_columns
.start
2480 >= last_correction
->m_printed_columns
.start
);
2482 if (printed_columns
.start
<= last_correction
->m_printed_columns
.finish
2483 && column_range::valid_p (last_correction
->m_affected_bytes
.finish
+ 1,
2484 affected_bytes
.start
- 1))
2486 /* We have two hints for which the printed forms of the hints
2487 would touch or overlap, so we need to consolidate them to avoid
2489 Attempt to inject a "replace" correction from immediately
2490 after the end of the last hint to immediately before the start
2491 of the next hint. */
2492 column_range
between (last_correction
->m_affected_bytes
.finish
+ 1,
2493 affected_bytes
.start
- 1);
2495 /* Try to read the source. */
2496 source_line
line (m_filename
, m_row
);
2497 if (line
.chars
&& between
.finish
< line
.width
)
2499 /* Consolidate into the last correction:
2500 add a no-op "replace" of the "between" text, and
2501 add the text from the new hint. */
2502 int old_byte_len
= last_correction
->m_byte_length
;
2503 gcc_assert (old_byte_len
>= 0);
2504 int between_byte_len
= between
.finish
+ 1 - between
.start
;
2505 gcc_assert (between_byte_len
>= 0);
2507 = old_byte_len
+ between_byte_len
+ hint
->get_length ();
2508 gcc_assert (new_byte_len
>= 0);
2509 last_correction
->ensure_capacity (new_byte_len
);
2510 last_correction
->overwrite
2512 line
.as_span ().subspan (between
.start
- 1,
2513 between
.finish
+ 1 - between
.start
));
2514 last_correction
->overwrite (old_byte_len
+ between_byte_len
,
2515 char_span (hint
->get_string (),
2516 hint
->get_length ()));
2517 last_correction
->m_byte_length
= new_byte_len
;
2518 last_correction
->ensure_terminated ();
2519 last_correction
->m_affected_bytes
.finish
2520 = affected_bytes
.finish
;
2521 last_correction
->m_affected_columns
.finish
2522 = affected_columns
.finish
;
2523 int prev_display_cols
= last_correction
->m_display_cols
;
2524 last_correction
->compute_display_cols ();
2525 last_correction
->m_printed_columns
.finish
2526 += last_correction
->m_display_cols
- prev_display_cols
;
2532 /* If no consolidation happened, add a new correction instance. */
2533 m_corrections
.safe_push (new correction (affected_bytes
,
2536 hint
->get_string (),
2537 hint
->get_length (),
2541 /* If there are any fixit hints on source line ROW, print them.
2542 They are printed in order, attempting to combine them onto lines, but
2543 starting new lines if necessary.
2544 Fix-it hints that insert new lines are handled separately,
2545 in layout::print_leading_fixits. */
2548 layout::print_trailing_fixits (linenum_type row
)
2550 /* Build a list of correction instances for the line,
2551 potentially consolidating hints (for the sake of readability). */
2552 line_corrections
corrections (m_policy
, m_exploc
.file
, row
);
2553 for (unsigned int i
= 0; i
< m_fixit_hints
.length (); i
++)
2555 const fixit_hint
*hint
= m_fixit_hints
[i
];
2557 /* Newline fixits are handled by layout::print_leading_fixits. */
2558 if (hint
->ends_with_newline_p ())
2561 if (hint
->affects_line_p (m_exploc
.file
, row
))
2562 corrections
.add_hint (hint
);
2565 /* Now print the corrections. */
2568 int column
= m_x_offset_display
;
2570 if (!corrections
.m_corrections
.is_empty ())
2571 start_annotation_line ();
2573 FOR_EACH_VEC_ELT (corrections
.m_corrections
, i
, c
)
2575 /* For now we assume each fixit hint can only touch one line. */
2576 if (c
->insertion_p ())
2578 /* This assumes the insertion just affects one line. */
2579 int start_column
= c
->m_printed_columns
.start
;
2580 move_to_column (&column
, start_column
, true);
2581 m_colorizer
.set_fixit_insert ();
2582 pp_string (m_pp
, c
->m_text
);
2583 m_colorizer
.set_normal_text ();
2584 column
+= c
->m_display_cols
;
2588 /* If the range of the replacement wasn't printed in the
2589 annotation line, then print an extra underline to
2590 indicate exactly what is being replaced.
2591 Always show it for removals. */
2592 int start_column
= c
->m_affected_columns
.start
;
2593 int finish_column
= c
->m_affected_columns
.finish
;
2594 if (!annotation_line_showed_range_p (row
, start_column
,
2596 || c
->m_byte_length
== 0)
2598 move_to_column (&column
, start_column
, true);
2599 m_colorizer
.set_fixit_delete ();
2600 for (; column
<= finish_column
; column
++)
2601 pp_character (m_pp
, '-');
2602 m_colorizer
.set_normal_text ();
2604 /* Print the replacement text. REPLACE also covers
2605 removals, so only do this extra work (potentially starting
2606 a new line) if we have actual replacement text. */
2607 if (c
->m_byte_length
> 0)
2609 move_to_column (&column
, start_column
, true);
2610 m_colorizer
.set_fixit_insert ();
2611 pp_string (m_pp
, c
->m_text
);
2612 m_colorizer
.set_normal_text ();
2613 column
+= c
->m_display_cols
;
2618 /* Add a trailing newline, if necessary. */
2619 move_to_column (&column
, 0, false);
2622 /* Disable any colorization and emit a newline. */
2625 layout::print_newline ()
2627 m_colorizer
.set_normal_text ();
2631 /* Return true if (ROW/COLUMN) is within a range of the layout.
2632 If it returns true, OUT_STATE is written to, with the
2633 range index, and whether we should draw the caret at
2634 (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2635 whether all inputs and outputs are in bytes or display column units. */
2638 layout::get_state_at_point (/* Inputs. */
2639 linenum_type row
, int column
,
2640 int first_non_ws
, int last_non_ws
,
2641 enum column_unit col_unit
,
2643 point_state
*out_state
)
2645 layout_range
*range
;
2647 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2649 if (range
->m_range_display_kind
== SHOW_LINES_WITHOUT_RANGE
)
2650 /* Bail out early, so that such ranges don't affect underlining or
2651 source colorization. */
2654 if (range
->contains_point (row
, column
, col_unit
))
2656 out_state
->range_idx
= i
;
2658 /* Are we at the range's caret? is it visible? */
2659 out_state
->draw_caret_p
= false;
2660 if (range
->m_range_display_kind
== SHOW_RANGE_WITH_CARET
2661 && row
== range
->m_caret
.m_line
2662 && column
== range
->m_caret
.m_columns
[col_unit
])
2663 out_state
->draw_caret_p
= true;
2665 /* Within a multiline range, don't display any underline
2666 in any leading or trailing whitespace on a line.
2667 We do display carets, however. */
2668 if (!out_state
->draw_caret_p
)
2669 if (column
< first_non_ws
|| column
> last_non_ws
)
2672 /* We are within a range. */
2680 /* Helper function for use by layout::print_line when printing the
2681 annotation line under the source line.
2682 Get the display column beyond the rightmost one that could contain a caret
2683 or range marker, given that we stop rendering at trailing whitespace.
2684 ROW is the source line within the given file.
2685 CARET_COLUMN is the display column of range 0's caret.
2686 LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
2687 character of source (as determined when printing the source line). */
2690 layout::get_x_bound_for_row (linenum_type row
, int caret_column
,
2691 int last_non_ws_column
)
2693 int result
= caret_column
+ 1;
2695 layout_range
*range
;
2697 FOR_EACH_VEC_ELT (m_layout_ranges
, i
, range
)
2699 if (row
>= range
->m_start
.m_line
)
2701 if (range
->m_finish
.m_line
== row
)
2703 /* On the final line within a range; ensure that
2704 we render up to the end of the range. */
2705 const int disp_col
= range
->m_finish
.m_columns
[CU_DISPLAY_COLS
];
2706 if (result
<= disp_col
)
2707 result
= disp_col
+ 1;
2709 else if (row
< range
->m_finish
.m_line
)
2711 /* Within a multiline range; ensure that we render up to the
2712 last non-whitespace column. */
2713 if (result
<= last_non_ws_column
)
2714 result
= last_non_ws_column
+ 1;
2722 /* Given *COLUMN as an x-coordinate, print spaces to position
2723 successive output at DEST_COLUMN, printing a newline if necessary,
2724 and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
2725 left margin after any newline. */
2728 layout::move_to_column (int *column
, int dest_column
, bool add_left_margin
)
2730 /* Start a new line if we need to. */
2731 if (*column
> dest_column
)
2734 if (add_left_margin
)
2735 start_annotation_line ();
2736 *column
= m_x_offset_display
;
2739 while (*column
< dest_column
)
2746 /* For debugging layout issues, render a ruler giving column numbers
2747 (after the 1-column indent). */
2750 layout::show_ruler (int max_column
) const
2753 if (max_column
> 99)
2755 start_annotation_line ();
2757 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2758 if (column
% 10 == 0)
2759 pp_character (m_pp
, '0' + (column
/ 100) % 10);
2766 start_annotation_line ();
2768 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2769 if (column
% 10 == 0)
2770 pp_character (m_pp
, '0' + (column
/ 10) % 10);
2776 start_annotation_line ();
2778 for (int column
= 1 + m_x_offset_display
; column
<= max_column
; column
++)
2779 pp_character (m_pp
, '0' + (column
% 10));
2783 /* Print leading fix-its (for new lines inserted before the source line)
2784 then the source line, followed by an annotation line
2785 consisting of any caret/underlines, then any fixits.
2786 If the source line can't be read, print nothing. */
2788 layout::print_line (linenum_type row
)
2790 char_span line
= location_get_source_line (m_exploc
.file
, row
);
2794 print_leading_fixits (row
);
2795 const line_bounds lbounds
2796 = print_source_line (row
, line
.get_buffer (), line
.length ());
2797 if (should_print_annotation_line_p (row
))
2798 print_annotation_line (row
, lbounds
);
2799 if (m_show_labels_p
)
2800 print_any_labels (row
);
2801 print_trailing_fixits (row
);
2804 } /* End of anonymous namespace. */
2806 /* If LOC is within the spans of lines that will already be printed for
2807 this gcc_rich_location, then add it as a secondary location and return true.
2809 Otherwise return false. */
2812 gcc_rich_location::add_location_if_nearby (location_t loc
,
2813 bool restrict_to_current_line_spans
,
2814 const range_label
*label
)
2816 /* Use the layout location-handling logic to sanitize LOC,
2817 filtering it to the current line spans within a temporary
2819 layout
layout (global_dc
, this, DK_ERROR
);
2820 location_range loc_range
;
2821 loc_range
.m_loc
= loc
;
2822 loc_range
.m_range_display_kind
= SHOW_RANGE_WITHOUT_CARET
;
2823 if (!layout
.maybe_add_location_range (&loc_range
, 0,
2824 restrict_to_current_line_spans
))
2827 add_range (loc
, SHOW_RANGE_WITHOUT_CARET
, label
);
2831 /* Print the physical source code corresponding to the location of
2832 this diagnostic, with additional annotations.
2833 If PP is non-null, then use it rather than CONTEXT's printer. */
2836 diagnostic_show_locus (diagnostic_context
* context
,
2837 rich_location
*richloc
,
2838 diagnostic_t diagnostic_kind
,
2841 location_t loc
= richloc
->get_loc ();
2842 /* Do nothing if source-printing has been disabled. */
2843 if (!context
->m_source_printing
.enabled
)
2846 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2847 if (loc
<= BUILTINS_LOCATION
)
2850 /* Don't print the same source location twice in a row, unless we have
2851 fix-it hints, or multiple locations, or a label. */
2852 if (loc
== context
->last_location
2853 && richloc
->get_num_fixit_hints () == 0
2854 && richloc
->get_num_locations () == 1
2855 && richloc
->get_range (0)->m_label
== NULL
)
2858 context
->last_location
= loc
;
2860 layout
layout (context
, richloc
, diagnostic_kind
, pp
);
2861 for (int line_span_idx
= 0; line_span_idx
< layout
.get_num_line_spans ();
2864 const line_span
*line_span
= layout
.get_line_span (line_span_idx
);
2865 if (context
->m_source_printing
.show_line_numbers_p
)
2867 /* With line numbers, we should show whenever the line-numbering
2869 if (line_span_idx
> 0)
2870 layout
.print_gap_in_line_numbering ();
2874 /* Without line numbers, we print headings for some line spans. */
2875 if (layout
.print_heading_for_line_span_index_p (line_span_idx
))
2877 expanded_location exploc
2878 = layout
.get_expanded_location (line_span
);
2879 context
->m_text_callbacks
.start_span (context
, exploc
);
2882 /* Iterate over the lines within this span (using linenum_arith_t to
2883 avoid overflow with 0xffffffff causing an infinite loop). */
2884 linenum_arith_t last_line
= line_span
->get_last_line ();
2885 for (linenum_arith_t row
= line_span
->get_first_line ();
2886 row
<= last_line
; row
++)
2887 layout
.print_line (row
);
2893 namespace selftest
{
2895 /* Selftests for diagnostic_show_locus. */
2897 /* Verify that cpp_display_width correctly handles escaping. */
2900 test_display_widths ()
2902 gcc_rich_location
richloc (UNKNOWN_LOCATION
);
2904 /* U+03C0 "GREEK SMALL LETTER PI". */
2905 const char *pi
= "\xCF\x80";
2906 /* U+1F642 "SLIGHTLY SMILING FACE". */
2907 const char *emoji
= "\xF0\x9F\x99\x82";
2908 /* Stray trailing byte of a UTF-8 character. */
2909 const char *stray
= "\xBF";
2911 const char *max_codepoint
= "\xF4\x8F\xBF\xBF";
2915 test_diagnostic_context dc
;
2916 char_display_policy
policy (make_policy (dc
, richloc
));
2917 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 1);
2918 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 2);
2919 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 1);
2920 /* Don't check width of U+10FFFF; it's in a private use plane. */
2923 richloc
.set_escape_on_output (true);
2926 test_diagnostic_context dc
;
2927 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
;
2928 char_display_policy
policy (make_policy (dc
, richloc
));
2929 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 8);
2930 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 9);
2931 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 4);
2932 ASSERT_EQ (cpp_display_width (max_codepoint
, strlen (max_codepoint
),
2934 strlen ("<U+10FFFF>"));
2938 test_diagnostic_context dc
;
2939 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_BYTES
;
2940 char_display_policy
policy (make_policy (dc
, richloc
));
2941 ASSERT_EQ (cpp_display_width (pi
, strlen (pi
), policy
), 8);
2942 ASSERT_EQ (cpp_display_width (emoji
, strlen (emoji
), policy
), 16);
2943 ASSERT_EQ (cpp_display_width (stray
, strlen (stray
), policy
), 4);
2944 ASSERT_EQ (cpp_display_width (max_codepoint
, strlen (max_codepoint
),
2950 /* For precise tests of the layout, make clear where the source line will
2951 start. test_left_margin sets the total byte count from the left side of the
2952 screen to the start of source lines, after the line number and the separator,
2953 which consists of the three characters " | ". */
2954 static const int test_linenum_sep
= 3;
2955 static const int test_left_margin
= 7;
2957 /* Helper function for test_layout_x_offset_display_utf8(). */
2959 test_offset_impl (int caret_byte_col
, int max_width
,
2960 int expected_x_offset_display
,
2961 int left_margin
= test_left_margin
)
2963 test_diagnostic_context dc
;
2964 dc
.m_source_printing
.max_width
= max_width
;
2965 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2966 the line number plus one space after. */
2967 dc
.m_source_printing
.min_margin_width
= left_margin
- test_linenum_sep
+ 1;
2968 dc
.m_source_printing
.show_line_numbers_p
= true;
2969 rich_location
richloc (line_table
,
2970 linemap_position_for_column (line_table
,
2972 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
2973 ASSERT_EQ (left_margin
- test_linenum_sep
,
2974 test_layout
.get_linenum_width ());
2975 ASSERT_EQ (expected_x_offset_display
,
2976 test_layout
.get_x_offset_display ());
2979 /* Test that layout::calculate_x_offset_display() works. */
2981 test_layout_x_offset_display_utf8 (const line_table_case
&case_
)
2985 = "This line is very long, so that we can use it to test the logic for "
2986 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2987 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2990 /* Number of bytes in the line, subtracting one to remove the newline. */
2991 const int line_bytes
= strlen (content
) - 1;
2993 /* Number of display columns occupied by the line; each of the 2 emojis
2994 takes up 2 fewer display columns than it does bytes. */
2995 const int line_display_cols
= line_bytes
- 2*2;
2997 /* The column of the first emoji. Byte or display is the same as there are
2998 no multibyte characters earlier on the line. */
2999 const int emoji_col
= 102;
3001 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3002 line_table_test
ltt (case_
);
3004 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3006 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
3008 /* Don't attempt to run the tests if column data might be unavailable. */
3009 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3012 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
3013 ASSERT_EQ (1, LOCATION_LINE (line_end
));
3014 ASSERT_EQ (line_bytes
, LOCATION_COLUMN (line_end
));
3016 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
3017 ASSERT_EQ (line_display_cols
,
3018 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
3020 ASSERT_EQ (line_display_cols
,
3021 location_compute_display_column (expand_location (line_end
),
3023 ASSERT_EQ (0, memcmp (lspan
.get_buffer () + (emoji_col
- 1),
3024 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3026 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3028 /* No constraint on the width -> no offset. */
3029 test_offset_impl (emoji_col
, 0, 0);
3031 /* Caret is before the beginning -> no offset. */
3032 test_offset_impl (0, 100, 0);
3034 /* Caret is past the end of the line -> no offset. */
3035 test_offset_impl (line_bytes
+1, 100, 0);
3037 /* Line fits in the display -> no offset. */
3038 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
, 0);
3039 test_offset_impl (emoji_col
, line_display_cols
+ test_left_margin
, 0);
3041 /* Line is too long for the display but caret location is OK
3042 anyway -> no offset. */
3043 static const int small_width
= 24;
3044 test_offset_impl (1, small_width
, 0);
3046 /* Width constraint is very small -> no offset. */
3047 test_offset_impl (emoji_col
, CARET_LINE_MARGIN
, 0);
3049 /* Line would be offset, but due to large line numbers, offsetting
3050 would remove the whole line -> no offset. */
3051 static const int huge_left_margin
= 100;
3052 test_offset_impl (emoji_col
, huge_left_margin
, 0, huge_left_margin
);
3054 /* Line is the same length as the display, but the line number makes it too
3055 long, so offset is required. Caret is at the end so padding on the right
3056 is not in effect. */
3057 for (int excess
= 1; excess
<= 3; ++excess
)
3058 test_offset_impl (line_bytes
, line_display_cols
+ test_left_margin
- excess
,
3061 /* Line is much too long for the display, caret is near the end ->
3062 offset should be such that the line fits in the display and caret
3063 remains the same distance from the end that it was. */
3064 for (int caret_offset
= 0, max_offset
= MIN (CARET_LINE_MARGIN
, 10);
3065 caret_offset
<= max_offset
; ++caret_offset
)
3066 test_offset_impl (line_bytes
- caret_offset
, small_width
,
3067 line_display_cols
+ test_left_margin
- small_width
);
3069 /* As previous case but caret is closer to the middle; now we want it to end
3070 up CARET_LINE_MARGIN bytes from the end. */
3071 ASSERT_GT (line_display_cols
- emoji_col
, CARET_LINE_MARGIN
);
3072 test_offset_impl (emoji_col
, small_width
,
3073 emoji_col
+ test_left_margin
3074 - (small_width
- CARET_LINE_MARGIN
));
3076 /* Test that the source line is offset as expected when printed. */
3078 test_diagnostic_context dc
;
3079 dc
.m_source_printing
.max_width
= small_width
- 6;
3080 dc
.m_source_printing
.min_margin_width
3081 = test_left_margin
- test_linenum_sep
+ 1;
3082 dc
.m_source_printing
.show_line_numbers_p
= true;
3083 dc
.m_source_printing
.show_ruler_p
= true;
3084 rich_location
richloc (line_table
,
3085 linemap_position_for_column (line_table
,
3087 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
3088 test_layout
.print_line (1);
3089 ASSERT_STREQ (" | 1 \n"
3091 " | 234567890123456789\n"
3092 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3093 "that occupies 8 bytes and 4 display columns, starting at "
3096 pp_formatted_text (dc
.printer
));
3099 /* Similar to the previous example, but now the offset called for would split
3100 the first emoji in the middle of the UTF-8 sequence. Check that we replace
3101 it with a padding space in this case. */
3103 test_diagnostic_context dc
;
3104 dc
.m_source_printing
.max_width
= small_width
- 5;
3105 dc
.m_source_printing
.min_margin_width
3106 = test_left_margin
- test_linenum_sep
+ 1;
3107 dc
.m_source_printing
.show_line_numbers_p
= true;
3108 dc
.m_source_printing
.show_ruler_p
= true;
3109 rich_location
richloc (line_table
,
3110 linemap_position_for_column (line_table
,
3112 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
3113 test_layout
.print_line (1);
3114 ASSERT_STREQ (" | 1 1 \n"
3116 " | 3456789012345678901\n"
3117 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3118 "that occupies 8 bytes and 4 display columns, starting at "
3121 pp_formatted_text (dc
.printer
));
3127 test_layout_x_offset_display_tab (const line_table_case
&case_
)
3130 = "This line is very long, so that we can use it to test the logic for "
3131 "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3132 "a variable number of display columns, starting at column #103.\n";
3134 /* Number of bytes in the line, subtracting one to remove the newline. */
3135 const int line_bytes
= strlen (content
) - 1;
3137 /* The column where the tab begins. Byte or display is the same as there are
3138 no multibyte characters earlier on the line. */
3139 const int tab_col
= 103;
3141 /* Effective extra size of the tab beyond what a single space would have taken
3142 up, indexed by tabstop. */
3143 static const int num_tabstops
= 11;
3144 int extra_width
[num_tabstops
];
3145 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3147 const int this_tab_size
= tabstop
- (tab_col
- 1) % tabstop
;
3148 extra_width
[tabstop
] = this_tab_size
- 1;
3150 /* Example of this calculation: if tabstop is 10, the tab starting at column
3151 #103 has to expand into 8 spaces, covering columns 103-110, so that the
3152 next character is at column #111. So it takes up 7 more columns than
3153 a space would have taken up. */
3154 ASSERT_EQ (7, extra_width
[10]);
3156 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3157 line_table_test
ltt (case_
);
3159 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3161 location_t line_end
= linemap_position_for_column (line_table
, line_bytes
);
3163 /* Don't attempt to run the tests if column data might be unavailable. */
3164 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3167 /* Check that cpp_display_width handles the tabs as expected. */
3168 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
3169 ASSERT_EQ ('\t', *(lspan
.get_buffer () + (tab_col
- 1)));
3170 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3172 cpp_char_column_policy
policy (tabstop
, cpp_wcwidth
);
3173 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
3174 cpp_display_width (lspan
.get_buffer (), lspan
.length (),
3176 ASSERT_EQ (line_bytes
+ extra_width
[tabstop
],
3177 location_compute_display_column (expand_location (line_end
),
3181 /* Check that the tab is expanded to the expected number of spaces. */
3182 rich_location
richloc (line_table
,
3183 linemap_position_for_column (line_table
,
3185 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3187 test_diagnostic_context dc
;
3188 dc
.tabstop
= tabstop
;
3189 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
3190 test_layout
.print_line (1);
3191 const char *out
= pp_formatted_text (dc
.printer
);
3192 ASSERT_EQ (NULL
, strchr (out
, '\t'));
3193 const char *left_quote
= strchr (out
, '`');
3194 const char *right_quote
= strchr (out
, '\'');
3195 ASSERT_NE (NULL
, left_quote
);
3196 ASSERT_NE (NULL
, right_quote
);
3197 ASSERT_EQ (right_quote
- left_quote
, extra_width
[tabstop
] + 2);
3200 /* Check that the line is offset properly and that the tab is broken up
3201 into the expected number of spaces when it is the last character skipped
3203 for (int tabstop
= 1; tabstop
!= num_tabstops
; ++tabstop
)
3205 test_diagnostic_context dc
;
3206 dc
.tabstop
= tabstop
;
3207 static const int small_width
= 24;
3208 dc
.m_source_printing
.max_width
= small_width
- 4;
3209 dc
.m_source_printing
.min_margin_width
3210 = test_left_margin
- test_linenum_sep
+ 1;
3211 dc
.m_source_printing
.show_line_numbers_p
= true;
3212 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
3213 test_layout
.print_line (1);
3215 /* We have arranged things so that two columns will be printed before
3216 the caret. If the tab results in more than one space, this should
3217 produce two spaces in the output; otherwise, it will be a single space
3218 preceded by the opening quote before the tab character. */
3220 = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3221 "display columns, starting at column #103.\n"
3224 = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3225 "display columns, starting at column #103.\n"
3227 const char *expected_output
= (extra_width
[tabstop
] ? output1
: output2
);
3228 ASSERT_STREQ (expected_output
, pp_formatted_text (dc
.printer
));
3233 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3236 test_diagnostic_show_locus_unknown_location ()
3238 test_diagnostic_context dc
;
3239 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3240 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3241 ASSERT_STREQ ("", pp_formatted_text (dc
.printer
));
3244 /* Verify that diagnostic_show_locus works sanely for various
3247 All of these work on the following 1-line source file:
3250 "foo = bar.field;\n"
3251 which is set up by test_diagnostic_show_locus_one_liner and calls
3257 test_one_liner_simple_caret ()
3259 test_diagnostic_context dc
;
3260 location_t caret
= linemap_position_for_column (line_table
, 10);
3261 rich_location
richloc (line_table
, caret
);
3262 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3263 ASSERT_STREQ (" foo = bar.field;\n"
3265 pp_formatted_text (dc
.printer
));
3268 /* Caret and range. */
3271 test_one_liner_caret_and_range ()
3273 test_diagnostic_context dc
;
3274 location_t caret
= linemap_position_for_column (line_table
, 10);
3275 location_t start
= linemap_position_for_column (line_table
, 7);
3276 location_t finish
= linemap_position_for_column (line_table
, 15);
3277 location_t loc
= make_location (caret
, start
, finish
);
3278 rich_location
richloc (line_table
, loc
);
3279 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3280 ASSERT_STREQ (" foo = bar.field;\n"
3282 pp_formatted_text (dc
.printer
));
3285 /* Multiple ranges and carets. */
3288 test_one_liner_multiple_carets_and_ranges ()
3290 test_diagnostic_context dc
;
3292 = make_location (linemap_position_for_column (line_table
, 2),
3293 linemap_position_for_column (line_table
, 1),
3294 linemap_position_for_column (line_table
, 3));
3295 dc
.m_source_printing
.caret_chars
[0] = 'A';
3298 = make_location (linemap_position_for_column (line_table
, 8),
3299 linemap_position_for_column (line_table
, 7),
3300 linemap_position_for_column (line_table
, 9));
3301 dc
.m_source_printing
.caret_chars
[1] = 'B';
3304 = make_location (linemap_position_for_column (line_table
, 13),
3305 linemap_position_for_column (line_table
, 11),
3306 linemap_position_for_column (line_table
, 15));
3307 dc
.m_source_printing
.caret_chars
[2] = 'C';
3309 rich_location
richloc (line_table
, foo
);
3310 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3311 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3312 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3313 ASSERT_STREQ (" foo = bar.field;\n"
3315 pp_formatted_text (dc
.printer
));
3318 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3321 test_one_liner_fixit_insert_before ()
3323 test_diagnostic_context dc
;
3324 location_t caret
= linemap_position_for_column (line_table
, 7);
3325 rich_location
richloc (line_table
, caret
);
3326 richloc
.add_fixit_insert_before ("&");
3327 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3328 ASSERT_STREQ (" foo = bar.field;\n"
3331 pp_formatted_text (dc
.printer
));
3334 /* Insertion fix-it hint: adding a "[0]" after "foo". */
3337 test_one_liner_fixit_insert_after ()
3339 test_diagnostic_context dc
;
3340 location_t start
= linemap_position_for_column (line_table
, 1);
3341 location_t finish
= linemap_position_for_column (line_table
, 3);
3342 location_t foo
= make_location (start
, start
, finish
);
3343 rich_location
richloc (line_table
, foo
);
3344 richloc
.add_fixit_insert_after ("[0]");
3345 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3346 ASSERT_STREQ (" foo = bar.field;\n"
3349 pp_formatted_text (dc
.printer
));
3352 /* Removal fix-it hint: removal of the ".field".
3353 Also verify the interaction of pp_set_prefix with rulers and
3357 test_one_liner_fixit_remove ()
3359 location_t start
= linemap_position_for_column (line_table
, 10);
3360 location_t finish
= linemap_position_for_column (line_table
, 15);
3361 location_t dot
= make_location (start
, start
, finish
);
3362 rich_location
richloc (line_table
, dot
);
3363 richloc
.add_fixit_remove ();
3367 test_diagnostic_context dc
;
3368 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3369 ASSERT_STREQ (" foo = bar.field;\n"
3372 pp_formatted_text (dc
.printer
));
3375 /* Test of adding a prefix. */
3377 test_diagnostic_context dc
;
3378 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3379 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3380 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3381 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3382 "TEST PREFIX: ^~~~~~\n"
3383 "TEST PREFIX: ------\n",
3384 pp_formatted_text (dc
.printer
));
3387 /* Normal, with ruler. */
3389 test_diagnostic_context dc
;
3390 dc
.m_source_printing
.show_ruler_p
= true;
3391 dc
.m_source_printing
.max_width
= 104;
3392 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3393 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3394 " 1 2 3 4 5 6 7 8 9 0 \n"
3395 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3396 " foo = bar.field;\n"
3399 pp_formatted_text (dc
.printer
));
3402 /* Test of adding a prefix, with ruler. */
3404 test_diagnostic_context dc
;
3405 dc
.m_source_printing
.show_ruler_p
= true;
3406 dc
.m_source_printing
.max_width
= 50;
3407 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3408 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3409 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3410 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3411 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3412 "TEST PREFIX: foo = bar.field;\n"
3413 "TEST PREFIX: ^~~~~~\n"
3414 "TEST PREFIX: ------\n",
3415 pp_formatted_text (dc
.printer
));
3418 /* Test of adding a prefix, with ruler and line numbers. */
3420 test_diagnostic_context dc
;
3421 dc
.m_source_printing
.show_ruler_p
= true;
3422 dc
.m_source_printing
.max_width
= 50;
3423 dc
.m_source_printing
.show_line_numbers_p
= true;
3424 pp_prefixing_rule (dc
.printer
) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
;
3425 pp_set_prefix (dc
.printer
, xstrdup ("TEST PREFIX:"));
3426 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3427 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3428 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3429 "TEST PREFIX: 1 | foo = bar.field;\n"
3430 "TEST PREFIX: | ^~~~~~\n"
3431 "TEST PREFIX: | ------\n",
3432 pp_formatted_text (dc
.printer
));
3436 /* Replace fix-it hint: replacing "field" with "m_field". */
3439 test_one_liner_fixit_replace ()
3441 test_diagnostic_context dc
;
3442 location_t start
= linemap_position_for_column (line_table
, 11);
3443 location_t finish
= linemap_position_for_column (line_table
, 15);
3444 location_t field
= make_location (start
, start
, finish
);
3445 rich_location
richloc (line_table
, field
);
3446 richloc
.add_fixit_replace ("m_field");
3447 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3448 ASSERT_STREQ (" foo = bar.field;\n"
3451 pp_formatted_text (dc
.printer
));
3454 /* Replace fix-it hint: replacing "field" with "m_field",
3455 but where the caret was elsewhere. */
3458 test_one_liner_fixit_replace_non_equal_range ()
3460 test_diagnostic_context dc
;
3461 location_t equals
= linemap_position_for_column (line_table
, 5);
3462 location_t start
= linemap_position_for_column (line_table
, 11);
3463 location_t finish
= linemap_position_for_column (line_table
, 15);
3464 rich_location
richloc (line_table
, equals
);
3466 range
.m_start
= start
;
3467 range
.m_finish
= finish
;
3468 richloc
.add_fixit_replace (range
, "m_field");
3469 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3470 /* The replacement range is not indicated in the annotation line, so
3471 it should be indicated via an additional underline. */
3472 ASSERT_STREQ (" foo = bar.field;\n"
3476 pp_formatted_text (dc
.printer
));
3479 /* Replace fix-it hint: replacing "field" with "m_field",
3480 where the caret was elsewhere, but where a secondary range
3481 exactly covers "field". */
3484 test_one_liner_fixit_replace_equal_secondary_range ()
3486 test_diagnostic_context dc
;
3487 location_t equals
= linemap_position_for_column (line_table
, 5);
3488 location_t start
= linemap_position_for_column (line_table
, 11);
3489 location_t finish
= linemap_position_for_column (line_table
, 15);
3490 rich_location
richloc (line_table
, equals
);
3491 location_t field
= make_location (start
, start
, finish
);
3492 richloc
.add_range (field
);
3493 richloc
.add_fixit_replace (field
, "m_field");
3494 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3495 /* The replacement range is indicated in the annotation line,
3496 so it shouldn't be indicated via an additional underline. */
3497 ASSERT_STREQ (" foo = bar.field;\n"
3500 pp_formatted_text (dc
.printer
));
3503 /* Verify that we can use ad-hoc locations when adding fixits to a
3507 test_one_liner_fixit_validation_adhoc_locations ()
3509 /* Generate a range that's too long to be packed, so must
3510 be stored as an ad-hoc location (given the defaults
3511 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3512 const location_t c7
= linemap_position_for_column (line_table
, 7);
3513 const location_t c47
= linemap_position_for_column (line_table
, 47);
3514 const location_t loc
= make_location (c7
, c7
, c47
);
3516 if (c47
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3519 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
3523 rich_location
richloc (line_table
, loc
);
3524 richloc
.add_fixit_insert_before (loc
, "test");
3525 /* It should not have been discarded by the validator. */
3526 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3528 test_diagnostic_context dc
;
3529 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3530 ASSERT_STREQ (" foo = bar.field;\n"
3533 pp_formatted_text (dc
.printer
));
3538 rich_location
richloc (line_table
, loc
);
3539 source_range range
= source_range::from_locations (loc
, c47
);
3540 richloc
.add_fixit_remove (range
);
3541 /* It should not have been discarded by the validator. */
3542 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3544 test_diagnostic_context dc
;
3545 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3546 ASSERT_STREQ (" foo = bar.field;\n"
3548 " -----------------------------------------\n",
3549 pp_formatted_text (dc
.printer
));
3554 rich_location
richloc (line_table
, loc
);
3555 source_range range
= source_range::from_locations (loc
, c47
);
3556 richloc
.add_fixit_replace (range
, "test");
3557 /* It should not have been discarded by the validator. */
3558 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3560 test_diagnostic_context dc
;
3561 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3562 ASSERT_STREQ (" foo = bar.field;\n"
3565 pp_formatted_text (dc
.printer
));
3569 /* Test of consolidating insertions at the same location. */
3572 test_one_liner_many_fixits_1 ()
3574 test_diagnostic_context dc
;
3575 location_t equals
= linemap_position_for_column (line_table
, 5);
3576 rich_location
richloc (line_table
, equals
);
3577 for (int i
= 0; i
< 19; i
++)
3578 richloc
.add_fixit_insert_before ("a");
3579 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
3580 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3581 ASSERT_STREQ (" foo = bar.field;\n"
3583 " aaaaaaaaaaaaaaaaaaa\n",
3584 pp_formatted_text (dc
.printer
));
3587 /* Ensure that we can add an arbitrary number of fix-it hints to a
3588 rich_location, even if they are not consolidated. */
3591 test_one_liner_many_fixits_2 ()
3593 test_diagnostic_context dc
;
3594 location_t equals
= linemap_position_for_column (line_table
, 5);
3595 rich_location
richloc (line_table
, equals
);
3596 for (int i
= 0; i
< 19; i
++)
3598 location_t loc
= linemap_position_for_column (line_table
, (i
* 2) + 1);
3599 richloc
.add_fixit_insert_before (loc
, "a");
3601 ASSERT_EQ (19, richloc
.get_num_fixit_hints ());
3602 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3603 ASSERT_STREQ (" foo = bar.field;\n"
3605 " a a a a a a a a a a a a a a a a a a a\n",
3606 pp_formatted_text (dc
.printer
));
3609 /* Test of labeling the ranges within a rich_location. */
3612 test_one_liner_labels ()
3615 = make_location (linemap_position_for_column (line_table
, 1),
3616 linemap_position_for_column (line_table
, 1),
3617 linemap_position_for_column (line_table
, 3));
3619 = make_location (linemap_position_for_column (line_table
, 7),
3620 linemap_position_for_column (line_table
, 7),
3621 linemap_position_for_column (line_table
, 9));
3623 = make_location (linemap_position_for_column (line_table
, 11),
3624 linemap_position_for_column (line_table
, 11),
3625 linemap_position_for_column (line_table
, 15));
3627 /* Example where all the labels fit on one line. */
3629 text_range_label
label0 ("0");
3630 text_range_label
label1 ("1");
3631 text_range_label
label2 ("2");
3632 gcc_rich_location
richloc (foo
, &label0
);
3633 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3634 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3637 test_diagnostic_context dc
;
3638 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3639 ASSERT_STREQ (" foo = bar.field;\n"
3643 pp_formatted_text (dc
.printer
));
3646 /* Verify that we can disable label-printing. */
3648 test_diagnostic_context dc
;
3649 dc
.m_source_printing
.show_labels_p
= false;
3650 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3651 ASSERT_STREQ (" foo = bar.field;\n"
3653 pp_formatted_text (dc
.printer
));
3657 /* Example where the labels need extra lines. */
3659 text_range_label
label0 ("label 0");
3660 text_range_label
label1 ("label 1");
3661 text_range_label
label2 ("label 2");
3662 gcc_rich_location
richloc (foo
, &label0
);
3663 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3664 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3666 test_diagnostic_context dc
;
3667 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3668 ASSERT_STREQ (" foo = bar.field;\n"
3674 pp_formatted_text (dc
.printer
));
3677 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3678 but label 1 just touches label 2. */
3680 text_range_label
label0 ("aaaaa");
3681 text_range_label
label1 ("bbbb");
3682 text_range_label
label2 ("c");
3683 gcc_rich_location
richloc (foo
, &label0
);
3684 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3685 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3687 test_diagnostic_context dc
;
3688 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3689 ASSERT_STREQ (" foo = bar.field;\n"
3694 pp_formatted_text (dc
.printer
));
3697 /* Example of out-of-order ranges (thus requiring a sort). */
3699 text_range_label
label0 ("0");
3700 text_range_label
label1 ("1");
3701 text_range_label
label2 ("2");
3702 gcc_rich_location
richloc (field
, &label0
);
3703 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3704 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3706 test_diagnostic_context dc
;
3707 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3708 ASSERT_STREQ (" foo = bar.field;\n"
3712 pp_formatted_text (dc
.printer
));
3715 /* Ensure we don't ICE if multiple ranges with labels are on
3718 text_range_label
label0 ("label 0");
3719 text_range_label
label1 ("label 1");
3720 text_range_label
label2 ("label 2");
3721 gcc_rich_location
richloc (bar
, &label0
);
3722 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3723 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3725 test_diagnostic_context dc
;
3726 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3727 ASSERT_STREQ (" foo = bar.field;\n"
3733 pp_formatted_text (dc
.printer
));
3736 /* Example of out-of-order ranges (thus requiring a sort), where
3737 they overlap, and there are multiple ranges on the same point. */
3739 text_range_label
label_0a ("label 0a");
3740 text_range_label
label_1a ("label 1a");
3741 text_range_label
label_2a ("label 2a");
3742 text_range_label
label_0b ("label 0b");
3743 text_range_label
label_1b ("label 1b");
3744 text_range_label
label_2b ("label 2b");
3745 text_range_label
label_0c ("label 0c");
3746 text_range_label
label_1c ("label 1c");
3747 text_range_label
label_2c ("label 2c");
3748 gcc_rich_location
richloc (field
, &label_0a
);
3749 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1a
);
3750 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2a
);
3752 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0b
);
3753 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1b
);
3754 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2b
);
3756 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label_0c
);
3757 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label_1c
);
3758 richloc
.add_range (foo
, SHOW_RANGE_WITHOUT_CARET
, &label_2c
);
3760 test_diagnostic_context dc
;
3761 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3762 ASSERT_STREQ (" foo = bar.field;\n"
3774 pp_formatted_text (dc
.printer
));
3777 /* Verify that a NULL result from range_label::get_text is
3778 handled gracefully. */
3780 text_range_label
label (NULL
);
3781 gcc_rich_location
richloc (bar
, &label
);
3783 test_diagnostic_context dc
;
3784 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3785 ASSERT_STREQ (" foo = bar.field;\n"
3787 pp_formatted_text (dc
.printer
));
3790 /* TODO: example of formatted printing (needs to be in
3791 gcc-rich-location.cc due to Makefile.in issues). */
3794 /* Run the various one-liner tests. */
3797 test_diagnostic_show_locus_one_liner (const line_table_case
&case_
)
3799 /* Create a tempfile and write some text to it.
3800 ....................0000000001111111.
3801 ....................1234567890123456. */
3802 const char *content
= "foo = bar.field;\n";
3803 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
3804 line_table_test
ltt (case_
);
3806 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
3808 location_t line_end
= linemap_position_for_column (line_table
, 16);
3810 /* Don't attempt to run the tests if column data might be unavailable. */
3811 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3814 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
3815 ASSERT_EQ (1, LOCATION_LINE (line_end
));
3816 ASSERT_EQ (16, LOCATION_COLUMN (line_end
));
3818 test_one_liner_simple_caret ();
3819 test_one_liner_caret_and_range ();
3820 test_one_liner_multiple_carets_and_ranges ();
3821 test_one_liner_fixit_insert_before ();
3822 test_one_liner_fixit_insert_after ();
3823 test_one_liner_fixit_remove ();
3824 test_one_liner_fixit_replace ();
3825 test_one_liner_fixit_replace_non_equal_range ();
3826 test_one_liner_fixit_replace_equal_secondary_range ();
3827 test_one_liner_fixit_validation_adhoc_locations ();
3828 test_one_liner_many_fixits_1 ();
3829 test_one_liner_many_fixits_2 ();
3830 test_one_liner_labels ();
3833 /* Version of all one-liner tests exercising multibyte awareness. For
3834 simplicity we stick to using two multibyte characters in the test, U+1F602
3835 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3836 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3837 below asserts would be easier to read if we used UTF-8 directly in the
3838 string constants, but it seems better not to demand the host compiler
3839 support this, when it isn't otherwise necessary. Instead, whenever an
3840 extended character appears in a string, we put a line break after it so that
3841 all succeeding characters can appear visually at the correct display column.
3843 All of these work on the following 1-line source file:
3845 .0000000001111111111222222 display
3846 .1234567890123456789012345 columns
3847 "SS_foo = P_bar.SS_fieldP;\n"
3848 .0000000111111111222222223 byte
3849 .1356789012456789134567891 columns
3851 which is set up by test_diagnostic_show_locus_one_liner and calls
3852 them. Here SS represents the two display columns for the U+1F602 emoji and
3853 P represents the one display column for the U+03C0 pi symbol. */
3858 test_one_liner_simple_caret_utf8 ()
3860 test_diagnostic_context dc
;
3861 location_t caret
= linemap_position_for_column (line_table
, 18);
3862 rich_location
richloc (line_table
, caret
);
3863 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3864 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3866 "_bar.\xf0\x9f\x98\x82"
3870 pp_formatted_text (dc
.printer
));
3873 /* Caret and range. */
3875 test_one_liner_caret_and_range_utf8 ()
3877 test_diagnostic_context dc
;
3878 location_t caret
= linemap_position_for_column (line_table
, 18);
3879 location_t start
= linemap_position_for_column (line_table
, 12);
3880 location_t finish
= linemap_position_for_column (line_table
, 30);
3881 location_t loc
= make_location (caret
, start
, finish
);
3882 rich_location
richloc (line_table
, loc
);
3883 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3884 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3886 "_bar.\xf0\x9f\x98\x82"
3889 " ~~~~~^~~~~~~~~~\n",
3890 pp_formatted_text (dc
.printer
));
3893 /* Multiple ranges and carets. */
3896 test_one_liner_multiple_carets_and_ranges_utf8 ()
3898 test_diagnostic_context dc
;
3900 = make_location (linemap_position_for_column (line_table
, 7),
3901 linemap_position_for_column (line_table
, 1),
3902 linemap_position_for_column (line_table
, 8));
3903 dc
.m_source_printing
.caret_chars
[0] = 'A';
3906 = make_location (linemap_position_for_column (line_table
, 16),
3907 linemap_position_for_column (line_table
, 12),
3908 linemap_position_for_column (line_table
, 17));
3909 dc
.m_source_printing
.caret_chars
[1] = 'B';
3912 = make_location (linemap_position_for_column (line_table
, 26),
3913 linemap_position_for_column (line_table
, 19),
3914 linemap_position_for_column (line_table
, 30));
3915 dc
.m_source_printing
.caret_chars
[2] = 'C';
3916 rich_location
richloc (line_table
, foo
);
3917 richloc
.add_range (bar
, SHOW_RANGE_WITH_CARET
);
3918 richloc
.add_range (field
, SHOW_RANGE_WITH_CARET
);
3919 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3920 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3922 "_bar.\xf0\x9f\x98\x82"
3925 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3926 pp_formatted_text (dc
.printer
));
3929 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3932 test_one_liner_fixit_insert_before_utf8 ()
3934 test_diagnostic_context dc
;
3935 location_t caret
= linemap_position_for_column (line_table
, 12);
3936 rich_location
richloc (line_table
, caret
);
3937 richloc
.add_fixit_insert_before ("&");
3938 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3939 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3941 "_bar.\xf0\x9f\x98\x82"
3946 pp_formatted_text (dc
.printer
));
3949 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3952 test_one_liner_fixit_insert_after_utf8 ()
3954 test_diagnostic_context dc
;
3955 location_t start
= linemap_position_for_column (line_table
, 1);
3956 location_t finish
= linemap_position_for_column (line_table
, 8);
3957 location_t foo
= make_location (start
, start
, finish
);
3958 rich_location
richloc (line_table
, foo
);
3959 richloc
.add_fixit_insert_after ("[0]");
3960 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3961 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3963 "_bar.\xf0\x9f\x98\x82"
3968 pp_formatted_text (dc
.printer
));
3971 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3974 test_one_liner_fixit_remove_utf8 ()
3976 test_diagnostic_context dc
;
3977 location_t start
= linemap_position_for_column (line_table
, 18);
3978 location_t finish
= linemap_position_for_column (line_table
, 30);
3979 location_t dot
= make_location (start
, start
, finish
);
3980 rich_location
richloc (line_table
, dot
);
3981 richloc
.add_fixit_remove ();
3982 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
3983 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3985 "_bar.\xf0\x9f\x98\x82"
3990 pp_formatted_text (dc
.printer
));
3993 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3996 test_one_liner_fixit_replace_utf8 ()
3998 test_diagnostic_context dc
;
3999 location_t start
= linemap_position_for_column (line_table
, 19);
4000 location_t finish
= linemap_position_for_column (line_table
, 30);
4001 location_t field
= make_location (start
, start
, finish
);
4002 rich_location
richloc (line_table
, field
);
4003 richloc
.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4004 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4005 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4007 "_bar.\xf0\x9f\x98\x82"
4011 " m_\xf0\x9f\x98\x82"
4013 pp_formatted_text (dc
.printer
));
4016 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4017 but where the caret was elsewhere. */
4020 test_one_liner_fixit_replace_non_equal_range_utf8 ()
4022 test_diagnostic_context dc
;
4023 location_t equals
= linemap_position_for_column (line_table
, 10);
4024 location_t start
= linemap_position_for_column (line_table
, 19);
4025 location_t finish
= linemap_position_for_column (line_table
, 30);
4026 rich_location
richloc (line_table
, equals
);
4028 range
.m_start
= start
;
4029 range
.m_finish
= finish
;
4030 richloc
.add_fixit_replace (range
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4031 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4032 /* The replacement range is not indicated in the annotation line, so
4033 it should be indicated via an additional underline. */
4034 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4036 "_bar.\xf0\x9f\x98\x82"
4041 " m_\xf0\x9f\x98\x82"
4043 pp_formatted_text (dc
.printer
));
4046 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4047 where the caret was elsewhere, but where a secondary range
4048 exactly covers "field". */
4051 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4053 test_diagnostic_context dc
;
4054 location_t equals
= linemap_position_for_column (line_table
, 10);
4055 location_t start
= linemap_position_for_column (line_table
, 19);
4056 location_t finish
= linemap_position_for_column (line_table
, 30);
4057 rich_location
richloc (line_table
, equals
);
4058 location_t field
= make_location (start
, start
, finish
);
4059 richloc
.add_range (field
);
4060 richloc
.add_fixit_replace (field
, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4061 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4062 /* The replacement range is indicated in the annotation line,
4063 so it shouldn't be indicated via an additional underline. */
4064 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4066 "_bar.\xf0\x9f\x98\x82"
4070 " m_\xf0\x9f\x98\x82"
4072 pp_formatted_text (dc
.printer
));
4075 /* Verify that we can use ad-hoc locations when adding fixits to a
4079 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4081 /* Generate a range that's too long to be packed, so must
4082 be stored as an ad-hoc location (given the defaults
4083 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
4084 const location_t c12
= linemap_position_for_column (line_table
, 12);
4085 const location_t c52
= linemap_position_for_column (line_table
, 52);
4086 const location_t loc
= make_location (c12
, c12
, c52
);
4088 if (c52
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4091 ASSERT_TRUE (IS_ADHOC_LOC (loc
));
4095 rich_location
richloc (line_table
, loc
);
4096 richloc
.add_fixit_insert_before (loc
, "test");
4097 /* It should not have been discarded by the validator. */
4098 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4100 test_diagnostic_context dc
;
4101 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4102 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4104 "_bar.\xf0\x9f\x98\x82"
4107 " ^~~~~~~~~~~~~~~~ \n"
4109 pp_formatted_text (dc
.printer
));
4114 rich_location
richloc (line_table
, loc
);
4115 source_range range
= source_range::from_locations (loc
, c52
);
4116 richloc
.add_fixit_remove (range
);
4117 /* It should not have been discarded by the validator. */
4118 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4120 test_diagnostic_context dc
;
4121 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4122 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4124 "_bar.\xf0\x9f\x98\x82"
4127 " ^~~~~~~~~~~~~~~~ \n"
4128 " -------------------------------------\n",
4129 pp_formatted_text (dc
.printer
));
4134 rich_location
richloc (line_table
, loc
);
4135 source_range range
= source_range::from_locations (loc
, c52
);
4136 richloc
.add_fixit_replace (range
, "test");
4137 /* It should not have been discarded by the validator. */
4138 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4140 test_diagnostic_context dc
;
4141 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4142 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4144 "_bar.\xf0\x9f\x98\x82"
4147 " ^~~~~~~~~~~~~~~~ \n"
4149 pp_formatted_text (dc
.printer
));
4153 /* Test of consolidating insertions at the same location. */
4156 test_one_liner_many_fixits_1_utf8 ()
4158 test_diagnostic_context dc
;
4159 location_t equals
= linemap_position_for_column (line_table
, 10);
4160 rich_location
richloc (line_table
, equals
);
4161 for (int i
= 0; i
< 19; i
++)
4162 richloc
.add_fixit_insert_before (i
& 1 ? "@" : "\xcf\x80");
4163 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4164 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4165 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4167 "_bar.\xf0\x9f\x98\x82"
4171 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4172 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4173 pp_formatted_text (dc
.printer
));
4176 /* Ensure that we can add an arbitrary number of fix-it hints to a
4177 rich_location, even if they are not consolidated. */
4180 test_one_liner_many_fixits_2_utf8 ()
4182 test_diagnostic_context dc
;
4183 location_t equals
= linemap_position_for_column (line_table
, 10);
4184 rich_location
richloc (line_table
, equals
);
4185 const int nlocs
= 19;
4186 int locs
[nlocs
] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4187 34, 36, 38, 40, 42, 44};
4188 for (int i
= 0; i
!= nlocs
; ++i
)
4190 location_t loc
= linemap_position_for_column (line_table
, locs
[i
]);
4191 richloc
.add_fixit_insert_before (loc
, i
& 1 ? "@" : "\xcf\x80");
4194 ASSERT_EQ (nlocs
, richloc
.get_num_fixit_hints ());
4195 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4196 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4198 "_bar.\xf0\x9f\x98\x82"
4202 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4203 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4204 pp_formatted_text (dc
.printer
));
4207 /* Test of labeling the ranges within a rich_location. */
4210 test_one_liner_labels_utf8 ()
4213 = make_location (linemap_position_for_column (line_table
, 1),
4214 linemap_position_for_column (line_table
, 1),
4215 linemap_position_for_column (line_table
, 8));
4217 = make_location (linemap_position_for_column (line_table
, 12),
4218 linemap_position_for_column (line_table
, 12),
4219 linemap_position_for_column (line_table
, 17));
4221 = make_location (linemap_position_for_column (line_table
, 19),
4222 linemap_position_for_column (line_table
, 19),
4223 linemap_position_for_column (line_table
, 30));
4225 /* Example where all the labels fit on one line. */
4227 /* These three labels contain multibyte characters such that their byte
4228 lengths are respectively (12, 10, 18), but their display widths are only
4229 (6, 5, 9). All three fit on the line when considering the display
4230 widths, but not when considering the byte widths, so verify that we do
4231 indeed put them all on one line. */
4232 text_range_label label0
4233 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4234 text_range_label label1
4235 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4236 text_range_label label2
4237 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4239 gcc_rich_location
richloc (foo
, &label0
);
4240 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4241 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4244 test_diagnostic_context dc
;
4245 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4246 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4248 "_bar.\xf0\x9f\x98\x82"
4251 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4253 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4254 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4255 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4256 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4257 pp_formatted_text (dc
.printer
));
4262 /* Example where the labels need extra lines. */
4264 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
4265 text_range_label
label1 ("label 1\xcf\x80");
4266 text_range_label
label2 ("label 2\xcf\x80");
4267 gcc_rich_location
richloc (foo
, &label0
);
4268 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4269 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4271 test_diagnostic_context dc
;
4272 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4274 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4276 "_bar.\xf0\x9f\x98\x82"
4279 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4281 " | | label 2\xcf\x80\n"
4282 " | label 1\xcf\x80\n"
4283 " label 0\xf0\x9f\x98\x82\n",
4284 pp_formatted_text (dc
.printer
));
4287 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4288 but label 1 just touches label 2. */
4290 text_range_label
label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4291 text_range_label
label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4292 text_range_label
label2 ("c");
4293 gcc_rich_location
richloc (foo
, &label0
);
4294 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4295 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4297 test_diagnostic_context dc
;
4298 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4299 ASSERT_STREQ (" \xf0\x9f\x98\x82"
4301 "_bar.\xf0\x9f\x98\x82"
4304 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4307 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4308 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4309 pp_formatted_text (dc
.printer
));
4312 /* Example of escaping the source lines. */
4314 text_range_label
label0 ("label 0\xf0\x9f\x98\x82");
4315 text_range_label
label1 ("label 1\xcf\x80");
4316 text_range_label
label2 ("label 2\xcf\x80");
4317 gcc_rich_location
richloc (foo
, &label0
);
4318 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
4319 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
4320 richloc
.set_escape_on_output (true);
4323 test_diagnostic_context dc
;
4324 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
;
4325 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4326 ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4327 " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4329 " | | label 2\xcf\x80\n"
4330 " | label 1\xcf\x80\n"
4331 " label 0\xf0\x9f\x98\x82\n",
4332 pp_formatted_text (dc
.printer
));
4335 test_diagnostic_context dc
;
4336 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_BYTES
;
4337 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4339 (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4340 " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4342 " | | label 2\xcf\x80\n"
4343 " | label 1\xcf\x80\n"
4344 " label 0\xf0\x9f\x98\x82\n",
4345 pp_formatted_text (dc
.printer
));
4350 /* Make sure that colorization codes don't interrupt a multibyte
4351 sequence, which would corrupt it. */
4353 test_one_liner_colorized_utf8 ()
4355 test_diagnostic_context dc
;
4356 dc
.m_source_printing
.colorize_source_p
= true;
4357 diagnostic_color_init (&dc
, DIAGNOSTICS_COLOR_YES
);
4358 const location_t pi
= linemap_position_for_column (line_table
, 12);
4359 rich_location
richloc (line_table
, pi
);
4360 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4362 /* In order to avoid having the test depend on exactly how the colorization
4363 was effected, just confirm there are two pi characters in the output. */
4364 const char *result
= pp_formatted_text (dc
.printer
);
4365 const char *null_term
= result
+ strlen (result
);
4366 const char *first_pi
= strstr (result
, "\xcf\x80");
4367 ASSERT_TRUE (first_pi
&& first_pi
<= null_term
- 2);
4368 ASSERT_STR_CONTAINS (first_pi
+ 2, "\xcf\x80");
4371 /* Run the various one-liner tests. */
4374 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case
&case_
)
4376 /* Create a tempfile and write some text to it. */
4379 0000000000000000000000011111111111111111111111111111112222222222222
4380 1111111122222222345678900000000123456666666677777777890123444444445 */
4381 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4382 /* 0000000000000000000001111111111111111111222222222222222222222233333
4383 1111222233334444567890122223333456789999000011112222345678999900001
4385 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4386 line_table_test
ltt (case_
);
4388 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
4390 location_t line_end
= linemap_position_for_column (line_table
, 31);
4392 /* Don't attempt to run the tests if column data might be unavailable. */
4393 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4396 ASSERT_STREQ (tmp
.get_filename (), LOCATION_FILE (line_end
));
4397 ASSERT_EQ (1, LOCATION_LINE (line_end
));
4398 ASSERT_EQ (31, LOCATION_COLUMN (line_end
));
4400 char_span lspan
= location_get_source_line (tmp
.get_filename (), 1);
4401 ASSERT_EQ (25, cpp_display_width (lspan
.get_buffer (), lspan
.length (),
4403 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end
),
4406 test_one_liner_simple_caret_utf8 ();
4407 test_one_liner_caret_and_range_utf8 ();
4408 test_one_liner_multiple_carets_and_ranges_utf8 ();
4409 test_one_liner_fixit_insert_before_utf8 ();
4410 test_one_liner_fixit_insert_after_utf8 ();
4411 test_one_liner_fixit_remove_utf8 ();
4412 test_one_liner_fixit_replace_utf8 ();
4413 test_one_liner_fixit_replace_non_equal_range_utf8 ();
4414 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4415 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4416 test_one_liner_many_fixits_1_utf8 ();
4417 test_one_liner_many_fixits_2_utf8 ();
4418 test_one_liner_labels_utf8 ();
4419 test_one_liner_colorized_utf8 ();
4422 /* Verify that gcc_rich_location::add_location_if_nearby works. */
4425 test_add_location_if_nearby (const line_table_case
&case_
)
4427 /* Create a tempfile and write some text to it.
4428 ...000000000111111111122222222223333333333.
4429 ...123456789012345678901234567890123456789. */
4431 = ("struct same_line { double x; double y; ;\n" /* line 1. */
4432 "struct different_line\n" /* line 2. */
4434 " double x;\n" /* line 4. */
4435 " double y;\n" /* line 5. */
4436 ";\n"); /* line 6. */
4437 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4438 line_table_test
ltt (case_
);
4440 const line_map_ordinary
*ord_map
4441 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4442 tmp
.get_filename (), 0));
4444 linemap_line_start (line_table
, 1, 100);
4446 const location_t final_line_end
4447 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 7);
4449 /* Don't attempt to run the tests if column data might be unavailable. */
4450 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4453 /* Test of add_location_if_nearby on the same line as the
4454 primary location. */
4456 const location_t missing_close_brace_1_39
4457 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 39);
4458 const location_t matching_open_brace_1_18
4459 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4460 gcc_rich_location
richloc (missing_close_brace_1_39
);
4461 bool added
= richloc
.add_location_if_nearby (matching_open_brace_1_18
);
4462 ASSERT_TRUE (added
);
4463 ASSERT_EQ (2, richloc
.get_num_locations ());
4464 test_diagnostic_context dc
;
4465 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4466 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
4468 pp_formatted_text (dc
.printer
));
4471 /* Test of add_location_if_nearby on a different line to the
4472 primary location. */
4474 const location_t missing_close_brace_6_1
4475 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 1);
4476 const location_t matching_open_brace_3_1
4477 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 1);
4478 gcc_rich_location
richloc (missing_close_brace_6_1
);
4479 bool added
= richloc
.add_location_if_nearby (matching_open_brace_3_1
);
4480 ASSERT_FALSE (added
);
4481 ASSERT_EQ (1, richloc
.get_num_locations ());
4485 /* Verify that we print fixits even if they only affect lines
4486 outside those covered by the ranges in the rich_location. */
4489 test_diagnostic_show_locus_fixit_lines (const line_table_case
&case_
)
4491 /* Create a tempfile and write some text to it.
4492 ...000000000111111111122222222223333333333.
4493 ...123456789012345678901234567890123456789. */
4495 = ("struct point { double x; double y; };\n" /* line 1. */
4496 "struct point origin = {x: 0.0,\n" /* line 2. */
4497 " y\n" /* line 3. */
4500 " : 0.0};\n"); /* line 6. */
4501 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
4502 line_table_test
ltt (case_
);
4504 const line_map_ordinary
*ord_map
4505 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4506 tmp
.get_filename (), 0));
4508 linemap_line_start (line_table
, 1, 100);
4510 const location_t final_line_end
4511 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4513 /* Don't attempt to run the tests if column data might be unavailable. */
4514 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4517 /* A pair of tests for modernizing the initializers to C99-style. */
4519 /* The one-liner case (line 2). */
4521 test_diagnostic_context dc
;
4523 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 24);
4524 const location_t colon
4525 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 25);
4526 rich_location
richloc (line_table
, colon
);
4527 richloc
.add_fixit_insert_before (x
, ".");
4528 richloc
.add_fixit_replace (colon
, "=");
4529 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4530 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
4533 pp_formatted_text (dc
.printer
));
4536 /* The multiline case. The caret for the rich_location is on line 6;
4537 verify that insertion fixit on line 3 is still printed (and that
4538 span starts are printed due to the gap between the span at line 3
4539 and that at line 6). */
4541 test_diagnostic_context dc
;
4543 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4544 const location_t colon
4545 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4546 rich_location
richloc (line_table
, colon
);
4547 richloc
.add_fixit_insert_before (y
, ".");
4548 richloc
.add_fixit_replace (colon
, "=");
4549 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4550 ASSERT_STREQ ("FILENAME:3:24:\n"
4557 pp_formatted_text (dc
.printer
));
4560 /* As above, but verify the behavior of multiple line spans
4561 with line-numbering enabled. */
4564 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 24);
4565 const location_t colon
4566 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 25);
4567 rich_location
richloc (line_table
, colon
);
4568 richloc
.add_fixit_insert_before (y
, ".");
4569 richloc
.add_fixit_replace (colon
, "=");
4570 test_diagnostic_context dc
;
4571 dc
.m_source_printing
.show_line_numbers_p
= true;
4572 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4573 ASSERT_STREQ (" 3 | y\n"
4579 pp_formatted_text (dc
.printer
));
4584 /* Verify that fix-it hints are appropriately consolidated.
4586 If any fix-it hints in a rich_location involve locations beyond
4587 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4588 the fix-it as a whole, so there should be none.
4590 Otherwise, verify that consecutive "replace" and "remove" fix-its
4591 are merged, and that other fix-its remain separate. */
4594 test_fixit_consolidation (const line_table_case
&case_
)
4596 line_table_test
ltt (case_
);
4598 linemap_add (line_table
, LC_ENTER
, false, "test.c", 1);
4600 const location_t c10
= linemap_position_for_column (line_table
, 10);
4601 const location_t c15
= linemap_position_for_column (line_table
, 15);
4602 const location_t c16
= linemap_position_for_column (line_table
, 16);
4603 const location_t c17
= linemap_position_for_column (line_table
, 17);
4604 const location_t c20
= linemap_position_for_column (line_table
, 20);
4605 const location_t c21
= linemap_position_for_column (line_table
, 21);
4606 const location_t caret
= c10
;
4608 /* Insert + insert. */
4610 rich_location
richloc (line_table
, caret
);
4611 richloc
.add_fixit_insert_before (c10
, "foo");
4612 richloc
.add_fixit_insert_before (c15
, "bar");
4614 if (c15
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4615 /* Bogus column info for 2nd fixit, so no fixits. */
4616 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4618 /* They should not have been merged. */
4619 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4622 /* Insert + replace. */
4624 rich_location
richloc (line_table
, caret
);
4625 richloc
.add_fixit_insert_before (c10
, "foo");
4626 richloc
.add_fixit_replace (source_range::from_locations (c15
, c17
),
4629 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4630 /* Bogus column info for 2nd fixit, so no fixits. */
4631 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4633 /* They should not have been merged. */
4634 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4637 /* Replace + non-consecutive insert. */
4639 rich_location
richloc (line_table
, caret
);
4640 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4642 richloc
.add_fixit_insert_before (c17
, "foo");
4644 if (c17
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4645 /* Bogus column info for 2nd fixit, so no fixits. */
4646 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4648 /* They should not have been merged. */
4649 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4652 /* Replace + non-consecutive replace. */
4654 rich_location
richloc (line_table
, caret
);
4655 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4657 richloc
.add_fixit_replace (source_range::from_locations (c17
, c20
),
4660 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4661 /* Bogus column info for 2nd fixit, so no fixits. */
4662 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4664 /* They should not have been merged. */
4665 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4668 /* Replace + consecutive replace. */
4670 rich_location
richloc (line_table
, caret
);
4671 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4673 richloc
.add_fixit_replace (source_range::from_locations (c16
, c20
),
4676 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4677 /* Bogus column info for 2nd fixit, so no fixits. */
4678 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4681 /* They should have been merged into a single "replace". */
4682 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4683 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4684 ASSERT_STREQ ("foobar", hint
->get_string ());
4685 ASSERT_EQ (c10
, hint
->get_start_loc ());
4686 ASSERT_EQ (c21
, hint
->get_next_loc ());
4690 /* Replace + consecutive removal. */
4692 rich_location
richloc (line_table
, caret
);
4693 richloc
.add_fixit_replace (source_range::from_locations (c10
, c15
),
4695 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4697 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4698 /* Bogus column info for 2nd fixit, so no fixits. */
4699 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4702 /* They should have been merged into a single replace, with the
4703 range extended to cover that of the removal. */
4704 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4705 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4706 ASSERT_STREQ ("foo", hint
->get_string ());
4707 ASSERT_EQ (c10
, hint
->get_start_loc ());
4708 ASSERT_EQ (c21
, hint
->get_next_loc ());
4712 /* Consecutive removals. */
4714 rich_location
richloc (line_table
, caret
);
4715 richloc
.add_fixit_remove (source_range::from_locations (c10
, c15
));
4716 richloc
.add_fixit_remove (source_range::from_locations (c16
, c20
));
4718 if (c20
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4719 /* Bogus column info for 2nd fixit, so no fixits. */
4720 ASSERT_EQ (0, richloc
.get_num_fixit_hints ());
4723 /* They should have been merged into a single "replace-with-empty". */
4724 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4725 const fixit_hint
*hint
= richloc
.get_fixit_hint (0);
4726 ASSERT_STREQ ("", hint
->get_string ());
4727 ASSERT_EQ (c10
, hint
->get_start_loc ());
4728 ASSERT_EQ (c21
, hint
->get_next_loc ());
4733 /* Verify that the line_corrections machinery correctly prints
4734 overlapping fixit-hints. */
4737 test_overlapped_fixit_printing (const line_table_case
&case_
)
4739 /* Create a tempfile and write some text to it.
4740 ...000000000111111111122222222223333333333.
4741 ...123456789012345678901234567890123456789. */
4743 = (" foo *f = (foo *)ptr->field;\n");
4744 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4745 line_table_test
ltt (case_
);
4747 const line_map_ordinary
*ord_map
4748 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4749 tmp
.get_filename (), 0));
4751 linemap_line_start (line_table
, 1, 100);
4753 const location_t final_line_end
4754 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 36);
4756 /* Don't attempt to run the tests if column data might be unavailable. */
4757 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4760 /* A test for converting a C-style cast to a C++-style cast. */
4761 const location_t open_paren
4762 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 12);
4763 const location_t close_paren
4764 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 18);
4765 const location_t expr_start
4766 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 19);
4767 const location_t expr_finish
4768 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 28);
4769 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4771 /* Various examples of fix-it hints that aren't themselves consolidated,
4772 but for which the *printing* may need consolidation. */
4774 /* Example where 3 fix-it hints are printed as one. */
4776 test_diagnostic_context dc
;
4777 rich_location
richloc (line_table
, expr
);
4778 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4779 richloc
.add_fixit_replace (close_paren
, "> (");
4780 richloc
.add_fixit_insert_after (")");
4782 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4783 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4785 " -----------------\n"
4786 " const_cast<foo *> (ptr->field)\n",
4787 pp_formatted_text (dc
.printer
));
4789 /* Unit-test the line_corrections machinery. */
4790 char_display_policy
policy (make_policy (dc
, richloc
));
4791 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
4792 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
4793 ASSERT_EQ (column_range (12, 12),
4794 get_affected_range (policy
, hint_0
, CU_BYTES
));
4795 ASSERT_EQ (column_range (12, 12),
4796 get_affected_range (policy
, hint_0
, CU_DISPLAY_COLS
));
4797 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy
, hint_0
));
4798 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
4799 ASSERT_EQ (column_range (18, 18),
4800 get_affected_range (policy
, hint_1
, CU_BYTES
));
4801 ASSERT_EQ (column_range (18, 18),
4802 get_affected_range (policy
, hint_1
, CU_DISPLAY_COLS
));
4803 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy
, hint_1
));
4804 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
4805 ASSERT_EQ (column_range (29, 28),
4806 get_affected_range (policy
, hint_2
, CU_BYTES
));
4807 ASSERT_EQ (column_range (29, 28),
4808 get_affected_range (policy
, hint_2
, CU_DISPLAY_COLS
));
4809 ASSERT_EQ (column_range (29, 29), get_printed_columns (policy
, hint_2
));
4811 /* Add each hint in turn to a line_corrections instance,
4812 and verify that they are consolidated into one correction instance
4814 line_corrections
lc (policy
, tmp
.get_filename (), 1);
4816 /* The first replace hint by itself. */
4817 lc
.add_hint (hint_0
);
4818 ASSERT_EQ (1, lc
.m_corrections
.length ());
4819 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_bytes
);
4820 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
4821 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
4822 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
4824 /* After the second replacement hint, they are printed together
4825 as a replacement (along with the text between them). */
4826 lc
.add_hint (hint_1
);
4827 ASSERT_EQ (1, lc
.m_corrections
.length ());
4828 ASSERT_STREQ ("const_cast<foo *> (", lc
.m_corrections
[0]->m_text
);
4829 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_bytes
);
4830 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
4831 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
4833 /* After the final insertion hint, they are all printed together
4834 as a replacement (along with the text between them). */
4835 lc
.add_hint (hint_2
);
4836 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4837 lc
.m_corrections
[0]->m_text
);
4838 ASSERT_EQ (1, lc
.m_corrections
.length ());
4839 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_bytes
);
4840 ASSERT_EQ (column_range (12, 28), lc
.m_corrections
[0]->m_affected_columns
);
4841 ASSERT_EQ (column_range (12, 41), lc
.m_corrections
[0]->m_printed_columns
);
4844 /* Example where two are consolidated during printing. */
4846 test_diagnostic_context dc
;
4847 rich_location
richloc (line_table
, expr
);
4848 richloc
.add_fixit_replace (open_paren
, "CAST (");
4849 richloc
.add_fixit_replace (close_paren
, ") (");
4850 richloc
.add_fixit_insert_after (")");
4852 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4853 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4858 pp_formatted_text (dc
.printer
));
4861 /* Example where none are consolidated during printing. */
4863 test_diagnostic_context dc
;
4864 rich_location
richloc (line_table
, expr
);
4865 richloc
.add_fixit_replace (open_paren
, "CST (");
4866 richloc
.add_fixit_replace (close_paren
, ") (");
4867 richloc
.add_fixit_insert_after (")");
4869 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4870 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4875 pp_formatted_text (dc
.printer
));
4878 /* Example of deletion fix-it hints. */
4880 test_diagnostic_context dc
;
4881 rich_location
richloc (line_table
, expr
);
4882 richloc
.add_fixit_insert_before (open_paren
, "(bar *)");
4883 source_range victim
= {open_paren
, close_paren
};
4884 richloc
.add_fixit_remove (victim
);
4886 /* This case is actually handled by fixit-consolidation,
4887 rather than by line_corrections. */
4888 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
4890 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4891 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4895 pp_formatted_text (dc
.printer
));
4898 /* Example of deletion fix-it hints that would overlap. */
4900 test_diagnostic_context dc
;
4901 rich_location
richloc (line_table
, expr
);
4902 richloc
.add_fixit_insert_before (open_paren
, "(longer *)");
4903 source_range victim
= {expr_start
, expr_finish
};
4904 richloc
.add_fixit_remove (victim
);
4906 /* These fixits are not consolidated. */
4907 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
4909 /* But the corrections are. */
4910 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4911 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4913 " -----------------\n"
4914 " (longer *)(foo *)\n",
4915 pp_formatted_text (dc
.printer
));
4918 /* Example of insertion fix-it hints that would overlap. */
4920 test_diagnostic_context dc
;
4921 rich_location
richloc (line_table
, expr
);
4922 richloc
.add_fixit_insert_before (open_paren
, "LONGER THAN THE CAST");
4923 richloc
.add_fixit_insert_after (close_paren
, "TEST");
4925 /* The first insertion is long enough that if printed naively,
4926 it would overlap with the second.
4927 Verify that they are printed as a single replacement. */
4928 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4929 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4932 " LONGER THAN THE CAST(foo *)TEST\n",
4933 pp_formatted_text (dc
.printer
));
4937 /* Multibyte-aware version of preceding tests. See comments above
4938 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4942 test_overlapped_fixit_printing_utf8 (const line_table_case
&case_
)
4944 /* Create a tempfile and write some text to it. */
4948 00000000000000000000000111111111111111111111111222222222222222223
4949 12344444444555555556789012344444444555555556789012345678999999990 */
4950 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4951 /* 00000000000000000000011111111111111111111112222222222333333333333
4952 12344445555666677778901234566667777888899990123456789012333344445
4955 temp_source_file
tmp (SELFTEST_LOCATION
, ".C", content
);
4956 line_table_test
ltt (case_
);
4958 const line_map_ordinary
*ord_map
4959 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
4960 tmp
.get_filename (), 0));
4962 linemap_line_start (line_table
, 1, 100);
4964 const location_t final_line_end
4965 = linemap_position_for_line_and_column (line_table
, ord_map
, 6, 50);
4967 /* Don't attempt to run the tests if column data might be unavailable. */
4968 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4971 /* A test for converting a C-style cast to a C++-style cast. */
4972 const location_t open_paren
4973 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 14);
4974 const location_t close_paren
4975 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 22);
4976 const location_t expr_start
4977 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
4978 const location_t expr_finish
4979 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 34);
4980 const location_t expr
= make_location (expr_start
, expr_start
, expr_finish
);
4982 /* Various examples of fix-it hints that aren't themselves consolidated,
4983 but for which the *printing* may need consolidation. */
4985 /* Example where 3 fix-it hints are printed as one. */
4987 test_diagnostic_context dc
;
4988 rich_location
richloc (line_table
, expr
);
4989 richloc
.add_fixit_replace (open_paren
, "const_cast<");
4990 richloc
.add_fixit_replace (close_paren
, "> (");
4991 richloc
.add_fixit_insert_after (")");
4993 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
4994 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4995 " *f = (f\xf0\x9f\x98\x82"
4996 " *)ptr->field\xcf\x80"
4999 " ------------------\n"
5000 " const_cast<f\xf0\x9f\x98\x82"
5001 " *> (ptr->field\xcf\x80"
5003 pp_formatted_text (dc
.printer
));
5005 /* Unit-test the line_corrections machinery. */
5006 char_display_policy
policy (make_policy (dc
, richloc
));
5007 ASSERT_EQ (3, richloc
.get_num_fixit_hints ());
5008 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
5009 ASSERT_EQ (column_range (14, 14),
5010 get_affected_range (policy
, hint_0
, CU_BYTES
));
5011 ASSERT_EQ (column_range (12, 12),
5012 get_affected_range (policy
, hint_0
, CU_DISPLAY_COLS
));
5013 ASSERT_EQ (column_range (12, 22), get_printed_columns (policy
, hint_0
));
5014 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
5015 ASSERT_EQ (column_range (22, 22),
5016 get_affected_range (policy
, hint_1
, CU_BYTES
));
5017 ASSERT_EQ (column_range (18, 18),
5018 get_affected_range (policy
, hint_1
, CU_DISPLAY_COLS
));
5019 ASSERT_EQ (column_range (18, 20), get_printed_columns (policy
, hint_1
));
5020 const fixit_hint
*hint_2
= richloc
.get_fixit_hint (2);
5021 ASSERT_EQ (column_range (35, 34),
5022 get_affected_range (policy
, hint_2
, CU_BYTES
));
5023 ASSERT_EQ (column_range (30, 29),
5024 get_affected_range (policy
, hint_2
, CU_DISPLAY_COLS
));
5025 ASSERT_EQ (column_range (30, 30), get_printed_columns (policy
, hint_2
));
5027 /* Add each hint in turn to a line_corrections instance,
5028 and verify that they are consolidated into one correction instance
5030 line_corrections
lc (policy
, tmp
.get_filename (), 1);
5032 /* The first replace hint by itself. */
5033 lc
.add_hint (hint_0
);
5034 ASSERT_EQ (1, lc
.m_corrections
.length ());
5035 ASSERT_EQ (column_range (14, 14), lc
.m_corrections
[0]->m_affected_bytes
);
5036 ASSERT_EQ (column_range (12, 12), lc
.m_corrections
[0]->m_affected_columns
);
5037 ASSERT_EQ (column_range (12, 22), lc
.m_corrections
[0]->m_printed_columns
);
5038 ASSERT_STREQ ("const_cast<", lc
.m_corrections
[0]->m_text
);
5040 /* After the second replacement hint, they are printed together
5041 as a replacement (along with the text between them). */
5042 lc
.add_hint (hint_1
);
5043 ASSERT_EQ (1, lc
.m_corrections
.length ());
5044 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5045 lc
.m_corrections
[0]->m_text
);
5046 ASSERT_EQ (column_range (14, 22), lc
.m_corrections
[0]->m_affected_bytes
);
5047 ASSERT_EQ (column_range (12, 18), lc
.m_corrections
[0]->m_affected_columns
);
5048 ASSERT_EQ (column_range (12, 30), lc
.m_corrections
[0]->m_printed_columns
);
5050 /* After the final insertion hint, they are all printed together
5051 as a replacement (along with the text between them). */
5052 lc
.add_hint (hint_2
);
5053 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5054 lc
.m_corrections
[0]->m_text
);
5055 ASSERT_EQ (1, lc
.m_corrections
.length ());
5056 ASSERT_EQ (column_range (14, 34), lc
.m_corrections
[0]->m_affected_bytes
);
5057 ASSERT_EQ (column_range (12, 29), lc
.m_corrections
[0]->m_affected_columns
);
5058 ASSERT_EQ (column_range (12, 42), lc
.m_corrections
[0]->m_printed_columns
);
5061 /* Example where two are consolidated during printing. */
5063 test_diagnostic_context dc
;
5064 rich_location
richloc (line_table
, expr
);
5065 richloc
.add_fixit_replace (open_paren
, "CAST (");
5066 richloc
.add_fixit_replace (close_paren
, ") (");
5067 richloc
.add_fixit_insert_after (")");
5069 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5070 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5071 " *f = (f\xf0\x9f\x98\x82"
5072 " *)ptr->field\xcf\x80"
5078 pp_formatted_text (dc
.printer
));
5081 /* Example where none are consolidated during printing. */
5083 test_diagnostic_context dc
;
5084 rich_location
richloc (line_table
, expr
);
5085 richloc
.add_fixit_replace (open_paren
, "CST (");
5086 richloc
.add_fixit_replace (close_paren
, ") (");
5087 richloc
.add_fixit_insert_after (")");
5089 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5090 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5091 " *f = (f\xf0\x9f\x98\x82"
5092 " *)ptr->field\xcf\x80"
5098 pp_formatted_text (dc
.printer
));
5101 /* Example of deletion fix-it hints. */
5103 test_diagnostic_context dc
;
5104 rich_location
richloc (line_table
, expr
);
5105 richloc
.add_fixit_insert_before (open_paren
, "(bar\xf0\x9f\x98\x82 *)");
5106 source_range victim
= {open_paren
, close_paren
};
5107 richloc
.add_fixit_remove (victim
);
5109 /* This case is actually handled by fixit-consolidation,
5110 rather than by line_corrections. */
5111 ASSERT_EQ (1, richloc
.get_num_fixit_hints ());
5113 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5114 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5115 " *f = (f\xf0\x9f\x98\x82"
5116 " *)ptr->field\xcf\x80"
5120 " (bar\xf0\x9f\x98\x82"
5122 pp_formatted_text (dc
.printer
));
5125 /* Example of deletion fix-it hints that would overlap. */
5127 test_diagnostic_context dc
;
5128 rich_location
richloc (line_table
, expr
);
5129 richloc
.add_fixit_insert_before (open_paren
, "(long\xf0\x9f\x98\x82 *)");
5130 source_range victim
= {expr_start
, expr_finish
};
5131 richloc
.add_fixit_remove (victim
);
5133 /* These fixits are not consolidated. */
5134 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5136 /* But the corrections are. */
5137 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5138 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5139 " *f = (f\xf0\x9f\x98\x82"
5140 " *)ptr->field\xcf\x80"
5143 " ------------------\n"
5144 " (long\xf0\x9f\x98\x82"
5145 " *)(f\xf0\x9f\x98\x82"
5147 pp_formatted_text (dc
.printer
));
5150 /* Example of insertion fix-it hints that would overlap. */
5152 test_diagnostic_context dc
;
5153 rich_location
richloc (line_table
, expr
);
5154 richloc
.add_fixit_insert_before
5155 (open_paren
, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5156 richloc
.add_fixit_insert_after (close_paren
, "TEST");
5158 /* The first insertion is long enough that if printed naively,
5159 it would overlap with the second.
5160 Verify that they are printed as a single replacement. */
5161 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5162 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5163 " *f = (f\xf0\x9f\x98\x82"
5164 " *)ptr->field\xcf\x80"
5168 " L\xf0\x9f\x98\x82"
5169 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5171 pp_formatted_text (dc
.printer
));
5175 /* Verify that the line_corrections machinery correctly prints
5176 overlapping fixit-hints that have been added in the wrong
5178 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5181 test_overlapped_fixit_printing_2 (const line_table_case
&case_
)
5183 /* Create a tempfile and write some text to it.
5184 ...000000000111111111122222222223333333333.
5185 ...123456789012345678901234567890123456789. */
5187 = ("int a5[][0][0] = { 1, 2 };\n");
5188 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5189 line_table_test
ltt (case_
);
5191 const line_map_ordinary
*ord_map
5192 = linemap_check_ordinary (linemap_add (line_table
, LC_ENTER
, false,
5193 tmp
.get_filename (), 0));
5195 linemap_line_start (line_table
, 1, 100);
5197 const location_t final_line_end
5198 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 100);
5200 /* Don't attempt to run the tests if column data might be unavailable. */
5201 if (final_line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5204 const location_t col_1
5205 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5206 const location_t col_20
5207 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 20);
5208 const location_t col_21
5209 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 21);
5210 const location_t col_23
5211 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 23);
5212 const location_t col_25
5213 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 25);
5215 /* Two insertions, in the wrong order. */
5217 test_diagnostic_context dc
;
5219 rich_location
richloc (line_table
, col_20
);
5220 richloc
.add_fixit_insert_before (col_23
, "{");
5221 richloc
.add_fixit_insert_before (col_21
, "}");
5223 /* These fixits should be accepted; they can't be consolidated. */
5224 char_display_policy
policy (make_policy (dc
, richloc
));
5225 ASSERT_EQ (2, richloc
.get_num_fixit_hints ());
5226 const fixit_hint
*hint_0
= richloc
.get_fixit_hint (0);
5227 ASSERT_EQ (column_range (23, 22),
5228 get_affected_range (policy
, hint_0
, CU_BYTES
));
5229 ASSERT_EQ (column_range (23, 23), get_printed_columns (policy
, hint_0
));
5230 const fixit_hint
*hint_1
= richloc
.get_fixit_hint (1);
5231 ASSERT_EQ (column_range (21, 20),
5232 get_affected_range (policy
, hint_1
, CU_BYTES
));
5233 ASSERT_EQ (column_range (21, 21), get_printed_columns (policy
, hint_1
));
5235 /* Verify that they're printed correctly. */
5236 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5237 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5240 pp_formatted_text (dc
.printer
));
5243 /* Various overlapping insertions, some occurring "out of order"
5244 (reproducing the fix-it hints from PR c/81405). */
5246 test_diagnostic_context dc
;
5247 rich_location
richloc (line_table
, col_20
);
5249 richloc
.add_fixit_insert_before (col_20
, "{{");
5250 richloc
.add_fixit_insert_before (col_21
, "}}");
5251 richloc
.add_fixit_insert_before (col_23
, "{");
5252 richloc
.add_fixit_insert_before (col_21
, "}");
5253 richloc
.add_fixit_insert_before (col_23
, "{{");
5254 richloc
.add_fixit_insert_before (col_25
, "}");
5255 richloc
.add_fixit_insert_before (col_21
, "}");
5256 richloc
.add_fixit_insert_before (col_1
, "{");
5257 richloc
.add_fixit_insert_before (col_25
, "}");
5258 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5259 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5262 " {{1}}}}, {{{2 }}\n",
5263 pp_formatted_text (dc
.printer
));
5267 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5270 test_fixit_insert_containing_newline (const line_table_case
&case_
)
5272 /* Create a tempfile and write some text to it.
5273 .........................0000000001111111.
5274 .........................1234567890123456. */
5275 const char *old_content
= (" case 'a':\n" /* line 1. */
5276 " x = a;\n" /* line 2. */
5277 " case 'b':\n" /* line 3. */
5278 " x = b;\n");/* line 4. */
5280 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5281 line_table_test
ltt (case_
);
5282 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 3);
5284 location_t case_start
= linemap_position_for_column (line_table
, 5);
5285 location_t case_finish
= linemap_position_for_column (line_table
, 13);
5286 location_t case_loc
= make_location (case_start
, case_start
, case_finish
);
5287 location_t line_start
= linemap_position_for_column (line_table
, 1);
5289 if (case_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5292 /* Add a "break;" on a line by itself before line 3 i.e. before
5293 column 1 of line 3. */
5295 rich_location
richloc (line_table
, case_loc
);
5296 richloc
.add_fixit_insert_before (line_start
, " break;\n");
5298 /* Without line numbers. */
5300 test_diagnostic_context dc
;
5301 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5302 ASSERT_STREQ (" x = a;\n"
5306 pp_formatted_text (dc
.printer
));
5309 /* With line numbers. */
5311 test_diagnostic_context dc
;
5312 dc
.m_source_printing
.show_line_numbers_p
= true;
5313 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5314 ASSERT_STREQ (" 2 | x = a;\n"
5318 pp_formatted_text (dc
.printer
));
5322 /* Verify that attempts to add text with a newline fail when the
5323 insertion point is *not* at the start of a line. */
5325 rich_location
richloc (line_table
, case_loc
);
5326 richloc
.add_fixit_insert_before (case_start
, "break;\n");
5327 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5328 test_diagnostic_context dc
;
5329 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5330 ASSERT_STREQ (" case 'b':\n"
5332 pp_formatted_text (dc
.printer
));
5336 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5337 of the file, where the fix-it is printed in a different line-span
5338 to the primary range of the diagnostic. */
5341 test_fixit_insert_containing_newline_2 (const line_table_case
&case_
)
5343 /* Create a tempfile and write some text to it.
5344 .........................0000000001111111.
5345 .........................1234567890123456. */
5346 const char *old_content
= ("test (int ch)\n" /* line 1. */
5348 " putchar (ch);\n" /* line 3. */
5349 "}\n"); /* line 4. */
5351 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5352 line_table_test
ltt (case_
);
5354 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5355 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5356 linemap_line_start (line_table
, 1, 100);
5358 /* The primary range is the "putchar" token. */
5359 location_t putchar_start
5360 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 2);
5361 location_t putchar_finish
5362 = linemap_position_for_line_and_column (line_table
, ord_map
, 3, 8);
5363 location_t putchar_loc
5364 = make_location (putchar_start
, putchar_start
, putchar_finish
);
5365 rich_location
richloc (line_table
, putchar_loc
);
5367 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5368 location_t file_start
5369 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5370 richloc
.add_fixit_insert_before (file_start
, "#include <stdio.h>\n");
5372 if (putchar_finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5376 test_diagnostic_context dc
;
5377 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5378 ASSERT_STREQ ("FILENAME:1:1:\n"
5379 "+#include <stdio.h>\n"
5384 pp_formatted_text (dc
.printer
));
5387 /* With line-numbering, the line spans are close enough to be
5388 consolidated, since it makes little sense to skip line 2. */
5390 test_diagnostic_context dc
;
5391 dc
.m_source_printing
.show_line_numbers_p
= true;
5392 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5393 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5394 " 1 | test (int ch)\n"
5396 " 3 | putchar (ch);\n"
5398 pp_formatted_text (dc
.printer
));
5402 /* Replacement fix-it hint containing a newline.
5403 This will fail, as newlines are only supported when inserting at the
5404 beginning of a line. */
5407 test_fixit_replace_containing_newline (const line_table_case
&case_
)
5409 /* Create a tempfile and write some text to it.
5410 .........................0000000001111.
5411 .........................1234567890123. */
5412 const char *old_content
= "foo = bar ();\n";
5414 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5415 line_table_test
ltt (case_
);
5416 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5418 /* Replace the " = " with "\n = ", as if we were reformatting an
5419 overly long line. */
5420 location_t start
= linemap_position_for_column (line_table
, 4);
5421 location_t finish
= linemap_position_for_column (line_table
, 6);
5422 location_t loc
= linemap_position_for_column (line_table
, 13);
5423 rich_location
richloc (line_table
, loc
);
5424 source_range range
= source_range::from_locations (start
, finish
);
5425 richloc
.add_fixit_replace (range
, "\n =");
5427 /* Arbitrary newlines are not yet supported within fix-it hints, so
5428 the fix-it should not be displayed. */
5429 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5431 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5434 test_diagnostic_context dc
;
5435 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5436 ASSERT_STREQ (" foo = bar ();\n"
5438 pp_formatted_text (dc
.printer
));
5441 /* Fix-it hint, attempting to delete a newline.
5442 This will fail, as we currently only support fix-it hints that
5443 affect one line at a time. */
5446 test_fixit_deletion_affecting_newline (const line_table_case
&case_
)
5448 /* Create a tempfile and write some text to it.
5449 ..........................0000000001111.
5450 ..........................1234567890123. */
5451 const char *old_content
= ("foo = bar (\n"
5454 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", old_content
);
5455 line_table_test
ltt (case_
);
5456 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5457 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5458 linemap_line_start (line_table
, 1, 100);
5460 /* Attempt to delete the " (\n...)". */
5462 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 10);
5464 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
5466 = linemap_position_for_line_and_column (line_table
, ord_map
, 2, 7);
5467 location_t loc
= make_location (caret
, start
, finish
);
5468 rich_location
richloc (line_table
, loc
);
5469 richloc
. add_fixit_remove ();
5471 /* Fix-it hints that affect more than one line are not yet supported, so
5472 the fix-it should not be displayed. */
5473 ASSERT_TRUE (richloc
.seen_impossible_fixit_p ());
5475 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5478 test_diagnostic_context dc
;
5479 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5480 ASSERT_STREQ (" foo = bar (\n"
5484 pp_formatted_text (dc
.printer
));
5488 test_tab_expansion (const line_table_case
&case_
)
5490 /* Create a tempfile and write some text to it. This example uses a tabstop
5491 of 8, as the column numbers attempt to indicate:
5493 .....................000.01111111111.22222333333 display
5494 .....................123.90123456789.56789012345 columns */
5495 const char *content
= " \t This: `\t' is a tab.\n";
5496 /* ....................000 00000011111 11111222222 byte
5497 ....................123 45678901234 56789012345 columns */
5499 const int tabstop
= 8;
5500 cpp_char_column_policy
policy (tabstop
, cpp_wcwidth
);
5501 const int first_non_ws_byte_col
= 7;
5502 const int right_quote_byte_col
= 15;
5503 const int last_byte_col
= 25;
5504 ASSERT_EQ (35, cpp_display_width (content
, last_byte_col
, policy
));
5506 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
);
5507 line_table_test
ltt (case_
);
5508 linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 1);
5510 /* Don't attempt to run the tests if column data might be unavailable. */
5511 location_t line_end
= linemap_position_for_column (line_table
, last_byte_col
);
5512 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5515 /* Check that the leading whitespace with mixed tabs and spaces is expanded
5516 into 11 spaces. Recall that print_line() also puts one space before
5519 test_diagnostic_context dc
;
5520 dc
.tabstop
= tabstop
;
5521 rich_location
richloc (line_table
,
5522 linemap_position_for_column (line_table
,
5523 first_non_ws_byte_col
));
5524 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5525 test_layout
.print_line (1);
5526 ASSERT_STREQ (" This: ` ' is a tab.\n"
5528 pp_formatted_text (dc
.printer
));
5531 /* Confirm the display width was tracked correctly across the internal tab
5534 test_diagnostic_context dc
;
5535 dc
.tabstop
= tabstop
;
5536 rich_location
richloc (line_table
,
5537 linemap_position_for_column (line_table
,
5538 right_quote_byte_col
));
5539 layout
test_layout (&dc
, &richloc
, DK_ERROR
);
5540 test_layout
.print_line (1);
5541 ASSERT_STREQ (" This: ` ' is a tab.\n"
5543 pp_formatted_text (dc
.printer
));
5547 /* Verify that the escaping machinery can cope with a variety of different
5551 test_escaping_bytes_1 (const line_table_case
&case_
)
5553 const char content
[] = "before\0\1\2\3\v\x80\xff""after\n";
5554 const size_t sz
= sizeof (content
);
5555 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
, sz
);
5556 line_table_test
ltt (case_
);
5557 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5558 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5559 linemap_line_start (line_table
, 1, 100);
5562 = linemap_position_for_line_and_column (line_table
, ord_map
, 1,
5565 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5568 /* Locations of the NUL and \v bytes. */
5570 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 7);
5572 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 11);
5573 gcc_rich_location
richloc (nul_loc
);
5574 richloc
.add_range (v_loc
);
5577 test_diagnostic_context dc
;
5578 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5579 ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
5581 pp_formatted_text (dc
.printer
));
5583 richloc
.set_escape_on_output (true);
5585 test_diagnostic_context dc
;
5586 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
;
5587 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5589 (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
5590 " ^~~~~~~~ ~~~~~~~~\n",
5591 pp_formatted_text (dc
.printer
));
5594 test_diagnostic_context dc
;
5595 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_BYTES
;
5596 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5597 ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
5599 pp_formatted_text (dc
.printer
));
5603 /* As above, but verify that we handle the initial byte of a line
5607 test_escaping_bytes_2 (const line_table_case
&case_
)
5609 const char content
[] = "\0after\n";
5610 const size_t sz
= sizeof (content
);
5611 temp_source_file
tmp (SELFTEST_LOCATION
, ".c", content
, sz
);
5612 line_table_test
ltt (case_
);
5613 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5614 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5615 linemap_line_start (line_table
, 1, 100);
5618 = linemap_position_for_line_and_column (line_table
, ord_map
, 1,
5621 if (finish
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
5624 /* Location of the NUL byte. */
5626 = linemap_position_for_line_and_column (line_table
, ord_map
, 1, 1);
5627 gcc_rich_location
richloc (nul_loc
);
5630 test_diagnostic_context dc
;
5631 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5632 ASSERT_STREQ (" after\n"
5634 pp_formatted_text (dc
.printer
));
5636 richloc
.set_escape_on_output (true);
5638 test_diagnostic_context dc
;
5639 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_UNICODE
;
5640 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5641 ASSERT_STREQ (" <U+0000>after\n"
5643 pp_formatted_text (dc
.printer
));
5646 test_diagnostic_context dc
;
5647 dc
.escape_format
= DIAGNOSTICS_ESCAPE_FORMAT_BYTES
;
5648 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5649 ASSERT_STREQ (" <00>after\n"
5651 pp_formatted_text (dc
.printer
));
5655 /* Verify that line numbers are correctly printed for the case of
5656 a multiline range in which the width of the line numbers changes
5657 (e.g. from "9" to "10"). */
5660 test_line_numbers_multiline_range ()
5662 /* Create a tempfile and write some text to it. */
5664 for (int i
= 0; i
< 20; i
++)
5665 /* .........0000000001111111.
5666 .............1234567890123456. */
5667 pp_printf (&pp
, "this is line %i\n", i
+ 1);
5668 temp_source_file
tmp (SELFTEST_LOCATION
, ".txt", pp_formatted_text (&pp
));
5669 line_table_test ltt
;
5671 const line_map_ordinary
*ord_map
= linemap_check_ordinary
5672 (linemap_add (line_table
, LC_ENTER
, false, tmp
.get_filename (), 0));
5673 linemap_line_start (line_table
, 1, 100);
5675 /* Create a multi-line location, starting at the "line" of line 9, with
5676 a caret on the "is" of line 10, finishing on the "this" line 11. */
5679 = linemap_position_for_line_and_column (line_table
, ord_map
, 9, 9);
5681 = linemap_position_for_line_and_column (line_table
, ord_map
, 10, 6);
5683 = linemap_position_for_line_and_column (line_table
, ord_map
, 11, 4);
5684 location_t loc
= make_location (caret
, start
, finish
);
5686 test_diagnostic_context dc
;
5687 dc
.m_source_printing
.show_line_numbers_p
= true;
5688 dc
.m_source_printing
.min_margin_width
= 0;
5689 gcc_rich_location
richloc (loc
);
5690 diagnostic_show_locus (&dc
, &richloc
, DK_ERROR
);
5691 ASSERT_STREQ (" 9 | this is line 9\n"
5693 "10 | this is line 10\n"
5694 " | ~~~~~^~~~~~~~~~\n"
5695 "11 | this is line 11\n"
5697 pp_formatted_text (dc
.printer
));
5700 /* Run all of the selftests within this file. */
5703 diagnostic_show_locus_cc_tests ()
5707 test_layout_range_for_single_point ();
5708 test_layout_range_for_single_line ();
5709 test_layout_range_for_multiple_lines ();
5711 test_display_widths ();
5713 for_each_line_table_case (test_layout_x_offset_display_utf8
);
5714 for_each_line_table_case (test_layout_x_offset_display_tab
);
5716 test_get_line_bytes_without_trailing_whitespace ();
5718 test_diagnostic_show_locus_unknown_location ();
5720 for_each_line_table_case (test_diagnostic_show_locus_one_liner
);
5721 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8
);
5722 for_each_line_table_case (test_add_location_if_nearby
);
5723 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines
);
5724 for_each_line_table_case (test_fixit_consolidation
);
5725 for_each_line_table_case (test_overlapped_fixit_printing
);
5726 for_each_line_table_case (test_overlapped_fixit_printing_utf8
);
5727 for_each_line_table_case (test_overlapped_fixit_printing_2
);
5728 for_each_line_table_case (test_fixit_insert_containing_newline
);
5729 for_each_line_table_case (test_fixit_insert_containing_newline_2
);
5730 for_each_line_table_case (test_fixit_replace_containing_newline
);
5731 for_each_line_table_case (test_fixit_deletion_affecting_newline
);
5732 for_each_line_table_case (test_tab_expansion
);
5733 for_each_line_table_case (test_escaping_bytes_1
);
5734 for_each_line_table_case (test_escaping_bytes_2
);
5736 test_line_numbers_multiline_range ();
5739 } // namespace selftest
5741 #endif /* #if CHECKING_P */
5744 # pragma GCC diagnostic pop