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