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