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