1 /* Diagnostic subroutines for printing source-code
2    Copyright (C) 1999-2019 Free Software Foundation, Inc.
3    Contributed by Gabriel Dos Reis <gdr@codesourcery.com>
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11 
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "version.h"
25 #include "demangle.h"
26 #include "intl.h"
27 #include "backtrace.h"
28 #include "diagnostic.h"
29 #include "diagnostic-color.h"
30 #include "gcc-rich-location.h"
31 #include "selftest.h"
32 #include "selftest-diagnostic.h"
33 
34 #ifdef HAVE_TERMIOS_H
35 # include <termios.h>
36 #endif
37 
38 #ifdef GWINSZ_IN_SYS_IOCTL
39 # include <sys/ioctl.h>
40 #endif
41 
42 /* Classes for rendering source code and diagnostics, within an
43    anonymous namespace.
44    The work is done by "class layout", which embeds and uses
45    "class colorizer" and "class layout_range" to get things done.  */
46 
47 namespace {
48 
49 /* The state at a given point of the source code, assuming that we're
50    in a range: which range are we in, and whether we should draw a caret at
51    this point.  */
52 
53 struct point_state
54 {
55   int range_idx;
56   bool draw_caret_p;
57 };
58 
59 /* A class to inject colorization codes when printing the diagnostic locus.
60 
61    It has one kind of colorization for each of:
62      - normal text
63      - range 0 (the "primary location")
64      - range 1
65      - range 2
66 
67    The class caches the lookup of the color codes for the above.
68 
69    The class also has responsibility for tracking which of the above is
70    active, filtering out unnecessary changes.  This allows
71    layout::print_source_line and layout::print_annotation_line
72    to simply request a colorization code for *every* character they print,
73    via this class, and have the filtering be done for them here.  */
74 
75 class colorizer
76 {
77  public:
78   colorizer (diagnostic_context *context,
79 	     diagnostic_t diagnostic_kind);
80   ~colorizer ();
81 
set_range(int range_idx)82   void set_range (int range_idx) { set_state (range_idx); }
set_normal_text()83   void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
set_fixit_insert()84   void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
set_fixit_delete()85   void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
86 
87  private:
88   void set_state (int state);
89   void begin_state (int state);
90   void finish_state (int state);
91   const char *get_color_by_name (const char *);
92 
93  private:
94   static const int STATE_NORMAL_TEXT = -1;
95   static const int STATE_FIXIT_INSERT  = -2;
96   static const int STATE_FIXIT_DELETE  = -3;
97 
98   diagnostic_context *m_context;
99   diagnostic_t m_diagnostic_kind;
100   int m_current_state;
101   const char *m_range1;
102   const char *m_range2;
103   const char *m_fixit_insert;
104   const char *m_fixit_delete;
105   const char *m_stop_color;
106 };
107 
108 /* A point within a layout_range; similar to an expanded_location,
109    but after filtering on file.  */
110 
111 class layout_point
112 {
113  public:
layout_point(const expanded_location & exploc)114   layout_point (const expanded_location &exploc)
115   : m_line (exploc.line),
116     m_column (exploc.column) {}
117 
118   linenum_type m_line;
119   int m_column;
120 };
121 
122 /* A class for use by "class layout" below: a filtered location_range.  */
123 
124 class layout_range
125 {
126  public:
127   layout_range (const expanded_location *start_exploc,
128 		const expanded_location *finish_exploc,
129 		enum range_display_kind range_display_kind,
130 		const expanded_location *caret_exploc,
131 		unsigned original_idx,
132 		const range_label *label);
133 
134   bool contains_point (linenum_type row, int column) const;
135   bool intersects_line_p (linenum_type row) const;
136 
137   layout_point m_start;
138   layout_point m_finish;
139   enum range_display_kind m_range_display_kind;
140   layout_point m_caret;
141   unsigned m_original_idx;
142   const range_label *m_label;
143 };
144 
145 /* A struct for use by layout::print_source_line for telling
146    layout::print_annotation_line the extents of the source line that
147    it printed, so that underlines can be clipped appropriately.  */
148 
149 struct line_bounds
150 {
151   int m_first_non_ws;
152   int m_last_non_ws;
153 };
154 
155 /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
156    or "line 23").  During the layout ctor, layout::calculate_line_spans
157    splits the pertinent source lines into a list of disjoint line_span
158    instances (e.g. lines 5-10, lines 15-20, line 23).  */
159 
160 struct line_span
161 {
line_spanline_span162   line_span (linenum_type first_line, linenum_type last_line)
163     : m_first_line (first_line), m_last_line (last_line)
164   {
165     gcc_assert (first_line <= last_line);
166   }
get_first_lineline_span167   linenum_type get_first_line () const { return m_first_line; }
get_last_lineline_span168   linenum_type get_last_line () const { return m_last_line; }
169 
contains_line_pline_span170   bool contains_line_p (linenum_type line) const
171   {
172     return line >= m_first_line && line <= m_last_line;
173   }
174 
comparatorline_span175   static int comparator (const void *p1, const void *p2)
176   {
177     const line_span *ls1 = (const line_span *)p1;
178     const line_span *ls2 = (const line_span *)p2;
179     int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
180     if (first_line_cmp)
181       return first_line_cmp;
182     return compare (ls1->m_last_line, ls2->m_last_line);
183   }
184 
185   linenum_type m_first_line;
186   linenum_type m_last_line;
187 };
188 
189 #if CHECKING_P
190 
191 /* Selftests for line_span.  */
192 
193 static void
test_line_span()194 test_line_span ()
195 {
196   line_span line_one (1, 1);
197   ASSERT_EQ (1, line_one.get_first_line ());
198   ASSERT_EQ (1, line_one.get_last_line ());
199   ASSERT_FALSE (line_one.contains_line_p (0));
200   ASSERT_TRUE (line_one.contains_line_p (1));
201   ASSERT_FALSE (line_one.contains_line_p (2));
202 
203   line_span lines_1_to_3 (1, 3);
204   ASSERT_EQ (1, lines_1_to_3.get_first_line ());
205   ASSERT_EQ (3, lines_1_to_3.get_last_line ());
206   ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
207   ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
208 
209   ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
210   ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
211   ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
212 
213   /* A linenum > 2^31.  */
214   const linenum_type LARGEST_LINE = 0xffffffff;
215   line_span largest_line (LARGEST_LINE, LARGEST_LINE);
216   ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
217   ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
218 
219   ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
220   ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
221 }
222 
223 #endif /* #if CHECKING_P */
224 
225 /* A class to control the overall layout when printing a diagnostic.
226 
227    The layout is determined within the constructor.
228    It is then printed by repeatedly calling the "print_source_line",
229    "print_annotation_line" and "print_any_fixits" methods.
230 
231    We assume we have disjoint ranges.  */
232 
233 class layout
234 {
235  public:
236   layout (diagnostic_context *context,
237 	  rich_location *richloc,
238 	  diagnostic_t diagnostic_kind);
239 
240   bool maybe_add_location_range (const location_range *loc_range,
241 				 unsigned original_idx,
242 				 bool restrict_to_current_line_spans);
243 
get_num_line_spans()244   int get_num_line_spans () const { return m_line_spans.length (); }
get_line_span(int idx)245   const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
246 
247   void print_gap_in_line_numbering ();
248   bool print_heading_for_line_span_index_p (int line_span_idx) const;
249 
250   expanded_location get_expanded_location (const line_span *) const;
251 
252   void print_line (linenum_type row);
253 
254  private:
255   bool will_show_line_p (linenum_type row) const;
256   void print_leading_fixits (linenum_type row);
257   void print_source_line (linenum_type row, const char *line, int line_width,
258 			  line_bounds *lbounds_out);
259   bool should_print_annotation_line_p (linenum_type row) const;
260   void start_annotation_line (char margin_char = ' ') const;
261   void print_annotation_line (linenum_type row, const line_bounds lbounds);
262   void print_any_labels (linenum_type row);
263   void print_trailing_fixits (linenum_type row);
264 
265   bool annotation_line_showed_range_p (linenum_type line, int start_column,
266 				       int finish_column) const;
267   void show_ruler (int max_column) const;
268 
269   bool validate_fixit_hint_p (const fixit_hint *hint);
270 
271   void calculate_line_spans ();
272 
273   void print_newline ();
274 
275   bool
276   get_state_at_point (/* Inputs.  */
277 		      linenum_type row, int column,
278 		      int first_non_ws, int last_non_ws,
279 		      /* Outputs.  */
280 		      point_state *out_state);
281 
282   int
283   get_x_bound_for_row (linenum_type row, int caret_column,
284 		       int last_non_ws);
285 
286   void
287   move_to_column (int *column, int dest_column, bool add_left_margin);
288 
289  private:
290   diagnostic_context *m_context;
291   pretty_printer *m_pp;
292   location_t m_primary_loc;
293   expanded_location m_exploc;
294   colorizer m_colorizer;
295   bool m_colorize_source_p;
296   bool m_show_labels_p;
297   bool m_show_line_numbers_p;
298   auto_vec <layout_range> m_layout_ranges;
299   auto_vec <const fixit_hint *> m_fixit_hints;
300   auto_vec <line_span> m_line_spans;
301   int m_linenum_width;
302   int m_x_offset;
303 };
304 
305 /* Implementation of "class colorizer".  */
306 
307 /* The constructor for "colorizer".  Lookup and store color codes for the
308    different kinds of things we might need to print.  */
309 
colorizer(diagnostic_context * context,diagnostic_t diagnostic_kind)310 colorizer::colorizer (diagnostic_context *context,
311 		      diagnostic_t diagnostic_kind) :
312   m_context (context),
313   m_diagnostic_kind (diagnostic_kind),
314   m_current_state (STATE_NORMAL_TEXT)
315 {
316   m_range1 = get_color_by_name ("range1");
317   m_range2 = get_color_by_name ("range2");
318   m_fixit_insert = get_color_by_name ("fixit-insert");
319   m_fixit_delete = get_color_by_name ("fixit-delete");
320   m_stop_color = colorize_stop (pp_show_color (context->printer));
321 }
322 
323 /* The destructor for "colorize".  If colorization is on, print a code to
324    turn it off.  */
325 
~colorizer()326 colorizer::~colorizer ()
327 {
328   finish_state (m_current_state);
329 }
330 
331 /* Update state, printing color codes if necessary if there's a state
332    change.  */
333 
334 void
set_state(int new_state)335 colorizer::set_state (int new_state)
336 {
337   if (m_current_state != new_state)
338     {
339       finish_state (m_current_state);
340       m_current_state = new_state;
341       begin_state (new_state);
342     }
343 }
344 
345 /* Turn on any colorization for STATE.  */
346 
347 void
begin_state(int state)348 colorizer::begin_state (int state)
349 {
350   switch (state)
351     {
352     case STATE_NORMAL_TEXT:
353       break;
354 
355     case STATE_FIXIT_INSERT:
356       pp_string (m_context->printer, m_fixit_insert);
357       break;
358 
359     case STATE_FIXIT_DELETE:
360       pp_string (m_context->printer, m_fixit_delete);
361       break;
362 
363     case 0:
364       /* Make range 0 be the same color as the "kind" text
365 	 (error vs warning vs note).  */
366       pp_string
367 	(m_context->printer,
368 	 colorize_start (pp_show_color (m_context->printer),
369 			 diagnostic_get_color_for_kind (m_diagnostic_kind)));
370       break;
371 
372     case 1:
373       pp_string (m_context->printer, m_range1);
374       break;
375 
376     case 2:
377       pp_string (m_context->printer, m_range2);
378       break;
379 
380     default:
381       /* For ranges beyond 2, alternate between color 1 and color 2.  */
382       {
383 	gcc_assert (state > 2);
384 	pp_string (m_context->printer,
385 		   state % 2 ? m_range1 : m_range2);
386       }
387       break;
388     }
389 }
390 
391 /* Turn off any colorization for STATE.  */
392 
393 void
finish_state(int state)394 colorizer::finish_state (int state)
395 {
396   if (state != STATE_NORMAL_TEXT)
397     pp_string (m_context->printer, m_stop_color);
398 }
399 
400 /* Get the color code for NAME (or the empty string if
401    colorization is disabled).  */
402 
403 const char *
get_color_by_name(const char * name)404 colorizer::get_color_by_name (const char *name)
405 {
406   return colorize_start (pp_show_color (m_context->printer), name);
407 }
408 
409 /* Implementation of class layout_range.  */
410 
411 /* The constructor for class layout_range.
412    Initialize various layout_point fields from expanded_location
413    equivalents; we've already filtered on file.  */
414 
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)415 layout_range::layout_range (const expanded_location *start_exploc,
416 			    const expanded_location *finish_exploc,
417 			    enum range_display_kind range_display_kind,
418 			    const expanded_location *caret_exploc,
419 			    unsigned original_idx,
420 			    const range_label *label)
421 : m_start (*start_exploc),
422   m_finish (*finish_exploc),
423   m_range_display_kind (range_display_kind),
424   m_caret (*caret_exploc),
425   m_original_idx (original_idx),
426   m_label (label)
427 {
428 }
429 
430 /* Is (column, row) within the given range?
431    We've already filtered on the file.
432 
433    Ranges are closed (both limits are within the range).
434 
435    Example A: a single-line range:
436      start:  (col=22, line=2)
437      finish: (col=38, line=2)
438 
439   |00000011111111112222222222333333333344444444444
440   |34567890123456789012345678901234567890123456789
441 --+-----------------------------------------------
442 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
443 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
444 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
445 
446    Example B: a multiline range with
447      start:  (col=14, line=3)
448      finish: (col=08, line=5)
449 
450   |00000011111111112222222222333333333344444444444
451   |34567890123456789012345678901234567890123456789
452 --+-----------------------------------------------
453 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
454 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
455 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
456 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
457 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
458 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
459 --+-----------------------------------------------
460 
461    Legend:
462    - 'b' indicates a point *before* the range
463    - 'S' indicates the start of the range
464    - 'w' indicates a point within the range
465    - 'F' indicates the finish of the range (which is
466 	 within it).
467    - 'a' indicates a subsequent point *after* the range.  */
468 
469 bool
contains_point(linenum_type row,int column)470 layout_range::contains_point (linenum_type row, int column) const
471 {
472   gcc_assert (m_start.m_line <= m_finish.m_line);
473   /* ...but the equivalent isn't true for the columns;
474      consider example B in the comment above.  */
475 
476   if (row < m_start.m_line)
477     /* Points before the first line of the range are
478        outside it (corresponding to line 01 in example A
479        and lines 01 and 02 in example B above).  */
480     return false;
481 
482   if (row == m_start.m_line)
483     /* On same line as start of range (corresponding
484        to line 02 in example A and line 03 in example B).  */
485     {
486       if (column < m_start.m_column)
487 	/* Points on the starting line of the range, but
488 	   before the column in which it begins.  */
489 	return false;
490 
491       if (row < m_finish.m_line)
492 	/* This is a multiline range; the point
493 	   is within it (corresponds to line 03 in example B
494 	   from column 14 onwards) */
495 	return true;
496       else
497 	{
498 	  /* This is a single-line range.  */
499 	  gcc_assert (row == m_finish.m_line);
500 	  return column <= m_finish.m_column;
501 	}
502     }
503 
504   /* The point is in a line beyond that containing the
505      start of the range: lines 03 onwards in example A,
506      and lines 04 onwards in example B.  */
507   gcc_assert (row > m_start.m_line);
508 
509   if (row > m_finish.m_line)
510     /* The point is beyond the final line of the range
511        (lines 03 onwards in example A, and lines 06 onwards
512        in example B).  */
513     return false;
514 
515   if (row < m_finish.m_line)
516     {
517       /* The point is in a line that's fully within a multiline
518 	 range (e.g. line 04 in example B).  */
519       gcc_assert (m_start.m_line < m_finish.m_line);
520       return true;
521     }
522 
523   gcc_assert (row ==  m_finish.m_line);
524 
525   return column <= m_finish.m_column;
526 }
527 
528 /* Does this layout_range contain any part of line ROW?  */
529 
530 bool
intersects_line_p(linenum_type row)531 layout_range::intersects_line_p (linenum_type row) const
532 {
533   gcc_assert (m_start.m_line <= m_finish.m_line);
534   if (row < m_start.m_line)
535     return false;
536   if (row > m_finish.m_line)
537     return false;
538   return true;
539 }
540 
541 #if CHECKING_P
542 
543 /* A helper function for testing layout_range.  */
544 
545 static layout_range
make_range(int start_line,int start_col,int end_line,int end_col)546 make_range (int start_line, int start_col, int end_line, int end_col)
547 {
548   const expanded_location start_exploc
549     = {"test.c", start_line, start_col, NULL, false};
550   const expanded_location finish_exploc
551     = {"test.c", end_line, end_col, NULL, false};
552   return layout_range (&start_exploc, &finish_exploc, SHOW_RANGE_WITHOUT_CARET,
553 		       &start_exploc, 0, NULL);
554 }
555 
556 /* Selftests for layout_range::contains_point and
557    layout_range::intersects_line_p.  */
558 
559 /* Selftest for layout_range, where the layout_range
560    is a range with start==end i.e. a single point.  */
561 
562 static void
test_layout_range_for_single_point()563 test_layout_range_for_single_point ()
564 {
565   layout_range point = make_range (7, 10, 7, 10);
566 
567   /* Tests for layout_range::contains_point.  */
568 
569   /* Before the line. */
570   ASSERT_FALSE (point.contains_point (6, 1));
571 
572   /* On the line, but before start.  */
573   ASSERT_FALSE (point.contains_point (7, 9));
574 
575   /* At the point.  */
576   ASSERT_TRUE (point.contains_point (7, 10));
577 
578   /* On the line, after the point.  */
579   ASSERT_FALSE (point.contains_point (7, 11));
580 
581   /* After the line.  */
582   ASSERT_FALSE (point.contains_point (8, 1));
583 
584   /* Tests for layout_range::intersects_line_p.  */
585   ASSERT_FALSE (point.intersects_line_p (6));
586   ASSERT_TRUE (point.intersects_line_p (7));
587   ASSERT_FALSE (point.intersects_line_p (8));
588 }
589 
590 /* Selftest for layout_range, where the layout_range
591    is the single-line range shown as "Example A" above.  */
592 
593 static void
test_layout_range_for_single_line()594 test_layout_range_for_single_line ()
595 {
596   layout_range example_a = make_range (2, 22, 2, 38);
597 
598   /* Tests for layout_range::contains_point.  */
599 
600   /* Before the line. */
601   ASSERT_FALSE (example_a.contains_point (1, 1));
602 
603   /* On the line, but before start.  */
604   ASSERT_FALSE (example_a.contains_point (2, 21));
605 
606   /* On the line, at the start.  */
607   ASSERT_TRUE (example_a.contains_point (2, 22));
608 
609   /* On the line, within the range.  */
610   ASSERT_TRUE (example_a.contains_point (2, 23));
611 
612   /* On the line, at the end.  */
613   ASSERT_TRUE (example_a.contains_point (2, 38));
614 
615   /* On the line, after the end.  */
616   ASSERT_FALSE (example_a.contains_point (2, 39));
617 
618   /* After the line.  */
619   ASSERT_FALSE (example_a.contains_point (2, 39));
620 
621   /* Tests for layout_range::intersects_line_p.  */
622   ASSERT_FALSE (example_a.intersects_line_p (1));
623   ASSERT_TRUE (example_a.intersects_line_p (2));
624   ASSERT_FALSE (example_a.intersects_line_p (3));
625 }
626 
627 /* Selftest for layout_range, where the layout_range
628    is the multi-line range shown as "Example B" above.  */
629 
630 static void
test_layout_range_for_multiple_lines()631 test_layout_range_for_multiple_lines ()
632 {
633   layout_range example_b = make_range (3, 14, 5, 8);
634 
635   /* Tests for layout_range::contains_point.  */
636 
637   /* Before first line. */
638   ASSERT_FALSE (example_b.contains_point (1, 1));
639 
640   /* On the first line, but before start.  */
641   ASSERT_FALSE (example_b.contains_point (3, 13));
642 
643   /* At the start.  */
644   ASSERT_TRUE (example_b.contains_point (3, 14));
645 
646   /* On the first line, within the range.  */
647   ASSERT_TRUE (example_b.contains_point (3, 15));
648 
649   /* On an interior line.
650      The column number should not matter; try various boundary
651      values.  */
652   ASSERT_TRUE (example_b.contains_point (4, 1));
653   ASSERT_TRUE (example_b.contains_point (4, 7));
654   ASSERT_TRUE (example_b.contains_point (4, 8));
655   ASSERT_TRUE (example_b.contains_point (4, 9));
656   ASSERT_TRUE (example_b.contains_point (4, 13));
657   ASSERT_TRUE (example_b.contains_point (4, 14));
658   ASSERT_TRUE (example_b.contains_point (4, 15));
659 
660   /* On the final line, before the end.  */
661   ASSERT_TRUE (example_b.contains_point (5, 7));
662 
663   /* On the final line, at the end.  */
664   ASSERT_TRUE (example_b.contains_point (5, 8));
665 
666   /* On the final line, after the end.  */
667   ASSERT_FALSE (example_b.contains_point (5, 9));
668 
669   /* After the line.  */
670   ASSERT_FALSE (example_b.contains_point (6, 1));
671 
672   /* Tests for layout_range::intersects_line_p.  */
673   ASSERT_FALSE (example_b.intersects_line_p (2));
674   ASSERT_TRUE (example_b.intersects_line_p (3));
675   ASSERT_TRUE (example_b.intersects_line_p (4));
676   ASSERT_TRUE (example_b.intersects_line_p (5));
677   ASSERT_FALSE (example_b.intersects_line_p (6));
678 }
679 
680 #endif /* #if CHECKING_P */
681 
682 /* Given a source line LINE of length LINE_WIDTH, determine the width
683    without any trailing whitespace.  */
684 
685 static int
get_line_width_without_trailing_whitespace(const char * line,int line_width)686 get_line_width_without_trailing_whitespace (const char *line, int line_width)
687 {
688   int result = line_width;
689   while (result > 0)
690     {
691       char ch = line[result - 1];
692       if (ch == ' ' || ch == '\t' || ch == '\r')
693 	result--;
694       else
695 	break;
696     }
697   gcc_assert (result >= 0);
698   gcc_assert (result <= line_width);
699   gcc_assert (result == 0 ||
700 	      (line[result - 1] != ' '
701 	       && line[result -1] != '\t'
702 	       && line[result -1] != '\r'));
703   return result;
704 }
705 
706 #if CHECKING_P
707 
708 /* A helper function for testing get_line_width_without_trailing_whitespace.  */
709 
710 static void
assert_eq(const char * line,int expected_width)711 assert_eq (const char *line, int expected_width)
712 {
713   int actual_value
714     = get_line_width_without_trailing_whitespace (line, strlen (line));
715   ASSERT_EQ (actual_value, expected_width);
716 }
717 
718 /* Verify that get_line_width_without_trailing_whitespace is sane for
719    various inputs.  It is not required to handle newlines.  */
720 
721 static void
test_get_line_width_without_trailing_whitespace()722 test_get_line_width_without_trailing_whitespace ()
723 {
724   assert_eq ("", 0);
725   assert_eq (" ", 0);
726   assert_eq ("\t", 0);
727   assert_eq ("\r", 0);
728   assert_eq ("hello world", 11);
729   assert_eq ("hello world     ", 11);
730   assert_eq ("hello world     \t\t  ", 11);
731   assert_eq ("hello world\r", 11);
732 }
733 
734 #endif /* #if CHECKING_P */
735 
736 /* Helper function for layout's ctor, for sanitizing locations relative
737    to the primary location within a diagnostic.
738 
739    Compare LOC_A and LOC_B to see if it makes sense to print underlines
740    connecting their expanded locations.  Doing so is only guaranteed to
741    make sense if the locations share the same macro expansion "history"
742    i.e. they can be traced through the same macro expansions, eventually
743    reaching an ordinary map.
744 
745    This may be too strong a condition, but it effectively sanitizes
746    PR c++/70105, which has an example of printing an expression where the
747    final location of the expression is in a different macro, which
748    erroneously was leading to hundreds of lines of irrelevant source
749    being printed.  */
750 
751 static bool
compatible_locations_p(location_t loc_a,location_t loc_b)752 compatible_locations_p (location_t loc_a, location_t loc_b)
753 {
754   if (IS_ADHOC_LOC (loc_a))
755     loc_a = get_location_from_adhoc_loc (line_table, loc_a);
756   if (IS_ADHOC_LOC (loc_b))
757     loc_b = get_location_from_adhoc_loc (line_table, loc_b);
758 
759   /* If either location is one of the special locations outside of a
760      linemap, they are only compatible if they are equal.  */
761   if (loc_a < RESERVED_LOCATION_COUNT
762       || loc_b < RESERVED_LOCATION_COUNT)
763     return loc_a == loc_b;
764 
765   const line_map *map_a = linemap_lookup (line_table, loc_a);
766   linemap_assert (map_a);
767 
768   const line_map *map_b = linemap_lookup (line_table, loc_b);
769   linemap_assert (map_b);
770 
771   /* Are they within the same map?  */
772   if (map_a == map_b)
773     {
774       /* Are both within the same macro expansion?  */
775       if (linemap_macro_expansion_map_p (map_a))
776 	{
777 	  /* Expand each location towards the spelling location, and
778 	     recurse.  */
779 	  const line_map_macro *macro_map = linemap_check_macro (map_a);
780 	  location_t loc_a_toward_spelling
781 	    = linemap_macro_map_loc_unwind_toward_spelling (line_table,
782 							    macro_map,
783 							    loc_a);
784 	  location_t loc_b_toward_spelling
785 	    = linemap_macro_map_loc_unwind_toward_spelling (line_table,
786 							    macro_map,
787 							    loc_b);
788 	  return compatible_locations_p (loc_a_toward_spelling,
789 					 loc_b_toward_spelling);
790 	}
791 
792       /* Otherwise they are within the same ordinary map.  */
793       return true;
794     }
795   else
796     {
797       /* Within different maps.  */
798 
799       /* If either is within a macro expansion, they are incompatible.  */
800       if (linemap_macro_expansion_map_p (map_a)
801 	  || linemap_macro_expansion_map_p (map_b))
802 	return false;
803 
804       /* Within two different ordinary maps; they are compatible iff they
805 	 are in the same file.  */
806       const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
807       const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
808       return ord_map_a->to_file == ord_map_b->to_file;
809     }
810 }
811 
812 /* Comparator for sorting fix-it hints.  */
813 
814 static int
fixit_cmp(const void * p_a,const void * p_b)815 fixit_cmp (const void *p_a, const void *p_b)
816 {
817   const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
818   const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
819   return hint_a->get_start_loc () - hint_b->get_start_loc ();
820 }
821 
822 /* Implementation of class layout.  */
823 
824 /* Constructor for class layout.
825 
826    Filter the ranges from the rich_location to those that we can
827    sanely print, populating m_layout_ranges and m_fixit_hints.
828    Determine the range of lines that we will print, splitting them
829    up into an ordered list of disjoint spans of contiguous line numbers.
830    Determine m_x_offset, to ensure that the primary caret
831    will fit within the max_width provided by the diagnostic_context.  */
832 
layout(diagnostic_context * context,rich_location * richloc,diagnostic_t diagnostic_kind)833 layout::layout (diagnostic_context * context,
834 		rich_location *richloc,
835 		diagnostic_t diagnostic_kind)
836 : m_context (context),
837   m_pp (context->printer),
838   m_primary_loc (richloc->get_range (0)->m_loc),
839   m_exploc (richloc->get_expanded_location (0)),
840   m_colorizer (context, diagnostic_kind),
841   m_colorize_source_p (context->colorize_source_p),
842   m_show_labels_p (context->show_labels_p),
843   m_show_line_numbers_p (context->show_line_numbers_p),
844   m_layout_ranges (richloc->get_num_locations ()),
845   m_fixit_hints (richloc->get_num_fixit_hints ()),
846   m_line_spans (1 + richloc->get_num_locations ()),
847   m_linenum_width (0),
848   m_x_offset (0)
849 {
850   for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
851     {
852       /* This diagnostic printer can only cope with "sufficiently sane" ranges.
853 	 Ignore any ranges that are awkward to handle.  */
854       const location_range *loc_range = richloc->get_range (idx);
855       maybe_add_location_range (loc_range, idx, false);
856     }
857 
858   /* Populate m_fixit_hints, filtering to only those that are in the
859      same file.  */
860   for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
861     {
862       const fixit_hint *hint = richloc->get_fixit_hint (i);
863       if (validate_fixit_hint_p (hint))
864 	m_fixit_hints.safe_push (hint);
865     }
866 
867   /* Sort m_fixit_hints.  */
868   m_fixit_hints.qsort (fixit_cmp);
869 
870   /* Populate m_line_spans.  */
871   calculate_line_spans ();
872 
873   /* Determine m_linenum_width.  */
874   gcc_assert (m_line_spans.length () > 0);
875   const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
876   int highest_line = last_span->m_last_line;
877   if (highest_line < 0)
878     highest_line = 0;
879   m_linenum_width = num_digits (highest_line);
880   /* If we're showing jumps in the line-numbering, allow at least 3 chars.  */
881   if (m_line_spans.length () > 1)
882     m_linenum_width = MAX (m_linenum_width, 3);
883   /* If there's a minimum margin width, apply it (subtracting 1 for the space
884      after the line number.  */
885   m_linenum_width = MAX (m_linenum_width, context->min_margin_width - 1);
886 
887   /* Adjust m_x_offset.
888      Center the primary caret to fit in max_width; all columns
889      will be adjusted accordingly.  */
890   size_t max_width = m_context->caret_max_width;
891   char_span line = location_get_source_line (m_exploc.file, m_exploc.line);
892   if (line && (size_t)m_exploc.column <= line.length ())
893     {
894       size_t right_margin = CARET_LINE_MARGIN;
895       size_t column = m_exploc.column;
896       if (m_show_line_numbers_p)
897 	column += m_linenum_width + 2;
898       right_margin = MIN (line.length () - column, right_margin);
899       right_margin = max_width - right_margin;
900       if (line.length () >= max_width && column > right_margin)
901 	m_x_offset = column - right_margin;
902       gcc_assert (m_x_offset >= 0);
903     }
904 
905   if (context->show_ruler_p)
906     show_ruler (m_x_offset + max_width);
907 }
908 
909 /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
910    those that we can sanely print.
911 
912    ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
913    (for use as extrinsic state by label ranges FIXME).
914 
915    If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
916    filtered against this layout instance's current line spans: it
917    will only be added if the location is fully within the lines
918    already specified by other locations.
919 
920    Return true iff LOC_RANGE was added.  */
921 
922 bool
maybe_add_location_range(const location_range * loc_range,unsigned original_idx,bool restrict_to_current_line_spans)923 layout::maybe_add_location_range (const location_range *loc_range,
924 				  unsigned original_idx,
925 				  bool restrict_to_current_line_spans)
926 {
927   gcc_assert (loc_range);
928 
929   /* Split the "range" into caret and range information.  */
930   source_range src_range = get_range_from_loc (line_table, loc_range->m_loc);
931 
932   /* Expand the various locations.  */
933   expanded_location start
934     = linemap_client_expand_location_to_spelling_point
935     (src_range.m_start, LOCATION_ASPECT_START);
936   expanded_location finish
937     = linemap_client_expand_location_to_spelling_point
938     (src_range.m_finish, LOCATION_ASPECT_FINISH);
939   expanded_location caret
940     = linemap_client_expand_location_to_spelling_point
941     (loc_range->m_loc, LOCATION_ASPECT_CARET);
942 
943   /* If any part of the range isn't in the same file as the primary
944      location of this diagnostic, ignore the range.  */
945   if (start.file != m_exploc.file)
946     return false;
947   if (finish.file != m_exploc.file)
948     return false;
949   if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
950     if (caret.file != m_exploc.file)
951       return false;
952 
953   /* Sanitize the caret location for non-primary ranges.  */
954   if (m_layout_ranges.length () > 0)
955     if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
956       if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
957 	/* Discard any non-primary ranges that can't be printed
958 	   sanely relative to the primary location.  */
959 	return false;
960 
961   /* Everything is now known to be in the correct source file,
962      but it may require further sanitization.  */
963   layout_range ri (&start, &finish, loc_range->m_range_display_kind, &caret,
964 		   original_idx, loc_range->m_label);
965 
966   /* If we have a range that finishes before it starts (perhaps
967      from something built via macro expansion), printing the
968      range is likely to be nonsensical.  Also, attempting to do so
969      breaks assumptions within the printing code  (PR c/68473).
970      Similarly, don't attempt to print ranges if one or both ends
971      of the range aren't sane to print relative to the
972      primary location (PR c++/70105).  */
973   if (start.line > finish.line
974       || !compatible_locations_p (src_range.m_start, m_primary_loc)
975       || !compatible_locations_p (src_range.m_finish, m_primary_loc))
976     {
977       /* Is this the primary location?  */
978       if (m_layout_ranges.length () == 0)
979 	{
980 	  /* We want to print the caret for the primary location, but
981 	     we must sanitize away m_start and m_finish.  */
982 	  ri.m_start = ri.m_caret;
983 	  ri.m_finish = ri.m_caret;
984 	}
985       else
986 	/* This is a non-primary range; ignore it.  */
987 	return false;
988     }
989 
990   /* Potentially filter to just the lines already specified by other
991      locations.  This is for use by gcc_rich_location::add_location_if_nearby.
992      The layout ctor doesn't use it, and can't because m_line_spans
993      hasn't been set up at that point.  */
994   if (restrict_to_current_line_spans)
995     {
996       if (!will_show_line_p (start.line))
997 	return false;
998       if (!will_show_line_p (finish.line))
999 	return false;
1000       if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
1001 	if (!will_show_line_p (caret.line))
1002 	  return false;
1003     }
1004 
1005   /* Passed all the tests; add the range to m_layout_ranges so that
1006      it will be printed.  */
1007   m_layout_ranges.safe_push (ri);
1008   return true;
1009 }
1010 
1011 /* Return true iff ROW is within one of the line spans for this layout.  */
1012 
1013 bool
will_show_line_p(linenum_type row)1014 layout::will_show_line_p (linenum_type row) const
1015 {
1016   for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
1017        line_span_idx++)
1018     {
1019       const line_span *line_span = get_line_span (line_span_idx);
1020       if (line_span->contains_line_p (row))
1021 	return true;
1022     }
1023   return false;
1024 }
1025 
1026 /* Print a line showing a gap in the line numbers, for showing the boundary
1027    between two line spans.  */
1028 
1029 void
print_gap_in_line_numbering()1030 layout::print_gap_in_line_numbering ()
1031 {
1032   gcc_assert (m_show_line_numbers_p);
1033 
1034   for (int i = 0; i < m_linenum_width + 1; i++)
1035     pp_character (m_pp, '.');
1036 
1037   pp_newline (m_pp);
1038 }
1039 
1040 /* Return true iff we should print a heading when starting the
1041    line span with the given index.  */
1042 
1043 bool
print_heading_for_line_span_index_p(int line_span_idx)1044 layout::print_heading_for_line_span_index_p (int line_span_idx) const
1045 {
1046   /* We print a heading for every change of line span, hence for every
1047      line span after the initial one.  */
1048   if (line_span_idx > 0)
1049     return true;
1050 
1051   /* We also do it for the initial span if the primary location of the
1052      diagnostic is in a different span.  */
1053   if (m_exploc.line > (int)get_line_span (0)->m_last_line)
1054     return true;
1055 
1056   return false;
1057 }
1058 
1059 /* Get an expanded_location for the first location of interest within
1060    the given line_span.
1061    Used when printing a heading to indicate a new line span.  */
1062 
1063 expanded_location
get_expanded_location(const line_span * line_span)1064 layout::get_expanded_location (const line_span *line_span) const
1065 {
1066   /* Whenever possible, use the caret location.  */
1067   if (line_span->contains_line_p (m_exploc.line))
1068     return m_exploc;
1069 
1070   /* Otherwise, use the start of the first range that's present
1071      within the line_span.  */
1072   for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1073     {
1074       const layout_range *lr = &m_layout_ranges[i];
1075       if (line_span->contains_line_p (lr->m_start.m_line))
1076 	{
1077 	  expanded_location exploc = m_exploc;
1078 	  exploc.line = lr->m_start.m_line;
1079 	  exploc.column = lr->m_start.m_column;
1080 	  return exploc;
1081 	}
1082     }
1083 
1084   /* Otherwise, use the location of the first fixit-hint present within
1085      the line_span.  */
1086   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1087     {
1088       const fixit_hint *hint = m_fixit_hints[i];
1089       location_t loc = hint->get_start_loc ();
1090       expanded_location exploc = expand_location (loc);
1091       if (line_span->contains_line_p (exploc.line))
1092 	return exploc;
1093     }
1094 
1095   /* It should not be possible to have a line span that didn't
1096      contain any of the layout_range or fixit_hint instances.  */
1097   gcc_unreachable ();
1098   return m_exploc;
1099 }
1100 
1101 /* Determine if HINT is meaningful to print within this layout.  */
1102 
1103 bool
validate_fixit_hint_p(const fixit_hint * hint)1104 layout::validate_fixit_hint_p (const fixit_hint *hint)
1105 {
1106   if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
1107     return false;
1108   if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
1109     return false;
1110 
1111   return true;
1112 }
1113 
1114 /* Determine the range of lines affected by HINT.
1115    This assumes that HINT has already been filtered by
1116    validate_fixit_hint_p, and so affects the correct source file.  */
1117 
1118 static line_span
get_line_span_for_fixit_hint(const fixit_hint * hint)1119 get_line_span_for_fixit_hint (const fixit_hint *hint)
1120 {
1121   gcc_assert (hint);
1122 
1123   int start_line = LOCATION_LINE (hint->get_start_loc ());
1124 
1125   /* For line-insertion fix-it hints, add the previous line to the
1126      span, to give the user more context on the proposed change.  */
1127   if (hint->ends_with_newline_p ())
1128     if (start_line > 1)
1129       start_line--;
1130 
1131   return line_span (start_line,
1132 		    LOCATION_LINE (hint->get_next_loc ()));
1133 }
1134 
1135 /* We want to print the pertinent source code at a diagnostic.  The
1136    rich_location can contain multiple locations.  This will have been
1137    filtered into m_exploc (the caret for the primary location) and
1138    m_layout_ranges, for those ranges within the same source file.
1139 
1140    We will print a subset of the lines within the source file in question,
1141    as a collection of "spans" of lines.
1142 
1143    This function populates m_line_spans with an ordered, disjoint list of
1144    the line spans of interest.
1145 
1146    Printing a gap between line spans takes one line, so, when printing
1147    line numbers, we allow a gap of up to one line between spans when
1148    merging, since it makes more sense to print the source line rather than a
1149    "gap-in-line-numbering" line.  When not printing line numbers, it's
1150    better to be more explicit about what's going on, so keeping them as
1151    separate spans is preferred.
1152 
1153    For example, if the primary range is on lines 8-10, with secondary ranges
1154    covering lines 5-6 and lines 13-15:
1155 
1156      004
1157      005                   |RANGE 1
1158      006                   |RANGE 1
1159      007
1160      008  |PRIMARY RANGE
1161      009  |PRIMARY CARET
1162      010  |PRIMARY RANGE
1163      011
1164      012
1165      013                                |RANGE 2
1166      014                                |RANGE 2
1167      015                                |RANGE 2
1168      016
1169 
1170    With line numbering on, we want two spans: lines 5-10 and lines 13-15.
1171 
1172    With line numbering off (with span headers), we want three spans: lines 5-6,
1173    lines 8-10, and lines 13-15.  */
1174 
1175 void
calculate_line_spans()1176 layout::calculate_line_spans ()
1177 {
1178   /* This should only be called once, by the ctor.  */
1179   gcc_assert (m_line_spans.length () == 0);
1180 
1181   /* Populate tmp_spans with individual spans, for each of
1182      m_exploc, and for m_layout_ranges.  */
1183   auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
1184   tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
1185   for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
1186     {
1187       const layout_range *lr = &m_layout_ranges[i];
1188       gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
1189       tmp_spans.safe_push (line_span (lr->m_start.m_line,
1190 				      lr->m_finish.m_line));
1191     }
1192 
1193   /* Also add spans for any fix-it hints, in case they cover other lines.  */
1194   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1195     {
1196       const fixit_hint *hint = m_fixit_hints[i];
1197       gcc_assert (hint);
1198       tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
1199     }
1200 
1201   /* Sort them.  */
1202   tmp_spans.qsort(line_span::comparator);
1203 
1204   /* Now iterate through tmp_spans, copying into m_line_spans, and
1205      combining where possible.  */
1206   gcc_assert (tmp_spans.length () > 0);
1207   m_line_spans.safe_push (tmp_spans[0]);
1208   for (unsigned int i = 1; i < tmp_spans.length (); i++)
1209     {
1210       line_span *current = &m_line_spans[m_line_spans.length () - 1];
1211       const line_span *next = &tmp_spans[i];
1212       gcc_assert (next->m_first_line >= current->m_first_line);
1213       const int merger_distance = m_show_line_numbers_p ? 1 : 0;
1214       if ((linenum_arith_t)next->m_first_line
1215 	  <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
1216 	{
1217 	  /* We can merge them. */
1218 	  if (next->m_last_line > current->m_last_line)
1219 	    current->m_last_line = next->m_last_line;
1220 	}
1221       else
1222 	{
1223 	  /* No merger possible.  */
1224 	  m_line_spans.safe_push (*next);
1225 	}
1226     }
1227 
1228   /* Verify the result, in m_line_spans.  */
1229   gcc_assert (m_line_spans.length () > 0);
1230   for (unsigned int i = 1; i < m_line_spans.length (); i++)
1231     {
1232       const line_span *prev = &m_line_spans[i - 1];
1233       const line_span *next = &m_line_spans[i];
1234       /* The individual spans must be sane.  */
1235       gcc_assert (prev->m_first_line <= prev->m_last_line);
1236       gcc_assert (next->m_first_line <= next->m_last_line);
1237       /* The spans must be ordered.  */
1238       gcc_assert (prev->m_first_line < next->m_first_line);
1239       /* There must be a gap of at least one line between separate spans.  */
1240       gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
1241     }
1242 }
1243 
1244 /* Print line ROW of source code, potentially colorized at any ranges, and
1245    populate *LBOUNDS_OUT.
1246    LINE is the source line (not necessarily 0-terminated) and LINE_WIDTH
1247    is its width.  */
1248 
1249 void
print_source_line(linenum_type row,const char * line,int line_width,line_bounds * lbounds_out)1250 layout::print_source_line (linenum_type row, const char *line, int line_width,
1251 			   line_bounds *lbounds_out)
1252 {
1253   m_colorizer.set_normal_text ();
1254 
1255   /* We will stop printing the source line at any trailing
1256      whitespace.  */
1257   line_width = get_line_width_without_trailing_whitespace (line,
1258 							   line_width);
1259   line += m_x_offset;
1260 
1261   if (m_show_line_numbers_p)
1262     {
1263       int width = num_digits (row);
1264       for (int i = 0; i < m_linenum_width - width; i++)
1265 	pp_space (m_pp);
1266       pp_printf (m_pp, "%i | ", row);
1267     }
1268   else
1269     pp_space (m_pp);
1270   int first_non_ws = INT_MAX;
1271   int last_non_ws = 0;
1272   int column;
1273   for (column = 1 + m_x_offset; column <= line_width; column++)
1274     {
1275       /* Assuming colorization is enabled for the caret and underline
1276 	 characters, we may also colorize the associated characters
1277 	 within the source line.
1278 
1279 	 For frontends that generate range information, we color the
1280 	 associated characters in the source line the same as the
1281 	 carets and underlines in the annotation line, to make it easier
1282 	 for the reader to see the pertinent code.
1283 
1284 	 For frontends that only generate carets, we don't colorize the
1285 	 characters above them, since this would look strange (e.g.
1286 	 colorizing just the first character in a token).  */
1287       if (m_colorize_source_p)
1288 	{
1289 	  bool in_range_p;
1290 	  point_state state;
1291 	  in_range_p = get_state_at_point (row, column,
1292 					   0, INT_MAX,
1293 					   &state);
1294 	  if (in_range_p)
1295 	    m_colorizer.set_range (state.range_idx);
1296 	  else
1297 	    m_colorizer.set_normal_text ();
1298 	}
1299       char c = *line;
1300       if (c == '\0' || c == '\t' || c == '\r')
1301 	c = ' ';
1302       if (c != ' ')
1303 	{
1304 	  last_non_ws = column;
1305 	  if (first_non_ws == INT_MAX)
1306 	    first_non_ws = column;
1307 	}
1308       pp_character (m_pp, c);
1309       line++;
1310     }
1311   print_newline ();
1312 
1313   lbounds_out->m_first_non_ws = first_non_ws;
1314   lbounds_out->m_last_non_ws = last_non_ws;
1315 }
1316 
1317 /* Determine if we should print an annotation line for ROW.
1318    i.e. if any of m_layout_ranges contains ROW.  */
1319 
1320 bool
should_print_annotation_line_p(linenum_type row)1321 layout::should_print_annotation_line_p (linenum_type row) const
1322 {
1323   layout_range *range;
1324   int i;
1325   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1326     {
1327       if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
1328 	return false;
1329       if (range->intersects_line_p (row))
1330 	return true;
1331     }
1332   return false;
1333 }
1334 
1335 /* Begin an annotation line.  If m_show_line_numbers_p, print the left
1336    margin, which is empty for annotation lines.  Otherwise, do nothing.  */
1337 
1338 void
start_annotation_line(char margin_char)1339 layout::start_annotation_line (char margin_char) const
1340 {
1341   if (m_show_line_numbers_p)
1342     {
1343       /* Print the margin.  If MARGIN_CHAR != ' ', then print up to 3
1344 	 of it, right-aligned, padded with spaces.  */
1345       int i;
1346       for (i = 0; i < m_linenum_width - 3; i++)
1347 	pp_space (m_pp);
1348       for (; i < m_linenum_width; i++)
1349 	pp_character (m_pp, margin_char);
1350       pp_string (m_pp, " |");
1351     }
1352 }
1353 
1354 /* Print a line consisting of the caret/underlines for the given
1355    source line.  */
1356 
1357 void
print_annotation_line(linenum_type row,const line_bounds lbounds)1358 layout::print_annotation_line (linenum_type row, const line_bounds lbounds)
1359 {
1360   int x_bound = get_x_bound_for_row (row, m_exploc.column,
1361 				     lbounds.m_last_non_ws);
1362 
1363   start_annotation_line ();
1364   pp_space (m_pp);
1365 
1366   for (int column = 1 + m_x_offset; column < x_bound; column++)
1367     {
1368       bool in_range_p;
1369       point_state state;
1370       in_range_p = get_state_at_point (row, column,
1371 				       lbounds.m_first_non_ws,
1372 				       lbounds.m_last_non_ws,
1373 				       &state);
1374       if (in_range_p)
1375 	{
1376 	  /* Within a range.  Draw either the caret or an underline.  */
1377 	  m_colorizer.set_range (state.range_idx);
1378 	  if (state.draw_caret_p)
1379 	    {
1380 	      /* Draw the caret.  */
1381 	      char caret_char;
1382 	      if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
1383 		caret_char = m_context->caret_chars[state.range_idx];
1384 	      else
1385 		caret_char = '^';
1386 	      pp_character (m_pp, caret_char);
1387 	    }
1388 	  else
1389 	    pp_character (m_pp, '~');
1390 	}
1391       else
1392 	{
1393 	  /* Not in a range.  */
1394 	  m_colorizer.set_normal_text ();
1395 	  pp_character (m_pp, ' ');
1396 	}
1397     }
1398   print_newline ();
1399 }
1400 
1401 /* Implementation detail of layout::print_any_labels.
1402 
1403    A label within the given row of source.  */
1404 
1405 struct line_label
1406 {
line_labelline_label1407   line_label (int state_idx, int column, label_text text)
1408   : m_state_idx (state_idx), m_column (column),
1409     m_text (text), m_length (strlen (text.m_buffer)),
1410     m_label_line (0)
1411   {}
1412 
1413   /* Sorting is primarily by column, then by state index.  */
comparatorline_label1414   static int comparator (const void *p1, const void *p2)
1415   {
1416     const line_label *ll1 = (const line_label *)p1;
1417     const line_label *ll2 = (const line_label *)p2;
1418     int column_cmp = compare (ll1->m_column, ll2->m_column);
1419     if (column_cmp)
1420       return column_cmp;
1421     return compare (ll1->m_state_idx, ll2->m_state_idx);
1422   }
1423 
1424   int m_state_idx;
1425   int m_column;
1426   label_text m_text;
1427   size_t m_length;
1428   int m_label_line;
1429 };
1430 
1431 /* Print any labels in this row.  */
1432 void
print_any_labels(linenum_type row)1433 layout::print_any_labels (linenum_type row)
1434 {
1435   int i;
1436   auto_vec<line_label> labels;
1437 
1438   /* Gather the labels that are to be printed into "labels".  */
1439   {
1440     layout_range *range;
1441     FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1442       {
1443 	/* Most ranges don't have labels, so reject this first.  */
1444 	if (range->m_label == NULL)
1445 	  continue;
1446 
1447 	/* The range's caret must be on this line.  */
1448 	if (range->m_caret.m_line != row)
1449 	  continue;
1450 
1451 	/* Reject labels that aren't fully visible due to clipping
1452 	   by m_x_offset.  */
1453 	if (range->m_caret.m_column <= m_x_offset)
1454 	  continue;
1455 
1456 	label_text text;
1457 	text = range->m_label->get_text (range->m_original_idx);
1458 
1459 	/* Allow for labels that return NULL from their get_text
1460 	   implementation (so e.g. such labels can control their own
1461 	   visibility).  */
1462 	if (text.m_buffer == NULL)
1463 	  continue;
1464 
1465 	labels.safe_push (line_label (i, range->m_caret.m_column, text));
1466       }
1467   }
1468 
1469   /* Bail out if there are no labels on this row.  */
1470   if (labels.length () == 0)
1471     return;
1472 
1473   /* Sort them.  */
1474   labels.qsort(line_label::comparator);
1475 
1476   /* Figure out how many "label lines" we need, and which
1477      one each label is printed in.
1478 
1479      For example, if the labels aren't too densely packed,
1480      we can fit them on the same line, giving two "label lines":
1481 
1482        foo + bar
1483        ~~~   ~~~
1484        |     |        : label line 0
1485        l0    l1       : label line 1
1486 
1487      If they would touch each other or overlap, then we need
1488      additional "label lines":
1489 
1490        foo + bar
1491        ~~~   ~~~
1492        |     |             : label line 0
1493        |     label 1       : label line 1
1494        label 0             : label line 2
1495 
1496      Place the final label on label line 1, and work backwards, adding
1497      label lines as needed.
1498 
1499      If multiple labels are at the same place, put them on separate
1500      label lines:
1501 
1502        foo + bar
1503            ^               : label line 0
1504            |               : label line 1
1505            label 1         : label line 2
1506            label 0         : label line 3.  */
1507 
1508   int max_label_line = 1;
1509   {
1510     int next_column = INT_MAX;
1511     line_label *label;
1512     FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
1513       {
1514 	/* Would this label "touch" or overlap the next label?  */
1515 	if (label->m_column + label->m_length >= (size_t)next_column)
1516 	  max_label_line++;
1517 
1518 	label->m_label_line = max_label_line;
1519 	next_column = label->m_column;
1520       }
1521   }
1522 
1523   /* Print the "label lines".  For each label within the line, print
1524      either a vertical bar ('|') for the labels that are lower down, or the
1525      labels themselves once we've reached their line.  */
1526   {
1527     /* Keep track of in which column we last printed a vertical bar.
1528        This allows us to suppress duplicate vertical bars for the case
1529        where multiple labels are on one column.  */
1530     int last_vbar = 0;
1531     for (int label_line = 0; label_line <= max_label_line; label_line++)
1532       {
1533 	start_annotation_line ();
1534 	pp_space (m_pp);
1535 	int column = 1 + m_x_offset;
1536 	line_label *label;
1537 	FOR_EACH_VEC_ELT (labels, i, label)
1538 	  {
1539 	    if (label_line > label->m_label_line)
1540 	      /* We've printed all the labels for this label line.  */
1541 	      break;
1542 
1543 	    if (label_line == label->m_label_line)
1544 	      {
1545 		gcc_assert (column <= label->m_column);
1546 		move_to_column (&column, label->m_column, true);
1547 		m_colorizer.set_range (label->m_state_idx);
1548 		pp_string (m_pp, label->m_text.m_buffer);
1549 		m_colorizer.set_normal_text ();
1550 		column += label->m_length;
1551 	      }
1552 	    else if (label->m_column != last_vbar)
1553 	      {
1554 		gcc_assert (column <= label->m_column);
1555 		move_to_column (&column, label->m_column, true);
1556 		m_colorizer.set_range (label->m_state_idx);
1557 		pp_character (m_pp, '|');
1558 		m_colorizer.set_normal_text ();
1559 		last_vbar = column;
1560 		column++;
1561 	      }
1562 	  }
1563 	print_newline ();
1564       }
1565     }
1566 
1567   /* Clean up.  */
1568   {
1569     line_label *label;
1570     FOR_EACH_VEC_ELT (labels, i, label)
1571       label->m_text.maybe_free ();
1572   }
1573 }
1574 
1575 /* If there are any fixit hints inserting new lines before source line ROW,
1576    print them.
1577 
1578    They are printed on lines of their own, before the source line
1579    itself, with a leading '+'.  */
1580 
1581 void
print_leading_fixits(linenum_type row)1582 layout::print_leading_fixits (linenum_type row)
1583 {
1584   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1585     {
1586       const fixit_hint *hint = m_fixit_hints[i];
1587 
1588       if (!hint->ends_with_newline_p ())
1589 	/* Not a newline fixit; print it in print_trailing_fixits.  */
1590 	continue;
1591 
1592       gcc_assert (hint->insertion_p ());
1593 
1594       if (hint->affects_line_p (m_exploc.file, row))
1595 	{
1596 	  /* Printing the '+' with normal colorization
1597 	     and the inserted line with "insert" colorization
1598 	     helps them stand out from each other, and from
1599 	     the surrounding text.  */
1600 	  m_colorizer.set_normal_text ();
1601 	  start_annotation_line ('+');
1602 	  pp_character (m_pp, '+');
1603 	  m_colorizer.set_fixit_insert ();
1604 	  /* Print all but the trailing newline of the fix-it hint.
1605 	     We have to print the newline separately to avoid
1606 	     getting additional pp prefixes printed.  */
1607 	  for (size_t i = 0; i < hint->get_length () - 1; i++)
1608 	    pp_character (m_pp, hint->get_string ()[i]);
1609 	  m_colorizer.set_normal_text ();
1610 	  pp_newline (m_pp);
1611 	}
1612     }
1613 }
1614 
1615 /* Subroutine of layout::print_trailing_fixits.
1616 
1617    Determine if the annotation line printed for LINE contained
1618    the exact range from START_COLUMN to FINISH_COLUMN.  */
1619 
1620 bool
annotation_line_showed_range_p(linenum_type line,int start_column,int finish_column)1621 layout::annotation_line_showed_range_p (linenum_type line, int start_column,
1622 					int finish_column) const
1623 {
1624   layout_range *range;
1625   int i;
1626   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
1627     if (range->m_start.m_line == line
1628 	&& range->m_start.m_column == start_column
1629 	&& range->m_finish.m_line == line
1630 	&& range->m_finish.m_column == finish_column)
1631       return true;
1632   return false;
1633 }
1634 
1635 /* Classes for printing trailing fix-it hints i.e. those that
1636    don't add new lines.
1637 
1638    For insertion, these can look like:
1639 
1640      new_text
1641 
1642    For replacement, these can look like:
1643 
1644      ------------- : underline showing affected range
1645      new_text
1646 
1647    For deletion, these can look like:
1648 
1649      ------------- : underline showing affected range
1650 
1651    This can become confusing if they overlap, and so we need
1652    to do some preprocessing to decide what to print.
1653    We use the list of fixit_hint instances affecting the line
1654    to build a list of "correction" instances, and print the
1655    latter.
1656 
1657    For example, consider a set of fix-its for converting
1658    a C-style cast to a C++ const_cast.
1659 
1660    Given:
1661 
1662    ..000000000111111111122222222223333333333.
1663    ..123456789012345678901234567890123456789.
1664      foo *f = (foo *)ptr->field;
1665                           ^~~~~
1666 
1667    and the fix-it hints:
1668      - replace col 10 (the open paren) with "const_cast<"
1669      - replace col 16 (the close paren) with "> ("
1670      - insert ")" before col 27
1671 
1672    then we would get odd-looking output:
1673 
1674      foo *f = (foo *)ptr->field;
1675                           ^~~~~
1676               -
1677               const_cast<
1678                     -
1679                     > (        )
1680 
1681    It would be better to detect when fixit hints are going to
1682    overlap (those that require new lines), and to consolidate
1683    the printing of such fixits, giving something like:
1684 
1685      foo *f = (foo *)ptr->field;
1686                           ^~~~~
1687               -----------------
1688               const_cast<foo *> (ptr->field)
1689 
1690    This works by detecting when the printing would overlap, and
1691    effectively injecting no-op replace hints into the gaps between
1692    such fix-its, so that the printing joins up.
1693 
1694    In the above example, the overlap of:
1695      - replace col 10 (the open paren) with "const_cast<"
1696    and:
1697      - replace col 16 (the close paren) with "> ("
1698    is fixed by injecting a no-op:
1699      - replace cols 11-15 with themselves ("foo *")
1700    and consolidating these, making:
1701      - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
1702    i.e.:
1703      - replace cols 10-16 with "const_cast<foo *> ("
1704 
1705    This overlaps with the final fix-it hint:
1706      - insert ")" before col 27
1707    and so we repeat the consolidation process, by injecting
1708    a no-op:
1709      - replace cols 17-26 with themselves ("ptr->field")
1710    giving:
1711      - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
1712    i.e.:
1713      - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
1714 
1715    and is thus printed as desired.  */
1716 
1717 /* A range of columns within a line.  */
1718 
1719 struct column_range
1720 {
column_rangecolumn_range1721   column_range (int start_, int finish_) : start (start_), finish (finish_)
1722   {
1723     /* We must have either a range, or an insertion.  */
1724     gcc_assert (start <= finish || finish == start - 1);
1725   }
1726 
1727   bool operator== (const column_range &other) const
1728   {
1729     return start == other.start && finish == other.finish;
1730   }
1731 
1732   int start;
1733   int finish;
1734 };
1735 
1736 /* Get the range of columns that HINT would affect.  */
1737 
1738 static column_range
get_affected_columns(const fixit_hint * hint)1739 get_affected_columns (const fixit_hint *hint)
1740 {
1741   int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1742   int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1743 
1744   return column_range (start_column, finish_column);
1745 }
1746 
1747 /* Get the range of columns that would be printed for HINT.  */
1748 
1749 static column_range
get_printed_columns(const fixit_hint * hint)1750 get_printed_columns (const fixit_hint *hint)
1751 {
1752   int start_column = LOCATION_COLUMN (hint->get_start_loc ());
1753   int final_hint_column = start_column + hint->get_length () - 1;
1754   if (hint->insertion_p ())
1755     {
1756       return column_range (start_column, final_hint_column);
1757     }
1758   else
1759     {
1760       int finish_column = LOCATION_COLUMN (hint->get_next_loc ()) - 1;
1761 
1762       return column_range (start_column,
1763 			   MAX (finish_column, final_hint_column));
1764     }
1765 }
1766 
1767 /* A correction on a particular line.
1768    This describes a plan for how to print one or more fixit_hint
1769    instances that affected the line, potentially consolidating hints
1770    into corrections to make the result easier for the user to read.  */
1771 
1772 struct correction
1773 {
correctioncorrection1774   correction (column_range affected_columns,
1775 	      column_range printed_columns,
1776 	      const char *new_text, size_t new_text_len)
1777   : m_affected_columns (affected_columns),
1778     m_printed_columns (printed_columns),
1779     m_text (xstrdup (new_text)),
1780     m_len (new_text_len),
1781     m_alloc_sz (new_text_len + 1)
1782   {
1783   }
1784 
~correctioncorrection1785   ~correction () { free (m_text); }
1786 
insertion_pcorrection1787   bool insertion_p () const
1788   {
1789     return m_affected_columns.start == m_affected_columns.finish + 1;
1790   }
1791 
1792   void ensure_capacity (size_t len);
1793   void ensure_terminated ();
1794 
overwritecorrection1795   void overwrite (int dst_offset, const char_span &src_span)
1796   {
1797     gcc_assert (dst_offset >= 0);
1798     gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
1799     memcpy (m_text + dst_offset, src_span.get_buffer (),
1800 	    src_span.length ());
1801   }
1802 
1803   /* If insert, then start: the column before which the text
1804      is to be inserted, and finish is offset by the length of
1805      the replacement.
1806      If replace, then the range of columns affected.  */
1807   column_range m_affected_columns;
1808 
1809   /* If insert, then start: the column before which the text
1810      is to be inserted, and finish is offset by the length of
1811      the replacement.
1812      If replace, then the range of columns affected.  */
1813   column_range m_printed_columns;
1814 
1815   /* The text to be inserted/used as replacement.  */
1816   char *m_text;
1817   size_t m_len;
1818   size_t m_alloc_sz;
1819 };
1820 
1821 /* Ensure that m_text can hold a string of length LEN
1822    (plus 1 for 0-termination).  */
1823 
1824 void
ensure_capacity(size_t len)1825 correction::ensure_capacity (size_t len)
1826 {
1827   /* Allow 1 extra byte for 0-termination.  */
1828   if (m_alloc_sz < (len + 1))
1829     {
1830       size_t new_alloc_sz = (len + 1) * 2;
1831       m_text = (char *)xrealloc (m_text, new_alloc_sz);
1832       m_alloc_sz = new_alloc_sz;
1833     }
1834 }
1835 
1836 /* Ensure that m_text is 0-terminated.  */
1837 
1838 void
ensure_terminated()1839 correction::ensure_terminated ()
1840 {
1841   /* 0-terminate the buffer.  */
1842   gcc_assert (m_len < m_alloc_sz);
1843   m_text[m_len] = '\0';
1844 }
1845 
1846 /* A list of corrections affecting a particular line.
1847    This is used by layout::print_trailing_fixits for planning
1848    how to print the fix-it hints affecting the line.  */
1849 
1850 struct line_corrections
1851 {
line_correctionsline_corrections1852   line_corrections (const char *filename, linenum_type row)
1853   : m_filename (filename), m_row (row)
1854   {}
1855   ~line_corrections ();
1856 
1857   void add_hint (const fixit_hint *hint);
1858 
1859   const char *m_filename;
1860   linenum_type m_row;
1861   auto_vec <correction *> m_corrections;
1862 };
1863 
1864 /* struct line_corrections.  */
1865 
~line_corrections()1866 line_corrections::~line_corrections ()
1867 {
1868   unsigned i;
1869   correction *c;
1870   FOR_EACH_VEC_ELT (m_corrections, i, c)
1871     delete c;
1872 }
1873 
1874 /* A struct wrapping a particular source line, allowing
1875    run-time bounds-checking of accesses in a checked build.  */
1876 
1877 struct source_line
1878 {
1879   source_line (const char *filename, int line);
1880 
as_spansource_line1881   char_span as_span () { return char_span (chars, width); }
1882 
1883   const char *chars;
1884   int width;
1885 };
1886 
1887 /* source_line's ctor.  */
1888 
source_line(const char * filename,int line)1889 source_line::source_line (const char *filename, int line)
1890 {
1891   char_span span = location_get_source_line (filename, line);
1892   chars = span.get_buffer ();
1893   width = span.length ();
1894 }
1895 
1896 /* Add HINT to the corrections for this line.
1897    Attempt to consolidate nearby hints so that they will not
1898    overlap with printed.  */
1899 
1900 void
add_hint(const fixit_hint * hint)1901 line_corrections::add_hint (const fixit_hint *hint)
1902 {
1903   column_range affected_columns = get_affected_columns (hint);
1904   column_range printed_columns = get_printed_columns (hint);
1905 
1906   /* Potentially consolidate.  */
1907   if (!m_corrections.is_empty ())
1908     {
1909       correction *last_correction
1910 	= m_corrections[m_corrections.length () - 1];
1911 
1912       /* The following consolidation code assumes that the fix-it hints
1913 	 have been sorted by start (done within layout's ctor).  */
1914       gcc_assert (affected_columns.start
1915 		  >= last_correction->m_affected_columns.start);
1916       gcc_assert (printed_columns.start
1917 		  >= last_correction->m_printed_columns.start);
1918 
1919       if (printed_columns.start <= last_correction->m_printed_columns.finish)
1920 	{
1921 	  /* We have two hints for which the printed forms of the hints
1922 	     would touch or overlap, so we need to consolidate them to avoid
1923 	     confusing the user.
1924 	     Attempt to inject a "replace" correction from immediately
1925 	     after the end of the last hint to immediately before the start
1926 	     of the next hint.  */
1927 	  column_range between (last_correction->m_affected_columns.finish + 1,
1928 				printed_columns.start - 1);
1929 
1930 	  /* Try to read the source.  */
1931 	  source_line line (m_filename, m_row);
1932 	  if (line.chars && between.finish < line.width)
1933 	    {
1934 	      /* Consolidate into the last correction:
1935 		 add a no-op "replace" of the "between" text, and
1936 		 add the text from the new hint.  */
1937 	      int old_len = last_correction->m_len;
1938 	      gcc_assert (old_len >= 0);
1939 	      int between_len = between.finish + 1 - between.start;
1940 	      gcc_assert (between_len >= 0);
1941 	      int new_len = old_len + between_len + hint->get_length ();
1942 	      gcc_assert (new_len >= 0);
1943 	      last_correction->ensure_capacity (new_len);
1944 	      last_correction->overwrite
1945 		(old_len,
1946 		 line.as_span ().subspan (between.start - 1,
1947 					  between.finish + 1 - between.start));
1948 	      last_correction->overwrite (old_len + between_len,
1949 					  char_span (hint->get_string (),
1950 						     hint->get_length ()));
1951 	      last_correction->m_len = new_len;
1952 	      last_correction->ensure_terminated ();
1953 	      last_correction->m_affected_columns.finish
1954 		= affected_columns.finish;
1955 	      last_correction->m_printed_columns.finish
1956 		+= between_len + hint->get_length ();
1957 	      return;
1958 	    }
1959 	}
1960     }
1961 
1962   /* If no consolidation happened, add a new correction instance.  */
1963   m_corrections.safe_push (new correction (affected_columns,
1964 					   printed_columns,
1965 					   hint->get_string (),
1966 					   hint->get_length ()));
1967 }
1968 
1969 /* If there are any fixit hints on source line ROW, print them.
1970    They are printed in order, attempting to combine them onto lines, but
1971    starting new lines if necessary.
1972    Fix-it hints that insert new lines are handled separately,
1973    in layout::print_leading_fixits.  */
1974 
1975 void
print_trailing_fixits(linenum_type row)1976 layout::print_trailing_fixits (linenum_type row)
1977 {
1978   /* Build a list of correction instances for the line,
1979      potentially consolidating hints (for the sake of readability).  */
1980   line_corrections corrections (m_exploc.file, row);
1981   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
1982     {
1983       const fixit_hint *hint = m_fixit_hints[i];
1984 
1985       /* Newline fixits are handled by layout::print_leading_fixits.  */
1986       if (hint->ends_with_newline_p ())
1987 	continue;
1988 
1989       if (hint->affects_line_p (m_exploc.file, row))
1990 	corrections.add_hint (hint);
1991     }
1992 
1993   /* Now print the corrections.  */
1994   unsigned i;
1995   correction *c;
1996   int column = m_x_offset;
1997 
1998   if (!corrections.m_corrections.is_empty ())
1999     start_annotation_line ();
2000 
2001   FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
2002     {
2003       /* For now we assume each fixit hint can only touch one line.  */
2004       if (c->insertion_p ())
2005 	{
2006 	  /* This assumes the insertion just affects one line.  */
2007 	  int start_column = c->m_printed_columns.start;
2008 	  move_to_column (&column, start_column, true);
2009 	  m_colorizer.set_fixit_insert ();
2010 	  pp_string (m_pp, c->m_text);
2011 	  m_colorizer.set_normal_text ();
2012 	  column += c->m_len;
2013 	}
2014       else
2015 	{
2016 	  /* If the range of the replacement wasn't printed in the
2017 	     annotation line, then print an extra underline to
2018 	     indicate exactly what is being replaced.
2019 	     Always show it for removals.  */
2020 	  int start_column = c->m_affected_columns.start;
2021 	  int finish_column = c->m_affected_columns.finish;
2022 	  if (!annotation_line_showed_range_p (row, start_column,
2023 					       finish_column)
2024 	      || c->m_len == 0)
2025 	    {
2026 	      move_to_column (&column, start_column, true);
2027 	      m_colorizer.set_fixit_delete ();
2028 	      for (; column <= finish_column; column++)
2029 		pp_character (m_pp, '-');
2030 	      m_colorizer.set_normal_text ();
2031 	    }
2032 	  /* Print the replacement text.  REPLACE also covers
2033 	     removals, so only do this extra work (potentially starting
2034 	     a new line) if we have actual replacement text.  */
2035 	  if (c->m_len > 0)
2036 	    {
2037 	      move_to_column (&column, start_column, true);
2038 	      m_colorizer.set_fixit_insert ();
2039 	      pp_string (m_pp, c->m_text);
2040 	      m_colorizer.set_normal_text ();
2041 	      column += c->m_len;
2042 	    }
2043 	}
2044     }
2045 
2046   /* Add a trailing newline, if necessary.  */
2047   move_to_column (&column, 0, false);
2048 }
2049 
2050 /* Disable any colorization and emit a newline.  */
2051 
2052 void
print_newline()2053 layout::print_newline ()
2054 {
2055   m_colorizer.set_normal_text ();
2056   pp_newline (m_pp);
2057 }
2058 
2059 /* Return true if (ROW/COLUMN) is within a range of the layout.
2060    If it returns true, OUT_STATE is written to, with the
2061    range index, and whether we should draw the caret at
2062    (ROW/COLUMN) (as opposed to an underline).  */
2063 
2064 bool
get_state_at_point(linenum_type row,int column,int first_non_ws,int last_non_ws,point_state * out_state)2065 layout::get_state_at_point (/* Inputs.  */
2066 			    linenum_type row, int column,
2067 			    int first_non_ws, int last_non_ws,
2068 			    /* Outputs.  */
2069 			    point_state *out_state)
2070 {
2071   layout_range *range;
2072   int i;
2073   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2074     {
2075       if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
2076 	/* Bail out early, so that such ranges don't affect underlining or
2077 	   source colorization.  */
2078 	continue;
2079 
2080       if (range->contains_point (row, column))
2081 	{
2082 	  out_state->range_idx = i;
2083 
2084 	  /* Are we at the range's caret?  is it visible? */
2085 	  out_state->draw_caret_p = false;
2086 	  if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
2087 	      && row == range->m_caret.m_line
2088 	      && column == range->m_caret.m_column)
2089 	    out_state->draw_caret_p = true;
2090 
2091 	  /* Within a multiline range, don't display any underline
2092 	     in any leading or trailing whitespace on a line.
2093 	     We do display carets, however.  */
2094 	  if (!out_state->draw_caret_p)
2095 	    if (column < first_non_ws || column > last_non_ws)
2096 	      return false;
2097 
2098 	  /* We are within a range.  */
2099 	  return true;
2100 	}
2101     }
2102 
2103   return false;
2104 }
2105 
2106 /* Helper function for use by layout::print_line when printing the
2107    annotation line under the source line.
2108    Get the column beyond the rightmost one that could contain a caret or
2109    range marker, given that we stop rendering at trailing whitespace.
2110    ROW is the source line within the given file.
2111    CARET_COLUMN is the column of range 0's caret.
2112    LAST_NON_WS_COLUMN is the last column containing a non-whitespace
2113    character of source (as determined when printing the source line).  */
2114 
2115 int
get_x_bound_for_row(linenum_type row,int caret_column,int last_non_ws_column)2116 layout::get_x_bound_for_row (linenum_type row, int caret_column,
2117 			     int last_non_ws_column)
2118 {
2119   int result = caret_column + 1;
2120 
2121   layout_range *range;
2122   int i;
2123   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
2124     {
2125       if (row >= range->m_start.m_line)
2126 	{
2127 	  if (range->m_finish.m_line == row)
2128 	    {
2129 	      /* On the final line within a range; ensure that
2130 		 we render up to the end of the range.  */
2131 	      if (result <= range->m_finish.m_column)
2132 		result = range->m_finish.m_column + 1;
2133 	    }
2134 	  else if (row < range->m_finish.m_line)
2135 	    {
2136 	      /* Within a multiline range; ensure that we render up to the
2137 		 last non-whitespace column.  */
2138 	      if (result <= last_non_ws_column)
2139 		result = last_non_ws_column + 1;
2140 	    }
2141 	}
2142     }
2143 
2144   return result;
2145 }
2146 
2147 /* Given *COLUMN as an x-coordinate, print spaces to position
2148    successive output at DEST_COLUMN, printing a newline if necessary,
2149    and updating *COLUMN.  If ADD_LEFT_MARGIN, then print the (empty)
2150    left margin after any newline.  */
2151 
2152 void
move_to_column(int * column,int dest_column,bool add_left_margin)2153 layout::move_to_column (int *column, int dest_column, bool add_left_margin)
2154 {
2155   /* Start a new line if we need to.  */
2156   if (*column > dest_column)
2157     {
2158       print_newline ();
2159       if (add_left_margin)
2160 	start_annotation_line ();
2161       *column = m_x_offset;
2162     }
2163 
2164   while (*column < dest_column)
2165     {
2166       pp_space (m_pp);
2167       (*column)++;
2168     }
2169 }
2170 
2171 /* For debugging layout issues, render a ruler giving column numbers
2172    (after the 1-column indent).  */
2173 
2174 void
show_ruler(int max_column)2175 layout::show_ruler (int max_column) const
2176 {
2177   /* Hundreds.  */
2178   if (max_column > 99)
2179     {
2180       start_annotation_line ();
2181       pp_space (m_pp);
2182       for (int column = 1 + m_x_offset; column <= max_column; column++)
2183 	if (column % 10 == 0)
2184 	  pp_character (m_pp, '0' + (column / 100) % 10);
2185 	else
2186 	  pp_space (m_pp);
2187       pp_newline (m_pp);
2188     }
2189 
2190   /* Tens.  */
2191   start_annotation_line ();
2192   pp_space (m_pp);
2193   for (int column = 1 + m_x_offset; column <= max_column; column++)
2194     if (column % 10 == 0)
2195       pp_character (m_pp, '0' + (column / 10) % 10);
2196     else
2197       pp_space (m_pp);
2198   pp_newline (m_pp);
2199 
2200   /* Units.  */
2201   start_annotation_line ();
2202   pp_space (m_pp);
2203   for (int column = 1 + m_x_offset; column <= max_column; column++)
2204     pp_character (m_pp, '0' + (column % 10));
2205   pp_newline (m_pp);
2206 }
2207 
2208 /* Print leading fix-its (for new lines inserted before the source line)
2209    then the source line, followed by an annotation line
2210    consisting of any caret/underlines, then any fixits.
2211    If the source line can't be read, print nothing.  */
2212 void
print_line(linenum_type row)2213 layout::print_line (linenum_type row)
2214 {
2215   char_span line = location_get_source_line (m_exploc.file, row);
2216   if (!line)
2217     return;
2218 
2219   line_bounds lbounds;
2220   print_leading_fixits (row);
2221   print_source_line (row, line.get_buffer (), line.length (), &lbounds);
2222   if (should_print_annotation_line_p (row))
2223     print_annotation_line (row, lbounds);
2224   if (m_show_labels_p)
2225     print_any_labels (row);
2226   print_trailing_fixits (row);
2227 }
2228 
2229 } /* End of anonymous namespace.  */
2230 
2231 /* If LOC is within the spans of lines that will already be printed for
2232    this gcc_rich_location, then add it as a secondary location and return true.
2233 
2234    Otherwise return false.  */
2235 
2236 bool
add_location_if_nearby(location_t loc)2237 gcc_rich_location::add_location_if_nearby (location_t loc)
2238 {
2239   /* Use the layout location-handling logic to sanitize LOC,
2240      filtering it to the current line spans within a temporary
2241      layout instance.  */
2242   layout layout (global_dc, this, DK_ERROR);
2243   location_range loc_range;
2244   loc_range.m_loc = loc;
2245   loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
2246   if (!layout.maybe_add_location_range (&loc_range, 0, true))
2247     return false;
2248 
2249   add_range (loc);
2250   return true;
2251 }
2252 
2253 /* Print the physical source code corresponding to the location of
2254    this diagnostic, with additional annotations.  */
2255 
2256 void
diagnostic_show_locus(diagnostic_context * context,rich_location * richloc,diagnostic_t diagnostic_kind)2257 diagnostic_show_locus (diagnostic_context * context,
2258 		       rich_location *richloc,
2259 		       diagnostic_t diagnostic_kind)
2260 {
2261   pp_newline (context->printer);
2262 
2263   location_t loc = richloc->get_loc ();
2264   /* Do nothing if source-printing has been disabled.  */
2265   if (!context->show_caret)
2266     return;
2267 
2268   /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins.  */
2269   if (loc <= BUILTINS_LOCATION)
2270     return;
2271 
2272   /* Don't print the same source location twice in a row, unless we have
2273      fix-it hints.  */
2274   if (loc == context->last_location
2275       && richloc->get_num_fixit_hints () == 0)
2276     return;
2277 
2278   context->last_location = loc;
2279 
2280   char *saved_prefix = pp_take_prefix (context->printer);
2281   pp_set_prefix (context->printer, NULL);
2282 
2283   layout layout (context, richloc, diagnostic_kind);
2284   for (int line_span_idx = 0; line_span_idx < layout.get_num_line_spans ();
2285        line_span_idx++)
2286     {
2287       const line_span *line_span = layout.get_line_span (line_span_idx);
2288       if (context->show_line_numbers_p)
2289 	{
2290 	  /* With line numbers, we should show whenever the line-numbering
2291 	     "jumps".  */
2292 	  if (line_span_idx > 0)
2293 	    layout.print_gap_in_line_numbering ();
2294 	}
2295       else
2296 	{
2297 	  /* Without line numbers, we print headings for some line spans.  */
2298 	  if (layout.print_heading_for_line_span_index_p (line_span_idx))
2299 	    {
2300 	      expanded_location exploc
2301 		= layout.get_expanded_location (line_span);
2302 	      context->start_span (context, exploc);
2303 	    }
2304 	}
2305       /* Iterate over the lines within this span (using linenum_arith_t to
2306 	 avoid overflow with 0xffffffff causing an infinite loop).  */
2307       linenum_arith_t last_line = line_span->get_last_line ();
2308       for (linenum_arith_t row = line_span->get_first_line ();
2309 	   row <= last_line; row++)
2310 	layout.print_line (row);
2311     }
2312 
2313   pp_set_prefix (context->printer, saved_prefix);
2314 }
2315 
2316 #if CHECKING_P
2317 
2318 namespace selftest {
2319 
2320 /* Selftests for diagnostic_show_locus.  */
2321 
2322 /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION.  */
2323 
2324 static void
test_diagnostic_show_locus_unknown_location()2325 test_diagnostic_show_locus_unknown_location ()
2326 {
2327   test_diagnostic_context dc;
2328   rich_location richloc (line_table, UNKNOWN_LOCATION);
2329   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2330   ASSERT_STREQ ("\n", pp_formatted_text (dc.printer));
2331 }
2332 
2333 /* Verify that diagnostic_show_locus works sanely for various
2334    single-line cases.
2335 
2336    All of these work on the following 1-line source file:
2337      .0000000001111111
2338      .1234567890123456
2339      "foo = bar.field;\n"
2340    which is set up by test_diagnostic_show_locus_one_liner and calls
2341    them.  */
2342 
2343 /* Just a caret.  */
2344 
2345 static void
test_one_liner_simple_caret()2346 test_one_liner_simple_caret ()
2347 {
2348   test_diagnostic_context dc;
2349   location_t caret = linemap_position_for_column (line_table, 10);
2350   rich_location richloc (line_table, caret);
2351   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2352   ASSERT_STREQ ("\n"
2353 		" foo = bar.field;\n"
2354 		"          ^\n",
2355 		pp_formatted_text (dc.printer));
2356 }
2357 
2358 /* Caret and range.  */
2359 
2360 static void
test_one_liner_caret_and_range()2361 test_one_liner_caret_and_range ()
2362 {
2363   test_diagnostic_context dc;
2364   location_t caret = linemap_position_for_column (line_table, 10);
2365   location_t start = linemap_position_for_column (line_table, 7);
2366   location_t finish = linemap_position_for_column (line_table, 15);
2367   location_t loc = make_location (caret, start, finish);
2368   rich_location richloc (line_table, loc);
2369   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2370   ASSERT_STREQ ("\n"
2371 		" foo = bar.field;\n"
2372 		"       ~~~^~~~~~\n",
2373 		pp_formatted_text (dc.printer));
2374 }
2375 
2376 /* Multiple ranges and carets.  */
2377 
2378 static void
test_one_liner_multiple_carets_and_ranges()2379 test_one_liner_multiple_carets_and_ranges ()
2380 {
2381   test_diagnostic_context dc;
2382   location_t foo
2383     = make_location (linemap_position_for_column (line_table, 2),
2384 		     linemap_position_for_column (line_table, 1),
2385 		     linemap_position_for_column (line_table, 3));
2386   dc.caret_chars[0] = 'A';
2387 
2388   location_t bar
2389     = make_location (linemap_position_for_column (line_table, 8),
2390 		     linemap_position_for_column (line_table, 7),
2391 		     linemap_position_for_column (line_table, 9));
2392   dc.caret_chars[1] = 'B';
2393 
2394   location_t field
2395     = make_location (linemap_position_for_column (line_table, 13),
2396 		     linemap_position_for_column (line_table, 11),
2397 		     linemap_position_for_column (line_table, 15));
2398   dc.caret_chars[2] = 'C';
2399 
2400   rich_location richloc (line_table, foo);
2401   richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
2402   richloc.add_range (field, SHOW_RANGE_WITH_CARET);
2403   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2404   ASSERT_STREQ ("\n"
2405 		" foo = bar.field;\n"
2406 		" ~A~   ~B~ ~~C~~\n",
2407 		pp_formatted_text (dc.printer));
2408 }
2409 
2410 /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
2411 
2412 static void
test_one_liner_fixit_insert_before()2413 test_one_liner_fixit_insert_before ()
2414 {
2415   test_diagnostic_context dc;
2416   location_t caret = linemap_position_for_column (line_table, 7);
2417   rich_location richloc (line_table, caret);
2418   richloc.add_fixit_insert_before ("&");
2419   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2420   ASSERT_STREQ ("\n"
2421 		" foo = bar.field;\n"
2422 		"       ^\n"
2423 		"       &\n",
2424 		pp_formatted_text (dc.printer));
2425 }
2426 
2427 /* Insertion fix-it hint: adding a "[0]" after "foo". */
2428 
2429 static void
test_one_liner_fixit_insert_after()2430 test_one_liner_fixit_insert_after ()
2431 {
2432   test_diagnostic_context dc;
2433   location_t start = linemap_position_for_column (line_table, 1);
2434   location_t finish = linemap_position_for_column (line_table, 3);
2435   location_t foo = make_location (start, start, finish);
2436   rich_location richloc (line_table, foo);
2437   richloc.add_fixit_insert_after ("[0]");
2438   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2439   ASSERT_STREQ ("\n"
2440 		" foo = bar.field;\n"
2441 		" ^~~\n"
2442 		"    [0]\n",
2443 		pp_formatted_text (dc.printer));
2444 }
2445 
2446 /* Removal fix-it hint: removal of the ".field". */
2447 
2448 static void
test_one_liner_fixit_remove()2449 test_one_liner_fixit_remove ()
2450 {
2451   test_diagnostic_context dc;
2452   location_t start = linemap_position_for_column (line_table, 10);
2453   location_t finish = linemap_position_for_column (line_table, 15);
2454   location_t dot = make_location (start, start, finish);
2455   rich_location richloc (line_table, dot);
2456   richloc.add_fixit_remove ();
2457   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2458   ASSERT_STREQ ("\n"
2459 		" foo = bar.field;\n"
2460 		"          ^~~~~~\n"
2461 		"          ------\n",
2462 		pp_formatted_text (dc.printer));
2463 }
2464 
2465 /* Replace fix-it hint: replacing "field" with "m_field". */
2466 
2467 static void
test_one_liner_fixit_replace()2468 test_one_liner_fixit_replace ()
2469 {
2470   test_diagnostic_context dc;
2471   location_t start = linemap_position_for_column (line_table, 11);
2472   location_t finish = linemap_position_for_column (line_table, 15);
2473   location_t field = make_location (start, start, finish);
2474   rich_location richloc (line_table, field);
2475   richloc.add_fixit_replace ("m_field");
2476   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2477   ASSERT_STREQ ("\n"
2478 		" foo = bar.field;\n"
2479 		"           ^~~~~\n"
2480 		"           m_field\n",
2481 		pp_formatted_text (dc.printer));
2482 }
2483 
2484 /* Replace fix-it hint: replacing "field" with "m_field",
2485    but where the caret was elsewhere.  */
2486 
2487 static void
test_one_liner_fixit_replace_non_equal_range()2488 test_one_liner_fixit_replace_non_equal_range ()
2489 {
2490   test_diagnostic_context dc;
2491   location_t equals = linemap_position_for_column (line_table, 5);
2492   location_t start = linemap_position_for_column (line_table, 11);
2493   location_t finish = linemap_position_for_column (line_table, 15);
2494   rich_location richloc (line_table, equals);
2495   source_range range;
2496   range.m_start = start;
2497   range.m_finish = finish;
2498   richloc.add_fixit_replace (range, "m_field");
2499   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2500   /* The replacement range is not indicated in the annotation line, so
2501      it should be indicated via an additional underline.  */
2502   ASSERT_STREQ ("\n"
2503 		" foo = bar.field;\n"
2504 		"     ^\n"
2505 		"           -----\n"
2506 		"           m_field\n",
2507 		pp_formatted_text (dc.printer));
2508 }
2509 
2510 /* Replace fix-it hint: replacing "field" with "m_field",
2511    where the caret was elsewhere, but where a secondary range
2512    exactly covers "field".  */
2513 
2514 static void
test_one_liner_fixit_replace_equal_secondary_range()2515 test_one_liner_fixit_replace_equal_secondary_range ()
2516 {
2517   test_diagnostic_context dc;
2518   location_t equals = linemap_position_for_column (line_table, 5);
2519   location_t start = linemap_position_for_column (line_table, 11);
2520   location_t finish = linemap_position_for_column (line_table, 15);
2521   rich_location richloc (line_table, equals);
2522   location_t field = make_location (start, start, finish);
2523   richloc.add_range (field);
2524   richloc.add_fixit_replace (field, "m_field");
2525   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2526   /* The replacement range is indicated in the annotation line,
2527      so it shouldn't be indicated via an additional underline.  */
2528   ASSERT_STREQ ("\n"
2529 		" foo = bar.field;\n"
2530 		"     ^     ~~~~~\n"
2531 		"           m_field\n",
2532 		pp_formatted_text (dc.printer));
2533 }
2534 
2535 /* Verify that we can use ad-hoc locations when adding fixits to a
2536    rich_location.  */
2537 
2538 static void
test_one_liner_fixit_validation_adhoc_locations()2539 test_one_liner_fixit_validation_adhoc_locations ()
2540 {
2541   /* Generate a range that's too long to be packed, so must
2542      be stored as an ad-hoc location (given the defaults
2543      of 5 bits or 0 bits of packed range); 41 columns > 2**5.  */
2544   const location_t c7 = linemap_position_for_column (line_table, 7);
2545   const location_t c47 = linemap_position_for_column (line_table, 47);
2546   const location_t loc = make_location (c7, c7, c47);
2547 
2548   if (c47 > LINE_MAP_MAX_LOCATION_WITH_COLS)
2549     return;
2550 
2551   ASSERT_TRUE (IS_ADHOC_LOC (loc));
2552 
2553   /* Insert.  */
2554   {
2555     rich_location richloc (line_table, loc);
2556     richloc.add_fixit_insert_before (loc, "test");
2557     /* It should not have been discarded by the validator.  */
2558     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2559 
2560     test_diagnostic_context dc;
2561     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2562     ASSERT_STREQ ("\n"
2563 		  " foo = bar.field;\n"
2564 		  "       ^~~~~~~~~~                               \n"
2565 		  "       test\n",
2566 		  pp_formatted_text (dc.printer));
2567   }
2568 
2569   /* Remove.  */
2570   {
2571     rich_location richloc (line_table, loc);
2572     source_range range = source_range::from_locations (loc, c47);
2573     richloc.add_fixit_remove (range);
2574     /* It should not have been discarded by the validator.  */
2575     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2576 
2577     test_diagnostic_context dc;
2578     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2579     ASSERT_STREQ ("\n"
2580 		  " foo = bar.field;\n"
2581 		  "       ^~~~~~~~~~                               \n"
2582 		  "       -----------------------------------------\n",
2583 		  pp_formatted_text (dc.printer));
2584   }
2585 
2586   /* Replace.  */
2587   {
2588     rich_location richloc (line_table, loc);
2589     source_range range = source_range::from_locations (loc, c47);
2590     richloc.add_fixit_replace (range, "test");
2591     /* It should not have been discarded by the validator.  */
2592     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2593 
2594     test_diagnostic_context dc;
2595     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2596     ASSERT_STREQ ("\n"
2597 		  " foo = bar.field;\n"
2598 		  "       ^~~~~~~~~~                               \n"
2599 		  "       test\n",
2600 		  pp_formatted_text (dc.printer));
2601   }
2602 }
2603 
2604 /* Test of consolidating insertions at the same location.  */
2605 
2606 static void
test_one_liner_many_fixits_1()2607 test_one_liner_many_fixits_1 ()
2608 {
2609   test_diagnostic_context dc;
2610   location_t equals = linemap_position_for_column (line_table, 5);
2611   rich_location richloc (line_table, equals);
2612   for (int i = 0; i < 19; i++)
2613     richloc.add_fixit_insert_before ("a");
2614   ASSERT_EQ (1, richloc.get_num_fixit_hints ());
2615   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2616   ASSERT_STREQ ("\n"
2617 		" foo = bar.field;\n"
2618 		"     ^\n"
2619 		"     aaaaaaaaaaaaaaaaaaa\n",
2620 		pp_formatted_text (dc.printer));
2621 }
2622 
2623 /* Ensure that we can add an arbitrary number of fix-it hints to a
2624    rich_location, even if they are not consolidated.  */
2625 
2626 static void
test_one_liner_many_fixits_2()2627 test_one_liner_many_fixits_2 ()
2628 {
2629   test_diagnostic_context dc;
2630   location_t equals = linemap_position_for_column (line_table, 5);
2631   rich_location richloc (line_table, equals);
2632   for (int i = 0; i < 19; i++)
2633     {
2634       location_t loc = linemap_position_for_column (line_table, i * 2);
2635       richloc.add_fixit_insert_before (loc, "a");
2636     }
2637   ASSERT_EQ (19, richloc.get_num_fixit_hints ());
2638   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2639   ASSERT_STREQ ("\n"
2640 		" foo = bar.field;\n"
2641 		"     ^\n"
2642 		"a a a a a a a a a a a a a a a a a a a\n",
2643 		pp_formatted_text (dc.printer));
2644 }
2645 
2646 /* Test of labeling the ranges within a rich_location.  */
2647 
2648 static void
test_one_liner_labels()2649 test_one_liner_labels ()
2650 {
2651   location_t foo
2652     = make_location (linemap_position_for_column (line_table, 1),
2653 		     linemap_position_for_column (line_table, 1),
2654 		     linemap_position_for_column (line_table, 3));
2655   location_t bar
2656     = make_location (linemap_position_for_column (line_table, 7),
2657 		     linemap_position_for_column (line_table, 7),
2658 		     linemap_position_for_column (line_table, 9));
2659   location_t field
2660     = make_location (linemap_position_for_column (line_table, 11),
2661 		     linemap_position_for_column (line_table, 11),
2662 		     linemap_position_for_column (line_table, 15));
2663 
2664   /* Example where all the labels fit on one line.  */
2665   {
2666     text_range_label label0 ("0");
2667     text_range_label label1 ("1");
2668     text_range_label label2 ("2");
2669     gcc_rich_location richloc (foo, &label0);
2670     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2671     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2672 
2673     {
2674       test_diagnostic_context dc;
2675       diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2676       ASSERT_STREQ ("\n"
2677 		    " foo = bar.field;\n"
2678 		    " ^~~   ~~~ ~~~~~\n"
2679 		    " |     |   |\n"
2680 		    " 0     1   2\n",
2681 		    pp_formatted_text (dc.printer));
2682     }
2683 
2684     /* Verify that we can disable label-printing.  */
2685     {
2686       test_diagnostic_context dc;
2687       dc.show_labels_p = false;
2688       diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2689       ASSERT_STREQ ("\n"
2690 		    " foo = bar.field;\n"
2691 		    " ^~~   ~~~ ~~~~~\n",
2692 		    pp_formatted_text (dc.printer));
2693     }
2694   }
2695 
2696   /* Example where the labels need extra lines.  */
2697   {
2698     text_range_label label0 ("label 0");
2699     text_range_label label1 ("label 1");
2700     text_range_label label2 ("label 2");
2701     gcc_rich_location richloc (foo, &label0);
2702     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2703     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2704 
2705     test_diagnostic_context dc;
2706     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2707     ASSERT_STREQ ("\n"
2708 		  " foo = bar.field;\n"
2709 		  " ^~~   ~~~ ~~~~~\n"
2710 		  " |     |   |\n"
2711 		  " |     |   label 2\n"
2712 		  " |     label 1\n"
2713 		  " label 0\n",
2714 		  pp_formatted_text (dc.printer));
2715   }
2716 
2717   /* Example of boundary conditions: label 0 and 1 have just enough clearance,
2718      but label 1 just touches label 2.  */
2719   {
2720     text_range_label label0 ("aaaaa");
2721     text_range_label label1 ("bbbb");
2722     text_range_label label2 ("c");
2723     gcc_rich_location richloc (foo, &label0);
2724     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2725     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
2726 
2727     test_diagnostic_context dc;
2728     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2729     ASSERT_STREQ ("\n"
2730 		  " foo = bar.field;\n"
2731 		  " ^~~   ~~~ ~~~~~\n"
2732 		  " |     |   |\n"
2733 		  " |     |   c\n"
2734 		  " aaaaa bbbb\n",
2735 		  pp_formatted_text (dc.printer));
2736   }
2737 
2738   /* Example of out-of-order ranges (thus requiring a sort).  */
2739   {
2740     text_range_label label0 ("0");
2741     text_range_label label1 ("1");
2742     text_range_label label2 ("2");
2743     gcc_rich_location richloc (field, &label0);
2744     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2745     richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
2746 
2747     test_diagnostic_context dc;
2748     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2749     ASSERT_STREQ ("\n"
2750 		  " foo = bar.field;\n"
2751 		  " ~~~   ~~~ ^~~~~\n"
2752 		  " |     |   |\n"
2753 		  " 2     1   0\n",
2754 		  pp_formatted_text (dc.printer));
2755   }
2756 
2757   /* Ensure we don't ICE if multiple ranges with labels are on
2758      the same point.  */
2759   {
2760     text_range_label label0 ("label 0");
2761     text_range_label label1 ("label 1");
2762     text_range_label label2 ("label 2");
2763     gcc_rich_location richloc (bar, &label0);
2764     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
2765     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
2766 
2767     test_diagnostic_context dc;
2768     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2769     ASSERT_STREQ ("\n"
2770 		  " foo = bar.field;\n"
2771 		  "       ^~~\n"
2772 		  "       |\n"
2773 		  "       label 2\n"
2774 		  "       label 1\n"
2775 		  "       label 0\n",
2776 		  pp_formatted_text (dc.printer));
2777   }
2778 
2779   /* Verify that a NULL result from range_label::get_text is
2780      handled gracefully.  */
2781   {
2782     text_range_label label (NULL);
2783     gcc_rich_location richloc (bar, &label);
2784 
2785     test_diagnostic_context dc;
2786     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2787     ASSERT_STREQ ("\n"
2788 		  " foo = bar.field;\n"
2789 		  "       ^~~\n",
2790 		  pp_formatted_text (dc.printer));
2791    }
2792 
2793   /* TODO: example of formatted printing (needs to be in
2794      gcc-rich-location.c due to Makefile.in issues).  */
2795 }
2796 
2797 /* Run the various one-liner tests.  */
2798 
2799 static void
test_diagnostic_show_locus_one_liner(const line_table_case & case_)2800 test_diagnostic_show_locus_one_liner (const line_table_case &case_)
2801 {
2802   /* Create a tempfile and write some text to it.
2803      ....................0000000001111111.
2804      ....................1234567890123456.  */
2805   const char *content = "foo = bar.field;\n";
2806   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2807   line_table_test ltt (case_);
2808 
2809   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
2810 
2811   location_t line_end = linemap_position_for_column (line_table, 16);
2812 
2813   /* Don't attempt to run the tests if column data might be unavailable.  */
2814   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2815     return;
2816 
2817   ASSERT_STREQ (tmp.get_filename (), LOCATION_FILE (line_end));
2818   ASSERT_EQ (1, LOCATION_LINE (line_end));
2819   ASSERT_EQ (16, LOCATION_COLUMN (line_end));
2820 
2821   test_one_liner_simple_caret ();
2822   test_one_liner_caret_and_range ();
2823   test_one_liner_multiple_carets_and_ranges ();
2824   test_one_liner_fixit_insert_before ();
2825   test_one_liner_fixit_insert_after ();
2826   test_one_liner_fixit_remove ();
2827   test_one_liner_fixit_replace ();
2828   test_one_liner_fixit_replace_non_equal_range ();
2829   test_one_liner_fixit_replace_equal_secondary_range ();
2830   test_one_liner_fixit_validation_adhoc_locations ();
2831   test_one_liner_many_fixits_1 ();
2832   test_one_liner_many_fixits_2 ();
2833   test_one_liner_labels ();
2834 }
2835 
2836 /* Verify that gcc_rich_location::add_location_if_nearby works.  */
2837 
2838 static void
test_add_location_if_nearby(const line_table_case & case_)2839 test_add_location_if_nearby (const line_table_case &case_)
2840 {
2841   /* Create a tempfile and write some text to it.
2842      ...000000000111111111122222222223333333333.
2843      ...123456789012345678901234567890123456789.  */
2844   const char *content
2845     = ("struct same_line { double x; double y; ;\n" /* line 1.  */
2846        "struct different_line\n"                    /* line 2.  */
2847        "{\n"                                        /* line 3.  */
2848        "  double x;\n"                              /* line 4.  */
2849        "  double y;\n"                              /* line 5.  */
2850        ";\n");                                      /* line 6.  */
2851   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2852   line_table_test ltt (case_);
2853 
2854   const line_map_ordinary *ord_map
2855     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2856 					   tmp.get_filename (), 0));
2857 
2858   linemap_line_start (line_table, 1, 100);
2859 
2860   const location_t final_line_end
2861     = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
2862 
2863   /* Don't attempt to run the tests if column data might be unavailable.  */
2864   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2865     return;
2866 
2867   /* Test of add_location_if_nearby on the same line as the
2868      primary location.  */
2869   {
2870     const location_t missing_close_brace_1_39
2871       = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
2872     const location_t matching_open_brace_1_18
2873       = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
2874     gcc_rich_location richloc (missing_close_brace_1_39);
2875     bool added = richloc.add_location_if_nearby (matching_open_brace_1_18);
2876     ASSERT_TRUE (added);
2877     ASSERT_EQ (2, richloc.get_num_locations ());
2878     test_diagnostic_context dc;
2879     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2880     ASSERT_STREQ ("\n"
2881 		  " struct same_line { double x; double y; ;\n"
2882 		  "                  ~                    ^\n",
2883 		  pp_formatted_text (dc.printer));
2884   }
2885 
2886   /* Test of add_location_if_nearby on a different line to the
2887      primary location.  */
2888   {
2889     const location_t missing_close_brace_6_1
2890       = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
2891     const location_t matching_open_brace_3_1
2892       = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
2893     gcc_rich_location richloc (missing_close_brace_6_1);
2894     bool added = richloc.add_location_if_nearby (matching_open_brace_3_1);
2895     ASSERT_FALSE (added);
2896     ASSERT_EQ (1, richloc.get_num_locations ());
2897   }
2898 }
2899 
2900 /* Verify that we print fixits even if they only affect lines
2901    outside those covered by the ranges in the rich_location.  */
2902 
2903 static void
test_diagnostic_show_locus_fixit_lines(const line_table_case & case_)2904 test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
2905 {
2906   /* Create a tempfile and write some text to it.
2907      ...000000000111111111122222222223333333333.
2908      ...123456789012345678901234567890123456789.  */
2909   const char *content
2910     = ("struct point { double x; double y; };\n" /* line 1.  */
2911        "struct point origin = {x: 0.0,\n"        /* line 2.  */
2912        "                       y\n"              /* line 3.  */
2913        "\n"                                      /* line 4.  */
2914        "\n"                                      /* line 5.  */
2915        "                        : 0.0};\n");     /* line 6.  */
2916   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
2917   line_table_test ltt (case_);
2918 
2919   const line_map_ordinary *ord_map
2920     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
2921 					   tmp.get_filename (), 0));
2922 
2923   linemap_line_start (line_table, 1, 100);
2924 
2925   const location_t final_line_end
2926     = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
2927 
2928   /* Don't attempt to run the tests if column data might be unavailable.  */
2929   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
2930     return;
2931 
2932   /* A pair of tests for modernizing the initializers to C99-style.  */
2933 
2934   /* The one-liner case (line 2).  */
2935   {
2936     test_diagnostic_context dc;
2937     const location_t x
2938       = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
2939     const location_t colon
2940       = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
2941     rich_location richloc (line_table, colon);
2942     richloc.add_fixit_insert_before (x, ".");
2943     richloc.add_fixit_replace (colon, "=");
2944     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2945     ASSERT_STREQ ("\n"
2946 		  " struct point origin = {x: 0.0,\n"
2947 		  "                         ^\n"
2948 		  "                        .=\n",
2949 		  pp_formatted_text (dc.printer));
2950   }
2951 
2952   /* The multiline case.  The caret for the rich_location is on line 6;
2953      verify that insertion fixit on line 3 is still printed (and that
2954      span starts are printed due to the gap between the span at line 3
2955      and that at line 6).  */
2956   {
2957     test_diagnostic_context dc;
2958     const location_t y
2959       = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2960     const location_t colon
2961       = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2962     rich_location richloc (line_table, colon);
2963     richloc.add_fixit_insert_before (y, ".");
2964     richloc.add_fixit_replace (colon, "=");
2965     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2966     ASSERT_STREQ ("\n"
2967 		  "FILENAME:3:24:\n"
2968 		  "                        y\n"
2969 		  "                        .\n"
2970 		  "FILENAME:6:25:\n"
2971 		  "                         : 0.0};\n"
2972 		  "                         ^\n"
2973 		  "                         =\n",
2974 		  pp_formatted_text (dc.printer));
2975   }
2976 
2977   /* As above, but verify the behavior of multiple line spans
2978      with line-numbering enabled.  */
2979   {
2980     const location_t y
2981       = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
2982     const location_t colon
2983       = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
2984     rich_location richloc (line_table, colon);
2985     richloc.add_fixit_insert_before (y, ".");
2986     richloc.add_fixit_replace (colon, "=");
2987     test_diagnostic_context dc;
2988     dc.show_line_numbers_p = true;
2989     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
2990     ASSERT_STREQ ("\n"
2991 		  "    3 |                        y\n"
2992 		  "      |                        .\n"
2993 		  "......\n"
2994 		  "    6 |                         : 0.0};\n"
2995 		  "      |                         ^\n"
2996 		  "      |                         =\n",
2997 		  pp_formatted_text (dc.printer));
2998   }
2999 }
3000 
3001 
3002 /* Verify that fix-it hints are appropriately consolidated.
3003 
3004    If any fix-it hints in a rich_location involve locations beyond
3005    LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
3006    the fix-it as a whole, so there should be none.
3007 
3008    Otherwise, verify that consecutive "replace" and "remove" fix-its
3009    are merged, and that other fix-its remain separate.   */
3010 
3011 static void
test_fixit_consolidation(const line_table_case & case_)3012 test_fixit_consolidation (const line_table_case &case_)
3013 {
3014   line_table_test ltt (case_);
3015 
3016   linemap_add (line_table, LC_ENTER, false, "test.c", 1);
3017 
3018   const location_t c10 = linemap_position_for_column (line_table, 10);
3019   const location_t c15 = linemap_position_for_column (line_table, 15);
3020   const location_t c16 = linemap_position_for_column (line_table, 16);
3021   const location_t c17 = linemap_position_for_column (line_table, 17);
3022   const location_t c20 = linemap_position_for_column (line_table, 20);
3023   const location_t c21 = linemap_position_for_column (line_table, 21);
3024   const location_t caret = c10;
3025 
3026   /* Insert + insert. */
3027   {
3028     rich_location richloc (line_table, caret);
3029     richloc.add_fixit_insert_before (c10, "foo");
3030     richloc.add_fixit_insert_before (c15, "bar");
3031 
3032     if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3033       /* Bogus column info for 2nd fixit, so no fixits.  */
3034       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3035     else
3036       /* They should not have been merged.  */
3037       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3038   }
3039 
3040   /* Insert + replace. */
3041   {
3042     rich_location richloc (line_table, caret);
3043     richloc.add_fixit_insert_before (c10, "foo");
3044     richloc.add_fixit_replace (source_range::from_locations (c15, c17),
3045 			       "bar");
3046 
3047     if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3048       /* Bogus column info for 2nd fixit, so no fixits.  */
3049       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3050     else
3051       /* They should not have been merged.  */
3052       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3053   }
3054 
3055   /* Replace + non-consecutive insert. */
3056   {
3057     rich_location richloc (line_table, caret);
3058     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3059 			       "bar");
3060     richloc.add_fixit_insert_before (c17, "foo");
3061 
3062     if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3063       /* Bogus column info for 2nd fixit, so no fixits.  */
3064       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3065     else
3066       /* They should not have been merged.  */
3067       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3068   }
3069 
3070   /* Replace + non-consecutive replace. */
3071   {
3072     rich_location richloc (line_table, caret);
3073     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3074 			       "foo");
3075     richloc.add_fixit_replace (source_range::from_locations (c17, c20),
3076 			       "bar");
3077 
3078     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3079       /* Bogus column info for 2nd fixit, so no fixits.  */
3080       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3081     else
3082       /* They should not have been merged.  */
3083       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3084   }
3085 
3086   /* Replace + consecutive replace. */
3087   {
3088     rich_location richloc (line_table, caret);
3089     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3090 			       "foo");
3091     richloc.add_fixit_replace (source_range::from_locations (c16, c20),
3092 			       "bar");
3093 
3094     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3095       /* Bogus column info for 2nd fixit, so no fixits.  */
3096       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3097     else
3098       {
3099 	/* They should have been merged into a single "replace".  */
3100 	ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3101 	const fixit_hint *hint = richloc.get_fixit_hint (0);
3102 	ASSERT_STREQ ("foobar", hint->get_string ());
3103 	ASSERT_EQ (c10, hint->get_start_loc ());
3104 	ASSERT_EQ (c21, hint->get_next_loc ());
3105       }
3106   }
3107 
3108   /* Replace + consecutive removal. */
3109   {
3110     rich_location richloc (line_table, caret);
3111     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
3112 			       "foo");
3113     richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3114 
3115     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3116       /* Bogus column info for 2nd fixit, so no fixits.  */
3117       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3118     else
3119       {
3120 	/* They should have been merged into a single replace, with the
3121 	   range extended to cover that of the removal.  */
3122 	ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3123 	const fixit_hint *hint = richloc.get_fixit_hint (0);
3124 	ASSERT_STREQ ("foo", hint->get_string ());
3125 	ASSERT_EQ (c10, hint->get_start_loc ());
3126 	ASSERT_EQ (c21, hint->get_next_loc ());
3127       }
3128   }
3129 
3130   /* Consecutive removals. */
3131   {
3132     rich_location richloc (line_table, caret);
3133     richloc.add_fixit_remove (source_range::from_locations (c10, c15));
3134     richloc.add_fixit_remove (source_range::from_locations (c16, c20));
3135 
3136     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
3137       /* Bogus column info for 2nd fixit, so no fixits.  */
3138       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
3139     else
3140       {
3141 	/* They should have been merged into a single "replace-with-empty".  */
3142 	ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3143 	const fixit_hint *hint = richloc.get_fixit_hint (0);
3144 	ASSERT_STREQ ("", hint->get_string ());
3145 	ASSERT_EQ (c10, hint->get_start_loc ());
3146 	ASSERT_EQ (c21, hint->get_next_loc ());
3147       }
3148   }
3149 }
3150 
3151 /* Verify that the line_corrections machinery correctly prints
3152    overlapping fixit-hints.  */
3153 
3154 static void
test_overlapped_fixit_printing(const line_table_case & case_)3155 test_overlapped_fixit_printing (const line_table_case &case_)
3156 {
3157   /* Create a tempfile and write some text to it.
3158      ...000000000111111111122222222223333333333.
3159      ...123456789012345678901234567890123456789.  */
3160   const char *content
3161     = ("  foo *f = (foo *)ptr->field;\n");
3162   temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
3163   line_table_test ltt (case_);
3164 
3165   const line_map_ordinary *ord_map
3166     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3167 					   tmp.get_filename (), 0));
3168 
3169   linemap_line_start (line_table, 1, 100);
3170 
3171   const location_t final_line_end
3172     = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
3173 
3174   /* Don't attempt to run the tests if column data might be unavailable.  */
3175   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3176     return;
3177 
3178   /* A test for converting a C-style cast to a C++-style cast.  */
3179   const location_t open_paren
3180     = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
3181   const location_t close_paren
3182     = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
3183   const location_t expr_start
3184     = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
3185   const location_t expr_finish
3186     = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
3187   const location_t expr = make_location (expr_start, expr_start, expr_finish);
3188 
3189   /* Various examples of fix-it hints that aren't themselves consolidated,
3190      but for which the *printing* may need consolidation.  */
3191 
3192   /* Example where 3 fix-it hints are printed as one.  */
3193   {
3194     test_diagnostic_context dc;
3195     rich_location richloc (line_table, expr);
3196     richloc.add_fixit_replace (open_paren, "const_cast<");
3197     richloc.add_fixit_replace (close_paren, "> (");
3198     richloc.add_fixit_insert_after (")");
3199 
3200     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3201     ASSERT_STREQ ("\n"
3202 		  "   foo *f = (foo *)ptr->field;\n"
3203 		  "                   ^~~~~~~~~~\n"
3204 		  "            -----------------\n"
3205 		  "            const_cast<foo *> (ptr->field)\n",
3206 		  pp_formatted_text (dc.printer));
3207 
3208     /* Unit-test the line_corrections machinery.  */
3209     ASSERT_EQ (3, richloc.get_num_fixit_hints ());
3210     const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3211     ASSERT_EQ (column_range (12, 12), get_affected_columns (hint_0));
3212     ASSERT_EQ (column_range (12, 22), get_printed_columns (hint_0));
3213     const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3214     ASSERT_EQ (column_range (18, 18), get_affected_columns (hint_1));
3215     ASSERT_EQ (column_range (18, 20), get_printed_columns (hint_1));
3216     const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
3217     ASSERT_EQ (column_range (29, 28), get_affected_columns (hint_2));
3218     ASSERT_EQ (column_range (29, 29), get_printed_columns (hint_2));
3219 
3220     /* Add each hint in turn to a line_corrections instance,
3221        and verify that they are consolidated into one correction instance
3222        as expected.  */
3223     line_corrections lc (tmp.get_filename (), 1);
3224 
3225     /* The first replace hint by itself.  */
3226     lc.add_hint (hint_0);
3227     ASSERT_EQ (1, lc.m_corrections.length ());
3228     ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
3229     ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
3230     ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
3231 
3232     /* After the second replacement hint, they are printed together
3233        as a replacement (along with the text between them).  */
3234     lc.add_hint (hint_1);
3235     ASSERT_EQ (1, lc.m_corrections.length ());
3236     ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
3237     ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
3238     ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
3239 
3240     /* After the final insertion hint, they are all printed together
3241        as a replacement (along with the text between them).  */
3242     lc.add_hint (hint_2);
3243     ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
3244 		  lc.m_corrections[0]->m_text);
3245     ASSERT_EQ (1, lc.m_corrections.length ());
3246     ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
3247     ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
3248   }
3249 
3250   /* Example where two are consolidated during printing.  */
3251   {
3252     test_diagnostic_context dc;
3253     rich_location richloc (line_table, expr);
3254     richloc.add_fixit_replace (open_paren, "CAST (");
3255     richloc.add_fixit_replace (close_paren, ") (");
3256     richloc.add_fixit_insert_after (")");
3257 
3258     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3259     ASSERT_STREQ ("\n"
3260 		  "   foo *f = (foo *)ptr->field;\n"
3261 		  "                   ^~~~~~~~~~\n"
3262 		  "            -\n"
3263 		  "            CAST (-\n"
3264 		  "                  ) (        )\n",
3265 		  pp_formatted_text (dc.printer));
3266   }
3267 
3268   /* Example where none are consolidated during printing.  */
3269   {
3270     test_diagnostic_context dc;
3271     rich_location richloc (line_table, expr);
3272     richloc.add_fixit_replace (open_paren, "CST (");
3273     richloc.add_fixit_replace (close_paren, ") (");
3274     richloc.add_fixit_insert_after (")");
3275 
3276     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3277     ASSERT_STREQ ("\n"
3278 		  "   foo *f = (foo *)ptr->field;\n"
3279 		  "                   ^~~~~~~~~~\n"
3280 		  "            -\n"
3281 		  "            CST ( -\n"
3282 		  "                  ) (        )\n",
3283 		  pp_formatted_text (dc.printer));
3284   }
3285 
3286   /* Example of deletion fix-it hints.  */
3287   {
3288     test_diagnostic_context dc;
3289     rich_location richloc (line_table, expr);
3290     richloc.add_fixit_insert_before (open_paren, "(bar *)");
3291     source_range victim = {open_paren, close_paren};
3292     richloc.add_fixit_remove (victim);
3293 
3294     /* This case is actually handled by fixit-consolidation,
3295        rather than by line_corrections.  */
3296     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
3297 
3298     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3299     ASSERT_STREQ ("\n"
3300 		  "   foo *f = (foo *)ptr->field;\n"
3301 		  "                   ^~~~~~~~~~\n"
3302 		  "            -------\n"
3303 		  "            (bar *)\n",
3304 		  pp_formatted_text (dc.printer));
3305   }
3306 
3307   /* Example of deletion fix-it hints that would overlap.  */
3308   {
3309     test_diagnostic_context dc;
3310     rich_location richloc (line_table, expr);
3311     richloc.add_fixit_insert_before (open_paren, "(longer *)");
3312     source_range victim = {expr_start, expr_finish};
3313     richloc.add_fixit_remove (victim);
3314 
3315     /* These fixits are not consolidated.  */
3316     ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3317 
3318     /* But the corrections are.  */
3319     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3320     ASSERT_STREQ ("\n"
3321 		  "   foo *f = (foo *)ptr->field;\n"
3322 		  "                   ^~~~~~~~~~\n"
3323 		  "            -----------------\n"
3324 		  "            (longer *)(foo *)\n",
3325 		  pp_formatted_text (dc.printer));
3326   }
3327 
3328   /* Example of insertion fix-it hints that would overlap.  */
3329   {
3330     test_diagnostic_context dc;
3331     rich_location richloc (line_table, expr);
3332     richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
3333     richloc.add_fixit_insert_after (close_paren, "TEST");
3334 
3335     /* The first insertion is long enough that if printed naively,
3336        it would overlap with the second.
3337        Verify that they are printed as a single replacement.  */
3338     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3339     ASSERT_STREQ ("\n"
3340 		  "   foo *f = (foo *)ptr->field;\n"
3341 		  "                   ^~~~~~~~~~\n"
3342 		  "            -------\n"
3343 		  "            LONGER THAN THE CAST(foo *)TEST\n",
3344 		  pp_formatted_text (dc.printer));
3345   }
3346 }
3347 
3348 /* Verify that the line_corrections machinery correctly prints
3349    overlapping fixit-hints that have been added in the wrong
3350    order.
3351    Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
3352 
3353 static void
test_overlapped_fixit_printing_2(const line_table_case & case_)3354 test_overlapped_fixit_printing_2 (const line_table_case &case_)
3355 {
3356   /* Create a tempfile and write some text to it.
3357      ...000000000111111111122222222223333333333.
3358      ...123456789012345678901234567890123456789.  */
3359   const char *content
3360     = ("int a5[][0][0] = { 1, 2 };\n");
3361   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
3362   line_table_test ltt (case_);
3363 
3364   const line_map_ordinary *ord_map
3365     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
3366 					   tmp.get_filename (), 0));
3367 
3368   linemap_line_start (line_table, 1, 100);
3369 
3370   const location_t final_line_end
3371     = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
3372 
3373   /* Don't attempt to run the tests if column data might be unavailable.  */
3374   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
3375     return;
3376 
3377   const location_t col_1
3378     = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
3379   const location_t col_20
3380     = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
3381   const location_t col_21
3382     = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
3383   const location_t col_23
3384     = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
3385   const location_t col_25
3386     = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
3387 
3388   /* Two insertions, in the wrong order.  */
3389   {
3390     rich_location richloc (line_table, col_20);
3391     richloc.add_fixit_insert_before (col_23, "{");
3392     richloc.add_fixit_insert_before (col_21, "}");
3393 
3394     /* These fixits should be accepted; they can't be consolidated.  */
3395     ASSERT_EQ (2, richloc.get_num_fixit_hints ());
3396     const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
3397     ASSERT_EQ (column_range (23, 22), get_affected_columns (hint_0));
3398     ASSERT_EQ (column_range (23, 23), get_printed_columns (hint_0));
3399     const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
3400     ASSERT_EQ (column_range (21, 20), get_affected_columns (hint_1));
3401     ASSERT_EQ (column_range (21, 21), get_printed_columns (hint_1));
3402 
3403     /* Verify that they're printed correctly.  */
3404     test_diagnostic_context dc;
3405     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3406     ASSERT_STREQ ("\n"
3407 		  " int a5[][0][0] = { 1, 2 };\n"
3408 		  "                    ^\n"
3409 		  "                     } {\n",
3410 		  pp_formatted_text (dc.printer));
3411   }
3412 
3413   /* Various overlapping insertions, some occurring "out of order"
3414      (reproducing the fix-it hints from PR c/81405).  */
3415   {
3416     test_diagnostic_context dc;
3417     rich_location richloc (line_table, col_20);
3418 
3419     richloc.add_fixit_insert_before (col_20, "{{");
3420     richloc.add_fixit_insert_before (col_21, "}}");
3421     richloc.add_fixit_insert_before (col_23, "{");
3422     richloc.add_fixit_insert_before (col_21, "}");
3423     richloc.add_fixit_insert_before (col_23, "{{");
3424     richloc.add_fixit_insert_before (col_25, "}");
3425     richloc.add_fixit_insert_before (col_21, "}");
3426     richloc.add_fixit_insert_before (col_1, "{");
3427     richloc.add_fixit_insert_before (col_25, "}");
3428     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3429     ASSERT_STREQ ("\n"
3430 		  " int a5[][0][0] = { 1, 2 };\n"
3431 		  "                    ^\n"
3432 		  " {                  -----\n"
3433 		  "                    {{1}}}}, {{{2 }}\n",
3434 		  pp_formatted_text (dc.printer));
3435   }
3436 }
3437 
3438 /* Insertion fix-it hint: adding a "break;" on a line by itself.  */
3439 
3440 static void
test_fixit_insert_containing_newline(const line_table_case & case_)3441 test_fixit_insert_containing_newline (const line_table_case &case_)
3442 {
3443   /* Create a tempfile and write some text to it.
3444      .........................0000000001111111.
3445      .........................1234567890123456.  */
3446   const char *old_content = ("    case 'a':\n" /* line 1. */
3447 			     "      x = a;\n"  /* line 2. */
3448 			     "    case 'b':\n" /* line 3. */
3449 			     "      x = b;\n");/* line 4. */
3450 
3451   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3452   line_table_test ltt (case_);
3453   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
3454 
3455   location_t case_start = linemap_position_for_column (line_table, 5);
3456   location_t case_finish = linemap_position_for_column (line_table, 13);
3457   location_t case_loc = make_location (case_start, case_start, case_finish);
3458   location_t line_start = linemap_position_for_column (line_table, 1);
3459 
3460   if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3461     return;
3462 
3463   /* Add a "break;" on a line by itself before line 3 i.e. before
3464      column 1 of line 3. */
3465   {
3466     rich_location richloc (line_table, case_loc);
3467     richloc.add_fixit_insert_before (line_start, "      break;\n");
3468 
3469     /* Without line numbers.  */
3470     {
3471       test_diagnostic_context dc;
3472       diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3473       ASSERT_STREQ ("\n"
3474 		    "       x = a;\n"
3475 		    "+      break;\n"
3476 		    "     case 'b':\n"
3477 		    "     ^~~~~~~~~\n",
3478 		    pp_formatted_text (dc.printer));
3479     }
3480 
3481     /* With line numbers.  */
3482     {
3483       test_diagnostic_context dc;
3484       dc.show_line_numbers_p = true;
3485       diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3486       ASSERT_STREQ ("\n"
3487 		    "    2 |       x = a;\n"
3488 		    "  +++ |+      break;\n"
3489 		    "    3 |     case 'b':\n"
3490 		    "      |     ^~~~~~~~~\n",
3491 		    pp_formatted_text (dc.printer));
3492     }
3493   }
3494 
3495   /* Verify that attempts to add text with a newline fail when the
3496      insertion point is *not* at the start of a line.  */
3497   {
3498     rich_location richloc (line_table, case_loc);
3499     richloc.add_fixit_insert_before (case_start, "break;\n");
3500     ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3501     test_diagnostic_context dc;
3502     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3503     ASSERT_STREQ ("\n"
3504 		  "     case 'b':\n"
3505 		  "     ^~~~~~~~~\n",
3506 		  pp_formatted_text (dc.printer));
3507   }
3508 }
3509 
3510 /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
3511    of the file, where the fix-it is printed in a different line-span
3512    to the primary range of the diagnostic.  */
3513 
3514 static void
test_fixit_insert_containing_newline_2(const line_table_case & case_)3515 test_fixit_insert_containing_newline_2 (const line_table_case &case_)
3516 {
3517   /* Create a tempfile and write some text to it.
3518      .........................0000000001111111.
3519      .........................1234567890123456.  */
3520   const char *old_content = ("test (int ch)\n"  /* line 1. */
3521 			     "{\n"              /* line 2. */
3522 			     " putchar (ch);\n" /* line 3. */
3523 			     "}\n");            /* line 4. */
3524 
3525   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3526   line_table_test ltt (case_);
3527 
3528   const line_map_ordinary *ord_map = linemap_check_ordinary
3529     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3530   linemap_line_start (line_table, 1, 100);
3531 
3532   /* The primary range is the "putchar" token.  */
3533   location_t putchar_start
3534     = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
3535   location_t putchar_finish
3536     = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
3537   location_t putchar_loc
3538     = make_location (putchar_start, putchar_start, putchar_finish);
3539   rich_location richloc (line_table, putchar_loc);
3540 
3541   /* Add a "#include <stdio.h>" on a line by itself at the top of the file.  */
3542   location_t file_start
3543      = linemap_position_for_line_and_column (line_table, ord_map,  1, 1);
3544   richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
3545 
3546   if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3547     return;
3548 
3549   {
3550     test_diagnostic_context dc;
3551     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3552     ASSERT_STREQ ("\n"
3553 		  "FILENAME:1:1:\n"
3554 		  "+#include <stdio.h>\n"
3555 		  " test (int ch)\n"
3556 		  "FILENAME:3:2:\n"
3557 		  "  putchar (ch);\n"
3558 		  "  ^~~~~~~\n",
3559 		  pp_formatted_text (dc.printer));
3560   }
3561 
3562   /* With line-numbering, the line spans are close enough to be
3563      consolidated, since it makes little sense to skip line 2.  */
3564   {
3565     test_diagnostic_context dc;
3566     dc.show_line_numbers_p = true;
3567     diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3568     ASSERT_STREQ ("\n"
3569 		  "  +++ |+#include <stdio.h>\n"
3570 		  "    1 | test (int ch)\n"
3571 		  "    2 | {\n"
3572 		  "    3 |  putchar (ch);\n"
3573 		  "      |  ^~~~~~~\n",
3574 		  pp_formatted_text (dc.printer));
3575   }
3576 }
3577 
3578 /* Replacement fix-it hint containing a newline.
3579    This will fail, as newlines are only supported when inserting at the
3580    beginning of a line.  */
3581 
3582 static void
test_fixit_replace_containing_newline(const line_table_case & case_)3583 test_fixit_replace_containing_newline (const line_table_case &case_)
3584 {
3585   /* Create a tempfile and write some text to it.
3586     .........................0000000001111.
3587     .........................1234567890123.  */
3588   const char *old_content = "foo = bar ();\n";
3589 
3590   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3591   line_table_test ltt (case_);
3592   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
3593 
3594   /* Replace the " = " with "\n  = ", as if we were reformatting an
3595      overly long line.  */
3596   location_t start = linemap_position_for_column (line_table, 4);
3597   location_t finish = linemap_position_for_column (line_table, 6);
3598   location_t loc = linemap_position_for_column (line_table, 13);
3599   rich_location richloc (line_table, loc);
3600   source_range range = source_range::from_locations (start, finish);
3601   richloc.add_fixit_replace (range, "\n =");
3602 
3603   /* Arbitrary newlines are not yet supported within fix-it hints, so
3604      the fix-it should not be displayed.  */
3605   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3606 
3607   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3608     return;
3609 
3610   test_diagnostic_context dc;
3611   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3612   ASSERT_STREQ ("\n"
3613 		" foo = bar ();\n"
3614 		"             ^\n",
3615 		pp_formatted_text (dc.printer));
3616 }
3617 
3618 /* Fix-it hint, attempting to delete a newline.
3619    This will fail, as we currently only support fix-it hints that
3620    affect one line at a time.  */
3621 
3622 static void
test_fixit_deletion_affecting_newline(const line_table_case & case_)3623 test_fixit_deletion_affecting_newline (const line_table_case &case_)
3624 {
3625   /* Create a tempfile and write some text to it.
3626     ..........................0000000001111.
3627     ..........................1234567890123.  */
3628   const char *old_content = ("foo = bar (\n"
3629 			     "      );\n");
3630 
3631   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
3632   line_table_test ltt (case_);
3633   const line_map_ordinary *ord_map = linemap_check_ordinary
3634     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3635   linemap_line_start (line_table, 1, 100);
3636 
3637   /* Attempt to delete the " (\n...)".  */
3638   location_t start
3639     = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
3640   location_t caret
3641     = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
3642   location_t finish
3643     = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
3644   location_t loc = make_location (caret, start, finish);
3645   rich_location richloc (line_table, loc);
3646   richloc. add_fixit_remove ();
3647 
3648   /* Fix-it hints that affect more than one line are not yet supported, so
3649      the fix-it should not be displayed.  */
3650   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
3651 
3652   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
3653     return;
3654 
3655   test_diagnostic_context dc;
3656   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3657   ASSERT_STREQ ("\n"
3658 		" foo = bar (\n"
3659 		"          ~^\n"
3660 		"       );\n"
3661 		"       ~    \n",
3662 		pp_formatted_text (dc.printer));
3663 }
3664 
3665 /* Verify that line numbers are correctly printed for the case of
3666    a multiline range in which the width of the line numbers changes
3667    (e.g. from "9" to "10").  */
3668 
3669 static void
test_line_numbers_multiline_range()3670 test_line_numbers_multiline_range ()
3671 {
3672   /* Create a tempfile and write some text to it.  */
3673   pretty_printer pp;
3674   for (int i = 0; i < 20; i++)
3675     /* .........0000000001111111.
3676    .............1234567890123456.  */
3677     pp_printf (&pp, "this is line %i\n", i + 1);
3678   temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
3679   line_table_test ltt;
3680 
3681   const line_map_ordinary *ord_map = linemap_check_ordinary
3682     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
3683   linemap_line_start (line_table, 1, 100);
3684 
3685   /* Create a multi-line location, starting at the "line" of line 9, with
3686      a caret on the "is" of line 10, finishing on the "this" line 11.  */
3687 
3688   location_t start
3689     = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
3690   location_t caret
3691     = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
3692   location_t finish
3693     = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
3694   location_t loc = make_location (caret, start, finish);
3695 
3696   test_diagnostic_context dc;
3697   dc.show_line_numbers_p = true;
3698   dc.min_margin_width = 0;
3699   gcc_rich_location richloc (loc);
3700   diagnostic_show_locus (&dc, &richloc, DK_ERROR);
3701   ASSERT_STREQ ("\n"
3702 		" 9 | this is line 9\n"
3703 		"   |         ~~~~~~\n"
3704 		"10 | this is line 10\n"
3705 		"   | ~~~~~^~~~~~~~~~\n"
3706 		"11 | this is line 11\n"
3707 		"   | ~~~~  \n",
3708 		pp_formatted_text (dc.printer));
3709 }
3710 
3711 /* Run all of the selftests within this file.  */
3712 
3713 void
diagnostic_show_locus_c_tests()3714 diagnostic_show_locus_c_tests ()
3715 {
3716   test_line_span ();
3717 
3718   test_layout_range_for_single_point ();
3719   test_layout_range_for_single_line ();
3720   test_layout_range_for_multiple_lines ();
3721 
3722   test_get_line_width_without_trailing_whitespace ();
3723 
3724   test_diagnostic_show_locus_unknown_location ();
3725 
3726   for_each_line_table_case (test_diagnostic_show_locus_one_liner);
3727   for_each_line_table_case (test_add_location_if_nearby);
3728   for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
3729   for_each_line_table_case (test_fixit_consolidation);
3730   for_each_line_table_case (test_overlapped_fixit_printing);
3731   for_each_line_table_case (test_overlapped_fixit_printing_2);
3732   for_each_line_table_case (test_fixit_insert_containing_newline);
3733   for_each_line_table_case (test_fixit_insert_containing_newline_2);
3734   for_each_line_table_case (test_fixit_replace_containing_newline);
3735   for_each_line_table_case (test_fixit_deletion_affecting_newline);
3736 
3737   test_line_numbers_multiline_range ();
3738 }
3739 
3740 } // namespace selftest
3741 
3742 #endif /* #if CHECKING_P */
3743