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