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