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