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