Branch data Line data Source code
1 : : /* Diagnostic subroutines for printing source-code
2 : : Copyright (C) 1999-2025 Free Software Foundation, Inc.
3 : : Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "config.h"
22 : : #define INCLUDE_VECTOR
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "version.h"
26 : : #include "demangle.h"
27 : : #include "intl.h"
28 : : #include "backtrace.h"
29 : : #include "diagnostic.h"
30 : : #include "diagnostic-color.h"
31 : : #include "gcc-rich-location.h"
32 : : #include "text-range-label.h"
33 : : #include "selftest.h"
34 : : #include "selftest-diagnostic.h"
35 : : #include "selftest-diagnostic-show-locus.h"
36 : : #include "cpplib.h"
37 : : #include "text-art/types.h"
38 : : #include "text-art/theme.h"
39 : : #include "diagnostic-label-effects.h"
40 : :
41 : : #ifdef HAVE_TERMIOS_H
42 : : # include <termios.h>
43 : : #endif
44 : :
45 : : #ifdef GWINSZ_IN_SYS_IOCTL
46 : : # include <sys/ioctl.h>
47 : : #endif
48 : :
49 : : /* Disable warnings about quoting issues in the pp_xxx calls below
50 : : that (intentionally) don't follow GCC diagnostic conventions. */
51 : : #if __GNUC__ >= 10
52 : : # pragma GCC diagnostic push
53 : : # pragma GCC diagnostic ignored "-Wformat-diag"
54 : : #endif
55 : :
56 : : /* Classes for rendering source code and diagnostics, within an
57 : : anonymous namespace.
58 : : The work is done by "class layout", which embeds and uses
59 : : "class colorizer" and "class layout_range" to get things done. */
60 : :
61 : : namespace {
62 : :
63 : : /* The state at a given point of the source code, assuming that we're
64 : : in a range: which range are we in, and whether we should draw a caret at
65 : : this point. */
66 : :
67 : : struct point_state
68 : : {
69 : : int range_idx;
70 : : bool draw_caret_p;
71 : : };
72 : :
73 : : /* A class to inject colorization codes when printing the diagnostic locus.
74 : :
75 : : It has one kind of colorization for each of:
76 : : - normal text
77 : : - range 0 (the "primary location")
78 : : - range 1
79 : : - range 2
80 : :
81 : : The class caches the lookup of the color codes for the above.
82 : :
83 : : The class also has responsibility for tracking which of the above is
84 : : active, filtering out unnecessary changes. This allows
85 : : layout_printer::print_source_line and layout_printer::print_annotation_line
86 : : to simply request a colorization code for *every* character they print,
87 : : via this class, and have the filtering be done for them here. */
88 : :
89 : : class colorizer
90 : : {
91 : : public:
92 : : colorizer (pretty_printer &pp,
93 : : const rich_location &richloc,
94 : : diagnostic_t diagnostic_kind);
95 : : ~colorizer ();
96 : :
97 : 213163 : void set_range (int range_idx)
98 : : {
99 : : /* If we have a specific highlight color for the range, use it. */
100 : 213163 : if (pp_show_highlight_colors (&m_pp))
101 : : {
102 : 136497 : const location_range *const loc_range = m_richloc.get_range (range_idx);
103 : 136497 : if (loc_range->m_highlight_color)
104 : : {
105 : 17701 : set_named_color (loc_range->m_highlight_color);
106 : 17701 : return;
107 : : }
108 : : }
109 : :
110 : : /* Otherwise, we emphasize the primary location, then alternate between
111 : : two colors for the secondary locations.
112 : : But if we're printing a run of events in a diagnostic path, that
113 : : makes no sense, so print all of them with the same colorization. */
114 : 195462 : if (m_diagnostic_kind == DK_DIAGNOSTIC_PATH)
115 : 37156 : set_state (0);
116 : : else
117 : 158306 : set_state (range_idx);
118 : : }
119 : 13116 : void set_cfg_edge () { set_state (0); }
120 : 13837791 : void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
121 : 11093 : void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
122 : 2394 : void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
123 : : void set_named_color (const char *color);
124 : :
125 : : private:
126 : : void set_state (int state);
127 : : void begin_state (int state);
128 : : void finish_state (int state);
129 : : const char *get_color_by_name (const char *);
130 : :
131 : : private:
132 : : static const int STATE_NORMAL_TEXT = -1;
133 : : static const int STATE_FIXIT_INSERT = -2;
134 : : static const int STATE_FIXIT_DELETE = -3;
135 : : static const int STATE_NAMED_COLOR = -4;
136 : :
137 : : pretty_printer &m_pp;
138 : : const rich_location &m_richloc;
139 : : diagnostic_t m_diagnostic_kind;
140 : : int m_current_state;
141 : : const char *m_range1;
142 : : const char *m_range2;
143 : : const char *m_fixit_insert;
144 : : const char *m_fixit_delete;
145 : : const char *m_stop_color;
146 : : };
147 : :
148 : : /* In order to handle multibyte sources properly, all of this logic needs to be
149 : : aware of the distinction between the number of bytes and the number of
150 : : display columns occupied by a character, which are not the same for non-ASCII
151 : : characters. For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
152 : : as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
153 : : display column when it is output. A typical emoji, such as U+1F602 (in
154 : : UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
155 : :
156 : : The below example line, which is also used for selftests below, shows how the
157 : : display column and byte column are related:
158 : :
159 : : 0000000001111111111222222 display
160 : : 1234567890123456789012345 columns
161 : : SS_foo = P_bar.SS_fieldP;
162 : : 0000000111111111222222223 byte
163 : : 1356789012456789134567891 columns
164 : :
165 : : Here SS represents the two display columns for the U+1F602 emoji, and P
166 : : represents the one display column for the U+03C0 pi symbol. As an example, a
167 : : diagnostic pointing to the final P on this line is at byte column 29 and
168 : : display column 24. This reflects the fact that the three extended characters
169 : : before the final P occupy cumulatively 5 more bytes than they do display
170 : : columns (a difference of 2 for each of the two SSs, and one for the other P).
171 : :
172 : : One or the other of the two column units is more useful depending on the
173 : : context. For instance, in order to output the caret at the correct location,
174 : : we need to count display columns; in order to colorize a source line, we need
175 : : to count the bytes. All locations are provided to us as byte counts, which
176 : : we augment with the display column on demand so that it can be used when
177 : : needed. This is not the most efficient way to do things since it requires
178 : : looping over the whole line each time, but it should be fine for the purpose
179 : : of outputting diagnostics.
180 : :
181 : : In order to keep straight which units (byte or display) are in use at a
182 : : given time, the following enum lets us specify that explicitly. */
183 : :
184 : : enum column_unit {
185 : : /* Measured in raw bytes. */
186 : : CU_BYTES = 0,
187 : :
188 : : /* Measured in display units. */
189 : : CU_DISPLAY_COLS,
190 : :
191 : : /* For arrays indexed by column_unit. */
192 : : CU_NUM_UNITS
193 : : };
194 : :
195 : : /* Utility class to augment an exploc with the corresponding display column. */
196 : :
197 : : class exploc_with_display_col : public expanded_location
198 : : {
199 : : public:
200 : 231823 : exploc_with_display_col (file_cache &fc,
201 : : const expanded_location &exploc,
202 : : const cpp_char_column_policy &policy,
203 : : enum location_aspect aspect)
204 : 231823 : : expanded_location (exploc),
205 : 231823 : m_display_col (location_compute_display_column (fc, exploc, policy))
206 : : {
207 : 231823 : if (exploc.column > 0)
208 : : {
209 : : /* m_display_col is now the final column of the byte.
210 : : If escaping has happened, we may want the first column instead. */
211 : 207663 : if (aspect != LOCATION_ASPECT_FINISH)
212 : : {
213 : 152633 : expanded_location prev_exploc (exploc);
214 : 152633 : prev_exploc.column--;
215 : 152633 : int prev_display_col
216 : 152633 : = (location_compute_display_column (fc, prev_exploc, policy));
217 : 152633 : m_display_col = prev_display_col + 1;
218 : : }
219 : : }
220 : 231823 : }
221 : :
222 : : int m_display_col;
223 : : };
224 : :
225 : :
226 : : /* A point within a layout_range; similar to an exploc_with_display_col,
227 : : but after filtering on file. */
228 : :
229 : : class layout_point
230 : : {
231 : : public:
232 : 61067 : layout_point (const exploc_with_display_col &exploc)
233 : 61067 : : m_line (exploc.line)
234 : : {
235 : 61067 : m_columns[CU_BYTES] = exploc.column;
236 : 61067 : m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
237 : : }
238 : :
239 : : linenum_type m_line;
240 : : int m_columns[CU_NUM_UNITS];
241 : : };
242 : :
243 : : /* A class for use by "class layout" below: a filtered location_range. */
244 : :
245 : : class layout_range
246 : : {
247 : : public:
248 : : layout_range (const exploc_with_display_col &start_exploc,
249 : : const exploc_with_display_col &finish_exploc,
250 : : enum range_display_kind range_display_kind,
251 : : const exploc_with_display_col &caret_exploc,
252 : : unsigned original_idx,
253 : : const range_label *label);
254 : :
255 : : bool contains_point (linenum_type row, int column,
256 : : enum column_unit col_unit) const;
257 : : bool intersects_line_p (linenum_type row) const;
258 : :
259 : : bool has_in_edge () const;
260 : : bool has_out_edge () const;
261 : :
262 : : layout_point m_start;
263 : : layout_point m_finish;
264 : : enum range_display_kind m_range_display_kind;
265 : : layout_point m_caret;
266 : : unsigned m_original_idx;
267 : : const range_label *m_label;
268 : : };
269 : :
270 : : /* A struct for use by layout::print_source_line for telling
271 : : layout::print_annotation_line the extents of the source line that
272 : : it printed, so that underlines can be clipped appropriately. Units
273 : : are 1-based display columns. */
274 : :
275 : : struct line_bounds
276 : : {
277 : : int m_first_non_ws_disp_col;
278 : : int m_last_non_ws_disp_col;
279 : :
280 : : line_bounds ()
281 : : {
282 : : m_first_non_ws_disp_col = INT_MAX;
283 : : m_last_non_ws_disp_col = 0;
284 : : }
285 : : };
286 : :
287 : : /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
288 : : or "line 23"). During the layout ctor, layout::calculate_line_spans
289 : : splits the pertinent source lines into a list of disjoint line_span
290 : : instances (e.g. lines 5-10, lines 15-20, line 23). */
291 : :
292 : : class line_span
293 : : {
294 : : public:
295 : 120118 : line_span (linenum_type first_line, linenum_type last_line)
296 : 105828 : : m_first_line (first_line), m_last_line (last_line)
297 : : {
298 : 0 : gcc_assert (first_line <= last_line);
299 : : }
300 : 44015 : linenum_type get_first_line () const { return m_first_line; }
301 : 44015 : linenum_type get_last_line () const { return m_last_line; }
302 : :
303 : 4092 : bool contains_line_p (linenum_type line) const
304 : : {
305 : 3447 : return line >= m_first_line && line <= m_last_line;
306 : : }
307 : :
308 : 332834 : static int comparator (const void *p1, const void *p2)
309 : : {
310 : 332834 : const line_span *ls1 = (const line_span *)p1;
311 : 332834 : const line_span *ls2 = (const line_span *)p2;
312 : 332834 : int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
313 : 332834 : if (first_line_cmp)
314 : 35022 : return first_line_cmp;
315 : 297812 : return compare (ls1->m_last_line, ls2->m_last_line);
316 : : }
317 : :
318 : : linenum_type m_first_line;
319 : : linenum_type m_last_line;
320 : : };
321 : :
322 : : #if CHECKING_P
323 : :
324 : : /* Selftests for line_span. */
325 : :
326 : : static void
327 : 4 : test_line_span ()
328 : : {
329 : 4 : line_span line_one (1, 1);
330 : 4 : ASSERT_EQ (1, line_one.get_first_line ());
331 : 4 : ASSERT_EQ (1, line_one.get_last_line ());
332 : 4 : ASSERT_FALSE (line_one.contains_line_p (0));
333 : 4 : ASSERT_TRUE (line_one.contains_line_p (1));
334 : 4 : ASSERT_FALSE (line_one.contains_line_p (2));
335 : :
336 : 4 : line_span lines_1_to_3 (1, 3);
337 : 4 : ASSERT_EQ (1, lines_1_to_3.get_first_line ());
338 : 4 : ASSERT_EQ (3, lines_1_to_3.get_last_line ());
339 : 4 : ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
340 : 4 : ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
341 : :
342 : 4 : ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
343 : 4 : ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
344 : 4 : ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
345 : :
346 : : /* A linenum > 2^31. */
347 : 4 : const linenum_type LARGEST_LINE = 0xffffffff;
348 : 4 : line_span largest_line (LARGEST_LINE, LARGEST_LINE);
349 : 4 : ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
350 : 4 : ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
351 : :
352 : 4 : ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
353 : 4 : ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
354 : 4 : }
355 : :
356 : : #endif /* #if CHECKING_P */
357 : :
358 : : /* A bundle of information containing how to print unicode
359 : : characters and bytes when quoting source code.
360 : :
361 : : Provides a unified place to support escaping some subset
362 : : of characters to some format.
363 : :
364 : : Extends char_column_policy; printing is split out to avoid
365 : : libcpp having to know about pretty_printer. */
366 : :
367 : : struct char_display_policy : public cpp_char_column_policy
368 : : {
369 : : public:
370 : 48810 : char_display_policy (int tabstop,
371 : : int (*width_cb) (cppchar_t c),
372 : : void (*print_cb) (pretty_printer *pp,
373 : : const cpp_decoded_char &cp))
374 : 48810 : : cpp_char_column_policy (tabstop, width_cb),
375 : 48810 : m_print_cb (print_cb)
376 : : {
377 : : }
378 : :
379 : : void (*m_print_cb) (pretty_printer *pp,
380 : : const cpp_decoded_char &cp);
381 : : };
382 : :
383 : : /* A class to control the overall layout when printing a diagnostic.
384 : :
385 : : The layout is determined within the constructor.
386 : :
387 : : Printing the layout is handled by class layout_printer. This separation
388 : : is to avoid depending on the pretty_printer in the layout.
389 : :
390 : : We assume we have disjoint ranges. */
391 : :
392 : : class layout
393 : : {
394 : : public:
395 : : friend class layout_printer;
396 : :
397 : : layout (const diagnostic_source_print_policy &source_policy,
398 : : const rich_location &richloc,
399 : : diagnostic_source_effect_info *effect_info = nullptr);
400 : :
401 : : bool maybe_add_location_range (const location_range *loc_range,
402 : : unsigned original_idx,
403 : : bool restrict_to_current_line_spans);
404 : :
405 : 180912 : int get_num_line_spans () const { return m_line_spans.length (); }
406 : 131452 : const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
407 : :
408 : 85921 : int get_linenum_width () const { return m_linenum_width; }
409 : 1472 : int get_x_offset_display () const { return m_x_offset_display; }
410 : :
411 : : bool print_heading_for_line_span_index_p (int line_span_idx) const;
412 : :
413 : : expanded_location get_expanded_location (const line_span *) const;
414 : :
415 : : void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
416 : :
417 : : private:
418 : : bool will_show_line_p (linenum_type row) const;
419 : : bool should_print_annotation_line_p (linenum_type row) const;
420 : :
421 : : bool annotation_line_showed_range_p (linenum_type line, int start_column,
422 : : int finish_column) const;
423 : : bool validate_fixit_hint_p (const fixit_hint *hint);
424 : :
425 : : void calculate_line_spans ();
426 : : void calculate_linenum_width ();
427 : : void calculate_x_offset_display ();
428 : :
429 : : bool
430 : : get_state_at_point (/* Inputs. */
431 : : linenum_type row, int column,
432 : : int first_non_ws, int last_non_ws,
433 : : enum column_unit col_unit,
434 : : /* Outputs. */
435 : : point_state *out_state) const;
436 : :
437 : : int
438 : : get_x_bound_for_row (linenum_type row, int caret_column,
439 : : int last_non_ws) const;
440 : :
441 : : private:
442 : : bool compatible_locations_p (location_t loc_a, location_t loc_b) const;
443 : :
444 : : const diagnostic_source_printing_options &m_options;
445 : : const line_maps *m_line_table;
446 : : file_cache &m_file_cache;
447 : : const text_art::ascii_theme m_fallback_theme;
448 : : const text_art::theme &m_theme;
449 : : diagnostic_source_effect_info *m_effect_info;
450 : : char_display_policy m_char_policy;
451 : : location_t m_primary_loc;
452 : : exploc_with_display_col m_exploc;
453 : : auto_vec <layout_range> m_layout_ranges;
454 : : auto_vec <const fixit_hint *> m_fixit_hints;
455 : : auto_vec <line_span> m_line_spans;
456 : : int m_linenum_width;
457 : : int m_x_offset_display;
458 : : bool m_escape_on_output;
459 : : };
460 : :
461 : : /* A bundle of state for printing a particular layout
462 : : to a particular pretty_printer. */
463 : :
464 : 43311 : class layout_printer
465 : : {
466 : : public:
467 : : layout_printer (pretty_printer &pp,
468 : : const layout &layout,
469 : : const rich_location &richloc,
470 : : diagnostic_t diagnostic_kind);
471 : :
472 : : void print (const diagnostic_source_print_policy &source_policy);
473 : :
474 : : private:
475 : : const diagnostic_source_printing_options &
476 : 396124 : get_options () const { return m_layout.m_options; }
477 : :
478 : : const text_art::theme &
479 : 15684 : get_theme () const { return m_layout.m_theme; }
480 : :
481 : : void show_ruler (int max_column);
482 : : void print_gap_in_line_numbering ();
483 : : void print_leading_fixits (linenum_type row);
484 : : void print_line (linenum_type row);
485 : : line_bounds print_source_line (linenum_type row, const char *line,
486 : : int line_bytes);
487 : : void print_leftmost_column ();
488 : : void start_annotation_line (char margin_char = ' ');
489 : : void print_annotation_line (linenum_type row, const line_bounds lbounds);
490 : : void print_any_labels (linenum_type row);
491 : : void print_trailing_fixits (linenum_type row);
492 : : void print_newline ();
493 : :
494 : : void
495 : : move_to_column (int *column, int dest_column, bool add_left_margin);
496 : :
497 : : void print_any_right_to_left_edge_lines ();
498 : :
499 : : private:
500 : : pretty_printer &m_pp;
501 : : const layout &m_layout;
502 : : colorizer m_colorizer;
503 : : bool m_is_diagnostic_path;
504 : :
505 : : /* Fields for handling links between labels (e.g. for showing CFG edges
506 : : in execution paths).
507 : : Note that the logic for printing such links makes various simplifying
508 : : assumptions about the set of labels in the rich_location, and users
509 : : of this code will need to split up labels into separate rich_location
510 : : instances to respect these assumptions, or the output will look wrong.
511 : : See the diagnostic_path-printing code for more information. */
512 : :
513 : : /* An enum for describing the state of the leftmost column,
514 : : used for showing links between labels.
515 : : Consider e.g.
516 : : .x0000000001111111111222222222233333333334444444444.
517 : : .x1234567890123456789012345678901234567890123456789.
518 : : | | <- none
519 : : | (9) following ‘false’ branch... ->-+ <- none
520 : : | | <- none
521 : : | | <- none
522 : : |+----------------------------------------+ <- rewinding to lhs
523 : : || result->i = i; <- at lhs
524 : : || ~~~~~~~~~~^~~ <- at lhs
525 : : || | <- at lhs
526 : : |+----------->(10) ...to here <- indenting to dest
527 : : ^^
528 : : ||
529 : : |leftmost column ("x" above).
530 : : "margin". */
531 : : enum class link_lhs_state {
532 : : none,
533 : : rewinding_to_lhs,
534 : : at_lhs,
535 : : indenting_to_dest
536 : : } m_link_lhs_state;
537 : :
538 : : /* The column of the current link on the RHS, if any, or
539 : : -1 if there is none.
540 : : Consider e.g.
541 : : .x0000000001111111111222222222233333333334444444444.
542 : : .x1234567890123456789012345678901234567890123456789.
543 : : | | <- -1
544 : : | (10) following ‘false’ branch... ->-+ <- 42
545 : : | | <- 42
546 : : | | <- 42
547 : : |+-----------------------------------------+ <- 42
548 : : || result->i = i; <- -1
549 : : || ~~~~~~~~~~^~~ <- -1
550 : : || | <- -1
551 : : |+----------->(11) ...to here <- -1. */
552 : : int m_link_rhs_column;
553 : : };
554 : :
555 : : /* Implementation of "class colorizer". */
556 : :
557 : : /* The constructor for "colorizer". Lookup and store color codes for the
558 : : different kinds of things we might need to print. */
559 : :
560 : 43311 : colorizer::colorizer (pretty_printer &pp,
561 : : const rich_location &richloc,
562 : 43311 : diagnostic_t diagnostic_kind) :
563 : 43311 : m_pp (pp),
564 : 43311 : m_richloc (richloc),
565 : 43311 : m_diagnostic_kind (diagnostic_kind),
566 : 43311 : m_current_state (STATE_NORMAL_TEXT)
567 : : {
568 : 86622 : m_range1 = get_color_by_name ("range1");
569 : 86622 : m_range2 = get_color_by_name ("range2");
570 : 86622 : m_fixit_insert = get_color_by_name ("fixit-insert");
571 : 86622 : m_fixit_delete = get_color_by_name ("fixit-delete");
572 : 43311 : m_stop_color = colorize_stop (pp_show_color (&m_pp));
573 : 43311 : }
574 : :
575 : : /* The destructor for "colorize". If colorization is on, print a code to
576 : : turn it off. */
577 : :
578 : 43311 : colorizer::~colorizer ()
579 : : {
580 : 86622 : finish_state (m_current_state);
581 : 43311 : }
582 : :
583 : : /* Update state, changing to the specific named color and printing its
584 : : color codes. */
585 : :
586 : : void
587 : 17701 : colorizer::set_named_color (const char *color)
588 : : {
589 : 35402 : finish_state (m_current_state);
590 : 17701 : m_current_state = STATE_NAMED_COLOR;
591 : 17701 : pp_string (&m_pp, colorize_start (pp_show_color (&m_pp), color));
592 : 17701 : }
593 : :
594 : : /* Update state, printing color codes if necessary if there's a state
595 : : change. */
596 : :
597 : : void
598 : 14059856 : colorizer::set_state (int new_state)
599 : : {
600 : 14059856 : if (m_current_state != new_state)
601 : : {
602 : 334650 : finish_state (m_current_state);
603 : 167325 : m_current_state = new_state;
604 : 167325 : begin_state (new_state);
605 : : }
606 : 14059856 : }
607 : :
608 : : /* Turn on any colorization for STATE. */
609 : :
610 : : void
611 : 167325 : colorizer::begin_state (int state)
612 : : {
613 : 167325 : switch (state)
614 : : {
615 : : case STATE_NORMAL_TEXT:
616 : : break;
617 : :
618 : 11093 : case STATE_FIXIT_INSERT:
619 : 11093 : pp_string (&m_pp, m_fixit_insert);
620 : 11093 : break;
621 : :
622 : 2394 : case STATE_FIXIT_DELETE:
623 : 2394 : pp_string (&m_pp, m_fixit_delete);
624 : 2394 : break;
625 : :
626 : 0 : case STATE_NAMED_COLOR:
627 : : /* Should be handled by colorizer::set_named_color. */
628 : 0 : gcc_unreachable ();
629 : :
630 : 60385 : case 0:
631 : : /* Make range 0 be the same color as the "kind" text
632 : : (error vs warning vs note). */
633 : 60385 : pp_string
634 : 60385 : (&m_pp,
635 : 60385 : colorize_start (pp_show_color (&m_pp),
636 : : diagnostic_get_color_for_kind (m_diagnostic_kind)));
637 : 60385 : break;
638 : :
639 : 3721 : case 1:
640 : 3721 : pp_string (&m_pp, m_range1);
641 : 3721 : break;
642 : :
643 : 3123 : case 2:
644 : 3123 : pp_string (&m_pp, m_range2);
645 : 3123 : break;
646 : :
647 : 763 : default:
648 : : /* For ranges beyond 2, alternate between color 1 and color 2. */
649 : 763 : {
650 : 763 : gcc_assert (state > 2);
651 : 763 : pp_string (&m_pp,
652 : : state % 2 ? m_range1 : m_range2);
653 : : }
654 : 763 : break;
655 : : }
656 : 167325 : }
657 : :
658 : : /* Turn off any colorization for STATE. */
659 : :
660 : : void
661 : 228337 : colorizer::finish_state (int state)
662 : : {
663 : 228337 : if (state != STATE_NORMAL_TEXT)
664 : 99180 : pp_string (&m_pp, m_stop_color);
665 : 0 : }
666 : :
667 : : /* Get the color code for NAME (or the empty string if
668 : : colorization is disabled). */
669 : :
670 : : const char *
671 : 173244 : colorizer::get_color_by_name (const char *name)
672 : : {
673 : 43311 : return colorize_start (pp_show_color (&m_pp), name);
674 : : }
675 : :
676 : : /* Implementation of class layout_range. */
677 : :
678 : : /* The constructor for class layout_range.
679 : : Initialize various layout_point fields from expanded_location
680 : : equivalents; we've already filtered on file. */
681 : :
682 : 61067 : layout_range::layout_range (const exploc_with_display_col &start_exploc,
683 : : const exploc_with_display_col &finish_exploc,
684 : : enum range_display_kind range_display_kind,
685 : : const exploc_with_display_col &caret_exploc,
686 : : unsigned original_idx,
687 : 61067 : const range_label *label)
688 : 61067 : : m_start (start_exploc),
689 : 61067 : m_finish (finish_exploc),
690 : 61067 : m_range_display_kind (range_display_kind),
691 : 61067 : m_caret (caret_exploc),
692 : 61067 : m_original_idx (original_idx),
693 : 61067 : m_label (label)
694 : : {
695 : 0 : }
696 : :
697 : : /* Is (column, row) within the given range?
698 : : We've already filtered on the file.
699 : :
700 : : Ranges are closed (both limits are within the range).
701 : :
702 : : Example A: a single-line range:
703 : : start: (col=22, line=2)
704 : : finish: (col=38, line=2)
705 : :
706 : : |00000011111111112222222222333333333344444444444
707 : : |34567890123456789012345678901234567890123456789
708 : : --+-----------------------------------------------
709 : : 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
710 : : 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
711 : : 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
712 : :
713 : : Example B: a multiline range with
714 : : start: (col=14, line=3)
715 : : finish: (col=08, line=5)
716 : :
717 : : |00000011111111112222222222333333333344444444444
718 : : |34567890123456789012345678901234567890123456789
719 : : --+-----------------------------------------------
720 : : 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
721 : : 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
722 : : 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
723 : : 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
724 : : 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
725 : : 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
726 : : --+-----------------------------------------------
727 : :
728 : : Legend:
729 : : - 'b' indicates a point *before* the range
730 : : - 'S' indicates the start of the range
731 : : - 'w' indicates a point within the range
732 : : - 'F' indicates the finish of the range (which is
733 : : within it).
734 : : - 'a' indicates a subsequent point *after* the range.
735 : :
736 : : COL_UNIT controls whether we check the byte column or
737 : : the display column; one or the other is more convenient
738 : : depending on the context. */
739 : :
740 : : bool
741 : 14171061 : layout_range::contains_point (linenum_type row, int column,
742 : : enum column_unit col_unit) const
743 : : {
744 : 14171061 : gcc_assert (m_start.m_line <= m_finish.m_line);
745 : : /* ...but the equivalent isn't true for the columns;
746 : : consider example B in the comment above. */
747 : :
748 : 14171061 : if (row < m_start.m_line)
749 : : /* Points before the first line of the range are
750 : : outside it (corresponding to line 01 in example A
751 : : and lines 01 and 02 in example B above). */
752 : : return false;
753 : :
754 : 14096753 : if (row == m_start.m_line)
755 : : /* On same line as start of range (corresponding
756 : : to line 02 in example A and line 03 in example B). */
757 : : {
758 : 13958491 : if (column < m_start.m_columns[col_unit])
759 : : /* Points on the starting line of the range, but
760 : : before the column in which it begins. */
761 : : return false;
762 : :
763 : 1831351 : if (row < m_finish.m_line)
764 : : /* This is a multiline range; the point
765 : : is within it (corresponds to line 03 in example B
766 : : from column 14 onwards) */
767 : : return true;
768 : : else
769 : : {
770 : : /* This is a single-line range. */
771 : 1824499 : gcc_assert (row == m_finish.m_line);
772 : 1824499 : return column <= m_finish.m_columns[col_unit];
773 : : }
774 : : }
775 : :
776 : : /* The point is in a line beyond that containing the
777 : : start of the range: lines 03 onwards in example A,
778 : : and lines 04 onwards in example B. */
779 : 138262 : gcc_assert (row > m_start.m_line);
780 : :
781 : 138262 : if (row > m_finish.m_line)
782 : : /* The point is beyond the final line of the range
783 : : (lines 03 onwards in example A, and lines 06 onwards
784 : : in example B). */
785 : : return false;
786 : :
787 : 6726 : if (row < m_finish.m_line)
788 : : {
789 : : /* The point is in a line that's fully within a multiline
790 : : range (e.g. line 04 in example B). */
791 : : gcc_assert (m_start.m_line < m_finish.m_line);
792 : : return true;
793 : : }
794 : :
795 : 3871 : gcc_assert (row == m_finish.m_line);
796 : :
797 : 3871 : return column <= m_finish.m_columns[col_unit];
798 : : }
799 : :
800 : : /* Does this layout_range contain any part of line ROW? */
801 : :
802 : : bool
803 : 40824 : layout_range::intersects_line_p (linenum_type row) const
804 : : {
805 : 40824 : gcc_assert (m_start.m_line <= m_finish.m_line);
806 : 40824 : if (row < m_start.m_line)
807 : : return false;
808 : 39488 : if (row > m_finish.m_line)
809 : 3216 : return false;
810 : : return true;
811 : : }
812 : :
813 : : /* Return true if this layout_range should have an in-edge. */
814 : :
815 : : bool
816 : 8588 : layout_range::has_in_edge () const
817 : : {
818 : 8588 : if (!m_label)
819 : : return false;
820 : 8588 : const label_effects *effects = m_label->get_effects (m_original_idx);
821 : 8588 : if (!effects)
822 : : return false;
823 : :
824 : 4621 : return effects->has_in_edge (m_original_idx);
825 : : }
826 : :
827 : : /* Return true if this layout_range should have an out-edge. */
828 : :
829 : : bool
830 : 8588 : layout_range::has_out_edge () const
831 : : {
832 : 8588 : if (!m_label)
833 : : return false;
834 : 8588 : const label_effects *effects = m_label->get_effects (m_original_idx);
835 : 8588 : if (!effects)
836 : : return false;
837 : :
838 : 4621 : return effects->has_out_edge (m_original_idx);
839 : : }
840 : :
841 : : #if CHECKING_P
842 : :
843 : : /* Default for when we don't care what the tab expansion is set to. */
844 : : static const int def_tabstop = 8;
845 : :
846 : 292 : static cpp_char_column_policy def_policy ()
847 : : {
848 : 12 : return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
849 : : }
850 : :
851 : : /* Create some expanded locations for testing layout_range. The filename
852 : : member of the explocs is set to the empty string. This member will only be
853 : : inspected by the calls to location_compute_display_column() made from the
854 : : layout_point constructors. That function will check for an empty filename
855 : : argument and not attempt to open it, rather treating the non-existent data
856 : : as if the display width were the same as the byte count. Tests exercising a
857 : : real difference between byte count and display width are performed later,
858 : : e.g. in test_diagnostic_show_locus_one_liner_utf8(). */
859 : :
860 : : static layout_range
861 : 12 : make_range (file_cache &fc,
862 : : int start_line, int start_col, int end_line, int end_col)
863 : : {
864 : 12 : const expanded_location start_exploc
865 : 12 : = {"", start_line, start_col, NULL, false};
866 : 12 : const expanded_location finish_exploc
867 : 12 : = {"", end_line, end_col, NULL, false};
868 : 12 : return layout_range (exploc_with_display_col (fc,
869 : 24 : start_exploc, def_policy (),
870 : 12 : LOCATION_ASPECT_START),
871 : 12 : exploc_with_display_col (fc,
872 : 24 : finish_exploc, def_policy (),
873 : 12 : LOCATION_ASPECT_FINISH),
874 : : SHOW_RANGE_WITHOUT_CARET,
875 : 12 : exploc_with_display_col (fc,
876 : 24 : start_exploc, def_policy (),
877 : 12 : LOCATION_ASPECT_CARET),
878 : 12 : 0, NULL);
879 : : }
880 : :
881 : : /* Selftests for layout_range::contains_point and
882 : : layout_range::intersects_line_p. */
883 : :
884 : : /* Selftest for layout_range, where the layout_range
885 : : is a range with start==end i.e. a single point. */
886 : :
887 : : static void
888 : 4 : test_layout_range_for_single_point ()
889 : : {
890 : 4 : file_cache fc;
891 : 4 : layout_range point = make_range (fc, 7, 10, 7, 10);
892 : :
893 : : /* Tests for layout_range::contains_point. */
894 : :
895 : 12 : for (int i = 0; i != CU_NUM_UNITS; ++i)
896 : : {
897 : 8 : const enum column_unit col_unit = (enum column_unit) i;
898 : :
899 : : /* Before the line. */
900 : 8 : ASSERT_FALSE (point.contains_point (6, 1, col_unit));
901 : :
902 : : /* On the line, but before start. */
903 : 8 : ASSERT_FALSE (point.contains_point (7, 9, col_unit));
904 : :
905 : : /* At the point. */
906 : 8 : ASSERT_TRUE (point.contains_point (7, 10, col_unit));
907 : :
908 : : /* On the line, after the point. */
909 : 8 : ASSERT_FALSE (point.contains_point (7, 11, col_unit));
910 : :
911 : : /* After the line. */
912 : 8 : ASSERT_FALSE (point.contains_point (8, 1, col_unit));
913 : : }
914 : :
915 : : /* Tests for layout_range::intersects_line_p. */
916 : 4 : ASSERT_FALSE (point.intersects_line_p (6));
917 : 4 : ASSERT_TRUE (point.intersects_line_p (7));
918 : 4 : ASSERT_FALSE (point.intersects_line_p (8));
919 : 4 : }
920 : :
921 : : /* Selftest for layout_range, where the layout_range
922 : : is the single-line range shown as "Example A" above. */
923 : :
924 : : static void
925 : 4 : test_layout_range_for_single_line ()
926 : : {
927 : 4 : file_cache fc;
928 : 4 : layout_range example_a = make_range (fc, 2, 22, 2, 38);
929 : :
930 : : /* Tests for layout_range::contains_point. */
931 : :
932 : 12 : for (int i = 0; i != CU_NUM_UNITS; ++i)
933 : : {
934 : 8 : const enum column_unit col_unit = (enum column_unit) i;
935 : :
936 : : /* Before the line. */
937 : 8 : ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
938 : :
939 : : /* On the line, but before start. */
940 : 8 : ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
941 : :
942 : : /* On the line, at the start. */
943 : 8 : ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
944 : :
945 : : /* On the line, within the range. */
946 : 8 : ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
947 : :
948 : : /* On the line, at the end. */
949 : 8 : ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
950 : :
951 : : /* On the line, after the end. */
952 : 8 : ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
953 : :
954 : : /* After the line. */
955 : 8 : ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
956 : : }
957 : :
958 : : /* Tests for layout_range::intersects_line_p. */
959 : 4 : ASSERT_FALSE (example_a.intersects_line_p (1));
960 : 4 : ASSERT_TRUE (example_a.intersects_line_p (2));
961 : 4 : ASSERT_FALSE (example_a.intersects_line_p (3));
962 : 4 : }
963 : :
964 : : /* Selftest for layout_range, where the layout_range
965 : : is the multi-line range shown as "Example B" above. */
966 : :
967 : : static void
968 : 4 : test_layout_range_for_multiple_lines ()
969 : : {
970 : 4 : file_cache fc;
971 : 4 : layout_range example_b = make_range (fc, 3, 14, 5, 8);
972 : :
973 : : /* Tests for layout_range::contains_point. */
974 : :
975 : 12 : for (int i = 0; i != CU_NUM_UNITS; ++i)
976 : : {
977 : 8 : const enum column_unit col_unit = (enum column_unit) i;
978 : :
979 : : /* Before first line. */
980 : 8 : ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
981 : :
982 : : /* On the first line, but before start. */
983 : 8 : ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
984 : :
985 : : /* At the start. */
986 : 8 : ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
987 : :
988 : : /* On the first line, within the range. */
989 : 8 : ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
990 : :
991 : : /* On an interior line.
992 : : The column number should not matter; try various boundary
993 : : values. */
994 : 8 : ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
995 : 8 : ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
996 : 8 : ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
997 : 8 : ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
998 : 8 : ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
999 : 8 : ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
1000 : 8 : ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
1001 : :
1002 : : /* On the final line, before the end. */
1003 : 8 : ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
1004 : :
1005 : : /* On the final line, at the end. */
1006 : 8 : ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
1007 : :
1008 : : /* On the final line, after the end. */
1009 : 8 : ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
1010 : :
1011 : : /* After the line. */
1012 : 8 : ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
1013 : : }
1014 : :
1015 : : /* Tests for layout_range::intersects_line_p. */
1016 : 4 : ASSERT_FALSE (example_b.intersects_line_p (2));
1017 : 4 : ASSERT_TRUE (example_b.intersects_line_p (3));
1018 : 4 : ASSERT_TRUE (example_b.intersects_line_p (4));
1019 : 4 : ASSERT_TRUE (example_b.intersects_line_p (5));
1020 : 4 : ASSERT_FALSE (example_b.intersects_line_p (6));
1021 : 4 : }
1022 : :
1023 : : #endif /* #if CHECKING_P */
1024 : :
1025 : : /* Given a source line LINE of length LINE_BYTES bytes, determine the length
1026 : : (still in bytes, not display cols) without any trailing whitespace. */
1027 : :
1028 : : static int
1029 : 89058 : get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
1030 : : {
1031 : 89058 : int result = line_bytes;
1032 : 59361753 : while (result > 0)
1033 : : {
1034 : 59344129 : char ch = line[result - 1];
1035 : 59344129 : if (ch == ' ' || ch == '\t' || ch == '\r')
1036 : 59272695 : result--;
1037 : : else
1038 : : break;
1039 : : }
1040 : 89058 : gcc_assert (result >= 0);
1041 : 89058 : gcc_assert (result <= line_bytes);
1042 : 89058 : gcc_assert (result == 0 ||
1043 : : (line[result - 1] != ' '
1044 : : && line[result -1] != '\t'
1045 : : && line[result -1] != '\r'));
1046 : 89058 : return result;
1047 : : }
1048 : :
1049 : : #if CHECKING_P
1050 : :
1051 : : /* A helper function for testing get_line_bytes_without_trailing_whitespace. */
1052 : :
1053 : : static void
1054 : 32 : assert_eq (const char *line, int expected_bytes)
1055 : : {
1056 : 32 : int actual_value
1057 : 32 : = get_line_bytes_without_trailing_whitespace (line, strlen (line));
1058 : 32 : ASSERT_EQ (actual_value, expected_bytes);
1059 : 32 : }
1060 : :
1061 : : /* Verify that get_line_bytes_without_trailing_whitespace is sane for
1062 : : various inputs. It is not required to handle newlines. */
1063 : :
1064 : : static void
1065 : 4 : test_get_line_bytes_without_trailing_whitespace ()
1066 : : {
1067 : 4 : assert_eq ("", 0);
1068 : 4 : assert_eq (" ", 0);
1069 : 4 : assert_eq ("\t", 0);
1070 : 4 : assert_eq ("\r", 0);
1071 : 4 : assert_eq ("hello world", 11);
1072 : 4 : assert_eq ("hello world ", 11);
1073 : 4 : assert_eq ("hello world \t\t ", 11);
1074 : 4 : assert_eq ("hello world\r", 11);
1075 : 4 : }
1076 : :
1077 : : #endif /* #if CHECKING_P */
1078 : :
1079 : : /* Helper function for layout's ctor, for sanitizing locations relative
1080 : : to the primary location within a diagnostic.
1081 : :
1082 : : Compare LOC_A and LOC_B to see if it makes sense to print underlines
1083 : : connecting their expanded locations. Doing so is only guaranteed to
1084 : : make sense if the locations share the same macro expansion "history"
1085 : : i.e. they can be traced through the same macro expansions, eventually
1086 : : reaching an ordinary map.
1087 : :
1088 : : This may be too strong a condition, but it effectively sanitizes
1089 : : PR c++/70105, which has an example of printing an expression where the
1090 : : final location of the expression is in a different macro, which
1091 : : erroneously was leading to hundreds of lines of irrelevant source
1092 : : being printed. */
1093 : :
1094 : : bool
1095 : 122400 : layout::compatible_locations_p (location_t loc_a, location_t loc_b) const
1096 : : {
1097 : 122767 : if (IS_ADHOC_LOC (loc_a))
1098 : 256 : loc_a = get_location_from_adhoc_loc (m_line_table, loc_a);
1099 : 122767 : if (IS_ADHOC_LOC (loc_b))
1100 : 23907 : loc_b = get_location_from_adhoc_loc (m_line_table, loc_b);
1101 : :
1102 : : /* If either location is one of the special locations outside of a
1103 : : linemap, they are only compatible if they are equal. */
1104 : 122767 : if (loc_a < RESERVED_LOCATION_COUNT
1105 : 122767 : || loc_b < RESERVED_LOCATION_COUNT)
1106 : 67 : return loc_a == loc_b;
1107 : :
1108 : 122700 : const line_map *map_a = linemap_lookup (m_line_table, loc_a);
1109 : 122700 : linemap_assert (map_a);
1110 : :
1111 : 122700 : const line_map *map_b = linemap_lookup (m_line_table, loc_b);
1112 : 122700 : linemap_assert (map_b);
1113 : :
1114 : : /* Are they within the same map? */
1115 : 122700 : if (map_a == map_b)
1116 : : {
1117 : : /* Are both within the same macro expansion? */
1118 : 122592 : if (linemap_macro_expansion_map_p (map_a))
1119 : : {
1120 : : /* If so, then they're only compatible if either both are
1121 : : from the macro definition, or both from the macro arguments. */
1122 : 371 : bool loc_a_from_defn
1123 : 371 : = linemap_location_from_macro_definition_p (m_line_table, loc_a);
1124 : 371 : bool loc_b_from_defn
1125 : 371 : = linemap_location_from_macro_definition_p (m_line_table, loc_b);
1126 : 371 : if (loc_a_from_defn != loc_b_from_defn)
1127 : : return false;
1128 : :
1129 : : /* Expand each location towards the spelling location, and
1130 : : recurse. */
1131 : 367 : const line_map_macro *macro_map = linemap_check_macro (map_a);
1132 : 367 : location_t loc_a_toward_spelling
1133 : 367 : = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
1134 : : macro_map,
1135 : : loc_a);
1136 : 367 : location_t loc_b_toward_spelling
1137 : 367 : = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
1138 : : macro_map,
1139 : : loc_b);
1140 : 367 : return compatible_locations_p (loc_a_toward_spelling,
1141 : 367 : loc_b_toward_spelling);
1142 : : }
1143 : :
1144 : : /* Otherwise they are within the same ordinary map. */
1145 : : return true;
1146 : : }
1147 : : else
1148 : : {
1149 : : /* Within different maps. */
1150 : :
1151 : : /* If either is within a macro expansion, they are incompatible. */
1152 : 108 : if (linemap_macro_expansion_map_p (map_a)
1153 : 108 : || linemap_macro_expansion_map_p (map_b))
1154 : 28 : return false;
1155 : :
1156 : : /* Within two different ordinary maps; they are compatible iff they
1157 : : are in the same file. */
1158 : 80 : const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
1159 : 80 : const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
1160 : 80 : return ord_map_a->to_file == ord_map_b->to_file;
1161 : : }
1162 : : }
1163 : :
1164 : : /* Comparator for sorting fix-it hints. */
1165 : :
1166 : : static int
1167 : 169663 : fixit_cmp (const void *p_a, const void *p_b)
1168 : : {
1169 : 169663 : const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
1170 : 169663 : const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
1171 : 169663 : return hint_a->get_start_loc () - hint_b->get_start_loc ();
1172 : : }
1173 : :
1174 : : /* Callbacks for use when not escaping the source. */
1175 : :
1176 : : /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth. */
1177 : :
1178 : : /* Callback for char_display_policy::m_print_cb for printing source chars
1179 : : when not escaping the source. */
1180 : :
1181 : : static void
1182 : 3778252 : default_print_decoded_ch (pretty_printer *pp,
1183 : : const cpp_decoded_char &decoded_ch)
1184 : : {
1185 : 7568661 : for (const char *ptr = decoded_ch.m_start_byte;
1186 : 7568661 : ptr != decoded_ch.m_next_byte; ptr++)
1187 : : {
1188 : 3790409 : if (*ptr == '\0' || *ptr == '\r')
1189 : : {
1190 : 128 : pp_space (pp);
1191 : 128 : continue;
1192 : : }
1193 : :
1194 : 3790281 : pp_character (pp, *ptr);
1195 : : }
1196 : 3778252 : }
1197 : :
1198 : : /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1199 : :
1200 : : static const int width_per_escaped_byte = 4;
1201 : :
1202 : : /* Callback for char_column_policy::m_width_cb for determining the
1203 : : display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1204 : :
1205 : : static int
1206 : 21585 : escape_as_bytes_width (cppchar_t ch)
1207 : : {
1208 : 21585 : if (ch < 0x80 && ISPRINT (ch))
1209 : 17075 : return cpp_wcwidth (ch);
1210 : : else
1211 : : {
1212 : : if (ch <= 0x7F) return 1 * width_per_escaped_byte;
1213 : 1743 : if (ch <= 0x7FF) return 2 * width_per_escaped_byte;
1214 : 1035 : if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
1215 : 1032 : return 4 * width_per_escaped_byte;
1216 : : }
1217 : : }
1218 : :
1219 : : /* Callback for char_display_policy::m_print_cb for printing source chars
1220 : : when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES. */
1221 : :
1222 : : static void
1223 : 3524 : escape_as_bytes_print (pretty_printer *pp,
1224 : : const cpp_decoded_char &decoded_ch)
1225 : : {
1226 : 3524 : if (!decoded_ch.m_valid_ch)
1227 : : {
1228 : 516 : for (const char *iter = decoded_ch.m_start_byte;
1229 : 516 : iter != decoded_ch.m_next_byte; ++iter)
1230 : : {
1231 : 258 : char buf[16];
1232 : 258 : sprintf (buf, "<%02x>", (unsigned char)*iter);
1233 : 258 : pp_string (pp, buf);
1234 : : }
1235 : : return;
1236 : : }
1237 : :
1238 : 3266 : cppchar_t ch = decoded_ch.m_ch;
1239 : 3266 : if (ch < 0x80 && ISPRINT (ch))
1240 : 2623 : pp_character (pp, ch);
1241 : : else
1242 : : {
1243 : 1800 : for (const char *iter = decoded_ch.m_start_byte;
1244 : 1800 : iter < decoded_ch.m_next_byte; ++iter)
1245 : : {
1246 : 1157 : char buf[16];
1247 : 1157 : sprintf (buf, "<%02x>", (unsigned char)*iter);
1248 : 1157 : pp_string (pp, buf);
1249 : : }
1250 : : }
1251 : : }
1252 : :
1253 : : /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1254 : :
1255 : : /* Callback for char_column_policy::m_width_cb for determining the
1256 : : display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1257 : :
1258 : : static int
1259 : 54939 : escape_as_unicode_width (cppchar_t ch)
1260 : : {
1261 : 54939 : if (ch < 0x80 && ISPRINT (ch))
1262 : 46269 : return cpp_wcwidth (ch);
1263 : : else
1264 : : {
1265 : : // Width of "<U+%04x>"
1266 : 8670 : if (ch > 0xfffff)
1267 : : return 10;
1268 : 8666 : else if (ch > 0xffff)
1269 : : return 9;
1270 : : else
1271 : 5590 : return 8;
1272 : : }
1273 : : }
1274 : :
1275 : : /* Callback for char_display_policy::m_print_cb for printing source chars
1276 : : when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE. */
1277 : :
1278 : : static void
1279 : 7184 : escape_as_unicode_print (pretty_printer *pp,
1280 : : const cpp_decoded_char &decoded_ch)
1281 : : {
1282 : 7184 : if (!decoded_ch.m_valid_ch)
1283 : : {
1284 : 129 : escape_as_bytes_print (pp, decoded_ch);
1285 : 129 : return;
1286 : : }
1287 : :
1288 : 7055 : cppchar_t ch = decoded_ch.m_ch;
1289 : 7055 : if (ch < 0x80 && ISPRINT (ch))
1290 : 5844 : pp_character (pp, ch);
1291 : : else
1292 : : {
1293 : 1211 : char buf[16];
1294 : 1211 : sprintf (buf, "<U+%04X>", ch);
1295 : 1211 : pp_string (pp, buf);
1296 : : }
1297 : : }
1298 : :
1299 : : /* Populate a char_display_policy based on SOURCE_POLICY and RICHLOC. */
1300 : :
1301 : : static char_display_policy
1302 : 48810 : make_char_policy (const diagnostic_source_print_policy &source_policy,
1303 : : const rich_location &richloc)
1304 : : {
1305 : : /* The default is to not escape non-ASCII bytes. */
1306 : 48810 : char_display_policy result
1307 : 48810 : (source_policy.get_column_policy ().get_tabstop (),
1308 : : cpp_wcwidth,
1309 : 48810 : default_print_decoded_ch);
1310 : :
1311 : : /* If the diagnostic suggests escaping non-ASCII bytes, then
1312 : : use policy from user-supplied options. */
1313 : 48810 : if (richloc.escape_on_output_p ())
1314 : : {
1315 : 554 : result.m_undecoded_byte_width = width_per_escaped_byte;
1316 : 554 : switch (source_policy.get_escape_format ())
1317 : : {
1318 : 0 : default:
1319 : 0 : gcc_unreachable ();
1320 : 353 : case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
1321 : 353 : result.m_width_cb = escape_as_unicode_width;
1322 : 353 : result.m_print_cb = escape_as_unicode_print;
1323 : 353 : break;
1324 : 201 : case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
1325 : 201 : result.m_width_cb = escape_as_bytes_width;
1326 : 201 : result.m_print_cb = escape_as_bytes_print;
1327 : 201 : break;
1328 : : }
1329 : : }
1330 : :
1331 : 48810 : return result;
1332 : : }
1333 : :
1334 : : /* Implementation of class layout. */
1335 : :
1336 : : /* Constructor for class layout.
1337 : :
1338 : : Filter the ranges from the rich_location to those that we can
1339 : : sanely print, populating m_layout_ranges and m_fixit_hints.
1340 : : Determine the range of lines that we will print, splitting them
1341 : : up into an ordered list of disjoint spans of contiguous line numbers.
1342 : : Determine m_x_offset_display, to ensure that the primary caret
1343 : : will fit within the max_width provided by the diagnostic_context. */
1344 : :
1345 : 48622 : layout::layout (const diagnostic_source_print_policy &source_policy,
1346 : : const rich_location &richloc,
1347 : 48622 : diagnostic_source_effect_info *effect_info)
1348 : 48622 : : m_options (source_policy.get_options ()),
1349 : 48622 : m_line_table (richloc.get_line_table ()),
1350 : 48622 : m_file_cache (source_policy.get_file_cache ()),
1351 : : /* Ensure we have a non-null m_theme. */
1352 : 97244 : m_theme (source_policy.get_diagram_theme ()
1353 : 48622 : ? *source_policy.get_diagram_theme ()
1354 : : : *static_cast <const text_art::theme *> (&m_fallback_theme)),
1355 : 48622 : m_effect_info (effect_info),
1356 : 48622 : m_char_policy (make_char_policy (source_policy, richloc)),
1357 : 48622 : m_primary_loc (richloc.get_range (0)->m_loc),
1358 : 48622 : m_exploc (m_file_cache,
1359 : 48622 : richloc.get_expanded_location (0), m_char_policy,
1360 : : LOCATION_ASPECT_CARET),
1361 : 48622 : m_layout_ranges (richloc.get_num_locations ()),
1362 : 48622 : m_fixit_hints (richloc.get_num_fixit_hints ()),
1363 : 48622 : m_line_spans (1 + richloc.get_num_locations ()),
1364 : 48622 : m_linenum_width (0),
1365 : 48622 : m_x_offset_display (0),
1366 : 48622 : m_escape_on_output (richloc.escape_on_output_p ())
1367 : : {
1368 : 105841 : for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
1369 : : {
1370 : : /* This diagnostic printer can only cope with "sufficiently sane" ranges.
1371 : : Ignore any ranges that are awkward to handle. */
1372 : 57219 : const location_range *loc_range = richloc.get_range (idx);
1373 : 57219 : maybe_add_location_range (loc_range, idx, false);
1374 : : }
1375 : :
1376 : : /* Populate m_fixit_hints, filtering to only those that are in the
1377 : : same file. */
1378 : 62912 : for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
1379 : : {
1380 : 14290 : const fixit_hint *hint = richloc.get_fixit_hint (i);
1381 : 14290 : if (validate_fixit_hint_p (hint))
1382 : 14290 : m_fixit_hints.safe_push (hint);
1383 : : }
1384 : :
1385 : : /* Sort m_fixit_hints. */
1386 : 48622 : m_fixit_hints.qsort (fixit_cmp);
1387 : :
1388 : : /* Populate the indicated members. */
1389 : 48622 : calculate_line_spans ();
1390 : 48622 : calculate_linenum_width ();
1391 : 48622 : calculate_x_offset_display ();
1392 : 48622 : }
1393 : :
1394 : :
1395 : : /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
1396 : : those that we can sanely print.
1397 : :
1398 : : ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
1399 : : (for use as extrinsic state by label ranges).
1400 : :
1401 : : If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
1402 : : filtered against this layout instance's current line spans: it
1403 : : will only be added if the location is fully within the lines
1404 : : already specified by other locations.
1405 : :
1406 : : Return true iff LOC_RANGE was added. */
1407 : :
1408 : : bool
1409 : 61058 : layout::maybe_add_location_range (const location_range *loc_range,
1410 : : unsigned original_idx,
1411 : : bool restrict_to_current_line_spans)
1412 : : {
1413 : 61058 : gcc_assert (loc_range);
1414 : :
1415 : : /* Split the "range" into caret and range information. */
1416 : 61058 : source_range src_range = get_range_from_loc (m_line_table, loc_range->m_loc);
1417 : :
1418 : : /* Expand the various locations. */
1419 : 61058 : expanded_location start
1420 : : = linemap_client_expand_location_to_spelling_point
1421 : 61058 : (m_line_table, src_range.m_start, LOCATION_ASPECT_START);
1422 : 61058 : expanded_location finish
1423 : : = linemap_client_expand_location_to_spelling_point
1424 : 61058 : (m_line_table, src_range.m_finish, LOCATION_ASPECT_FINISH);
1425 : 61058 : expanded_location caret
1426 : : = linemap_client_expand_location_to_spelling_point
1427 : 61058 : (m_line_table, loc_range->m_loc, LOCATION_ASPECT_CARET);
1428 : :
1429 : : /* If any part of the range isn't in the same file as the primary
1430 : : location of this diagnostic, ignore the range. */
1431 : 61058 : if (start.file != m_exploc.file)
1432 : : return false;
1433 : 61055 : if (finish.file != m_exploc.file)
1434 : : return false;
1435 : 61055 : if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1436 : 48775 : if (caret.file != m_exploc.file)
1437 : : return false;
1438 : :
1439 : : /* Sanitize the caret location for non-primary ranges. */
1440 : 61055 : if (m_layout_ranges.length () > 0)
1441 : 12434 : if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1442 : 331 : if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
1443 : : /* Discard any non-primary ranges that can't be printed
1444 : : sanely relative to the primary location. */
1445 : : return false;
1446 : :
1447 : : /* If there's no column information, then don't try to print
1448 : : annotation lines for this range. */
1449 : 61055 : enum range_display_kind range_display_kind
1450 : : = loc_range->m_range_display_kind;
1451 : 61055 : if (start.column == 0
1452 : 55014 : || finish.column == 0
1453 : 55014 : || caret.column == 0)
1454 : 6041 : range_display_kind = SHOW_LINES_WITHOUT_RANGE;
1455 : :
1456 : : /* Everything is now known to be in the correct source file,
1457 : : but it may require further sanitization. */
1458 : 122110 : layout_range ri (exploc_with_display_col (m_file_cache,
1459 : : start, m_char_policy,
1460 : 61055 : LOCATION_ASPECT_START),
1461 : 61055 : exploc_with_display_col (m_file_cache,
1462 : : finish, m_char_policy,
1463 : 61055 : LOCATION_ASPECT_FINISH),
1464 : : range_display_kind,
1465 : 61055 : exploc_with_display_col (m_file_cache,
1466 : : caret, m_char_policy,
1467 : 61055 : LOCATION_ASPECT_CARET),
1468 : 61055 : original_idx, loc_range->m_label);
1469 : :
1470 : : /* If we have a range that finishes before it starts (perhaps
1471 : : from something built via macro expansion), printing the
1472 : : range is likely to be nonsensical. Also, attempting to do so
1473 : : breaks assumptions within the printing code (PR c/68473).
1474 : : Similarly, don't attempt to print ranges if one or both ends
1475 : : of the range aren't sane to print relative to the
1476 : : primary location (PR c++/70105). */
1477 : 61055 : if (start.line > finish.line
1478 : 61046 : || !compatible_locations_p (src_range.m_start, m_primary_loc)
1479 : 122078 : || !compatible_locations_p (src_range.m_finish, m_primary_loc))
1480 : : {
1481 : : /* Is this the primary location? */
1482 : 41 : if (m_layout_ranges.length () == 0)
1483 : : {
1484 : : /* We want to print the caret for the primary location, but
1485 : : we must sanitize away m_start and m_finish. */
1486 : 18 : ri.m_start = ri.m_caret;
1487 : 18 : ri.m_finish = ri.m_caret;
1488 : : }
1489 : : else
1490 : : /* This is a non-primary range; ignore it. */
1491 : : return false;
1492 : : }
1493 : :
1494 : : /* Potentially filter to just the lines already specified by other
1495 : : locations. This is for use by gcc_rich_location::add_location_if_nearby.
1496 : : The layout ctor doesn't use it, and can't because m_line_spans
1497 : : hasn't been set up at that point. */
1498 : 61032 : if (restrict_to_current_line_spans)
1499 : : {
1500 : 1565 : if (!will_show_line_p (start.line))
1501 : : return false;
1502 : 1373 : if (!will_show_line_p (finish.line))
1503 : : return false;
1504 : 1373 : if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1505 : 0 : if (!will_show_line_p (caret.line))
1506 : : return false;
1507 : : }
1508 : :
1509 : : /* Passed all the tests; add the range to m_layout_ranges so that
1510 : : it will be printed. */
1511 : 60840 : m_layout_ranges.safe_push (ri);
1512 : 60840 : return true;
1513 : : }
1514 : :
1515 : : /* Return true iff ROW is within one of the line spans for this layout. */
1516 : :
1517 : : bool
1518 : 2938 : layout::will_show_line_p (linenum_type row) const
1519 : : {
1520 : 6260 : for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1521 : : line_span_idx++)
1522 : : {
1523 : 2938 : const line_span *line_span = get_line_span (line_span_idx);
1524 : 2938 : if (line_span->contains_line_p (row))
1525 : : return true;
1526 : : }
1527 : : return false;
1528 : : }
1529 : :
1530 : : /* Print a line showing a gap in the line numbers, for showing the boundary
1531 : : between two line spans. */
1532 : :
1533 : : void
1534 : 360 : layout_printer::print_gap_in_line_numbering ()
1535 : : {
1536 : 360 : gcc_assert (m_layout.m_options.show_line_numbers_p);
1537 : :
1538 : 360 : pp_emit_prefix (&m_pp);
1539 : :
1540 : 2520 : for (int i = 0; i < m_layout.get_linenum_width () + 1; i++)
1541 : 2160 : pp_character (&m_pp, '.');
1542 : :
1543 : 360 : pp_newline (&m_pp);
1544 : 360 : }
1545 : :
1546 : : /* Return true iff we should print a heading when starting the
1547 : : line span with the given index. */
1548 : :
1549 : : bool
1550 : 19117 : layout::print_heading_for_line_span_index_p (int line_span_idx) const
1551 : : {
1552 : : /* We print a heading for every change of line span, hence for every
1553 : : line span after the initial one. */
1554 : 19117 : if (line_span_idx > 0)
1555 : : return true;
1556 : :
1557 : : /* We also do it for the initial span if the primary location of the
1558 : : diagnostic is in a different span. */
1559 : 18773 : if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1560 : 119 : return true;
1561 : :
1562 : : return false;
1563 : : }
1564 : :
1565 : : /* Get an expanded_location for the first location of interest within
1566 : : the given line_span.
1567 : : Used when printing a heading to indicate a new line span. */
1568 : :
1569 : : expanded_location
1570 : 463 : layout::get_expanded_location (const line_span *line_span) const
1571 : : {
1572 : : /* Whenever possible, use the caret location. */
1573 : 463 : if (line_span->contains_line_p (m_exploc.line))
1574 : 119 : return m_exploc;
1575 : :
1576 : : /* Otherwise, use the start of the first range that's present
1577 : : within the line_span. */
1578 : 1382 : for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1579 : : {
1580 : 575 : const layout_range *lr = &m_layout_ranges[i];
1581 : 694 : if (line_span->contains_line_p (lr->m_start.m_line))
1582 : : {
1583 : 228 : expanded_location exploc = m_exploc;
1584 : 228 : exploc.line = lr->m_start.m_line;
1585 : 228 : exploc.column = lr->m_start.m_columns[CU_BYTES];
1586 : 228 : return exploc;
1587 : : }
1588 : : }
1589 : :
1590 : : /* Otherwise, use the location of the first fixit-hint present within
1591 : : the line_span. */
1592 : 116 : for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1593 : : {
1594 : 116 : const fixit_hint *hint = m_fixit_hints[i];
1595 : 116 : location_t loc = hint->get_start_loc ();
1596 : 116 : expanded_location exploc = expand_location (loc);
1597 : 116 : if (line_span->contains_line_p (exploc.line))
1598 : 116 : return exploc;
1599 : : }
1600 : :
1601 : : /* It should not be possible to have a line span that didn't
1602 : : contain any of the layout_range or fixit_hint instances. */
1603 : 0 : gcc_unreachable ();
1604 : : return m_exploc;
1605 : : }
1606 : :
1607 : : /* Determine if HINT is meaningful to print within this layout. */
1608 : :
1609 : : bool
1610 : 14290 : layout::validate_fixit_hint_p (const fixit_hint *hint)
1611 : : {
1612 : 14290 : if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1613 : : return false;
1614 : 14290 : if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1615 : : return false;
1616 : :
1617 : : return true;
1618 : : }
1619 : :
1620 : : /* Determine the range of lines affected by HINT.
1621 : : This assumes that HINT has already been filtered by
1622 : : validate_fixit_hint_p, and so affects the correct source file. */
1623 : :
1624 : : static line_span
1625 : 14290 : get_line_span_for_fixit_hint (const fixit_hint *hint)
1626 : : {
1627 : 14290 : gcc_assert (hint);
1628 : :
1629 : 14290 : int start_line = LOCATION_LINE (hint->get_start_loc ());
1630 : :
1631 : : /* For line-insertion fix-it hints, add the previous line to the
1632 : : span, to give the user more context on the proposed change. */
1633 : 14290 : if (hint->ends_with_newline_p ())
1634 : 437 : if (start_line > 1)
1635 : 292 : start_line--;
1636 : :
1637 : 14290 : return line_span (start_line,
1638 : 14290 : LOCATION_LINE (hint->get_next_loc ()));
1639 : : }
1640 : :
1641 : : /* We want to print the pertinent source code at a diagnostic. The
1642 : : rich_location can contain multiple locations. This will have been
1643 : : filtered into m_exploc (the caret for the primary location) and
1644 : : m_layout_ranges, for those ranges within the same source file.
1645 : :
1646 : : We will print a subset of the lines within the source file in question,
1647 : : as a collection of "spans" of lines.
1648 : :
1649 : : This function populates m_line_spans with an ordered, disjoint list of
1650 : : the line spans of interest.
1651 : :
1652 : : Printing a gap between line spans takes one line, so, when printing
1653 : : line numbers, we allow a gap of up to one line between spans when
1654 : : merging, since it makes more sense to print the source line rather than a
1655 : : "gap-in-line-numbering" line. When not printing line numbers, it's
1656 : : better to be more explicit about what's going on, so keeping them as
1657 : : separate spans is preferred.
1658 : :
1659 : : For example, if the primary range is on lines 8-10, with secondary ranges
1660 : : covering lines 5-6 and lines 13-15:
1661 : :
1662 : : 004
1663 : : 005 |RANGE 1
1664 : : 006 |RANGE 1
1665 : : 007
1666 : : 008 |PRIMARY RANGE
1667 : : 009 |PRIMARY CARET
1668 : : 010 |PRIMARY RANGE
1669 : : 011
1670 : : 012
1671 : : 013 |RANGE 2
1672 : : 014 |RANGE 2
1673 : : 015 |RANGE 2
1674 : : 016
1675 : :
1676 : : With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1677 : :
1678 : : With line numbering off (with span headers), we want three spans: lines 5-6,
1679 : : lines 8-10, and lines 13-15. */
1680 : :
1681 : : void
1682 : 48622 : layout::calculate_line_spans ()
1683 : : {
1684 : : /* This should only be called once, by the ctor. */
1685 : 48622 : gcc_assert (m_line_spans.length () == 0);
1686 : :
1687 : : /* Populate tmp_spans with individual spans, for each of
1688 : : m_exploc, and for m_layout_ranges. */
1689 : 97244 : auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1690 : 48622 : tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1691 : 211632 : for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1692 : : {
1693 : 57194 : const layout_range *lr = &m_layout_ranges[i];
1694 : 57194 : gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1695 : 57194 : tmp_spans.safe_push (line_span (lr->m_start.m_line,
1696 : 57194 : lr->m_finish.m_line));
1697 : : }
1698 : :
1699 : : /* Also add spans for any fix-it hints, in case they cover other lines. */
1700 : 62912 : for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1701 : : {
1702 : 14290 : const fixit_hint *hint = m_fixit_hints[i];
1703 : 14290 : gcc_assert (hint);
1704 : 14290 : tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1705 : : }
1706 : :
1707 : : /* Sort them. */
1708 : 48622 : tmp_spans.qsort(line_span::comparator);
1709 : :
1710 : : /* Now iterate through tmp_spans, copying into m_line_spans, and
1711 : : combining where possible. */
1712 : 48622 : gcc_assert (tmp_spans.length () > 0);
1713 : 48622 : m_line_spans.safe_push (tmp_spans[0]);
1714 : 120106 : for (unsigned int i = 1; i < tmp_spans.length (); i++)
1715 : : {
1716 : 142968 : line_span *current = &m_line_spans[m_line_spans.length () - 1];
1717 : 71484 : const line_span *next = &tmp_spans[i];
1718 : 71484 : gcc_assert (next->m_first_line >= current->m_first_line);
1719 : 71484 : const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
1720 : 71484 : if ((linenum_arith_t)next->m_first_line
1721 : 71484 : <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1722 : : {
1723 : : /* We can merge them. */
1724 : 70313 : if (next->m_last_line > current->m_last_line)
1725 : 2034 : current->m_last_line = next->m_last_line;
1726 : : }
1727 : : else
1728 : : {
1729 : : /* No merger possible. */
1730 : 1171 : m_line_spans.safe_push (*next);
1731 : : }
1732 : : }
1733 : :
1734 : : /* Verify the result, in m_line_spans. */
1735 : 48622 : gcc_assert (m_line_spans.length () > 0);
1736 : 49793 : for (unsigned int i = 1; i < m_line_spans.length (); i++)
1737 : : {
1738 : 1171 : const line_span *prev = &m_line_spans[i - 1];
1739 : 1171 : const line_span *next = &m_line_spans[i];
1740 : : /* The individual spans must be sane. */
1741 : 1171 : gcc_assert (prev->m_first_line <= prev->m_last_line);
1742 : 1171 : gcc_assert (next->m_first_line <= next->m_last_line);
1743 : : /* The spans must be ordered. */
1744 : 1171 : gcc_assert (prev->m_first_line < next->m_first_line);
1745 : : /* There must be a gap of at least one line between separate spans. */
1746 : 1171 : gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1747 : : }
1748 : 48622 : }
1749 : :
1750 : : /* Determine how many display columns need to be reserved for line numbers,
1751 : : based on the largest line number that will be needed, and populate
1752 : : m_linenum_width. */
1753 : :
1754 : : void
1755 : 48622 : layout::calculate_linenum_width ()
1756 : : {
1757 : 48622 : gcc_assert (m_line_spans.length () > 0);
1758 : 48622 : const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
1759 : 48622 : int highest_line = last_span->m_last_line;
1760 : 48622 : if (highest_line < 0)
1761 : : highest_line = 0;
1762 : 48622 : m_linenum_width = num_digits (highest_line);
1763 : : /* If we're showing jumps in the line-numbering, allow at least 3 chars. */
1764 : 48622 : if (m_line_spans.length () > 1)
1765 : 1124 : m_linenum_width = MAX (m_linenum_width, 3);
1766 : : /* If there's a minimum margin width, apply it (subtracting 1 for the space
1767 : : after the line number. */
1768 : 48622 : m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
1769 : 48622 : }
1770 : :
1771 : : /* Calculate m_x_offset_display, which improves readability in case the source
1772 : : line of interest is longer than the user's display. All lines output will be
1773 : : shifted to the left (so that their beginning is no longer displayed) by
1774 : : m_x_offset_display display columns, so that the caret is in a reasonable
1775 : : location. */
1776 : :
1777 : : void
1778 : 48622 : layout::calculate_x_offset_display ()
1779 : : {
1780 : 48622 : m_x_offset_display = 0;
1781 : :
1782 : 48622 : const int max_width = m_options.max_width;
1783 : 48622 : if (!max_width)
1784 : : {
1785 : : /* Nothing to do, the width is not capped. */
1786 : 46153 : return;
1787 : : }
1788 : :
1789 : 48558 : const char_span line = m_file_cache.get_source_line (m_exploc.file,
1790 : : m_exploc.line);
1791 : 48558 : if (!line)
1792 : : {
1793 : : /* Nothing to do, we couldn't find the source line. */
1794 : : return;
1795 : : }
1796 : 45635 : int caret_display_column = m_exploc.m_display_col;
1797 : 45635 : const int line_bytes
1798 : 91270 : = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
1799 : 45635 : line.length ());
1800 : 45635 : int eol_display_column
1801 : 45635 : = cpp_display_width (line.get_buffer (), line_bytes, m_char_policy);
1802 : 45635 : if (caret_display_column > eol_display_column
1803 : 45635 : || !caret_display_column)
1804 : : {
1805 : : /* This does not make sense, so don't try to do anything in this case. */
1806 : : return;
1807 : : }
1808 : :
1809 : : /* Adjust caret and eol positions to include the left margin. If we are
1810 : : outputting line numbers, then the left margin is equal to m_linenum_width
1811 : : plus three for the " | " which follows it. Otherwise the left margin width
1812 : : is equal to 1, because layout::print_source_line() will prefix each line
1813 : : with a space. */
1814 : 32253 : const int source_display_cols = eol_display_column;
1815 : 32253 : int left_margin_size = 1;
1816 : 32253 : if (m_options.show_line_numbers_p)
1817 : 21192 : left_margin_size = m_linenum_width + 3;
1818 : 32253 : caret_display_column += left_margin_size;
1819 : 32253 : eol_display_column += left_margin_size;
1820 : :
1821 : 32253 : if (eol_display_column <= max_width)
1822 : : {
1823 : : /* Nothing to do, everything fits in the display. */
1824 : : return;
1825 : : }
1826 : :
1827 : : /* The line is too long for the display. Calculate an offset such that the
1828 : : caret is not too close to the right edge of the screen. It will be
1829 : : CARET_LINE_MARGIN display columns from the right edge, unless it is closer
1830 : : than that to the end of the source line anyway. */
1831 : 2597 : int right_margin_size = CARET_LINE_MARGIN;
1832 : 2597 : right_margin_size = MIN (eol_display_column - caret_display_column,
1833 : : right_margin_size);
1834 : 2597 : if (right_margin_size + left_margin_size >= max_width)
1835 : : {
1836 : : /* The max_width is very small, so anything we try to do will not be very
1837 : : effective; just punt in this case and output with no offset. */
1838 : : return;
1839 : : }
1840 : 2469 : const int max_caret_display_column = max_width - right_margin_size;
1841 : 2469 : if (caret_display_column > max_caret_display_column)
1842 : : {
1843 : 2372 : m_x_offset_display = caret_display_column - max_caret_display_column;
1844 : : /* Make sure we don't offset the line into oblivion. */
1845 : 2372 : static const int min_cols_visible = 2;
1846 : 2372 : if (source_display_cols - m_x_offset_display < min_cols_visible)
1847 : 0 : m_x_offset_display = 0;
1848 : : }
1849 : : }
1850 : :
1851 : : /* Print line ROW of source code, potentially colorized at any ranges, and
1852 : : return the line bounds. LINE is the source line (not necessarily
1853 : : 0-terminated) and LINE_BYTES is its length in bytes. In order to handle both
1854 : : colorization and tab expansion, this function tracks the line position in
1855 : : both byte and display column units. */
1856 : :
1857 : : line_bounds
1858 : 43391 : layout_printer::print_source_line (linenum_type row, const char *line, int line_bytes)
1859 : : {
1860 : 43391 : m_colorizer.set_normal_text ();
1861 : :
1862 : 43391 : pp_emit_prefix (&m_pp);
1863 : 43391 : if (m_layout.m_options.show_line_numbers_p)
1864 : : {
1865 : 24238 : int width = num_digits (row);
1866 : 81929 : for (int i = 0; i < m_layout.get_linenum_width () - width; i++)
1867 : 57691 : pp_space (&m_pp);
1868 : 24238 : pp_printf (&m_pp, "%i |", row);
1869 : : }
1870 : :
1871 : 43391 : print_leftmost_column ();
1872 : :
1873 : : /* We will stop printing the source line at any trailing whitespace. */
1874 : 43391 : line_bytes = get_line_bytes_without_trailing_whitespace (line,
1875 : : line_bytes);
1876 : :
1877 : : /* This object helps to keep track of which display column we are at, which is
1878 : : necessary for computing the line bounds in display units, for doing
1879 : : tab expansion, and for implementing m_x_offset_display. */
1880 : 43391 : cpp_display_width_computation dw (line, line_bytes, m_layout.m_char_policy);
1881 : :
1882 : : /* Skip the first m_x_offset_display display columns. In case the leading
1883 : : portion that will be skipped ends with a character with wcwidth > 1, then
1884 : : it is possible we skipped too much, so account for that by padding with
1885 : : spaces. Note that this does the right thing too in case a tab was the last
1886 : : character to be skipped over; the tab is effectively replaced by the
1887 : : correct number of trailing spaces needed to offset by the desired number of
1888 : : display columns. */
1889 : 44223 : for (int skipped_display_cols
1890 : 43391 : = dw.advance_display_cols (m_layout.m_x_offset_display);
1891 : 44223 : skipped_display_cols > m_layout.m_x_offset_display; --skipped_display_cols)
1892 : 832 : pp_space (&m_pp);
1893 : :
1894 : : /* Print the line and compute the line_bounds. */
1895 : : line_bounds lbounds;
1896 : 3833963 : while (!dw.done ())
1897 : : {
1898 : : /* Assuming colorization is enabled for the caret and underline
1899 : : characters, we may also colorize the associated characters
1900 : : within the source line.
1901 : :
1902 : : For frontends that generate range information, we color the
1903 : : associated characters in the source line the same as the
1904 : : carets and underlines in the annotation line, to make it easier
1905 : : for the reader to see the pertinent code.
1906 : :
1907 : : For frontends that only generate carets, we don't colorize the
1908 : : characters above them, since this would look strange (e.g.
1909 : : colorizing just the first character in a token). */
1910 : 3790572 : if (m_layout.m_options.colorize_source_p)
1911 : : {
1912 : 2928727 : bool in_range_p;
1913 : 2928727 : point_state state;
1914 : 2928727 : const int start_byte_col = dw.bytes_processed () + 1;
1915 : 2928727 : in_range_p = m_layout.get_state_at_point (row, start_byte_col,
1916 : : 0, INT_MAX,
1917 : : CU_BYTES,
1918 : : &state);
1919 : 2928727 : if (in_range_p)
1920 : 58662 : m_colorizer.set_range (state.range_idx);
1921 : : else
1922 : 2870065 : m_colorizer.set_normal_text ();
1923 : : }
1924 : :
1925 : : /* Get the display width of the next character to be output, expanding
1926 : : tabs and replacing some control bytes with spaces as necessary. */
1927 : 3790572 : const char *c = dw.next_byte ();
1928 : 3790572 : const int start_disp_col = dw.display_cols_processed () + 1;
1929 : 3790572 : cpp_decoded_char cp;
1930 : 3790572 : const int this_display_width = dw.process_next_codepoint (&cp);
1931 : 3790572 : if (*c == '\t')
1932 : : {
1933 : : /* The returned display width is the number of spaces into which the
1934 : : tab should be expanded. */
1935 : 10425 : for (int i = 0; i != this_display_width; ++i)
1936 : 8684 : pp_space (&m_pp);
1937 : 1741 : continue;
1938 : 1741 : }
1939 : :
1940 : : /* We have a (possibly multibyte) character to output; update the line
1941 : : bounds if it is not whitespace. */
1942 : 3788831 : if (*c != ' ')
1943 : : {
1944 : 3199713 : lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
1945 : 3199713 : if (lbounds.m_first_non_ws_disp_col == INT_MAX)
1946 : 34549 : lbounds.m_first_non_ws_disp_col = start_disp_col;
1947 : : }
1948 : :
1949 : : /* Output the character. */
1950 : 3788831 : m_layout.m_char_policy.m_print_cb (&m_pp, cp);
1951 : 3788831 : c = dw.next_byte ();
1952 : : }
1953 : 43391 : print_newline ();
1954 : 43391 : return lbounds;
1955 : : }
1956 : :
1957 : : /* Determine if we should print an annotation line for ROW.
1958 : : i.e. if any of m_layout_ranges contains ROW. */
1959 : :
1960 : : bool
1961 : 43391 : layout::should_print_annotation_line_p (linenum_type row) const
1962 : : {
1963 : 43391 : layout_range *range;
1964 : 43391 : int i;
1965 : 47919 : FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1966 : : {
1967 : 46933 : if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1968 : : return false;
1969 : 40780 : if (range->intersects_line_p (row))
1970 : : return true;
1971 : : }
1972 : : return false;
1973 : : }
1974 : :
1975 : : /* Print the leftmost column after the margin, which is used for showing
1976 : : links between labels (e.g. for CFG edges in execution paths). */
1977 : :
1978 : : void
1979 : 104669 : layout_printer::print_leftmost_column ()
1980 : : {
1981 : 104669 : if (!get_options ().show_event_links_p)
1982 : 52194 : gcc_assert (m_link_lhs_state == link_lhs_state::none);
1983 : :
1984 : 104669 : switch (m_link_lhs_state)
1985 : : {
1986 : 0 : default:
1987 : 0 : gcc_unreachable ();
1988 : 98059 : case link_lhs_state::none:
1989 : 98059 : pp_space (&m_pp);
1990 : 98059 : break;
1991 : 1254 : case link_lhs_state::rewinding_to_lhs:
1992 : 1254 : {
1993 : 1254 : m_colorizer.set_cfg_edge ();
1994 : 1254 : const cppchar_t ch = get_theme ().get_cppchar
1995 : 1254 : (text_art::theme::cell_kind::CFG_FROM_LEFT_TO_DOWN);
1996 : 1254 : pp_unicode_character (&m_pp, ch);
1997 : 1254 : m_colorizer.set_normal_text ();
1998 : : }
1999 : 1254 : break;
2000 : 4042 : case link_lhs_state::at_lhs:
2001 : 4042 : {
2002 : 4042 : m_colorizer.set_cfg_edge ();
2003 : 4042 : const cppchar_t ch = get_theme ().get_cppchar
2004 : 4042 : (text_art::theme::cell_kind::CFG_DOWN);
2005 : 4042 : pp_unicode_character (&m_pp, ch);
2006 : 4042 : m_colorizer.set_normal_text ();
2007 : : }
2008 : 4042 : break;
2009 : 1314 : case link_lhs_state::indenting_to_dest:
2010 : 1314 : {
2011 : 1314 : m_colorizer.set_cfg_edge ();
2012 : 1314 : const cppchar_t ch = get_theme ().get_cppchar
2013 : 1314 : (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_RIGHT);
2014 : 1314 : pp_unicode_character (&m_pp, ch);
2015 : 1314 : m_colorizer.set_normal_text ();
2016 : : }
2017 : 1314 : break;
2018 : : }
2019 : 104669 : }
2020 : :
2021 : : /* Begin an annotation line. If m_show_line_numbers_p, print the left
2022 : : margin, which is empty for annotation lines.
2023 : : After any left margin, print a leftmost column, which is used for
2024 : : showing links between labels (e.g. for CFG edges in execution paths). */
2025 : :
2026 : : void
2027 : 61715 : layout_printer::start_annotation_line (char margin_char)
2028 : : {
2029 : 61715 : pp_emit_prefix (&m_pp);
2030 : 61715 : if (get_options ().show_line_numbers_p)
2031 : : {
2032 : : /* Print the margin. If MARGIN_CHAR != ' ', then print up to 3
2033 : : of it, right-aligned, padded with spaces. */
2034 : : int i;
2035 : 104111 : for (i = 0; i < m_layout.m_linenum_width - 3; i++)
2036 : 68524 : pp_space (&m_pp);
2037 : 141199 : for (; i < m_layout.m_linenum_width; i++)
2038 : 105612 : pp_character (&m_pp, margin_char);
2039 : 35587 : pp_string (&m_pp, " |");
2040 : : }
2041 : 61715 : if (margin_char == ' ')
2042 : 61278 : print_leftmost_column ();
2043 : : else
2044 : 437 : pp_character (&m_pp, margin_char);
2045 : 61715 : }
2046 : :
2047 : : /* Print a line consisting of the caret/underlines for the given
2048 : : source line. */
2049 : :
2050 : : void
2051 : 36252 : layout_printer::print_annotation_line (linenum_type row,
2052 : : const line_bounds lbounds)
2053 : : {
2054 : 36252 : int x_bound = m_layout.get_x_bound_for_row (row,
2055 : 36252 : m_layout.m_exploc.m_display_col,
2056 : : lbounds.m_last_non_ws_disp_col);
2057 : :
2058 : 36252 : start_annotation_line ();
2059 : :
2060 : 10956324 : for (int column = 1 + m_layout.m_x_offset_display; column < x_bound; column++)
2061 : : {
2062 : 10920072 : bool in_range_p;
2063 : 10920072 : point_state state;
2064 : 21840144 : in_range_p = m_layout.get_state_at_point (row, column,
2065 : 10920072 : lbounds.m_first_non_ws_disp_col,
2066 : : lbounds.m_last_non_ws_disp_col,
2067 : : CU_DISPLAY_COLS,
2068 : : &state);
2069 : 10920072 : if (in_range_p)
2070 : : {
2071 : : /* Within a range. Draw either the caret or an underline. */
2072 : 141566 : m_colorizer.set_range (state.range_idx);
2073 : 141566 : if (state.draw_caret_p)
2074 : : {
2075 : : /* Draw the caret. */
2076 : 34721 : char caret_char;
2077 : 34721 : if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
2078 : 34654 : caret_char = get_options ().caret_chars[state.range_idx];
2079 : : else
2080 : : caret_char = '^';
2081 : 34721 : pp_character (&m_pp, caret_char);
2082 : : }
2083 : : else
2084 : 106845 : pp_character (&m_pp, '~');
2085 : : }
2086 : : else
2087 : : {
2088 : : /* Not in a range. */
2089 : 10778506 : m_colorizer.set_normal_text ();
2090 : 10778506 : pp_character (&m_pp, ' ');
2091 : : }
2092 : : }
2093 : 36252 : print_newline ();
2094 : 36252 : }
2095 : :
2096 : : /* A version of label_text that can live inside a vec.
2097 : : Requires manual cleanup via maybe_free. */
2098 : :
2099 : : struct pod_label_text
2100 : : {
2101 : : pod_label_text ()
2102 : : : m_buffer (NULL), m_caller_owned (false)
2103 : : {}
2104 : :
2105 : 8588 : pod_label_text (label_text &&other)
2106 : 8588 : : m_buffer (const_cast<char*> (other.get ())),
2107 : 8588 : m_caller_owned (other.is_owner ())
2108 : : {
2109 : 8588 : other.release ();
2110 : : }
2111 : :
2112 : 8588 : void maybe_free ()
2113 : : {
2114 : 8588 : if (m_caller_owned)
2115 : 5176 : free (m_buffer);
2116 : : }
2117 : :
2118 : : char *m_buffer;
2119 : : bool m_caller_owned;
2120 : : };
2121 : :
2122 : : /* Implementation detail of layout::print_any_labels.
2123 : :
2124 : : A label within the given row of source. */
2125 : :
2126 : : class line_label
2127 : : {
2128 : : public:
2129 : 8588 : line_label (int state_idx, int column,
2130 : : label_text text,
2131 : : bool has_in_edge,
2132 : : bool has_out_edge)
2133 : 8588 : : m_state_idx (state_idx), m_column (column),
2134 : 8588 : m_text (std::move (text)), m_label_line (0), m_has_vbar (true),
2135 : 8588 : m_has_in_edge (has_in_edge),
2136 : 8588 : m_has_out_edge (has_out_edge)
2137 : : {
2138 : : /* Using styled_string rather than cpp_display_width here
2139 : : lets us skip SGR formatting characters for color and URLs.
2140 : : It doesn't handle tabs and unicode escaping, but we don't
2141 : : expect to see either of those in labels. */
2142 : 8588 : text_art::style_manager sm;
2143 : 8588 : text_art::styled_string str (sm, m_text.m_buffer);
2144 : 8588 : m_display_width = str.calc_canvas_width ();
2145 : 8588 : }
2146 : :
2147 : : /* Sorting is primarily by column, then by state index. */
2148 : 19757 : static int comparator (const void *p1, const void *p2)
2149 : : {
2150 : 19757 : const line_label *ll1 = (const line_label *)p1;
2151 : 19757 : const line_label *ll2 = (const line_label *)p2;
2152 : 19757 : int column_cmp = compare (ll1->m_column, ll2->m_column);
2153 : 19757 : if (column_cmp)
2154 : 14449 : return column_cmp;
2155 : : /* Order by reverse state index, so that labels are printed
2156 : : in order of insertion into the rich_location when the
2157 : : sorted list is walked backwards. */
2158 : 10616 : return -compare (ll1->m_state_idx, ll2->m_state_idx);
2159 : : }
2160 : :
2161 : : int m_state_idx;
2162 : : int m_column;
2163 : : pod_label_text m_text;
2164 : : size_t m_display_width;
2165 : : int m_label_line;
2166 : : bool m_has_vbar;
2167 : : bool m_has_in_edge;
2168 : : bool m_has_out_edge;
2169 : : };
2170 : :
2171 : : /* Print any labels in this row. */
2172 : : void
2173 : 43009 : layout_printer::print_any_labels (linenum_type row)
2174 : : {
2175 : 43009 : int i;
2176 : 43009 : auto_vec<line_label> labels;
2177 : :
2178 : : /* Gather the labels that are to be printed into "labels". */
2179 : 43009 : {
2180 : 43009 : layout_range *range;
2181 : 96685 : FOR_EACH_VEC_ELT (m_layout.m_layout_ranges, i, range)
2182 : : {
2183 : : /* Most ranges don't have labels, so reject this first. */
2184 : 53676 : if (range->m_label == NULL)
2185 : 45088 : continue;
2186 : :
2187 : : /* The range's caret must be on this line. */
2188 : 15841 : if (range->m_caret.m_line != row)
2189 : 7187 : continue;
2190 : :
2191 : : /* Reject labels that aren't fully visible due to clipping
2192 : : by m_x_offset_display. */
2193 : 8654 : const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
2194 : 8654 : if (disp_col <= m_layout.m_x_offset_display)
2195 : 2 : continue;
2196 : :
2197 : 8652 : label_text text;
2198 : 8652 : text = range->m_label->get_text (range->m_original_idx);
2199 : :
2200 : : /* Allow for labels that return NULL from their get_text
2201 : : implementation (so e.g. such labels can control their own
2202 : : visibility). */
2203 : 8652 : if (text.get () == NULL)
2204 : 64 : continue;
2205 : :
2206 : 8588 : labels.safe_push (line_label (i, disp_col, std::move (text),
2207 : 8588 : range->has_in_edge (),
2208 : 17176 : range->has_out_edge ()));
2209 : 8652 : }
2210 : : }
2211 : :
2212 : : /* Bail out if there are no labels on this row. */
2213 : 43009 : if (labels.length () == 0)
2214 : 37530 : return;
2215 : :
2216 : : /* Sort them. */
2217 : 5479 : labels.qsort(line_label::comparator);
2218 : :
2219 : : /* Figure out how many "label lines" we need, and which
2220 : : one each label is printed in.
2221 : :
2222 : : For example, if the labels aren't too densely packed,
2223 : : we can fit them on the same line, giving two "label lines":
2224 : :
2225 : : foo + bar
2226 : : ~~~ ~~~
2227 : : | | : label line 0
2228 : : l0 l1 : label line 1
2229 : :
2230 : : If they would touch each other or overlap, then we need
2231 : : additional "label lines":
2232 : :
2233 : : foo + bar
2234 : : ~~~ ~~~
2235 : : | | : label line 0
2236 : : | label 1 : label line 1
2237 : : label 0 : label line 2
2238 : :
2239 : : Place the final label on label line 1, and work backwards, adding
2240 : : label lines as needed.
2241 : :
2242 : : If multiple labels are at the same place, put them on separate
2243 : : label lines:
2244 : :
2245 : : foo + bar
2246 : : ^ : label line 0
2247 : : | : label line 1
2248 : : label 0 : label line 2
2249 : : label 1 : label line 3. */
2250 : :
2251 : 5479 : int max_label_line = 1;
2252 : 5479 : int label_line_with_in_edge = -1;
2253 : 5479 : {
2254 : 5479 : int next_column = INT_MAX;
2255 : 5479 : line_label *label;
2256 : 14067 : FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
2257 : : {
2258 : : /* Would this label "touch" or overlap the next label? */
2259 : 8588 : if (label->m_column + label->m_display_width >= (size_t)next_column)
2260 : : {
2261 : 1874 : max_label_line++;
2262 : :
2263 : : /* If we've already seen labels with the same column, suppress the
2264 : : vertical bar for subsequent ones in this backwards iteration;
2265 : : hence only the one with the highest label_line has m_has_vbar set. */
2266 : 1874 : if (label->m_column == next_column)
2267 : 1151 : label->m_has_vbar = false;
2268 : : }
2269 : :
2270 : 8588 : label->m_label_line = max_label_line;
2271 : 8588 : if (get_options ().show_event_links_p)
2272 : 3380 : if (label->m_has_in_edge)
2273 : 8588 : label_line_with_in_edge = max_label_line;
2274 : 8588 : next_column = label->m_column;
2275 : : }
2276 : : }
2277 : :
2278 : 5479 : gcc_assert (labels.length () > 0);
2279 : :
2280 : : /* Print the "label lines". For each label within the line, print
2281 : : either a vertical bar ('|') for the labels that are lower down, or the
2282 : : labels themselves once we've reached their line. */
2283 : : {
2284 : 18311 : for (int label_line = 0; label_line <= max_label_line; label_line++)
2285 : : {
2286 : 12832 : if (label_line == label_line_with_in_edge)
2287 : : {
2288 : 1314 : gcc_assert (get_options ().show_event_links_p);
2289 : 1314 : m_link_lhs_state = link_lhs_state::indenting_to_dest;
2290 : : }
2291 : 12832 : start_annotation_line ();
2292 : :
2293 : 12832 : int column = 1 + m_layout.m_x_offset_display;
2294 : 12832 : line_label *label;
2295 : 34100 : FOR_EACH_VEC_ELT (labels, i, label)
2296 : : {
2297 : 23142 : if (label_line > label->m_label_line)
2298 : : /* We've printed all the labels for this label line. */
2299 : : break;
2300 : :
2301 : 21268 : if (label_line == label->m_label_line)
2302 : : {
2303 : 8588 : gcc_assert (column <= label->m_column);
2304 : :
2305 : 8588 : if (label_line == label_line_with_in_edge)
2306 : : {
2307 : : /* Print a prefix showing an incoming
2308 : : link from another label.
2309 : : .|+----------->(10) ...to here
2310 : : . ^~~~~~~~~~~~~
2311 : : . this text. */
2312 : 1314 : gcc_assert (get_options ().show_event_links_p);
2313 : 1314 : m_colorizer.set_cfg_edge ();
2314 : 1314 : const cppchar_t right= get_theme ().get_cppchar
2315 : 1314 : (text_art::theme::cell_kind::CFG_RIGHT);
2316 : 13834 : while (column < label->m_column - 1)
2317 : : {
2318 : 11206 : pp_unicode_character (&m_pp, right);
2319 : 11206 : column++;
2320 : : }
2321 : 1314 : if (column == label->m_column - 1)
2322 : : {
2323 : 1030 : pp_character (&m_pp, '>');
2324 : 1030 : column++;
2325 : : }
2326 : 1314 : m_colorizer.set_normal_text ();
2327 : 1314 : m_link_lhs_state = link_lhs_state::none;
2328 : 1314 : label_line_with_in_edge = -1;
2329 : : }
2330 : : else
2331 : 7274 : move_to_column (&column, label->m_column, true);
2332 : 8588 : gcc_assert (column == label->m_column);
2333 : : /* Colorize the text, unless it's for events in a
2334 : : diagnostic_path. */
2335 : 8588 : if (!m_is_diagnostic_path)
2336 : 3967 : m_colorizer.set_range (label->m_state_idx);
2337 : 8588 : pp_string (&m_pp, label->m_text.m_buffer);
2338 : 8588 : m_colorizer.set_normal_text ();
2339 : 8588 : column += label->m_display_width;
2340 : 8588 : if (get_options ().show_event_links_p && label->m_has_out_edge)
2341 : : {
2342 : : /* Print a suffix showing the start of a linkage
2343 : : to another label e.g. " ->-+" which will be the
2344 : : first part of e.g.
2345 : : . (9) following ‘false’ branch... ->-+ <- HERE
2346 : : . |
2347 : : . |
2348 : : . */
2349 : 1314 : const cppchar_t right= get_theme ().get_cppchar
2350 : 1314 : (text_art::theme::cell_kind::CFG_RIGHT);
2351 : 1314 : const cppchar_t from_right_to_down= get_theme ().get_cppchar
2352 : 1314 : (text_art::theme::cell_kind::CFG_FROM_RIGHT_TO_DOWN);
2353 : 1314 : m_colorizer.set_cfg_edge ();
2354 : 1314 : pp_space (&m_pp);
2355 : 1314 : pp_unicode_character (&m_pp, right);
2356 : 1314 : pp_unicode_character (&m_pp, '>');
2357 : 1314 : pp_unicode_character (&m_pp, right);
2358 : 1314 : pp_unicode_character (&m_pp, from_right_to_down);
2359 : 1314 : m_colorizer.set_normal_text ();
2360 : 1314 : column += 5;
2361 : 1314 : m_link_rhs_column = column - 1;
2362 : : }
2363 : : }
2364 : 12680 : else if (label->m_has_vbar)
2365 : : {
2366 : 8968 : gcc_assert (column <= label->m_column);
2367 : 8968 : move_to_column (&column, label->m_column, true);
2368 : 8968 : m_colorizer.set_range (label->m_state_idx);
2369 : 8968 : pp_character (&m_pp, '|');
2370 : 8968 : m_colorizer.set_normal_text ();
2371 : 8968 : column++;
2372 : : }
2373 : : }
2374 : :
2375 : : /* If we have a vertical link line on the RHS, print the
2376 : : '|' on this annotation line after the labels. */
2377 : 12832 : if (m_link_rhs_column != -1 && column < m_link_rhs_column)
2378 : : {
2379 : 56 : move_to_column (&column, m_link_rhs_column, true);
2380 : 56 : m_colorizer.set_cfg_edge ();
2381 : 56 : const cppchar_t down= get_theme ().get_cppchar
2382 : 56 : (text_art::theme::cell_kind::CFG_DOWN);
2383 : 56 : pp_unicode_character (&m_pp, down);
2384 : 56 : m_colorizer.set_normal_text ();
2385 : : }
2386 : :
2387 : 12832 : print_newline ();
2388 : : }
2389 : : }
2390 : :
2391 : : /* If we have a vertical link line on the RHS, print a trailing
2392 : : annotation line showing the vertical line. */
2393 : 5479 : if (m_link_rhs_column != -1)
2394 : : {
2395 : 1314 : int column = 1 + m_layout.m_x_offset_display;
2396 : 1314 : start_annotation_line ();
2397 : 1314 : move_to_column (&column, m_link_rhs_column, true);
2398 : 1314 : m_colorizer.set_cfg_edge ();
2399 : 1314 : const cppchar_t down= get_theme ().get_cppchar
2400 : 1314 : (text_art::theme::cell_kind::CFG_DOWN);
2401 : 1314 : pp_unicode_character (&m_pp, down);
2402 : 1314 : m_colorizer.set_normal_text ();
2403 : 1314 : print_newline ();
2404 : : }
2405 : :
2406 : : /* Clean up. */
2407 : : {
2408 : : line_label *label;
2409 : 14067 : FOR_EACH_VEC_ELT (labels, i, label)
2410 : 13764 : label->m_text.maybe_free ();
2411 : : }
2412 : 43009 : }
2413 : :
2414 : : /* If there are any fixit hints inserting new lines before source line ROW,
2415 : : print them.
2416 : :
2417 : : They are printed on lines of their own, before the source line
2418 : : itself, with a leading '+'. */
2419 : :
2420 : : void
2421 : 43391 : layout_printer::print_leading_fixits (linenum_type row)
2422 : : {
2423 : 58031 : for (unsigned int i = 0; i < m_layout.m_fixit_hints.length (); i++)
2424 : : {
2425 : 14640 : const fixit_hint *hint = m_layout.m_fixit_hints[i];
2426 : :
2427 : 14640 : if (!hint->ends_with_newline_p ())
2428 : : /* Not a newline fixit; print it in print_trailing_fixits. */
2429 : 13731 : continue;
2430 : :
2431 : 909 : gcc_assert (hint->insertion_p ());
2432 : :
2433 : 909 : if (hint->affects_line_p (m_layout.m_line_table,
2434 : 909 : m_layout.m_exploc.file,
2435 : : row))
2436 : : {
2437 : : /* Printing the '+' with normal colorization
2438 : : and the inserted line with "insert" colorization
2439 : : helps them stand out from each other, and from
2440 : : the surrounding text. */
2441 : 437 : m_colorizer.set_normal_text ();
2442 : 437 : start_annotation_line ('+');
2443 : 437 : m_colorizer.set_fixit_insert ();
2444 : : /* Print all but the trailing newline of the fix-it hint.
2445 : : We have to print the newline separately to avoid
2446 : : getting additional pp prefixes printed. */
2447 : 7386 : for (size_t i = 0; i < hint->get_length () - 1; i++)
2448 : 6949 : pp_character (&m_pp, hint->get_string ()[i]);
2449 : 437 : m_colorizer.set_normal_text ();
2450 : 437 : pp_newline (&m_pp);
2451 : : }
2452 : : }
2453 : 43391 : }
2454 : :
2455 : : /* Subroutine of layout::print_trailing_fixits.
2456 : :
2457 : : Determine if the annotation line printed for LINE contained
2458 : : the exact range from START_COLUMN to FINISH_COLUMN (in display units). */
2459 : :
2460 : : bool
2461 : 3896 : layout::annotation_line_showed_range_p (linenum_type line, int start_column,
2462 : : int finish_column) const
2463 : : {
2464 : 3896 : layout_range *range;
2465 : 3896 : int i;
2466 : 8065 : FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2467 : 6439 : if (range->m_start.m_line == line
2468 : 4297 : && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
2469 : 2839 : && range->m_finish.m_line == line
2470 : 2839 : && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
2471 : : return true;
2472 : : return false;
2473 : : }
2474 : :
2475 : : /* Classes for printing trailing fix-it hints i.e. those that
2476 : : don't add new lines.
2477 : :
2478 : : For insertion, these can look like:
2479 : :
2480 : : new_text
2481 : :
2482 : : For replacement, these can look like:
2483 : :
2484 : : ------------- : underline showing affected range
2485 : : new_text
2486 : :
2487 : : For deletion, these can look like:
2488 : :
2489 : : ------------- : underline showing affected range
2490 : :
2491 : : This can become confusing if they overlap, and so we need
2492 : : to do some preprocessing to decide what to print.
2493 : : We use the list of fixit_hint instances affecting the line
2494 : : to build a list of "correction" instances, and print the
2495 : : latter.
2496 : :
2497 : : For example, consider a set of fix-its for converting
2498 : : a C-style cast to a C++ const_cast.
2499 : :
2500 : : Given:
2501 : :
2502 : : ..000000000111111111122222222223333333333.
2503 : : ..123456789012345678901234567890123456789.
2504 : : foo *f = (foo *)ptr->field;
2505 : : ^~~~~
2506 : :
2507 : : and the fix-it hints:
2508 : : - replace col 10 (the open paren) with "const_cast<"
2509 : : - replace col 16 (the close paren) with "> ("
2510 : : - insert ")" before col 27
2511 : :
2512 : : then we would get odd-looking output:
2513 : :
2514 : : foo *f = (foo *)ptr->field;
2515 : : ^~~~~
2516 : : -
2517 : : const_cast<
2518 : : -
2519 : : > ( )
2520 : :
2521 : : It would be better to detect when fixit hints are going to
2522 : : overlap (those that require new lines), and to consolidate
2523 : : the printing of such fixits, giving something like:
2524 : :
2525 : : foo *f = (foo *)ptr->field;
2526 : : ^~~~~
2527 : : -----------------
2528 : : const_cast<foo *> (ptr->field)
2529 : :
2530 : : This works by detecting when the printing would overlap, and
2531 : : effectively injecting no-op replace hints into the gaps between
2532 : : such fix-its, so that the printing joins up.
2533 : :
2534 : : In the above example, the overlap of:
2535 : : - replace col 10 (the open paren) with "const_cast<"
2536 : : and:
2537 : : - replace col 16 (the close paren) with "> ("
2538 : : is fixed by injecting a no-op:
2539 : : - replace cols 11-15 with themselves ("foo *")
2540 : : and consolidating these, making:
2541 : : - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
2542 : : i.e.:
2543 : : - replace cols 10-16 with "const_cast<foo *> ("
2544 : :
2545 : : This overlaps with the final fix-it hint:
2546 : : - insert ")" before col 27
2547 : : and so we repeat the consolidation process, by injecting
2548 : : a no-op:
2549 : : - replace cols 17-26 with themselves ("ptr->field")
2550 : : giving:
2551 : : - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
2552 : : i.e.:
2553 : : - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
2554 : :
2555 : : and is thus printed as desired. */
2556 : :
2557 : : /* A range of (byte or display) columns within a line. */
2558 : :
2559 : : class column_range
2560 : : {
2561 : : public:
2562 : 43552 : column_range (int start_, int finish_) : start (start_), finish (finish_)
2563 : : {
2564 : 0 : gcc_assert (valid_p (start, finish));
2565 : 41280 : }
2566 : :
2567 : 2272 : bool operator== (const column_range &other) const
2568 : : {
2569 : 2272 : return start == other.start && finish == other.finish;
2570 : : }
2571 : :
2572 : 44709 : static bool valid_p (int start, int finish)
2573 : : {
2574 : : /* We must have either a range, or an insertion. */
2575 : 41540 : return (start <= finish || finish == start - 1);
2576 : : }
2577 : :
2578 : : int start;
2579 : : int finish;
2580 : : };
2581 : :
2582 : : /* Get the range of bytes or display columns that HINT would affect. */
2583 : : static column_range
2584 : 26706 : get_affected_range (file_cache &fc,
2585 : : const cpp_char_column_policy &policy,
2586 : : const fixit_hint *hint, enum column_unit col_unit)
2587 : : {
2588 : 26706 : expanded_location exploc_start = expand_location (hint->get_start_loc ());
2589 : 26706 : expanded_location exploc_finish = expand_location (hint->get_next_loc ());
2590 : 26706 : --exploc_finish.column;
2591 : :
2592 : 26706 : int start_column;
2593 : 26706 : int finish_column;
2594 : 26706 : if (col_unit == CU_DISPLAY_COLS)
2595 : : {
2596 : 13289 : start_column = location_compute_display_column (fc, exploc_start, policy);
2597 : 13289 : if (hint->insertion_p ())
2598 : 8995 : finish_column = start_column - 1;
2599 : : else
2600 : 4294 : finish_column
2601 : 4294 : = location_compute_display_column (fc, exploc_finish, policy);
2602 : : }
2603 : : else
2604 : : {
2605 : 13417 : start_column = exploc_start.column;
2606 : 13417 : finish_column = exploc_finish.column;
2607 : : }
2608 : 26706 : return column_range (start_column, finish_column);
2609 : : }
2610 : :
2611 : : /* Get the range of display columns that would be printed for HINT. */
2612 : :
2613 : : static column_range
2614 : 13417 : get_printed_columns (file_cache &fc,
2615 : : const cpp_char_column_policy &policy,
2616 : : const fixit_hint *hint)
2617 : : {
2618 : 13417 : expanded_location exploc = expand_location (hint->get_start_loc ());
2619 : 13417 : int start_column = location_compute_display_column (fc, exploc, policy);
2620 : 13417 : int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
2621 : : policy);
2622 : 13417 : int final_hint_column = start_column + hint_width - 1;
2623 : 13417 : if (hint->insertion_p ())
2624 : : {
2625 : 9123 : return column_range (start_column, final_hint_column);
2626 : : }
2627 : : else
2628 : : {
2629 : 4294 : exploc = expand_location (hint->get_next_loc ());
2630 : 4294 : --exploc.column;
2631 : 4294 : int finish_column = location_compute_display_column (fc, exploc, policy);
2632 : 8588 : return column_range (start_column,
2633 : 4294 : MAX (finish_column, final_hint_column));
2634 : : }
2635 : : }
2636 : :
2637 : : /* A correction on a particular line.
2638 : : This describes a plan for how to print one or more fixit_hint
2639 : : instances that affected the line, potentially consolidating hints
2640 : : into corrections to make the result easier for the user to read. */
2641 : :
2642 : : class correction
2643 : : {
2644 : : public:
2645 : 11796 : correction (column_range affected_bytes,
2646 : : column_range affected_columns,
2647 : : column_range printed_columns,
2648 : : const char *new_text, size_t new_text_len,
2649 : : const cpp_char_column_policy &policy)
2650 : 11796 : : m_affected_bytes (affected_bytes),
2651 : 11796 : m_affected_columns (affected_columns),
2652 : 11796 : m_printed_columns (printed_columns),
2653 : 11796 : m_text (xstrdup (new_text)),
2654 : 11796 : m_byte_length (new_text_len),
2655 : 11796 : m_policy (policy),
2656 : 11796 : m_alloc_sz (new_text_len + 1)
2657 : : {
2658 : 11796 : compute_display_cols ();
2659 : 11796 : }
2660 : :
2661 : 11796 : ~correction () { free (m_text); }
2662 : :
2663 : 11684 : bool insertion_p () const
2664 : : {
2665 : 11684 : return m_affected_bytes.start == m_affected_bytes.finish + 1;
2666 : : }
2667 : :
2668 : : void ensure_capacity (size_t len);
2669 : : void ensure_terminated ();
2670 : :
2671 : 12953 : void compute_display_cols ()
2672 : : {
2673 : 12953 : m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
2674 : 12953 : }
2675 : :
2676 : 2314 : void overwrite (int dst_offset, const char_span &src_span)
2677 : : {
2678 : 2314 : gcc_assert (dst_offset >= 0);
2679 : 2314 : gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
2680 : 2314 : memcpy (m_text + dst_offset, src_span.get_buffer (),
2681 : : src_span.length ());
2682 : 2314 : }
2683 : :
2684 : : /* If insert, then start: the column before which the text
2685 : : is to be inserted, and finish is offset by the length of
2686 : : the replacement.
2687 : : If replace, then the range of columns affected. */
2688 : : column_range m_affected_bytes;
2689 : : column_range m_affected_columns;
2690 : :
2691 : : /* If insert, then start: the column before which the text
2692 : : is to be inserted, and finish is offset by the length of
2693 : : the replacement.
2694 : : If replace, then the range of columns affected. */
2695 : : column_range m_printed_columns;
2696 : :
2697 : : /* The text to be inserted/used as replacement. */
2698 : : char *m_text;
2699 : : size_t m_byte_length; /* Not including null-terminator. */
2700 : : int m_display_cols;
2701 : : const cpp_char_column_policy &m_policy;
2702 : : size_t m_alloc_sz;
2703 : : };
2704 : :
2705 : : /* Ensure that m_text can hold a string of length LEN
2706 : : (plus 1 for 0-termination). */
2707 : :
2708 : : void
2709 : 1157 : correction::ensure_capacity (size_t len)
2710 : : {
2711 : : /* Allow 1 extra byte for 0-termination. */
2712 : 1157 : if (m_alloc_sz < (len + 1))
2713 : : {
2714 : 593 : size_t new_alloc_sz = (len + 1) * 2;
2715 : 593 : m_text = (char *)xrealloc (m_text, new_alloc_sz);
2716 : 593 : m_alloc_sz = new_alloc_sz;
2717 : : }
2718 : 1157 : }
2719 : :
2720 : : /* Ensure that m_text is 0-terminated. */
2721 : :
2722 : : void
2723 : 1157 : correction::ensure_terminated ()
2724 : : {
2725 : : /* 0-terminate the buffer. */
2726 : 1157 : gcc_assert (m_byte_length < m_alloc_sz);
2727 : 1157 : m_text[m_byte_length] = '\0';
2728 : 1157 : }
2729 : :
2730 : : /* A list of corrections affecting a particular line.
2731 : : This is used by layout::print_trailing_fixits for planning
2732 : : how to print the fix-it hints affecting the line. */
2733 : :
2734 : : class line_corrections
2735 : : {
2736 : : public:
2737 : 43503 : line_corrections (file_cache &fc,
2738 : : const char_display_policy &policy,
2739 : : const char *filename,
2740 : : linenum_type row)
2741 : 43503 : : m_file_cache (fc),
2742 : 112 : m_policy (policy), m_filename (filename), m_row (row)
2743 : : {}
2744 : : ~line_corrections ();
2745 : :
2746 : : void add_hint (const fixit_hint *hint);
2747 : :
2748 : : file_cache &m_file_cache;
2749 : : const char_display_policy &m_policy;
2750 : : const char *m_filename;
2751 : : linenum_type m_row;
2752 : : auto_vec <correction *> m_corrections;
2753 : : };
2754 : :
2755 : : /* struct line_corrections. */
2756 : :
2757 : 43503 : line_corrections::~line_corrections ()
2758 : : {
2759 : 43503 : unsigned i;
2760 : 43503 : correction *c;
2761 : 55299 : FOR_EACH_VEC_ELT (m_corrections, i, c)
2762 : 11796 : delete c;
2763 : 43503 : }
2764 : :
2765 : : /* A struct wrapping a particular source line, allowing
2766 : : run-time bounds-checking of accesses in a checked build. */
2767 : :
2768 : : class source_line
2769 : : {
2770 : : public:
2771 : : source_line (file_cache &fc, const char *filename, int line);
2772 : :
2773 : 1157 : char_span as_span () { return char_span (chars, width); }
2774 : :
2775 : : const char *chars;
2776 : : int width;
2777 : : };
2778 : :
2779 : : /* source_line's ctor. */
2780 : :
2781 : 1157 : source_line::source_line (file_cache &fc, const char *filename, int line)
2782 : : {
2783 : 0 : char_span span = fc.get_source_line (filename, line);
2784 : 1157 : chars = span.get_buffer ();
2785 : 1157 : width = span.length ();
2786 : 0 : }
2787 : :
2788 : : /* Add HINT to the corrections for this line.
2789 : : Attempt to consolidate nearby hints so that they will not
2790 : : overlap with printed. */
2791 : :
2792 : : void
2793 : 12953 : line_corrections::add_hint (const fixit_hint *hint)
2794 : : {
2795 : 12953 : column_range affected_bytes
2796 : 12953 : = get_affected_range (m_file_cache, m_policy, hint, CU_BYTES);
2797 : 12953 : column_range affected_columns
2798 : 12953 : = get_affected_range (m_file_cache, m_policy, hint, CU_DISPLAY_COLS);
2799 : 12953 : column_range printed_columns
2800 : 12953 : = get_printed_columns (m_file_cache, m_policy, hint);
2801 : :
2802 : : /* Potentially consolidate. */
2803 : 12953 : if (!m_corrections.is_empty ())
2804 : : {
2805 : 6679 : correction *last_correction
2806 : 6679 : = m_corrections[m_corrections.length () - 1];
2807 : :
2808 : : /* The following consolidation code assumes that the fix-it hints
2809 : : have been sorted by start (done within layout's ctor). */
2810 : 6679 : gcc_assert (affected_bytes.start
2811 : : >= last_correction->m_affected_bytes.start);
2812 : 6679 : gcc_assert (printed_columns.start
2813 : : >= last_correction->m_printed_columns.start);
2814 : :
2815 : 6679 : if (printed_columns.start <= last_correction->m_printed_columns.finish
2816 : 6679 : && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
2817 : : affected_bytes.start - 1))
2818 : : {
2819 : : /* We have two hints for which the printed forms of the hints
2820 : : would touch or overlap, so we need to consolidate them to avoid
2821 : : confusing the user.
2822 : : Attempt to inject a "replace" correction from immediately
2823 : : after the end of the last hint to immediately before the start
2824 : : of the next hint. */
2825 : 1157 : column_range between (last_correction->m_affected_bytes.finish + 1,
2826 : 1157 : affected_bytes.start - 1);
2827 : :
2828 : : /* Try to read the source. */
2829 : 1157 : source_line line (m_file_cache, m_filename, m_row);
2830 : 1157 : if (line.chars && between.finish < line.width)
2831 : : {
2832 : : /* Consolidate into the last correction:
2833 : : add a no-op "replace" of the "between" text, and
2834 : : add the text from the new hint. */
2835 : 1157 : int old_byte_len = last_correction->m_byte_length;
2836 : 1157 : gcc_assert (old_byte_len >= 0);
2837 : 1157 : int between_byte_len = between.finish + 1 - between.start;
2838 : 1157 : gcc_assert (between_byte_len >= 0);
2839 : 1157 : int new_byte_len
2840 : 1157 : = old_byte_len + between_byte_len + hint->get_length ();
2841 : 1157 : gcc_assert (new_byte_len >= 0);
2842 : 1157 : last_correction->ensure_capacity (new_byte_len);
2843 : 1157 : last_correction->overwrite
2844 : 1157 : (old_byte_len,
2845 : 1157 : line.as_span ().subspan (between.start - 1,
2846 : : between.finish + 1 - between.start));
2847 : 1157 : last_correction->overwrite (old_byte_len + between_byte_len,
2848 : 1157 : char_span (hint->get_string (),
2849 : 1157 : hint->get_length ()));
2850 : 1157 : last_correction->m_byte_length = new_byte_len;
2851 : 1157 : last_correction->ensure_terminated ();
2852 : 1157 : last_correction->m_affected_bytes.finish
2853 : 1157 : = affected_bytes.finish;
2854 : 1157 : last_correction->m_affected_columns.finish
2855 : 1157 : = affected_columns.finish;
2856 : 1157 : int prev_display_cols = last_correction->m_display_cols;
2857 : 1157 : last_correction->compute_display_cols ();
2858 : 1157 : last_correction->m_printed_columns.finish
2859 : 1157 : += last_correction->m_display_cols - prev_display_cols;
2860 : 1157 : return;
2861 : : }
2862 : : }
2863 : : }
2864 : :
2865 : : /* If no consolidation happened, add a new correction instance. */
2866 : 11796 : m_corrections.safe_push (new correction (affected_bytes,
2867 : : affected_columns,
2868 : : printed_columns,
2869 : : hint->get_string (),
2870 : : hint->get_length (),
2871 : 11796 : m_policy));
2872 : : }
2873 : :
2874 : : /* If there are any fixit hints on source line ROW, print them.
2875 : : They are printed in order, attempting to combine them onto lines, but
2876 : : starting new lines if necessary.
2877 : : Fix-it hints that insert new lines are handled separately,
2878 : : in layout::print_leading_fixits. */
2879 : :
2880 : : void
2881 : 43391 : layout_printer::print_trailing_fixits (linenum_type row)
2882 : : {
2883 : : /* Build a list of correction instances for the line,
2884 : : potentially consolidating hints (for the sake of readability). */
2885 : 43391 : line_corrections corrections (m_layout.m_file_cache, m_layout.m_char_policy,
2886 : 43391 : m_layout.m_exploc.file, row);
2887 : 58031 : for (unsigned int i = 0; i < m_layout.m_fixit_hints.length (); i++)
2888 : : {
2889 : 14640 : const fixit_hint *hint = m_layout.m_fixit_hints[i];
2890 : :
2891 : : /* Newline fixits are handled by layout::print_leading_fixits. */
2892 : 14640 : if (hint->ends_with_newline_p ())
2893 : 909 : continue;
2894 : :
2895 : 13731 : if (hint->affects_line_p (m_layout.m_line_table,
2896 : 13731 : m_layout.m_exploc.file,
2897 : : row))
2898 : 12617 : corrections.add_hint (hint);
2899 : : }
2900 : :
2901 : : /* Now print the corrections. */
2902 : 43391 : unsigned i;
2903 : 43391 : correction *c;
2904 : 43391 : int column = 1 + m_layout.m_x_offset_display;
2905 : :
2906 : 43391 : if (!corrections.m_corrections.is_empty ())
2907 : 6162 : start_annotation_line ();
2908 : :
2909 : 55075 : FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2910 : : {
2911 : : /* For now we assume each fixit hint can only touch one line. */
2912 : 11684 : if (c->insertion_p ())
2913 : : {
2914 : : /* This assumes the insertion just affects one line. */
2915 : 7788 : int start_column = c->m_printed_columns.start;
2916 : 7788 : move_to_column (&column, start_column, true);
2917 : 7788 : m_colorizer.set_fixit_insert ();
2918 : 7788 : pp_string (&m_pp, c->m_text);
2919 : 7788 : m_colorizer.set_normal_text ();
2920 : 7788 : column += c->m_display_cols;
2921 : : }
2922 : : else
2923 : : {
2924 : : /* If the range of the replacement wasn't printed in the
2925 : : annotation line, then print an extra underline to
2926 : : indicate exactly what is being replaced.
2927 : : Always show it for removals. */
2928 : 3896 : int start_column = c->m_affected_columns.start;
2929 : 3896 : int finish_column = c->m_affected_columns.finish;
2930 : 3896 : if (!m_layout.annotation_line_showed_range_p (row, start_column,
2931 : : finish_column)
2932 : 3896 : || c->m_byte_length == 0)
2933 : : {
2934 : 2394 : move_to_column (&column, start_column, true);
2935 : 2394 : m_colorizer.set_fixit_delete ();
2936 : 1872360 : for (; column <= finish_column; column++)
2937 : 1869966 : pp_character (&m_pp, '-');
2938 : 2394 : m_colorizer.set_normal_text ();
2939 : : }
2940 : : /* Print the replacement text. REPLACE also covers
2941 : : removals, so only do this extra work (potentially starting
2942 : : a new line) if we have actual replacement text. */
2943 : 3896 : if (c->m_byte_length > 0)
2944 : : {
2945 : 2868 : move_to_column (&column, start_column, true);
2946 : 2868 : m_colorizer.set_fixit_insert ();
2947 : 2868 : pp_string (&m_pp, c->m_text);
2948 : 2868 : m_colorizer.set_normal_text ();
2949 : 2868 : column += c->m_display_cols;
2950 : : }
2951 : : }
2952 : : }
2953 : :
2954 : : /* Add a trailing newline, if necessary. */
2955 : 43391 : move_to_column (&column, 1 + m_layout.m_x_offset_display, false);
2956 : 43391 : }
2957 : :
2958 : : /* Disable any colorization and emit a newline. */
2959 : :
2960 : : void
2961 : 101233 : layout_printer::print_newline ()
2962 : : {
2963 : 101233 : m_colorizer.set_normal_text ();
2964 : 101233 : pp_newline (&m_pp);
2965 : 101233 : }
2966 : :
2967 : : /* Return true if (ROW/COLUMN) is within a range of the layout.
2968 : : If it returns true, OUT_STATE is written to, with the
2969 : : range index, and whether we should draw the caret at
2970 : : (ROW/COLUMN) (as opposed to an underline). COL_UNIT controls
2971 : : whether all inputs and outputs are in bytes or display column units. */
2972 : :
2973 : : bool
2974 : 13848799 : layout::get_state_at_point (/* Inputs. */
2975 : : linenum_type row, int column,
2976 : : int first_non_ws, int last_non_ws,
2977 : : enum column_unit col_unit,
2978 : : /* Outputs. */
2979 : : point_state *out_state) const
2980 : : {
2981 : 13848799 : layout_range *range;
2982 : 13848799 : int i;
2983 : 27768415 : FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2984 : : {
2985 : 14175234 : if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2986 : : /* Bail out early, so that such ranges don't affect underlining or
2987 : : source colorization. */
2988 : 4389 : continue;
2989 : :
2990 : 14170845 : if (range->contains_point (row, column, col_unit))
2991 : : {
2992 : 255618 : out_state->range_idx = i;
2993 : :
2994 : : /* Are we at the range's caret? is it visible? */
2995 : 255618 : out_state->draw_caret_p = false;
2996 : 255618 : if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2997 : 210859 : && row == range->m_caret.m_line
2998 : 207628 : && column == range->m_caret.m_columns[col_unit])
2999 : 40743 : out_state->draw_caret_p = true;
3000 : :
3001 : : /* Within a multiline range, don't display any underline
3002 : : in any leading or trailing whitespace on a line.
3003 : : We do display carets, however. */
3004 : 255618 : if (!out_state->draw_caret_p)
3005 : 214875 : if (column < first_non_ws || column > last_non_ws)
3006 : : return false;
3007 : :
3008 : : /* We are within a range. */
3009 : : return true;
3010 : : }
3011 : : }
3012 : :
3013 : : return false;
3014 : : }
3015 : :
3016 : : /* Helper function for use by layout::print_line when printing the
3017 : : annotation line under the source line.
3018 : : Get the display column beyond the rightmost one that could contain a caret
3019 : : or range marker, given that we stop rendering at trailing whitespace.
3020 : : ROW is the source line within the given file.
3021 : : CARET_COLUMN is the display column of range 0's caret.
3022 : : LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
3023 : : character of source (as determined when printing the source line). */
3024 : :
3025 : : int
3026 : 36252 : layout::get_x_bound_for_row (linenum_type row, int caret_column,
3027 : : int last_non_ws_column) const
3028 : : {
3029 : 36252 : int result = caret_column + 1;
3030 : :
3031 : 36252 : layout_range *range;
3032 : 36252 : int i;
3033 : 82357 : FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
3034 : : {
3035 : 46105 : if (row >= range->m_start.m_line)
3036 : : {
3037 : 42930 : if (range->m_finish.m_line == row)
3038 : : {
3039 : : /* On the final line within a range; ensure that
3040 : : we render up to the end of the range. */
3041 : 40201 : const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
3042 : 40201 : if (result <= disp_col)
3043 : 13912 : result = disp_col + 1;
3044 : : }
3045 : 2729 : else if (row < range->m_finish.m_line)
3046 : : {
3047 : : /* Within a multiline range; ensure that we render up to the
3048 : : last non-whitespace column. */
3049 : 137 : if (result <= last_non_ws_column)
3050 : 63 : result = last_non_ws_column + 1;
3051 : : }
3052 : : }
3053 : : }
3054 : :
3055 : 36252 : return result;
3056 : : }
3057 : :
3058 : : /* Given *COLUMN as an x-coordinate, print spaces to position
3059 : : successive output at DEST_COLUMN, printing a newline if necessary,
3060 : : and updating *COLUMN. If ADD_LEFT_MARGIN, then print the (empty)
3061 : : left margin after any newline. */
3062 : :
3063 : : void
3064 : 75307 : layout_printer::move_to_column (int *column,
3065 : : int dest_column,
3066 : : bool add_left_margin)
3067 : : {
3068 : : /* Start a new line if we need to. */
3069 : 75307 : if (*column > dest_column)
3070 : : {
3071 : 7444 : print_newline ();
3072 : 7444 : if (add_left_margin)
3073 : 1366 : start_annotation_line ();
3074 : 7444 : *column = 1 + m_layout.m_x_offset_display;
3075 : : }
3076 : :
3077 : 4343067 : while (*column < dest_column)
3078 : : {
3079 : : /* For debugging column issues, it can be helpful to replace this
3080 : : pp_space call with
3081 : : pp_character (&m_pp, '0' + (*column % 10));
3082 : : to visualize the changing value of "*column". */
3083 : 4267760 : pp_space (&m_pp);
3084 : 4267760 : (*column)++;
3085 : : }
3086 : 75307 : }
3087 : :
3088 : : /* For debugging layout issues, render a ruler giving column numbers
3089 : : (after the 1-column indent). */
3090 : :
3091 : : void
3092 : 324 : layout_printer::show_ruler (int max_column)
3093 : : {
3094 : : /* Hundreds. */
3095 : 324 : if (max_column > 99)
3096 : : {
3097 : 196 : start_annotation_line ();
3098 : 9504 : for (int column = 1 + m_layout.m_x_offset_display;
3099 : 9504 : column <= max_column;
3100 : : ++column)
3101 : 9308 : if (column % 10 == 0)
3102 : 860 : pp_character (&m_pp, '0' + (column / 100) % 10);
3103 : : else
3104 : 8448 : pp_space (&m_pp);
3105 : 196 : pp_newline (&m_pp);
3106 : : }
3107 : :
3108 : : /* Tens. */
3109 : 324 : start_annotation_line ();
3110 : 16032 : for (int column = 1 + m_layout.m_x_offset_display;
3111 : 16032 : column <= max_column;
3112 : : ++column)
3113 : 15708 : if (column % 10 == 0)
3114 : 1500 : pp_character (&m_pp, '0' + (column / 10) % 10);
3115 : : else
3116 : 14208 : pp_space (&m_pp);
3117 : 324 : pp_newline (&m_pp);
3118 : :
3119 : : /* Units. */
3120 : 324 : start_annotation_line ();
3121 : 16032 : for (int column = 1 + m_layout.m_x_offset_display;
3122 : 16032 : column <= max_column;
3123 : : ++column)
3124 : 15708 : pp_character (&m_pp, '0' + (column % 10));
3125 : 324 : pp_newline (&m_pp);
3126 : 324 : }
3127 : :
3128 : : /* Print leading fix-its (for new lines inserted before the source line)
3129 : : then the source line, followed by an annotation line
3130 : : consisting of any caret/underlines, then any fixits.
3131 : : If the source line can't be read, print nothing. */
3132 : : void
3133 : 46310 : layout_printer::print_line (linenum_type row)
3134 : : {
3135 : 46310 : char_span line
3136 : 46310 : = m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row);
3137 : 46310 : if (!line)
3138 : 2919 : return;
3139 : :
3140 : 43391 : print_any_right_to_left_edge_lines ();
3141 : 43391 : print_leading_fixits (row);
3142 : 43391 : const line_bounds lbounds
3143 : 43391 : = print_source_line (row, line.get_buffer (), line.length ());
3144 : 43391 : if (m_layout.should_print_annotation_line_p (row))
3145 : 36252 : print_annotation_line (row, lbounds);
3146 : 43391 : if (get_options ().show_labels_p)
3147 : 43009 : print_any_labels (row);
3148 : 43391 : print_trailing_fixits (row);
3149 : : }
3150 : :
3151 : : /* If there's a link column in the RHS, print something like this:
3152 : : " │\n"
3153 : : "┌──────────────────────────────────────────┘\n"
3154 : : showing the link entering at the top right and emerging
3155 : : at the bottom left. */
3156 : :
3157 : : void
3158 : 43391 : layout_printer::print_any_right_to_left_edge_lines ()
3159 : : {
3160 : 43391 : if (m_link_rhs_column == -1)
3161 : : /* Can also happen if the out-edge had UNKNOWN_LOCATION. */
3162 : 42137 : return;
3163 : :
3164 : 1254 : gcc_assert (get_options ().show_event_links_p);
3165 : :
3166 : : /* Print the line with "|". */
3167 : 1254 : start_annotation_line ();
3168 : 1254 : int column = 1 + m_layout.m_x_offset_display;
3169 : 1254 : move_to_column (&column, m_link_rhs_column, true);
3170 : 1254 : m_colorizer.set_cfg_edge ();
3171 : 1254 : const cppchar_t down= get_theme ().get_cppchar
3172 : 1254 : (text_art::theme::cell_kind::CFG_DOWN);
3173 : 1254 : pp_unicode_character (&m_pp, down);
3174 : 1254 : m_colorizer.set_normal_text ();
3175 : 1254 : pp_newline (&m_pp);
3176 : :
3177 : : /* Print the line with "┌──────────────────────────────────────────┘". */
3178 : 1254 : m_link_lhs_state = link_lhs_state::rewinding_to_lhs;
3179 : 1254 : start_annotation_line ();
3180 : 1254 : m_colorizer.set_cfg_edge ();
3181 : 1254 : const cppchar_t left= get_theme ().get_cppchar
3182 : 1254 : (text_art::theme::cell_kind::CFG_LEFT);
3183 : 53854 : for (int column = 1 + m_layout.m_x_offset_display;
3184 : 53854 : column < m_link_rhs_column;
3185 : : ++column)
3186 : 52600 : pp_unicode_character (&m_pp, left);
3187 : 1254 : const cppchar_t from_down_to_left = get_theme ().get_cppchar
3188 : 1254 : (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_LEFT);
3189 : 1254 : pp_unicode_character (&m_pp, from_down_to_left);
3190 : 1254 : m_colorizer.set_normal_text ();
3191 : 1254 : pp_newline (&m_pp);
3192 : :
3193 : : /* We now have a link line on the LHS,
3194 : : and no longer have one on the RHS. */
3195 : 1254 : m_link_lhs_state = link_lhs_state::at_lhs;
3196 : 1254 : m_link_rhs_column = -1;
3197 : : }
3198 : :
3199 : 43311 : layout_printer::layout_printer (pretty_printer &pp,
3200 : : const layout &layout,
3201 : : const rich_location &richloc,
3202 : 43311 : diagnostic_t diagnostic_kind)
3203 : 43311 : : m_pp (pp),
3204 : 43311 : m_layout (layout),
3205 : 43311 : m_colorizer (m_pp, richloc, diagnostic_kind),
3206 : 43311 : m_is_diagnostic_path (diagnostic_kind == DK_DIAGNOSTIC_PATH),
3207 : 43311 : m_link_lhs_state (link_lhs_state::none),
3208 : 43311 : m_link_rhs_column (-1)
3209 : : {
3210 : 43311 : if (get_options ().show_event_links_p)
3211 : 22591 : if (auto effect_info = m_layout.m_effect_info)
3212 : 1663 : if (effect_info->m_leading_in_edge_column)
3213 : 1663 : m_link_rhs_column = effect_info->m_leading_in_edge_column;
3214 : 43311 : }
3215 : :
3216 : : } /* End of anonymous namespace. */
3217 : :
3218 : : /* If LOC is within the spans of lines that will already be printed for
3219 : : this gcc_rich_location, then add it as a secondary location and return true.
3220 : :
3221 : : Otherwise return false.
3222 : :
3223 : : Use POLICY for determining how spans of lines would be printed. */
3224 : :
3225 : : bool
3226 : 3839 : gcc_rich_location::
3227 : : add_location_if_nearby (const diagnostic_source_print_policy &policy,
3228 : : location_t loc,
3229 : : bool restrict_to_current_line_spans,
3230 : : const range_label *label)
3231 : : {
3232 : : /* Use the layout location-handling logic to sanitize LOC,
3233 : : filtering it to the current line spans within a temporary
3234 : : layout instance. */
3235 : :
3236 : 3839 : layout layout (policy, *this);
3237 : 3839 : location_range loc_range;
3238 : 3839 : loc_range.m_loc = loc;
3239 : 3839 : loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
3240 : 3839 : loc_range.m_label = nullptr;
3241 : 3839 : if (!layout.maybe_add_location_range (&loc_range, 0,
3242 : : restrict_to_current_line_spans))
3243 : : return false;
3244 : :
3245 : 3646 : add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
3246 : 3646 : return true;
3247 : 3839 : }
3248 : :
3249 : : bool
3250 : 1565 : gcc_rich_location::
3251 : : add_location_if_nearby (const diagnostic_context &dc,
3252 : : location_t loc,
3253 : : bool restrict_to_current_line_spans,
3254 : : const range_label *label)
3255 : : {
3256 : 1565 : diagnostic_source_print_policy source_policy (dc,
3257 : 1565 : dc.m_source_printing);
3258 : 1565 : return add_location_if_nearby (source_policy, loc,
3259 : 1565 : restrict_to_current_line_spans, label);
3260 : : }
3261 : :
3262 : :
3263 : : /* As per diagnostic_source_print_policy::print, but don't print anything
3264 : : if source printing is disabled, or if the location hasn't changed. */
3265 : :
3266 : : void
3267 : 357340 : diagnostic_context::maybe_show_locus (const rich_location &richloc,
3268 : : const diagnostic_source_printing_options &opts,
3269 : : diagnostic_t diagnostic_kind,
3270 : : pretty_printer &pp,
3271 : : diagnostic_source_effect_info *effects)
3272 : : {
3273 : 357340 : const location_t loc = richloc.get_loc ();
3274 : : /* Do nothing if source-printing has been disabled. */
3275 : 357340 : if (!opts.enabled)
3276 : 320905 : return;
3277 : :
3278 : : /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
3279 : 38267 : if (loc <= BUILTINS_LOCATION)
3280 : : return;
3281 : :
3282 : : /* Don't print the same source location twice in a row, unless we have
3283 : : fix-it hints, or multiple locations, or a label. */
3284 : 37899 : if (loc == m_last_location
3285 : 1559 : && richloc.get_num_fixit_hints () == 0
3286 : 1502 : && richloc.get_num_locations () == 1
3287 : 39369 : && richloc.get_range (0)->m_label == NULL)
3288 : : return;
3289 : :
3290 : 36435 : m_last_location = loc;
3291 : :
3292 : 36435 : diagnostic_source_print_policy source_policy (*this, opts);
3293 : 36435 : source_policy.print (pp, richloc, diagnostic_kind, effects);
3294 : : }
3295 : :
3296 : 10093 : diagnostic_source_print_policy::
3297 : 10093 : diagnostic_source_print_policy (const diagnostic_context &dc)
3298 : 10093 : : m_options (dc.m_source_printing),
3299 : 10093 : m_location_policy (dc),
3300 : 10093 : m_start_span_cb (dc.m_text_callbacks.m_start_span),
3301 : 10093 : m_file_cache (dc.get_file_cache ()),
3302 : 10093 : m_diagram_theme (dc.get_diagram_theme ()),
3303 : 10093 : m_escape_format (dc.get_escape_format ())
3304 : : {
3305 : 10093 : }
3306 : :
3307 : 38000 : diagnostic_source_print_policy::
3308 : : diagnostic_source_print_policy (const diagnostic_context &dc,
3309 : 38000 : const diagnostic_source_printing_options &opts)
3310 : 38000 : : m_options (opts),
3311 : 38000 : m_location_policy (dc),
3312 : 38000 : m_start_span_cb (dc.m_text_callbacks.m_start_span),
3313 : 38000 : m_file_cache (dc.get_file_cache ()),
3314 : 38000 : m_diagram_theme (dc.get_diagram_theme ()),
3315 : 38000 : m_escape_format (dc.get_escape_format ())
3316 : : {
3317 : 38000 : }
3318 : :
3319 : : /* Print to PP the physical source code corresponding to the location(s)
3320 : : in RICHLOC, with additional annotations, as if for a diagnostic of the
3321 : : given DIAGNOSTIC_KIND.
3322 : : If EFFECTS is non-null, then use and update it. */
3323 : :
3324 : : void
3325 : 41903 : diagnostic_source_print_policy::print (pretty_printer &pp,
3326 : : const rich_location &richloc,
3327 : : diagnostic_t diagnostic_kind,
3328 : : diagnostic_source_effect_info *effects)
3329 : : const
3330 : : {
3331 : 41903 : layout layout (*this, richloc, effects);
3332 : 41903 : layout_printer lp (pp, layout, richloc, diagnostic_kind);
3333 : 41903 : lp.print (*this);
3334 : 41903 : }
3335 : :
3336 : : void
3337 : 43311 : layout_printer::print (const diagnostic_source_print_policy &source_policy)
3338 : : {
3339 : 43311 : diagnostic_prefixing_rule_t saved_rule = pp_prefixing_rule (&m_pp);
3340 : 43311 : pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3341 : :
3342 : 43311 : if (get_options ().show_ruler_p)
3343 : 324 : show_ruler (m_layout.m_x_offset_display + get_options ().max_width);
3344 : :
3345 : 174652 : for (int line_span_idx = 0; line_span_idx < m_layout.get_num_line_spans ();
3346 : : line_span_idx++)
3347 : : {
3348 : 44015 : const line_span *line_span = m_layout.get_line_span (line_span_idx);
3349 : 44015 : if (get_options ().show_line_numbers_p)
3350 : : {
3351 : : /* With line numbers, we should show whenever the line-numbering
3352 : : "jumps". */
3353 : 24898 : if (line_span_idx > 0)
3354 : 360 : print_gap_in_line_numbering ();
3355 : : }
3356 : : else
3357 : : {
3358 : : /* Without line numbers, we print headings for some line spans. */
3359 : 19117 : if (m_layout.print_heading_for_line_span_index_p (line_span_idx))
3360 : : {
3361 : 463 : expanded_location exploc
3362 : 463 : = m_layout.get_expanded_location (line_span);
3363 : 463 : const diagnostic_location_print_policy &
3364 : 463 : loc_policy = source_policy.get_location_policy ();
3365 : 463 : source_policy.get_start_span_fn () (loc_policy, &m_pp, exploc);
3366 : : }
3367 : : }
3368 : : /* Iterate over the lines within this span (using linenum_arith_t to
3369 : : avoid overflow with 0xffffffff causing an infinite loop). */
3370 : 44015 : linenum_arith_t last_line = line_span->get_last_line ();
3371 : 90325 : for (linenum_arith_t row = line_span->get_first_line ();
3372 : 90325 : row <= last_line; row++)
3373 : 46310 : print_line (row);
3374 : : }
3375 : :
3376 : 43311 : if (auto effect_info = m_layout.m_effect_info)
3377 : 2348 : effect_info->m_trailing_out_edge_column = m_link_rhs_column;
3378 : :
3379 : 43311 : pp_prefixing_rule (&m_pp) = saved_rule;
3380 : 43311 : }
3381 : :
3382 : : #if CHECKING_P
3383 : :
3384 : : namespace selftest {
3385 : :
3386 : : /* Selftests for diagnostic_show_locus. */
3387 : :
3388 : 672 : diagnostic_show_locus_fixture::
3389 : : diagnostic_show_locus_fixture (const line_table_case &case_,
3390 : 672 : const char *content)
3391 : 672 : : m_content (content),
3392 : 672 : m_tmp_source_file (SELFTEST_LOCATION, ".c", content),
3393 : 672 : m_ltt (case_),
3394 : 672 : m_fc ()
3395 : : {
3396 : 672 : linemap_add (line_table, LC_ENTER, false,
3397 : : m_tmp_source_file.get_filename (), 1);
3398 : 672 : }
3399 : :
3400 : : /* Populate a char_display_policy based on DC and RICHLOC. */
3401 : :
3402 : : static char_display_policy
3403 : 188 : make_char_policy (const diagnostic_context &dc,
3404 : : const rich_location &richloc)
3405 : : {
3406 : 188 : diagnostic_source_print_policy source_policy (dc);
3407 : 188 : return ::make_char_policy (source_policy, richloc);
3408 : : }
3409 : :
3410 : : /* Verify that cpp_display_width correctly handles escaping. */
3411 : :
3412 : : static void
3413 : 4 : test_display_widths ()
3414 : : {
3415 : 4 : gcc_rich_location richloc (UNKNOWN_LOCATION);
3416 : :
3417 : : /* U+03C0 "GREEK SMALL LETTER PI". */
3418 : 4 : const char *pi = "\xCF\x80";
3419 : : /* U+1F642 "SLIGHTLY SMILING FACE". */
3420 : 4 : const char *emoji = "\xF0\x9F\x99\x82";
3421 : : /* Stray trailing byte of a UTF-8 character. */
3422 : 4 : const char *stray = "\xBF";
3423 : : /* U+10FFFF. */
3424 : 4 : const char *max_codepoint = "\xF4\x8F\xBF\xBF";
3425 : :
3426 : : /* No escaping. */
3427 : 4 : {
3428 : 4 : test_diagnostic_context dc;
3429 : 4 : char_display_policy policy (make_char_policy (dc, richloc));
3430 : 4 : ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
3431 : 4 : ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
3432 : 4 : ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
3433 : : /* Don't check width of U+10FFFF; it's in a private use plane. */
3434 : 4 : }
3435 : :
3436 : 4 : richloc.set_escape_on_output (true);
3437 : :
3438 : 4 : {
3439 : 4 : test_diagnostic_context dc;
3440 : 4 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
3441 : 4 : char_display_policy policy (make_char_policy (dc, richloc));
3442 : 4 : ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
3443 : 4 : ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
3444 : 4 : ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
3445 : 4 : ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
3446 : : policy),
3447 : : strlen ("<U+10FFFF>"));
3448 : 4 : }
3449 : :
3450 : 4 : {
3451 : 4 : test_diagnostic_context dc;
3452 : 4 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
3453 : 4 : char_display_policy policy (make_char_policy (dc, richloc));
3454 : 4 : ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
3455 : 4 : ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
3456 : 4 : ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
3457 : 4 : ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
3458 : : policy),
3459 : : 16);
3460 : 4 : }
3461 : 4 : }
3462 : :
3463 : : /* For precise tests of the layout, make clear where the source line will
3464 : : start. test_left_margin sets the total byte count from the left side of the
3465 : : screen to the start of source lines, after the line number and the separator,
3466 : : which consists of the three characters " | ". */
3467 : : static const int test_linenum_sep = 3;
3468 : : static const int test_left_margin = 7;
3469 : :
3470 : : /* Helper function for test_layout_x_offset_display_utf8(). */
3471 : : static void
3472 : 1472 : test_offset_impl (int caret_byte_col, int max_width,
3473 : : int expected_x_offset_display,
3474 : : int left_margin = test_left_margin)
3475 : : {
3476 : 1472 : test_diagnostic_context dc;
3477 : 1472 : dc.m_source_printing.max_width = max_width;
3478 : : /* diagnostic_context::min_margin_width sets the minimum space reserved for
3479 : : the line number plus one space after. */
3480 : 1472 : dc.m_source_printing.min_margin_width = left_margin - test_linenum_sep + 1;
3481 : 1472 : dc.m_source_printing.show_line_numbers_p = true;
3482 : 1472 : diagnostic_source_print_policy source_policy (dc);
3483 : 1472 : rich_location richloc (line_table,
3484 : : linemap_position_for_column (line_table,
3485 : 1472 : caret_byte_col));
3486 : 1472 : layout test_layout (source_policy, richloc, nullptr);
3487 : 1472 : ASSERT_EQ (left_margin - test_linenum_sep,
3488 : : test_layout.get_linenum_width ());
3489 : 1472 : ASSERT_EQ (expected_x_offset_display,
3490 : : test_layout.get_x_offset_display ());
3491 : 1472 : }
3492 : :
3493 : : /* Test that layout::calculate_x_offset_display() works. */
3494 : : static void
3495 : 96 : test_layout_x_offset_display_utf8 (const line_table_case &case_)
3496 : : {
3497 : :
3498 : 96 : const char *content
3499 : : = "This line is very long, so that we can use it to test the logic for "
3500 : : "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
3501 : : "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
3502 : : "column #102.\n";
3503 : :
3504 : : /* Number of bytes in the line, subtracting one to remove the newline. */
3505 : 96 : const int line_bytes = strlen (content) - 1;
3506 : :
3507 : : /* Number of display columns occupied by the line; each of the 2 emojis
3508 : : takes up 2 fewer display columns than it does bytes. */
3509 : 96 : const int line_display_cols = line_bytes - 2*2;
3510 : :
3511 : : /* The column of the first emoji. Byte or display is the same as there are
3512 : : no multibyte characters earlier on the line. */
3513 : 96 : const int emoji_col = 102;
3514 : :
3515 : 96 : diagnostic_show_locus_fixture f (case_, content);
3516 : :
3517 : 96 : linemap_add (line_table, LC_ENTER, false, f.get_filename (), 1);
3518 : :
3519 : 96 : location_t line_end = linemap_position_for_column (line_table, line_bytes);
3520 : :
3521 : : /* Don't attempt to run the tests if column data might be unavailable. */
3522 : 96 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3523 : 32 : return;
3524 : :
3525 : 64 : ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
3526 : 64 : ASSERT_EQ (1, LOCATION_LINE (line_end));
3527 : 64 : ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
3528 : :
3529 : 64 : char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
3530 : 64 : ASSERT_EQ (line_display_cols,
3531 : : cpp_display_width (lspan.get_buffer (), lspan.length (),
3532 : : def_policy ()));
3533 : 64 : ASSERT_EQ (line_display_cols,
3534 : : location_compute_display_column (f.m_fc,
3535 : : expand_location (line_end),
3536 : : def_policy ()));
3537 : 64 : ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
3538 : : "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
3539 : :
3540 : : /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
3541 : :
3542 : : /* No constraint on the width -> no offset. */
3543 : 64 : test_offset_impl (emoji_col, 0, 0);
3544 : :
3545 : : /* Caret is before the beginning -> no offset. */
3546 : 64 : test_offset_impl (0, 100, 0);
3547 : :
3548 : : /* Caret is past the end of the line -> no offset. */
3549 : 64 : test_offset_impl (line_bytes+1, 100, 0);
3550 : :
3551 : : /* Line fits in the display -> no offset. */
3552 : 64 : test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
3553 : 64 : test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
3554 : :
3555 : : /* Line is too long for the display but caret location is OK
3556 : : anyway -> no offset. */
3557 : 64 : static const int small_width = 24;
3558 : 64 : test_offset_impl (1, small_width, 0);
3559 : :
3560 : : /* Width constraint is very small -> no offset. */
3561 : 64 : test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
3562 : :
3563 : : /* Line would be offset, but due to large line numbers, offsetting
3564 : : would remove the whole line -> no offset. */
3565 : 64 : static const int huge_left_margin = 100;
3566 : 64 : test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
3567 : :
3568 : : /* Line is the same length as the display, but the line number makes it too
3569 : : long, so offset is required. Caret is at the end so padding on the right
3570 : : is not in effect. */
3571 : 256 : for (int excess = 1; excess <= 3; ++excess)
3572 : 192 : test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
3573 : : excess);
3574 : :
3575 : : /* Line is much too long for the display, caret is near the end ->
3576 : : offset should be such that the line fits in the display and caret
3577 : : remains the same distance from the end that it was. */
3578 : 704 : for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
3579 : 768 : caret_offset <= max_offset; ++caret_offset)
3580 : 704 : test_offset_impl (line_bytes - caret_offset, small_width,
3581 : : line_display_cols + test_left_margin - small_width);
3582 : :
3583 : : /* As previous case but caret is closer to the middle; now we want it to end
3584 : : up CARET_LINE_MARGIN bytes from the end. */
3585 : 64 : ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
3586 : 64 : test_offset_impl (emoji_col, small_width,
3587 : : emoji_col + test_left_margin
3588 : : - (small_width - CARET_LINE_MARGIN));
3589 : :
3590 : : /* Test that the source line is offset as expected when printed. */
3591 : 64 : {
3592 : 64 : test_diagnostic_context dc;
3593 : 64 : dc.m_source_printing.max_width = small_width - 6;
3594 : 64 : dc.m_source_printing.min_margin_width
3595 : 64 : = test_left_margin - test_linenum_sep + 1;
3596 : 64 : dc.m_source_printing.show_line_numbers_p = true;
3597 : 64 : dc.m_source_printing.show_ruler_p = true;
3598 : 64 : diagnostic_source_print_policy policy (dc);
3599 : 64 : rich_location richloc (line_table,
3600 : : linemap_position_for_column (line_table,
3601 : 64 : emoji_col));
3602 : 64 : layout test_layout (policy, richloc, nullptr);
3603 : 64 : layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3604 : 64 : lp.print (policy);
3605 : 64 : ASSERT_STREQ (" | 1 \n"
3606 : : " | 1 \n"
3607 : : " | 234567890123456789\n"
3608 : : " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
3609 : : "that occupies 8 bytes and 4 display columns, starting at "
3610 : : "column #102.\n"
3611 : : " | ^\n",
3612 : : pp_formatted_text (dc.get_reference_printer ()));
3613 : 64 : }
3614 : :
3615 : : /* Similar to the previous example, but now the offset called for would split
3616 : : the first emoji in the middle of the UTF-8 sequence. Check that we replace
3617 : : it with a padding space in this case. */
3618 : 64 : {
3619 : 64 : test_diagnostic_context dc;
3620 : 64 : dc.m_source_printing.max_width = small_width - 5;
3621 : 64 : dc.m_source_printing.min_margin_width
3622 : 64 : = test_left_margin - test_linenum_sep + 1;
3623 : 64 : dc.m_source_printing.show_line_numbers_p = true;
3624 : 64 : dc.m_source_printing.show_ruler_p = true;
3625 : 64 : diagnostic_source_print_policy policy (dc);
3626 : 64 : rich_location richloc (line_table,
3627 : : linemap_position_for_column (line_table,
3628 : 64 : emoji_col + 2));
3629 : 64 : layout test_layout (dc, richloc, nullptr);
3630 : 64 : layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3631 : 64 : lp.print (policy);
3632 : 64 : ASSERT_STREQ (" | 1 1 \n"
3633 : : " | 1 2 \n"
3634 : : " | 3456789012345678901\n"
3635 : : " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
3636 : : "that occupies 8 bytes and 4 display columns, starting at "
3637 : : "column #102.\n"
3638 : : " | ^\n",
3639 : : pp_formatted_text (dc.get_reference_printer ()));
3640 : 64 : }
3641 : :
3642 : 96 : }
3643 : :
3644 : : static void
3645 : 96 : test_layout_x_offset_display_tab (const line_table_case &case_)
3646 : : {
3647 : 96 : const char *content
3648 : : = "This line is very long, so that we can use it to test the logic for "
3649 : : "clipping long lines. Also this: `\t' is a tab that occupies 1 byte and "
3650 : : "a variable number of display columns, starting at column #103.\n";
3651 : :
3652 : : /* Number of bytes in the line, subtracting one to remove the newline. */
3653 : 96 : const int line_bytes = strlen (content) - 1;
3654 : :
3655 : : /* The column where the tab begins. Byte or display is the same as there are
3656 : : no multibyte characters earlier on the line. */
3657 : 96 : const int tab_col = 103;
3658 : :
3659 : : /* Effective extra size of the tab beyond what a single space would have taken
3660 : : up, indexed by tabstop. */
3661 : 96 : static const int num_tabstops = 11;
3662 : 96 : int extra_width[num_tabstops];
3663 : 1056 : for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3664 : : {
3665 : 960 : const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
3666 : 960 : extra_width[tabstop] = this_tab_size - 1;
3667 : : }
3668 : : /* Example of this calculation: if tabstop is 10, the tab starting at column
3669 : : #103 has to expand into 8 spaces, covering columns 103-110, so that the
3670 : : next character is at column #111. So it takes up 7 more columns than
3671 : : a space would have taken up. */
3672 : 96 : ASSERT_EQ (7, extra_width[10]);
3673 : :
3674 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3675 : 96 : file_cache fc;
3676 : 96 : line_table_test ltt (case_);
3677 : :
3678 : 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3679 : :
3680 : 96 : location_t line_end = linemap_position_for_column (line_table, line_bytes);
3681 : :
3682 : : /* Don't attempt to run the tests if column data might be unavailable. */
3683 : 96 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3684 : 32 : return;
3685 : :
3686 : : /* Check that cpp_display_width handles the tabs as expected. */
3687 : 64 : char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
3688 : 64 : ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
3689 : 704 : for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3690 : : {
3691 : 640 : cpp_char_column_policy policy (tabstop, cpp_wcwidth);
3692 : 640 : ASSERT_EQ (line_bytes + extra_width[tabstop],
3693 : : cpp_display_width (lspan.get_buffer (), lspan.length (),
3694 : : policy));
3695 : 640 : ASSERT_EQ (line_bytes + extra_width[tabstop],
3696 : : location_compute_display_column (fc,
3697 : : expand_location (line_end),
3698 : : policy));
3699 : : }
3700 : :
3701 : : /* Check that the tab is expanded to the expected number of spaces. */
3702 : 64 : rich_location richloc (line_table,
3703 : : linemap_position_for_column (line_table,
3704 : 64 : tab_col + 1));
3705 : 704 : for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3706 : : {
3707 : 640 : test_diagnostic_context dc;
3708 : 640 : dc.m_tabstop = tabstop;
3709 : 640 : diagnostic_source_print_policy policy (dc);
3710 : 640 : layout test_layout (policy, richloc, nullptr);
3711 : 640 : layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3712 : 640 : lp.print (policy);
3713 : 640 : const char *out = pp_formatted_text (dc.get_reference_printer ());
3714 : 640 : ASSERT_EQ (NULL, strchr (out, '\t'));
3715 : 640 : const char *left_quote = strchr (out, '`');
3716 : 640 : const char *right_quote = strchr (out, '\'');
3717 : 640 : ASSERT_NE (NULL, left_quote);
3718 : 640 : ASSERT_NE (NULL, right_quote);
3719 : 640 : ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
3720 : 640 : }
3721 : :
3722 : : /* Check that the line is offset properly and that the tab is broken up
3723 : : into the expected number of spaces when it is the last character skipped
3724 : : over. */
3725 : 704 : for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
3726 : : {
3727 : 640 : test_diagnostic_context dc;
3728 : 640 : dc.m_tabstop = tabstop;
3729 : 640 : static const int small_width = 24;
3730 : 640 : dc.m_source_printing.max_width = small_width - 4;
3731 : 640 : dc.m_source_printing.min_margin_width
3732 : 640 : = test_left_margin - test_linenum_sep + 1;
3733 : 640 : dc.m_source_printing.show_line_numbers_p = true;
3734 : 640 : diagnostic_source_print_policy policy (dc);
3735 : 640 : layout test_layout (policy, richloc, nullptr);
3736 : 640 : layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
3737 : 640 : lp.print (policy);
3738 : :
3739 : : /* We have arranged things so that two columns will be printed before
3740 : : the caret. If the tab results in more than one space, this should
3741 : : produce two spaces in the output; otherwise, it will be a single space
3742 : : preceded by the opening quote before the tab character. */
3743 : 640 : const char *output1
3744 : : = " 1 | ' is a tab that occupies 1 byte and a variable number of "
3745 : : "display columns, starting at column #103.\n"
3746 : : " | ^\n";
3747 : 640 : const char *output2
3748 : : = " 1 | ` ' is a tab that occupies 1 byte and a variable number of "
3749 : : "display columns, starting at column #103.\n"
3750 : : " | ^\n";
3751 : 640 : const char *expected_output = (extra_width[tabstop] ? output1 : output2);
3752 : 640 : ASSERT_STREQ (expected_output, pp_formatted_text (dc.get_reference_printer ()));
3753 : 640 : }
3754 : 96 : }
3755 : :
3756 : :
3757 : : /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
3758 : :
3759 : : static void
3760 : 4 : test_diagnostic_show_locus_unknown_location ()
3761 : : {
3762 : 4 : test_diagnostic_context dc;
3763 : 4 : rich_location richloc (line_table, UNKNOWN_LOCATION);
3764 : 4 : ASSERT_STREQ ("", dc.test_show_locus (richloc));
3765 : 4 : }
3766 : :
3767 : : /* Verify that diagnostic_show_locus works sanely for various
3768 : : single-line cases.
3769 : :
3770 : : All of these work on the following 1-line source file:
3771 : : .0000000001111111
3772 : : .1234567890123456
3773 : : "foo = bar.field;\n"
3774 : : which is set up by test_diagnostic_show_locus_one_liner and calls
3775 : : them. */
3776 : :
3777 : : /* Just a caret. */
3778 : :
3779 : : static void
3780 : 64 : test_one_liner_simple_caret ()
3781 : : {
3782 : 64 : test_diagnostic_context dc;
3783 : 64 : location_t caret = linemap_position_for_column (line_table, 10);
3784 : 64 : rich_location richloc (line_table, caret);
3785 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3786 : : " ^\n",
3787 : : dc.test_show_locus (richloc));
3788 : 64 : }
3789 : :
3790 : : /* No column information (column == 0).
3791 : : No annotation line should be printed. */
3792 : :
3793 : : static void
3794 : 64 : test_one_liner_no_column ()
3795 : : {
3796 : 64 : test_diagnostic_context dc;
3797 : 64 : location_t caret = linemap_position_for_column (line_table, 0);
3798 : 64 : rich_location richloc (line_table, caret);
3799 : 64 : ASSERT_STREQ (" foo = bar.field;\n",
3800 : : dc.test_show_locus (richloc));
3801 : 64 : }
3802 : :
3803 : : /* Caret and range. */
3804 : :
3805 : : static void
3806 : 64 : test_one_liner_caret_and_range ()
3807 : : {
3808 : 64 : test_diagnostic_context dc;
3809 : 64 : location_t caret = linemap_position_for_column (line_table, 10);
3810 : 64 : location_t start = linemap_position_for_column (line_table, 7);
3811 : 64 : location_t finish = linemap_position_for_column (line_table, 15);
3812 : 64 : location_t loc = make_location (caret, start, finish);
3813 : 64 : rich_location richloc (line_table, loc);
3814 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3815 : : " ~~~^~~~~~\n",
3816 : : dc.test_show_locus (richloc));
3817 : 64 : }
3818 : :
3819 : : /* Multiple ranges and carets. */
3820 : :
3821 : : static void
3822 : 64 : test_one_liner_multiple_carets_and_ranges ()
3823 : : {
3824 : 64 : test_diagnostic_context dc;
3825 : 64 : location_t foo
3826 : 64 : = make_location (linemap_position_for_column (line_table, 2),
3827 : : linemap_position_for_column (line_table, 1),
3828 : : linemap_position_for_column (line_table, 3));
3829 : 64 : dc.m_source_printing.caret_chars[0] = 'A';
3830 : :
3831 : 64 : location_t bar
3832 : 64 : = make_location (linemap_position_for_column (line_table, 8),
3833 : : linemap_position_for_column (line_table, 7),
3834 : : linemap_position_for_column (line_table, 9));
3835 : 64 : dc.m_source_printing.caret_chars[1] = 'B';
3836 : :
3837 : 64 : location_t field
3838 : 64 : = make_location (linemap_position_for_column (line_table, 13),
3839 : : linemap_position_for_column (line_table, 11),
3840 : : linemap_position_for_column (line_table, 15));
3841 : 64 : dc.m_source_printing.caret_chars[2] = 'C';
3842 : :
3843 : 64 : rich_location richloc (line_table, foo);
3844 : 64 : richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3845 : 64 : richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3846 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3847 : : " ~A~ ~B~ ~~C~~\n",
3848 : : dc.test_show_locus (richloc));
3849 : 64 : }
3850 : :
3851 : : /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
3852 : :
3853 : : static void
3854 : 64 : test_one_liner_fixit_insert_before ()
3855 : : {
3856 : 64 : test_diagnostic_context dc;
3857 : 64 : location_t caret = linemap_position_for_column (line_table, 7);
3858 : 64 : rich_location richloc (line_table, caret);
3859 : 64 : richloc.add_fixit_insert_before ("&");
3860 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3861 : : " ^\n"
3862 : : " &\n",
3863 : : dc.test_show_locus (richloc));
3864 : 64 : }
3865 : :
3866 : : /* Insertion fix-it hint: adding a "[0]" after "foo". */
3867 : :
3868 : : static void
3869 : 64 : test_one_liner_fixit_insert_after ()
3870 : : {
3871 : 64 : test_diagnostic_context dc;
3872 : 64 : location_t start = linemap_position_for_column (line_table, 1);
3873 : 64 : location_t finish = linemap_position_for_column (line_table, 3);
3874 : 64 : location_t foo = make_location (start, start, finish);
3875 : 64 : rich_location richloc (line_table, foo);
3876 : 64 : richloc.add_fixit_insert_after ("[0]");
3877 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3878 : : " ^~~\n"
3879 : : " [0]\n",
3880 : : dc.test_show_locus (richloc));
3881 : 64 : }
3882 : :
3883 : : /* Removal fix-it hint: removal of the ".field".
3884 : : Also verify the interaction of pp_set_prefix with rulers and
3885 : : fix-it hints. */
3886 : :
3887 : : static void
3888 : 64 : test_one_liner_fixit_remove ()
3889 : : {
3890 : 64 : location_t start = linemap_position_for_column (line_table, 10);
3891 : 64 : location_t finish = linemap_position_for_column (line_table, 15);
3892 : 64 : location_t dot = make_location (start, start, finish);
3893 : 64 : rich_location richloc (line_table, dot);
3894 : 64 : richloc.add_fixit_remove ();
3895 : :
3896 : : /* Normal. */
3897 : 64 : {
3898 : 64 : test_diagnostic_context dc;
3899 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3900 : : " ^~~~~~\n"
3901 : : " ------\n",
3902 : : dc.test_show_locus (richloc));
3903 : 64 : }
3904 : :
3905 : : /* Test of adding a prefix. */
3906 : 64 : {
3907 : 64 : test_diagnostic_context dc;
3908 : 64 : pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3909 : 64 : pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3910 : 64 : ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
3911 : : "TEST PREFIX: ^~~~~~\n"
3912 : : "TEST PREFIX: ------\n",
3913 : : dc.test_show_locus (richloc));
3914 : 64 : }
3915 : :
3916 : : /* Normal, with ruler. */
3917 : 64 : {
3918 : 64 : test_diagnostic_context dc;
3919 : 64 : dc.m_source_printing.show_ruler_p = true;
3920 : 64 : dc.m_source_printing.max_width = 104;
3921 : 64 : ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
3922 : : " 1 2 3 4 5 6 7 8 9 0 \n"
3923 : : " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
3924 : : " foo = bar.field;\n"
3925 : : " ^~~~~~\n"
3926 : : " ------\n",
3927 : : dc.test_show_locus (richloc));
3928 : 64 : }
3929 : :
3930 : : /* Test of adding a prefix, with ruler. */
3931 : 64 : {
3932 : 64 : test_diagnostic_context dc;
3933 : 64 : dc.m_source_printing.show_ruler_p = true;
3934 : 64 : dc.m_source_printing.max_width = 50;
3935 : 64 : pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3936 : 64 : pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3937 : 64 : ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
3938 : : "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
3939 : : "TEST PREFIX: foo = bar.field;\n"
3940 : : "TEST PREFIX: ^~~~~~\n"
3941 : : "TEST PREFIX: ------\n",
3942 : : dc.test_show_locus (richloc));
3943 : 64 : }
3944 : :
3945 : : /* Test of adding a prefix, with ruler and line numbers. */
3946 : 64 : {
3947 : 64 : test_diagnostic_context dc;
3948 : 64 : dc.m_source_printing.show_ruler_p = true;
3949 : 64 : dc.m_source_printing.max_width = 50;
3950 : 64 : dc.m_source_printing.show_line_numbers_p = true;
3951 : 64 : pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
3952 : 64 : pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
3953 : 64 : ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
3954 : : "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
3955 : : "TEST PREFIX: 1 | foo = bar.field;\n"
3956 : : "TEST PREFIX: | ^~~~~~\n"
3957 : : "TEST PREFIX: | ------\n",
3958 : : dc.test_show_locus (richloc));
3959 : 64 : }
3960 : 64 : }
3961 : :
3962 : : /* Replace fix-it hint: replacing "field" with "m_field". */
3963 : :
3964 : : static void
3965 : 64 : test_one_liner_fixit_replace ()
3966 : : {
3967 : 64 : test_diagnostic_context dc;
3968 : 64 : location_t start = linemap_position_for_column (line_table, 11);
3969 : 64 : location_t finish = linemap_position_for_column (line_table, 15);
3970 : 64 : location_t field = make_location (start, start, finish);
3971 : 64 : rich_location richloc (line_table, field);
3972 : 64 : richloc.add_fixit_replace ("m_field");
3973 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3974 : : " ^~~~~\n"
3975 : : " m_field\n",
3976 : : dc.test_show_locus (richloc));
3977 : 64 : }
3978 : :
3979 : : /* Replace fix-it hint: replacing "field" with "m_field",
3980 : : but where the caret was elsewhere. */
3981 : :
3982 : : static void
3983 : 64 : test_one_liner_fixit_replace_non_equal_range ()
3984 : : {
3985 : 64 : test_diagnostic_context dc;
3986 : 64 : location_t equals = linemap_position_for_column (line_table, 5);
3987 : 64 : location_t start = linemap_position_for_column (line_table, 11);
3988 : 64 : location_t finish = linemap_position_for_column (line_table, 15);
3989 : 64 : rich_location richloc (line_table, equals);
3990 : 64 : source_range range;
3991 : 64 : range.m_start = start;
3992 : 64 : range.m_finish = finish;
3993 : 64 : richloc.add_fixit_replace (range, "m_field");
3994 : : /* The replacement range is not indicated in the annotation line, so
3995 : : it should be indicated via an additional underline. */
3996 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
3997 : : " ^\n"
3998 : : " -----\n"
3999 : : " m_field\n",
4000 : : dc.test_show_locus (richloc));
4001 : 64 : }
4002 : :
4003 : : /* Replace fix-it hint: replacing "field" with "m_field",
4004 : : where the caret was elsewhere, but where a secondary range
4005 : : exactly covers "field". */
4006 : :
4007 : : static void
4008 : 64 : test_one_liner_fixit_replace_equal_secondary_range ()
4009 : : {
4010 : 64 : test_diagnostic_context dc;
4011 : 64 : location_t equals = linemap_position_for_column (line_table, 5);
4012 : 64 : location_t start = linemap_position_for_column (line_table, 11);
4013 : 64 : location_t finish = linemap_position_for_column (line_table, 15);
4014 : 64 : rich_location richloc (line_table, equals);
4015 : 64 : location_t field = make_location (start, start, finish);
4016 : 64 : richloc.add_range (field);
4017 : 64 : richloc.add_fixit_replace (field, "m_field");
4018 : : /* The replacement range is indicated in the annotation line,
4019 : : so it shouldn't be indicated via an additional underline. */
4020 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4021 : : " ^ ~~~~~\n"
4022 : : " m_field\n",
4023 : : dc.test_show_locus (richloc));
4024 : 64 : }
4025 : :
4026 : : /* Verify that we can use ad-hoc locations when adding fixits to a
4027 : : rich_location. */
4028 : :
4029 : : static void
4030 : 64 : test_one_liner_fixit_validation_adhoc_locations ()
4031 : : {
4032 : : /* Generate a range that's too long to be packed, so must
4033 : : be stored as an ad-hoc location (given the defaults
4034 : : of 5 or 7 bits or 0 bits of packed range); 150 columns > 2**7. */
4035 : 64 : const location_t c7 = linemap_position_for_column (line_table, 7);
4036 : 64 : const location_t c157 = linemap_position_for_column (line_table, 157);
4037 : 64 : const location_t loc = make_location (c7, c7, c157);
4038 : :
4039 : 64 : if (c157 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4040 : : return;
4041 : :
4042 : 64 : ASSERT_TRUE (IS_ADHOC_LOC (loc));
4043 : :
4044 : : /* Insert. */
4045 : 64 : {
4046 : 64 : rich_location richloc (line_table, loc);
4047 : 64 : richloc.add_fixit_insert_before (loc, "test");
4048 : : /* It should not have been discarded by the validator. */
4049 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4050 : :
4051 : 64 : test_diagnostic_context dc;
4052 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4053 : : " ^~~~~~~~~~ "
4054 : : " "
4055 : : " "
4056 : : " "
4057 : : " "
4058 : : " "
4059 : : " "
4060 : : " "
4061 : : " "
4062 : : " "
4063 : : " "
4064 : : " \n"
4065 : : " test\n",
4066 : : dc.test_show_locus (richloc));
4067 : 64 : }
4068 : :
4069 : : /* Remove. */
4070 : 64 : {
4071 : 64 : rich_location richloc (line_table, loc);
4072 : 64 : source_range range = source_range::from_locations (loc, c157);
4073 : 64 : richloc.add_fixit_remove (range);
4074 : : /* It should not have been discarded by the validator. */
4075 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4076 : :
4077 : 64 : test_diagnostic_context dc;
4078 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4079 : : " ^~~~~~~~~~ "
4080 : : " "
4081 : : " "
4082 : : " "
4083 : : " "
4084 : : " "
4085 : : " "
4086 : : " "
4087 : : " "
4088 : : " "
4089 : : " "
4090 : : " \n"
4091 : : " -----------------------------------------"
4092 : : "----------"
4093 : : "----------"
4094 : : "----------"
4095 : : "----------"
4096 : : "----------"
4097 : : "----------"
4098 : : "----------"
4099 : : "----------"
4100 : : "----------"
4101 : : "----------"
4102 : : "----------\n",
4103 : : dc.test_show_locus (richloc));
4104 : 64 : }
4105 : :
4106 : : /* Replace. */
4107 : 64 : {
4108 : 64 : rich_location richloc (line_table, loc);
4109 : 64 : source_range range = source_range::from_locations (loc, c157);
4110 : 64 : richloc.add_fixit_replace (range, "test");
4111 : : /* It should not have been discarded by the validator. */
4112 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4113 : :
4114 : 64 : test_diagnostic_context dc;
4115 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4116 : : " ^~~~~~~~~~ "
4117 : : " "
4118 : : " "
4119 : : " "
4120 : : " "
4121 : : " "
4122 : : " "
4123 : : " "
4124 : : " "
4125 : : " "
4126 : : " "
4127 : : " \n"
4128 : : " test\n",
4129 : : dc.test_show_locus (richloc));
4130 : 64 : }
4131 : : }
4132 : :
4133 : : /* Test of consolidating insertions at the same location. */
4134 : :
4135 : : static void
4136 : 64 : test_one_liner_many_fixits_1 ()
4137 : : {
4138 : 64 : test_diagnostic_context dc;
4139 : 64 : location_t equals = linemap_position_for_column (line_table, 5);
4140 : 64 : rich_location richloc (line_table, equals);
4141 : 1280 : for (int i = 0; i < 19; i++)
4142 : 1216 : richloc.add_fixit_insert_before ("a");
4143 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4144 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4145 : : " ^\n"
4146 : : " aaaaaaaaaaaaaaaaaaa\n",
4147 : : dc.test_show_locus (richloc));
4148 : 64 : }
4149 : :
4150 : : /* Ensure that we can add an arbitrary number of fix-it hints to a
4151 : : rich_location, even if they are not consolidated. */
4152 : :
4153 : : static void
4154 : 64 : test_one_liner_many_fixits_2 ()
4155 : : {
4156 : 64 : test_diagnostic_context dc;
4157 : 64 : location_t equals = linemap_position_for_column (line_table, 5);
4158 : 64 : rich_location richloc (line_table, equals);
4159 : 1280 : for (int i = 0; i < 19; i++)
4160 : : {
4161 : 1216 : location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
4162 : 1216 : richloc.add_fixit_insert_before (loc, "a");
4163 : : }
4164 : 64 : ASSERT_EQ (19, richloc.get_num_fixit_hints ());
4165 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4166 : : " ^\n"
4167 : : " a a a a a a a a a a a a a a a a a a a\n",
4168 : : dc.test_show_locus (richloc));
4169 : 64 : }
4170 : :
4171 : : /* Test of labeling the ranges within a rich_location. */
4172 : :
4173 : : static void
4174 : 64 : test_one_liner_labels ()
4175 : : {
4176 : 64 : location_t foo
4177 : 64 : = make_location (linemap_position_for_column (line_table, 1),
4178 : : linemap_position_for_column (line_table, 1),
4179 : : linemap_position_for_column (line_table, 3));
4180 : 64 : location_t bar
4181 : 64 : = make_location (linemap_position_for_column (line_table, 7),
4182 : : linemap_position_for_column (line_table, 7),
4183 : : linemap_position_for_column (line_table, 9));
4184 : 64 : location_t field
4185 : 64 : = make_location (linemap_position_for_column (line_table, 11),
4186 : : linemap_position_for_column (line_table, 11),
4187 : : linemap_position_for_column (line_table, 15));
4188 : :
4189 : : /* Example where all the labels fit on one line. */
4190 : 64 : {
4191 : 64 : text_range_label label0 ("0");
4192 : 64 : text_range_label label1 ("1");
4193 : 64 : text_range_label label2 ("2");
4194 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4195 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4196 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4197 : :
4198 : 64 : {
4199 : 64 : test_diagnostic_context dc;
4200 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4201 : : " ^~~ ~~~ ~~~~~\n"
4202 : : " | | |\n"
4203 : : " 0 1 2\n",
4204 : : dc.test_show_locus (richloc));
4205 : 64 : }
4206 : :
4207 : : /* Verify that we can disable label-printing. */
4208 : 64 : {
4209 : 64 : test_diagnostic_context dc;
4210 : 64 : dc.m_source_printing.show_labels_p = false;
4211 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4212 : : " ^~~ ~~~ ~~~~~\n",
4213 : : dc.test_show_locus (richloc));
4214 : 64 : }
4215 : 64 : }
4216 : :
4217 : : /* Example where the labels need extra lines. */
4218 : 64 : {
4219 : 64 : text_range_label label0 ("label 0");
4220 : 64 : text_range_label label1 ("label 1");
4221 : 64 : text_range_label label2 ("label 2");
4222 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4223 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4224 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4225 : :
4226 : 64 : test_diagnostic_context dc;
4227 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4228 : : " ^~~ ~~~ ~~~~~\n"
4229 : : " | | |\n"
4230 : : " | | label 2\n"
4231 : : " | label 1\n"
4232 : : " label 0\n",
4233 : : dc.test_show_locus (richloc));
4234 : 64 : }
4235 : :
4236 : : /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4237 : : but label 1 just touches label 2. */
4238 : 64 : {
4239 : 64 : text_range_label label0 ("aaaaa");
4240 : 64 : text_range_label label1 ("bbbb");
4241 : 64 : text_range_label label2 ("c");
4242 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4243 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4244 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4245 : :
4246 : 64 : test_diagnostic_context dc;
4247 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4248 : : " ^~~ ~~~ ~~~~~\n"
4249 : : " | | |\n"
4250 : : " | | c\n"
4251 : : " aaaaa bbbb\n",
4252 : : dc.test_show_locus (richloc));
4253 : 64 : }
4254 : :
4255 : : /* Example of out-of-order ranges (thus requiring a sort). */
4256 : 64 : {
4257 : 64 : text_range_label label0 ("0");
4258 : 64 : text_range_label label1 ("1");
4259 : 64 : text_range_label label2 ("2");
4260 : 64 : gcc_rich_location richloc (field, &label0, nullptr);
4261 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4262 : 64 : richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
4263 : :
4264 : 64 : test_diagnostic_context dc;
4265 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4266 : : " ~~~ ~~~ ^~~~~\n"
4267 : : " | | |\n"
4268 : : " 2 1 0\n",
4269 : : dc.test_show_locus (richloc));
4270 : 64 : }
4271 : :
4272 : : /* Ensure we don't ICE if multiple ranges with labels are on
4273 : : the same point. */
4274 : 64 : {
4275 : 64 : text_range_label label0 ("label 0");
4276 : 64 : text_range_label label1 ("label 1");
4277 : 64 : text_range_label label2 ("label 2");
4278 : 64 : gcc_rich_location richloc (bar, &label0, nullptr);
4279 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4280 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
4281 : :
4282 : 64 : test_diagnostic_context dc;
4283 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4284 : : " ^~~\n"
4285 : : " |\n"
4286 : : " label 0\n"
4287 : : " label 1\n"
4288 : : " label 2\n",
4289 : : dc.test_show_locus (richloc));
4290 : 64 : }
4291 : :
4292 : : /* Example of out-of-order ranges (thus requiring a sort), where
4293 : : they overlap, and there are multiple ranges on the same point. */
4294 : 64 : {
4295 : 64 : text_range_label label_0a ("label 0a");
4296 : 64 : text_range_label label_1a ("label 1a");
4297 : 64 : text_range_label label_2a ("label 2a");
4298 : 64 : text_range_label label_0b ("label 0b");
4299 : 64 : text_range_label label_1b ("label 1b");
4300 : 64 : text_range_label label_2b ("label 2b");
4301 : 64 : text_range_label label_0c ("label 0c");
4302 : 64 : text_range_label label_1c ("label 1c");
4303 : 64 : text_range_label label_2c ("label 2c");
4304 : 64 : gcc_rich_location richloc (field, &label_0a, nullptr);
4305 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
4306 : 64 : richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
4307 : :
4308 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
4309 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
4310 : 64 : richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
4311 : :
4312 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
4313 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
4314 : 64 : richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
4315 : :
4316 : 64 : test_diagnostic_context dc;
4317 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4318 : : " ~~~ ~~~ ^~~~~\n"
4319 : : " | | |\n"
4320 : : " | | label 0a\n"
4321 : : " | | label 0b\n"
4322 : : " | | label 0c\n"
4323 : : " | label 1a\n"
4324 : : " | label 1b\n"
4325 : : " | label 1c\n"
4326 : : " label 2a\n"
4327 : : " label 2b\n"
4328 : : " label 2c\n",
4329 : : dc.test_show_locus (richloc));
4330 : 64 : }
4331 : :
4332 : : /* Verify that a NULL result from range_label::get_text is
4333 : : handled gracefully. */
4334 : 64 : {
4335 : 64 : text_range_label label (NULL);
4336 : 64 : gcc_rich_location richloc (bar, &label, nullptr);
4337 : :
4338 : 64 : test_diagnostic_context dc;
4339 : 64 : ASSERT_STREQ (" foo = bar.field;\n"
4340 : : " ^~~\n",
4341 : : dc.test_show_locus (richloc));
4342 : 64 : }
4343 : :
4344 : : /* TODO: example of formatted printing (needs to be in
4345 : : gcc-rich-location.cc due to Makefile.in issues). */
4346 : 64 : }
4347 : :
4348 : : /* Run the various one-liner tests. */
4349 : :
4350 : : static void
4351 : 96 : test_diagnostic_show_locus_one_liner (const line_table_case &case_)
4352 : : {
4353 : : /* Create a tempfile and write some text to it.
4354 : : ....................0000000001111111.
4355 : : ....................1234567890123456. */
4356 : 96 : const char *content = "foo = bar.field;\n";
4357 : :
4358 : 96 : diagnostic_show_locus_fixture f (case_, content);
4359 : :
4360 : 96 : location_t line_end = linemap_position_for_column (line_table, 16);
4361 : :
4362 : : /* Don't attempt to run the tests if column data might be unavailable. */
4363 : 96 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4364 : 32 : return;
4365 : :
4366 : 64 : ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
4367 : 64 : ASSERT_EQ (1, LOCATION_LINE (line_end));
4368 : 64 : ASSERT_EQ (16, LOCATION_COLUMN (line_end));
4369 : :
4370 : 64 : test_one_liner_simple_caret ();
4371 : 64 : test_one_liner_no_column ();
4372 : 64 : test_one_liner_caret_and_range ();
4373 : 64 : test_one_liner_multiple_carets_and_ranges ();
4374 : 64 : test_one_liner_fixit_insert_before ();
4375 : 64 : test_one_liner_fixit_insert_after ();
4376 : 64 : test_one_liner_fixit_remove ();
4377 : 64 : test_one_liner_fixit_replace ();
4378 : 64 : test_one_liner_fixit_replace_non_equal_range ();
4379 : 64 : test_one_liner_fixit_replace_equal_secondary_range ();
4380 : 64 : test_one_liner_fixit_validation_adhoc_locations ();
4381 : 64 : test_one_liner_many_fixits_1 ();
4382 : 64 : test_one_liner_many_fixits_2 ();
4383 : 64 : test_one_liner_labels ();
4384 : 96 : }
4385 : :
4386 : : /* Version of all one-liner tests exercising multibyte awareness.
4387 : : These are all called from test_diagnostic_show_locus_one_liner,
4388 : : which uses diagnostic_show_locus_fixture_one_liner_utf8 to create
4389 : : the test file; see the notes in diagnostic-show-locus-selftest.h.
4390 : :
4391 : : Note: all of the below asserts would be easier to read if we used UTF-8
4392 : : directly in the string constants, but it seems better not to demand the
4393 : : host compiler support this, when it isn't otherwise necessary. Instead,
4394 : : whenever an extended character appears in a string, we put a line break
4395 : : after it so that all succeeding characters can appear visually at the
4396 : : correct display column. */
4397 : :
4398 : : /* Just a caret. */
4399 : :
4400 : : static void
4401 : 64 : test_one_liner_simple_caret_utf8 ()
4402 : : {
4403 : 64 : test_diagnostic_context dc;
4404 : 64 : location_t caret = linemap_position_for_column (line_table, 18);
4405 : 64 : rich_location richloc (line_table, caret);
4406 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4407 : : "_foo = \xcf\x80"
4408 : : "_bar.\xf0\x9f\x98\x82"
4409 : : "_field\xcf\x80"
4410 : : ";\n"
4411 : : " ^\n",
4412 : : dc.test_show_locus (richloc));
4413 : 64 : }
4414 : :
4415 : : /* Caret and range. */
4416 : : static void
4417 : 64 : test_one_liner_caret_and_range_utf8 ()
4418 : : {
4419 : 64 : test_diagnostic_context dc;
4420 : 64 : location_t caret = linemap_position_for_column (line_table, 18);
4421 : 64 : location_t start = linemap_position_for_column (line_table, 12);
4422 : 64 : location_t finish = linemap_position_for_column (line_table, 30);
4423 : 64 : location_t loc = make_location (caret, start, finish);
4424 : 64 : rich_location richloc (line_table, loc);
4425 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4426 : : "_foo = \xcf\x80"
4427 : : "_bar.\xf0\x9f\x98\x82"
4428 : : "_field\xcf\x80"
4429 : : ";\n"
4430 : : " ~~~~~^~~~~~~~~~\n",
4431 : : dc.test_show_locus (richloc));
4432 : 64 : }
4433 : :
4434 : : /* Multiple ranges and carets. */
4435 : :
4436 : : static void
4437 : 64 : test_one_liner_multiple_carets_and_ranges_utf8 ()
4438 : : {
4439 : 64 : test_diagnostic_context dc;
4440 : 64 : location_t foo
4441 : 64 : = make_location (linemap_position_for_column (line_table, 7),
4442 : : linemap_position_for_column (line_table, 1),
4443 : : linemap_position_for_column (line_table, 8));
4444 : 64 : dc.m_source_printing.caret_chars[0] = 'A';
4445 : :
4446 : 64 : location_t bar
4447 : 64 : = make_location (linemap_position_for_column (line_table, 16),
4448 : : linemap_position_for_column (line_table, 12),
4449 : : linemap_position_for_column (line_table, 17));
4450 : 64 : dc.m_source_printing.caret_chars[1] = 'B';
4451 : :
4452 : 64 : location_t field
4453 : 64 : = make_location (linemap_position_for_column (line_table, 26),
4454 : : linemap_position_for_column (line_table, 19),
4455 : : linemap_position_for_column (line_table, 30));
4456 : 64 : dc.m_source_printing.caret_chars[2] = 'C';
4457 : 64 : rich_location richloc (line_table, foo);
4458 : 64 : richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
4459 : 64 : richloc.add_range (field, SHOW_RANGE_WITH_CARET);
4460 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4461 : : "_foo = \xcf\x80"
4462 : : "_bar.\xf0\x9f\x98\x82"
4463 : : "_field\xcf\x80"
4464 : : ";\n"
4465 : : " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
4466 : : dc.test_show_locus (richloc));
4467 : 64 : }
4468 : :
4469 : : /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
4470 : :
4471 : : static void
4472 : 64 : test_one_liner_fixit_insert_before_utf8 ()
4473 : : {
4474 : 64 : test_diagnostic_context dc;
4475 : 64 : location_t caret = linemap_position_for_column (line_table, 12);
4476 : 64 : rich_location richloc (line_table, caret);
4477 : 64 : richloc.add_fixit_insert_before ("&");
4478 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4479 : : "_foo = \xcf\x80"
4480 : : "_bar.\xf0\x9f\x98\x82"
4481 : : "_field\xcf\x80"
4482 : : ";\n"
4483 : : " ^\n"
4484 : : " &\n",
4485 : : dc.test_show_locus (richloc));
4486 : 64 : }
4487 : :
4488 : : /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
4489 : :
4490 : : static void
4491 : 64 : test_one_liner_fixit_insert_after_utf8 ()
4492 : : {
4493 : 64 : test_diagnostic_context dc;
4494 : 64 : location_t start = linemap_position_for_column (line_table, 1);
4495 : 64 : location_t finish = linemap_position_for_column (line_table, 8);
4496 : 64 : location_t foo = make_location (start, start, finish);
4497 : 64 : rich_location richloc (line_table, foo);
4498 : 64 : richloc.add_fixit_insert_after ("[0]");
4499 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4500 : : "_foo = \xcf\x80"
4501 : : "_bar.\xf0\x9f\x98\x82"
4502 : : "_field\xcf\x80"
4503 : : ";\n"
4504 : : " ^~~~~~\n"
4505 : : " [0]\n",
4506 : : dc.test_show_locus (richloc));
4507 : 64 : }
4508 : :
4509 : : /* Removal fix-it hint: removal of the ".SS_fieldP". */
4510 : :
4511 : : static void
4512 : 64 : test_one_liner_fixit_remove_utf8 ()
4513 : : {
4514 : 64 : test_diagnostic_context dc;
4515 : 64 : location_t start = linemap_position_for_column (line_table, 18);
4516 : 64 : location_t finish = linemap_position_for_column (line_table, 30);
4517 : 64 : location_t dot = make_location (start, start, finish);
4518 : 64 : rich_location richloc (line_table, dot);
4519 : 64 : richloc.add_fixit_remove ();
4520 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4521 : : "_foo = \xcf\x80"
4522 : : "_bar.\xf0\x9f\x98\x82"
4523 : : "_field\xcf\x80"
4524 : : ";\n"
4525 : : " ^~~~~~~~~~\n"
4526 : : " ----------\n",
4527 : : dc.test_show_locus (richloc));
4528 : 64 : }
4529 : :
4530 : : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
4531 : :
4532 : : static void
4533 : 64 : test_one_liner_fixit_replace_utf8 ()
4534 : : {
4535 : 64 : test_diagnostic_context dc;
4536 : 64 : location_t start = linemap_position_for_column (line_table, 19);
4537 : 64 : location_t finish = linemap_position_for_column (line_table, 30);
4538 : 64 : location_t field = make_location (start, start, finish);
4539 : 64 : rich_location richloc (line_table, field);
4540 : 64 : richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
4541 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4542 : : "_foo = \xcf\x80"
4543 : : "_bar.\xf0\x9f\x98\x82"
4544 : : "_field\xcf\x80"
4545 : : ";\n"
4546 : : " ^~~~~~~~~\n"
4547 : : " m_\xf0\x9f\x98\x82"
4548 : : "_field\xcf\x80\n",
4549 : : dc.test_show_locus (richloc));
4550 : 64 : }
4551 : :
4552 : : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4553 : : but where the caret was elsewhere. */
4554 : :
4555 : : static void
4556 : 64 : test_one_liner_fixit_replace_non_equal_range_utf8 ()
4557 : : {
4558 : 64 : test_diagnostic_context dc;
4559 : 64 : location_t equals = linemap_position_for_column (line_table, 10);
4560 : 64 : location_t start = linemap_position_for_column (line_table, 19);
4561 : 64 : location_t finish = linemap_position_for_column (line_table, 30);
4562 : 64 : rich_location richloc (line_table, equals);
4563 : 64 : source_range range;
4564 : 64 : range.m_start = start;
4565 : 64 : range.m_finish = finish;
4566 : 64 : richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4567 : : /* The replacement range is not indicated in the annotation line, so
4568 : : it should be indicated via an additional underline. */
4569 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4570 : : "_foo = \xcf\x80"
4571 : : "_bar.\xf0\x9f\x98\x82"
4572 : : "_field\xcf\x80"
4573 : : ";\n"
4574 : : " ^\n"
4575 : : " ---------\n"
4576 : : " m_\xf0\x9f\x98\x82"
4577 : : "_field\xcf\x80\n",
4578 : : dc.test_show_locus (richloc));
4579 : 64 : }
4580 : :
4581 : : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
4582 : : where the caret was elsewhere, but where a secondary range
4583 : : exactly covers "field". */
4584 : :
4585 : : static void
4586 : 64 : test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
4587 : : {
4588 : 64 : test_diagnostic_context dc;
4589 : 64 : location_t equals = linemap_position_for_column (line_table, 10);
4590 : 64 : location_t start = linemap_position_for_column (line_table, 19);
4591 : 64 : location_t finish = linemap_position_for_column (line_table, 30);
4592 : 64 : rich_location richloc (line_table, equals);
4593 : 64 : location_t field = make_location (start, start, finish);
4594 : 64 : richloc.add_range (field);
4595 : 64 : richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
4596 : : /* The replacement range is indicated in the annotation line,
4597 : : so it shouldn't be indicated via an additional underline. */
4598 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4599 : : "_foo = \xcf\x80"
4600 : : "_bar.\xf0\x9f\x98\x82"
4601 : : "_field\xcf\x80"
4602 : : ";\n"
4603 : : " ^ ~~~~~~~~~\n"
4604 : : " m_\xf0\x9f\x98\x82"
4605 : : "_field\xcf\x80\n",
4606 : : dc.test_show_locus (richloc));
4607 : 64 : }
4608 : :
4609 : : /* Verify that we can use ad-hoc locations when adding fixits to a
4610 : : rich_location. */
4611 : :
4612 : : static void
4613 : 64 : test_one_liner_fixit_validation_adhoc_locations_utf8 ()
4614 : : {
4615 : : /* Generate a range that's too long to be packed, so must
4616 : : be stored as an ad-hoc location (given the defaults
4617 : : of 5 bits or 7 bits or 0 bits of packed range); 150 columns > 2**7. */
4618 : 64 : const location_t c12 = linemap_position_for_column (line_table, 12);
4619 : 64 : const location_t c162 = linemap_position_for_column (line_table, 162);
4620 : 64 : const location_t loc = make_location (c12, c12, c162);
4621 : :
4622 : 64 : if (c162 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4623 : : return;
4624 : :
4625 : 64 : ASSERT_TRUE (IS_ADHOC_LOC (loc));
4626 : :
4627 : : /* Insert. */
4628 : 64 : {
4629 : 64 : rich_location richloc (line_table, loc);
4630 : 64 : richloc.add_fixit_insert_before (loc, "test");
4631 : : /* It should not have been discarded by the validator. */
4632 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4633 : :
4634 : 64 : test_diagnostic_context dc;
4635 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4636 : : "_foo = \xcf\x80"
4637 : : "_bar.\xf0\x9f\x98\x82"
4638 : : "_field\xcf\x80"
4639 : : ";\n"
4640 : : " ^~~~~~~~~~~~~~~~ "
4641 : : " "
4642 : : " "
4643 : : " "
4644 : : " "
4645 : : " "
4646 : : " "
4647 : : " "
4648 : : " "
4649 : : " "
4650 : : " "
4651 : : " \n"
4652 : : " test\n",
4653 : : dc.test_show_locus (richloc));
4654 : 64 : }
4655 : :
4656 : : /* Remove. */
4657 : 64 : {
4658 : 64 : rich_location richloc (line_table, loc);
4659 : 64 : source_range range = source_range::from_locations (loc, c162);
4660 : 64 : richloc.add_fixit_remove (range);
4661 : : /* It should not have been discarded by the validator. */
4662 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4663 : :
4664 : 64 : test_diagnostic_context dc;
4665 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4666 : : "_foo = \xcf\x80"
4667 : : "_bar.\xf0\x9f\x98\x82"
4668 : : "_field\xcf\x80"
4669 : : ";\n"
4670 : : " ^~~~~~~~~~~~~~~~ "
4671 : : " "
4672 : : " "
4673 : : " "
4674 : : " "
4675 : : " "
4676 : : " "
4677 : : " "
4678 : : " "
4679 : : " "
4680 : : " "
4681 : : " \n"
4682 : : " -------------------------------------"
4683 : : "----------"
4684 : : "----------"
4685 : : "----------"
4686 : : "----------"
4687 : : "----------"
4688 : : "----------"
4689 : : "----------"
4690 : : "----------"
4691 : : "----------"
4692 : : "----------"
4693 : : "----------\n",
4694 : : dc.test_show_locus (richloc));
4695 : 64 : }
4696 : :
4697 : : /* Replace. */
4698 : 64 : {
4699 : 64 : rich_location richloc (line_table, loc);
4700 : 64 : source_range range = source_range::from_locations (loc, c162);
4701 : 64 : richloc.add_fixit_replace (range, "test");
4702 : : /* It should not have been discarded by the validator. */
4703 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4704 : :
4705 : 64 : test_diagnostic_context dc;
4706 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4707 : : "_foo = \xcf\x80"
4708 : : "_bar.\xf0\x9f\x98\x82"
4709 : : "_field\xcf\x80"
4710 : : ";\n"
4711 : : " ^~~~~~~~~~~~~~~~ "
4712 : : " "
4713 : : " "
4714 : : " "
4715 : : " "
4716 : : " "
4717 : : " "
4718 : : " "
4719 : : " "
4720 : : " "
4721 : : " "
4722 : : " \n"
4723 : : " test\n",
4724 : : dc.test_show_locus (richloc));
4725 : 64 : }
4726 : : }
4727 : :
4728 : : /* Test of consolidating insertions at the same location. */
4729 : :
4730 : : static void
4731 : 64 : test_one_liner_many_fixits_1_utf8 ()
4732 : : {
4733 : 64 : test_diagnostic_context dc;
4734 : 64 : location_t equals = linemap_position_for_column (line_table, 10);
4735 : 64 : rich_location richloc (line_table, equals);
4736 : 1280 : for (int i = 0; i < 19; i++)
4737 : 1856 : richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
4738 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4739 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4740 : : "_foo = \xcf\x80"
4741 : : "_bar.\xf0\x9f\x98\x82"
4742 : : "_field\xcf\x80"
4743 : : ";\n"
4744 : : " ^\n"
4745 : : " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
4746 : : "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
4747 : : dc.test_show_locus (richloc));
4748 : 64 : }
4749 : :
4750 : : /* Ensure that we can add an arbitrary number of fix-it hints to a
4751 : : rich_location, even if they are not consolidated. */
4752 : :
4753 : : static void
4754 : 64 : test_one_liner_many_fixits_2_utf8 ()
4755 : : {
4756 : 64 : test_diagnostic_context dc;
4757 : 64 : location_t equals = linemap_position_for_column (line_table, 10);
4758 : 64 : rich_location richloc (line_table, equals);
4759 : 64 : const int nlocs = 19;
4760 : 64 : int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
4761 : : 34, 36, 38, 40, 42, 44};
4762 : 1280 : for (int i = 0; i != nlocs; ++i)
4763 : : {
4764 : 1216 : location_t loc = linemap_position_for_column (line_table, locs[i]);
4765 : 1856 : richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
4766 : : }
4767 : :
4768 : 64 : ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
4769 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4770 : : "_foo = \xcf\x80"
4771 : : "_bar.\xf0\x9f\x98\x82"
4772 : : "_field\xcf\x80"
4773 : : ";\n"
4774 : : " ^\n"
4775 : : " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
4776 : : " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
4777 : : dc.test_show_locus (richloc));
4778 : 64 : }
4779 : :
4780 : : /* Test of labeling the ranges within a rich_location. */
4781 : :
4782 : : static void
4783 : 64 : test_one_liner_labels_utf8 ()
4784 : : {
4785 : 64 : location_t foo
4786 : 64 : = make_location (linemap_position_for_column (line_table, 1),
4787 : : linemap_position_for_column (line_table, 1),
4788 : : linemap_position_for_column (line_table, 8));
4789 : 64 : location_t bar
4790 : 64 : = make_location (linemap_position_for_column (line_table, 12),
4791 : : linemap_position_for_column (line_table, 12),
4792 : : linemap_position_for_column (line_table, 17));
4793 : 64 : location_t field
4794 : 64 : = make_location (linemap_position_for_column (line_table, 19),
4795 : : linemap_position_for_column (line_table, 19),
4796 : : linemap_position_for_column (line_table, 30));
4797 : :
4798 : : /* Example where all the labels fit on one line. */
4799 : 64 : {
4800 : : /* These three labels contain multibyte characters such that their byte
4801 : : lengths are respectively (12, 10, 18), but their display widths are only
4802 : : (6, 5, 9). All three fit on the line when considering the display
4803 : : widths, but not when considering the byte widths, so verify that we do
4804 : : indeed put them all on one line. */
4805 : 64 : text_range_label label0
4806 : 64 : ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
4807 : 64 : text_range_label label1
4808 : 64 : ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
4809 : 64 : text_range_label label2
4810 : : ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4811 : 64 : "\xcf\x80");
4812 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4813 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4814 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4815 : :
4816 : 64 : {
4817 : 64 : test_diagnostic_context dc;
4818 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4819 : : "_foo = \xcf\x80"
4820 : : "_bar.\xf0\x9f\x98\x82"
4821 : : "_field\xcf\x80"
4822 : : ";\n"
4823 : : " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4824 : : " | | |\n"
4825 : : " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
4826 : : " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
4827 : : " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
4828 : : "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
4829 : : dc.test_show_locus (richloc));
4830 : 64 : }
4831 : :
4832 : 64 : }
4833 : :
4834 : : /* Example where the labels need extra lines. */
4835 : 64 : {
4836 : 64 : text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4837 : 64 : text_range_label label1 ("label 1\xcf\x80");
4838 : 64 : text_range_label label2 ("label 2\xcf\x80");
4839 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4840 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4841 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4842 : :
4843 : 64 : test_diagnostic_context dc;
4844 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4845 : : "_foo = \xcf\x80"
4846 : : "_bar.\xf0\x9f\x98\x82"
4847 : : "_field\xcf\x80"
4848 : : ";\n"
4849 : : " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4850 : : " | | |\n"
4851 : : " | | label 2\xcf\x80\n"
4852 : : " | label 1\xcf\x80\n"
4853 : : " label 0\xf0\x9f\x98\x82\n",
4854 : : dc.test_show_locus (richloc));
4855 : 64 : }
4856 : :
4857 : : /* Example of boundary conditions: label 0 and 1 have just enough clearance,
4858 : : but label 1 just touches label 2. */
4859 : 64 : {
4860 : 64 : text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
4861 : 64 : text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
4862 : 64 : text_range_label label2 ("c");
4863 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4864 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4865 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4866 : :
4867 : 64 : test_diagnostic_context dc;
4868 : 64 : ASSERT_STREQ (" \xf0\x9f\x98\x82"
4869 : : "_foo = \xcf\x80"
4870 : : "_bar.\xf0\x9f\x98\x82"
4871 : : "_field\xcf\x80"
4872 : : ";\n"
4873 : : " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
4874 : : " | | |\n"
4875 : : " | | c\n"
4876 : : " aaaaa\xf0\x9f\x98\x82\xcf\x80"
4877 : : " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
4878 : : dc.test_show_locus (richloc));
4879 : 64 : }
4880 : :
4881 : : /* Example of escaping the source lines. */
4882 : 64 : {
4883 : 64 : text_range_label label0 ("label 0\xf0\x9f\x98\x82");
4884 : 64 : text_range_label label1 ("label 1\xcf\x80");
4885 : 64 : text_range_label label2 ("label 2\xcf\x80");
4886 : 64 : gcc_rich_location richloc (foo, &label0, nullptr);
4887 : 64 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4888 : 64 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4889 : 64 : richloc.set_escape_on_output (true);
4890 : :
4891 : 64 : {
4892 : 64 : test_diagnostic_context dc;
4893 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
4894 : 64 : ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4895 : : " ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4896 : : " | | |\n"
4897 : : " label 0\xf0\x9f\x98\x82"
4898 : : /* ... */ " label 1\xcf\x80"
4899 : : /* ...................*/ " label 2\xcf\x80\n",
4900 : : dc.test_show_locus (richloc));
4901 : 64 : }
4902 : 64 : {
4903 : 64 : test_diagnostic_context dc;
4904 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
4905 : 64 : ASSERT_STREQ
4906 : : (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
4907 : : " ^~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
4908 : : " | | |\n"
4909 : : " label 0\xf0\x9f\x98\x82"
4910 : : /* ... */ " label 1\xcf\x80"
4911 : : /* ..........................*/ " label 2\xcf\x80\n",
4912 : : dc.test_show_locus (richloc));
4913 : 64 : }
4914 : 64 : }
4915 : 64 : }
4916 : :
4917 : : /* Make sure that colorization codes don't interrupt a multibyte
4918 : : sequence, which would corrupt it. */
4919 : : static void
4920 : 64 : test_one_liner_colorized_utf8 ()
4921 : : {
4922 : 64 : test_diagnostic_context dc;
4923 : 64 : dc.m_source_printing.colorize_source_p = true;
4924 : 64 : diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
4925 : 64 : const location_t pi = linemap_position_for_column (line_table, 12);
4926 : 64 : rich_location richloc (line_table, pi);
4927 : :
4928 : : /* In order to avoid having the test depend on exactly how the colorization
4929 : : was effected, just confirm there are two pi characters in the output. */
4930 : 64 : const char *result = dc.test_show_locus (richloc);
4931 : 64 : const char *null_term = result + strlen (result);
4932 : 64 : const char *first_pi = strstr (result, "\xcf\x80");
4933 : 64 : ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
4934 : 64 : ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
4935 : 64 : }
4936 : :
4937 : : static const char * const one_liner_utf8_content
4938 : : /* Display columns.
4939 : : 0000000000000000000000011111111111111111111111111111112222222222222
4940 : : 1111111122222222345678900000000123456666666677777777890123444444445 */
4941 : : = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
4942 : : /* 0000000000000000000001111111111111111111222222222222222222222233333
4943 : : 1111222233334444567890122223333456789999000011112222345678999900001
4944 : : Byte columns. */
4945 : :
4946 : 288 : diagnostic_show_locus_fixture_one_liner_utf8::
4947 : 288 : diagnostic_show_locus_fixture_one_liner_utf8 (const line_table_case &case_)
4948 : 288 : : diagnostic_show_locus_fixture (case_, one_liner_utf8_content)
4949 : : {
4950 : 288 : }
4951 : :
4952 : : /* Run the various one-liner tests. */
4953 : :
4954 : : static void
4955 : 96 : test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
4956 : : {
4957 : 96 : diagnostic_show_locus_fixture_one_liner_utf8 f (case_);
4958 : :
4959 : 96 : location_t line_end = linemap_position_for_column (line_table, 31);
4960 : :
4961 : : /* Don't attempt to run the tests if column data might be unavailable. */
4962 : 96 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4963 : 32 : return;
4964 : :
4965 : 64 : ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
4966 : 64 : ASSERT_EQ (1, LOCATION_LINE (line_end));
4967 : 64 : ASSERT_EQ (31, LOCATION_COLUMN (line_end));
4968 : :
4969 : 64 : char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
4970 : 64 : ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
4971 : : def_policy ()));
4972 : 64 : ASSERT_EQ (25, location_compute_display_column (f.m_fc,
4973 : : expand_location (line_end),
4974 : : def_policy ()));
4975 : :
4976 : 64 : test_one_liner_simple_caret_utf8 ();
4977 : 64 : test_one_liner_caret_and_range_utf8 ();
4978 : 64 : test_one_liner_multiple_carets_and_ranges_utf8 ();
4979 : 64 : test_one_liner_fixit_insert_before_utf8 ();
4980 : 64 : test_one_liner_fixit_insert_after_utf8 ();
4981 : 64 : test_one_liner_fixit_remove_utf8 ();
4982 : 64 : test_one_liner_fixit_replace_utf8 ();
4983 : 64 : test_one_liner_fixit_replace_non_equal_range_utf8 ();
4984 : 64 : test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
4985 : 64 : test_one_liner_fixit_validation_adhoc_locations_utf8 ();
4986 : 64 : test_one_liner_many_fixits_1_utf8 ();
4987 : 64 : test_one_liner_many_fixits_2_utf8 ();
4988 : 64 : test_one_liner_labels_utf8 ();
4989 : 64 : test_one_liner_colorized_utf8 ();
4990 : 96 : }
4991 : :
4992 : : /* Verify that gcc_rich_location::add_location_if_nearby works. */
4993 : :
4994 : : static void
4995 : 96 : test_add_location_if_nearby (const line_table_case &case_)
4996 : : {
4997 : : /* Create a tempfile and write some text to it.
4998 : : ...000000000111111111122222222223333333333.
4999 : : ...123456789012345678901234567890123456789. */
5000 : 96 : const char *content
5001 : : = ("struct same_line { double x; double y; ;\n" /* line 1. */
5002 : : "struct different_line\n" /* line 2. */
5003 : : "{\n" /* line 3. */
5004 : : " double x;\n" /* line 4. */
5005 : : " double y;\n" /* line 5. */
5006 : : ";\n"); /* line 6. */
5007 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content, nullptr);
5008 : 96 : line_table_test ltt (case_);
5009 : :
5010 : 96 : const line_map_ordinary *ord_map
5011 : 96 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5012 : : tmp.get_filename (), 0));
5013 : :
5014 : 96 : linemap_line_start (line_table, 1, 100);
5015 : :
5016 : 96 : const location_t final_line_end
5017 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
5018 : :
5019 : : /* Don't attempt to run the tests if column data might be unavailable. */
5020 : 96 : if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5021 : 40 : return;
5022 : :
5023 : : /* Test of add_location_if_nearby on the same line as the
5024 : : primary location. */
5025 : 56 : {
5026 : 56 : test_diagnostic_context dc;
5027 : 56 : const location_t missing_close_brace_1_39
5028 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
5029 : 56 : const location_t matching_open_brace_1_18
5030 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
5031 : 56 : gcc_rich_location richloc (missing_close_brace_1_39);
5032 : 56 : bool added = richloc.add_location_if_nearby (dc,
5033 : : matching_open_brace_1_18);
5034 : 56 : ASSERT_TRUE (added);
5035 : 56 : ASSERT_EQ (2, richloc.get_num_locations ());
5036 : 56 : ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
5037 : : " ~ ^\n",
5038 : : dc.test_show_locus (richloc));
5039 : 56 : }
5040 : :
5041 : : /* Test of add_location_if_nearby on a different line to the
5042 : : primary location. */
5043 : 56 : {
5044 : 56 : test_diagnostic_context dc;
5045 : 56 : const location_t missing_close_brace_6_1
5046 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
5047 : 56 : const location_t matching_open_brace_3_1
5048 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
5049 : 56 : gcc_rich_location richloc (missing_close_brace_6_1);
5050 : 56 : bool added = richloc.add_location_if_nearby (dc,
5051 : : matching_open_brace_3_1);
5052 : 56 : ASSERT_FALSE (added);
5053 : 56 : ASSERT_EQ (1, richloc.get_num_locations ());
5054 : 56 : }
5055 : 96 : }
5056 : :
5057 : : /* Verify that we print fixits even if they only affect lines
5058 : : outside those covered by the ranges in the rich_location. */
5059 : :
5060 : : static void
5061 : 96 : test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
5062 : : {
5063 : : /* Create a tempfile and write some text to it.
5064 : : ...000000000111111111122222222223333333333.
5065 : : ...123456789012345678901234567890123456789. */
5066 : 96 : const char *content
5067 : : = ("struct point { double x; double y; };\n" /* line 1. */
5068 : : "struct point origin = {x: 0.0,\n" /* line 2. */
5069 : : " y\n" /* line 3. */
5070 : : "\n" /* line 4. */
5071 : : "\n" /* line 5. */
5072 : : " : 0.0};\n"); /* line 6. */
5073 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5074 : 96 : line_table_test ltt (case_);
5075 : :
5076 : 96 : const line_map_ordinary *ord_map
5077 : 96 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5078 : : tmp.get_filename (), 0));
5079 : :
5080 : 96 : linemap_line_start (line_table, 1, 100);
5081 : :
5082 : 96 : const location_t final_line_end
5083 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
5084 : :
5085 : : /* Don't attempt to run the tests if column data might be unavailable. */
5086 : 96 : if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5087 : 40 : return;
5088 : :
5089 : : /* A pair of tests for modernizing the initializers to C99-style. */
5090 : :
5091 : : /* The one-liner case (line 2). */
5092 : 56 : {
5093 : 56 : test_diagnostic_context dc;
5094 : 56 : const location_t x
5095 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
5096 : 56 : const location_t colon
5097 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
5098 : 56 : rich_location richloc (line_table, colon);
5099 : 56 : richloc.add_fixit_insert_before (x, ".");
5100 : 56 : richloc.add_fixit_replace (colon, "=");
5101 : 56 : ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
5102 : : " ^\n"
5103 : : " .=\n",
5104 : : dc.test_show_locus (richloc));
5105 : 56 : }
5106 : :
5107 : : /* The multiline case. The caret for the rich_location is on line 6;
5108 : : verify that insertion fixit on line 3 is still printed (and that
5109 : : span starts are printed due to the gap between the span at line 3
5110 : : and that at line 6). */
5111 : 56 : {
5112 : 56 : test_diagnostic_context dc;
5113 : 56 : const location_t y
5114 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
5115 : 56 : const location_t colon
5116 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
5117 : 56 : rich_location richloc (line_table, colon);
5118 : 56 : richloc.add_fixit_insert_before (y, ".");
5119 : 56 : richloc.add_fixit_replace (colon, "=");
5120 : 56 : ASSERT_STREQ ("FILENAME:3:24:\n"
5121 : : " y\n"
5122 : : " .\n"
5123 : : "FILENAME:6:25:\n"
5124 : : " : 0.0};\n"
5125 : : " ^\n"
5126 : : " =\n",
5127 : : dc.test_show_locus (richloc));
5128 : 56 : }
5129 : :
5130 : : /* As above, but verify the behavior of multiple line spans
5131 : : with line-numbering enabled. */
5132 : 56 : {
5133 : 56 : const location_t y
5134 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
5135 : 56 : const location_t colon
5136 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
5137 : 56 : rich_location richloc (line_table, colon);
5138 : 56 : richloc.add_fixit_insert_before (y, ".");
5139 : 56 : richloc.add_fixit_replace (colon, "=");
5140 : 56 : test_diagnostic_context dc;
5141 : 56 : dc.m_source_printing.show_line_numbers_p = true;
5142 : 56 : ASSERT_STREQ (" 3 | y\n"
5143 : : " | .\n"
5144 : : "......\n"
5145 : : " 6 | : 0.0};\n"
5146 : : " | ^\n"
5147 : : " | =\n",
5148 : : dc.test_show_locus (richloc));
5149 : 56 : }
5150 : 96 : }
5151 : :
5152 : :
5153 : : /* Verify that fix-it hints are appropriately consolidated.
5154 : :
5155 : : If any fix-it hints in a rich_location involve locations beyond
5156 : : LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
5157 : : the fix-it as a whole, so there should be none.
5158 : :
5159 : : Otherwise, verify that consecutive "replace" and "remove" fix-its
5160 : : are merged, and that other fix-its remain separate. */
5161 : :
5162 : : static void
5163 : 96 : test_fixit_consolidation (const line_table_case &case_)
5164 : : {
5165 : 96 : line_table_test ltt (case_);
5166 : :
5167 : 96 : linemap_add (line_table, LC_ENTER, false, "test.c", 1);
5168 : :
5169 : 96 : const location_t c10 = linemap_position_for_column (line_table, 10);
5170 : 96 : const location_t c15 = linemap_position_for_column (line_table, 15);
5171 : 96 : const location_t c16 = linemap_position_for_column (line_table, 16);
5172 : 96 : const location_t c17 = linemap_position_for_column (line_table, 17);
5173 : 96 : const location_t c20 = linemap_position_for_column (line_table, 20);
5174 : 96 : const location_t c21 = linemap_position_for_column (line_table, 21);
5175 : 96 : const location_t caret = c10;
5176 : :
5177 : : /* Insert + insert. */
5178 : 96 : {
5179 : 96 : rich_location richloc (line_table, caret);
5180 : 96 : richloc.add_fixit_insert_before (c10, "foo");
5181 : 96 : richloc.add_fixit_insert_before (c15, "bar");
5182 : :
5183 : 96 : if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5184 : : /* Bogus column info for 2nd fixit, so no fixits. */
5185 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5186 : : else
5187 : : /* They should not have been merged. */
5188 : 64 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5189 : 96 : }
5190 : :
5191 : : /* Insert + replace. */
5192 : 96 : {
5193 : 96 : rich_location richloc (line_table, caret);
5194 : 96 : richloc.add_fixit_insert_before (c10, "foo");
5195 : 96 : richloc.add_fixit_replace (source_range::from_locations (c15, c17),
5196 : : "bar");
5197 : :
5198 : 96 : if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5199 : : /* Bogus column info for 2nd fixit, so no fixits. */
5200 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5201 : : else
5202 : : /* They should not have been merged. */
5203 : 64 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5204 : 96 : }
5205 : :
5206 : : /* Replace + non-consecutive insert. */
5207 : 96 : {
5208 : 96 : rich_location richloc (line_table, caret);
5209 : 96 : richloc.add_fixit_replace (source_range::from_locations (c10, c15),
5210 : : "bar");
5211 : 96 : richloc.add_fixit_insert_before (c17, "foo");
5212 : :
5213 : 96 : if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5214 : : /* Bogus column info for 2nd fixit, so no fixits. */
5215 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5216 : : else
5217 : : /* They should not have been merged. */
5218 : 64 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5219 : 96 : }
5220 : :
5221 : : /* Replace + non-consecutive replace. */
5222 : 96 : {
5223 : 96 : rich_location richloc (line_table, caret);
5224 : 96 : richloc.add_fixit_replace (source_range::from_locations (c10, c15),
5225 : : "foo");
5226 : 96 : richloc.add_fixit_replace (source_range::from_locations (c17, c20),
5227 : : "bar");
5228 : :
5229 : 96 : if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5230 : : /* Bogus column info for 2nd fixit, so no fixits. */
5231 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5232 : : else
5233 : : /* They should not have been merged. */
5234 : 64 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5235 : 96 : }
5236 : :
5237 : : /* Replace + consecutive replace. */
5238 : 96 : {
5239 : 96 : rich_location richloc (line_table, caret);
5240 : 96 : richloc.add_fixit_replace (source_range::from_locations (c10, c15),
5241 : : "foo");
5242 : 96 : richloc.add_fixit_replace (source_range::from_locations (c16, c20),
5243 : : "bar");
5244 : :
5245 : 96 : if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5246 : : /* Bogus column info for 2nd fixit, so no fixits. */
5247 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5248 : : else
5249 : : {
5250 : : /* They should have been merged into a single "replace". */
5251 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5252 : 64 : const fixit_hint *hint = richloc.get_fixit_hint (0);
5253 : 64 : ASSERT_STREQ ("foobar", hint->get_string ());
5254 : 64 : ASSERT_EQ (c10, hint->get_start_loc ());
5255 : 64 : ASSERT_EQ (c21, hint->get_next_loc ());
5256 : : }
5257 : 96 : }
5258 : :
5259 : : /* Replace + consecutive removal. */
5260 : 96 : {
5261 : 96 : rich_location richloc (line_table, caret);
5262 : 96 : richloc.add_fixit_replace (source_range::from_locations (c10, c15),
5263 : : "foo");
5264 : 96 : richloc.add_fixit_remove (source_range::from_locations (c16, c20));
5265 : :
5266 : 96 : if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5267 : : /* Bogus column info for 2nd fixit, so no fixits. */
5268 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5269 : : else
5270 : : {
5271 : : /* They should have been merged into a single replace, with the
5272 : : range extended to cover that of the removal. */
5273 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5274 : 64 : const fixit_hint *hint = richloc.get_fixit_hint (0);
5275 : 64 : ASSERT_STREQ ("foo", hint->get_string ());
5276 : 64 : ASSERT_EQ (c10, hint->get_start_loc ());
5277 : 64 : ASSERT_EQ (c21, hint->get_next_loc ());
5278 : : }
5279 : 96 : }
5280 : :
5281 : : /* Consecutive removals. */
5282 : 96 : {
5283 : 96 : rich_location richloc (line_table, caret);
5284 : 96 : richloc.add_fixit_remove (source_range::from_locations (c10, c15));
5285 : 96 : richloc.add_fixit_remove (source_range::from_locations (c16, c20));
5286 : :
5287 : 96 : if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
5288 : : /* Bogus column info for 2nd fixit, so no fixits. */
5289 : 32 : ASSERT_EQ (0, richloc.get_num_fixit_hints ());
5290 : : else
5291 : : {
5292 : : /* They should have been merged into a single "replace-with-empty". */
5293 : 64 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5294 : 64 : const fixit_hint *hint = richloc.get_fixit_hint (0);
5295 : 64 : ASSERT_STREQ ("", hint->get_string ());
5296 : 64 : ASSERT_EQ (c10, hint->get_start_loc ());
5297 : 64 : ASSERT_EQ (c21, hint->get_next_loc ());
5298 : : }
5299 : 96 : }
5300 : 96 : }
5301 : :
5302 : : /* Verify that the line_corrections machinery correctly prints
5303 : : overlapping fixit-hints. */
5304 : :
5305 : : static void
5306 : 96 : test_overlapped_fixit_printing (const line_table_case &case_)
5307 : : {
5308 : : /* Create a tempfile and write some text to it.
5309 : : ...000000000111111111122222222223333333333.
5310 : : ...123456789012345678901234567890123456789. */
5311 : 96 : const char *content
5312 : : = (" foo *f = (foo *)ptr->field;\n");
5313 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
5314 : 96 : file_cache fc;
5315 : 96 : line_table_test ltt (case_);
5316 : :
5317 : 96 : const line_map_ordinary *ord_map
5318 : 96 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5319 : : tmp.get_filename (), 0));
5320 : :
5321 : 96 : linemap_line_start (line_table, 1, 100);
5322 : :
5323 : 96 : const location_t final_line_end
5324 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
5325 : :
5326 : : /* Don't attempt to run the tests if column data might be unavailable. */
5327 : 96 : if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5328 : 40 : return;
5329 : :
5330 : : /* A test for converting a C-style cast to a C++-style cast. */
5331 : 56 : const location_t open_paren
5332 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
5333 : 56 : const location_t close_paren
5334 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
5335 : 56 : const location_t expr_start
5336 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
5337 : 56 : const location_t expr_finish
5338 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
5339 : 56 : const location_t expr = make_location (expr_start, expr_start, expr_finish);
5340 : :
5341 : : /* Various examples of fix-it hints that aren't themselves consolidated,
5342 : : but for which the *printing* may need consolidation. */
5343 : :
5344 : : /* Example where 3 fix-it hints are printed as one. */
5345 : 56 : {
5346 : 56 : test_diagnostic_context dc;
5347 : 56 : rich_location richloc (line_table, expr);
5348 : 56 : richloc.add_fixit_replace (open_paren, "const_cast<");
5349 : 56 : richloc.add_fixit_replace (close_paren, "> (");
5350 : 56 : richloc.add_fixit_insert_after (")");
5351 : :
5352 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5353 : : " ^~~~~~~~~~\n"
5354 : : " -----------------\n"
5355 : : " const_cast<foo *> (ptr->field)\n",
5356 : : dc.test_show_locus (richloc));
5357 : :
5358 : : /* Unit-test the line_corrections machinery. */
5359 : 56 : char_display_policy policy (make_char_policy (dc, richloc));
5360 : 56 : ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5361 : 56 : const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5362 : 112 : ASSERT_EQ (column_range (12, 12),
5363 : : get_affected_range (fc, policy, hint_0, CU_BYTES));
5364 : 112 : ASSERT_EQ (column_range (12, 12),
5365 : : get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
5366 : 112 : ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
5367 : 56 : const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5368 : 112 : ASSERT_EQ (column_range (18, 18),
5369 : : get_affected_range (fc, policy, hint_1, CU_BYTES));
5370 : 112 : ASSERT_EQ (column_range (18, 18),
5371 : : get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
5372 : 112 : ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
5373 : 56 : const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5374 : 112 : ASSERT_EQ (column_range (29, 28),
5375 : : get_affected_range (fc, policy, hint_2, CU_BYTES));
5376 : 112 : ASSERT_EQ (column_range (29, 28),
5377 : : get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
5378 : 112 : ASSERT_EQ (column_range (29, 29), get_printed_columns (fc, policy, hint_2));
5379 : :
5380 : : /* Add each hint in turn to a line_corrections instance,
5381 : : and verify that they are consolidated into one correction instance
5382 : : as expected. */
5383 : 56 : line_corrections lc (fc, policy, tmp.get_filename (), 1);
5384 : :
5385 : : /* The first replace hint by itself. */
5386 : 56 : lc.add_hint (hint_0);
5387 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5388 : 112 : ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
5389 : 112 : ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5390 : 112 : ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5391 : 56 : ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5392 : :
5393 : : /* After the second replacement hint, they are printed together
5394 : : as a replacement (along with the text between them). */
5395 : 56 : lc.add_hint (hint_1);
5396 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5397 : 56 : ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
5398 : 112 : ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
5399 : 112 : ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5400 : 112 : ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5401 : :
5402 : : /* After the final insertion hint, they are all printed together
5403 : : as a replacement (along with the text between them). */
5404 : 56 : lc.add_hint (hint_2);
5405 : 56 : ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
5406 : : lc.m_corrections[0]->m_text);
5407 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5408 : 112 : ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
5409 : 112 : ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
5410 : 112 : ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
5411 : 56 : }
5412 : :
5413 : : /* Example where two are consolidated during printing. */
5414 : 56 : {
5415 : 56 : test_diagnostic_context dc;
5416 : 56 : rich_location richloc (line_table, expr);
5417 : 56 : richloc.add_fixit_replace (open_paren, "CAST (");
5418 : 56 : richloc.add_fixit_replace (close_paren, ") (");
5419 : 56 : richloc.add_fixit_insert_after (")");
5420 : :
5421 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5422 : : " ^~~~~~~~~~\n"
5423 : : " -\n"
5424 : : " CAST (-\n"
5425 : : " ) ( )\n",
5426 : : dc.test_show_locus (richloc));
5427 : 56 : }
5428 : :
5429 : : /* Example where none are consolidated during printing. */
5430 : 56 : {
5431 : 56 : test_diagnostic_context dc;
5432 : 56 : rich_location richloc (line_table, expr);
5433 : 56 : richloc.add_fixit_replace (open_paren, "CST (");
5434 : 56 : richloc.add_fixit_replace (close_paren, ") (");
5435 : 56 : richloc.add_fixit_insert_after (")");
5436 : :
5437 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5438 : : " ^~~~~~~~~~\n"
5439 : : " -\n"
5440 : : " CST ( -\n"
5441 : : " ) ( )\n",
5442 : : dc.test_show_locus (richloc));
5443 : 56 : }
5444 : :
5445 : : /* Example of deletion fix-it hints. */
5446 : 56 : {
5447 : 56 : test_diagnostic_context dc;
5448 : 56 : rich_location richloc (line_table, expr);
5449 : 56 : richloc.add_fixit_insert_before (open_paren, "(bar *)");
5450 : 56 : source_range victim = {open_paren, close_paren};
5451 : 56 : richloc.add_fixit_remove (victim);
5452 : :
5453 : : /* This case is actually handled by fixit-consolidation,
5454 : : rather than by line_corrections. */
5455 : 56 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5456 : :
5457 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5458 : : " ^~~~~~~~~~\n"
5459 : : " -------\n"
5460 : : " (bar *)\n",
5461 : : dc.test_show_locus (richloc));
5462 : 56 : }
5463 : :
5464 : : /* Example of deletion fix-it hints that would overlap. */
5465 : 56 : {
5466 : 56 : test_diagnostic_context dc;
5467 : 56 : rich_location richloc (line_table, expr);
5468 : 56 : richloc.add_fixit_insert_before (open_paren, "(longer *)");
5469 : 56 : source_range victim = {expr_start, expr_finish};
5470 : 56 : richloc.add_fixit_remove (victim);
5471 : :
5472 : : /* These fixits are not consolidated. */
5473 : 56 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5474 : :
5475 : : /* But the corrections are. */
5476 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5477 : : " ^~~~~~~~~~\n"
5478 : : " -----------------\n"
5479 : : " (longer *)(foo *)\n",
5480 : : dc.test_show_locus (richloc));
5481 : 56 : }
5482 : :
5483 : : /* Example of insertion fix-it hints that would overlap. */
5484 : 56 : {
5485 : 56 : test_diagnostic_context dc;
5486 : 56 : rich_location richloc (line_table, expr);
5487 : 56 : richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
5488 : 56 : richloc.add_fixit_insert_after (close_paren, "TEST");
5489 : :
5490 : : /* The first insertion is long enough that if printed naively,
5491 : : it would overlap with the second.
5492 : : Verify that they are printed as a single replacement. */
5493 : 56 : ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
5494 : : " ^~~~~~~~~~\n"
5495 : : " -------\n"
5496 : : " LONGER THAN THE CAST(foo *)TEST\n",
5497 : : dc.test_show_locus (richloc));
5498 : 56 : }
5499 : 96 : }
5500 : :
5501 : : /* Multibyte-aware version of preceding tests. See comments above
5502 : : test_one_liner_simple_caret_utf8() too, we use the same two multibyte
5503 : : characters here. */
5504 : :
5505 : : static void
5506 : 96 : test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
5507 : : {
5508 : : /* Create a tempfile and write some text to it. */
5509 : :
5510 : 96 : const char *content
5511 : : /* Display columns.
5512 : : 00000000000000000000000111111111111111111111111222222222222222223
5513 : : 12344444444555555556789012344444444555555556789012345678999999990 */
5514 : : = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
5515 : : /* 00000000000000000000011111111111111111111112222222222333333333333
5516 : : 12344445555666677778901234566667777888899990123456789012333344445
5517 : : Byte columns. */
5518 : :
5519 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
5520 : 96 : line_table_test ltt (case_);
5521 : :
5522 : 96 : const line_map_ordinary *ord_map
5523 : 96 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5524 : : tmp.get_filename (), 0));
5525 : :
5526 : 96 : linemap_line_start (line_table, 1, 100);
5527 : :
5528 : 96 : const location_t final_line_end
5529 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
5530 : :
5531 : : /* Don't attempt to run the tests if column data might be unavailable. */
5532 : 96 : if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5533 : 40 : return;
5534 : :
5535 : : /* A test for converting a C-style cast to a C++-style cast. */
5536 : 56 : const location_t open_paren
5537 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
5538 : 56 : const location_t close_paren
5539 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
5540 : 56 : const location_t expr_start
5541 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5542 : 56 : const location_t expr_finish
5543 : 56 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
5544 : 56 : const location_t expr = make_location (expr_start, expr_start, expr_finish);
5545 : :
5546 : : /* Various examples of fix-it hints that aren't themselves consolidated,
5547 : : but for which the *printing* may need consolidation. */
5548 : :
5549 : : /* Example where 3 fix-it hints are printed as one. */
5550 : 56 : {
5551 : 56 : test_diagnostic_context dc;
5552 : 56 : file_cache &fc = dc.get_file_cache ();
5553 : 56 : rich_location richloc (line_table, expr);
5554 : 56 : richloc.add_fixit_replace (open_paren, "const_cast<");
5555 : 56 : richloc.add_fixit_replace (close_paren, "> (");
5556 : 56 : richloc.add_fixit_insert_after (")");
5557 : :
5558 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5559 : : " *f = (f\xf0\x9f\x98\x82"
5560 : : " *)ptr->field\xcf\x80"
5561 : : ";\n"
5562 : : " ^~~~~~~~~~~\n"
5563 : : " ------------------\n"
5564 : : " const_cast<f\xf0\x9f\x98\x82"
5565 : : " *> (ptr->field\xcf\x80"
5566 : : ")\n",
5567 : : dc.test_show_locus (richloc));
5568 : :
5569 : : /* Unit-test the line_corrections machinery. */
5570 : 56 : char_display_policy policy (make_char_policy (dc, richloc));
5571 : 56 : ASSERT_EQ (3, richloc.get_num_fixit_hints ());
5572 : 56 : const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5573 : 112 : ASSERT_EQ (column_range (14, 14),
5574 : : get_affected_range (fc, policy, hint_0, CU_BYTES));
5575 : 112 : ASSERT_EQ (column_range (12, 12),
5576 : : get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
5577 : 112 : ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
5578 : 56 : const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5579 : 112 : ASSERT_EQ (column_range (22, 22),
5580 : : get_affected_range (fc, policy, hint_1, CU_BYTES));
5581 : 112 : ASSERT_EQ (column_range (18, 18),
5582 : : get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
5583 : 112 : ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
5584 : 56 : const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
5585 : 112 : ASSERT_EQ (column_range (35, 34),
5586 : : get_affected_range (fc, policy, hint_2, CU_BYTES));
5587 : 112 : ASSERT_EQ (column_range (30, 29),
5588 : : get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
5589 : 112 : ASSERT_EQ (column_range (30, 30), get_printed_columns (fc, policy, hint_2));
5590 : :
5591 : : /* Add each hint in turn to a line_corrections instance,
5592 : : and verify that they are consolidated into one correction instance
5593 : : as expected. */
5594 : 56 : line_corrections lc (fc, policy, tmp.get_filename (), 1);
5595 : :
5596 : : /* The first replace hint by itself. */
5597 : 56 : lc.add_hint (hint_0);
5598 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5599 : 112 : ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
5600 : 112 : ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
5601 : 112 : ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
5602 : 56 : ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
5603 : :
5604 : : /* After the second replacement hint, they are printed together
5605 : : as a replacement (along with the text between them). */
5606 : 56 : lc.add_hint (hint_1);
5607 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5608 : 56 : ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
5609 : : lc.m_corrections[0]->m_text);
5610 : 112 : ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
5611 : 112 : ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
5612 : 112 : ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
5613 : :
5614 : : /* After the final insertion hint, they are all printed together
5615 : : as a replacement (along with the text between them). */
5616 : 56 : lc.add_hint (hint_2);
5617 : 56 : ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
5618 : : lc.m_corrections[0]->m_text);
5619 : 56 : ASSERT_EQ (1, lc.m_corrections.length ());
5620 : 112 : ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
5621 : 112 : ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
5622 : 112 : ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
5623 : 56 : }
5624 : :
5625 : : /* Example where two are consolidated during printing. */
5626 : 56 : {
5627 : 56 : test_diagnostic_context dc;
5628 : 56 : rich_location richloc (line_table, expr);
5629 : 56 : richloc.add_fixit_replace (open_paren, "CAST (");
5630 : 56 : richloc.add_fixit_replace (close_paren, ") (");
5631 : 56 : richloc.add_fixit_insert_after (")");
5632 : :
5633 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5634 : : " *f = (f\xf0\x9f\x98\x82"
5635 : : " *)ptr->field\xcf\x80"
5636 : : ";\n"
5637 : : " ^~~~~~~~~~~\n"
5638 : : " -\n"
5639 : : " CAST (-\n"
5640 : : " ) ( )\n",
5641 : : dc.test_show_locus (richloc));
5642 : 56 : }
5643 : :
5644 : : /* Example where none are consolidated during printing. */
5645 : 56 : {
5646 : 56 : test_diagnostic_context dc;
5647 : 56 : rich_location richloc (line_table, expr);
5648 : 56 : richloc.add_fixit_replace (open_paren, "CST (");
5649 : 56 : richloc.add_fixit_replace (close_paren, ") (");
5650 : 56 : richloc.add_fixit_insert_after (")");
5651 : :
5652 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5653 : : " *f = (f\xf0\x9f\x98\x82"
5654 : : " *)ptr->field\xcf\x80"
5655 : : ";\n"
5656 : : " ^~~~~~~~~~~\n"
5657 : : " -\n"
5658 : : " CST ( -\n"
5659 : : " ) ( )\n",
5660 : : dc.test_show_locus (richloc));
5661 : 56 : }
5662 : :
5663 : : /* Example of deletion fix-it hints. */
5664 : 56 : {
5665 : 56 : test_diagnostic_context dc;
5666 : 56 : rich_location richloc (line_table, expr);
5667 : 56 : richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
5668 : 56 : source_range victim = {open_paren, close_paren};
5669 : 56 : richloc.add_fixit_remove (victim);
5670 : :
5671 : : /* This case is actually handled by fixit-consolidation,
5672 : : rather than by line_corrections. */
5673 : 56 : ASSERT_EQ (1, richloc.get_num_fixit_hints ());
5674 : :
5675 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5676 : : " *f = (f\xf0\x9f\x98\x82"
5677 : : " *)ptr->field\xcf\x80"
5678 : : ";\n"
5679 : : " ^~~~~~~~~~~\n"
5680 : : " -------\n"
5681 : : " (bar\xf0\x9f\x98\x82"
5682 : : " *)\n",
5683 : : dc.test_show_locus (richloc));
5684 : 56 : }
5685 : :
5686 : : /* Example of deletion fix-it hints that would overlap. */
5687 : 56 : {
5688 : 56 : test_diagnostic_context dc;
5689 : 56 : rich_location richloc (line_table, expr);
5690 : 56 : richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
5691 : 56 : source_range victim = {expr_start, expr_finish};
5692 : 56 : richloc.add_fixit_remove (victim);
5693 : :
5694 : : /* These fixits are not consolidated. */
5695 : 56 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5696 : :
5697 : : /* But the corrections are. */
5698 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5699 : : " *f = (f\xf0\x9f\x98\x82"
5700 : : " *)ptr->field\xcf\x80"
5701 : : ";\n"
5702 : : " ^~~~~~~~~~~\n"
5703 : : " ------------------\n"
5704 : : " (long\xf0\x9f\x98\x82"
5705 : : " *)(f\xf0\x9f\x98\x82"
5706 : : " *)\n",
5707 : : dc.test_show_locus (richloc));
5708 : 56 : }
5709 : :
5710 : : /* Example of insertion fix-it hints that would overlap. */
5711 : 56 : {
5712 : 56 : test_diagnostic_context dc;
5713 : 56 : rich_location richloc (line_table, expr);
5714 : 56 : richloc.add_fixit_insert_before
5715 : 56 : (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
5716 : 56 : richloc.add_fixit_insert_after (close_paren, "TEST");
5717 : :
5718 : : /* The first insertion is long enough that if printed naively,
5719 : : it would overlap with the second.
5720 : : Verify that they are printed as a single replacement. */
5721 : 56 : ASSERT_STREQ (" f\xf0\x9f\x98\x82"
5722 : : " *f = (f\xf0\x9f\x98\x82"
5723 : : " *)ptr->field\xcf\x80"
5724 : : ";\n"
5725 : : " ^~~~~~~~~~~\n"
5726 : : " -------\n"
5727 : : " L\xf0\x9f\x98\x82"
5728 : : "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
5729 : : " *)TEST\n",
5730 : : dc.test_show_locus (richloc));
5731 : 56 : }
5732 : 96 : }
5733 : :
5734 : : /* Verify that the line_corrections machinery correctly prints
5735 : : overlapping fixit-hints that have been added in the wrong
5736 : : order.
5737 : : Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
5738 : :
5739 : : static void
5740 : 96 : test_overlapped_fixit_printing_2 (const line_table_case &case_)
5741 : : {
5742 : : /* Create a tempfile and write some text to it.
5743 : : ...000000000111111111122222222223333333333.
5744 : : ...123456789012345678901234567890123456789. */
5745 : 96 : const char *content
5746 : : = ("int a5[][0][0] = { 1, 2 };\n");
5747 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
5748 : 96 : line_table_test ltt (case_);
5749 : :
5750 : 96 : const line_map_ordinary *ord_map
5751 : 96 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
5752 : : tmp.get_filename (), 0));
5753 : :
5754 : 96 : linemap_line_start (line_table, 1, 100);
5755 : :
5756 : 96 : const location_t final_line_end
5757 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
5758 : :
5759 : : /* Don't attempt to run the tests if column data might be unavailable. */
5760 : 96 : if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5761 : 32 : return;
5762 : :
5763 : 64 : const location_t col_1
5764 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5765 : 64 : const location_t col_20
5766 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
5767 : 64 : const location_t col_21
5768 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
5769 : 64 : const location_t col_23
5770 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
5771 : 64 : const location_t col_25
5772 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
5773 : :
5774 : : /* Two insertions, in the wrong order. */
5775 : 64 : {
5776 : 64 : test_diagnostic_context dc;
5777 : 64 : file_cache &fc = dc.get_file_cache ();
5778 : :
5779 : 64 : rich_location richloc (line_table, col_20);
5780 : 64 : richloc.add_fixit_insert_before (col_23, "{");
5781 : 64 : richloc.add_fixit_insert_before (col_21, "}");
5782 : :
5783 : : /* These fixits should be accepted; they can't be consolidated. */
5784 : 64 : char_display_policy policy (make_char_policy (dc, richloc));
5785 : 64 : ASSERT_EQ (2, richloc.get_num_fixit_hints ());
5786 : 64 : const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
5787 : 128 : ASSERT_EQ (column_range (23, 22),
5788 : : get_affected_range (fc, policy, hint_0, CU_BYTES));
5789 : 128 : ASSERT_EQ (column_range (23, 23), get_printed_columns (fc, policy, hint_0));
5790 : 64 : const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
5791 : 128 : ASSERT_EQ (column_range (21, 20),
5792 : : get_affected_range (fc, policy, hint_1, CU_BYTES));
5793 : 128 : ASSERT_EQ (column_range (21, 21), get_printed_columns (fc, policy, hint_1));
5794 : :
5795 : : /* Verify that they're printed correctly. */
5796 : 64 : ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5797 : : " ^\n"
5798 : : " } {\n",
5799 : : dc.test_show_locus (richloc));
5800 : 64 : }
5801 : :
5802 : : /* Various overlapping insertions, some occurring "out of order"
5803 : : (reproducing the fix-it hints from PR c/81405). */
5804 : 64 : {
5805 : 64 : test_diagnostic_context dc;
5806 : 64 : rich_location richloc (line_table, col_20);
5807 : :
5808 : 64 : richloc.add_fixit_insert_before (col_20, "{{");
5809 : 64 : richloc.add_fixit_insert_before (col_21, "}}");
5810 : 64 : richloc.add_fixit_insert_before (col_23, "{");
5811 : 64 : richloc.add_fixit_insert_before (col_21, "}");
5812 : 64 : richloc.add_fixit_insert_before (col_23, "{{");
5813 : 64 : richloc.add_fixit_insert_before (col_25, "}");
5814 : 64 : richloc.add_fixit_insert_before (col_21, "}");
5815 : 64 : richloc.add_fixit_insert_before (col_1, "{");
5816 : 64 : richloc.add_fixit_insert_before (col_25, "}");
5817 : :
5818 : 64 : ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
5819 : : " ^\n"
5820 : : " { -----\n"
5821 : : " {{1}}}}, {{{2 }}\n",
5822 : : dc.test_show_locus (richloc));
5823 : 64 : }
5824 : 96 : }
5825 : :
5826 : : /* Insertion fix-it hint: adding a "break;" on a line by itself. */
5827 : :
5828 : : static void
5829 : 96 : test_fixit_insert_containing_newline (const line_table_case &case_)
5830 : : {
5831 : : /* Create a tempfile and write some text to it.
5832 : : .........................0000000001111111.
5833 : : .........................1234567890123456. */
5834 : 96 : const char *old_content = (" case 'a':\n" /* line 1. */
5835 : : " x = a;\n" /* line 2. */
5836 : : " case 'b':\n" /* line 3. */
5837 : : " x = b;\n");/* line 4. */
5838 : :
5839 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5840 : 96 : line_table_test ltt (case_);
5841 : 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
5842 : :
5843 : 96 : location_t case_start = linemap_position_for_column (line_table, 5);
5844 : 96 : location_t case_finish = linemap_position_for_column (line_table, 13);
5845 : 96 : location_t case_loc = make_location (case_start, case_start, case_finish);
5846 : 96 : location_t line_start = linemap_position_for_column (line_table, 1);
5847 : :
5848 : 96 : if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5849 : 32 : return;
5850 : :
5851 : : /* Add a "break;" on a line by itself before line 3 i.e. before
5852 : : column 1 of line 3. */
5853 : 64 : {
5854 : 64 : rich_location richloc (line_table, case_loc);
5855 : 64 : richloc.add_fixit_insert_before (line_start, " break;\n");
5856 : :
5857 : : /* Without line numbers. */
5858 : 64 : {
5859 : 64 : test_diagnostic_context dc;
5860 : 64 : ASSERT_STREQ (" x = a;\n"
5861 : : "+ break;\n"
5862 : : " case 'b':\n"
5863 : : " ^~~~~~~~~\n",
5864 : : dc.test_show_locus (richloc));
5865 : 64 : }
5866 : :
5867 : : /* With line numbers. */
5868 : 64 : {
5869 : 64 : test_diagnostic_context dc;
5870 : 64 : dc.m_source_printing.show_line_numbers_p = true;
5871 : 64 : ASSERT_STREQ (" 2 | x = a;\n"
5872 : : " +++ |+ break;\n"
5873 : : " 3 | case 'b':\n"
5874 : : " | ^~~~~~~~~\n",
5875 : : dc.test_show_locus (richloc));
5876 : 64 : }
5877 : 64 : }
5878 : :
5879 : : /* Verify that attempts to add text with a newline fail when the
5880 : : insertion point is *not* at the start of a line. */
5881 : 64 : {
5882 : 64 : rich_location richloc (line_table, case_loc);
5883 : 64 : richloc.add_fixit_insert_before (case_start, "break;\n");
5884 : 64 : ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5885 : 64 : test_diagnostic_context dc;
5886 : 64 : ASSERT_STREQ (" case 'b':\n"
5887 : : " ^~~~~~~~~\n",
5888 : : dc.test_show_locus (richloc));
5889 : 64 : }
5890 : 96 : }
5891 : :
5892 : : /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
5893 : : of the file, where the fix-it is printed in a different line-span
5894 : : to the primary range of the diagnostic. */
5895 : :
5896 : : static void
5897 : 96 : test_fixit_insert_containing_newline_2 (const line_table_case &case_)
5898 : : {
5899 : : /* Create a tempfile and write some text to it.
5900 : : .........................0000000001111111.
5901 : : .........................1234567890123456. */
5902 : 96 : const char *old_content = ("test (int ch)\n" /* line 1. */
5903 : : "{\n" /* line 2. */
5904 : : " putchar (ch);\n" /* line 3. */
5905 : : "}\n"); /* line 4. */
5906 : :
5907 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5908 : 96 : line_table_test ltt (case_);
5909 : :
5910 : 96 : const line_map_ordinary *ord_map = linemap_check_ordinary
5911 : 96 : (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
5912 : 96 : linemap_line_start (line_table, 1, 100);
5913 : :
5914 : : /* The primary range is the "putchar" token. */
5915 : 96 : location_t putchar_start
5916 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
5917 : 96 : location_t putchar_finish
5918 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
5919 : 96 : location_t putchar_loc
5920 : 96 : = make_location (putchar_start, putchar_start, putchar_finish);
5921 : 96 : rich_location richloc (line_table, putchar_loc);
5922 : :
5923 : : /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
5924 : 96 : location_t file_start
5925 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
5926 : 96 : richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
5927 : :
5928 : 96 : if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5929 : 36 : return;
5930 : :
5931 : 60 : {
5932 : 60 : test_diagnostic_context dc;
5933 : 60 : ASSERT_STREQ ("FILENAME:1:1:\n"
5934 : : "+#include <stdio.h>\n"
5935 : : " test (int ch)\n"
5936 : : "FILENAME:3:2:\n"
5937 : : " putchar (ch);\n"
5938 : : " ^~~~~~~\n",
5939 : : dc.test_show_locus (richloc));
5940 : 60 : }
5941 : :
5942 : : /* With line-numbering, the line spans are close enough to be
5943 : : consolidated, since it makes little sense to skip line 2. */
5944 : 60 : {
5945 : 60 : test_diagnostic_context dc;
5946 : 60 : dc.m_source_printing.show_line_numbers_p = true;
5947 : 60 : ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
5948 : : " 1 | test (int ch)\n"
5949 : : " 2 | {\n"
5950 : : " 3 | putchar (ch);\n"
5951 : : " | ^~~~~~~\n",
5952 : : dc.test_show_locus (richloc));
5953 : 60 : }
5954 : 96 : }
5955 : :
5956 : : /* Replacement fix-it hint containing a newline.
5957 : : This will fail, as newlines are only supported when inserting at the
5958 : : beginning of a line. */
5959 : :
5960 : : static void
5961 : 96 : test_fixit_replace_containing_newline (const line_table_case &case_)
5962 : : {
5963 : : /* Create a tempfile and write some text to it.
5964 : : .........................0000000001111.
5965 : : .........................1234567890123. */
5966 : 96 : const char *old_content = "foo = bar ();\n";
5967 : :
5968 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
5969 : 96 : line_table_test ltt (case_);
5970 : 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
5971 : :
5972 : : /* Replace the " = " with "\n = ", as if we were reformatting an
5973 : : overly long line. */
5974 : 96 : location_t start = linemap_position_for_column (line_table, 4);
5975 : 96 : location_t finish = linemap_position_for_column (line_table, 6);
5976 : 96 : location_t loc = linemap_position_for_column (line_table, 13);
5977 : 96 : rich_location richloc (line_table, loc);
5978 : 96 : source_range range = source_range::from_locations (start, finish);
5979 : 96 : richloc.add_fixit_replace (range, "\n =");
5980 : :
5981 : : /* Arbitrary newlines are not yet supported within fix-it hints, so
5982 : : the fix-it should not be displayed. */
5983 : 96 : ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
5984 : :
5985 : 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
5986 : 32 : return;
5987 : :
5988 : 64 : test_diagnostic_context dc;
5989 : 64 : ASSERT_STREQ (" foo = bar ();\n"
5990 : : " ^\n",
5991 : : dc.test_show_locus (richloc));
5992 : 96 : }
5993 : :
5994 : : /* Fix-it hint, attempting to delete a newline.
5995 : : This will fail, as we currently only support fix-it hints that
5996 : : affect one line at a time. */
5997 : :
5998 : : static void
5999 : 96 : test_fixit_deletion_affecting_newline (const line_table_case &case_)
6000 : : {
6001 : : /* Create a tempfile and write some text to it.
6002 : : ..........................0000000001111.
6003 : : ..........................1234567890123. */
6004 : 96 : const char *old_content = ("foo = bar (\n"
6005 : : " );\n");
6006 : :
6007 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
6008 : 96 : line_table_test ltt (case_);
6009 : 96 : const line_map_ordinary *ord_map = linemap_check_ordinary
6010 : 96 : (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
6011 : 96 : linemap_line_start (line_table, 1, 100);
6012 : :
6013 : : /* Attempt to delete the " (\n...)". */
6014 : 96 : location_t start
6015 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
6016 : 96 : location_t caret
6017 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
6018 : 96 : location_t finish
6019 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
6020 : 96 : location_t loc = make_location (caret, start, finish);
6021 : 96 : rich_location richloc (line_table, loc);
6022 : 96 : richloc. add_fixit_remove ();
6023 : :
6024 : : /* Fix-it hints that affect more than one line are not yet supported, so
6025 : : the fix-it should not be displayed. */
6026 : 96 : ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
6027 : :
6028 : 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
6029 : 32 : return;
6030 : :
6031 : 64 : test_diagnostic_context dc;
6032 : 64 : ASSERT_STREQ (" foo = bar (\n"
6033 : : " ~^\n"
6034 : : " );\n"
6035 : : " ~ \n",
6036 : : dc.test_show_locus (richloc));
6037 : 96 : }
6038 : :
6039 : : static void
6040 : 96 : test_tab_expansion (const line_table_case &case_)
6041 : : {
6042 : : /* Create a tempfile and write some text to it. This example uses a tabstop
6043 : : of 8, as the column numbers attempt to indicate:
6044 : :
6045 : : .....................000.01111111111.22222333333 display
6046 : : .....................123.90123456789.56789012345 columns */
6047 : 96 : const char *content = " \t This: `\t' is a tab.\n";
6048 : : /* ....................000 00000011111 11111222222 byte
6049 : : ....................123 45678901234 56789012345 columns */
6050 : :
6051 : 96 : const int tabstop = 8;
6052 : 96 : cpp_char_column_policy policy (tabstop, cpp_wcwidth);
6053 : 96 : const int first_non_ws_byte_col = 7;
6054 : 96 : const int right_quote_byte_col = 15;
6055 : 96 : const int last_byte_col = 25;
6056 : 96 : ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
6057 : :
6058 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
6059 : 96 : line_table_test ltt (case_);
6060 : 96 : linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
6061 : :
6062 : : /* Don't attempt to run the tests if column data might be unavailable. */
6063 : 96 : location_t line_end = linemap_position_for_column (line_table, last_byte_col);
6064 : 96 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
6065 : 32 : return;
6066 : :
6067 : : /* Check that the leading whitespace with mixed tabs and spaces is expanded
6068 : : into 11 spaces. Recall that print_line() also puts one space before
6069 : : everything too. */
6070 : 64 : {
6071 : 64 : test_diagnostic_context dc;
6072 : 64 : dc.m_tabstop = tabstop;
6073 : 64 : rich_location richloc (line_table,
6074 : : linemap_position_for_column (line_table,
6075 : 64 : first_non_ws_byte_col));
6076 : 64 : ASSERT_STREQ (" This: ` ' is a tab.\n"
6077 : : " ^\n",
6078 : : dc.test_show_locus (richloc));
6079 : 64 : }
6080 : :
6081 : : /* Confirm the display width was tracked correctly across the internal tab
6082 : : as well. */
6083 : 64 : {
6084 : 64 : test_diagnostic_context dc;
6085 : 64 : dc.m_tabstop = tabstop;
6086 : 64 : rich_location richloc (line_table,
6087 : : linemap_position_for_column (line_table,
6088 : 64 : right_quote_byte_col));
6089 : 64 : ASSERT_STREQ (" This: ` ' is a tab.\n"
6090 : : " ^\n",
6091 : : dc.test_show_locus (richloc));
6092 : 64 : }
6093 : 96 : }
6094 : :
6095 : : /* Verify that the escaping machinery can cope with a variety of different
6096 : : invalid bytes. */
6097 : :
6098 : : static void
6099 : 96 : test_escaping_bytes_1 (const line_table_case &case_)
6100 : : {
6101 : 96 : const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
6102 : 96 : const size_t sz = sizeof (content);
6103 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
6104 : 96 : line_table_test ltt (case_);
6105 : 96 : const line_map_ordinary *ord_map = linemap_check_ordinary
6106 : 96 : (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
6107 : 96 : linemap_line_start (line_table, 1, 100);
6108 : :
6109 : 96 : location_t finish
6110 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1,
6111 : : strlen (content));
6112 : :
6113 : 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
6114 : 32 : return;
6115 : :
6116 : : /* Locations of the NUL and \v bytes. */
6117 : 64 : location_t nul_loc
6118 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
6119 : 64 : location_t v_loc
6120 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
6121 : 64 : gcc_rich_location richloc (nul_loc);
6122 : 64 : richloc.add_range (v_loc);
6123 : :
6124 : 64 : {
6125 : 64 : test_diagnostic_context dc;
6126 : 64 : ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
6127 : : " ^ ~\n",
6128 : : dc.test_show_locus (richloc));
6129 : 64 : }
6130 : 64 : richloc.set_escape_on_output (true);
6131 : 64 : {
6132 : 64 : test_diagnostic_context dc;
6133 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
6134 : 64 : ASSERT_STREQ
6135 : : (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
6136 : : " ^~~~~~~~ ~~~~~~~~\n",
6137 : : dc.test_show_locus (richloc));
6138 : 64 : }
6139 : 64 : {
6140 : 64 : test_diagnostic_context dc;
6141 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
6142 : 64 : ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
6143 : : " ^~~~ ~~~~\n",
6144 : : dc.test_show_locus (richloc));
6145 : 64 : }
6146 : 96 : }
6147 : :
6148 : : /* As above, but verify that we handle the initial byte of a line
6149 : : correctly. */
6150 : :
6151 : : static void
6152 : 96 : test_escaping_bytes_2 (const line_table_case &case_)
6153 : : {
6154 : 96 : const char content[] = "\0after\n";
6155 : 96 : const size_t sz = sizeof (content);
6156 : 96 : temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
6157 : 96 : line_table_test ltt (case_);
6158 : 96 : const line_map_ordinary *ord_map = linemap_check_ordinary
6159 : 96 : (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
6160 : 96 : linemap_line_start (line_table, 1, 100);
6161 : :
6162 : 96 : location_t finish
6163 : 96 : = linemap_position_for_line_and_column (line_table, ord_map, 1,
6164 : : strlen (content));
6165 : :
6166 : 96 : if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
6167 : 32 : return;
6168 : :
6169 : : /* Location of the NUL byte. */
6170 : 64 : location_t nul_loc
6171 : 64 : = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
6172 : 64 : gcc_rich_location richloc (nul_loc);
6173 : :
6174 : 64 : {
6175 : 64 : test_diagnostic_context dc;
6176 : 64 : ASSERT_STREQ (" after\n"
6177 : : " ^\n",
6178 : : dc.test_show_locus (richloc));
6179 : 64 : }
6180 : 64 : richloc.set_escape_on_output (true);
6181 : 64 : {
6182 : 64 : test_diagnostic_context dc;
6183 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
6184 : 64 : ASSERT_STREQ (" <U+0000>after\n"
6185 : : " ^~~~~~~~\n",
6186 : : dc.test_show_locus (richloc));
6187 : 64 : }
6188 : 64 : {
6189 : 64 : test_diagnostic_context dc;
6190 : 64 : dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
6191 : 64 : ASSERT_STREQ (" <00>after\n"
6192 : : " ^~~~\n",
6193 : : dc.test_show_locus (richloc));
6194 : 64 : }
6195 : 96 : }
6196 : :
6197 : : /* Verify that line numbers are correctly printed for the case of
6198 : : a multiline range in which the width of the line numbers changes
6199 : : (e.g. from "9" to "10"). */
6200 : :
6201 : : static void
6202 : 4 : test_line_numbers_multiline_range ()
6203 : : {
6204 : : /* Create a tempfile and write some text to it. */
6205 : 4 : pretty_printer pp;
6206 : 84 : for (int i = 0; i < 20; i++)
6207 : : /* .........0000000001111111.
6208 : : .............1234567890123456. */
6209 : 80 : pp_printf (&pp, "this is line %i\n", i + 1);
6210 : 4 : temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
6211 : 4 : line_table_test ltt;
6212 : :
6213 : 4 : const line_map_ordinary *ord_map = linemap_check_ordinary
6214 : 4 : (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
6215 : 4 : linemap_line_start (line_table, 1, 100);
6216 : :
6217 : : /* Create a multi-line location, starting at the "line" of line 9, with
6218 : : a caret on the "is" of line 10, finishing on the "this" line 11. */
6219 : :
6220 : 4 : location_t start
6221 : 4 : = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
6222 : 4 : location_t caret
6223 : 4 : = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
6224 : 4 : location_t finish
6225 : 4 : = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
6226 : 4 : location_t loc = make_location (caret, start, finish);
6227 : :
6228 : 4 : test_diagnostic_context dc;
6229 : 4 : dc.m_source_printing.show_line_numbers_p = true;
6230 : 4 : dc.m_source_printing.min_margin_width = 0;
6231 : 4 : gcc_rich_location richloc (loc);
6232 : 4 : ASSERT_STREQ (" 9 | this is line 9\n"
6233 : : " | ~~~~~~\n"
6234 : : "10 | this is line 10\n"
6235 : : " | ~~~~~^~~~~~~~~~\n"
6236 : : "11 | this is line 11\n"
6237 : : " | ~~~~ \n",
6238 : : dc.test_show_locus (richloc));
6239 : 4 : }
6240 : :
6241 : : /* Run all of the selftests within this file. */
6242 : :
6243 : : void
6244 : 4 : diagnostic_show_locus_cc_tests ()
6245 : : {
6246 : 4 : test_line_span ();
6247 : :
6248 : 4 : test_layout_range_for_single_point ();
6249 : 4 : test_layout_range_for_single_line ();
6250 : 4 : test_layout_range_for_multiple_lines ();
6251 : :
6252 : 4 : test_display_widths ();
6253 : :
6254 : 4 : for_each_line_table_case (test_layout_x_offset_display_utf8);
6255 : 4 : for_each_line_table_case (test_layout_x_offset_display_tab);
6256 : :
6257 : 4 : test_get_line_bytes_without_trailing_whitespace ();
6258 : :
6259 : 4 : test_diagnostic_show_locus_unknown_location ();
6260 : :
6261 : 4 : for_each_line_table_case (test_diagnostic_show_locus_one_liner);
6262 : 4 : for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
6263 : 4 : for_each_line_table_case (test_add_location_if_nearby);
6264 : 4 : for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
6265 : 4 : for_each_line_table_case (test_fixit_consolidation);
6266 : 4 : for_each_line_table_case (test_overlapped_fixit_printing);
6267 : 4 : for_each_line_table_case (test_overlapped_fixit_printing_utf8);
6268 : 4 : for_each_line_table_case (test_overlapped_fixit_printing_2);
6269 : 4 : for_each_line_table_case (test_fixit_insert_containing_newline);
6270 : 4 : for_each_line_table_case (test_fixit_insert_containing_newline_2);
6271 : 4 : for_each_line_table_case (test_fixit_replace_containing_newline);
6272 : 4 : for_each_line_table_case (test_fixit_deletion_affecting_newline);
6273 : 4 : for_each_line_table_case (test_tab_expansion);
6274 : 4 : for_each_line_table_case (test_escaping_bytes_1);
6275 : 4 : for_each_line_table_case (test_escaping_bytes_2);
6276 : :
6277 : 4 : test_line_numbers_multiline_range ();
6278 : 4 : }
6279 : :
6280 : : } // namespace selftest
6281 : :
6282 : : #endif /* #if CHECKING_P */
6283 : :
6284 : : #if __GNUC__ >= 10
6285 : : # pragma GCC diagnostic pop
6286 : : #endif
|