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