diagnostic_show_locus: move initial newline to callers
[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 location_t loc = richloc->get_loc ();
2534 /* Do nothing if source-printing has been disabled. */
2535 if (!context->show_caret)
2536 return;
2537
2538 /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins. */
2539 if (loc <= BUILTINS_LOCATION)
2540 return;
2541
2542 /* Don't print the same source location twice in a row, unless we have
2543 fix-it hints. */
2544 if (loc == context->last_location
2545 && richloc->get_num_fixit_hints () == 0)
2546 return;
2547
2548 context->last_location = loc;
2549
2550 layout layout (context, richloc, diagnostic_kind);
2551 for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2552 line_span_idx++)
2553 {
2554 const line_span *line_span = layout.get_line_span (line_span_idx);
2555 if (context->show_line_numbers_p)
2556 {
2557 /* With line numbers, we should show whenever the line-numbering
2558 "jumps". */
2559 if (line_span_idx > 0)
2560 layout.print_gap_in_line_numbering ();
2561 }
2562 else
2563 {
2564 /* Without line numbers, we print headings for some line spans. */
2565 if (layout.print_heading_for_line_span_index_p (line_span_idx))
2566 {
2567 expanded_location exploc
2568 = layout.get_expanded_location (line_span);
2569 context->start_span (context, exploc);
2570 }
2571 }
2572 /* Iterate over the lines within this span (using linenum_arith_t to
2573 avoid overflow with 0xffffffff causing an infinite loop). */
2574 linenum_arith_t last_line = line_span->get_last_line ();
2575 for (linenum_arith_t row = line_span->get_first_line ();
2576 row <= last_line; row++)
2577 layout.print_line (row);
2578 }
2579 }
2580
2581 #if CHECKING_P
2582
2583 namespace selftest {
2584
2585 /* Selftests for diagnostic_show_locus. */
2586
2587 /* For precise tests of the layout, make clear where the source line will
2588 start. test_left_margin sets the total byte count from the left side of the
2589 screen to the start of source lines, after the line number and the separator,
2590 which consists of the three characters " | ". */
2591 static const int test_linenum_sep = 3;
2592 static const int test_left_margin = 7;
2593
2594 /* Helper function for test_layout_x_offset_display_utf8(). */
2595 static void
2596 test_offset_impl (int caret_byte_col, int max_width,
2597 int expected_x_offset_display,
2598 int left_margin = test_left_margin)
2599 {
2600 test_diagnostic_context dc;
2601 dc.caret_max_width = max_width;
2602 /* diagnostic_context::min_margin_width sets the minimum space reserved for
2603 the line number plus one space after. */
2604 dc.min_margin_width = left_margin - test_linenum_sep + 1;
2605 dc.show_line_numbers_p = true;
2606 rich_location richloc (line_table,
2607 linemap_position_for_column (line_table,
2608 caret_byte_col));
2609 layout test_layout (&dc, &richloc, DK_ERROR);
2610 ASSERT_EQ (left_margin - test_linenum_sep,
2611 test_layout.get_linenum_width ());
2612 ASSERT_EQ (expected_x_offset_display,
2613 test_layout.get_x_offset_display ());
2614 }
2615
2616 /* Test that layout::calculate_x_offset_display() works. */
2617 static void
2618 test_layout_x_offset_display_utf8 (const line_table_case &case_)
2619 {
2620
2621 const char *content
2622 = "This line is very long, so that we can use it to test the logic for "
2623 "clipping long lines. Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
2624 "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
2625 "column #102.\n";
2626
2627 /* Number of bytes in the line, subtracting one to remove the newline. */
2628 const int line_bytes = strlen (content) - 1;
2629
2630 /* Number of display columns occupied by the line; each of the 2 emojis
2631 takes up 2 fewer display columns than it does bytes. */
2632 const int line_display_cols = line_bytes - 2*2;
2633
2634 /* The column of the first emoji. Byte or display is the same as there are
2635 no multibyte characters earlier on the line. */
2636 const int emoji_col = 102;
2637
2638 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2639 line_table_test ltt (case_);
2640
2641 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2642
2643 location_t line_end = linemap_position_for_column (line_table, line_bytes);
2644
2645 /* Don't attempt to run the tests if column data might be unavailable. */
2646 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2647 return;
2648
2649 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2650 ASSERT_EQ (1, LOCATION_LINE (line_end));
2651 ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
2652
2653 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
2654 ASSERT_EQ (line_display_cols,
2655 cpp_display_width (lspan.get_buffer (), lspan.length ()));
2656 ASSERT_EQ (line_display_cols,
2657 location_compute_display_column (expand_location (line_end)));
2658 ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
2659 "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
2660
2661 /* (caret_byte, max_width, expected_x_offset_display, [left_margin]) */
2662
2663 /* No constraint on the width -> no offset. */
2664 test_offset_impl (emoji_col, 0, 0);
2665
2666 /* Caret is before the beginning -> no offset. */
2667 test_offset_impl (0, 100, 0);
2668
2669 /* Caret is past the end of the line -> no offset. */
2670 test_offset_impl (line_bytes+1, 100, 0);
2671
2672 /* Line fits in the display -> no offset. */
2673 test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
2674 test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
2675
2676 /* Line is too long for the display but caret location is OK
2677 anyway -> no offset. */
2678 static const int small_width = 24;
2679 test_offset_impl (1, small_width, 0);
2680
2681 /* Width constraint is very small -> no offset. */
2682 test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
2683
2684 /* Line would be offset, but due to large line numbers, offsetting
2685 would remove the whole line -> no offset. */
2686 static const int huge_left_margin = 100;
2687 test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
2688
2689 /* Line is the same length as the display, but the line number makes it too
2690 long, so offset is required. Caret is at the end so padding on the right
2691 is not in effect. */
2692 for (int excess = 1; excess <= 3; ++excess)
2693 test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
2694 excess);
2695
2696 /* Line is much too long for the display, caret is near the end ->
2697 offset should be such that the line fits in the display and caret
2698 remains the same distance from the end that it was. */
2699 for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
2700 caret_offset <= max_offset; ++caret_offset)
2701 test_offset_impl (line_bytes - caret_offset, small_width,
2702 line_display_cols + test_left_margin - small_width);
2703
2704 /* As previous case but caret is closer to the middle; now we want it to end
2705 up CARET_LINE_MARGIN bytes from the end. */
2706 ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
2707 test_offset_impl (emoji_col, small_width,
2708 emoji_col + test_left_margin
2709 - (small_width - CARET_LINE_MARGIN));
2710
2711 /* Test that the source line is offset as expected when printed. */
2712 {
2713 test_diagnostic_context dc;
2714 dc.caret_max_width = small_width - 6;
2715 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2716 dc.show_line_numbers_p = true;
2717 dc.show_ruler_p = true;
2718 rich_location richloc (line_table,
2719 linemap_position_for_column (line_table,
2720 emoji_col));
2721 layout test_layout (&dc, &richloc, DK_ERROR);
2722 test_layout.print_line (1);
2723 ASSERT_STREQ (" | 1 \n"
2724 " | 1 \n"
2725 " | 234567890123456789\n"
2726 " 1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
2727 "that occupies 8 bytes and 4 display columns, starting at "
2728 "column #102.\n"
2729 " | ^\n\n",
2730 pp_formatted_text (dc.printer));
2731 }
2732
2733 /* Similar to the previous example, but now the offset called for would split
2734 the first emoji in the middle of the UTF-8 sequence. Check that we replace
2735 it with a padding space in this case. */
2736 {
2737 test_diagnostic_context dc;
2738 dc.caret_max_width = small_width - 5;
2739 dc.min_margin_width = test_left_margin - test_linenum_sep + 1;
2740 dc.show_line_numbers_p = true;
2741 dc.show_ruler_p = true;
2742 rich_location richloc (line_table,
2743 linemap_position_for_column (line_table,
2744 emoji_col + 2));
2745 layout test_layout (&dc, &richloc, DK_ERROR);
2746 test_layout.print_line (1);
2747 ASSERT_STREQ (" | 1 1 \n"
2748 " | 1 2 \n"
2749 " | 3456789012345678901\n"
2750 " 1 | \xf0\x9f\x98\x82 is a pair of emojis "
2751 "that occupies 8 bytes and 4 display columns, starting at "
2752 "column #102.\n"
2753 " | ^\n\n",
2754 pp_formatted_text (dc.printer));
2755 }
2756
2757 }
2758
2759 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION. */
2760
2761 static void
2762 test_diagnostic_show_locus_unknown_location ()
2763 {
2764 test_diagnostic_context dc;
2765 rich_location richloc (line_table, UNKNOWN_LOCATION);
2766 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2767 ASSERT_STREQ ("", pp_formatted_text (dc.printer));
2768 }
2769
2770 /* Verify that diagnostic_show_locus works sanely for various
2771 single-line cases.
2772
2773 All of these work on the following 1-line source file:
2774 .0000000001111111
2775 .1234567890123456
2776 "foo = bar.field;\n"
2777 which is set up by test_diagnostic_show_locus_one_liner and calls
2778 them. */
2779
2780 /* Just a caret. */
2781
2782 static void
2783 test_one_liner_simple_caret ()
2784 {
2785 test_diagnostic_context dc;
2786 location_t caret = linemap_position_for_column (line_table, 10);
2787 rich_location richloc (line_table, caret);
2788 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2789 ASSERT_STREQ (" foo = bar.field;\n"
2790 " ^\n",
2791 pp_formatted_text (dc.printer));
2792 }
2793
2794 /* Caret and range. */
2795
2796 static void
2797 test_one_liner_caret_and_range ()
2798 {
2799 test_diagnostic_context dc;
2800 location_t caret = linemap_position_for_column (line_table, 10);
2801 location_t start = linemap_position_for_column (line_table, 7);
2802 location_t finish = linemap_position_for_column (line_table, 15);
2803 location_t loc = make_location (caret, start, finish);
2804 rich_location richloc (line_table, loc);
2805 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2806 ASSERT_STREQ (" foo = bar.field;\n"
2807 " ~~~^~~~~~\n",
2808 pp_formatted_text (dc.printer));
2809 }
2810
2811 /* Multiple ranges and carets. */
2812
2813 static void
2814 test_one_liner_multiple_carets_and_ranges ()
2815 {
2816 test_diagnostic_context dc;
2817 location_t foo
2818 = make_location (linemap_position_for_column (line_table, 2),
2819 linemap_position_for_column (line_table, 1),
2820 linemap_position_for_column (line_table, 3));
2821 dc.caret_chars[0] = 'A';
2822
2823 location_t bar
2824 = make_location (linemap_position_for_column (line_table, 8),
2825 linemap_position_for_column (line_table, 7),
2826 linemap_position_for_column (line_table, 9));
2827 dc.caret_chars[1] = 'B';
2828
2829 location_t field
2830 = make_location (linemap_position_for_column (line_table, 13),
2831 linemap_position_for_column (line_table, 11),
2832 linemap_position_for_column (line_table, 15));
2833 dc.caret_chars[2] = 'C';
2834
2835 rich_location richloc (line_table, foo);
2836 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2837 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2838 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2839 ASSERT_STREQ (" foo = bar.field;\n"
2840 " ~A~ ~B~ ~~C~~\n",
2841 pp_formatted_text (dc.printer));
2842 }
2843
2844 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2845
2846 static void
2847 test_one_liner_fixit_insert_before ()
2848 {
2849 test_diagnostic_context dc;
2850 location_t caret = linemap_position_for_column (line_table, 7);
2851 rich_location richloc (line_table, caret);
2852 richloc.add_fixit_insert_before ("&");
2853 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2854 ASSERT_STREQ (" foo = bar.field;\n"
2855 " ^\n"
2856 " &\n",
2857 pp_formatted_text (dc.printer));
2858 }
2859
2860 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2861
2862 static void
2863 test_one_liner_fixit_insert_after ()
2864 {
2865 test_diagnostic_context dc;
2866 location_t start = linemap_position_for_column (line_table, 1);
2867 location_t finish = linemap_position_for_column (line_table, 3);
2868 location_t foo = make_location (start, start, finish);
2869 rich_location richloc (line_table, foo);
2870 richloc.add_fixit_insert_after ("[0]");
2871 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2872 ASSERT_STREQ (" foo = bar.field;\n"
2873 " ^~~\n"
2874 " [0]\n",
2875 pp_formatted_text (dc.printer));
2876 }
2877
2878 /* Removal fix-it hint: removal of the ".field".
2879 Also verify the interaction of pp_set_prefix with rulers and
2880 fix-it hints. */
2881
2882 static void
2883 test_one_liner_fixit_remove ()
2884 {
2885 location_t start = linemap_position_for_column (line_table, 10);
2886 location_t finish = linemap_position_for_column (line_table, 15);
2887 location_t dot = make_location (start, start, finish);
2888 rich_location richloc (line_table, dot);
2889 richloc.add_fixit_remove ();
2890
2891 /* Normal. */
2892 {
2893 test_diagnostic_context dc;
2894 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2895 ASSERT_STREQ (" foo = bar.field;\n"
2896 " ^~~~~~\n"
2897 " ------\n",
2898 pp_formatted_text (dc.printer));
2899 }
2900
2901 /* Test of adding a prefix. */
2902 {
2903 test_diagnostic_context dc;
2904 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2905 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2906 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2907 ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
2908 "TEST PREFIX: ^~~~~~\n"
2909 "TEST PREFIX: ------\n",
2910 pp_formatted_text (dc.printer));
2911 }
2912
2913 /* Normal, with ruler. */
2914 {
2915 test_diagnostic_context dc;
2916 dc.show_ruler_p = true;
2917 dc.caret_max_width = 104;
2918 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2919 ASSERT_STREQ (" 0 0 0 0 0 0 0 0 0 1 \n"
2920 " 1 2 3 4 5 6 7 8 9 0 \n"
2921 " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
2922 " foo = bar.field;\n"
2923 " ^~~~~~\n"
2924 " ------\n",
2925 pp_formatted_text (dc.printer));
2926 }
2927
2928 /* Test of adding a prefix, with ruler. */
2929 {
2930 test_diagnostic_context dc;
2931 dc.show_ruler_p = true;
2932 dc.caret_max_width = 50;
2933 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2934 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2935 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2936 ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
2937 "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
2938 "TEST PREFIX: foo = bar.field;\n"
2939 "TEST PREFIX: ^~~~~~\n"
2940 "TEST PREFIX: ------\n",
2941 pp_formatted_text (dc.printer));
2942 }
2943
2944 /* Test of adding a prefix, with ruler and line numbers. */
2945 {
2946 test_diagnostic_context dc;
2947 dc.show_ruler_p = true;
2948 dc.caret_max_width = 50;
2949 dc.show_line_numbers_p = true;
2950 pp_prefixing_rule (dc.printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
2951 pp_set_prefix (dc.printer, xstrdup ("TEST PREFIX:"));
2952 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2953 ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
2954 "TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
2955 "TEST PREFIX: 1 | foo = bar.field;\n"
2956 "TEST PREFIX: | ^~~~~~\n"
2957 "TEST PREFIX: | ------\n",
2958 pp_formatted_text (dc.printer));
2959 }
2960 }
2961
2962 /* Replace fix-it hint: replacing "field" with "m_field". */
2963
2964 static void
2965 test_one_liner_fixit_replace ()
2966 {
2967 test_diagnostic_context dc;
2968 location_t start = linemap_position_for_column (line_table, 11);
2969 location_t finish = linemap_position_for_column (line_table, 15);
2970 location_t field = make_location (start, start, finish);
2971 rich_location richloc (line_table, field);
2972 richloc.add_fixit_replace ("m_field");
2973 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2974 ASSERT_STREQ (" foo = bar.field;\n"
2975 " ^~~~~\n"
2976 " m_field\n",
2977 pp_formatted_text (dc.printer));
2978 }
2979
2980 /* Replace fix-it hint: replacing "field" with "m_field",
2981 but where the caret was elsewhere. */
2982
2983 static void
2984 test_one_liner_fixit_replace_non_equal_range ()
2985 {
2986 test_diagnostic_context dc;
2987 location_t equals = linemap_position_for_column (line_table, 5);
2988 location_t start = linemap_position_for_column (line_table, 11);
2989 location_t finish = linemap_position_for_column (line_table, 15);
2990 rich_location richloc (line_table, equals);
2991 source_range range;
2992 range.m_start = start;
2993 range.m_finish = finish;
2994 richloc.add_fixit_replace (range, "m_field");
2995 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2996 /* The replacement range is not indicated in the annotation line, so
2997 it should be indicated via an additional underline. */
2998 ASSERT_STREQ (" foo = bar.field;\n"
2999 " ^\n"
3000 " -----\n"
3001 " m_field\n",
3002 pp_formatted_text (dc.printer));
3003 }
3004
3005 /* Replace fix-it hint: replacing "field" with "m_field",
3006 where the caret was elsewhere, but where a secondary range
3007 exactly covers "field". */
3008
3009 static void
3010 test_one_liner_fixit_replace_equal_secondary_range ()
3011 {
3012 test_diagnostic_context dc;
3013 location_t equals = linemap_position_for_column (line_table, 5);
3014 location_t start = linemap_position_for_column (line_table, 11);
3015 location_t finish = linemap_position_for_column (line_table, 15);
3016 rich_location richloc (line_table, equals);
3017 location_t field = make_location (start, start, finish);
3018 richloc.add_range (field);
3019 richloc.add_fixit_replace (field, "m_field");
3020 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3021 /* The replacement range is indicated in the annotation line,
3022 so it shouldn't be indicated via an additional underline. */
3023 ASSERT_STREQ (" foo = bar.field;\n"
3024 " ^ ~~~~~\n"
3025 " m_field\n",
3026 pp_formatted_text (dc.printer));
3027 }
3028
3029 /* Verify that we can use ad-hoc locations when adding fixits to a
3030 rich_location. */
3031
3032 static void
3033 test_one_liner_fixit_validation_adhoc_locations ()
3034 {
3035 /* Generate a range that's too long to be packed, so must
3036 be stored as an ad-hoc location (given the defaults
3037 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3038 const location_t c7 = linemap_position_for_column (line_table, 7);
3039 const location_t c47 = linemap_position_for_column (line_table, 47);
3040 const location_t loc = make_location (c7, c7, c47);
3041
3042 if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3043 return;
3044
3045 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3046
3047 /* Insert. */
3048 {
3049 rich_location richloc (line_table, loc);
3050 richloc.add_fixit_insert_before (loc, "test");
3051 /* It should not have been discarded by the validator. */
3052 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3053
3054 test_diagnostic_context dc;
3055 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3056 ASSERT_STREQ (" foo = bar.field;\n"
3057 " ^~~~~~~~~~ \n"
3058 " test\n",
3059 pp_formatted_text (dc.printer));
3060 }
3061
3062 /* Remove. */
3063 {
3064 rich_location richloc (line_table, loc);
3065 source_range range = source_range::from_locations (loc, c47);
3066 richloc.add_fixit_remove (range);
3067 /* It should not have been discarded by the validator. */
3068 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3069
3070 test_diagnostic_context dc;
3071 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3072 ASSERT_STREQ (" foo = bar.field;\n"
3073 " ^~~~~~~~~~ \n"
3074 " -----------------------------------------\n",
3075 pp_formatted_text (dc.printer));
3076 }
3077
3078 /* Replace. */
3079 {
3080 rich_location richloc (line_table, loc);
3081 source_range range = source_range::from_locations (loc, c47);
3082 richloc.add_fixit_replace (range, "test");
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 (" foo = bar.field;\n"
3089 " ^~~~~~~~~~ \n"
3090 " test\n",
3091 pp_formatted_text (dc.printer));
3092 }
3093 }
3094
3095 /* Test of consolidating insertions at the same location. */
3096
3097 static void
3098 test_one_liner_many_fixits_1 ()
3099 {
3100 test_diagnostic_context dc;
3101 location_t equals = linemap_position_for_column (line_table, 5);
3102 rich_location richloc (line_table, equals);
3103 for (int i = 0; i < 19; i++)
3104 richloc.add_fixit_insert_before ("a");
3105 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3106 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3107 ASSERT_STREQ (" foo = bar.field;\n"
3108 " ^\n"
3109 " aaaaaaaaaaaaaaaaaaa\n",
3110 pp_formatted_text (dc.printer));
3111 }
3112
3113 /* Ensure that we can add an arbitrary number of fix-it hints to a
3114 rich_location, even if they are not consolidated. */
3115
3116 static void
3117 test_one_liner_many_fixits_2 ()
3118 {
3119 test_diagnostic_context dc;
3120 location_t equals = linemap_position_for_column (line_table, 5);
3121 rich_location richloc (line_table, equals);
3122 for (int i = 0; i < 19; i++)
3123 {
3124 location_t loc = linemap_position_for_column (line_table, i * 2);
3125 richloc.add_fixit_insert_before (loc, "a");
3126 }
3127 ASSERT_EQ (19, richloc.get_num_fixit_hints ());
3128 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3129 ASSERT_STREQ (" foo = bar.field;\n"
3130 " ^\n"
3131 "a a a a a a a a a a a a a a a a a a a\n",
3132 pp_formatted_text (dc.printer));
3133 }
3134
3135 /* Test of labeling the ranges within a rich_location. */
3136
3137 static void
3138 test_one_liner_labels ()
3139 {
3140 location_t foo
3141 = make_location (linemap_position_for_column (line_table, 1),
3142 linemap_position_for_column (line_table, 1),
3143 linemap_position_for_column (line_table, 3));
3144 location_t bar
3145 = make_location (linemap_position_for_column (line_table, 7),
3146 linemap_position_for_column (line_table, 7),
3147 linemap_position_for_column (line_table, 9));
3148 location_t field
3149 = make_location (linemap_position_for_column (line_table, 11),
3150 linemap_position_for_column (line_table, 11),
3151 linemap_position_for_column (line_table, 15));
3152
3153 /* Example where all the labels fit on one line. */
3154 {
3155 text_range_label label0 ("0");
3156 text_range_label label1 ("1");
3157 text_range_label label2 ("2");
3158 gcc_rich_location richloc (foo, &label0);
3159 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3160 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3161
3162 {
3163 test_diagnostic_context dc;
3164 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3165 ASSERT_STREQ (" foo = bar.field;\n"
3166 " ^~~ ~~~ ~~~~~\n"
3167 " | | |\n"
3168 " 0 1 2\n",
3169 pp_formatted_text (dc.printer));
3170 }
3171
3172 /* Verify that we can disable label-printing. */
3173 {
3174 test_diagnostic_context dc;
3175 dc.show_labels_p = false;
3176 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3177 ASSERT_STREQ (" foo = bar.field;\n"
3178 " ^~~ ~~~ ~~~~~\n",
3179 pp_formatted_text (dc.printer));
3180 }
3181 }
3182
3183 /* Example where the labels need extra lines. */
3184 {
3185 text_range_label label0 ("label 0");
3186 text_range_label label1 ("label 1");
3187 text_range_label label2 ("label 2");
3188 gcc_rich_location richloc (foo, &label0);
3189 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3190 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3191
3192 test_diagnostic_context dc;
3193 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3194 ASSERT_STREQ (" foo = bar.field;\n"
3195 " ^~~ ~~~ ~~~~~\n"
3196 " | | |\n"
3197 " | | label 2\n"
3198 " | label 1\n"
3199 " label 0\n",
3200 pp_formatted_text (dc.printer));
3201 }
3202
3203 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3204 but label 1 just touches label 2. */
3205 {
3206 text_range_label label0 ("aaaaa");
3207 text_range_label label1 ("bbbb");
3208 text_range_label label2 ("c");
3209 gcc_rich_location richloc (foo, &label0);
3210 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3211 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3212
3213 test_diagnostic_context dc;
3214 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3215 ASSERT_STREQ (" foo = bar.field;\n"
3216 " ^~~ ~~~ ~~~~~\n"
3217 " | | |\n"
3218 " | | c\n"
3219 " aaaaa bbbb\n",
3220 pp_formatted_text (dc.printer));
3221 }
3222
3223 /* Example of out-of-order ranges (thus requiring a sort). */
3224 {
3225 text_range_label label0 ("0");
3226 text_range_label label1 ("1");
3227 text_range_label label2 ("2");
3228 gcc_rich_location richloc (field, &label0);
3229 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3230 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
3231
3232 test_diagnostic_context dc;
3233 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3234 ASSERT_STREQ (" foo = bar.field;\n"
3235 " ~~~ ~~~ ^~~~~\n"
3236 " | | |\n"
3237 " 2 1 0\n",
3238 pp_formatted_text (dc.printer));
3239 }
3240
3241 /* Ensure we don't ICE if multiple ranges with labels are on
3242 the same point. */
3243 {
3244 text_range_label label0 ("label 0");
3245 text_range_label label1 ("label 1");
3246 text_range_label label2 ("label 2");
3247 gcc_rich_location richloc (bar, &label0);
3248 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3249 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
3250
3251 test_diagnostic_context dc;
3252 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3253 ASSERT_STREQ (" foo = bar.field;\n"
3254 " ^~~\n"
3255 " |\n"
3256 " label 0\n"
3257 " label 1\n"
3258 " label 2\n",
3259 pp_formatted_text (dc.printer));
3260 }
3261
3262 /* Example of out-of-order ranges (thus requiring a sort), where
3263 they overlap, and there are multiple ranges on the same point. */
3264 {
3265 text_range_label label_0a ("label 0a");
3266 text_range_label label_1a ("label 1a");
3267 text_range_label label_2a ("label 2a");
3268 text_range_label label_0b ("label 0b");
3269 text_range_label label_1b ("label 1b");
3270 text_range_label label_2b ("label 2b");
3271 text_range_label label_0c ("label 0c");
3272 text_range_label label_1c ("label 1c");
3273 text_range_label label_2c ("label 2c");
3274 gcc_rich_location richloc (field, &label_0a);
3275 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
3276 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
3277
3278 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
3279 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
3280 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
3281
3282 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
3283 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
3284 richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
3285
3286 test_diagnostic_context dc;
3287 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3288 ASSERT_STREQ (" foo = bar.field;\n"
3289 " ~~~ ~~~ ^~~~~\n"
3290 " | | |\n"
3291 " | | label 0a\n"
3292 " | | label 0b\n"
3293 " | | label 0c\n"
3294 " | label 1a\n"
3295 " | label 1b\n"
3296 " | label 1c\n"
3297 " label 2a\n"
3298 " label 2b\n"
3299 " label 2c\n",
3300 pp_formatted_text (dc.printer));
3301 }
3302
3303 /* Verify that a NULL result from range_label::get_text is
3304 handled gracefully. */
3305 {
3306 text_range_label label (NULL);
3307 gcc_rich_location richloc (bar, &label);
3308
3309 test_diagnostic_context dc;
3310 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3311 ASSERT_STREQ (" foo = bar.field;\n"
3312 " ^~~\n",
3313 pp_formatted_text (dc.printer));
3314 }
3315
3316 /* TODO: example of formatted printing (needs to be in
3317 gcc-rich-location.c due to Makefile.in issues). */
3318 }
3319
3320 /* Run the various one-liner tests. */
3321
3322 static void
3323 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
3324 {
3325 /* Create a tempfile and write some text to it.
3326 ....................0000000001111111.
3327 ....................1234567890123456. */
3328 const char *content = "foo = bar.field;\n";
3329 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3330 line_table_test ltt (case_);
3331
3332 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3333
3334 location_t line_end = linemap_position_for_column (line_table, 16);
3335
3336 /* Don't attempt to run the tests if column data might be unavailable. */
3337 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3338 return;
3339
3340 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3341 ASSERT_EQ (1, LOCATION_LINE (line_end));
3342 ASSERT_EQ (16, LOCATION_COLUMN (line_end));
3343
3344 test_one_liner_simple_caret ();
3345 test_one_liner_caret_and_range ();
3346 test_one_liner_multiple_carets_and_ranges ();
3347 test_one_liner_fixit_insert_before ();
3348 test_one_liner_fixit_insert_after ();
3349 test_one_liner_fixit_remove ();
3350 test_one_liner_fixit_replace ();
3351 test_one_liner_fixit_replace_non_equal_range ();
3352 test_one_liner_fixit_replace_equal_secondary_range ();
3353 test_one_liner_fixit_validation_adhoc_locations ();
3354 test_one_liner_many_fixits_1 ();
3355 test_one_liner_many_fixits_2 ();
3356 test_one_liner_labels ();
3357 }
3358
3359 /* Version of all one-liner tests exercising multibyte awareness. For
3360 simplicity we stick to using two multibyte characters in the test, U+1F602
3361 == "\xf0\x9f\x98\x82", which uses 4 bytes and 2 display columns, and U+03C0
3362 == "\xcf\x80", which uses 2 bytes and 1 display column. Note: all of the
3363 below asserts would be easier to read if we used UTF-8 directly in the
3364 string constants, but it seems better not to demand the host compiler
3365 support this, when it isn't otherwise necessary. Instead, whenever an
3366 extended character appears in a string, we put a line break after it so that
3367 all succeeding characters can appear visually at the correct display column.
3368
3369 All of these work on the following 1-line source file:
3370
3371 .0000000001111111111222222 display
3372 .1234567890123456789012345 columns
3373 "SS_foo = P_bar.SS_fieldP;\n"
3374 .0000000111111111222222223 byte
3375 .1356789012456789134567891 columns
3376
3377 which is set up by test_diagnostic_show_locus_one_liner and calls
3378 them. Here SS represents the two display columns for the U+1F602 emoji and
3379 P represents the one display column for the U+03C0 pi symbol. */
3380
3381 /* Just a caret. */
3382
3383 static void
3384 test_one_liner_simple_caret_utf8 ()
3385 {
3386 test_diagnostic_context dc;
3387 location_t caret = linemap_position_for_column (line_table, 18);
3388 rich_location richloc (line_table, caret);
3389 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3390 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3391 "_foo = \xcf\x80"
3392 "_bar.\xf0\x9f\x98\x82"
3393 "_field\xcf\x80"
3394 ";\n"
3395 " ^\n",
3396 pp_formatted_text (dc.printer));
3397 }
3398
3399 /* Caret and range. */
3400 static void
3401 test_one_liner_caret_and_range_utf8 ()
3402 {
3403 test_diagnostic_context dc;
3404 location_t caret = linemap_position_for_column (line_table, 18);
3405 location_t start = linemap_position_for_column (line_table, 12);
3406 location_t finish = linemap_position_for_column (line_table, 30);
3407 location_t loc = make_location (caret, start, finish);
3408 rich_location richloc (line_table, loc);
3409 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3410 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3411 "_foo = \xcf\x80"
3412 "_bar.\xf0\x9f\x98\x82"
3413 "_field\xcf\x80"
3414 ";\n"
3415 " ~~~~~^~~~~~~~~~\n",
3416 pp_formatted_text (dc.printer));
3417 }
3418
3419 /* Multiple ranges and carets. */
3420
3421 static void
3422 test_one_liner_multiple_carets_and_ranges_utf8 ()
3423 {
3424 test_diagnostic_context dc;
3425 location_t foo
3426 = make_location (linemap_position_for_column (line_table, 7),
3427 linemap_position_for_column (line_table, 1),
3428 linemap_position_for_column (line_table, 8));
3429 dc.caret_chars[0] = 'A';
3430
3431 location_t bar
3432 = make_location (linemap_position_for_column (line_table, 16),
3433 linemap_position_for_column (line_table, 12),
3434 linemap_position_for_column (line_table, 17));
3435 dc.caret_chars[1] = 'B';
3436
3437 location_t field
3438 = make_location (linemap_position_for_column (line_table, 26),
3439 linemap_position_for_column (line_table, 19),
3440 linemap_position_for_column (line_table, 30));
3441 dc.caret_chars[2] = 'C';
3442 rich_location richloc (line_table, foo);
3443 richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
3444 richloc.add_range (field, SHOW_RANGE_WITH_CARET);
3445 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3446 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3447 "_foo = \xcf\x80"
3448 "_bar.\xf0\x9f\x98\x82"
3449 "_field\xcf\x80"
3450 ";\n"
3451 " ~~~~A~ ~~~B~ ~~~~~C~~~\n",
3452 pp_formatted_text (dc.printer));
3453 }
3454
3455 /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
3456
3457 static void
3458 test_one_liner_fixit_insert_before_utf8 ()
3459 {
3460 test_diagnostic_context dc;
3461 location_t caret = linemap_position_for_column (line_table, 12);
3462 rich_location richloc (line_table, caret);
3463 richloc.add_fixit_insert_before ("&");
3464 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3465 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3466 "_foo = \xcf\x80"
3467 "_bar.\xf0\x9f\x98\x82"
3468 "_field\xcf\x80"
3469 ";\n"
3470 " ^\n"
3471 " &\n",
3472 pp_formatted_text (dc.printer));
3473 }
3474
3475 /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
3476
3477 static void
3478 test_one_liner_fixit_insert_after_utf8 ()
3479 {
3480 test_diagnostic_context dc;
3481 location_t start = linemap_position_for_column (line_table, 1);
3482 location_t finish = linemap_position_for_column (line_table, 8);
3483 location_t foo = make_location (start, start, finish);
3484 rich_location richloc (line_table, foo);
3485 richloc.add_fixit_insert_after ("[0]");
3486 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3487 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3488 "_foo = \xcf\x80"
3489 "_bar.\xf0\x9f\x98\x82"
3490 "_field\xcf\x80"
3491 ";\n"
3492 " ^~~~~~\n"
3493 " [0]\n",
3494 pp_formatted_text (dc.printer));
3495 }
3496
3497 /* Removal fix-it hint: removal of the ".SS_fieldP". */
3498
3499 static void
3500 test_one_liner_fixit_remove_utf8 ()
3501 {
3502 test_diagnostic_context dc;
3503 location_t start = linemap_position_for_column (line_table, 18);
3504 location_t finish = linemap_position_for_column (line_table, 30);
3505 location_t dot = make_location (start, start, finish);
3506 rich_location richloc (line_table, dot);
3507 richloc.add_fixit_remove ();
3508 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3509 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3510 "_foo = \xcf\x80"
3511 "_bar.\xf0\x9f\x98\x82"
3512 "_field\xcf\x80"
3513 ";\n"
3514 " ^~~~~~~~~~\n"
3515 " ----------\n",
3516 pp_formatted_text (dc.printer));
3517 }
3518
3519 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
3520
3521 static void
3522 test_one_liner_fixit_replace_utf8 ()
3523 {
3524 test_diagnostic_context dc;
3525 location_t start = linemap_position_for_column (line_table, 19);
3526 location_t finish = linemap_position_for_column (line_table, 30);
3527 location_t field = make_location (start, start, finish);
3528 rich_location richloc (line_table, field);
3529 richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
3530 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3531 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3532 "_foo = \xcf\x80"
3533 "_bar.\xf0\x9f\x98\x82"
3534 "_field\xcf\x80"
3535 ";\n"
3536 " ^~~~~~~~~\n"
3537 " m_\xf0\x9f\x98\x82"
3538 "_field\xcf\x80\n",
3539 pp_formatted_text (dc.printer));
3540 }
3541
3542 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3543 but where the caret was elsewhere. */
3544
3545 static void
3546 test_one_liner_fixit_replace_non_equal_range_utf8 ()
3547 {
3548 test_diagnostic_context dc;
3549 location_t equals = linemap_position_for_column (line_table, 10);
3550 location_t start = linemap_position_for_column (line_table, 19);
3551 location_t finish = linemap_position_for_column (line_table, 30);
3552 rich_location richloc (line_table, equals);
3553 source_range range;
3554 range.m_start = start;
3555 range.m_finish = finish;
3556 richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3557 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3558 /* The replacement range is not indicated in the annotation line, so
3559 it should be indicated via an additional underline. */
3560 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3561 "_foo = \xcf\x80"
3562 "_bar.\xf0\x9f\x98\x82"
3563 "_field\xcf\x80"
3564 ";\n"
3565 " ^\n"
3566 " ---------\n"
3567 " m_\xf0\x9f\x98\x82"
3568 "_field\xcf\x80\n",
3569 pp_formatted_text (dc.printer));
3570 }
3571
3572 /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
3573 where the caret was elsewhere, but where a secondary range
3574 exactly covers "field". */
3575
3576 static void
3577 test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
3578 {
3579 test_diagnostic_context dc;
3580 location_t equals = linemap_position_for_column (line_table, 10);
3581 location_t start = linemap_position_for_column (line_table, 19);
3582 location_t finish = linemap_position_for_column (line_table, 30);
3583 rich_location richloc (line_table, equals);
3584 location_t field = make_location (start, start, finish);
3585 richloc.add_range (field);
3586 richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
3587 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3588 /* The replacement range is indicated in the annotation line,
3589 so it shouldn't be indicated via an additional underline. */
3590 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3591 "_foo = \xcf\x80"
3592 "_bar.\xf0\x9f\x98\x82"
3593 "_field\xcf\x80"
3594 ";\n"
3595 " ^ ~~~~~~~~~\n"
3596 " m_\xf0\x9f\x98\x82"
3597 "_field\xcf\x80\n",
3598 pp_formatted_text (dc.printer));
3599 }
3600
3601 /* Verify that we can use ad-hoc locations when adding fixits to a
3602 rich_location. */
3603
3604 static void
3605 test_one_liner_fixit_validation_adhoc_locations_utf8 ()
3606 {
3607 /* Generate a range that's too long to be packed, so must
3608 be stored as an ad-hoc location (given the defaults
3609 of 5 bits or 0 bits of packed range); 41 columns > 2**5. */
3610 const location_t c12 = linemap_position_for_column (line_table, 12);
3611 const location_t c52 = linemap_position_for_column (line_table, 52);
3612 const location_t loc = make_location (c12, c12, c52);
3613
3614 if (c52 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3615 return;
3616
3617 ASSERT_TRUE (IS_ADHOC_LOC (loc));
3618
3619 /* Insert. */
3620 {
3621 rich_location richloc (line_table, loc);
3622 richloc.add_fixit_insert_before (loc, "test");
3623 /* It should not have been discarded by the validator. */
3624 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3625
3626 test_diagnostic_context dc;
3627 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3628 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3629 "_foo = \xcf\x80"
3630 "_bar.\xf0\x9f\x98\x82"
3631 "_field\xcf\x80"
3632 ";\n"
3633 " ^~~~~~~~~~~~~~~~ \n"
3634 " test\n",
3635 pp_formatted_text (dc.printer));
3636 }
3637
3638 /* Remove. */
3639 {
3640 rich_location richloc (line_table, loc);
3641 source_range range = source_range::from_locations (loc, c52);
3642 richloc.add_fixit_remove (range);
3643 /* It should not have been discarded by the validator. */
3644 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3645
3646 test_diagnostic_context dc;
3647 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3648 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3649 "_foo = \xcf\x80"
3650 "_bar.\xf0\x9f\x98\x82"
3651 "_field\xcf\x80"
3652 ";\n"
3653 " ^~~~~~~~~~~~~~~~ \n"
3654 " -------------------------------------\n",
3655 pp_formatted_text (dc.printer));
3656 }
3657
3658 /* Replace. */
3659 {
3660 rich_location richloc (line_table, loc);
3661 source_range range = source_range::from_locations (loc, c52);
3662 richloc.add_fixit_replace (range, "test");
3663 /* It should not have been discarded by the validator. */
3664 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3665
3666 test_diagnostic_context dc;
3667 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3668 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3669 "_foo = \xcf\x80"
3670 "_bar.\xf0\x9f\x98\x82"
3671 "_field\xcf\x80"
3672 ";\n"
3673 " ^~~~~~~~~~~~~~~~ \n"
3674 " test\n",
3675 pp_formatted_text (dc.printer));
3676 }
3677 }
3678
3679 /* Test of consolidating insertions at the same location. */
3680
3681 static void
3682 test_one_liner_many_fixits_1_utf8 ()
3683 {
3684 test_diagnostic_context dc;
3685 location_t equals = linemap_position_for_column (line_table, 10);
3686 rich_location richloc (line_table, equals);
3687 for (int i = 0; i < 19; i++)
3688 richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
3689 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3690 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3691 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3692 "_foo = \xcf\x80"
3693 "_bar.\xf0\x9f\x98\x82"
3694 "_field\xcf\x80"
3695 ";\n"
3696 " ^\n"
3697 " \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
3698 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
3699 pp_formatted_text (dc.printer));
3700 }
3701
3702 /* Ensure that we can add an arbitrary number of fix-it hints to a
3703 rich_location, even if they are not consolidated. */
3704
3705 static void
3706 test_one_liner_many_fixits_2_utf8 ()
3707 {
3708 test_diagnostic_context dc;
3709 location_t equals = linemap_position_for_column (line_table, 10);
3710 rich_location richloc (line_table, equals);
3711 const int nlocs = 19;
3712 int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
3713 34, 36, 38, 40, 42, 44};
3714 for (int i = 0; i != nlocs; ++i)
3715 {
3716 location_t loc = linemap_position_for_column (line_table, locs[i]);
3717 richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
3718 }
3719
3720 ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
3721 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3722 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3723 "_foo = \xcf\x80"
3724 "_bar.\xf0\x9f\x98\x82"
3725 "_field\xcf\x80"
3726 ";\n"
3727 " ^\n"
3728 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @"
3729 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
3730 pp_formatted_text (dc.printer));
3731 }
3732
3733 /* Test of labeling the ranges within a rich_location. */
3734
3735 static void
3736 test_one_liner_labels_utf8 ()
3737 {
3738 location_t foo
3739 = make_location (linemap_position_for_column (line_table, 1),
3740 linemap_position_for_column (line_table, 1),
3741 linemap_position_for_column (line_table, 8));
3742 location_t bar
3743 = make_location (linemap_position_for_column (line_table, 12),
3744 linemap_position_for_column (line_table, 12),
3745 linemap_position_for_column (line_table, 17));
3746 location_t field
3747 = make_location (linemap_position_for_column (line_table, 19),
3748 linemap_position_for_column (line_table, 19),
3749 linemap_position_for_column (line_table, 30));
3750
3751 /* Example where all the labels fit on one line. */
3752 {
3753 /* These three labels contain multibyte characters such that their byte
3754 lengths are respectively (12, 10, 18), but their display widths are only
3755 (6, 5, 9). All three fit on the line when considering the display
3756 widths, but not when considering the byte widths, so verify that we do
3757 indeed put them all on one line. */
3758 text_range_label label0
3759 ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
3760 text_range_label label1
3761 ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
3762 text_range_label label2
3763 ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3764 "\xcf\x80");
3765 gcc_rich_location richloc (foo, &label0);
3766 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3767 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3768
3769 {
3770 test_diagnostic_context dc;
3771 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3772 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3773 "_foo = \xcf\x80"
3774 "_bar.\xf0\x9f\x98\x82"
3775 "_field\xcf\x80"
3776 ";\n"
3777 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3778 " | | |\n"
3779 " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
3780 " \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
3781 " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
3782 "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
3783 pp_formatted_text (dc.printer));
3784 }
3785
3786 }
3787
3788 /* Example where the labels need extra lines. */
3789 {
3790 text_range_label label0 ("label 0\xf0\x9f\x98\x82");
3791 text_range_label label1 ("label 1\xcf\x80");
3792 text_range_label label2 ("label 2\xcf\x80");
3793 gcc_rich_location richloc (foo, &label0);
3794 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3795 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3796
3797 test_diagnostic_context dc;
3798 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3799
3800 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3801 "_foo = \xcf\x80"
3802 "_bar.\xf0\x9f\x98\x82"
3803 "_field\xcf\x80"
3804 ";\n"
3805 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3806 " | | |\n"
3807 " | | label 2\xcf\x80\n"
3808 " | label 1\xcf\x80\n"
3809 " label 0\xf0\x9f\x98\x82\n",
3810 pp_formatted_text (dc.printer));
3811 }
3812
3813 /* Example of boundary conditions: label 0 and 1 have just enough clearance,
3814 but label 1 just touches label 2. */
3815 {
3816 text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
3817 text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
3818 text_range_label label2 ("c");
3819 gcc_rich_location richloc (foo, &label0);
3820 richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
3821 richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
3822
3823 test_diagnostic_context dc;
3824 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3825 ASSERT_STREQ (" \xf0\x9f\x98\x82"
3826 "_foo = \xcf\x80"
3827 "_bar.\xf0\x9f\x98\x82"
3828 "_field\xcf\x80"
3829 ";\n"
3830 " ^~~~~~ ~~~~~ ~~~~~~~~~\n"
3831 " | | |\n"
3832 " | | c\n"
3833 " aaaaa\xf0\x9f\x98\x82\xcf\x80"
3834 " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
3835 pp_formatted_text (dc.printer));
3836 }
3837 }
3838
3839 /* Run the various one-liner tests. */
3840
3841 static void
3842 test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
3843 {
3844 /* Create a tempfile and write some text to it. */
3845 const char *content
3846 /* Display columns.
3847 0000000000000000000000011111111111111111111111111111112222222222222
3848 1111111122222222345678900000000123456666666677777777890123444444445 */
3849 = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
3850 /* 0000000000000000000001111111111111111111222222222222222222222233333
3851 1111222233334444567890122223333456789999000011112222345678999900001
3852 Byte columns. */
3853 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3854 line_table_test ltt (case_);
3855
3856 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3857
3858 location_t line_end = linemap_position_for_column (line_table, 31);
3859
3860 /* Don't attempt to run the tests if column data might be unavailable. */
3861 if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3862 return;
3863
3864 ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
3865 ASSERT_EQ (1, LOCATION_LINE (line_end));
3866 ASSERT_EQ (31, LOCATION_COLUMN (line_end));
3867
3868 char_span lspan = location_get_source_line (tmp.get_filename (), 1);
3869 ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length ()));
3870 ASSERT_EQ (25, location_compute_display_column (expand_location (line_end)));
3871
3872 test_one_liner_simple_caret_utf8 ();
3873 test_one_liner_caret_and_range_utf8 ();
3874 test_one_liner_multiple_carets_and_ranges_utf8 ();
3875 test_one_liner_fixit_insert_before_utf8 ();
3876 test_one_liner_fixit_insert_after_utf8 ();
3877 test_one_liner_fixit_remove_utf8 ();
3878 test_one_liner_fixit_replace_utf8 ();
3879 test_one_liner_fixit_replace_non_equal_range_utf8 ();
3880 test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
3881 test_one_liner_fixit_validation_adhoc_locations_utf8 ();
3882 test_one_liner_many_fixits_1_utf8 ();
3883 test_one_liner_many_fixits_2_utf8 ();
3884 test_one_liner_labels_utf8 ();
3885 }
3886
3887 /* Verify that gcc_rich_location::add_location_if_nearby works. */
3888
3889 static void
3890 test_add_location_if_nearby (const line_table_case &case_)
3891 {
3892 /* Create a tempfile and write some text to it.
3893 ...000000000111111111122222222223333333333.
3894 ...123456789012345678901234567890123456789. */
3895 const char *content
3896 = ("struct same_line { double x; double y; ;\n" /* line 1. */
3897 "struct different_line\n" /* line 2. */
3898 "{\n" /* line 3. */
3899 " double x;\n" /* line 4. */
3900 " double y;\n" /* line 5. */
3901 ";\n"); /* line 6. */
3902 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3903 line_table_test ltt (case_);
3904
3905 const line_map_ordinary *ord_map
3906 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3907 tmp.get_filename (), 0));
3908
3909 linemap_line_start (line_table, 1, 100);
3910
3911 const location_t final_line_end
3912 = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
3913
3914 /* Don't attempt to run the tests if column data might be unavailable. */
3915 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3916 return;
3917
3918 /* Test of add_location_if_nearby on the same line as the
3919 primary location. */
3920 {
3921 const location_t missing_close_brace_1_39
3922 = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
3923 const location_t matching_open_brace_1_18
3924 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3925 gcc_rich_location richloc (missing_close_brace_1_39);
3926 bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
3927 ASSERT_TRUE (added);
3928 ASSERT_EQ (2, richloc.get_num_locations ());
3929 test_diagnostic_context dc;
3930 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3931 ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
3932 " ~ ^\n",
3933 pp_formatted_text (dc.printer));
3934 }
3935
3936 /* Test of add_location_if_nearby on a different line to the
3937 primary location. */
3938 {
3939 const location_t missing_close_brace_6_1
3940 = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
3941 const location_t matching_open_brace_3_1
3942 = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
3943 gcc_rich_location richloc (missing_close_brace_6_1);
3944 bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
3945 ASSERT_FALSE (added);
3946 ASSERT_EQ (1, richloc.get_num_locations ());
3947 }
3948 }
3949
3950 /* Verify that we print fixits even if they only affect lines
3951 outside those covered by the ranges in the rich_location. */
3952
3953 static void
3954 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
3955 {
3956 /* Create a tempfile and write some text to it.
3957 ...000000000111111111122222222223333333333.
3958 ...123456789012345678901234567890123456789. */
3959 const char *content
3960 = ("struct point { double x; double y; };\n" /* line 1. */
3961 "struct point origin = {x: 0.0,\n" /* line 2. */
3962 " y\n" /* line 3. */
3963 "\n" /* line 4. */
3964 "\n" /* line 5. */
3965 " : 0.0};\n"); /* line 6. */
3966 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3967 line_table_test ltt (case_);
3968
3969 const line_map_ordinary *ord_map
3970 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3971 tmp.get_filename (), 0));
3972
3973 linemap_line_start (line_table, 1, 100);
3974
3975 const location_t final_line_end
3976 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3977
3978 /* Don't attempt to run the tests if column data might be unavailable. */
3979 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3980 return;
3981
3982 /* A pair of tests for modernizing the initializers to C99-style. */
3983
3984 /* The one-liner case (line 2). */
3985 {
3986 test_diagnostic_context dc;
3987 const location_t x
3988 = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
3989 const location_t colon
3990 = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
3991 rich_location richloc (line_table, colon);
3992 richloc.add_fixit_insert_before (x, ".");
3993 richloc.add_fixit_replace (colon, "=");
3994 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3995 ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
3996 " ^\n"
3997 " .=\n",
3998 pp_formatted_text (dc.printer));
3999 }
4000
4001 /* The multiline case. The caret for the rich_location is on line 6;
4002 verify that insertion fixit on line 3 is still printed (and that
4003 span starts are printed due to the gap between the span at line 3
4004 and that at line 6). */
4005 {
4006 test_diagnostic_context dc;
4007 const location_t y
4008 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4009 const location_t colon
4010 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4011 rich_location richloc (line_table, colon);
4012 richloc.add_fixit_insert_before (y, ".");
4013 richloc.add_fixit_replace (colon, "=");
4014 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4015 ASSERT_STREQ ("FILENAME:3:24:\n"
4016 " y\n"
4017 " .\n"
4018 "FILENAME:6:25:\n"
4019 " : 0.0};\n"
4020 " ^\n"
4021 " =\n",
4022 pp_formatted_text (dc.printer));
4023 }
4024
4025 /* As above, but verify the behavior of multiple line spans
4026 with line-numbering enabled. */
4027 {
4028 const location_t y
4029 = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
4030 const location_t colon
4031 = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
4032 rich_location richloc (line_table, colon);
4033 richloc.add_fixit_insert_before (y, ".");
4034 richloc.add_fixit_replace (colon, "=");
4035 test_diagnostic_context dc;
4036 dc.show_line_numbers_p = true;
4037 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4038 ASSERT_STREQ (" 3 | y\n"
4039 " | .\n"
4040 "......\n"
4041 " 6 | : 0.0};\n"
4042 " | ^\n"
4043 " | =\n",
4044 pp_formatted_text (dc.printer));
4045 }
4046 }
4047
4048
4049 /* Verify that fix-it hints are appropriately consolidated.
4050
4051 If any fix-it hints in a rich_location involve locations beyond
4052 LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
4053 the fix-it as a whole, so there should be none.
4054
4055 Otherwise, verify that consecutive "replace" and "remove" fix-its
4056 are merged, and that other fix-its remain separate. */
4057
4058 static void
4059 test_fixit_consolidation (const line_table_case &case_)
4060 {
4061 line_table_test ltt (case_);
4062
4063 linemap_add (line_table, LC_ENTER, false, "test.c", 1);
4064
4065 const location_t c10 = linemap_position_for_column (line_table, 10);
4066 const location_t c15 = linemap_position_for_column (line_table, 15);
4067 const location_t c16 = linemap_position_for_column (line_table, 16);
4068 const location_t c17 = linemap_position_for_column (line_table, 17);
4069 const location_t c20 = linemap_position_for_column (line_table, 20);
4070 const location_t c21 = linemap_position_for_column (line_table, 21);
4071 const location_t caret = c10;
4072
4073 /* Insert + insert. */
4074 {
4075 rich_location richloc (line_table, caret);
4076 richloc.add_fixit_insert_before (c10, "foo");
4077 richloc.add_fixit_insert_before (c15, "bar");
4078
4079 if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4080 /* Bogus column info for 2nd fixit, so no fixits. */
4081 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4082 else
4083 /* They should not have been merged. */
4084 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4085 }
4086
4087 /* Insert + replace. */
4088 {
4089 rich_location richloc (line_table, caret);
4090 richloc.add_fixit_insert_before (c10, "foo");
4091 richloc.add_fixit_replace (source_range::from_locations (c15, c17),
4092 "bar");
4093
4094 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4095 /* Bogus column info for 2nd fixit, so no fixits. */
4096 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4097 else
4098 /* They should not have been merged. */
4099 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4100 }
4101
4102 /* Replace + non-consecutive insert. */
4103 {
4104 rich_location richloc (line_table, caret);
4105 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4106 "bar");
4107 richloc.add_fixit_insert_before (c17, "foo");
4108
4109 if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4110 /* Bogus column info for 2nd fixit, so no fixits. */
4111 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4112 else
4113 /* They should not have been merged. */
4114 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4115 }
4116
4117 /* Replace + non-consecutive replace. */
4118 {
4119 rich_location richloc (line_table, caret);
4120 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4121 "foo");
4122 richloc.add_fixit_replace (source_range::from_locations (c17, c20),
4123 "bar");
4124
4125 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4126 /* Bogus column info for 2nd fixit, so no fixits. */
4127 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4128 else
4129 /* They should not have been merged. */
4130 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4131 }
4132
4133 /* Replace + consecutive replace. */
4134 {
4135 rich_location richloc (line_table, caret);
4136 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4137 "foo");
4138 richloc.add_fixit_replace (source_range::from_locations (c16, c20),
4139 "bar");
4140
4141 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4142 /* Bogus column info for 2nd fixit, so no fixits. */
4143 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4144 else
4145 {
4146 /* They should have been merged into a single "replace". */
4147 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4148 const fixit_hint *hint = richloc.get_fixit_hint (0);
4149 ASSERT_STREQ ("foobar", hint->get_string ());
4150 ASSERT_EQ (c10, hint->get_start_loc ());
4151 ASSERT_EQ (c21, hint->get_next_loc ());
4152 }
4153 }
4154
4155 /* Replace + consecutive removal. */
4156 {
4157 rich_location richloc (line_table, caret);
4158 richloc.add_fixit_replace (source_range::from_locations (c10, c15),
4159 "foo");
4160 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4161
4162 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4163 /* Bogus column info for 2nd fixit, so no fixits. */
4164 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4165 else
4166 {
4167 /* They should have been merged into a single replace, with the
4168 range extended to cover that of the removal. */
4169 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4170 const fixit_hint *hint = richloc.get_fixit_hint (0);
4171 ASSERT_STREQ ("foo", hint->get_string ());
4172 ASSERT_EQ (c10, hint->get_start_loc ());
4173 ASSERT_EQ (c21, hint->get_next_loc ());
4174 }
4175 }
4176
4177 /* Consecutive removals. */
4178 {
4179 rich_location richloc (line_table, caret);
4180 richloc.add_fixit_remove (source_range::from_locations (c10, c15));
4181 richloc.add_fixit_remove (source_range::from_locations (c16, c20));
4182
4183 if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
4184 /* Bogus column info for 2nd fixit, so no fixits. */
4185 ASSERT_EQ (0, richloc.get_num_fixit_hints ());
4186 else
4187 {
4188 /* They should have been merged into a single "replace-with-empty". */
4189 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4190 const fixit_hint *hint = richloc.get_fixit_hint (0);
4191 ASSERT_STREQ ("", hint->get_string ());
4192 ASSERT_EQ (c10, hint->get_start_loc ());
4193 ASSERT_EQ (c21, hint->get_next_loc ());
4194 }
4195 }
4196 }
4197
4198 /* Verify that the line_corrections machinery correctly prints
4199 overlapping fixit-hints. */
4200
4201 static void
4202 test_overlapped_fixit_printing (const line_table_case &case_)
4203 {
4204 /* Create a tempfile and write some text to it.
4205 ...000000000111111111122222222223333333333.
4206 ...123456789012345678901234567890123456789. */
4207 const char *content
4208 = (" foo *f = (foo *)ptr->field;\n");
4209 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4210 line_table_test ltt (case_);
4211
4212 const line_map_ordinary *ord_map
4213 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4214 tmp.get_filename (), 0));
4215
4216 linemap_line_start (line_table, 1, 100);
4217
4218 const location_t final_line_end
4219 = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
4220
4221 /* Don't attempt to run the tests if column data might be unavailable. */
4222 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4223 return;
4224
4225 /* A test for converting a C-style cast to a C++-style cast. */
4226 const location_t open_paren
4227 = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
4228 const location_t close_paren
4229 = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
4230 const location_t expr_start
4231 = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
4232 const location_t expr_finish
4233 = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
4234 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4235
4236 /* Various examples of fix-it hints that aren't themselves consolidated,
4237 but for which the *printing* may need consolidation. */
4238
4239 /* Example where 3 fix-it hints are printed as one. */
4240 {
4241 test_diagnostic_context dc;
4242 rich_location richloc (line_table, expr);
4243 richloc.add_fixit_replace (open_paren, "const_cast<");
4244 richloc.add_fixit_replace (close_paren, "> (");
4245 richloc.add_fixit_insert_after (")");
4246
4247 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4248 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4249 " ^~~~~~~~~~\n"
4250 " -----------------\n"
4251 " const_cast<foo *> (ptr->field)\n",
4252 pp_formatted_text (dc.printer));
4253
4254 /* Unit-test the line_corrections machinery. */
4255 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4256 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4257 ASSERT_EQ (column_range (12, 12), get_affected_range (hint_0, CU_BYTES));
4258 ASSERT_EQ (column_range (12, 12),
4259 get_affected_range (hint_0, CU_DISPLAY_COLS));
4260 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
4261 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4262 ASSERT_EQ (column_range (18, 18), get_affected_range (hint_1, CU_BYTES));
4263 ASSERT_EQ (column_range (18, 18),
4264 get_affected_range (hint_1, CU_DISPLAY_COLS));
4265 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
4266 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4267 ASSERT_EQ (column_range (29, 28), get_affected_range (hint_2, CU_BYTES));
4268 ASSERT_EQ (column_range (29, 28),
4269 get_affected_range (hint_2, CU_DISPLAY_COLS));
4270 ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
4271
4272 /* Add each hint in turn to a line_corrections instance,
4273 and verify that they are consolidated into one correction instance
4274 as expected. */
4275 line_corrections lc (tmp.get_filename (), 1);
4276
4277 /* The first replace hint by itself. */
4278 lc.add_hint (hint_0);
4279 ASSERT_EQ (1, lc.m_corrections.length ());
4280 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
4281 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4282 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4283 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4284
4285 /* After the second replacement hint, they are printed together
4286 as a replacement (along with the text between them). */
4287 lc.add_hint (hint_1);
4288 ASSERT_EQ (1, lc.m_corrections.length ());
4289 ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
4290 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
4291 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4292 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4293
4294 /* After the final insertion hint, they are all printed together
4295 as a replacement (along with the text between them). */
4296 lc.add_hint (hint_2);
4297 ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
4298 lc.m_corrections[0]->m_text);
4299 ASSERT_EQ (1, lc.m_corrections.length ());
4300 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
4301 ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
4302 ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
4303 }
4304
4305 /* Example where two are consolidated during printing. */
4306 {
4307 test_diagnostic_context dc;
4308 rich_location richloc (line_table, expr);
4309 richloc.add_fixit_replace (open_paren, "CAST (");
4310 richloc.add_fixit_replace (close_paren, ") (");
4311 richloc.add_fixit_insert_after (")");
4312
4313 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4314 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4315 " ^~~~~~~~~~\n"
4316 " -\n"
4317 " CAST (-\n"
4318 " ) ( )\n",
4319 pp_formatted_text (dc.printer));
4320 }
4321
4322 /* Example where none are consolidated during printing. */
4323 {
4324 test_diagnostic_context dc;
4325 rich_location richloc (line_table, expr);
4326 richloc.add_fixit_replace (open_paren, "CST (");
4327 richloc.add_fixit_replace (close_paren, ") (");
4328 richloc.add_fixit_insert_after (")");
4329
4330 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4331 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4332 " ^~~~~~~~~~\n"
4333 " -\n"
4334 " CST ( -\n"
4335 " ) ( )\n",
4336 pp_formatted_text (dc.printer));
4337 }
4338
4339 /* Example of deletion fix-it hints. */
4340 {
4341 test_diagnostic_context dc;
4342 rich_location richloc (line_table, expr);
4343 richloc.add_fixit_insert_before (open_paren, "(bar *)");
4344 source_range victim = {open_paren, close_paren};
4345 richloc.add_fixit_remove (victim);
4346
4347 /* This case is actually handled by fixit-consolidation,
4348 rather than by line_corrections. */
4349 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4350
4351 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4352 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4353 " ^~~~~~~~~~\n"
4354 " -------\n"
4355 " (bar *)\n",
4356 pp_formatted_text (dc.printer));
4357 }
4358
4359 /* Example of deletion fix-it hints that would overlap. */
4360 {
4361 test_diagnostic_context dc;
4362 rich_location richloc (line_table, expr);
4363 richloc.add_fixit_insert_before (open_paren, "(longer *)");
4364 source_range victim = {expr_start, expr_finish};
4365 richloc.add_fixit_remove (victim);
4366
4367 /* These fixits are not consolidated. */
4368 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4369
4370 /* But the corrections are. */
4371 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4372 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4373 " ^~~~~~~~~~\n"
4374 " -----------------\n"
4375 " (longer *)(foo *)\n",
4376 pp_formatted_text (dc.printer));
4377 }
4378
4379 /* Example of insertion fix-it hints that would overlap. */
4380 {
4381 test_diagnostic_context dc;
4382 rich_location richloc (line_table, expr);
4383 richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
4384 richloc.add_fixit_insert_after (close_paren, "TEST");
4385
4386 /* The first insertion is long enough that if printed naively,
4387 it would overlap with the second.
4388 Verify that they are printed as a single replacement. */
4389 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4390 ASSERT_STREQ (" foo *f = (foo *)ptr->field;\n"
4391 " ^~~~~~~~~~\n"
4392 " -------\n"
4393 " LONGER THAN THE CAST(foo *)TEST\n",
4394 pp_formatted_text (dc.printer));
4395 }
4396 }
4397
4398 /* Multibyte-aware version of preceding tests. See comments above
4399 test_one_liner_simple_caret_utf8() too, we use the same two multibyte
4400 characters here. */
4401
4402 static void
4403 test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
4404 {
4405 /* Create a tempfile and write some text to it. */
4406
4407 const char *content
4408 /* Display columns.
4409 00000000000000000000000111111111111111111111111222222222222222223
4410 12344444444555555556789012344444444555555556789012345678999999990 */
4411 = " f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
4412 /* 00000000000000000000011111111111111111111112222222222333333333333
4413 12344445555666677778901234566667777888899990123456789012333344445
4414 Byte columns. */
4415
4416 temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
4417 line_table_test ltt (case_);
4418
4419 const line_map_ordinary *ord_map
4420 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4421 tmp.get_filename (), 0));
4422
4423 linemap_line_start (line_table, 1, 100);
4424
4425 const location_t final_line_end
4426 = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
4427
4428 /* Don't attempt to run the tests if column data might be unavailable. */
4429 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4430 return;
4431
4432 /* A test for converting a C-style cast to a C++-style cast. */
4433 const location_t open_paren
4434 = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
4435 const location_t close_paren
4436 = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
4437 const location_t expr_start
4438 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4439 const location_t expr_finish
4440 = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
4441 const location_t expr = make_location (expr_start, expr_start, expr_finish);
4442
4443 /* Various examples of fix-it hints that aren't themselves consolidated,
4444 but for which the *printing* may need consolidation. */
4445
4446 /* Example where 3 fix-it hints are printed as one. */
4447 {
4448 test_diagnostic_context dc;
4449 rich_location richloc (line_table, expr);
4450 richloc.add_fixit_replace (open_paren, "const_cast<");
4451 richloc.add_fixit_replace (close_paren, "> (");
4452 richloc.add_fixit_insert_after (")");
4453
4454 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4455 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4456 " *f = (f\xf0\x9f\x98\x82"
4457 " *)ptr->field\xcf\x80"
4458 ";\n"
4459 " ^~~~~~~~~~~\n"
4460 " ------------------\n"
4461 " const_cast<f\xf0\x9f\x98\x82"
4462 " *> (ptr->field\xcf\x80"
4463 ")\n",
4464 pp_formatted_text (dc.printer));
4465
4466 /* Unit-test the line_corrections machinery. */
4467 ASSERT_EQ (3, richloc.get_num_fixit_hints ());
4468 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4469 ASSERT_EQ (column_range (14, 14), get_affected_range (hint_0, CU_BYTES));
4470 ASSERT_EQ (column_range (12, 12),
4471 get_affected_range (hint_0, CU_DISPLAY_COLS));
4472 ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
4473 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4474 ASSERT_EQ (column_range (22, 22), get_affected_range (hint_1, CU_BYTES));
4475 ASSERT_EQ (column_range (18, 18),
4476 get_affected_range (hint_1, CU_DISPLAY_COLS));
4477 ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
4478 const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
4479 ASSERT_EQ (column_range (35, 34), get_affected_range (hint_2, CU_BYTES));
4480 ASSERT_EQ (column_range (30, 29),
4481 get_affected_range (hint_2, CU_DISPLAY_COLS));
4482 ASSERT_EQ (column_range (30, 30), get_printed_columns (hint_2));
4483
4484 /* Add each hint in turn to a line_corrections instance,
4485 and verify that they are consolidated into one correction instance
4486 as expected. */
4487 line_corrections lc (tmp.get_filename (), 1);
4488
4489 /* The first replace hint by itself. */
4490 lc.add_hint (hint_0);
4491 ASSERT_EQ (1, lc.m_corrections.length ());
4492 ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
4493 ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
4494 ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
4495 ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
4496
4497 /* After the second replacement hint, they are printed together
4498 as a replacement (along with the text between them). */
4499 lc.add_hint (hint_1);
4500 ASSERT_EQ (1, lc.m_corrections.length ());
4501 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
4502 lc.m_corrections[0]->m_text);
4503 ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
4504 ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
4505 ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
4506
4507 /* After the final insertion hint, they are all printed together
4508 as a replacement (along with the text between them). */
4509 lc.add_hint (hint_2);
4510 ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
4511 lc.m_corrections[0]->m_text);
4512 ASSERT_EQ (1, lc.m_corrections.length ());
4513 ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
4514 ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
4515 ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
4516 }
4517
4518 /* Example where two are consolidated during printing. */
4519 {
4520 test_diagnostic_context dc;
4521 rich_location richloc (line_table, expr);
4522 richloc.add_fixit_replace (open_paren, "CAST (");
4523 richloc.add_fixit_replace (close_paren, ") (");
4524 richloc.add_fixit_insert_after (")");
4525
4526 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4527 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4528 " *f = (f\xf0\x9f\x98\x82"
4529 " *)ptr->field\xcf\x80"
4530 ";\n"
4531 " ^~~~~~~~~~~\n"
4532 " -\n"
4533 " CAST (-\n"
4534 " ) ( )\n",
4535 pp_formatted_text (dc.printer));
4536 }
4537
4538 /* Example where none are consolidated during printing. */
4539 {
4540 test_diagnostic_context dc;
4541 rich_location richloc (line_table, expr);
4542 richloc.add_fixit_replace (open_paren, "CST (");
4543 richloc.add_fixit_replace (close_paren, ") (");
4544 richloc.add_fixit_insert_after (")");
4545
4546 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4547 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4548 " *f = (f\xf0\x9f\x98\x82"
4549 " *)ptr->field\xcf\x80"
4550 ";\n"
4551 " ^~~~~~~~~~~\n"
4552 " -\n"
4553 " CST ( -\n"
4554 " ) ( )\n",
4555 pp_formatted_text (dc.printer));
4556 }
4557
4558 /* Example of deletion fix-it hints. */
4559 {
4560 test_diagnostic_context dc;
4561 rich_location richloc (line_table, expr);
4562 richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
4563 source_range victim = {open_paren, close_paren};
4564 richloc.add_fixit_remove (victim);
4565
4566 /* This case is actually handled by fixit-consolidation,
4567 rather than by line_corrections. */
4568 ASSERT_EQ (1, richloc.get_num_fixit_hints ());
4569
4570 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4571 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4572 " *f = (f\xf0\x9f\x98\x82"
4573 " *)ptr->field\xcf\x80"
4574 ";\n"
4575 " ^~~~~~~~~~~\n"
4576 " -------\n"
4577 " (bar\xf0\x9f\x98\x82"
4578 " *)\n",
4579 pp_formatted_text (dc.printer));
4580 }
4581
4582 /* Example of deletion fix-it hints that would overlap. */
4583 {
4584 test_diagnostic_context dc;
4585 rich_location richloc (line_table, expr);
4586 richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
4587 source_range victim = {expr_start, expr_finish};
4588 richloc.add_fixit_remove (victim);
4589
4590 /* These fixits are not consolidated. */
4591 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4592
4593 /* But the corrections are. */
4594 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4595 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4596 " *f = (f\xf0\x9f\x98\x82"
4597 " *)ptr->field\xcf\x80"
4598 ";\n"
4599 " ^~~~~~~~~~~\n"
4600 " ------------------\n"
4601 " (long\xf0\x9f\x98\x82"
4602 " *)(f\xf0\x9f\x98\x82"
4603 " *)\n",
4604 pp_formatted_text (dc.printer));
4605 }
4606
4607 /* Example of insertion fix-it hints that would overlap. */
4608 {
4609 test_diagnostic_context dc;
4610 rich_location richloc (line_table, expr);
4611 richloc.add_fixit_insert_before
4612 (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
4613 richloc.add_fixit_insert_after (close_paren, "TEST");
4614
4615 /* The first insertion is long enough that if printed naively,
4616 it would overlap with the second.
4617 Verify that they are printed as a single replacement. */
4618 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4619 ASSERT_STREQ (" f\xf0\x9f\x98\x82"
4620 " *f = (f\xf0\x9f\x98\x82"
4621 " *)ptr->field\xcf\x80"
4622 ";\n"
4623 " ^~~~~~~~~~~\n"
4624 " -------\n"
4625 " L\xf0\x9f\x98\x82"
4626 "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
4627 " *)TEST\n",
4628 pp_formatted_text (dc.printer));
4629 }
4630 }
4631
4632 /* Verify that the line_corrections machinery correctly prints
4633 overlapping fixit-hints that have been added in the wrong
4634 order.
4635 Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
4636
4637 static void
4638 test_overlapped_fixit_printing_2 (const line_table_case &case_)
4639 {
4640 /* Create a tempfile and write some text to it.
4641 ...000000000111111111122222222223333333333.
4642 ...123456789012345678901234567890123456789. */
4643 const char *content
4644 = ("int a5[][0][0] = { 1, 2 };\n");
4645 temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
4646 line_table_test ltt (case_);
4647
4648 const line_map_ordinary *ord_map
4649 = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
4650 tmp.get_filename (), 0));
4651
4652 linemap_line_start (line_table, 1, 100);
4653
4654 const location_t final_line_end
4655 = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
4656
4657 /* Don't attempt to run the tests if column data might be unavailable. */
4658 if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4659 return;
4660
4661 const location_t col_1
4662 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4663 const location_t col_20
4664 = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
4665 const location_t col_21
4666 = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
4667 const location_t col_23
4668 = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
4669 const location_t col_25
4670 = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
4671
4672 /* Two insertions, in the wrong order. */
4673 {
4674 rich_location richloc (line_table, col_20);
4675 richloc.add_fixit_insert_before (col_23, "{");
4676 richloc.add_fixit_insert_before (col_21, "}");
4677
4678 /* These fixits should be accepted; they can't be consolidated. */
4679 ASSERT_EQ (2, richloc.get_num_fixit_hints ());
4680 const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
4681 ASSERT_EQ (column_range (23, 22), get_affected_range (hint_0, CU_BYTES));
4682 ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
4683 const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
4684 ASSERT_EQ (column_range (21, 20), get_affected_range (hint_1, CU_BYTES));
4685 ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
4686
4687 /* Verify that they're printed correctly. */
4688 test_diagnostic_context dc;
4689 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4690 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4691 " ^\n"
4692 " } {\n",
4693 pp_formatted_text (dc.printer));
4694 }
4695
4696 /* Various overlapping insertions, some occurring "out of order"
4697 (reproducing the fix-it hints from PR c/81405). */
4698 {
4699 test_diagnostic_context dc;
4700 rich_location richloc (line_table, col_20);
4701
4702 richloc.add_fixit_insert_before (col_20, "{{");
4703 richloc.add_fixit_insert_before (col_21, "}}");
4704 richloc.add_fixit_insert_before (col_23, "{");
4705 richloc.add_fixit_insert_before (col_21, "}");
4706 richloc.add_fixit_insert_before (col_23, "{{");
4707 richloc.add_fixit_insert_before (col_25, "}");
4708 richloc.add_fixit_insert_before (col_21, "}");
4709 richloc.add_fixit_insert_before (col_1, "{");
4710 richloc.add_fixit_insert_before (col_25, "}");
4711 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4712 ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
4713 " ^\n"
4714 " { -----\n"
4715 " {{1}}}}, {{{2 }}\n",
4716 pp_formatted_text (dc.printer));
4717 }
4718 }
4719
4720 /* Insertion fix-it hint: adding a "break;" on a line by itself. */
4721
4722 static void
4723 test_fixit_insert_containing_newline (const line_table_case &case_)
4724 {
4725 /* Create a tempfile and write some text to it.
4726 .........................0000000001111111.
4727 .........................1234567890123456. */
4728 const char *old_content = (" case 'a':\n" /* line 1. */
4729 " x = a;\n" /* line 2. */
4730 " case 'b':\n" /* line 3. */
4731 " x = b;\n");/* line 4. */
4732
4733 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4734 line_table_test ltt (case_);
4735 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
4736
4737 location_t case_start = linemap_position_for_column (line_table, 5);
4738 location_t case_finish = linemap_position_for_column (line_table, 13);
4739 location_t case_loc = make_location (case_start, case_start, case_finish);
4740 location_t line_start = linemap_position_for_column (line_table, 1);
4741
4742 if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4743 return;
4744
4745 /* Add a "break;" on a line by itself before line 3 i.e. before
4746 column 1 of line 3. */
4747 {
4748 rich_location richloc (line_table, case_loc);
4749 richloc.add_fixit_insert_before (line_start, " break;\n");
4750
4751 /* Without line numbers. */
4752 {
4753 test_diagnostic_context dc;
4754 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4755 ASSERT_STREQ (" x = a;\n"
4756 "+ break;\n"
4757 " case 'b':\n"
4758 " ^~~~~~~~~\n",
4759 pp_formatted_text (dc.printer));
4760 }
4761
4762 /* With line numbers. */
4763 {
4764 test_diagnostic_context dc;
4765 dc.show_line_numbers_p = true;
4766 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4767 ASSERT_STREQ (" 2 | x = a;\n"
4768 " +++ |+ break;\n"
4769 " 3 | case 'b':\n"
4770 " | ^~~~~~~~~\n",
4771 pp_formatted_text (dc.printer));
4772 }
4773 }
4774
4775 /* Verify that attempts to add text with a newline fail when the
4776 insertion point is *not* at the start of a line. */
4777 {
4778 rich_location richloc (line_table, case_loc);
4779 richloc.add_fixit_insert_before (case_start, "break;\n");
4780 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4781 test_diagnostic_context dc;
4782 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4783 ASSERT_STREQ (" case 'b':\n"
4784 " ^~~~~~~~~\n",
4785 pp_formatted_text (dc.printer));
4786 }
4787 }
4788
4789 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
4790 of the file, where the fix-it is printed in a different line-span
4791 to the primary range of the diagnostic. */
4792
4793 static void
4794 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
4795 {
4796 /* Create a tempfile and write some text to it.
4797 .........................0000000001111111.
4798 .........................1234567890123456. */
4799 const char *old_content = ("test (int ch)\n" /* line 1. */
4800 "{\n" /* line 2. */
4801 " putchar (ch);\n" /* line 3. */
4802 "}\n"); /* line 4. */
4803
4804 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4805 line_table_test ltt (case_);
4806
4807 const line_map_ordinary *ord_map = linemap_check_ordinary
4808 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4809 linemap_line_start (line_table, 1, 100);
4810
4811 /* The primary range is the "putchar" token. */
4812 location_t putchar_start
4813 = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
4814 location_t putchar_finish
4815 = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
4816 location_t putchar_loc
4817 = make_location (putchar_start, putchar_start, putchar_finish);
4818 rich_location richloc (line_table, putchar_loc);
4819
4820 /* Add a "#include <stdio.h>" on a line by itself at the top of the file. */
4821 location_t file_start
4822 = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
4823 richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
4824
4825 if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4826 return;
4827
4828 {
4829 test_diagnostic_context dc;
4830 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4831 ASSERT_STREQ ("FILENAME:1:1:\n"
4832 "+#include <stdio.h>\n"
4833 " test (int ch)\n"
4834 "FILENAME:3:2:\n"
4835 " putchar (ch);\n"
4836 " ^~~~~~~\n",
4837 pp_formatted_text (dc.printer));
4838 }
4839
4840 /* With line-numbering, the line spans are close enough to be
4841 consolidated, since it makes little sense to skip line 2. */
4842 {
4843 test_diagnostic_context dc;
4844 dc.show_line_numbers_p = true;
4845 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4846 ASSERT_STREQ (" +++ |+#include <stdio.h>\n"
4847 " 1 | test (int ch)\n"
4848 " 2 | {\n"
4849 " 3 | putchar (ch);\n"
4850 " | ^~~~~~~\n",
4851 pp_formatted_text (dc.printer));
4852 }
4853 }
4854
4855 /* Replacement fix-it hint containing a newline.
4856 This will fail, as newlines are only supported when inserting at the
4857 beginning of a line. */
4858
4859 static void
4860 test_fixit_replace_containing_newline (const line_table_case &case_)
4861 {
4862 /* Create a tempfile and write some text to it.
4863 .........................0000000001111.
4864 .........................1234567890123. */
4865 const char *old_content = "foo = bar ();\n";
4866
4867 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4868 line_table_test ltt (case_);
4869 linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
4870
4871 /* Replace the " = " with "\n = ", as if we were reformatting an
4872 overly long line. */
4873 location_t start = linemap_position_for_column (line_table, 4);
4874 location_t finish = linemap_position_for_column (line_table, 6);
4875 location_t loc = linemap_position_for_column (line_table, 13);
4876 rich_location richloc (line_table, loc);
4877 source_range range = source_range::from_locations (start, finish);
4878 richloc.add_fixit_replace (range, "\n =");
4879
4880 /* Arbitrary newlines are not yet supported within fix-it hints, so
4881 the fix-it should not be displayed. */
4882 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4883
4884 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4885 return;
4886
4887 test_diagnostic_context dc;
4888 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4889 ASSERT_STREQ (" foo = bar ();\n"
4890 " ^\n",
4891 pp_formatted_text (dc.printer));
4892 }
4893
4894 /* Fix-it hint, attempting to delete a newline.
4895 This will fail, as we currently only support fix-it hints that
4896 affect one line at a time. */
4897
4898 static void
4899 test_fixit_deletion_affecting_newline (const line_table_case &case_)
4900 {
4901 /* Create a tempfile and write some text to it.
4902 ..........................0000000001111.
4903 ..........................1234567890123. */
4904 const char *old_content = ("foo = bar (\n"
4905 " );\n");
4906
4907 temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
4908 line_table_test ltt (case_);
4909 const line_map_ordinary *ord_map = linemap_check_ordinary
4910 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4911 linemap_line_start (line_table, 1, 100);
4912
4913 /* Attempt to delete the " (\n...)". */
4914 location_t start
4915 = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
4916 location_t caret
4917 = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
4918 location_t finish
4919 = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
4920 location_t loc = make_location (caret, start, finish);
4921 rich_location richloc (line_table, loc);
4922 richloc. add_fixit_remove ();
4923
4924 /* Fix-it hints that affect more than one line are not yet supported, so
4925 the fix-it should not be displayed. */
4926 ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
4927
4928 if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
4929 return;
4930
4931 test_diagnostic_context dc;
4932 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4933 ASSERT_STREQ (" foo = bar (\n"
4934 " ~^\n"
4935 " );\n"
4936 " ~ \n",
4937 pp_formatted_text (dc.printer));
4938 }
4939
4940 /* Verify that line numbers are correctly printed for the case of
4941 a multiline range in which the width of the line numbers changes
4942 (e.g. from "9" to "10"). */
4943
4944 static void
4945 test_line_numbers_multiline_range ()
4946 {
4947 /* Create a tempfile and write some text to it. */
4948 pretty_printer pp;
4949 for (int i = 0; i < 20; i++)
4950 /* .........0000000001111111.
4951 .............1234567890123456. */
4952 pp_printf (&pp, "this is line %i\n", i + 1);
4953 temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
4954 line_table_test ltt;
4955
4956 const line_map_ordinary *ord_map = linemap_check_ordinary
4957 (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
4958 linemap_line_start (line_table, 1, 100);
4959
4960 /* Create a multi-line location, starting at the "line" of line 9, with
4961 a caret on the "is" of line 10, finishing on the "this" line 11. */
4962
4963 location_t start
4964 = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
4965 location_t caret
4966 = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
4967 location_t finish
4968 = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
4969 location_t loc = make_location (caret, start, finish);
4970
4971 test_diagnostic_context dc;
4972 dc.show_line_numbers_p = true;
4973 dc.min_margin_width = 0;
4974 gcc_rich_location richloc (loc);
4975 diagnostic_show_locus (&dc, &richloc, DK_ERROR);
4976 ASSERT_STREQ (" 9 | this is line 9\n"
4977 " | ~~~~~~\n"
4978 "10 | this is line 10\n"
4979 " | ~~~~~^~~~~~~~~~\n"
4980 "11 | this is line 11\n"
4981 " | ~~~~ \n",
4982 pp_formatted_text (dc.printer));
4983 }
4984
4985 /* Run all of the selftests within this file. */
4986
4987 void
4988 diagnostic_show_locus_c_tests ()
4989 {
4990 test_line_span ();
4991
4992 test_layout_range_for_single_point ();
4993 test_layout_range_for_single_line ();
4994 test_layout_range_for_multiple_lines ();
4995
4996 for_each_line_table_case (test_layout_x_offset_display_utf8);
4997
4998 test_get_line_bytes_without_trailing_whitespace ();
4999
5000 test_diagnostic_show_locus_unknown_location ();
5001
5002 for_each_line_table_case (test_diagnostic_show_locus_one_liner);
5003 for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
5004 for_each_line_table_case (test_add_location_if_nearby);
5005 for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
5006 for_each_line_table_case (test_fixit_consolidation);
5007 for_each_line_table_case (test_overlapped_fixit_printing);
5008 for_each_line_table_case (test_overlapped_fixit_printing_utf8);
5009 for_each_line_table_case (test_overlapped_fixit_printing_2);
5010 for_each_line_table_case (test_fixit_insert_containing_newline);
5011 for_each_line_table_case (test_fixit_insert_containing_newline_2);
5012 for_each_line_table_case (test_fixit_replace_containing_newline);
5013 for_each_line_table_case (test_fixit_deletion_affecting_newline);
5014
5015 test_line_numbers_multiline_range ();
5016 }
5017
5018 } // namespace selftest
5019
5020 #endif /* #if CHECKING_P */
5021
5022 #if __GNUC__ >= 10
5023 # pragma GCC diagnostic pop
5024 #endif