nrelease - fix/improve livecd
[dragonfly.git] / contrib / gcc-8.0 / gcc / diagnostic-show-locus.c
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
82   void set_range (int range_idx) { set_state (range_idx); }
83   void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
84   void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
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:
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 {
158   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   }
163   linenum_type get_first_line () const { return m_first_line; }
164   linenum_type get_last_line () const { return m_last_line; }
165
166   bool contains_line_p (linenum_type line) const
167   {
168     return line >= m_first_line && line <= m_last_line;
169   }
170
171   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
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
239   int get_num_line_spans () const { return m_line_spans.length (); }
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 {
1440   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
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
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 {
1491   char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {}
1492
1493   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 {
1513   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
1524   ~correction () { free (m_text); }
1525
1526   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
1534   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
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
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 {
1591   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
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
1620   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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 */