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