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