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