LCOV - code coverage report
Current view: top level - gcc/diagnostics - source-printing.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 99.0 % 2814 2786
Test Date: 2026-10-17 16:30:31 Functions: 97.8 % 180 176
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Diagnostic subroutines for printing source-code
       2              :    Copyright (C) 1999-2026 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              : #define INCLUDE_MAP
      23              : #define INCLUDE_STRING
      24              : #define INCLUDE_VECTOR
      25              : #include "system.h"
      26              : #include "coretypes.h"
      27              : #include "version.h"
      28              : #include "intl.h"
      29              : #include "diagnostic.h"
      30              : #include "diagnostics/color.h"
      31              : #include "gcc-rich-location.h"
      32              : #include "text-range-label.h"
      33              : #include "selftest.h"
      34              : #include "diagnostics/selftest-context.h"
      35              : #include "diagnostics/selftest-source-printing.h"
      36              : #include "cpplib.h"
      37              : #include "text-art/types.h"
      38              : #include "text-art/theme.h"
      39              : #include "diagnostics/source-printing-effects.h"
      40              : #include "diagnostics/file-cache.h"
      41              : #include "xml.h"
      42              : #include "xml-printer.h"
      43              : 
      44              : #ifdef HAVE_TERMIOS_H
      45              : # include <termios.h>
      46              : #endif
      47              : 
      48              : #ifdef GWINSZ_IN_SYS_IOCTL
      49              : # include <sys/ioctl.h>
      50              : #endif
      51              : 
      52              : /* Disable warnings about quoting issues in the pp_xxx calls below
      53              :    that (intentionally) don't follow GCC diagnostic conventions.  */
      54              : #if __GNUC__ >= 10
      55              : #  pragma GCC diagnostic push
      56              : #  pragma GCC diagnostic ignored "-Wformat-diag"
      57              : #endif
      58              : 
      59              : /* Classes for rendering source code and diagnostics, within an
      60              :    anonymous namespace.
      61              :    The work is done by "class layout", which embeds and uses
      62              :    "class colorizer" and "class layout_range" to get things done.  */
      63              : 
      64              : namespace {
      65              : 
      66              : /* The state at a given point of the source code, assuming that we're
      67              :    in a range: which range are we in, and whether we should draw a caret at
      68              :    this point.  */
      69              : 
      70              : struct point_state
      71              : {
      72              :   int range_idx;
      73              :   bool draw_caret_p;
      74              : };
      75              : 
      76              : /* A class to inject colorization codes when printing the diagnostic locus
      77              :    as text.
      78              : 
      79              :    It has one kind of colorization for each of:
      80              :      - normal text
      81              :      - range 0 (the "primary location")
      82              :      - range 1
      83              :      - range 2
      84              : 
      85              :    The class caches the lookup of the color codes for the above.
      86              : 
      87              :    The class also has responsibility for tracking which of the above is
      88              :    active, filtering out unnecessary changes.  This allows
      89              :    layout_printer::print_source_line and layout_printer::print_annotation_line
      90              :    to simply request a colorization code for *every* character they print,
      91              :    via this class, and have the filtering be done for them here.  */
      92              : 
      93              : class colorizer
      94              : {
      95              :  public:
      96              :   colorizer (pretty_printer &pp,
      97              :              const rich_location &richloc,
      98              :              enum diagnostics::kind diagnostic_kind);
      99              :   ~colorizer ();
     100              : 
     101        61183 :   void set_range (int range_idx)
     102              :   {
     103              :     /* If we have a specific highlight color for the range, use it.  */
     104        61183 :     if (pp_show_highlight_colors (&m_pp))
     105              :       {
     106        36195 :         const location_range *const loc_range = m_richloc.get_range (range_idx);
     107        36195 :         if (loc_range->m_highlight_color)
     108              :           {
     109         4179 :             set_named_color (loc_range->m_highlight_color);
     110         4179 :             return;
     111              :           }
     112              :       }
     113              : 
     114              :     /* Otherwise, we emphasize the primary location, then alternate between
     115              :        two colors for the secondary locations.
     116              :        But if we're printing a run of events in a diagnostic path, that
     117              :        makes no sense, so print all of them with the same colorization.  */
     118        57004 :     if (m_diagnostic_kind == diagnostics::kind::path)
     119         9208 :       set_state (0);
     120              :     else
     121        47796 :       set_state (range_idx);
     122              :   }
     123        13116 :   void set_cfg_edge () { set_state (0); }
     124       242573 :   void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
     125        11088 :   void set_fixit_insert () { set_state (STATE_FIXIT_INSERT); }
     126         2460 :   void set_fixit_delete () { set_state (STATE_FIXIT_DELETE); }
     127              :   void set_named_color (const char *color);
     128              : 
     129              :  private:
     130              :   void set_state (int state);
     131              :   void begin_state (int state);
     132              :   void finish_state (int state);
     133              :   const char *get_color_by_name (const char *);
     134              : 
     135              :  private:
     136              :   static const int STATE_NORMAL_TEXT = -1;
     137              :   static const int STATE_FIXIT_INSERT  = -2;
     138              :   static const int STATE_FIXIT_DELETE  = -3;
     139              :   static const int STATE_NAMED_COLOR  = -4;
     140              : 
     141              :   pretty_printer &m_pp;
     142              :   const rich_location &m_richloc;
     143              :   enum diagnostics::kind m_diagnostic_kind;
     144              :   int m_current_state;
     145              :   const char *m_range1;
     146              :   const char *m_range2;
     147              :   const char *m_fixit_insert;
     148              :   const char *m_fixit_delete;
     149              :   const char *m_stop_color;
     150              :   std::string m_current_named_color;
     151              : };
     152              : 
     153              : /* In order to handle multibyte sources properly, all of this logic needs to be
     154              :    aware of the distinction between the number of bytes and the number of
     155              :    display columns occupied by a character, which are not the same for non-ASCII
     156              :    characters.  For example, the Unicode pi symbol, U+03C0, is encoded in UTF-8
     157              :    as "\xcf\x80", and thus occupies 2 bytes of space while only occupying 1
     158              :    display column when it is output.  A typical emoji, such as U+1F602 (in
     159              :    UTF-8, "\xf0\x9f\x98\x82"), requires 4 bytes and has a display width of 2.
     160              : 
     161              :    The below example line, which is also used for selftests below, shows how the
     162              :    display column and byte column are related:
     163              : 
     164              :      0000000001111111111222222   display
     165              :      1234567890123456789012345   columns
     166              :      SS_foo = P_bar.SS_fieldP;
     167              :      0000000111111111222222223   byte
     168              :      1356789012456789134567891   columns
     169              : 
     170              :    Here SS represents the two display columns for the U+1F602 emoji, and P
     171              :    represents the one display column for the U+03C0 pi symbol.  As an example, a
     172              :    diagnostic pointing to the final P on this line is at byte column 29 and
     173              :    display column 24.  This reflects the fact that the three extended characters
     174              :    before the final P occupy cumulatively 5 more bytes than they do display
     175              :    columns (a difference of 2 for each of the two SSs, and one for the other P).
     176              : 
     177              :    One or the other of the two column units is more useful depending on the
     178              :    context.  For instance, in order to output the caret at the correct location,
     179              :    we need to count display columns; in order to colorize a source line, we need
     180              :    to count the bytes.  All locations are provided to us as byte counts, which
     181              :    we augment with the display column on demand so that it can be used when
     182              :    needed.  This is not the most efficient way to do things since it requires
     183              :    looping over the whole line each time, but it should be fine for the purpose
     184              :    of outputting diagnostics.
     185              : 
     186              :    In order to keep straight which units (byte or display) are in use at a
     187              :    given time, the following enum lets us specify that explicitly.  */
     188              : 
     189              : enum column_unit {
     190              :   /* Measured in raw bytes.  */
     191              :   CU_BYTES = 0,
     192              : 
     193              :   /* Measured in display units.  */
     194              :   CU_DISPLAY_COLS,
     195              : 
     196              :   /* For arrays indexed by column_unit.  */
     197              :   CU_NUM_UNITS
     198              : };
     199              : 
     200              : /* Utility class to augment an exploc with the corresponding display column.  */
     201              : 
     202              : class exploc_with_display_col : public expanded_location
     203              : {
     204              :  public:
     205       241532 :   exploc_with_display_col (diagnostics::file_cache &fc,
     206              :                            const expanded_location &exploc,
     207              :                            const cpp_char_column_policy &policy,
     208              :                            enum location_aspect aspect)
     209              :   : expanded_location (exploc),
     210       241532 :     m_display_col (location_compute_display_column (fc, exploc, policy))
     211              :   {
     212       241532 :     if (exploc.column > 0)
     213              :       {
     214              :         /* m_display_col is now the final column of the byte.
     215              :            If escaping has happened, we may want the first column instead.  */
     216       217464 :         if (aspect != location_aspect::finish)
     217              :           {
     218       159882 :             expanded_location prev_exploc (exploc);
     219       159882 :             prev_exploc.column--;
     220       159882 :             int prev_display_col
     221       159882 :               = (location_compute_display_column (fc, prev_exploc, policy));
     222       159882 :             m_display_col = prev_display_col + 1;
     223              :           }
     224              :       }
     225       241532 :   }
     226              : 
     227              :   int m_display_col;
     228              : };
     229              : 
     230              : 
     231              : /* A point within a layout_range; similar to an exploc_with_display_col,
     232              :    but after filtering on file.  */
     233              : 
     234              : class layout_point
     235              : {
     236              :  public:
     237        63599 :   layout_point (const exploc_with_display_col &exploc)
     238        63599 :     : m_line (exploc.line)
     239              :   {
     240        63599 :     m_columns[CU_BYTES] = exploc.column;
     241        63599 :     m_columns[CU_DISPLAY_COLS] = exploc.m_display_col;
     242              :   }
     243              : 
     244              :   linenum_type m_line;
     245              :   int m_columns[CU_NUM_UNITS];
     246              : };
     247              : 
     248              : /* A class for use by "class layout" below: a filtered location_range.  */
     249              : 
     250              : class layout_range
     251              : {
     252              :  public:
     253              :   layout_range (const exploc_with_display_col &start_exploc,
     254              :                 const exploc_with_display_col &finish_exploc,
     255              :                 enum range_display_kind range_display_kind,
     256              :                 const exploc_with_display_col &caret_exploc,
     257              :                 unsigned original_idx,
     258              :                 const range_label *label);
     259              : 
     260              :   bool contains_point (linenum_type row, int column,
     261              :                        enum column_unit col_unit) const;
     262              :   bool intersects_line_p (linenum_type row) const;
     263              : 
     264              :   bool has_in_edge () const;
     265              :   bool has_out_edge () const;
     266              : 
     267              :   layout_point m_start;
     268              :   layout_point m_finish;
     269              :   enum range_display_kind m_range_display_kind;
     270              :   layout_point m_caret;
     271              :   unsigned m_original_idx;
     272              :   const range_label *m_label;
     273              : };
     274              : 
     275              : /* A struct for use by layout::print_source_line for telling
     276              :    layout::print_annotation_line the extents of the source line that
     277              :    it printed, so that underlines can be clipped appropriately.  Units
     278              :    are 1-based display columns.  */
     279              : 
     280              : struct line_bounds
     281              : {
     282              :   int m_first_non_ws_disp_col;
     283              :   int m_last_non_ws_disp_col;
     284              : 
     285              :   line_bounds ()
     286              :   {
     287              :     m_first_non_ws_disp_col = INT_MAX;
     288              :     m_last_non_ws_disp_col = 0;
     289              :   }
     290              : };
     291              : 
     292              : /* A range of contiguous source lines within a layout (e.g. "lines 5-10"
     293              :    or "line 23").  During the layout ctor, layout::calculate_line_spans
     294              :    splits the pertinent source lines into a list of disjoint line_span
     295              :    instances (e.g. lines 5-10, lines 15-20, line 23).  */
     296              : 
     297              : class line_span
     298              : {
     299              : public:
     300       124787 :   line_span (linenum_type first_line, linenum_type last_line)
     301       110364 :     : m_first_line (first_line), m_last_line (last_line)
     302              :   {
     303            0 :     gcc_assert (first_line <= last_line);
     304              :   }
     305        46122 :   linenum_type get_first_line () const { return m_first_line; }
     306        46122 :   linenum_type get_last_line () const { return m_last_line; }
     307              : 
     308         4239 :   bool contains_line_p (linenum_type line) const
     309              :   {
     310         3585 :     return line >= m_first_line && line <= m_last_line;
     311              :   }
     312              : 
     313       342297 :   static int comparator (const void *p1, const void *p2)
     314              :   {
     315       342297 :     const line_span *ls1 = (const line_span *)p1;
     316       342297 :     const line_span *ls2 = (const line_span *)p2;
     317       342297 :     int first_line_cmp = compare (ls1->m_first_line, ls2->m_first_line);
     318        17860 :     if (first_line_cmp)
     319              :       return first_line_cmp;
     320       307026 :     return compare (ls1->m_last_line, ls2->m_last_line);
     321              :   }
     322              : 
     323              :   linenum_type m_first_line;
     324              :   linenum_type m_last_line;
     325              : };
     326              : 
     327              : #if CHECKING_P
     328              : 
     329              : /* Selftests for line_span.  */
     330              : 
     331              : static void
     332            4 : test_line_span ()
     333              : {
     334            4 :   line_span line_one (1, 1);
     335            4 :   ASSERT_EQ (1, line_one.get_first_line ());
     336            4 :   ASSERT_EQ (1, line_one.get_last_line ());
     337            4 :   ASSERT_FALSE (line_one.contains_line_p (0));
     338            4 :   ASSERT_TRUE (line_one.contains_line_p (1));
     339            4 :   ASSERT_FALSE (line_one.contains_line_p (2));
     340              : 
     341            4 :   line_span lines_1_to_3 (1, 3);
     342            4 :   ASSERT_EQ (1, lines_1_to_3.get_first_line ());
     343            4 :   ASSERT_EQ (3, lines_1_to_3.get_last_line ());
     344            4 :   ASSERT_TRUE (lines_1_to_3.contains_line_p (1));
     345            4 :   ASSERT_TRUE (lines_1_to_3.contains_line_p (3));
     346              : 
     347            4 :   ASSERT_EQ (0, line_span::comparator (&line_one, &line_one));
     348            4 :   ASSERT_GT (line_span::comparator (&lines_1_to_3, &line_one), 0);
     349            4 :   ASSERT_LT (line_span::comparator (&line_one, &lines_1_to_3), 0);
     350              : 
     351              :   /* A linenum > 2^31.  */
     352            4 :   const linenum_type LARGEST_LINE = 0xffffffff;
     353            4 :   line_span largest_line (LARGEST_LINE, LARGEST_LINE);
     354            4 :   ASSERT_EQ (LARGEST_LINE, largest_line.get_first_line ());
     355            4 :   ASSERT_EQ (LARGEST_LINE, largest_line.get_last_line ());
     356              : 
     357            4 :   ASSERT_GT (line_span::comparator (&largest_line, &line_one), 0);
     358            4 :   ASSERT_LT (line_span::comparator (&line_one, &largest_line), 0);
     359            4 : }
     360              : 
     361              : #endif /* #if CHECKING_P */
     362              : 
     363              : /* A bundle of information containing how to print unicode
     364              :    characters and bytes when quoting source code.
     365              : 
     366              :    Provides a unified place to support escaping some subset
     367              :    of characters to some format.
     368              : 
     369              :    Extends char_column_policy; printing is split out to avoid
     370              :    libcpp having to know about pretty_printer.  */
     371              : 
     372              : struct char_display_policy : public cpp_char_column_policy
     373              : {
     374              :  public:
     375        50923 :   char_display_policy (int tabstop,
     376              :                        int (*width_cb) (cppchar_t c),
     377              :                        void (*print_text_cb) (diagnostics::to_text &text_out,
     378              :                                               const cpp_decoded_char &cp),
     379              :                        void (*print_html_cb) (diagnostics::to_html &html_out,
     380              :                                               const cpp_decoded_char &cp))
     381              :   : cpp_char_column_policy (tabstop, width_cb),
     382        50923 :     m_print_text_cb (print_text_cb),
     383        50923 :     m_print_html_cb (print_html_cb)
     384              :   {
     385              :   }
     386              : 
     387              :   void (*m_print_text_cb) (diagnostics::to_text &text_out,
     388              :                            const cpp_decoded_char &cp);
     389              :   void (*m_print_html_cb) (diagnostics::to_html &html_out,
     390              :                            const cpp_decoded_char &cp);
     391              : };
     392              : 
     393              : template <typename TextOrHtml> class layout_printer;
     394              : 
     395              : } // anonymous namespace
     396              : 
     397              : namespace diagnostics {
     398              : 
     399              : /* This code is written generically to write either:
     400              :    - text, to a pretty_printer, potentially with colorization codes, or
     401              :    - html, to an xml::printer, with nested HTML tags.
     402              : 
     403              :    This is handled via a "TextOrHtml" template, which is either to_text
     404              :    or to_html.  */
     405              : 
     406              : /* Writing text output.  */
     407              : 
     408              : struct to_text
     409              : {
     410              :   friend class layout_printer<to_text>;
     411              : 
     412              :   // This is a RAII class for HTML, but is a no-op for text.
     413              :   struct auto_check_tag_nesting
     414              :   {
     415       138423 :     auto_check_tag_nesting (to_text &) {}
     416              :   };
     417              : 
     418        45040 :   to_text (pretty_printer &pp,
     419              :            colorizer &colorizer)
     420        45040 :   : m_pp (pp),
     421        45040 :     m_colorizer (&colorizer)
     422              :   {
     423         1408 :     m_saved_rule = pp_prefixing_rule (&m_pp);
     424        45040 :     pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
     425              :   }
     426         1330 :   to_text (pretty_printer &pp,
     427              :            colorizer *colorizer)
     428         1330 :   : m_pp (pp),
     429         1330 :     m_colorizer (colorizer)
     430              :   {
     431         1330 :     m_saved_rule = pp_prefixing_rule (&m_pp);
     432         1330 :     pp_prefixing_rule (&m_pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
     433              :   }
     434        46370 :   ~to_text ()
     435              :   {
     436              : 
     437        46370 :     pp_prefixing_rule (&m_pp) = m_saved_rule;
     438        45040 :   }
     439              : 
     440              :   static bool is_text () { return true; }
     441              :   static bool is_html () { return false; }
     442              : 
     443       109190 :   void emit_text_prefix ()
     444              :   {
     445       217921 :     pp_emit_prefix (&m_pp);
     446              :   }
     447              : 
     448              :   void push_html_tag (const char *, bool)
     449              :   {
     450              :     // no-op for text
     451              :   }
     452              :   void push_html_tag_with_class (const char *, const char *, bool)
     453              :   {
     454              :     // no-op for text
     455              :   }
     456              :   void pop_html_tag (const char *)
     457              :   {
     458              :     // no-op for text
     459              :   }
     460              : 
     461              :   void add_html_tag_with_class (const char *, const char *, bool)
     462              :   {
     463              :     // no-op for text
     464              :   }
     465              : 
     466      4529437 :   void add_space ()
     467              :   {
     468          128 :     pp_space (&m_pp);
     469              :   }
     470              : 
     471     13064448 :   void add_character (cppchar_t ch)
     472              :   {
     473         6949 :     pp_unicode_character (&m_pp, ch);
     474              :   }
     475              : 
     476      3865109 :   void add_utf8_byte (char b)
     477              :   {
     478      3865109 :     pp_character (&m_pp, b);
     479              :   }
     480              : 
     481       110960 :   void add_text (const char *text)
     482              :   {
     483       110960 :     pp_string (&m_pp, text);
     484              :   }
     485              : 
     486      3863659 :   void print_decoded_char (const char_display_policy &char_policy,
     487              :                            cpp_decoded_char cp)
     488              :   {
     489      3863659 :     char_policy.m_print_text_cb (*this, cp);
     490              :   }
     491              : 
     492       242573 :   void colorize_text_ensure_normal ()
     493              :   {
     494       242573 :     gcc_assert (m_colorizer);
     495       242573 :     m_colorizer->set_normal_text ();
     496       242573 :   }
     497              : 
     498              :   void colorize_text_for_range_idx (int range_idx)
     499              :   {
     500              :     gcc_assert (m_colorizer);
     501              :     m_colorizer->set_range (range_idx);
     502              :   }
     503              : 
     504        13116 :   void colorize_text_for_cfg_edge ()
     505              :   {
     506        13116 :     gcc_assert (m_colorizer);
     507        13116 :     m_colorizer->set_cfg_edge ();
     508        13116 :   }
     509              : 
     510        11088 :   void colorize_text_for_fixit_insert ()
     511              :   {
     512        11088 :     gcc_assert (m_colorizer);
     513        11088 :     m_colorizer->set_fixit_insert ();
     514        11088 :   }
     515              : 
     516         2460 :   void colorize_text_for_fixit_delete ()
     517              :   {
     518         2460 :     gcc_assert (m_colorizer);
     519         2460 :     m_colorizer->set_fixit_delete ();
     520         2460 :   }
     521              : 
     522              :   void
     523          463 :   invoke_start_span_fn (const source_print_policy &source_policy,
     524              :                         const location_print_policy &loc_policy,
     525              :                         const expanded_location &exploc)
     526              :   {
     527          463 :     source_policy.get_text_start_span_fn () (loc_policy, *this, exploc);
     528              :   }
     529              : 
     530              :   // Text-specific functions
     531       109106 :   void add_newline ()
     532              :   {
     533       109106 :     pp_newline (&m_pp);
     534              :   }
     535              : 
     536              :   pretty_printer &m_pp;
     537              : private:
     538              :   colorizer *m_colorizer;
     539              :   diagnostic_prefixing_rule_t m_saved_rule;
     540              : };
     541              : 
     542              : /* Writing HTML output.  */
     543              : 
     544          560 : struct to_html
     545              : {
     546              :   friend class layout_printer<to_html>;
     547              : 
     548              :   // RAII class for ensuring that the tags nested correctly
     549         1746 :   struct auto_check_tag_nesting : public xml::auto_check_tag_nesting
     550              :   {
     551              :   public:
     552          873 :     auto_check_tag_nesting (to_html &out)
     553         1746 :     : xml::auto_check_tag_nesting (out.m_xp)
     554              :     {
     555              :     }
     556              :   };
     557              : 
     558          280 :   to_html (xml::printer &xp,
     559              :            const rich_location *richloc,
     560              :            html_label_writer *html_label_writer)
     561          280 :   : m_xp (xp),
     562          280 :     m_richloc (richloc),
     563          280 :     m_html_label_writer (html_label_writer)
     564              :   {}
     565              : 
     566              :   static bool is_text () { return false; }
     567              :   static bool is_html () { return true; }
     568              : 
     569              :   void emit_text_prefix ()
     570              :   {
     571              :     // no-op for HTML
     572              :   }
     573              : 
     574         1025 :   void push_html_tag (const char *name,
     575              :                       bool preserve_whitespace)
     576              :   {
     577         1025 :     m_xp.push_tag (name, preserve_whitespace);
     578         1025 :   }
     579              : 
     580         3202 :   void push_html_tag_with_class (const char *name,
     581              :                                  const char *class_,
     582              :                                  bool preserve_whitespace)
     583              :   {
     584         3202 :     m_xp.push_tag_with_class (name, class_, preserve_whitespace);
     585         3202 :   }
     586              : 
     587         4227 :   void pop_html_tag (const char *expected_name)
     588              :   {
     589         4227 :     m_xp.pop_tag (expected_name);
     590              :   }
     591              : 
     592              :   void add_html_tag_with_class (const char *name,
     593              :                                 const char *class_,
     594              :                                 bool preserve_whitespace)
     595              :   {
     596              :     auto element = std::make_unique<xml::element> (name, preserve_whitespace);
     597              :     element->set_attr ("class", class_);
     598              :     m_xp.append (std::move (element));
     599              :   }
     600              : 
     601            0 :   void add_raw_html (const char *src)
     602              :   {
     603            0 :     m_xp.add_raw (src);
     604            0 :   }
     605              : 
     606         7571 :   void add_space ()
     607              :   {
     608         7571 :     m_xp.add_text (" ");
     609         7571 :   }
     610              : 
     611         7388 :   void add_character (cppchar_t ch)
     612              :   {
     613         7388 :     pp_clear_output_area (&m_scratch_pp);
     614         7388 :     pp_unicode_character (&m_scratch_pp, ch);
     615         7388 :     m_xp.add_text_from_pp (m_scratch_pp);
     616         7388 :   }
     617              : 
     618        12648 :   void add_utf8_byte (char b)
     619              :   {
     620        12648 :     m_xp.add_text (std::string (1, b));
     621        12648 :   }
     622              : 
     623          471 :   void add_text (const char *text)
     624              :   {
     625          471 :     m_xp.add_text (text);
     626          471 :   }
     627              : 
     628        12648 :   void print_decoded_char (const char_display_policy &char_policy,
     629              :                            cpp_decoded_char cp)
     630              :   {
     631        12648 :     char_policy.m_print_html_cb (*this, cp);
     632              :   }
     633              : 
     634              :   void colorize_text_ensure_normal ()
     635              :   {
     636              :     // no-op for HTML
     637              :   }
     638              : 
     639              :   void colorize_text_for_cfg_edge ()
     640              :   {
     641              :     // no-op for HTML
     642              :   }
     643              : 
     644              :   void colorize_text_for_fixit_insert ()
     645              :   {
     646              :     // no-op for HTML
     647              :   }
     648              : 
     649              :   void colorize_text_for_fixit_delete ()
     650              :   {
     651              :     // no-op for HTML
     652              :   }
     653              : 
     654              :   void
     655            0 :   invoke_start_span_fn (const source_print_policy &source_policy,
     656              :                         const location_print_policy &loc_policy,
     657              :                         const expanded_location &exploc)
     658              :   {
     659            0 :     source_policy.get_html_start_span_fn () (loc_policy, *this, exploc);
     660              :   }
     661              : 
     662              :   const location_range *
     663         2720 :   get_location_range_by_idx (int range_idx)
     664              :   {
     665         2720 :     if (!m_richloc)
     666              :       return nullptr;
     667         2720 :     return m_richloc->get_range (range_idx);
     668              :   }
     669              : 
     670              :   const char *
     671         2720 :   get_highlight_color_for_range_idx (int range_idx)
     672              :   {
     673         2720 :     const location_range *const loc_range
     674         5440 :       = get_location_range_by_idx (range_idx);
     675         2720 :     if (!loc_range)
     676              :       return nullptr;
     677         2720 :     return loc_range->m_highlight_color;
     678              :   }
     679              : 
     680              :   xml::printer &m_xp;
     681              :   const rich_location *m_richloc;
     682              : private:
     683              :   html_label_writer *m_html_label_writer;
     684              :   pretty_printer m_scratch_pp;
     685              : };
     686              : 
     687              : void
     688         1330 : location_print_policy::
     689              : print_text_span_start (const diagnostics::context &dc,
     690              :                        pretty_printer &pp,
     691              :                        const expanded_location &exploc)
     692              : {
     693         1330 :   to_text out (pp, nullptr);
     694         1330 :   source_print_policy source_policy (dc);
     695         1330 :   source_policy.get_text_start_span_fn () (*this, out, exploc);
     696         1330 : }
     697              : 
     698              : void
     699            1 : location_print_policy::
     700              : print_html_span_start (const diagnostics::context &dc,
     701              :                        xml::printer &xp,
     702              :                        const expanded_location &exploc)
     703              : {
     704            1 :   to_html out (xp, nullptr, nullptr);
     705            1 :   source_print_policy source_policy (dc);
     706            1 :   source_policy.get_html_start_span_fn () (*this, out, exploc);
     707            1 : }
     708              : 
     709              : pretty_printer *
     710            1 : get_printer (to_text &out)
     711              : {
     712            1 :   return &out.m_pp;
     713              : }
     714              : 
     715              : template<>
     716              : void
     717         1792 : default_start_span_fn<to_text> (const location_print_policy &loc_policy,
     718              :                                 to_text &out,
     719              :                                 expanded_location exploc)
     720              : {
     721         1792 :   const column_policy &column_policy_ = loc_policy.get_column_policy ();
     722         1792 :   label_text text
     723              :     = column_policy_.get_location_text (exploc,
     724              :                                         loc_policy.show_column_p (),
     725         1792 :                                         pp_show_color (&out.m_pp));
     726         1792 :   pp_string (&out.m_pp, text.get ());
     727         1792 :   pp_newline (&out.m_pp);
     728         1792 : }
     729              : 
     730              : template<>
     731              : void
     732            1 : default_start_span_fn<to_html> (const location_print_policy &loc_policy,
     733              :                                 to_html &out,
     734              :                                 expanded_location exploc)
     735              : {
     736            1 :   const column_policy &column_policy_
     737            1 :     = loc_policy.get_column_policy ();
     738            1 :   label_text text
     739              :     = column_policy_.get_location_text (exploc,
     740              :                                         loc_policy.show_column_p (),
     741            1 :                                         false);
     742            1 :   out.m_xp.push_tag_with_class ("span", "location", true);
     743            1 :   out.m_xp.add_text (text.get ());
     744            1 :   out.m_xp.pop_tag ("span");
     745            1 : }
     746              : 
     747              : } // namespace diagnostics
     748              : 
     749              : namespace {
     750              : 
     751              : /* A class to control the overall layout when printing a diagnostic.
     752              : 
     753              :    The layout is determined within the constructor.
     754              : 
     755              :    Printing the layout is handled by class layout_printer.  This separation
     756              :    is to avoid depending on the pretty_printer in the layout.
     757              : 
     758              :    We assume we have disjoint ranges.  */
     759              : 
     760              : class layout
     761              : {
     762              :  public:
     763              :   friend class layout_printer<diagnostics::to_text>;
     764              :   friend class layout_printer<diagnostics::to_html>;
     765              : 
     766              :   layout (const diagnostics::source_print_policy &source_policy,
     767              :           const rich_location &richloc,
     768              :           diagnostics::source_effect_info *effect_info = nullptr);
     769              : 
     770              :   bool maybe_add_location_range (const location_range *loc_range,
     771              :                                  unsigned original_idx,
     772              :                                  bool restrict_to_current_line_spans);
     773              : 
     774       189454 :   int get_num_line_spans () const { return m_line_spans.length (); }
     775       136414 :   const line_span *get_line_span (int idx) const { return &m_line_spans[idx]; }
     776              : 
     777        91830 :   int get_linenum_width () const { return m_linenum_width; }
     778         1472 :   int get_x_offset_display () const { return m_x_offset_display; }
     779              : 
     780              :   bool print_heading_for_line_span_index_p (int line_span_idx) const;
     781              : 
     782              :   expanded_location get_expanded_location (const line_span *) const;
     783              : 
     784              :   void on_bad_codepoint (const char *ptr, cppchar_t ch, size_t ch_sz);
     785              : 
     786              :  private:
     787              :   bool will_show_line_p (linenum_type row) const;
     788              :   bool should_print_annotation_line_p (linenum_type row) const;
     789              : 
     790              :   bool annotation_line_showed_range_p (linenum_type line, int start_column,
     791              :                                        int finish_column) const;
     792              :   bool validate_fixit_hint_p (const fixit_hint *hint);
     793              : 
     794              :   void calculate_line_spans ();
     795              :   void calculate_linenum_width ();
     796              :   void calculate_x_offset_display ();
     797              : 
     798              :   bool
     799              :   get_state_at_point (/* Inputs.  */
     800              :                       linenum_type row, int column,
     801              :                       int first_non_ws, int last_non_ws,
     802              :                       enum column_unit col_unit,
     803              :                       /* Outputs.  */
     804              :                       point_state *out_state) const;
     805              : 
     806              :   int
     807              :   get_x_bound_for_row (linenum_type row, int caret_column,
     808              :                        int last_non_ws) const;
     809              : 
     810              :  private:
     811              :   bool compatible_locations_p (location_t loc_a, location_t loc_b) const;
     812              : 
     813              :   const diagnostics::source_printing_options &m_options;
     814              :   const line_maps *m_line_table;
     815              :   diagnostics::file_cache &m_file_cache;
     816              :   const text_art::ascii_theme m_fallback_theme;
     817              :   const text_art::theme &m_theme;
     818              :   diagnostics::source_effect_info *m_effect_info;
     819              :   char_display_policy m_char_policy;
     820              :   location_t m_primary_loc;
     821              :   exploc_with_display_col m_exploc;
     822              :   auto_vec <layout_range> m_layout_ranges;
     823              :   auto_vec <const fixit_hint *> m_fixit_hints;
     824              :   auto_vec <line_span> m_line_spans;
     825              :   int m_linenum_width;
     826              :   int m_x_offset_display;
     827              :   bool m_escape_on_output;
     828              : };
     829              : 
     830              : class line_label;
     831              : 
     832              : enum class margin_kind
     833              : {
     834              :   normal,
     835              :   insertion,
     836              :   ruler
     837              : };
     838              : 
     839              : /* A bundle of state for printing a particular layout
     840              :    to a particular TextOrHtml (either to_text or to_html).  */
     841              : template <typename TextOrHtml>
     842              : class layout_printer
     843              : {
     844              : public:
     845              :   layout_printer (TextOrHtml &text_or_html,
     846              :                   const layout &layout,
     847              :                   bool is_diagnostic_path);
     848              : 
     849              :   void print (const diagnostics::source_print_policy &source_policy);
     850              : 
     851              : private:
     852              :   const diagnostics::source_printing_options &
     853       414006 :   get_options () const { return m_layout.m_options; }
     854              : 
     855              :   const text_art::theme &
     856        15684 :   get_theme () const { return m_layout.m_theme; }
     857              : 
     858              :   void show_ruler (int max_column);
     859              :   void print_gap_in_line_numbering ();
     860              :   void print_leading_fixits (linenum_type row);
     861              :   void print_line (linenum_type row);
     862              :   line_bounds print_source_line (linenum_type row, const char *line,
     863              :                                  int line_bytes);
     864              :   void print_leftmost_column ();
     865              :   void start_annotation_line (enum margin_kind);
     866              :   void end_line ();
     867              :   void print_annotation_line (linenum_type row, const line_bounds lbounds);
     868              :   void print_any_labels (linenum_type row);
     869              :   void begin_label (int state_idx, bool is_label_text);
     870              :   void end_label (int state_idx, bool is_label_text);
     871              :   void print_trailing_fixits (linenum_type row);
     872              : 
     873              :   void
     874              :   move_to_column (int *column, int dest_column, bool add_left_margin);
     875              : 
     876              :   void print_any_right_to_left_edge_lines ();
     877              : 
     878              :   void set_in_range (int range_idx);
     879              :   void set_outside_range ();
     880              : 
     881              : private:
     882              :   TextOrHtml &m_text_or_html;
     883              :   const layout &m_layout;
     884              :   bool m_is_diagnostic_path;
     885              : 
     886              :   bool m_was_in_range_p;
     887              :   int m_last_range_idx;
     888              : 
     889              :   /* Fields for handling links between labels (e.g. for showing CFG edges
     890              :      in execution paths).
     891              :      Note that the logic for printing such links makes various simplifying
     892              :      assumptions about the set of labels in the rich_location, and users
     893              :      of this code will need to split up labels into separate rich_location
     894              :      instances to respect these assumptions, or the output will look wrong.
     895              :      See the diagnostic_path-printing code for more information.  */
     896              : 
     897              :   /* An enum for describing the state of the leftmost column,
     898              :      used for showing links between labels.
     899              :      Consider e.g.
     900              :      .x0000000001111111111222222222233333333334444444444.
     901              :      .x1234567890123456789012345678901234567890123456789.
     902              :      |      |                                    <- none
     903              :      |      (9) following ‘false’ branch... ->-+ <- none
     904              :      |                                         | <- none
     905              :      |                                         | <- none
     906              :      |+----------------------------------------+ <- rewinding to lhs
     907              :      ||  result->i = i;                          <- at lhs
     908              :      ||  ~~~~~~~~~~^~~                           <- at lhs
     909              :      ||            |                             <- at lhs
     910              :      |+----------->(10) ...to here               <- indenting to dest
     911              :      ^^
     912              :      ||
     913              :      |leftmost column ("x" above).
     914              :      "margin".  */
     915              :   enum class link_lhs_state {
     916              :     none,
     917              :     rewinding_to_lhs,
     918              :     at_lhs,
     919              :     indenting_to_dest
     920              :   } m_link_lhs_state;
     921              : 
     922              :   /* The column of the current link on the RHS, if any, or
     923              :      -1 if there is none.
     924              :      Consider e.g.
     925              :      .x0000000001111111111222222222233333333334444444444.
     926              :      .x1234567890123456789012345678901234567890123456789.
     927              :      |      |                                     <- -1
     928              :      |      (10) following ‘false’ branch... ->-+ <- 42
     929              :      |                                          | <- 42
     930              :      |                                          | <- 42
     931              :      |+-----------------------------------------+ <- 42
     932              :      ||  result->i = i;                           <- -1
     933              :      ||  ~~~~~~~~~~^~~                            <- -1
     934              :      ||            |                              <- -1
     935              :      |+----------->(11) ...to here                <- -1.  */
     936              :   int m_link_rhs_column;
     937              : };
     938              : 
     939              : /* Implementation of "class colorizer".  */
     940              : 
     941              : /* The constructor for "colorizer".  Lookup and store color codes for the
     942              :    different kinds of things we might need to print.  */
     943              : 
     944        45040 : colorizer::colorizer (pretty_printer &pp,
     945              :                       const rich_location &richloc,
     946              :                       enum diagnostics::kind diagnostic_kind) :
     947        45040 :   m_pp (pp),
     948        45040 :   m_richloc (richloc),
     949        45040 :   m_diagnostic_kind (diagnostic_kind),
     950        45040 :   m_current_state (STATE_NORMAL_TEXT)
     951              : {
     952        90080 :   m_range1 = get_color_by_name ("range1");
     953        90080 :   m_range2 = get_color_by_name ("range2");
     954        90080 :   m_fixit_insert = get_color_by_name ("fixit-insert");
     955        90080 :   m_fixit_delete = get_color_by_name ("fixit-delete");
     956        45040 :   m_stop_color = colorize_stop (pp_show_color (&m_pp));
     957        45040 : }
     958              : 
     959              : /* The destructor for "colorize".  If colorization is on, print a code to
     960              :    turn it off.  */
     961              : 
     962        45040 : colorizer::~colorizer ()
     963              : {
     964        90080 :   finish_state (m_current_state);
     965        45040 : }
     966              : 
     967              : /* Update state, changing to the specific named color and printing its
     968              :    color codes.  */
     969              : 
     970              : void
     971         4179 : colorizer::set_named_color (const char *color)
     972              : {
     973         4179 :   if (m_current_state == STATE_NAMED_COLOR
     974         4179 :       && color == m_current_named_color)
     975              :     return;
     976         8358 :   finish_state (m_current_state);
     977         4179 :   m_current_state = STATE_NAMED_COLOR;
     978         4179 :   pp_string (&m_pp, colorize_start (pp_show_color (&m_pp), color));
     979         4179 :   m_current_named_color = color;
     980              : }
     981              : 
     982              : /* Update state, printing color codes if necessary if there's a state
     983              :    change.  */
     984              : 
     985              : void
     986       326241 : colorizer::set_state (int new_state)
     987              : {
     988       326241 :   if (m_current_state != new_state)
     989              :     {
     990       343030 :       finish_state (m_current_state);
     991       171515 :       m_current_state = new_state;
     992       171515 :       begin_state (new_state);
     993              :     }
     994       326241 : }
     995              : 
     996              : /* Turn on any colorization for STATE.  */
     997              : 
     998              : void
     999       171515 : colorizer::begin_state (int state)
    1000              : {
    1001       171515 :   switch (state)
    1002              :     {
    1003              :     case STATE_NORMAL_TEXT:
    1004              :       break;
    1005              : 
    1006        11088 :     case STATE_FIXIT_INSERT:
    1007        11088 :       pp_string (&m_pp, m_fixit_insert);
    1008        11088 :       break;
    1009              : 
    1010         2460 :     case STATE_FIXIT_DELETE:
    1011         2460 :       pp_string (&m_pp, m_fixit_delete);
    1012         2460 :       break;
    1013              : 
    1014            0 :     case STATE_NAMED_COLOR:
    1015              :       /* Should be handled by colorizer::set_named_color.  */
    1016            0 :       gcc_unreachable ();
    1017              : 
    1018        62392 :     case 0:
    1019              :       /* Make range 0 be the same color as the "kind" text
    1020              :          (error vs warning vs note).  */
    1021        62392 :       pp_string
    1022        62392 :         (&m_pp,
    1023        62392 :          colorize_start (pp_show_color (&m_pp),
    1024              :                          get_color_for_kind (m_diagnostic_kind)));
    1025        62392 :       break;
    1026              : 
    1027         3780 :     case 1:
    1028         3780 :       pp_string (&m_pp, m_range1);
    1029         3780 :       break;
    1030              : 
    1031         3171 :     case 2:
    1032         3171 :       pp_string (&m_pp, m_range2);
    1033         3171 :       break;
    1034              : 
    1035          777 :     default:
    1036              :       /* For ranges beyond 2, alternate between color 1 and color 2.  */
    1037          777 :       {
    1038          777 :         gcc_assert (state > 2);
    1039          777 :         pp_string (&m_pp,
    1040              :                    state % 2 ? m_range1 : m_range2);
    1041              :       }
    1042          777 :       break;
    1043              :     }
    1044       171515 : }
    1045              : 
    1046              : /* Turn off any colorization for STATE.  */
    1047              : 
    1048              : void
    1049       220734 : colorizer::finish_state (int state)
    1050              : {
    1051       220734 :   if (state != STATE_NORMAL_TEXT)
    1052        87847 :     pp_string (&m_pp, m_stop_color);
    1053            0 : }
    1054              : 
    1055              : /* Get the color code for NAME (or the empty string if
    1056              :    colorization is disabled).  */
    1057              : 
    1058              : const char *
    1059       180160 : colorizer::get_color_by_name (const char *name)
    1060              : {
    1061        45040 :   return colorize_start (pp_show_color (&m_pp), name);
    1062              : }
    1063              : 
    1064              : /* Implementation of class layout_range.  */
    1065              : 
    1066              : /* The constructor for class layout_range.
    1067              :    Initialize various layout_point fields from expanded_location
    1068              :    equivalents; we've already filtered on file.  */
    1069              : 
    1070        63599 : layout_range::layout_range (const exploc_with_display_col &start_exploc,
    1071              :                             const exploc_with_display_col &finish_exploc,
    1072              :                             enum range_display_kind range_display_kind,
    1073              :                             const exploc_with_display_col &caret_exploc,
    1074              :                             unsigned original_idx,
    1075              :                             const range_label *label)
    1076        63599 : : m_start (start_exploc),
    1077        63599 :   m_finish (finish_exploc),
    1078        63599 :   m_range_display_kind (range_display_kind),
    1079        63599 :   m_caret (caret_exploc),
    1080        63599 :   m_original_idx (original_idx),
    1081        63599 :   m_label (label)
    1082              : {
    1083            0 : }
    1084              : 
    1085              : /* Is (column, row) within the given range?
    1086              :    We've already filtered on the file.
    1087              : 
    1088              :    Ranges are closed (both limits are within the range).
    1089              : 
    1090              :    Example A: a single-line range:
    1091              :      start:  (col=22, line=2)
    1092              :      finish: (col=38, line=2)
    1093              : 
    1094              :   |00000011111111112222222222333333333344444444444
    1095              :   |34567890123456789012345678901234567890123456789
    1096              : --+-----------------------------------------------
    1097              : 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    1098              : 02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
    1099              : 03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    1100              : 
    1101              :    Example B: a multiline range with
    1102              :      start:  (col=14, line=3)
    1103              :      finish: (col=08, line=5)
    1104              : 
    1105              :   |00000011111111112222222222333333333344444444444
    1106              :   |34567890123456789012345678901234567890123456789
    1107              : --+-----------------------------------------------
    1108              : 01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    1109              : 02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    1110              : 03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    1111              : 04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
    1112              : 05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    1113              : 06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    1114              : --+-----------------------------------------------
    1115              : 
    1116              :    Legend:
    1117              :    - 'b' indicates a point *before* the range
    1118              :    - 'S' indicates the start of the range
    1119              :    - 'w' indicates a point within the range
    1120              :    - 'F' indicates the finish of the range (which is
    1121              :          within it).
    1122              :    - 'a' indicates a subsequent point *after* the range.
    1123              : 
    1124              :    COL_UNIT controls whether we check the byte column or
    1125              :    the display column; one or the other is more convenient
    1126              :    depending on the context.  */
    1127              : 
    1128              : bool
    1129     14253837 : layout_range::contains_point (linenum_type row, int column,
    1130              :                               enum column_unit col_unit) const
    1131              : {
    1132     14253837 :   gcc_assert (m_start.m_line <= m_finish.m_line);
    1133              :   /* ...but the equivalent isn't true for the columns;
    1134              :      consider example B in the comment above.  */
    1135              : 
    1136     14253837 :   if (row < m_start.m_line)
    1137              :     /* Points before the first line of the range are
    1138              :        outside it (corresponding to line 01 in example A
    1139              :        and lines 01 and 02 in example B above).  */
    1140              :     return false;
    1141              : 
    1142     14176174 :   if (row == m_start.m_line)
    1143              :     /* On same line as start of range (corresponding
    1144              :        to line 02 in example A and line 03 in example B).  */
    1145              :     {
    1146     14031549 :       if (column < m_start.m_columns[col_unit])
    1147              :         /* Points on the starting line of the range, but
    1148              :            before the column in which it begins.  */
    1149              :         return false;
    1150              : 
    1151      1867589 :       if (row < m_finish.m_line)
    1152              :         /* This is a multiline range; the point
    1153              :            is within it (corresponds to line 03 in example B
    1154              :            from column 14 onwards) */
    1155              :         return true;
    1156              :       else
    1157              :         {
    1158              :           /* This is a single-line range.  */
    1159      1849433 :           gcc_assert (row == m_finish.m_line);
    1160      1849433 :           return column <= m_finish.m_columns[col_unit];
    1161              :         }
    1162              :     }
    1163              : 
    1164              :   /* The point is in a line beyond that containing the
    1165              :      start of the range: lines 03 onwards in example A,
    1166              :      and lines 04 onwards in example B.  */
    1167       144625 :   gcc_assert (row > m_start.m_line);
    1168              : 
    1169       144625 :   if (row > m_finish.m_line)
    1170              :     /* The point is beyond the final line of the range
    1171              :        (lines 03 onwards in example A, and lines 06 onwards
    1172              :        in example B).  */
    1173              :     return false;
    1174              : 
    1175        10802 :   if (row < m_finish.m_line)
    1176              :     {
    1177              :       /* The point is in a line that's fully within a multiline
    1178              :          range (e.g. line 04 in example B).  */
    1179              :       gcc_assert (m_start.m_line < m_finish.m_line);
    1180              :       return true;
    1181              :     }
    1182              : 
    1183         7323 :   gcc_assert (row ==  m_finish.m_line);
    1184              : 
    1185         7323 :   return column <= m_finish.m_columns[col_unit];
    1186              : }
    1187              : 
    1188              : /* Does this layout_range contain any part of line ROW?  */
    1189              : 
    1190              : bool
    1191        43018 : layout_range::intersects_line_p (linenum_type row) const
    1192              : {
    1193        43018 :   gcc_assert (m_start.m_line <= m_finish.m_line);
    1194        43018 :   if (row < m_start.m_line)
    1195              :     return false;
    1196        41678 :   if (row > m_finish.m_line)
    1197         3226 :     return false;
    1198              :   return true;
    1199              : }
    1200              : 
    1201              : /* Return true if this layout_range should have an in-edge.  */
    1202              : 
    1203              : bool
    1204         8802 : layout_range::has_in_edge () const
    1205              : {
    1206         8802 :   if (!m_label)
    1207              :     return false;
    1208         8802 :   const diagnostics::label_effects *effects
    1209         8802 :     = m_label->get_effects (m_original_idx);
    1210         8802 :   if (!effects)
    1211              :     return false;
    1212              : 
    1213         4669 :   return effects->has_in_edge (m_original_idx);
    1214              : }
    1215              : 
    1216              : /* Return true if this layout_range should have an out-edge.  */
    1217              : 
    1218              : bool
    1219         8802 : layout_range::has_out_edge () const
    1220              : {
    1221         8802 :   if (!m_label)
    1222              :     return false;
    1223         8802 :   const diagnostics::label_effects *effects
    1224         8802 :     = m_label->get_effects (m_original_idx);
    1225         8802 :   if (!effects)
    1226              :     return false;
    1227              : 
    1228         4669 :   return effects->has_out_edge (m_original_idx);
    1229              : }
    1230              : 
    1231              : #if CHECKING_P
    1232              : 
    1233              : /* Default for when we don't care what the tab expansion is set to.  */
    1234              : static const int def_tabstop = 8;
    1235              : 
    1236          292 : static cpp_char_column_policy def_policy ()
    1237              : {
    1238           12 :   return cpp_char_column_policy (def_tabstop, cpp_wcwidth);
    1239              : }
    1240              : 
    1241              : /* Create some expanded locations for testing layout_range.  The filename
    1242              :    member of the explocs is set to the empty string.  This member will only be
    1243              :    inspected by the calls to location_compute_display_column() made from the
    1244              :    layout_point constructors.  That function will check for an empty filename
    1245              :    argument and not attempt to open it, rather treating the non-existent data
    1246              :    as if the display width were the same as the byte count.  Tests exercising a
    1247              :    real difference between byte count and display width are performed later,
    1248              :    e.g. in test_diagnostic_show_locus_one_liner_utf8().  */
    1249              : 
    1250              : static layout_range
    1251           12 : make_range (diagnostics::file_cache &fc,
    1252              :             int start_line, int start_col, int end_line, int end_col)
    1253              : {
    1254           12 :   const expanded_location start_exploc
    1255           12 :     = {"", start_line, start_col, nullptr, false};
    1256           12 :   const expanded_location finish_exploc
    1257           12 :     = {"", end_line, end_col, nullptr, false};
    1258           12 :   return layout_range (exploc_with_display_col (fc,
    1259           24 :                                                 start_exploc, def_policy (),
    1260           12 :                                                 location_aspect::start),
    1261           12 :                        exploc_with_display_col (fc,
    1262           24 :                                                 finish_exploc, def_policy (),
    1263           12 :                                                 location_aspect::finish),
    1264              :                        SHOW_RANGE_WITHOUT_CARET,
    1265           12 :                        exploc_with_display_col (fc,
    1266           24 :                                                 start_exploc, def_policy (),
    1267           12 :                                                 location_aspect::caret),
    1268           12 :                        0, nullptr);
    1269              : }
    1270              : 
    1271              : /* Selftests for layout_range::contains_point and
    1272              :    layout_range::intersects_line_p.  */
    1273              : 
    1274              : /* Selftest for layout_range, where the layout_range
    1275              :    is a range with start==end i.e. a single point.  */
    1276              : 
    1277              : static void
    1278            4 : test_layout_range_for_single_point ()
    1279              : {
    1280            4 :   diagnostics::file_cache fc;
    1281            4 :   layout_range point = make_range (fc, 7, 10, 7, 10);
    1282              : 
    1283              :   /* Tests for layout_range::contains_point.  */
    1284              : 
    1285           16 :   for (int i = 0; i != CU_NUM_UNITS; ++i)
    1286              :     {
    1287            8 :       const enum column_unit col_unit = (enum column_unit) i;
    1288              : 
    1289              :       /* Before the line.  */
    1290            8 :       ASSERT_FALSE (point.contains_point (6, 1, col_unit));
    1291              : 
    1292              :       /* On the line, but before start.  */
    1293            8 :       ASSERT_FALSE (point.contains_point (7, 9, col_unit));
    1294              : 
    1295              :       /* At the point.  */
    1296            8 :       ASSERT_TRUE (point.contains_point (7, 10, col_unit));
    1297              : 
    1298              :       /* On the line, after the point.  */
    1299            8 :       ASSERT_FALSE (point.contains_point (7, 11, col_unit));
    1300              : 
    1301              :       /* After the line.  */
    1302            8 :       ASSERT_FALSE (point.contains_point (8, 1, col_unit));
    1303              :     }
    1304              : 
    1305              :   /* Tests for layout_range::intersects_line_p.  */
    1306            4 :   ASSERT_FALSE (point.intersects_line_p (6));
    1307            4 :   ASSERT_TRUE (point.intersects_line_p (7));
    1308            4 :   ASSERT_FALSE (point.intersects_line_p (8));
    1309            4 : }
    1310              : 
    1311              : /* Selftest for layout_range, where the layout_range
    1312              :    is the single-line range shown as "Example A" above.  */
    1313              : 
    1314              : static void
    1315            4 : test_layout_range_for_single_line ()
    1316              : {
    1317            4 :   diagnostics::file_cache fc;
    1318            4 :   layout_range example_a = make_range (fc, 2, 22, 2, 38);
    1319              : 
    1320              :   /* Tests for layout_range::contains_point.  */
    1321              : 
    1322           16 :   for (int i = 0; i != CU_NUM_UNITS; ++i)
    1323              :     {
    1324            8 :       const enum column_unit col_unit = (enum column_unit) i;
    1325              : 
    1326              :       /* Before the line.  */
    1327            8 :       ASSERT_FALSE (example_a.contains_point (1, 1, col_unit));
    1328              : 
    1329              :       /* On the line, but before start.  */
    1330            8 :       ASSERT_FALSE (example_a.contains_point (2, 21, col_unit));
    1331              : 
    1332              :       /* On the line, at the start.  */
    1333            8 :       ASSERT_TRUE (example_a.contains_point (2, 22, col_unit));
    1334              : 
    1335              :       /* On the line, within the range.  */
    1336            8 :       ASSERT_TRUE (example_a.contains_point (2, 23, col_unit));
    1337              : 
    1338              :       /* On the line, at the end.  */
    1339            8 :       ASSERT_TRUE (example_a.contains_point (2, 38, col_unit));
    1340              : 
    1341              :       /* On the line, after the end.  */
    1342            8 :       ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
    1343              : 
    1344              :       /* After the line.  */
    1345            8 :       ASSERT_FALSE (example_a.contains_point (2, 39, col_unit));
    1346              :     }
    1347              : 
    1348              :   /* Tests for layout_range::intersects_line_p.  */
    1349            4 :   ASSERT_FALSE (example_a.intersects_line_p (1));
    1350            4 :   ASSERT_TRUE (example_a.intersects_line_p (2));
    1351            4 :   ASSERT_FALSE (example_a.intersects_line_p (3));
    1352            4 : }
    1353              : 
    1354              : /* Selftest for layout_range, where the layout_range
    1355              :    is the multi-line range shown as "Example B" above.  */
    1356              : 
    1357              : static void
    1358            4 : test_layout_range_for_multiple_lines ()
    1359              : {
    1360            4 :   diagnostics::file_cache fc;
    1361            4 :   layout_range example_b = make_range (fc, 3, 14, 5, 8);
    1362              : 
    1363              :   /* Tests for layout_range::contains_point.  */
    1364              : 
    1365           16 :   for (int i = 0; i != CU_NUM_UNITS; ++i)
    1366              :     {
    1367            8 :       const enum column_unit col_unit = (enum column_unit) i;
    1368              : 
    1369              :       /* Before first line.  */
    1370            8 :       ASSERT_FALSE (example_b.contains_point (1, 1, col_unit));
    1371              : 
    1372              :       /* On the first line, but before start.  */
    1373            8 :       ASSERT_FALSE (example_b.contains_point (3, 13, col_unit));
    1374              : 
    1375              :       /* At the start.  */
    1376            8 :       ASSERT_TRUE (example_b.contains_point (3, 14, col_unit));
    1377              : 
    1378              :       /* On the first line, within the range.  */
    1379            8 :       ASSERT_TRUE (example_b.contains_point (3, 15, col_unit));
    1380              : 
    1381              :       /* On an interior line.
    1382              :          The column number should not matter; try various boundary
    1383              :          values.  */
    1384            8 :       ASSERT_TRUE (example_b.contains_point (4, 1, col_unit));
    1385            8 :       ASSERT_TRUE (example_b.contains_point (4, 7, col_unit));
    1386            8 :       ASSERT_TRUE (example_b.contains_point (4, 8, col_unit));
    1387            8 :       ASSERT_TRUE (example_b.contains_point (4, 9, col_unit));
    1388            8 :       ASSERT_TRUE (example_b.contains_point (4, 13, col_unit));
    1389            8 :       ASSERT_TRUE (example_b.contains_point (4, 14, col_unit));
    1390            8 :       ASSERT_TRUE (example_b.contains_point (4, 15, col_unit));
    1391              : 
    1392              :       /* On the final line, before the end.  */
    1393            8 :       ASSERT_TRUE (example_b.contains_point (5, 7, col_unit));
    1394              : 
    1395              :       /* On the final line, at the end.  */
    1396            8 :       ASSERT_TRUE (example_b.contains_point (5, 8, col_unit));
    1397              : 
    1398              :       /* On the final line, after the end.  */
    1399            8 :       ASSERT_FALSE (example_b.contains_point (5, 9, col_unit));
    1400              : 
    1401              :       /* After the line.  */
    1402            8 :       ASSERT_FALSE (example_b.contains_point (6, 1, col_unit));
    1403              :     }
    1404              : 
    1405              :   /* Tests for layout_range::intersects_line_p.  */
    1406            4 :   ASSERT_FALSE (example_b.intersects_line_p (2));
    1407            4 :   ASSERT_TRUE (example_b.intersects_line_p (3));
    1408            4 :   ASSERT_TRUE (example_b.intersects_line_p (4));
    1409            4 :   ASSERT_TRUE (example_b.intersects_line_p (5));
    1410            4 :   ASSERT_FALSE (example_b.intersects_line_p (6));
    1411            4 : }
    1412              : 
    1413              : #endif /* #if CHECKING_P */
    1414              : 
    1415              : /* Given a source line LINE of length LINE_BYTES bytes, determine the length
    1416              :    (still in bytes, not display cols) without any trailing whitespace.  */
    1417              : 
    1418              : static int
    1419        93345 : get_line_bytes_without_trailing_whitespace (const char *line, int line_bytes)
    1420              : {
    1421        93345 :   int result = line_bytes;
    1422     59366042 :   while (result > 0)
    1423              :     {
    1424     59348417 :       char ch = line[result - 1];
    1425     59348417 :       if (ch == ' ' || ch == '\t' || ch == '\r')
    1426     59272697 :         result--;
    1427              :       else
    1428              :         break;
    1429              :     }
    1430        93345 :   gcc_assert (result >= 0);
    1431        93345 :   gcc_assert (result <= line_bytes);
    1432        93345 :   gcc_assert (result == 0 ||
    1433              :               (line[result - 1] != ' '
    1434              :                && line[result -1] != '\t'
    1435              :                && line[result -1] != '\r'));
    1436        93345 :   return result;
    1437              : }
    1438              : 
    1439              : #if CHECKING_P
    1440              : 
    1441              : /* A helper function for testing get_line_bytes_without_trailing_whitespace.  */
    1442              : 
    1443              : static void
    1444           32 : assert_eq (const char *line, int expected_bytes)
    1445              : {
    1446           32 :   int actual_value
    1447           32 :     = get_line_bytes_without_trailing_whitespace (line, strlen (line));
    1448           32 :   ASSERT_EQ (actual_value, expected_bytes);
    1449           32 : }
    1450              : 
    1451              : /* Verify that get_line_bytes_without_trailing_whitespace is sane for
    1452              :    various inputs.  It is not required to handle newlines.  */
    1453              : 
    1454              : static void
    1455            4 : test_get_line_bytes_without_trailing_whitespace ()
    1456              : {
    1457            4 :   assert_eq ("", 0);
    1458            4 :   assert_eq (" ", 0);
    1459            4 :   assert_eq ("\t", 0);
    1460            4 :   assert_eq ("\r", 0);
    1461            4 :   assert_eq ("hello world", 11);
    1462            4 :   assert_eq ("hello world     ", 11);
    1463            4 :   assert_eq ("hello world     \t\t  ", 11);
    1464            4 :   assert_eq ("hello world\r", 11);
    1465            4 : }
    1466              : 
    1467              : #endif /* #if CHECKING_P */
    1468              : 
    1469              : /* Helper function for layout's ctor, for sanitizing locations relative
    1470              :    to the primary location within a diagnostic.
    1471              : 
    1472              :    Compare LOC_A and LOC_B to see if it makes sense to print underlines
    1473              :    connecting their expanded locations.  Doing so is only guaranteed to
    1474              :    make sense if the locations share the same macro expansion "history"
    1475              :    i.e. they can be traced through the same macro expansions, eventually
    1476              :    reaching an ordinary map.
    1477              : 
    1478              :    This may be too strong a condition, but it effectively sanitizes
    1479              :    PR c++/70105, which has an example of printing an expression where the
    1480              :    final location of the expression is in a different macro, which
    1481              :    erroneously was leading to hundreds of lines of irrelevant source
    1482              :    being printed.  */
    1483              : 
    1484              : bool
    1485       127456 : layout::compatible_locations_p (location_t loc_a, location_t loc_b) const
    1486              : {
    1487       127831 :   if (IS_ADHOC_LOC (loc_a))
    1488          256 :     loc_a = get_location_from_adhoc_loc (m_line_table, loc_a);
    1489       127831 :   if (IS_ADHOC_LOC (loc_b))
    1490        25249 :     loc_b = get_location_from_adhoc_loc (m_line_table, loc_b);
    1491              : 
    1492              :   /* If either location is one of the special locations outside of a
    1493              :      linemap, they are only compatible if they are equal.  */
    1494       127831 :   if (loc_a < RESERVED_LOCATION_COUNT
    1495       127831 :       || loc_b < RESERVED_LOCATION_COUNT)
    1496           19 :     return loc_a == loc_b;
    1497              : 
    1498       127812 :   const line_map *map_a = linemap_lookup (m_line_table, loc_a);
    1499       127812 :   linemap_assert (map_a);
    1500              : 
    1501       127812 :   const line_map *map_b = linemap_lookup (m_line_table, loc_b);
    1502       127812 :   linemap_assert (map_b);
    1503              : 
    1504              :   /* Are they within the same map?  */
    1505       127812 :   if (map_a == map_b)
    1506              :     {
    1507              :       /* Are both within the same macro expansion?  */
    1508       127700 :       if (linemap_macro_expansion_map_p (map_a))
    1509              :         {
    1510              :           /* If so, then they're only compatible if either both are
    1511              :              from the macro definition, or both from the macro arguments.  */
    1512          379 :           bool loc_a_from_defn
    1513          379 :             = linemap_location_from_macro_definition_p (m_line_table, loc_a);
    1514          379 :           bool loc_b_from_defn
    1515          379 :             = linemap_location_from_macro_definition_p (m_line_table, loc_b);
    1516          379 :           if (loc_a_from_defn != loc_b_from_defn)
    1517              :             return false;
    1518              : 
    1519              :           /* Expand each location towards the spelling location, and
    1520              :              recurse.  */
    1521          375 :           const line_map_macro *macro_map = linemap_check_macro (map_a);
    1522          375 :           location_t loc_a_toward_spelling
    1523          375 :             = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
    1524              :                                                             macro_map,
    1525              :                                                             loc_a);
    1526          375 :           location_t loc_b_toward_spelling
    1527          375 :             = linemap_macro_map_loc_unwind_toward_spelling (m_line_table,
    1528              :                                                             macro_map,
    1529              :                                                             loc_b);
    1530          375 :           return compatible_locations_p (loc_a_toward_spelling,
    1531          375 :                                          loc_b_toward_spelling);
    1532              :         }
    1533              : 
    1534              :       /* Otherwise they are within the same ordinary map.  */
    1535              :       return true;
    1536              :     }
    1537              :   else
    1538              :     {
    1539              :       /* Within different maps.  */
    1540              : 
    1541              :       /* If either is within a macro expansion, they are incompatible.  */
    1542          112 :       if (linemap_macro_expansion_map_p (map_a)
    1543          112 :           || linemap_macro_expansion_map_p (map_b))
    1544              :         return false;
    1545              : 
    1546              :       /* Within two different ordinary maps; they are compatible iff they
    1547              :          are in the same file.  */
    1548           80 :       const line_map_ordinary *ord_map_a = linemap_check_ordinary (map_a);
    1549           80 :       const line_map_ordinary *ord_map_b = linemap_check_ordinary (map_b);
    1550           80 :       return ord_map_a->to_file == ord_map_b->to_file;
    1551              :     }
    1552              : }
    1553              : 
    1554              : /* Comparator for sorting fix-it hints.  */
    1555              : 
    1556              : static int
    1557       169939 : fixit_cmp (const void *p_a, const void *p_b)
    1558              : {
    1559       169939 :   const fixit_hint * hint_a = *static_cast<const fixit_hint * const *> (p_a);
    1560       169939 :   const fixit_hint * hint_b = *static_cast<const fixit_hint * const *> (p_b);
    1561       169939 :   return hint_a->get_start_loc () - hint_b->get_start_loc ();
    1562              : }
    1563              : 
    1564              : /* Callbacks for use when not escaping the source.  */
    1565              : 
    1566              : /* The default callback for char_column_policy::m_width_cb is cpp_wcwidth.  */
    1567              : 
    1568              : /* Callback for char_display_policy::m_print_cb for printing source chars
    1569              :    when not escaping the source.  */
    1570              : 
    1571              : template <class TextOrHtml>
    1572              : static void
    1573      3865728 : default_print_decoded_ch (TextOrHtml &text_or_html,
    1574              :                           const cpp_decoded_char &decoded_ch)
    1575              : {
    1576      7743613 :   for (const char *ptr = decoded_ch.m_start_byte;
    1577      7743613 :        ptr != decoded_ch.m_next_byte; ptr++)
    1578              :     {
    1579      3877885 :       if (*ptr == '\0' || *ptr == '\r')
    1580              :         {
    1581          128 :           text_or_html.add_space ();
    1582          128 :           continue;
    1583              :         }
    1584              : 
    1585      3877757 :       text_or_html.add_utf8_byte (*ptr);
    1586              :     }
    1587      3865728 : }
    1588              : 
    1589              : /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_BYTES.  */
    1590              : 
    1591              : static const int width_per_escaped_byte = 4;
    1592              : 
    1593              : /* Callback for char_column_policy::m_width_cb for determining the
    1594              :    display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES.  */
    1595              : 
    1596              : static int
    1597        21585 : escape_as_bytes_width (cppchar_t ch)
    1598              : {
    1599        21585 :   if (ch < 0x80 && ISPRINT (ch))
    1600        17075 :     return cpp_wcwidth (ch);
    1601              :   else
    1602              :     {
    1603              :       if (ch <=   0x7F) return 1 * width_per_escaped_byte;
    1604         1743 :       if (ch <=  0x7FF) return 2 * width_per_escaped_byte;
    1605         1035 :       if (ch <= 0xFFFF) return 3 * width_per_escaped_byte;
    1606         1032 :       return 4 * width_per_escaped_byte;
    1607              :     }
    1608              : }
    1609              : 
    1610              : /* Callback for char_display_policy::m_print_cb for printing source chars
    1611              :    when escaping with DIAGNOSTICS_ESCAPE_FORMAT_BYTES.  */
    1612              : 
    1613              : template <typename TextOrHtml>
    1614              : static void
    1615         3524 : escape_as_bytes_print (TextOrHtml &text_or_html,
    1616              :                        const cpp_decoded_char &decoded_ch)
    1617              : {
    1618         3524 :   if (!decoded_ch.m_valid_ch)
    1619              :     {
    1620          258 :       for (const char *iter = decoded_ch.m_start_byte;
    1621          516 :            iter != decoded_ch.m_next_byte; ++iter)
    1622              :         {
    1623              :           char buf[16];
    1624          258 :           sprintf (buf, "<%02x>", (unsigned char)*iter);
    1625          258 :           text_or_html.add_text (buf);
    1626              :         }
    1627              :       return;
    1628              :     }
    1629              : 
    1630         3266 :   cppchar_t ch = decoded_ch.m_ch;
    1631         3266 :   if (ch < 0x80 && ISPRINT (ch))
    1632         2623 :     text_or_html.add_character (ch);
    1633              :   else
    1634              :     {
    1635          643 :       for (const char *iter = decoded_ch.m_start_byte;
    1636         1800 :            iter < decoded_ch.m_next_byte; ++iter)
    1637              :         {
    1638              :           char buf[16];
    1639         1157 :           sprintf (buf, "<%02x>", (unsigned char)*iter);
    1640         1157 :           text_or_html.add_text (buf);
    1641              :         }
    1642              :     }
    1643              : }
    1644              : 
    1645              : /* Callbacks for use with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE.  */
    1646              : 
    1647              : /* Callback for char_column_policy::m_width_cb for determining the
    1648              :    display width when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE.  */
    1649              : 
    1650              : static int
    1651        54939 : escape_as_unicode_width (cppchar_t ch)
    1652              : {
    1653        54939 :   if (ch < 0x80 && ISPRINT (ch))
    1654        46269 :     return cpp_wcwidth (ch);
    1655              :   else
    1656              :     {
    1657              :       // Width of "<U+%04x>"
    1658         8670 :       if (ch > 0xfffff)
    1659              :         return 10;
    1660         8666 :       else if (ch > 0xffff)
    1661              :         return 9;
    1662              :       else
    1663         5590 :         return 8;
    1664              :     }
    1665              : }
    1666              : 
    1667              : /* Callback for char_display_policy::m_print_cb for printing source chars
    1668              :    when escaping with DIAGNOSTICS_ESCAPE_FORMAT_UNICODE.  */
    1669              : 
    1670              : template <typename TextOrHtml>
    1671              : static void
    1672         7184 : escape_as_unicode_print (TextOrHtml &text_or_html,
    1673              :                          const cpp_decoded_char &decoded_ch)
    1674              : {
    1675         7184 :   if (!decoded_ch.m_valid_ch)
    1676              :     {
    1677          129 :       escape_as_bytes_print<TextOrHtml> (text_or_html, decoded_ch);
    1678          129 :       return;
    1679              :     }
    1680              : 
    1681         7055 :   cppchar_t ch = decoded_ch.m_ch;
    1682         7055 :   if (ch < 0x80 && ISPRINT (ch))
    1683         5844 :     text_or_html.add_character (ch);
    1684              :   else
    1685              :     {
    1686              :       char buf[16];
    1687         1211 :       sprintf (buf, "<U+%04X>", ch);
    1688         1211 :       text_or_html.add_text (buf);
    1689              :     }
    1690              : }
    1691              : 
    1692              : /* Populate a char_display_policy based on SOURCE_POLICY and RICHLOC.  */
    1693              : 
    1694              : static char_display_policy
    1695        50923 : make_char_policy (const diagnostics::source_print_policy &source_policy,
    1696              :                   const rich_location &richloc)
    1697              : {
    1698              :   /* The default is to not escape non-ASCII bytes.  */
    1699        50923 :   char_display_policy result
    1700        50923 :     (source_policy.get_column_policy ().get_tabstop (),
    1701              :      cpp_wcwidth,
    1702              :      default_print_decoded_ch<diagnostics::to_text>,
    1703        50923 :      default_print_decoded_ch<diagnostics::to_html>);
    1704              : 
    1705              :   /* If the diagnostic suggests escaping non-ASCII bytes, then
    1706              :      use policy from user-supplied options.  */
    1707        50923 :   if (richloc.escape_on_output_p ())
    1708              :     {
    1709          554 :       result.m_undecoded_byte_width = width_per_escaped_byte;
    1710          554 :       switch (source_policy.get_escape_format ())
    1711              :         {
    1712            0 :         default:
    1713            0 :           gcc_unreachable ();
    1714          353 :         case DIAGNOSTICS_ESCAPE_FORMAT_UNICODE:
    1715          353 :           result.m_width_cb = escape_as_unicode_width;
    1716          353 :           result.m_print_text_cb
    1717          353 :             = escape_as_unicode_print<diagnostics::to_text>;
    1718          353 :           result.m_print_html_cb
    1719          353 :             = escape_as_unicode_print<diagnostics::to_html>;
    1720          353 :           break;
    1721          201 :         case DIAGNOSTICS_ESCAPE_FORMAT_BYTES:
    1722          201 :           result.m_width_cb = escape_as_bytes_width;
    1723          201 :           result.m_print_text_cb = escape_as_bytes_print<diagnostics::to_text>;
    1724          201 :           result.m_print_html_cb = escape_as_bytes_print<diagnostics::to_html>;
    1725          201 :           break;
    1726              :         }
    1727              :     }
    1728              : 
    1729        50923 :   return result;
    1730              : }
    1731              : 
    1732              : /* Implementation of class layout.  */
    1733              : 
    1734              : /* Constructor for class layout.
    1735              : 
    1736              :    Filter the ranges from the rich_location to those that we can
    1737              :    sanely print, populating m_layout_ranges and m_fixit_hints.
    1738              :    Determine the range of lines that we will print, splitting them
    1739              :    up into an ordered list of disjoint spans of contiguous line numbers.
    1740              :    Determine m_x_offset_display, to ensure that the primary caret
    1741              :    will fit within the max_width provided by the diagnostics::context.  */
    1742              : 
    1743        50735 : layout::layout (const diagnostics::source_print_policy &source_policy,
    1744              :                 const rich_location &richloc,
    1745              :                 diagnostics::source_effect_info *effect_info)
    1746        50735 : : m_options (source_policy.get_options ()),
    1747        50735 :   m_line_table (richloc.get_line_table ()),
    1748        50735 :   m_file_cache (source_policy.get_file_cache ()),
    1749              :   /* Ensure we have a non-null m_theme. */
    1750       101470 :   m_theme (source_policy.get_diagram_theme ()
    1751        50735 :            ? *source_policy.get_diagram_theme ()
    1752              :            : *static_cast <const text_art::theme *> (&m_fallback_theme)),
    1753        50735 :   m_effect_info (effect_info),
    1754        50735 :   m_char_policy (make_char_policy (source_policy, richloc)),
    1755        50735 :   m_primary_loc (richloc.get_range (0)->m_loc),
    1756        50735 :   m_exploc (m_file_cache,
    1757        50735 :             richloc.get_expanded_location (0), m_char_policy,
    1758              :             location_aspect::caret),
    1759        50735 :   m_layout_ranges (richloc.get_num_locations ()),
    1760        50735 :   m_fixit_hints (richloc.get_num_fixit_hints ()),
    1761        50735 :   m_line_spans (1 + richloc.get_num_locations ()),
    1762        50735 :   m_linenum_width (0),
    1763        50735 :   m_x_offset_display (0),
    1764        50735 :   m_escape_on_output (richloc.escape_on_output_p ())
    1765              : {
    1766       110387 :   for (unsigned int idx = 0; idx < richloc.get_num_locations (); idx++)
    1767              :     {
    1768              :       /* This diagnostic printer can only cope with "sufficiently sane" ranges.
    1769              :          Ignore any ranges that are awkward to handle.  */
    1770        59652 :       const location_range *loc_range = richloc.get_range (idx);
    1771        59652 :       maybe_add_location_range (loc_range, idx, false);
    1772              :     }
    1773              : 
    1774              :   /* Populate m_fixit_hints, filtering to only those that are in the
    1775              :      same file.  */
    1776        65158 :   for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
    1777              :     {
    1778        14423 :       const fixit_hint *hint = richloc.get_fixit_hint (i);
    1779        14423 :       if (validate_fixit_hint_p (hint))
    1780        14423 :         m_fixit_hints.safe_push (hint);
    1781              :     }
    1782              : 
    1783              :   /* Sort m_fixit_hints.  */
    1784        50735 :   m_fixit_hints.qsort (fixit_cmp);
    1785              : 
    1786              :   /* Populate the indicated members.  */
    1787        50735 :   calculate_line_spans ();
    1788        50735 :   calculate_linenum_width ();
    1789        50735 :   calculate_x_offset_display ();
    1790        50735 : }
    1791              : 
    1792              : 
    1793              : /* Attempt to add LOC_RANGE to m_layout_ranges, filtering them to
    1794              :    those that we can sanely print.
    1795              : 
    1796              :    ORIGINAL_IDX is the index of LOC_RANGE within its rich_location,
    1797              :    (for use as extrinsic state by label ranges).
    1798              : 
    1799              :    If RESTRICT_TO_CURRENT_LINE_SPANS is true, then LOC_RANGE is also
    1800              :    filtered against this layout instance's current line spans: it
    1801              :    will only be added if the location is fully within the lines
    1802              :    already specified by other locations.
    1803              : 
    1804              :    Return true iff LOC_RANGE was added.  */
    1805              : 
    1806              : bool
    1807        63596 : layout::maybe_add_location_range (const location_range *loc_range,
    1808              :                                   unsigned original_idx,
    1809              :                                   bool restrict_to_current_line_spans)
    1810              : {
    1811        63596 :   gcc_assert (loc_range);
    1812              : 
    1813              :   /* Split the "range" into caret and range information.  */
    1814        63596 :   source_range src_range = get_range_from_loc (m_line_table, loc_range->m_loc);
    1815              : 
    1816              :   /* Expand the various locations.  */
    1817        63596 :   expanded_location start
    1818              :     = linemap_client_expand_location_to_spelling_point
    1819        63596 :     (m_line_table, src_range.m_start, location_aspect::start);
    1820        63596 :   expanded_location finish
    1821              :     = linemap_client_expand_location_to_spelling_point
    1822        63596 :     (m_line_table, src_range.m_finish, location_aspect::finish);
    1823        63596 :   expanded_location caret
    1824              :     = linemap_client_expand_location_to_spelling_point
    1825        63596 :     (m_line_table, loc_range->m_loc, location_aspect::caret);
    1826              : 
    1827              :   /* If any part of the range isn't in the same file as the primary
    1828              :      location of this diagnostic, ignore the range.  */
    1829        63596 :   if (start.file != m_exploc.file)
    1830              :     return false;
    1831        63587 :   if (finish.file != m_exploc.file)
    1832              :     return false;
    1833        63587 :   if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
    1834        50888 :     if (caret.file != m_exploc.file)
    1835              :       return false;
    1836              : 
    1837              :   /* Sanitize the caret location for non-primary ranges.  */
    1838        63587 :   if (m_layout_ranges.length () > 0)
    1839        12853 :     if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
    1840          331 :       if (!compatible_locations_p (loc_range->m_loc, m_primary_loc))
    1841              :         /* Discard any non-primary ranges that can't be printed
    1842              :            sanely relative to the primary location.  */
    1843              :         return false;
    1844              : 
    1845              :   /* If there's no column information, then don't try to print
    1846              :      annotation lines for this range.  */
    1847        63587 :   enum range_display_kind range_display_kind
    1848              :     = loc_range->m_range_display_kind;
    1849        63587 :   if (start.column == 0
    1850        57570 :       || finish.column == 0
    1851        57570 :       || caret.column == 0)
    1852         6017 :     range_display_kind = SHOW_LINES_WITHOUT_RANGE;
    1853              : 
    1854              :   /* Everything is now known to be in the correct source file,
    1855              :      but it may require further sanitization.  */
    1856       127174 :   layout_range ri (exploc_with_display_col (m_file_cache,
    1857              :                                             start, m_char_policy,
    1858        63587 :                                             location_aspect::start),
    1859        63587 :                    exploc_with_display_col (m_file_cache,
    1860              :                                             finish, m_char_policy,
    1861        63587 :                                             location_aspect::finish),
    1862              :                    range_display_kind,
    1863        63587 :                    exploc_with_display_col (m_file_cache,
    1864              :                                             caret, m_char_policy,
    1865        63587 :                                             location_aspect::caret),
    1866        63587 :                    original_idx, loc_range->m_label);
    1867              : 
    1868              :   /* If we have a range that finishes before it starts (perhaps
    1869              :      from something built via macro expansion), printing the
    1870              :      range is likely to be nonsensical.  Also, attempting to do so
    1871              :      breaks assumptions within the printing code  (PR c/68473).
    1872              :      Similarly, don't attempt to print ranges if one or both ends
    1873              :      of the range aren't sane to print relative to the
    1874              :      primary location (PR c++/70105).  */
    1875        63587 :   if (start.line > finish.line
    1876        63576 :       || !compatible_locations_p (src_range.m_start, m_primary_loc)
    1877       127136 :       || !compatible_locations_p (src_range.m_finish, m_primary_loc))
    1878              :     {
    1879              :       /* Is this the primary location?  */
    1880           47 :       if (m_layout_ranges.length () == 0)
    1881              :         {
    1882              :           /* We want to print the caret for the primary location, but
    1883              :              we must sanitize away m_start and m_finish.  */
    1884           20 :           ri.m_start = ri.m_caret;
    1885           20 :           ri.m_finish = ri.m_caret;
    1886              :         }
    1887              :       else
    1888              :         /* This is a non-primary range; ignore it.  */
    1889              :         return false;
    1890              :     }
    1891              : 
    1892              :   /* Potentially filter to just the lines already specified by other
    1893              :      locations.  This is for use by gcc_rich_location::add_location_if_nearby.
    1894              :      The layout ctor doesn't use it, and can't because m_line_spans
    1895              :      hasn't been set up at that point.  */
    1896        63560 :   if (restrict_to_current_line_spans)
    1897              :     {
    1898         1643 :       if (!will_show_line_p (start.line))
    1899              :         return false;
    1900         1442 :       if (!will_show_line_p (finish.line))
    1901              :         return false;
    1902         1442 :       if (loc_range->m_range_display_kind == SHOW_RANGE_WITH_CARET)
    1903            0 :         if (!will_show_line_p (caret.line))
    1904              :           return false;
    1905              :     }
    1906              : 
    1907              :   /* Passed all the tests; add the range to m_layout_ranges so that
    1908              :      it will be printed.  */
    1909        63359 :   m_layout_ranges.safe_push (ri);
    1910        63359 :   return true;
    1911              : }
    1912              : 
    1913              : /* Return true iff ROW is within one of the line spans for this layout.  */
    1914              : 
    1915              : bool
    1916         3085 : layout::will_show_line_p (linenum_type row) const
    1917              : {
    1918         6572 :   for (int line_span_idx = 0; line_span_idx < get_num_line_spans ();
    1919              :        line_span_idx++)
    1920              :     {
    1921         3085 :       const line_span *line_span = get_line_span (line_span_idx);
    1922         3085 :       if (line_span->contains_line_p (row))
    1923              :         return true;
    1924              :     }
    1925              :   return false;
    1926              : }
    1927              : 
    1928              : /* Print a line showing a gap in the line numbers, for showing the boundary
    1929              :    between two line spans.  */
    1930              : 
    1931              : template<>
    1932              : void
    1933          459 : layout_printer<diagnostics::to_text>::print_gap_in_line_numbering ()
    1934              : {
    1935          459 :   gcc_assert (m_layout.m_options.show_line_numbers_p);
    1936              : 
    1937          459 :   m_text_or_html.emit_text_prefix ();
    1938              : 
    1939         3672 :   for (int i = 0; i < m_layout.get_linenum_width () + 1; i++)
    1940         2754 :     m_text_or_html.add_character ('.');
    1941              : 
    1942          459 :   m_text_or_html.add_newline ();
    1943          459 : }
    1944              : 
    1945              : template<>
    1946              : void
    1947            0 : layout_printer<diagnostics::to_html>::print_gap_in_line_numbering ()
    1948              : {
    1949            0 :   gcc_assert (m_layout.m_options.show_line_numbers_p);
    1950              : 
    1951            0 :   m_text_or_html.add_raw_html
    1952            0 :     ("<tbody class=\"line-span-jump\">\n"
    1953              :      "<tr class=\"line-span-jump-row\">"
    1954              :      "<td class=\"linenum-gap\">[...]</td>"
    1955              :      "<td class=\"source-gap\"/></tr>\n"
    1956              :      "</tbody>\n");
    1957            0 : }
    1958              : 
    1959              : /* Return true iff we should print a heading when starting the
    1960              :    line span with the given index.  */
    1961              : 
    1962              : bool
    1963        19344 : layout::print_heading_for_line_span_index_p (int line_span_idx) const
    1964              : {
    1965              :   /* We print a heading for every change of line span, hence for every
    1966              :      line span after the initial one.  */
    1967        19344 :   if (line_span_idx > 0)
    1968              :     return true;
    1969              : 
    1970              :   /* We also do it for the initial span if the primary location of the
    1971              :      diagnostic is in a different span.  */
    1972        19000 :   if (m_exploc.line > (int)get_line_span (0)->m_last_line)
    1973          119 :     return true;
    1974              : 
    1975              :   return false;
    1976              : }
    1977              : 
    1978              : /* Get an expanded_location for the first location of interest within
    1979              :    the given line_span.
    1980              :    Used when printing a heading to indicate a new line span.  */
    1981              : 
    1982              : expanded_location
    1983          463 : layout::get_expanded_location (const line_span *line_span) const
    1984              : {
    1985              :   /* Whenever possible, use the caret location.  */
    1986          463 :   if (line_span->contains_line_p (m_exploc.line))
    1987          119 :     return m_exploc;
    1988              : 
    1989              :   /* Otherwise, use the start of the first range that's present
    1990              :      within the line_span.  */
    1991         1382 :   for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
    1992              :     {
    1993          575 :       const layout_range *lr = &m_layout_ranges[i];
    1994          694 :       if (line_span->contains_line_p (lr->m_start.m_line))
    1995              :         {
    1996          228 :           expanded_location exploc = m_exploc;
    1997          228 :           exploc.line = lr->m_start.m_line;
    1998          228 :           exploc.column = lr->m_start.m_columns[CU_BYTES];
    1999          228 :           return exploc;
    2000              :         }
    2001              :     }
    2002              : 
    2003              :   /* Otherwise, use the location of the first fixit-hint present within
    2004              :      the line_span.  */
    2005          116 :   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
    2006              :     {
    2007          116 :       const fixit_hint *hint = m_fixit_hints[i];
    2008          116 :       location_t loc = hint->get_start_loc ();
    2009          116 :       expanded_location exploc = expand_location (loc);
    2010          116 :       if (line_span->contains_line_p (exploc.line))
    2011          116 :         return exploc;
    2012              :     }
    2013              : 
    2014              :   /* It should not be possible to have a line span that didn't
    2015              :      contain any of the layout_range or fixit_hint instances.  */
    2016            0 :   gcc_unreachable ();
    2017              :   return m_exploc;
    2018              : }
    2019              : 
    2020              : /* Determine if HINT is meaningful to print within this layout.  */
    2021              : 
    2022              : bool
    2023        14423 : layout::validate_fixit_hint_p (const fixit_hint *hint)
    2024              : {
    2025        14423 :   if (LOCATION_FILE (hint->get_start_loc ()) != m_exploc.file)
    2026              :     return false;
    2027        14423 :   if (LOCATION_FILE (hint->get_next_loc ()) != m_exploc.file)
    2028            0 :     return false;
    2029              : 
    2030              :   return true;
    2031              : }
    2032              : 
    2033              : /* Determine the range of lines affected by HINT.
    2034              :    This assumes that HINT has already been filtered by
    2035              :    validate_fixit_hint_p, and so affects the correct source file.  */
    2036              : 
    2037              : static line_span
    2038        14423 : get_line_span_for_fixit_hint (const fixit_hint *hint)
    2039              : {
    2040        14423 :   gcc_assert (hint);
    2041              : 
    2042        14423 :   int start_line = LOCATION_LINE (hint->get_start_loc ());
    2043              : 
    2044              :   /* For line-insertion fix-it hints, add the previous line to the
    2045              :      span, to give the user more context on the proposed change.  */
    2046        14423 :   if (hint->ends_with_newline_p ())
    2047          438 :     if (start_line > 1)
    2048          293 :       start_line--;
    2049              : 
    2050        14423 :   return line_span (start_line,
    2051        14423 :                     LOCATION_LINE (hint->get_next_loc ()));
    2052              : }
    2053              : 
    2054              : /* We want to print the pertinent source code at a diagnostic.  The
    2055              :    rich_location can contain multiple locations.  This will have been
    2056              :    filtered into m_exploc (the caret for the primary location) and
    2057              :    m_layout_ranges, for those ranges within the same source file.
    2058              : 
    2059              :    We will print a subset of the lines within the source file in question,
    2060              :    as a collection of "spans" of lines.
    2061              : 
    2062              :    This function populates m_line_spans with an ordered, disjoint list of
    2063              :    the line spans of interest.
    2064              : 
    2065              :    Printing a gap between line spans takes one line, so, when printing
    2066              :    line numbers, we allow a gap of up to one line between spans when
    2067              :    merging, since it makes more sense to print the source line rather than a
    2068              :    "gap-in-line-numbering" line.  When not printing line numbers, it's
    2069              :    better to be more explicit about what's going on, so keeping them as
    2070              :    separate spans is preferred.
    2071              : 
    2072              :    For example, if the primary range is on lines 8-10, with secondary ranges
    2073              :    covering lines 5-6 and lines 13-15:
    2074              : 
    2075              :      004
    2076              :      005                   |RANGE 1
    2077              :      006                   |RANGE 1
    2078              :      007
    2079              :      008  |PRIMARY RANGE
    2080              :      009  |PRIMARY CARET
    2081              :      010  |PRIMARY RANGE
    2082              :      011
    2083              :      012
    2084              :      013                                |RANGE 2
    2085              :      014                                |RANGE 2
    2086              :      015                                |RANGE 2
    2087              :      016
    2088              : 
    2089              :    With line numbering on, we want two spans: lines 5-10 and lines 13-15.
    2090              : 
    2091              :    With line numbering off (with span headers), we want three spans: lines 5-6,
    2092              :    lines 8-10, and lines 13-15.  */
    2093              : 
    2094              : void
    2095        50735 : layout::calculate_line_spans ()
    2096              : {
    2097              :   /* This should only be called once, by the ctor.  */
    2098        50735 :   gcc_assert (m_line_spans.length () == 0);
    2099              : 
    2100              :   /* Populate tmp_spans with individual spans, for each of
    2101              :      m_exploc, and for m_layout_ranges.  */
    2102       101470 :   auto_vec<line_span> tmp_spans (1 + m_layout_ranges.length ());
    2103        50735 :   tmp_spans.safe_push (line_span (m_exploc.line, m_exploc.line));
    2104       220704 :   for (unsigned int i = 0; i < m_layout_ranges.length (); i++)
    2105              :     {
    2106        59617 :       const layout_range *lr = &m_layout_ranges[i];
    2107        59617 :       gcc_assert (lr->m_start.m_line <= lr->m_finish.m_line);
    2108        59617 :       tmp_spans.safe_push (line_span (lr->m_start.m_line,
    2109        59617 :                                       lr->m_finish.m_line));
    2110              :     }
    2111              : 
    2112              :   /* Also add spans for any fix-it hints, in case they cover other lines.  */
    2113        65158 :   for (unsigned int i = 0; i < m_fixit_hints.length (); i++)
    2114              :     {
    2115        14423 :       const fixit_hint *hint = m_fixit_hints[i];
    2116        14423 :       gcc_assert (hint);
    2117        14423 :       tmp_spans.safe_push (get_line_span_for_fixit_hint (hint));
    2118              :     }
    2119              : 
    2120              :   /* Sort them.  */
    2121        50735 :   tmp_spans.qsort(line_span::comparator);
    2122              : 
    2123              :   /* Now iterate through tmp_spans, copying into m_line_spans, and
    2124              :      combining where possible.  */
    2125        50735 :   gcc_assert (tmp_spans.length () > 0);
    2126        50735 :   m_line_spans.safe_push (tmp_spans[0]);
    2127       175510 :   for (unsigned int i = 1; i < tmp_spans.length (); i++)
    2128              :     {
    2129       148080 :       line_span *current = &m_line_spans[m_line_spans.length () - 1];
    2130        74040 :       const line_span *next = &tmp_spans[i];
    2131        74040 :       gcc_assert (next->m_first_line >= current->m_first_line);
    2132        74040 :       const int merger_distance = m_options.show_line_numbers_p ? 1 : 0;
    2133        74040 :       if ((linenum_arith_t)next->m_first_line
    2134        74040 :           <= (linenum_arith_t)current->m_last_line + 1 + merger_distance)
    2135              :         {
    2136              :           /* We can merge them. */
    2137        72769 :           if (next->m_last_line > current->m_last_line)
    2138         2083 :             current->m_last_line = next->m_last_line;
    2139              :         }
    2140              :       else
    2141              :         {
    2142              :           /* No merger possible.  */
    2143         1271 :           m_line_spans.safe_push (*next);
    2144              :         }
    2145              :     }
    2146              : 
    2147              :   /* Verify the result, in m_line_spans.  */
    2148        50735 :   gcc_assert (m_line_spans.length () > 0);
    2149        52006 :   for (unsigned int i = 1; i < m_line_spans.length (); i++)
    2150              :     {
    2151         1271 :       const line_span *prev = &m_line_spans[i - 1];
    2152         1271 :       const line_span *next = &m_line_spans[i];
    2153              :       /* The individual spans must be sane.  */
    2154         1271 :       gcc_assert (prev->m_first_line <= prev->m_last_line);
    2155         1271 :       gcc_assert (next->m_first_line <= next->m_last_line);
    2156              :       /* The spans must be ordered.  */
    2157         1271 :       gcc_assert (prev->m_first_line < next->m_first_line);
    2158              :       /* There must be a gap of at least one line between separate spans.  */
    2159         1271 :       gcc_assert ((prev->m_last_line + 1) < next->m_first_line);
    2160              :     }
    2161        50735 : }
    2162              : 
    2163              : /* Determine how many display columns need to be reserved for line numbers,
    2164              :    based on the largest line number that will be needed, and populate
    2165              :    m_linenum_width.  */
    2166              : 
    2167              : void
    2168        50735 : layout::calculate_linenum_width ()
    2169              : {
    2170        50735 :   gcc_assert (m_line_spans.length () > 0);
    2171        50735 :   const line_span *last_span = &m_line_spans[m_line_spans.length () - 1];
    2172        50735 :   int highest_line = last_span->m_last_line;
    2173        50735 :   if (highest_line < 0)
    2174              :     highest_line = 0;
    2175        50735 :   m_linenum_width = diagnostics::num_digits (highest_line);
    2176              :   /* If we're showing jumps in the line-numbering, allow at least 3 chars.  */
    2177        50735 :   if (m_line_spans.length () > 1)
    2178         1223 :     m_linenum_width = MAX (m_linenum_width, 3);
    2179              :   /* If there's a minimum margin width, apply it (subtracting 1 for the space
    2180              :      after the line number.  */
    2181        50735 :   m_linenum_width = MAX (m_linenum_width, m_options.min_margin_width - 1);
    2182        50735 : }
    2183              : 
    2184              : /* Calculate m_x_offset_display, which improves readability in case the source
    2185              :    line of interest is longer than the user's display.  All lines output will be
    2186              :    shifted to the left (so that their beginning is no longer displayed) by
    2187              :    m_x_offset_display display columns, so that the caret is in a reasonable
    2188              :    location.  */
    2189              : 
    2190              : void
    2191        50735 : layout::calculate_x_offset_display ()
    2192              : {
    2193        50735 :   m_x_offset_display = 0;
    2194              : 
    2195        50735 :   const int max_width = m_options.max_width;
    2196        50735 :   if (!max_width)
    2197              :     {
    2198              :       /* Nothing to do, the width is not capped.  */
    2199        48262 :       return;
    2200              :     }
    2201              : 
    2202        50671 :   const diagnostics::char_span line
    2203        50671 :     = m_file_cache.get_source_line (m_exploc.file,
    2204              :                                     m_exploc.line);
    2205        50671 :   if (!line)
    2206              :     {
    2207              :       /* Nothing to do, we couldn't find the source line.  */
    2208              :       return;
    2209              :     }
    2210        47772 :   int caret_display_column = m_exploc.m_display_col;
    2211        47772 :   const int line_bytes
    2212        95544 :     = get_line_bytes_without_trailing_whitespace (line.get_buffer (),
    2213        47772 :                                                   line.length ());
    2214        47772 :   int eol_display_column
    2215        47772 :     = cpp_display_width (line.get_buffer (), line_bytes, m_char_policy);
    2216        47772 :   if (caret_display_column > eol_display_column
    2217        47772 :       || !caret_display_column)
    2218              :     {
    2219              :       /* This does not make sense, so don't try to do anything in this case.  */
    2220              :       return;
    2221              :     }
    2222              : 
    2223              :   /* Adjust caret and eol positions to include the left margin.  If we are
    2224              :      outputting line numbers, then the left margin is equal to m_linenum_width
    2225              :      plus three for the " | " which follows it.  Otherwise the left margin width
    2226              :      is equal to 1, because layout::print_source_line() will prefix each line
    2227              :      with a space.  */
    2228        34385 :   const int source_display_cols = eol_display_column;
    2229        34385 :   int left_margin_size = 1;
    2230        34385 :   if (m_options.show_line_numbers_p)
    2231        23020 :       left_margin_size = m_linenum_width + 3;
    2232        34385 :   caret_display_column += left_margin_size;
    2233        34385 :   eol_display_column += left_margin_size;
    2234              : 
    2235        34385 :   if (eol_display_column <= max_width)
    2236              :     {
    2237              :       /* Nothing to do, everything fits in the display.  */
    2238              :       return;
    2239              :     }
    2240              : 
    2241              :   /* The line is too long for the display.  Calculate an offset such that the
    2242              :      caret is not too close to the right edge of the screen.  It will be
    2243              :      CARET_LINE_MARGIN display columns from the right edge, unless it is closer
    2244              :      than that to the end of the source line anyway.  */
    2245         2601 :   int right_margin_size = CARET_LINE_MARGIN;
    2246         2601 :   right_margin_size = MIN (eol_display_column - caret_display_column,
    2247              :                            right_margin_size);
    2248         2601 :   if (right_margin_size + left_margin_size >= max_width)
    2249              :     {
    2250              :       /* The max_width is very small, so anything we try to do will not be very
    2251              :          effective; just punt in this case and output with no offset.  */
    2252              :       return;
    2253              :     }
    2254         2473 :   const int max_caret_display_column = max_width - right_margin_size;
    2255         2473 :   if (caret_display_column > max_caret_display_column)
    2256              :     {
    2257         2373 :       m_x_offset_display = caret_display_column - max_caret_display_column;
    2258              :       /* Make sure we don't offset the line into oblivion.  */
    2259         2373 :       static const int min_cols_visible = 2;
    2260         2373 :       if (source_display_cols - m_x_offset_display < min_cols_visible)
    2261            0 :         m_x_offset_display = 0;
    2262              :     }
    2263              : }
    2264              : 
    2265              : /* Print line ROW of source code, potentially colorized at any ranges, and
    2266              :    return the line bounds.  LINE is the source line (not necessarily
    2267              :    0-terminated) and LINE_BYTES is its length in bytes.  In order to handle both
    2268              :    colorization and tab expansion, this function tracks the line position in
    2269              :    both byte and display column units.  */
    2270              : 
    2271              : template<typename TextOrHtml>
    2272              : line_bounds
    2273        45541 : layout_printer<TextOrHtml>::print_source_line (linenum_type row,
    2274              :                                          const char *line,
    2275              :                                          int line_bytes)
    2276              : {
    2277        45244 :   m_text_or_html.colorize_text_ensure_normal ();
    2278          297 :   m_text_or_html.push_html_tag ("tr", true);
    2279        45244 :   m_text_or_html.emit_text_prefix ();
    2280        45541 :   if (m_layout.m_options.show_line_numbers_p)
    2281              :     {
    2282          153 :       m_text_or_html.push_html_tag_with_class ("td", "linenum", true);
    2283        26153 :       int width = diagnostics::num_digits (row);
    2284        87145 :       for (int i = 0; i < m_layout.get_linenum_width () - width; i++)
    2285        60992 :         m_text_or_html.add_space ();
    2286              :       char buf[20];
    2287        26153 :       sprintf (buf, "%i", row);
    2288        26153 :       m_text_or_html.add_text (buf);
    2289              :       if (TextOrHtml::is_text ())
    2290        26000 :         m_text_or_html.add_text (" |");
    2291          153 :       m_text_or_html.pop_html_tag ("td");
    2292              :     }
    2293              : 
    2294          297 :   m_text_or_html.push_html_tag_with_class ("td", "left-margin", true);
    2295        45541 :   print_leftmost_column ();
    2296          297 :   m_text_or_html.pop_html_tag ("td");
    2297              : 
    2298              :   /* We will stop printing the source line at any trailing whitespace.  */
    2299        45541 :   line_bytes = get_line_bytes_without_trailing_whitespace (line,
    2300              :                                                            line_bytes);
    2301              : 
    2302              :   /* This object helps to keep track of which display column we are at, which is
    2303              :      necessary for computing the line bounds in display units, for doing
    2304              :      tab expansion, and for implementing m_x_offset_display.  */
    2305        45541 :   cpp_display_width_computation dw (line, line_bytes, m_layout.m_char_policy);
    2306              : 
    2307          297 :   m_text_or_html.push_html_tag_with_class ("td", "source", true);
    2308              : 
    2309              :   /* Skip the first m_x_offset_display display columns.  In case the leading
    2310              :      portion that will be skipped ends with a character with wcwidth > 1, then
    2311              :      it is possible we skipped too much, so account for that by padding with
    2312              :      spaces.  Note that this does the right thing too in case a tab was the last
    2313              :      character to be skipped over; the tab is effectively replaced by the
    2314              :      correct number of trailing spaces needed to offset by the desired number of
    2315              :      display columns.  */
    2316        45541 :   for (int skipped_display_cols
    2317        45541 :          = dw.advance_display_cols (m_layout.m_x_offset_display);
    2318        46373 :        skipped_display_cols > m_layout.m_x_offset_display; --skipped_display_cols)
    2319          832 :     m_text_or_html.add_space ();
    2320              : 
    2321              :   /* Print the line and compute the line_bounds.  */
    2322              :   line_bounds lbounds;
    2323      3923875 :   while (!dw.done ())
    2324              :     {
    2325              :       /* Assuming colorization is enabled for the caret and underline
    2326              :          characters, we may also colorize the associated characters
    2327              :          within the source line.
    2328              : 
    2329              :          For frontends that generate range information, we color the
    2330              :          associated characters in the source line the same as the
    2331              :          carets and underlines in the annotation line, to make it easier
    2332              :          for the reader to see the pertinent code.
    2333              : 
    2334              :          For frontends that only generate carets, we don't colorize the
    2335              :          characters above them, since this would look strange (e.g.
    2336              :          colorizing just the first character in a token).  */
    2337      3878334 :       if (m_layout.m_options.colorize_source_p)
    2338              :         {
    2339              :           bool in_range_p;
    2340              :           point_state state;
    2341      2953596 :           const int start_byte_col = dw.bytes_processed () + 1;
    2342      2953596 :           in_range_p = m_layout.get_state_at_point (row, start_byte_col,
    2343              :                                                     0, INT_MAX,
    2344              :                                                     CU_BYTES,
    2345              :                                                     &state);
    2346      2953596 :           if (in_range_p)
    2347        67383 :             set_in_range (state.range_idx);
    2348              :           else
    2349      2886213 :             set_outside_range ();
    2350              :         }
    2351              : 
    2352              :       /* Get the display width of the next character to be output, expanding
    2353              :          tabs and replacing some control bytes with spaces as necessary.  */
    2354      3878334 :       const char *c = dw.next_byte ();
    2355      3878334 :       const int start_disp_col = dw.display_cols_processed () + 1;
    2356              :       cpp_decoded_char cp;
    2357      3878334 :       const int this_display_width = dw.process_next_codepoint (&cp);
    2358      3878334 :       if (*c == '\t')
    2359              :         {
    2360              :           /* The returned display width is the number of spaces into which the
    2361              :              tab should be expanded.  */
    2362        12849 :           for (int i = 0; i != this_display_width; ++i)
    2363        10822 :             m_text_or_html.add_space ();
    2364         2027 :           continue;
    2365         2027 :         }
    2366              : 
    2367              :       /* We have a (possibly multibyte) character to output; update the line
    2368              :          bounds if it is not whitespace.  */
    2369      3876307 :       if (*c != ' ')
    2370              :         {
    2371      3271517 :           lbounds.m_last_non_ws_disp_col = dw.display_cols_processed ();
    2372      3271517 :           if (lbounds.m_first_non_ws_disp_col == INT_MAX)
    2373        36698 :             lbounds.m_first_non_ws_disp_col = start_disp_col;
    2374              :         }
    2375              : 
    2376              :       /* Output the character.  */
    2377      3876307 :       m_text_or_html.print_decoded_char (m_layout.m_char_policy, cp);
    2378      3876307 :       c = dw.next_byte ();
    2379              :     }
    2380        45541 :   set_outside_range ();
    2381        45541 :   end_line ();
    2382        45541 :   return lbounds;
    2383              : }
    2384              : 
    2385              : /* Determine if we should print an annotation line for ROW.
    2386              :    i.e. if any of m_layout_ranges contains ROW.  */
    2387              : 
    2388              : bool
    2389        45541 : layout::should_print_annotation_line_p (linenum_type row) const
    2390              : {
    2391        45541 :   layout_range *range;
    2392        45541 :   int i;
    2393        50083 :   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
    2394              :     {
    2395        49127 :       if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
    2396              :         return false;
    2397        42974 :       if (range->intersects_line_p (row))
    2398              :         return true;
    2399              :     }
    2400              :   return false;
    2401              : }
    2402              : 
    2403              : /* Print the leftmost column after the margin, which is used for showing
    2404              :    links between labels (e.g. for CFG edges in execution paths).  */
    2405              : 
    2406              : template<typename TextOrHtml>
    2407              : void
    2408       109318 : layout_printer<TextOrHtml>::print_leftmost_column ()
    2409              : {
    2410       109318 :   if (!get_options ().show_event_links_p)
    2411        54505 :     gcc_assert (m_link_lhs_state == link_lhs_state::none);
    2412              : 
    2413       109318 :   switch (m_link_lhs_state)
    2414              :     {
    2415            0 :     default:
    2416            0 :       gcc_unreachable ();
    2417       102708 :     case link_lhs_state::none:
    2418       102708 :       m_text_or_html.add_space ();
    2419       102708 :       break;
    2420         1254 :     case link_lhs_state::rewinding_to_lhs:
    2421              :       {
    2422         1254 :         m_text_or_html.colorize_text_for_cfg_edge ();
    2423         1254 :         const cppchar_t ch = get_theme ().get_cppchar
    2424         1254 :           (text_art::theme::cell_kind::CFG_FROM_LEFT_TO_DOWN);
    2425         1254 :         m_text_or_html.add_character (ch);
    2426         1254 :         m_text_or_html.colorize_text_ensure_normal ();
    2427              :       }
    2428         1254 :       break;
    2429         4042 :     case link_lhs_state::at_lhs:
    2430              :       {
    2431         4042 :         m_text_or_html.colorize_text_for_cfg_edge ();
    2432         4042 :         const cppchar_t ch = get_theme ().get_cppchar
    2433         4042 :           (text_art::theme::cell_kind::CFG_DOWN);
    2434         4042 :         m_text_or_html.add_character (ch);
    2435         4042 :         m_text_or_html.colorize_text_ensure_normal ();
    2436              :       }
    2437         4042 :       break;
    2438         1314 :     case link_lhs_state::indenting_to_dest:
    2439              :       {
    2440         1314 :         m_text_or_html.colorize_text_for_cfg_edge ();
    2441         1314 :         const cppchar_t ch = get_theme ().get_cppchar
    2442         1314 :           (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_RIGHT);
    2443         1314 :         m_text_or_html.add_character (ch);
    2444         1314 :         m_text_or_html.colorize_text_ensure_normal ();
    2445              :       }
    2446         1314 :       break;
    2447              :     }
    2448       109318 : }
    2449              : 
    2450              : /* Begin an annotation line for either text or html output
    2451              : 
    2452              :    If m_show_line_numbers_p, print the left margin, which is empty
    2453              :    for annotation lines.
    2454              :    After any left margin, print a leftmost column, which is used for
    2455              :    showing links between labels (e.g. for CFG edges in execution paths).
    2456              : 
    2457              :    For text output, this also first prints the text prefix.
    2458              :    For html output, this also pushes <tr> and <td> open tags, where the
    2459              :    <td> is for the coming annotations.  */
    2460              : 
    2461              : template<typename TextOrHtml>
    2462              : void
    2463        64215 : layout_printer<TextOrHtml>::start_annotation_line (enum margin_kind margin)
    2464              : {
    2465        63487 :   m_text_or_html.emit_text_prefix ();
    2466          728 :   m_text_or_html.push_html_tag ("tr", true);
    2467              : 
    2468        63777 :   char margin_char = (margin == margin_kind::insertion
    2469        64215 :                       ? '+'
    2470              :                       : ' ');
    2471              : 
    2472        64215 :   if (get_options ().show_line_numbers_p)
    2473              :     {
    2474              :       /* Print the margin.  If MARGIN_CHAR != ' ', then print up to 3
    2475              :          of it, right-aligned, padded with spaces.  */
    2476          440 :       m_text_or_html.push_html_tag_with_class ("td", "linenum", true);
    2477              :       int i;
    2478       109525 :       for (i = 0; i < m_layout.m_linenum_width - 3; i++)
    2479        71918 :         m_text_or_html.add_space ();
    2480       148944 :       for (; i < m_layout.m_linenum_width; i++)
    2481       111337 :         m_text_or_html.add_character (margin_char);
    2482              :       if (TextOrHtml::is_text ())
    2483        37167 :         m_text_or_html.add_text (" |");
    2484          440 :       m_text_or_html.pop_html_tag ("td");
    2485              :     }
    2486              : 
    2487          728 :   m_text_or_html.push_html_tag_with_class ("td", "left-margin", true);
    2488        64215 :   if (margin == margin_kind::insertion)
    2489          438 :     m_text_or_html.add_character (margin_char);
    2490              :   else
    2491        63777 :     print_leftmost_column ();
    2492          728 :   m_text_or_html.pop_html_tag ("td");
    2493              : 
    2494         1453 :   m_text_or_html.push_html_tag_with_class ("td",
    2495              :                                            (margin == margin_kind::ruler
    2496              :                                             ? "ruler"
    2497              :                                             : "annotation"),
    2498              :                                            true);
    2499        64215 : }
    2500              : 
    2501              : /* End a source or annotation line: text implementation.
    2502              :    Reset any colorization and emit a newline.  */
    2503              : 
    2504              : template<>
    2505              : void
    2506       108647 : layout_printer<diagnostics::to_text>::end_line ()
    2507              : {
    2508       108647 :   m_text_or_html.colorize_text_ensure_normal ();
    2509       108647 :   m_text_or_html.add_newline ();
    2510       108647 : }
    2511              : 
    2512              : /* End a source or annotation line: HTML implementation.
    2513              :    Close the <td> and <tr> tags.  */
    2514              : 
    2515              : template<>
    2516              : void
    2517         1025 : layout_printer<diagnostics::to_html>::end_line ()
    2518              : {
    2519         1025 :   m_text_or_html.pop_html_tag ("td");
    2520         1025 :   m_text_or_html.pop_html_tag ("tr");
    2521         1025 : }
    2522              : 
    2523              : /* Handle the various transitions between being-in-range and
    2524              :    not-being-in-a-range, and between ranges.  */
    2525              : 
    2526              : template<typename TextOrHtml>
    2527              : void
    2528       222244 : layout_printer<TextOrHtml>::set_in_range (int range_idx)
    2529              : {
    2530       222244 :   if (m_was_in_range_p)
    2531              :     {
    2532       173275 :       if (m_last_range_idx != range_idx)
    2533              :         {
    2534              :           /* transition between ranges.  */
    2535          104 :           end_label (m_last_range_idx, false);
    2536           54 :           begin_label (range_idx, false);
    2537              :         }
    2538              :     }
    2539              :   else
    2540              :     {
    2541              :       /* transition from "not in a range" to "in a range".  */
    2542        48969 :       begin_label (range_idx, false);
    2543        48969 :       m_was_in_range_p = true;
    2544              :     }
    2545       222244 :   m_last_range_idx = range_idx;
    2546       222244 : }
    2547              : 
    2548              : template<typename TextOrHtml>
    2549              : void
    2550     13778908 : layout_printer<TextOrHtml>::set_outside_range ()
    2551              : {
    2552     13778908 :   if (m_was_in_range_p)
    2553              :     /* transition from "in a range" to "not in a range".  */
    2554        48969 :     end_label (m_last_range_idx, false);
    2555     13778908 :   m_was_in_range_p = false;
    2556     13778908 : }
    2557              : 
    2558              : /* Print a line consisting of the caret/underlines for the given
    2559              :    source line.  */
    2560              : 
    2561              : template<typename TextOrHtml>
    2562              : void
    2563        38432 : layout_printer<TextOrHtml>::print_annotation_line (linenum_type row,
    2564              :                                              const line_bounds lbounds)
    2565              : {
    2566        38432 :   int x_bound = m_layout.get_x_bound_for_row (row,
    2567        38432 :                                               m_layout.m_exploc.m_display_col,
    2568              :                                               lbounds.m_last_non_ws_disp_col);
    2569              : 
    2570        38432 :   start_annotation_line (margin_kind::normal);
    2571              : 
    2572     11002015 :   for (int column = 1 + m_layout.m_x_offset_display; column < x_bound; column++)
    2573              :     {
    2574              :       bool in_range_p;
    2575              :       point_state state;
    2576     21927166 :       in_range_p = m_layout.get_state_at_point (row, column,
    2577     10963583 :                                                 lbounds.m_first_non_ws_disp_col,
    2578              :                                                 lbounds.m_last_non_ws_disp_col,
    2579              :                                                 CU_DISPLAY_COLS,
    2580              :                                                 &state);
    2581     10963583 :       if (in_range_p)
    2582       154861 :         set_in_range (state.range_idx);
    2583              :       else
    2584     10808722 :         set_outside_range ();
    2585              : 
    2586     10963583 :       if (in_range_p)
    2587              :         {
    2588              :           /* Within a range.  Draw either the caret or an underline.  */
    2589       154861 :           if (state.draw_caret_p)
    2590              :             {
    2591              :               /* Draw the caret.  */
    2592              :               char caret_char;
    2593        36753 :               if (state.range_idx < rich_location::STATICALLY_ALLOCATED_RANGES)
    2594        36686 :                 caret_char = get_options ().caret_chars[state.range_idx];
    2595              :               else
    2596              :                 caret_char = '^';
    2597        36753 :               m_text_or_html.add_character (caret_char);
    2598              :             }
    2599              :           else
    2600       118108 :             m_text_or_html.add_character ('~');
    2601              :         }
    2602              :       else
    2603              :         {
    2604              :           /* Not in a range.  */
    2605     10808722 :           m_text_or_html.add_character (' ');
    2606              :         }
    2607              :     }
    2608              : 
    2609        38432 :   set_outside_range ();
    2610              : 
    2611        38432 :   end_line ();
    2612        38432 : }
    2613              : 
    2614              : /* A version of label_text that can live inside a vec.
    2615              :    Requires manual cleanup via maybe_free.  */
    2616              : 
    2617              : struct pod_label_text
    2618              : {
    2619              :   pod_label_text ()
    2620              :   : m_buffer (nullptr), m_caller_owned (false)
    2621              :   {}
    2622              : 
    2623         8802 :   pod_label_text (label_text &&other)
    2624         8802 :   : m_buffer (const_cast<char*> (other.get ())),
    2625         8802 :     m_caller_owned (other.is_owner ())
    2626              :   {
    2627         8802 :     other.release ();
    2628              :   }
    2629              : 
    2630         8802 :   void maybe_free ()
    2631              :   {
    2632         8802 :     if (m_caller_owned)
    2633         5243 :       free (m_buffer);
    2634              :   }
    2635              : 
    2636              :   char *m_buffer;
    2637              :   bool m_caller_owned;
    2638              : };
    2639              : 
    2640              : /* Implementation detail of layout::print_any_labels.
    2641              : 
    2642              :    A label within the given row of source.  */
    2643              : 
    2644              : class line_label
    2645              : {
    2646              : public:
    2647         8802 :   line_label (unsigned original_range_idx,
    2648              :               int state_idx, int column,
    2649              :               label_text text,
    2650              :               bool has_in_edge,
    2651              :               bool has_out_edge)
    2652         8802 :   : m_original_range_idx (original_range_idx),
    2653         8802 :     m_state_idx (state_idx), m_column (column),
    2654         8802 :     m_text (std::move (text)), m_label_line (0), m_has_vbar (true),
    2655         8802 :     m_has_in_edge (has_in_edge),
    2656         8802 :     m_has_out_edge (has_out_edge)
    2657              :   {
    2658              :     /* Using styled_string rather than cpp_display_width here
    2659              :        lets us skip SGR formatting characters for color and URLs.
    2660              :        It doesn't handle tabs and unicode escaping, but we don't
    2661              :        expect to see either of those in labels.  */
    2662         8802 :     text_art::style_manager sm;
    2663         8802 :     text_art::styled_string str (sm, m_text.m_buffer);
    2664         8802 :     m_display_width = str.calc_canvas_width ();
    2665         8802 :   }
    2666              : 
    2667              :   /* Sorting is primarily by column, then by state index.  */
    2668        20563 :   static int comparator (const void *p1, const void *p2)
    2669              :   {
    2670        20563 :     const line_label *ll1 = (const line_label *)p1;
    2671        20563 :     const line_label *ll2 = (const line_label *)p2;
    2672        20563 :     int column_cmp = compare (ll1->m_column, ll2->m_column);
    2673         7616 :     if (column_cmp)
    2674              :       return column_cmp;
    2675              :     /* Order by reverse state index, so that labels are printed
    2676              :        in order of insertion into the rich_location when the
    2677              :        sorted list is walked backwards.  */
    2678        10656 :     return -compare (ll1->m_state_idx, ll2->m_state_idx);
    2679              :   }
    2680              : 
    2681              :   unsigned m_original_range_idx;
    2682              :   int m_state_idx;
    2683              :   int m_column;
    2684              :   pod_label_text m_text;
    2685              :   size_t m_display_width;
    2686              :   int m_label_line;
    2687              :   bool m_has_vbar;
    2688              :   bool m_has_in_edge;
    2689              :   bool m_has_out_edge;
    2690              : };
    2691              : 
    2692              : /* Implementations of layout_printer::{begin,end}_label for
    2693              :    to_text and to_html.
    2694              : 
    2695              :    RANGE_IDX is the index of the range within the rich_location.
    2696              : 
    2697              :    IS_LABEL_TEXT is true for the text of the label,
    2698              :    false when quoting the source code, underlining the source
    2699              :    code, and for the vertical bars connecting the underlines
    2700              :    to the text of the label.  */
    2701              : 
    2702              : template<>
    2703              : void
    2704        65840 : layout_printer<diagnostics::to_text>::begin_label (int range_idx,
    2705              :                                                    bool is_label_text)
    2706              : {
    2707              :   /* Colorize the text, unless it's for labels for events in a
    2708              :      diagnostic path.  */
    2709        65840 :   if (is_label_text && m_is_diagnostic_path)
    2710              :     return;
    2711              : 
    2712        61183 :   gcc_assert (m_text_or_html.m_colorizer);
    2713        61183 :   m_text_or_html.m_colorizer->set_range (range_idx);
    2714              : }
    2715              : 
    2716              : template<>
    2717              : void
    2718         1360 : layout_printer<diagnostics::to_html>::begin_label (int range_idx,
    2719              :                                                    bool is_label_text)
    2720              : {
    2721         1360 :   if (is_label_text && m_text_or_html.m_html_label_writer)
    2722           12 :     m_text_or_html.m_html_label_writer->begin_label ();
    2723              : 
    2724         2720 :   if (const char *highlight_color
    2725         1360 :         = m_text_or_html.get_highlight_color_for_range_idx (range_idx))
    2726          382 :     m_text_or_html.m_xp.push_tag_with_class ("span", highlight_color);
    2727         1360 : }
    2728              : 
    2729              : template<>
    2730              : void
    2731        65840 : layout_printer<diagnostics::to_text>::end_label (int, bool)
    2732              : {
    2733        48429 :   m_text_or_html.colorize_text_ensure_normal ();
    2734            0 : }
    2735              : 
    2736              : template<>
    2737              : void
    2738         1360 : layout_printer<diagnostics::to_html>::end_label (int range_idx,
    2739              :                                                  bool is_label_text)
    2740              : {
    2741         1360 :   if (m_text_or_html.get_highlight_color_for_range_idx (range_idx))
    2742          382 :     m_text_or_html.m_xp.pop_tag ("span");
    2743              : 
    2744         1360 :   if (is_label_text && m_text_or_html.m_html_label_writer)
    2745           12 :     m_text_or_html.m_html_label_writer->end_label ();
    2746         1360 : }
    2747              : 
    2748              : /* Print any labels in this row.  */
    2749              : template <typename TextOrHtml>
    2750              : void
    2751        44836 : layout_printer<TextOrHtml>::print_any_labels (linenum_type row)
    2752              : {
    2753              :   int i;
    2754        44836 :   auto_vec<line_label> labels;
    2755              : 
    2756              :   /* Gather the labels that are to be printed into "labels".  */
    2757              :   {
    2758              :     layout_range *range;
    2759       145555 :     FOR_EACH_VEC_ELT (m_layout.m_layout_ranges, i, range)
    2760              :       {
    2761              :         /* Most ranges don't have labels, so reject this first.  */
    2762        55819 :         if (range->m_label == nullptr)
    2763        47017 :           continue;
    2764              : 
    2765              :         /* The range's caret must be on this line.  */
    2766        15960 :         if (range->m_caret.m_line != row)
    2767         7091 :           continue;
    2768              : 
    2769              :         /* Reject labels that aren't fully visible due to clipping
    2770              :            by m_x_offset_display.  */
    2771         8869 :         const int disp_col = range->m_caret.m_columns[CU_DISPLAY_COLS];
    2772         8869 :         if (disp_col <= m_layout.m_x_offset_display)
    2773            3 :           continue;
    2774              : 
    2775         8866 :         label_text text;
    2776         8866 :         text = range->m_label->get_text (range->m_original_idx);
    2777              : 
    2778              :         /* Allow for labels that return nullptr from their get_text
    2779              :            implementation (so e.g. such labels can control their own
    2780              :            visibility).  */
    2781         8866 :         if (text.get () == nullptr)
    2782              :           continue;
    2783              : 
    2784         8802 :         labels.safe_push (line_label (range->m_original_idx,
    2785              :                                       i, disp_col, std::move (text),
    2786              :                                       range->has_in_edge (),
    2787              :                                       range->has_out_edge ()));
    2788              :       }
    2789              :   }
    2790              : 
    2791              :   /* Bail out if there are no labels on this row.  */
    2792         5541 :   if (labels.length () == 0)
    2793        39295 :     return;
    2794              : 
    2795              :   /* Sort them.  */
    2796         5541 :   labels.qsort(line_label::comparator);
    2797              : 
    2798              :   /* Figure out how many "label lines" we need, and which
    2799              :      one each label is printed in.
    2800              : 
    2801              :      For example, if the labels aren't too densely packed,
    2802              :      we can fit them on the same line, giving two "label lines":
    2803              : 
    2804              :        foo + bar
    2805              :        ~~~   ~~~
    2806              :        |     |        : label line 0
    2807              :        l0    l1       : label line 1
    2808              : 
    2809              :      If they would touch each other or overlap, then we need
    2810              :      additional "label lines":
    2811              : 
    2812              :        foo + bar
    2813              :        ~~~   ~~~
    2814              :        |     |             : label line 0
    2815              :        |     label 1       : label line 1
    2816              :        label 0             : label line 2
    2817              : 
    2818              :      Place the final label on label line 1, and work backwards, adding
    2819              :      label lines as needed.
    2820              : 
    2821              :      If multiple labels are at the same place, put them on separate
    2822              :      label lines:
    2823              : 
    2824              :        foo + bar
    2825              :            ^               : label line 0
    2826              :            |               : label line 1
    2827              :            label 0         : label line 2
    2828              :            label 1         : label line 3.  */
    2829              : 
    2830         5541 :   int max_label_line = 1;
    2831         5541 :   int label_line_with_in_edge = -1;
    2832              :   {
    2833         5541 :     int next_column = INT_MAX;
    2834              :     line_label *label;
    2835        14343 :     FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
    2836              :       {
    2837              :         /* Would this label "touch" or overlap the next label?  */
    2838         8802 :         if (label->m_column + label->m_display_width >= (size_t)next_column)
    2839              :           {
    2840         2011 :             max_label_line++;
    2841              : 
    2842              :             /* If we've already seen labels with the same column, suppress the
    2843              :                vertical bar for subsequent ones in this backwards iteration;
    2844              :                hence only the one with the highest label_line has m_has_vbar set.  */
    2845         2011 :             if (label->m_column == next_column)
    2846         1156 :               label->m_has_vbar = false;
    2847              :           }
    2848              : 
    2849         8802 :         label->m_label_line = max_label_line;
    2850         8802 :         if (get_options ().show_event_links_p)
    2851         3228 :           if (label->m_has_in_edge)
    2852         8802 :             label_line_with_in_edge = max_label_line;
    2853         8802 :         next_column = label->m_column;
    2854              :       }
    2855              :   }
    2856              : 
    2857         5541 :   gcc_assert (labels.length () > 0);
    2858              : 
    2859              :   /* Print the "label lines".  For each label within the line, print
    2860              :      either a vertical bar ('|') for the labels that are lower down, or the
    2861              :      labels themselves once we've reached their line.  */
    2862              :   {
    2863        18634 :     for (int label_line = 0; label_line <= max_label_line; label_line++)
    2864              :       {
    2865        13093 :         if (label_line == label_line_with_in_edge)
    2866              :           {
    2867         1314 :             gcc_assert (get_options ().show_event_links_p);
    2868         1314 :             m_link_lhs_state = link_lhs_state::indenting_to_dest;
    2869              :           }
    2870        13093 :         start_annotation_line (margin_kind::normal);
    2871              : 
    2872        13093 :         int column = 1 + m_layout.m_x_offset_display;
    2873              :         line_label *label;
    2874        34992 :         FOR_EACH_VEC_ELT (labels, i, label)
    2875              :           {
    2876        23910 :             if (label_line > label->m_label_line)
    2877              :               /* We've printed all the labels for this label line.  */
    2878              :               break;
    2879              : 
    2880        21899 :             if (label_line == label->m_label_line)
    2881              :               {
    2882         8802 :                 gcc_assert (column <= label->m_column);
    2883              : 
    2884         8802 :                 if (label_line == label_line_with_in_edge)
    2885              :                   {
    2886              :                     /* Print a prefix showing an incoming
    2887              :                        link from another label.
    2888              :                        .|+----------->(10) ...to here
    2889              :                        . ^~~~~~~~~~~~~
    2890              :                        . this text.  */
    2891         1314 :                     gcc_assert (get_options ().show_event_links_p);
    2892         1314 :                     m_text_or_html.colorize_text_for_cfg_edge ();
    2893         1314 :                     const cppchar_t right= get_theme ().get_cppchar
    2894         1314 :                       (text_art::theme::cell_kind::CFG_RIGHT);
    2895        12520 :                     while (column < label->m_column - 1)
    2896              :                       {
    2897        11206 :                         m_text_or_html.add_character (right);
    2898        11206 :                         column++;
    2899              :                       }
    2900         1314 :                     if (column == label->m_column - 1)
    2901              :                       {
    2902         1030 :                         m_text_or_html.add_character ('>');
    2903         1030 :                         column++;
    2904              :                       }
    2905         1314 :                     m_text_or_html.colorize_text_ensure_normal ();
    2906         1314 :                     m_link_lhs_state = link_lhs_state::none;
    2907         1314 :                     label_line_with_in_edge = -1;
    2908              :                   }
    2909              :                 else
    2910         7488 :                   move_to_column (&column, label->m_column, true);
    2911         8802 :                 gcc_assert (column == label->m_column);
    2912              : 
    2913         8802 :                 begin_label (label->m_state_idx, true);
    2914         8802 :                 m_text_or_html.add_text (label->m_text.m_buffer);
    2915         8802 :                 end_label (label->m_state_idx, true);
    2916              : 
    2917         8802 :                 column += label->m_display_width;
    2918         8802 :                 if (get_options ().show_event_links_p && label->m_has_out_edge)
    2919              :                   {
    2920              :                     /* Print a suffix showing the start of a linkage
    2921              :                        to another label e.g. " ->-+" which will be the
    2922              :                        first part of e.g.
    2923              :                        .      (9) following ‘false’ branch... ->-+ <- HERE
    2924              :                        .                                         |
    2925              :                        .                                         |
    2926              :                        .  */
    2927         1314 :                     const cppchar_t right= get_theme ().get_cppchar
    2928         1314 :                       (text_art::theme::cell_kind::CFG_RIGHT);
    2929         1314 :                     const cppchar_t from_right_to_down= get_theme ().get_cppchar
    2930         1314 :                       (text_art::theme::cell_kind::CFG_FROM_RIGHT_TO_DOWN);
    2931         1314 :                     m_text_or_html.colorize_text_for_cfg_edge ();
    2932         1314 :                     m_text_or_html.add_space ();
    2933         1314 :                     m_text_or_html.add_character (right);
    2934         1314 :                     m_text_or_html.add_character ('>');
    2935         1314 :                     m_text_or_html.add_character (right);
    2936         1314 :                     m_text_or_html.add_character (from_right_to_down);
    2937         1314 :                     m_text_or_html.colorize_text_ensure_normal ();
    2938         1314 :                     column += 5;
    2939         1314 :                     m_link_rhs_column = column - 1;
    2940              :                   }
    2941              :               }
    2942        13097 :             else if (label->m_has_vbar)
    2943              :               {
    2944         9375 :                 gcc_assert (column <= label->m_column);
    2945         9375 :                 move_to_column (&column, label->m_column, true);
    2946         9375 :                 begin_label (label->m_state_idx, false);
    2947         9375 :                 m_text_or_html.add_character ('|');
    2948         9375 :                 end_label (label->m_state_idx, false);
    2949         9375 :                 column++;
    2950              :               }
    2951              :           }
    2952              : 
    2953              :         /* If we have a vertical link line on the RHS, print the
    2954              :            '|' on this annotation line after the labels.  */
    2955        13093 :         if (m_link_rhs_column != -1 && column < m_link_rhs_column)
    2956              :           {
    2957           56 :             move_to_column (&column, m_link_rhs_column, true);
    2958           56 :             m_text_or_html.colorize_text_for_cfg_edge ();
    2959           56 :             const cppchar_t down= get_theme ().get_cppchar
    2960           56 :               (text_art::theme::cell_kind::CFG_DOWN);
    2961           56 :             m_text_or_html.add_character (down);
    2962           56 :             m_text_or_html.colorize_text_ensure_normal ();
    2963              :           }
    2964              : 
    2965        13093 :         end_line ();
    2966              :       }
    2967              :     }
    2968              : 
    2969              :   /* If we have a vertical link line on the RHS, print a trailing
    2970              :      annotation line showing the vertical line.  */
    2971         5541 :   if (m_link_rhs_column != -1)
    2972              :     {
    2973         1314 :       int column = 1 + m_layout.m_x_offset_display;
    2974         1314 :       start_annotation_line (margin_kind::normal);
    2975         1314 :       move_to_column (&column, m_link_rhs_column, true);
    2976         1314 :       m_text_or_html.colorize_text_for_cfg_edge ();
    2977         1314 :       const cppchar_t down= get_theme ().get_cppchar
    2978         1314 :         (text_art::theme::cell_kind::CFG_DOWN);
    2979         1314 :       m_text_or_html.add_character (down);
    2980         1314 :       end_line ();
    2981              :     }
    2982              : 
    2983              :   /* Clean up.  */
    2984              :   {
    2985              :     line_label *label;
    2986        14343 :     FOR_EACH_VEC_ELT (labels, i, label)
    2987        14045 :       label->m_text.maybe_free ();
    2988              :   }
    2989        44836 : }
    2990              : 
    2991              : /* If there are any fixit hints inserting new lines before source line ROW,
    2992              :    print them.
    2993              : 
    2994              :    They are printed on lines of their own, before the source line
    2995              :    itself, with a leading '+'.  */
    2996              : 
    2997              : template <typename TextOrHtml>
    2998              : void
    2999        45541 : layout_printer<TextOrHtml>::print_leading_fixits (linenum_type row)
    3000              : {
    3001        60131 :   for (unsigned int i = 0; i < m_layout.m_fixit_hints.length (); i++)
    3002              :     {
    3003        14590 :       const fixit_hint *hint = m_layout.m_fixit_hints[i];
    3004              : 
    3005        14590 :       if (!hint->ends_with_newline_p ())
    3006              :         /* Not a newline fixit; print it in print_trailing_fixits.  */
    3007        13679 :         continue;
    3008              : 
    3009          911 :       gcc_assert (hint->insertion_p ());
    3010              : 
    3011          911 :       if (hint->affects_line_p (m_layout.m_line_table,
    3012          911 :                                 m_layout.m_exploc.file,
    3013              :                                 row))
    3014              :         {
    3015              :           /* Printing the '+' with normal colorization
    3016              :              and the inserted line with "insert" colorization
    3017              :              helps them stand out from each other, and from
    3018              :              the surrounding text.  */
    3019          437 :           m_text_or_html.colorize_text_ensure_normal ();
    3020          438 :           start_annotation_line (margin_kind::insertion);
    3021          437 :           m_text_or_html.colorize_text_for_fixit_insert ();
    3022              :           /* Print all but the trailing newline of the fix-it hint.
    3023              :              We have to print the newline separately to avoid
    3024              :              getting additional pp prefixes printed.  */
    3025         7399 :           for (size_t i = 0; i < hint->get_length () - 1; i++)
    3026         6961 :             m_text_or_html.add_character (hint->get_string ()[i]);
    3027          438 :           end_line ();
    3028              :         }
    3029              :     }
    3030        45541 : }
    3031              : 
    3032              : /* Subroutine of layout::print_trailing_fixits.
    3033              : 
    3034              :    Determine if the annotation line printed for LINE contained
    3035              :    the exact range from START_COLUMN to FINISH_COLUMN (in display units).  */
    3036              : 
    3037              : bool
    3038         3936 : layout::annotation_line_showed_range_p (linenum_type line, int start_column,
    3039              :                                         int finish_column) const
    3040              : {
    3041         3936 :   layout_range *range;
    3042         3936 :   int i;
    3043         8171 :   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
    3044         6479 :     if (range->m_start.m_line == line
    3045         4337 :         && range->m_start.m_columns[CU_DISPLAY_COLS] == start_column
    3046         2839 :         && range->m_finish.m_line == line
    3047         2839 :         && range->m_finish.m_columns[CU_DISPLAY_COLS] == finish_column)
    3048              :       return true;
    3049              :   return false;
    3050              : }
    3051              : 
    3052              : /* Classes for printing trailing fix-it hints i.e. those that
    3053              :    don't add new lines.
    3054              : 
    3055              :    For insertion, these can look like:
    3056              : 
    3057              :      new_text
    3058              : 
    3059              :    For replacement, these can look like:
    3060              : 
    3061              :      ------------- : underline showing affected range
    3062              :      new_text
    3063              : 
    3064              :    For deletion, these can look like:
    3065              : 
    3066              :      ------------- : underline showing affected range
    3067              : 
    3068              :    This can become confusing if they overlap, and so we need
    3069              :    to do some preprocessing to decide what to print.
    3070              :    We use the list of fixit_hint instances affecting the line
    3071              :    to build a list of "correction" instances, and print the
    3072              :    latter.
    3073              : 
    3074              :    For example, consider a set of fix-its for converting
    3075              :    a C-style cast to a C++ const_cast.
    3076              : 
    3077              :    Given:
    3078              : 
    3079              :    ..000000000111111111122222222223333333333.
    3080              :    ..123456789012345678901234567890123456789.
    3081              :      foo *f = (foo *)ptr->field;
    3082              :                           ^~~~~
    3083              : 
    3084              :    and the fix-it hints:
    3085              :      - replace col 10 (the open paren) with "const_cast<"
    3086              :      - replace col 16 (the close paren) with "> ("
    3087              :      - insert ")" before col 27
    3088              : 
    3089              :    then we would get odd-looking output:
    3090              : 
    3091              :      foo *f = (foo *)ptr->field;
    3092              :                           ^~~~~
    3093              :               -
    3094              :               const_cast<
    3095              :                     -
    3096              :                     > (        )
    3097              : 
    3098              :    It would be better to detect when fixit hints are going to
    3099              :    overlap (those that require new lines), and to consolidate
    3100              :    the printing of such fixits, giving something like:
    3101              : 
    3102              :      foo *f = (foo *)ptr->field;
    3103              :                           ^~~~~
    3104              :               -----------------
    3105              :               const_cast<foo *> (ptr->field)
    3106              : 
    3107              :    This works by detecting when the printing would overlap, and
    3108              :    effectively injecting no-op replace hints into the gaps between
    3109              :    such fix-its, so that the printing joins up.
    3110              : 
    3111              :    In the above example, the overlap of:
    3112              :      - replace col 10 (the open paren) with "const_cast<"
    3113              :    and:
    3114              :      - replace col 16 (the close paren) with "> ("
    3115              :    is fixed by injecting a no-op:
    3116              :      - replace cols 11-15 with themselves ("foo *")
    3117              :    and consolidating these, making:
    3118              :      - replace cols 10-16 with "const_cast<" + "foo *" + "> ("
    3119              :    i.e.:
    3120              :      - replace cols 10-16 with "const_cast<foo *> ("
    3121              : 
    3122              :    This overlaps with the final fix-it hint:
    3123              :      - insert ")" before col 27
    3124              :    and so we repeat the consolidation process, by injecting
    3125              :    a no-op:
    3126              :      - replace cols 17-26 with themselves ("ptr->field")
    3127              :    giving:
    3128              :      - replace cols 10-26 with "const_cast<foo *> (" + "ptr->field" + ")"
    3129              :    i.e.:
    3130              :      - replace cols 10-26 with "const_cast<foo *> (ptr->field)"
    3131              : 
    3132              :    and is thus printed as desired.  */
    3133              : 
    3134              : /* A range of (byte or display) columns within a line.  */
    3135              : 
    3136              : class column_range
    3137              : {
    3138              : public:
    3139        43766 :   column_range (int start_, int finish_) : start (start_), finish (finish_)
    3140              :   {
    3141            0 :     gcc_assert (valid_p (start, finish));
    3142        41494 :   }
    3143              : 
    3144         2272 :   bool operator== (const column_range &other) const
    3145              :   {
    3146         2272 :     return start == other.start && finish == other.finish;
    3147              :   }
    3148              : 
    3149        44933 :   static bool valid_p (int start, int finish)
    3150              :   {
    3151              :     /* We must have either a range, or an insertion.  */
    3152        41754 :     return (start <= finish || finish == start - 1);
    3153              :   }
    3154              : 
    3155              :   int start;
    3156              :   int finish;
    3157              : };
    3158              : 
    3159              : /* Get the range of bytes or display columns that HINT would affect.  */
    3160              : static column_range
    3161        26842 : get_affected_range (diagnostics::file_cache &fc,
    3162              :                     const cpp_char_column_policy &policy,
    3163              :                     const fixit_hint *hint, enum column_unit col_unit)
    3164              : {
    3165        26842 :   expanded_location exploc_start = expand_location (hint->get_start_loc ());
    3166        26842 :   expanded_location exploc_finish = expand_location (hint->get_next_loc ());
    3167        26842 :   --exploc_finish.column;
    3168              : 
    3169        26842 :   int start_column;
    3170        26842 :   int finish_column;
    3171        26842 :   if (col_unit == CU_DISPLAY_COLS)
    3172              :     {
    3173        13357 :       start_column = location_compute_display_column (fc, exploc_start, policy);
    3174        13357 :       if (hint->insertion_p ())
    3175         9033 :         finish_column = start_column - 1;
    3176              :       else
    3177         4324 :         finish_column
    3178         4324 :           = location_compute_display_column (fc, exploc_finish, policy);
    3179              :     }
    3180              :   else
    3181              :     {
    3182        13485 :       start_column = exploc_start.column;
    3183        13485 :       finish_column = exploc_finish.column;
    3184              :     }
    3185        26842 :   return column_range (start_column, finish_column);
    3186              : }
    3187              : 
    3188              : /* Get the range of display columns that would be printed for HINT.  */
    3189              : 
    3190              : static column_range
    3191        13485 : get_printed_columns (diagnostics::file_cache &fc,
    3192              :                      const cpp_char_column_policy &policy,
    3193              :                      const fixit_hint *hint)
    3194              : {
    3195        13485 :   expanded_location exploc = expand_location (hint->get_start_loc ());
    3196        13485 :   int start_column = location_compute_display_column (fc, exploc, policy);
    3197        13485 :   int hint_width = cpp_display_width (hint->get_string (), hint->get_length (),
    3198              :                                       policy);
    3199        13485 :   int final_hint_column = start_column + hint_width - 1;
    3200        13485 :   if (hint->insertion_p ())
    3201              :     {
    3202         9161 :       return column_range (start_column, final_hint_column);
    3203              :     }
    3204              :   else
    3205              :     {
    3206         4324 :       exploc = expand_location (hint->get_next_loc ());
    3207         4324 :       --exploc.column;
    3208         4324 :       int finish_column = location_compute_display_column (fc, exploc, policy);
    3209         8648 :       return column_range (start_column,
    3210         4324 :                            MAX (finish_column, final_hint_column));
    3211              :     }
    3212              : }
    3213              : 
    3214              : /* A correction on a particular line.
    3215              :    This describes a plan for how to print one or more fixit_hint
    3216              :    instances that affected the line, potentially consolidating hints
    3217              :    into corrections to make the result easier for the user to read.  */
    3218              : 
    3219              : class correction
    3220              : {
    3221              : public:
    3222        11854 :   correction (column_range affected_bytes,
    3223              :               column_range affected_columns,
    3224              :               column_range printed_columns,
    3225              :               const char *new_text, size_t new_text_len,
    3226              :               const cpp_char_column_policy &policy)
    3227        11854 :   : m_affected_bytes (affected_bytes),
    3228        11854 :     m_affected_columns (affected_columns),
    3229        11854 :     m_printed_columns (printed_columns),
    3230        11854 :     m_text (xstrdup (new_text)),
    3231        11854 :     m_byte_length (new_text_len),
    3232        11854 :     m_policy (policy),
    3233        11854 :     m_alloc_sz (new_text_len + 1)
    3234              :   {
    3235        11854 :     compute_display_cols ();
    3236        11854 :   }
    3237              : 
    3238        11854 :   ~correction () { free (m_text); }
    3239              : 
    3240        11742 :   bool insertion_p () const
    3241              :   {
    3242        11742 :     return m_affected_bytes.start == m_affected_bytes.finish + 1;
    3243              :   }
    3244              : 
    3245              :   void ensure_capacity (size_t len);
    3246              :   void ensure_terminated ();
    3247              : 
    3248        13021 :   void compute_display_cols ()
    3249              :   {
    3250        13021 :     m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy);
    3251        13021 :   }
    3252              : 
    3253         2334 :   void overwrite (int dst_offset, const diagnostics::char_span &src_span)
    3254              :   {
    3255         2334 :     gcc_assert (dst_offset >= 0);
    3256         2334 :     gcc_assert (dst_offset + src_span.length () < m_alloc_sz);
    3257         2334 :     memcpy (m_text + dst_offset, src_span.get_buffer (),
    3258              :             src_span.length ());
    3259         2334 :   }
    3260              : 
    3261              :   /* If insert, then start: the column before which the text
    3262              :      is to be inserted, and finish is offset by the length of
    3263              :      the replacement.
    3264              :      If replace, then the range of columns affected.  */
    3265              :   column_range m_affected_bytes;
    3266              :   column_range m_affected_columns;
    3267              : 
    3268              :   /* If insert, then start: the column before which the text
    3269              :      is to be inserted, and finish is offset by the length of
    3270              :      the replacement.
    3271              :      If replace, then the range of columns affected.  */
    3272              :   column_range m_printed_columns;
    3273              : 
    3274              :   /* The text to be inserted/used as replacement.  */
    3275              :   char *m_text;
    3276              :   size_t m_byte_length; /* Not including null-terminator.  */
    3277              :   int m_display_cols;
    3278              :   const cpp_char_column_policy &m_policy;
    3279              :   size_t m_alloc_sz;
    3280              : };
    3281              : 
    3282              : /* Ensure that m_text can hold a string of length LEN
    3283              :    (plus 1 for 0-termination).  */
    3284              : 
    3285              : void
    3286         1167 : correction::ensure_capacity (size_t len)
    3287              : {
    3288              :   /* Allow 1 extra byte for 0-termination.  */
    3289         1167 :   if (m_alloc_sz < (len + 1))
    3290              :     {
    3291          603 :       size_t new_alloc_sz = (len + 1) * 2;
    3292          603 :       m_text = (char *)xrealloc (m_text, new_alloc_sz);
    3293          603 :       m_alloc_sz = new_alloc_sz;
    3294              :     }
    3295         1167 : }
    3296              : 
    3297              : /* Ensure that m_text is 0-terminated.  */
    3298              : 
    3299              : void
    3300         1167 : correction::ensure_terminated ()
    3301              : {
    3302              :   /* 0-terminate the buffer.  */
    3303         1167 :   gcc_assert (m_byte_length < m_alloc_sz);
    3304         1167 :   m_text[m_byte_length] = '\0';
    3305         1167 : }
    3306              : 
    3307              : /* A list of corrections affecting a particular line.
    3308              :    This is used by layout::print_trailing_fixits for planning
    3309              :    how to print the fix-it hints affecting the line.  */
    3310              : 
    3311              : class line_corrections
    3312              : {
    3313              : public:
    3314        45653 :   line_corrections (diagnostics::file_cache &fc,
    3315              :                     const char_display_policy &policy,
    3316              :                     const char *filename,
    3317              :                     linenum_type row)
    3318        45653 :   : m_file_cache (fc),
    3319          112 :     m_policy (policy), m_filename (filename), m_row (row)
    3320              :   {}
    3321              :   ~line_corrections ();
    3322              : 
    3323              :   void add_hint (const fixit_hint *hint);
    3324              : 
    3325              :   diagnostics::file_cache &m_file_cache;
    3326              :   const char_display_policy &m_policy;
    3327              :   const char *m_filename;
    3328              :   linenum_type m_row;
    3329              :   auto_vec <correction *> m_corrections;
    3330              : };
    3331              : 
    3332              : /* struct line_corrections.  */
    3333              : 
    3334        45653 : line_corrections::~line_corrections ()
    3335              : {
    3336        45653 :   unsigned i;
    3337        45653 :   correction *c;
    3338        57507 :   FOR_EACH_VEC_ELT (m_corrections, i, c)
    3339        11854 :     delete c;
    3340        45653 : }
    3341              : 
    3342              : /* A struct wrapping a particular source line, allowing
    3343              :    run-time bounds-checking of accesses in a checked build.  */
    3344              : 
    3345              : class source_line
    3346              : {
    3347              : public:
    3348              :   source_line (diagnostics::file_cache &fc, const char *filename, int line);
    3349              : 
    3350         1167 :   diagnostics::char_span as_span ()
    3351              :   {
    3352         1167 :     return diagnostics::char_span (chars, width);
    3353              :   }
    3354              : 
    3355              :   const char *chars;
    3356              :   int width;
    3357              : };
    3358              : 
    3359              : /* source_line's ctor.  */
    3360              : 
    3361         1167 : source_line::source_line (diagnostics::file_cache &fc,
    3362              :                           const char *filename,
    3363              :                           int line)
    3364              : {
    3365            0 :   diagnostics::char_span span = fc.get_source_line (filename, line);
    3366         1167 :   chars = span.get_buffer ();
    3367         1167 :   width = span.length ();
    3368            0 : }
    3369              : 
    3370              : /* Add HINT to the corrections for this line.
    3371              :    Attempt to consolidate nearby hints so that they will not
    3372              :    overlap with printed.  */
    3373              : 
    3374              : void
    3375        13021 : line_corrections::add_hint (const fixit_hint *hint)
    3376              : {
    3377        13021 :   column_range affected_bytes
    3378        13021 :     = get_affected_range (m_file_cache, m_policy, hint, CU_BYTES);
    3379        13021 :   column_range affected_columns
    3380        13021 :     = get_affected_range (m_file_cache, m_policy, hint, CU_DISPLAY_COLS);
    3381        13021 :   column_range printed_columns
    3382        13021 :     = get_printed_columns (m_file_cache, m_policy, hint);
    3383              : 
    3384              :   /* Potentially consolidate.  */
    3385        13021 :   if (!m_corrections.is_empty ())
    3386              :     {
    3387         6728 :       correction *last_correction
    3388         6728 :         = m_corrections[m_corrections.length () - 1];
    3389              : 
    3390              :       /* The following consolidation code assumes that the fix-it hints
    3391              :          have been sorted by start (done within layout's ctor).  */
    3392         6728 :       gcc_assert (affected_bytes.start
    3393              :                   >= last_correction->m_affected_bytes.start);
    3394         6728 :       gcc_assert (printed_columns.start
    3395              :                   >= last_correction->m_printed_columns.start);
    3396              : 
    3397         6728 :       if (printed_columns.start <= last_correction->m_printed_columns.finish
    3398         6728 :           && column_range::valid_p (last_correction->m_affected_bytes.finish + 1,
    3399              :                                     affected_bytes.start - 1))
    3400              :         {
    3401              :           /* We have two hints for which the printed forms of the hints
    3402              :              would touch or overlap, so we need to consolidate them to avoid
    3403              :              confusing the user.
    3404              :              Attempt to inject a "replace" correction from immediately
    3405              :              after the end of the last hint to immediately before the start
    3406              :              of the next hint.  */
    3407         1167 :           column_range between (last_correction->m_affected_bytes.finish + 1,
    3408         1167 :                                 affected_bytes.start - 1);
    3409              : 
    3410              :           /* Try to read the source.  */
    3411         1167 :           source_line line (m_file_cache, m_filename, m_row);
    3412         1167 :           if (line.chars && between.finish < line.width)
    3413              :             {
    3414              :               /* Consolidate into the last correction:
    3415              :                  add a no-op "replace" of the "between" text, and
    3416              :                  add the text from the new hint.  */
    3417         1167 :               int old_byte_len = last_correction->m_byte_length;
    3418         1167 :               gcc_assert (old_byte_len >= 0);
    3419         1167 :               int between_byte_len = between.finish + 1 - between.start;
    3420         1167 :               gcc_assert (between_byte_len >= 0);
    3421         1167 :               int new_byte_len
    3422         1167 :                 = old_byte_len + between_byte_len + hint->get_length ();
    3423         1167 :               gcc_assert (new_byte_len >= 0);
    3424         1167 :               last_correction->ensure_capacity (new_byte_len);
    3425         1167 :               last_correction->overwrite
    3426         1167 :                 (old_byte_len,
    3427         1167 :                  line.as_span ().subspan (between.start - 1,
    3428              :                                           between.finish + 1 - between.start));
    3429         1167 :               last_correction->overwrite
    3430         1167 :                 (old_byte_len + between_byte_len,
    3431         1167 :                  diagnostics::char_span (hint->get_string (),
    3432         1167 :                                          hint->get_length ()));
    3433         1167 :               last_correction->m_byte_length = new_byte_len;
    3434         1167 :               last_correction->ensure_terminated ();
    3435         1167 :               last_correction->m_affected_bytes.finish
    3436         1167 :                 = affected_bytes.finish;
    3437         1167 :               last_correction->m_affected_columns.finish
    3438         1167 :                 = affected_columns.finish;
    3439         1167 :               int prev_display_cols = last_correction->m_display_cols;
    3440         1167 :               last_correction->compute_display_cols ();
    3441         1167 :               last_correction->m_printed_columns.finish
    3442         1167 :                 += last_correction->m_display_cols - prev_display_cols;
    3443         1167 :               return;
    3444              :             }
    3445              :         }
    3446              :     }
    3447              : 
    3448              :   /* If no consolidation happened, add a new correction instance.  */
    3449        11854 :   m_corrections.safe_push (new correction (affected_bytes,
    3450              :                                            affected_columns,
    3451              :                                            printed_columns,
    3452              :                                            hint->get_string (),
    3453              :                                            hint->get_length (),
    3454        11854 :                                            m_policy));
    3455              : }
    3456              : 
    3457              : /* If there are any fixit hints on source line ROW, print them.
    3458              :    They are printed in order, attempting to combine them onto lines, but
    3459              :    starting new lines if necessary.
    3460              :    Fix-it hints that insert new lines are handled separately,
    3461              :    in layout::print_leading_fixits.  */
    3462              : 
    3463              : template<typename TextOrHtml>
    3464              : void
    3465        45541 : layout_printer<TextOrHtml>::print_trailing_fixits (linenum_type row)
    3466              : {
    3467        45541 :   typename TextOrHtml::auto_check_tag_nesting sentinel (m_text_or_html);
    3468              : 
    3469              :   /* Build a list of correction instances for the line,
    3470              :      potentially consolidating hints (for the sake of readability).  */
    3471        45541 :   line_corrections corrections (m_layout.m_file_cache, m_layout.m_char_policy,
    3472        45541 :                                 m_layout.m_exploc.file, row);
    3473        60131 :   for (unsigned int i = 0; i < m_layout.m_fixit_hints.length (); i++)
    3474              :     {
    3475        14590 :       const fixit_hint *hint = m_layout.m_fixit_hints[i];
    3476              : 
    3477              :       /* Newline fixits are handled by layout::print_leading_fixits.  */
    3478        14590 :       if (hint->ends_with_newline_p ())
    3479          911 :         continue;
    3480              : 
    3481        13679 :       if (hint->affects_line_p (m_layout.m_line_table,
    3482        13679 :                                 m_layout.m_exploc.file,
    3483              :                                 row))
    3484        12685 :         corrections.add_hint (hint);
    3485              :     }
    3486              : 
    3487              :   /* Now print the corrections.  */
    3488              :   unsigned i;
    3489              :   correction *c;
    3490        45541 :   int column = 1 + m_layout.m_x_offset_display;
    3491              : 
    3492        51722 :   if (!corrections.m_corrections.is_empty ())
    3493         6181 :     start_annotation_line (margin_kind::normal);
    3494              : 
    3495        57283 :   FOR_EACH_VEC_ELT (corrections.m_corrections, i, c)
    3496              :     {
    3497              :       /* For now we assume each fixit hint can only touch one line.  */
    3498        11742 :       if (c->insertion_p ())
    3499              :         {
    3500              :           /* This assumes the insertion just affects one line.  */
    3501         7806 :           int start_column = c->m_printed_columns.start;
    3502         7806 :           move_to_column (&column, start_column, true);
    3503         7804 :           m_text_or_html.colorize_text_for_fixit_insert ();
    3504         7806 :           m_text_or_html.add_text (c->m_text);
    3505         7804 :           m_text_or_html.colorize_text_ensure_normal ();
    3506         7806 :           column += c->m_display_cols;
    3507              :         }
    3508              :       else
    3509              :         {
    3510              :           /* If the range of the replacement wasn't printed in the
    3511              :              annotation line, then print an extra underline to
    3512              :              indicate exactly what is being replaced.
    3513              :              Always show it for removals.  */
    3514         3936 :           int start_column = c->m_affected_columns.start;
    3515         3936 :           int finish_column = c->m_affected_columns.finish;
    3516         3936 :           if (!m_layout.annotation_line_showed_range_p (row, start_column,
    3517              :                                                         finish_column)
    3518         3936 :               || c->m_byte_length == 0)
    3519              :             {
    3520         2461 :               move_to_column (&column, start_column, true);
    3521         2460 :               m_text_or_html.colorize_text_for_fixit_delete ();
    3522      1872649 :               for (; column <= finish_column; column++)
    3523      1870188 :                 m_text_or_html.add_character ('-');
    3524         2460 :               m_text_or_html.colorize_text_ensure_normal ();
    3525              :             }
    3526              :           /* Print the replacement text.  REPLACE also covers
    3527              :              removals, so only do this extra work (potentially starting
    3528              :              a new line) if we have actual replacement text.  */
    3529         3936 :           if (c->m_byte_length > 0)
    3530              :             {
    3531         2877 :               move_to_column (&column, start_column, true);
    3532         2847 :               m_text_or_html.colorize_text_for_fixit_insert ();
    3533         2877 :               m_text_or_html.add_text (c->m_text);
    3534         2847 :               m_text_or_html.colorize_text_ensure_normal ();
    3535         2877 :               column += c->m_display_cols;
    3536              :             }
    3537              :         }
    3538              :     }
    3539              : 
    3540              :   /* Add a trailing newline, if necessary.  */
    3541        45541 :   move_to_column (&column, 1 + m_layout.m_x_offset_display, false);
    3542        45541 : }
    3543              : 
    3544              : /* Return true if (ROW/COLUMN) is within a range of the layout.
    3545              :    If it returns true, OUT_STATE is written to, with the
    3546              :    range index, and whether we should draw the caret at
    3547              :    (ROW/COLUMN) (as opposed to an underline).  COL_UNIT controls
    3548              :    whether all inputs and outputs are in bytes or display column units.  */
    3549              : 
    3550              : bool
    3551     13917179 : layout::get_state_at_point (/* Inputs.  */
    3552              :                             linenum_type row, int column,
    3553              :                             int first_non_ws, int last_non_ws,
    3554              :                             enum column_unit col_unit,
    3555              :                             /* Outputs.  */
    3556              :                             point_state *out_state) const
    3557              : {
    3558     13917179 :   layout_range *range;
    3559     13917179 :   int i;
    3560     27896191 :   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
    3561              :     {
    3562     14258010 :       if (range->m_range_display_kind == SHOW_LINES_WITHOUT_RANGE)
    3563              :         /* Bail out early, so that such ranges don't affect underlining or
    3564              :            source colorization.  */
    3565         4389 :         continue;
    3566              : 
    3567     14253621 :       if (range->contains_point (row, column, col_unit))
    3568              :         {
    3569       278998 :           out_state->range_idx = i;
    3570              : 
    3571              :           /* Are we at the range's caret?  is it visible? */
    3572       278998 :           out_state->draw_caret_p = false;
    3573       278998 :           if (range->m_range_display_kind == SHOW_RANGE_WITH_CARET
    3574       233577 :               && row == range->m_caret.m_line
    3575       226510 :               && column == range->m_caret.m_columns[col_unit])
    3576        42948 :             out_state->draw_caret_p = true;
    3577              : 
    3578              :           /* Within a multiline range, don't display any underline
    3579              :              in any leading or trailing whitespace on a line.
    3580              :              We do display carets, however.  */
    3581       278998 :           if (!out_state->draw_caret_p)
    3582       236050 :             if (column < first_non_ws || column > last_non_ws)
    3583              :               return false;
    3584              : 
    3585              :           /* We are within a range.  */
    3586       222244 :           return true;
    3587              :         }
    3588              :     }
    3589              : 
    3590              :   return false;
    3591              : }
    3592              : 
    3593              : /* Helper function for use by layout::print_line when printing the
    3594              :    annotation line under the source line.
    3595              :    Get the display column beyond the rightmost one that could contain a caret
    3596              :    or range marker, given that we stop rendering at trailing whitespace.
    3597              :    ROW is the source line within the given file.
    3598              :    CARET_COLUMN is the display column of range 0's caret.
    3599              :    LAST_NON_WS_COLUMN is the last display column containing a non-whitespace
    3600              :    character of source (as determined when printing the source line).  */
    3601              : 
    3602              : int
    3603        38432 : layout::get_x_bound_for_row (linenum_type row, int caret_column,
    3604              :                              int last_non_ws_column) const
    3605              : {
    3606        38432 :   int result = caret_column + 1;
    3607              : 
    3608        38432 :   layout_range *range;
    3609        38432 :   int i;
    3610        87057 :   FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
    3611              :     {
    3612        48625 :       if (row >= range->m_start.m_line)
    3613              :         {
    3614        45359 :           if (range->m_finish.m_line == row)
    3615              :             {
    3616              :               /* On the final line within a range; ensure that
    3617              :                  we render up to the end of the range.  */
    3618        42476 :               const int disp_col = range->m_finish.m_columns[CU_DISPLAY_COLS];
    3619        42476 :               if (result <= disp_col)
    3620        14730 :                 result = disp_col + 1;
    3621              :             }
    3622         2883 :           else if (row < range->m_finish.m_line)
    3623              :             {
    3624              :               /* Within a multiline range; ensure that we render up to the
    3625              :                  last non-whitespace column.  */
    3626          227 :               if (result <= last_non_ws_column)
    3627          153 :                 result = last_non_ws_column + 1;
    3628              :             }
    3629              :         }
    3630              :     }
    3631              : 
    3632        38432 :   return result;
    3633              : }
    3634              : 
    3635              : /* Given *COLUMN as an x-coordinate, print spaces to position
    3636              :    successive output at DEST_COLUMN, printing a newline if necessary,
    3637              :    and updating *COLUMN.  If ADD_LEFT_MARGIN, then print the (empty)
    3638              :    left margin after any newline.  */
    3639              : 
    3640              : template<typename TextOrHtml>
    3641              : void
    3642        78172 : layout_printer<TextOrHtml>::move_to_column (int *column,
    3643              :                                       int dest_column,
    3644              :                                       bool add_left_margin)
    3645              : {
    3646              :   /* Start a new line if we need to.  */
    3647        78172 :   if (*column > dest_column)
    3648              :     {
    3649         7499 :       end_line ();
    3650         7499 :       if (add_left_margin)
    3651         1402 :         start_annotation_line (margin_kind::normal);
    3652         7499 :       *column = 1 + m_layout.m_x_offset_display;
    3653              :     }
    3654              : 
    3655      4343682 :   while (*column < dest_column)
    3656              :     {
    3657              :       /* For debugging column issues, it can be helpful to replace this
    3658              :          add_space call with
    3659              :            m_text_or_html.add_character ('0' + (*column % 10));
    3660              :          to visualize the changing value of "*column".  */
    3661      4265510 :       m_text_or_html.add_space ();
    3662      4265510 :       (*column)++;
    3663              :     }
    3664        78172 : }
    3665              : 
    3666              : /* For debugging layout issues, render a ruler giving column numbers
    3667              :    (after the 1-column indent).  */
    3668              : 
    3669              : template<typename TextOrHtml>
    3670              : void
    3671          325 : layout_printer<TextOrHtml>::show_ruler (int max_column)
    3672              : {
    3673            1 :   m_text_or_html.push_html_tag_with_class("thead", "ruler", false);
    3674              : 
    3675              :   /* Hundreds.  */
    3676          325 :   if (max_column > 99)
    3677              :     {
    3678          197 :       start_annotation_line (margin_kind::ruler);
    3679         9576 :       for (int column = 1 + m_layout.m_x_offset_display;
    3680         9576 :            column <= max_column;
    3681              :            ++column)
    3682         9379 :         if (column % 10 == 0)
    3683          867 :           m_text_or_html.add_character ('0' + (column / 100) % 10);
    3684              :         else
    3685         8512 :           m_text_or_html.add_space ();
    3686          197 :       end_line ();
    3687              :     }
    3688              : 
    3689              :   /* Tens.  */
    3690          325 :   start_annotation_line (margin_kind::ruler);
    3691        16104 :   for (int column = 1 + m_layout.m_x_offset_display;
    3692        16104 :        column <= max_column;
    3693              :        ++column)
    3694        15779 :     if (column % 10 == 0)
    3695         1507 :       m_text_or_html.add_character ('0' + (column / 10) % 10);
    3696              :     else
    3697        14272 :       m_text_or_html.add_space ();
    3698          325 :   end_line ();
    3699              : 
    3700              :   /* Units.  */
    3701          325 :   start_annotation_line (margin_kind::ruler);
    3702          396 :   for (int column = 1 + m_layout.m_x_offset_display;
    3703        16104 :        column <= max_column;
    3704              :        ++column)
    3705        15779 :     m_text_or_html.add_character ('0' + (column % 10));
    3706          325 :   end_line ();
    3707              : 
    3708            1 :   m_text_or_html.pop_html_tag("thead"); // thead
    3709          325 : }
    3710              : 
    3711              : /* Print leading fix-its (for new lines inserted before the source line)
    3712              :    then the source line, followed by an annotation line
    3713              :    consisting of any caret/underlines, then any fixits.
    3714              :    If the source line can't be read, print nothing.  */
    3715              : template<typename TextOrHtml>
    3716              : void
    3717        48436 : layout_printer<TextOrHtml>::print_line (linenum_type row)
    3718              : {
    3719        48436 :   typename TextOrHtml::auto_check_tag_nesting sentinel (m_text_or_html);
    3720              : 
    3721        48436 :   diagnostics::char_span line
    3722        48436 :     = m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row);
    3723        48436 :   if (!line)
    3724         2895 :     return;
    3725              : 
    3726        45541 :   print_any_right_to_left_edge_lines ();
    3727        45541 :   print_leading_fixits (row);
    3728        45541 :   const line_bounds lbounds
    3729        45541 :     = print_source_line (row, line.get_buffer (), line.length ());
    3730        45541 :   if (m_layout.should_print_annotation_line_p (row))
    3731        38432 :     print_annotation_line (row, lbounds);
    3732        45541 :   if (get_options ().show_labels_p)
    3733        44836 :     print_any_labels (row);
    3734        45541 :   print_trailing_fixits (row);
    3735          297 : }
    3736              : 
    3737              : /* If there's a link column in the RHS, print something like this:
    3738              :    "                                           │\n"
    3739              :    "┌──────────────────────────────────────────┘\n"
    3740              :    showing the link entering at the top right and emerging
    3741              :    at the bottom left.  */
    3742              : 
    3743              : template<typename TextOrHtml>
    3744              : void
    3745        45541 : layout_printer<TextOrHtml>::print_any_right_to_left_edge_lines ()
    3746              : {
    3747        45541 :   if (m_link_rhs_column == -1)
    3748              :     /* Can also happen if the out-edge had UNKNOWN_LOCATION.  */
    3749        44287 :     return;
    3750              : 
    3751         1254 :   gcc_assert (get_options ().show_event_links_p);
    3752              : 
    3753              :   /* Print the line with "|".  */
    3754         1254 :   start_annotation_line (margin_kind::normal);
    3755              : 
    3756         1254 :   int column = 1 + m_layout.m_x_offset_display;
    3757         1254 :   move_to_column (&column, m_link_rhs_column, true);
    3758         1254 :   m_text_or_html.colorize_text_for_cfg_edge ();
    3759         1254 :   const cppchar_t down= get_theme ().get_cppchar
    3760         1254 :     (text_art::theme::cell_kind::CFG_DOWN);
    3761         1254 :   m_text_or_html.add_character (down);
    3762         1254 :   end_line ();
    3763              : 
    3764              :   /* Print the line with "┌──────────────────────────────────────────┘".  */
    3765         1254 :   m_link_lhs_state = link_lhs_state::rewinding_to_lhs;
    3766         1254 :   start_annotation_line (margin_kind::normal);
    3767         1254 :   m_text_or_html.colorize_text_for_cfg_edge ();
    3768         1254 :   const cppchar_t left= get_theme ().get_cppchar
    3769         1254 :     (text_art::theme::cell_kind::CFG_LEFT);
    3770         1254 :   for (int column = 1 + m_layout.m_x_offset_display;
    3771        53854 :        column < m_link_rhs_column;
    3772              :        ++column)
    3773        52600 :     m_text_or_html.add_character (left);
    3774         1254 :   const cppchar_t from_down_to_left = get_theme ().get_cppchar
    3775         1254 :     (text_art::theme::cell_kind::CFG_FROM_DOWN_TO_LEFT);
    3776         1254 :   m_text_or_html.add_character (from_down_to_left);
    3777         1254 :   end_line ();
    3778              : 
    3779              :   /* We now have a link line on the LHS,
    3780              :      and no longer have one on the RHS.  */
    3781         1254 :   m_link_lhs_state = link_lhs_state::at_lhs;
    3782         1254 :   m_link_rhs_column = -1;
    3783              : }
    3784              : 
    3785              : template<typename TextOrHtml>
    3786        45319 : layout_printer<TextOrHtml>::layout_printer (TextOrHtml &text_or_html,
    3787              :                                       const layout &layout,
    3788              :                                       bool is_diagnostic_path)
    3789        45319 : : m_text_or_html (text_or_html),
    3790        45319 :   m_layout (layout),
    3791        45319 :   m_is_diagnostic_path (is_diagnostic_path),
    3792        45319 :   m_was_in_range_p (false),
    3793        45319 :   m_last_range_idx (0),
    3794        45319 :   m_link_lhs_state (link_lhs_state::none),
    3795        45319 :   m_link_rhs_column (-1)
    3796              : {
    3797        45319 :   if (get_options ().show_event_links_p)
    3798        23865 :     if (auto effect_info = m_layout.m_effect_info)
    3799         1663 :       if (effect_info->m_leading_in_edge_column)
    3800         1663 :         m_link_rhs_column = effect_info->m_leading_in_edge_column;
    3801        45319 : }
    3802              : 
    3803              : } /* End of anonymous namespace.  */
    3804              : 
    3805              : /* If LOC is within the spans of lines that will already be printed for
    3806              :    this gcc_rich_location, then add it as a secondary location and return true.
    3807              : 
    3808              :    Otherwise return false.
    3809              : 
    3810              :    Use POLICY for determining how spans of lines would be printed.  */
    3811              : 
    3812              : bool
    3813         3944 : gcc_rich_location::
    3814              : add_location_if_nearby (const diagnostics::source_print_policy &policy,
    3815              :                         location_t loc,
    3816              :                         bool restrict_to_current_line_spans,
    3817              :                         const range_label *label)
    3818              : {
    3819              :   /* Use the layout location-handling logic to sanitize LOC,
    3820              :      filtering it to the current line spans within a temporary
    3821              :      layout instance.  */
    3822              : 
    3823         3944 :   layout layout (policy, *this);
    3824         3944 :   location_range loc_range;
    3825         3944 :   loc_range.m_loc = loc;
    3826         3944 :   loc_range.m_range_display_kind = SHOW_RANGE_WITHOUT_CARET;
    3827         3944 :   loc_range.m_label = nullptr;
    3828         3944 :   if (!layout.maybe_add_location_range (&loc_range, 0,
    3829              :                                         restrict_to_current_line_spans))
    3830              :     return false;
    3831              : 
    3832         3742 :   add_range (loc, SHOW_RANGE_WITHOUT_CARET, label);
    3833         3742 :   return true;
    3834         3944 : }
    3835              : 
    3836              : bool
    3837         1643 : gcc_rich_location::
    3838              : add_location_if_nearby (const diagnostics::context &dc,
    3839              :                         location_t loc,
    3840              :                         bool restrict_to_current_line_spans,
    3841              :                         const range_label *label)
    3842              : {
    3843         1643 :   diagnostics::source_print_policy
    3844         1643 :     source_policy (dc,
    3845         1643 :                    dc.get_source_printing_options ());
    3846         1643 :   return add_location_if_nearby (source_policy, loc,
    3847         1643 :                                  restrict_to_current_line_spans, label);
    3848              : }
    3849              : 
    3850              : namespace diagnostics {
    3851              : 
    3852              : /* As per diagnostics::source_print_policy::print, but don't print anything
    3853              :    if source printing is disabled, or if the location hasn't changed.  */
    3854              : 
    3855              : void
    3856       417673 : context::maybe_show_locus (const rich_location &richloc,
    3857              :                            const source_printing_options &opts,
    3858              :                            enum kind diagnostic_kind,
    3859              :                            pretty_printer &pp,
    3860              :                            source_effect_info *effects)
    3861              : {
    3862       417673 :   const location_t loc = richloc.get_loc ();
    3863              :   /* Do nothing if source-printing has been disabled.  */
    3864       417673 :   if (!opts.enabled)
    3865       379808 :     return;
    3866              : 
    3867              :   /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins.  */
    3868        39715 :   if (loc <= BUILTINS_LOCATION)
    3869              :     return;
    3870              : 
    3871              :   /* Don't print the same source location twice in a row, unless we have
    3872              :      fix-it hints, or multiple locations, or a label.  */
    3873        39344 :   if (loc == m_last_location
    3874         1579 :       && richloc.get_num_fixit_hints () == 0
    3875         1518 :       && richloc.get_num_locations () == 1
    3876        40825 :       && richloc.get_range (0)->m_label == nullptr)
    3877              :     return;
    3878              : 
    3879        37865 :   m_last_location = loc;
    3880              : 
    3881        37865 :   source_print_policy source_policy (*this, opts);
    3882        37865 :   source_policy.print (pp, richloc, diagnostic_kind, effects);
    3883              : }
    3884              : 
    3885              : /* As above, but print in HTML form to XP.
    3886              :    If non-null, use LABEL_WRITER when writing labelled ranges.  */
    3887              : 
    3888              : void
    3889          304 : context::maybe_show_locus_as_html (const rich_location &richloc,
    3890              :                                    const source_printing_options &opts,
    3891              :                                    enum kind diagnostic_kind,
    3892              :                                    xml::printer &xp,
    3893              :                                    source_effect_info *effects,
    3894              :                                    html_label_writer *label_writer)
    3895              : {
    3896          304 :   const location_t loc = richloc.get_loc ();
    3897              :   /* Do nothing if source-printing has been disabled.  */
    3898          304 :   if (!opts.enabled)
    3899           25 :     return;
    3900              : 
    3901              :   /* Don't attempt to print source for UNKNOWN_LOCATION and for builtins.  */
    3902          284 :   if (loc <= BUILTINS_LOCATION)
    3903              :     return;
    3904              : 
    3905              :   /* Don't print the same source location twice in a row, unless we have
    3906              :      fix-it hints, or multiple locations, or a label.  */
    3907          280 :   if (loc == m_last_location
    3908            2 :       && richloc.get_num_fixit_hints () == 0
    3909            2 :       && richloc.get_num_locations () == 1
    3910          282 :       && richloc.get_range (0)->m_label == nullptr)
    3911              :     return;
    3912              : 
    3913          279 :   m_last_location = loc;
    3914              : 
    3915          279 :   source_print_policy source_policy (*this, opts);
    3916          279 :   source_policy.print_as_html (xp, richloc, diagnostic_kind, effects,
    3917              :                                label_writer);
    3918              : }
    3919              : 
    3920        11749 : source_print_policy::
    3921              : source_print_policy (const context &dc)
    3922        11749 : : m_options (dc.m_source_printing),
    3923        11749 :   m_location_policy (dc),
    3924        11749 :   m_text_start_span_cb (dc.m_text_callbacks.m_text_start_span),
    3925        11749 :   m_html_start_span_cb (dc.m_text_callbacks.m_html_start_span),
    3926        11749 :   m_file_cache (dc.get_file_cache ()),
    3927        11749 :   m_diagram_theme (dc.get_diagram_theme ()),
    3928        11749 :   m_escape_format (dc.get_escape_format ())
    3929              : {
    3930        11749 : }
    3931              : 
    3932        39787 : source_print_policy::source_print_policy (const context &dc,
    3933              :                                           const source_printing_options &opts)
    3934        39787 : : m_options (opts),
    3935        39787 :   m_location_policy (dc),
    3936        39787 :   m_text_start_span_cb (dc.m_text_callbacks.m_text_start_span),
    3937        39787 :   m_html_start_span_cb (dc.m_text_callbacks.m_html_start_span),
    3938        39787 :   m_file_cache (dc.get_file_cache ()),
    3939        39787 :   m_diagram_theme (dc.get_diagram_theme ()),
    3940        39787 :   m_escape_format (dc.get_escape_format ())
    3941              : {
    3942        39787 : }
    3943              : 
    3944              : /* Print to PP the physical source code corresponding to the location(s)
    3945              :    in RICHLOC, with additional annotations, as if for a diagnostic of the
    3946              :    given DIAGNOSTIC_KIND.
    3947              :    If EFFECTS is non-null, then use and update it.  */
    3948              : 
    3949              : void
    3950        43632 : source_print_policy::print (pretty_printer &pp,
    3951              :                             const rich_location &richloc,
    3952              :                             enum kind diagnostic_kind,
    3953              :                             source_effect_info *effects) const
    3954              : {
    3955        43632 :   layout layout (*this, richloc, effects);
    3956        43632 :   colorizer col (pp, richloc, diagnostic_kind);
    3957        43632 :   to_text text_or_html (pp, col);
    3958        43632 :   layout_printer<to_text> lp (text_or_html, layout,
    3959        43632 :                               diagnostic_kind == diagnostics::kind::path);
    3960        43632 :   lp.print (*this);
    3961        43632 : }
    3962              : 
    3963              : /* As above, but print in HTML form to XP.
    3964              :    If non-null, use LABEL_WRITER when writing labelled ranges.  */
    3965              : 
    3966              : void
    3967          279 : source_print_policy::print_as_html (xml::printer &xp,
    3968              :                                     const rich_location &richloc,
    3969              :                                     enum kind diagnostic_kind,
    3970              :                                     source_effect_info *effects,
    3971              :                                     html_label_writer *label_writer) const
    3972              : {
    3973          279 :   layout layout (*this, richloc, effects);
    3974          279 :   to_html text_or_html (xp, &richloc, label_writer);
    3975          279 :   layout_printer<to_html> lp (text_or_html, layout,
    3976          279 :                               diagnostic_kind == diagnostics::kind::path);
    3977          279 :   xml::auto_check_tag_nesting sentinel (xp);
    3978          279 :   lp.print (*this);
    3979          279 : }
    3980              : 
    3981              : } // namespace diagnostics
    3982              : 
    3983              : template <typename TextOrHtml>
    3984              : void
    3985        45319 : layout_printer<TextOrHtml>::
    3986              : print (const diagnostics::source_print_policy &source_policy)
    3987              : {
    3988        45319 :   typename TextOrHtml::auto_check_tag_nesting sentinel (m_text_or_html);
    3989              : 
    3990        45319 :   m_text_or_html.push_html_tag_with_class ("table", "locus", false);
    3991              : 
    3992        45319 :   if (get_options ().show_ruler_p)
    3993          325 :     show_ruler (m_layout.m_x_offset_display + get_options ().max_width);
    3994              : 
    3995       182882 :   for (int line_span_idx = 0; line_span_idx < m_layout.get_num_line_spans ();
    3996              :        line_span_idx++)
    3997              :     {
    3998        46122 :       const line_span *line_span = m_layout.get_line_span (line_span_idx);
    3999        46122 :       if (get_options ().show_line_numbers_p)
    4000              :         {
    4001              :           /* With line numbers, we should show whenever the line-numbering
    4002              :              "jumps".  */
    4003        26778 :           if (line_span_idx > 0)
    4004          459 :             print_gap_in_line_numbering ();
    4005              :         }
    4006              :       else
    4007              :         {
    4008              :           /* Without line numbers, we print headings for some line spans.  */
    4009        19344 :           if (m_layout.print_heading_for_line_span_index_p (line_span_idx))
    4010              :             {
    4011          463 :               expanded_location exploc
    4012          463 :                 = m_layout.get_expanded_location (line_span);
    4013              :               const diagnostics::location_print_policy &
    4014          463 :                 loc_policy = source_policy.get_location_policy ();
    4015          463 :               m_text_or_html.invoke_start_span_fn (source_policy, loc_policy, exploc);
    4016              :             }
    4017              :         }
    4018              : 
    4019        46122 :       m_text_or_html.push_html_tag_with_class ("tbody", "line-span", false);
    4020              : 
    4021              :       /* Iterate over the lines within this span (using linenum_arith_t to
    4022              :          avoid overflow with 0xffffffff causing an infinite loop).  */
    4023        46122 :       linenum_arith_t last_line = line_span->get_last_line ();
    4024        94558 :       for (linenum_arith_t row = line_span->get_first_line ();
    4025        94558 :            row <= last_line; row++)
    4026        48436 :         print_line (row);
    4027              : 
    4028          279 :       m_text_or_html.pop_html_tag ("tbody");
    4029              :     }
    4030              : 
    4031        45319 :   if (auto effect_info = m_layout.m_effect_info)
    4032         2369 :     effect_info->m_trailing_out_edge_column = m_link_rhs_column;
    4033              : 
    4034          279 :   m_text_or_html.pop_html_tag ("table");
    4035        45319 : }
    4036              : 
    4037              : #if CHECKING_P
    4038              : 
    4039              : namespace diagnostics {
    4040              : namespace selftest {
    4041              : 
    4042              : using test_context = diagnostics::selftest::test_context;
    4043              : 
    4044              : using line_table_case = ::selftest::line_table_case;
    4045              : using line_table_test = ::selftest::line_table_test;
    4046              : using temp_source_file = ::selftest::temp_source_file;
    4047              : 
    4048              : static std::unique_ptr<xml::node>
    4049          192 : make_element_for_locus (const rich_location &rich_loc,
    4050              :                         enum kind kind,
    4051              :                         diagnostics::context &dc)
    4052              : {
    4053          192 :   dc.m_last_location = UNKNOWN_LOCATION;
    4054              : 
    4055          192 :   xml::element wrapper ("wrapper", false);
    4056          192 :   xml::printer xp (wrapper);
    4057          192 :   dc.maybe_show_locus_as_html (rich_loc,
    4058          192 :                                dc.get_source_printing_options (),
    4059              :                                kind,
    4060              :                                xp,
    4061              :                                nullptr,
    4062              :                                nullptr); // label_writer
    4063          192 :   if (wrapper.m_children.size () > 0)
    4064          192 :     return std::move (wrapper.m_children[0]);
    4065              :   else
    4066            0 :     return nullptr;
    4067          192 : }
    4068              : 
    4069              : static label_text
    4070          192 : make_raw_html_for_locus (const rich_location &rich_loc,
    4071              :                          enum kind kind,
    4072              :                          diagnostics::context &dc)
    4073              : {
    4074          192 :   auto node = make_element_for_locus (rich_loc, kind, dc);
    4075          192 :   pretty_printer pp;
    4076          192 :   if (node)
    4077          192 :     node->write_as_xml (&pp, 0, true);
    4078          192 :   return label_text::take (xstrdup (pp_formatted_text (&pp)));
    4079          192 : }
    4080              : 
    4081              : /* Selftests for diagnostic_show_locus.  */
    4082              : 
    4083          672 : source_printing_fixture::
    4084              : source_printing_fixture (const line_table_case &case_,
    4085              :                          const char *content)
    4086          672 : : m_content (content),
    4087          672 :   m_tmp_source_file (SELFTEST_LOCATION, ".c", content),
    4088          672 :   m_ltt (case_),
    4089          672 :   m_fc ()
    4090              : {
    4091          672 :   linemap_add (line_table, LC_ENTER, false,
    4092              :                m_tmp_source_file.get_filename (), 1);
    4093          672 : }
    4094              : 
    4095              : /* Populate a char_display_policy based on DC and RICHLOC.  */
    4096              : 
    4097              : static char_display_policy
    4098          188 : make_char_policy (const diagnostics::context &dc,
    4099              :                   const rich_location &richloc)
    4100              : {
    4101          188 :   diagnostics::source_print_policy source_policy (dc);
    4102          188 :   return ::make_char_policy (source_policy, richloc);
    4103              : }
    4104              : 
    4105              : /* Verify that cpp_display_width correctly handles escaping.  */
    4106              : 
    4107              : static void
    4108            4 : test_display_widths ()
    4109              : {
    4110            4 :   gcc_rich_location richloc (UNKNOWN_LOCATION);
    4111              : 
    4112              :   /* U+03C0 "GREEK SMALL LETTER PI".  */
    4113            4 :   const char *pi = "\xCF\x80";
    4114              :   /* U+1F642 "SLIGHTLY SMILING FACE".  */
    4115            4 :   const char *emoji = "\xF0\x9F\x99\x82";
    4116              :   /* Stray trailing byte of a UTF-8 character.  */
    4117            4 :   const char *stray = "\xBF";
    4118              :   /* U+10FFFF.  */
    4119            4 :   const char *max_codepoint = "\xF4\x8F\xBF\xBF";
    4120              : 
    4121              :   /* No escaping.  */
    4122            4 :   {
    4123            4 :     test_context dc;
    4124            4 :     char_display_policy policy (make_char_policy (dc, richloc));
    4125            4 :     ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 1);
    4126            4 :     ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 2);
    4127            4 :     ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 1);
    4128              :     /* Don't check width of U+10FFFF; it's in a private use plane.  */
    4129            4 :   }
    4130              : 
    4131            4 :   richloc.set_escape_on_output (true);
    4132              : 
    4133            4 :   {
    4134            4 :     test_context dc;
    4135            4 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
    4136            4 :     char_display_policy policy (make_char_policy (dc, richloc));
    4137            4 :     ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
    4138            4 :     ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 9);
    4139            4 :     ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
    4140            4 :     ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
    4141              :                                   policy),
    4142              :                strlen ("<U+10FFFF>"));
    4143            4 :   }
    4144              : 
    4145            4 :   {
    4146            4 :     test_context dc;
    4147            4 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
    4148            4 :     char_display_policy policy (make_char_policy (dc, richloc));
    4149            4 :     ASSERT_EQ (cpp_display_width (pi, strlen (pi), policy), 8);
    4150            4 :     ASSERT_EQ (cpp_display_width (emoji, strlen (emoji), policy), 16);
    4151            4 :     ASSERT_EQ (cpp_display_width (stray, strlen (stray), policy), 4);
    4152            4 :     ASSERT_EQ (cpp_display_width (max_codepoint, strlen (max_codepoint),
    4153              :                                   policy),
    4154              :                16);
    4155            4 :   }
    4156            4 : }
    4157              : 
    4158              : /* For precise tests of the layout, make clear where the source line will
    4159              :    start.  test_left_margin sets the total byte count from the left side of the
    4160              :    screen to the start of source lines, after the line number and the separator,
    4161              :    which consists of the three characters " | ".  */
    4162              : static const int test_linenum_sep = 3;
    4163              : static const int test_left_margin = 7;
    4164              : 
    4165              : /* Helper function for test_layout_x_offset_display_utf8().  */
    4166              : static void
    4167         1472 : test_offset_impl (int caret_byte_col, int max_width,
    4168              :                   int expected_x_offset_display,
    4169              :                   int left_margin = test_left_margin)
    4170              : {
    4171         1472 :   test_context dc;
    4172         1472 :   auto &source_printing_opts = dc.get_source_printing_options ();
    4173         1472 :   source_printing_opts.max_width = max_width;
    4174              :   /* min_margin_width sets the minimum space reserved for
    4175              :      the line number plus one space after.  */
    4176         1472 :   source_printing_opts.min_margin_width = left_margin - test_linenum_sep + 1;
    4177         1472 :   dc.show_line_numbers (true);
    4178         1472 :   diagnostics::source_print_policy source_policy (dc);
    4179         1472 :   rich_location richloc (line_table,
    4180              :                          linemap_position_for_column (line_table,
    4181         1472 :                                                       caret_byte_col));
    4182         1472 :   layout test_layout (source_policy, richloc, nullptr);
    4183         1472 :   ASSERT_EQ (left_margin - test_linenum_sep,
    4184              :              test_layout.get_linenum_width ());
    4185         1472 :   ASSERT_EQ (expected_x_offset_display,
    4186              :              test_layout.get_x_offset_display ());
    4187         1472 : }
    4188              : 
    4189              : /* Test that layout::calculate_x_offset_display() works.  */
    4190              : static void
    4191           96 : test_layout_x_offset_display_utf8 (const line_table_case &case_)
    4192              : {
    4193              : 
    4194           96 :   const char *content
    4195              :     = "This line is very long, so that we can use it to test the logic for "
    4196              :       "clipping long lines.  Also this: \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a "
    4197              :       "pair of emojis that occupies 8 bytes and 4 display columns, starting at "
    4198              :       "column #102.\n";
    4199              : 
    4200              :   /* Number of bytes in the line, subtracting one to remove the newline.  */
    4201           96 :   const int line_bytes = strlen (content) - 1;
    4202              : 
    4203              :   /* Number of display columns occupied by the line; each of the 2 emojis
    4204              :      takes up 2 fewer display columns than it does bytes.  */
    4205           96 :   const int line_display_cols = line_bytes - 2*2;
    4206              : 
    4207              :   /* The column of the first emoji.  Byte or display is the same as there are
    4208              :      no multibyte characters earlier on the line.  */
    4209           96 :   const int emoji_col = 102;
    4210              : 
    4211           96 :   source_printing_fixture f (case_, content);
    4212              : 
    4213           96 :   linemap_add (line_table, LC_ENTER, false, f.get_filename (), 1);
    4214              : 
    4215           96 :   location_t line_end = linemap_position_for_column (line_table, line_bytes);
    4216              : 
    4217              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    4218           96 :   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    4219           32 :     return;
    4220              : 
    4221           64 :   ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
    4222           64 :   ASSERT_EQ (1, LOCATION_LINE (line_end));
    4223           64 :   ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end));
    4224              : 
    4225           64 :   diagnostics::char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
    4226           64 :   ASSERT_EQ (line_display_cols,
    4227              :              cpp_display_width (lspan.get_buffer (), lspan.length (),
    4228              :                                 def_policy ()));
    4229           64 :   ASSERT_EQ (line_display_cols,
    4230              :              location_compute_display_column (f.m_fc,
    4231              :                                               expand_location (line_end),
    4232              :                                               def_policy ()));
    4233           64 :   ASSERT_EQ (0, memcmp (lspan.get_buffer () + (emoji_col - 1),
    4234              :                         "\xf0\x9f\x98\x82\xf0\x9f\x98\x82", 8));
    4235              : 
    4236              :   /* (caret_byte, max_width, expected_x_offset_display, [left_margin])  */
    4237              : 
    4238              :   /* No constraint on the width -> no offset.  */
    4239           64 :   test_offset_impl (emoji_col, 0, 0);
    4240              : 
    4241              :   /* Caret is before the beginning -> no offset.  */
    4242           64 :   test_offset_impl (0, 100, 0);
    4243              : 
    4244              :   /* Caret is past the end of the line -> no offset.  */
    4245           64 :   test_offset_impl (line_bytes+1, 100, 0);
    4246              : 
    4247              :   /* Line fits in the display -> no offset.  */
    4248           64 :   test_offset_impl (line_bytes, line_display_cols + test_left_margin, 0);
    4249           64 :   test_offset_impl (emoji_col, line_display_cols + test_left_margin, 0);
    4250              : 
    4251              :   /* Line is too long for the display but caret location is OK
    4252              :      anyway -> no offset.  */
    4253           64 :   static const int small_width = 24;
    4254           64 :   test_offset_impl (1, small_width, 0);
    4255              : 
    4256              :   /* Width constraint is very small -> no offset.  */
    4257           64 :   test_offset_impl (emoji_col, CARET_LINE_MARGIN, 0);
    4258              : 
    4259              :   /* Line would be offset, but due to large line numbers, offsetting
    4260              :      would remove the whole line -> no offset.  */
    4261           64 :   static const int huge_left_margin = 100;
    4262           64 :   test_offset_impl (emoji_col, huge_left_margin, 0, huge_left_margin);
    4263              : 
    4264              :   /* Line is the same length as the display, but the line number makes it too
    4265              :      long, so offset is required.  Caret is at the end so padding on the right
    4266              :      is not in effect.  */
    4267          320 :   for (int excess = 1; excess <= 3; ++excess)
    4268          192 :     test_offset_impl (line_bytes, line_display_cols + test_left_margin - excess,
    4269              :                       excess);
    4270              : 
    4271              :   /* Line is much too long for the display, caret is near the end ->
    4272              :      offset should be such that the line fits in the display and caret
    4273              :      remains the same distance from the end that it was.  */
    4274          704 :   for (int caret_offset = 0, max_offset = MIN (CARET_LINE_MARGIN, 10);
    4275          768 :        caret_offset <= max_offset; ++caret_offset)
    4276          704 :     test_offset_impl (line_bytes - caret_offset, small_width,
    4277              :                       line_display_cols + test_left_margin - small_width);
    4278              : 
    4279              :   /* As previous case but caret is closer to the middle; now we want it to end
    4280              :      up CARET_LINE_MARGIN bytes from the end.  */
    4281           64 :   ASSERT_GT (line_display_cols - emoji_col, CARET_LINE_MARGIN);
    4282           64 :   test_offset_impl (emoji_col, small_width,
    4283              :                     emoji_col + test_left_margin
    4284              :                     - (small_width - CARET_LINE_MARGIN));
    4285              : 
    4286              :   /* Test that the source line is offset as expected when printed.  */
    4287           64 :   {
    4288           64 :     test_context dc;
    4289           64 :     auto &source_printing_opts = dc.get_source_printing_options ();
    4290           64 :     source_printing_opts.max_width = small_width - 6;
    4291           64 :     source_printing_opts.min_margin_width
    4292           64 :       = test_left_margin - test_linenum_sep + 1;
    4293           64 :     dc.show_line_numbers (true);
    4294           64 :     dc.show_ruler (true);
    4295           64 :     diagnostics::source_print_policy policy (dc);
    4296           64 :     rich_location richloc (line_table,
    4297              :                            linemap_position_for_column (line_table,
    4298           64 :                                                         emoji_col));
    4299           64 :     layout test_layout (policy, richloc, nullptr);
    4300           64 :     colorizer col (*dc.get_reference_printer (),
    4301           64 :                    richloc, diagnostics::kind::error);
    4302           64 :     diagnostics::to_text text_or_html (*dc.get_reference_printer (), col);
    4303           64 :     layout_printer<diagnostics::to_text> lp (text_or_html, test_layout, false);
    4304           64 :     lp.print (policy);
    4305           64 :     ASSERT_STREQ ("     |         1         \n"
    4306              :                   "     |         1         \n"
    4307              :                   "     | 234567890123456789\n"
    4308              :                   "   1 | \xf0\x9f\x98\x82\xf0\x9f\x98\x82 is a pair of emojis "
    4309              :                   "that occupies 8 bytes and 4 display columns, starting at "
    4310              :                   "column #102.\n"
    4311              :                   "     | ^\n",
    4312              :                   pp_formatted_text (dc.get_reference_printer ()));
    4313           64 :   }
    4314              : 
    4315              :   /* Similar to the previous example, but now the offset called for would split
    4316              :      the first emoji in the middle of the UTF-8 sequence.  Check that we replace
    4317              :      it with a padding space in this case.  */
    4318           64 :   {
    4319           64 :     test_context dc;
    4320           64 :     auto &source_printing_opts = dc.get_source_printing_options ();
    4321           64 :     source_printing_opts.max_width = small_width - 5;
    4322           64 :     source_printing_opts.min_margin_width
    4323           64 :       = test_left_margin - test_linenum_sep + 1;
    4324           64 :     dc.show_line_numbers (true);
    4325           64 :     dc.show_ruler (true);
    4326           64 :     diagnostics::source_print_policy policy (dc);
    4327           64 :     rich_location richloc (line_table,
    4328              :                            linemap_position_for_column (line_table,
    4329           64 :                                                         emoji_col + 2));
    4330           64 :     layout test_layout (dc, richloc, nullptr);
    4331           64 :     colorizer col (*dc.get_reference_printer (),
    4332           64 :                    richloc, diagnostics::kind::error);
    4333           64 :     diagnostics::to_text text_or_html (*dc.get_reference_printer (), col);
    4334           64 :     layout_printer<diagnostics::to_text> lp (text_or_html, test_layout, false);
    4335           64 :     lp.print (policy);
    4336           64 :     ASSERT_STREQ ("     |        1         1 \n"
    4337              :                   "     |        1         2 \n"
    4338              :                   "     | 3456789012345678901\n"
    4339              :                   "   1 |  \xf0\x9f\x98\x82 is a pair of emojis "
    4340              :                   "that occupies 8 bytes and 4 display columns, starting at "
    4341              :                   "column #102.\n"
    4342              :                   "     |  ^\n",
    4343              :                   pp_formatted_text (dc.get_reference_printer ()));
    4344           64 :   }
    4345              : 
    4346           96 : }
    4347              : 
    4348              : static void
    4349           96 : test_layout_x_offset_display_tab (const line_table_case &case_)
    4350              : {
    4351           96 :   const char *content
    4352              :     = "This line is very long, so that we can use it to test the logic for "
    4353              :       "clipping long lines.  Also this: `\t' is a tab that occupies 1 byte and "
    4354              :       "a variable number of display columns, starting at column #103.\n";
    4355              : 
    4356              :   /* Number of bytes in the line, subtracting one to remove the newline.  */
    4357           96 :   const int line_bytes = strlen (content) - 1;
    4358              : 
    4359              :  /* The column where the tab begins.  Byte or display is the same as there are
    4360              :     no multibyte characters earlier on the line.  */
    4361           96 :   const int tab_col = 103;
    4362              : 
    4363              :   /* Effective extra size of the tab beyond what a single space would have taken
    4364              :      up, indexed by tabstop.  */
    4365           96 :   static const int num_tabstops = 11;
    4366           96 :   int extra_width[num_tabstops];
    4367         1056 :   for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
    4368              :     {
    4369          960 :       const int this_tab_size = tabstop - (tab_col - 1) % tabstop;
    4370          960 :       extra_width[tabstop] = this_tab_size - 1;
    4371              :     }
    4372              :   /* Example of this calculation: if tabstop is 10, the tab starting at column
    4373              :      #103 has to expand into 8 spaces, covering columns 103-110, so that the
    4374              :      next character is at column #111.  So it takes up 7 more columns than
    4375              :      a space would have taken up.  */
    4376           96 :   ASSERT_EQ (7, extra_width[10]);
    4377              : 
    4378           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
    4379           96 :   diagnostics::file_cache fc;
    4380           96 :   line_table_test ltt (case_);
    4381              : 
    4382           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
    4383              : 
    4384           96 :   location_t line_end = linemap_position_for_column (line_table, line_bytes);
    4385              : 
    4386              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    4387           96 :   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    4388           32 :     return;
    4389              : 
    4390              :   /* Check that cpp_display_width handles the tabs as expected.  */
    4391           64 :   diagnostics::char_span lspan = fc.get_source_line (tmp.get_filename (), 1);
    4392           64 :   ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1)));
    4393          704 :   for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
    4394              :     {
    4395          640 :       cpp_char_column_policy policy (tabstop, cpp_wcwidth);
    4396          640 :       ASSERT_EQ (line_bytes + extra_width[tabstop],
    4397              :                  cpp_display_width (lspan.get_buffer (), lspan.length (),
    4398              :                                     policy));
    4399          640 :       ASSERT_EQ (line_bytes + extra_width[tabstop],
    4400              :                  location_compute_display_column (fc,
    4401              :                                                   expand_location (line_end),
    4402              :                                                   policy));
    4403              :     }
    4404              : 
    4405              :   /* Check that the tab is expanded to the expected number of spaces.  */
    4406           64 :   rich_location richloc (line_table,
    4407              :                          linemap_position_for_column (line_table,
    4408           64 :                                                       tab_col + 1));
    4409          768 :   for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
    4410              :     {
    4411          640 :       test_context dc;
    4412          640 :       dc.get_column_options ().m_tabstop = tabstop;
    4413          640 :       diagnostics::source_print_policy policy (dc);
    4414          640 :       layout test_layout (policy, richloc, nullptr);
    4415          640 :       colorizer col (*dc.get_reference_printer (),
    4416          640 :                      richloc, diagnostics::kind::error);
    4417          640 :       diagnostics::to_text text_or_html (*dc.get_reference_printer (), col);
    4418          640 :       layout_printer<diagnostics::to_text> lp
    4419          640 :         (text_or_html, test_layout, false);
    4420          640 :       lp.print (policy);
    4421          640 :       const char *out = pp_formatted_text (dc.get_reference_printer ());
    4422          640 :       ASSERT_EQ (nullptr, strchr (out, '\t'));
    4423          640 :       const char *left_quote = strchr (out, '`');
    4424          640 :       const char *right_quote = strchr (out, '\'');
    4425          640 :       ASSERT_NE (nullptr, left_quote);
    4426          640 :       ASSERT_NE (nullptr, right_quote);
    4427          640 :       ASSERT_EQ (right_quote - left_quote, extra_width[tabstop] + 2);
    4428          640 :     }
    4429              : 
    4430              :   /* Check that the line is offset properly and that the tab is broken up
    4431              :      into the expected number of spaces when it is the last character skipped
    4432              :      over.  */
    4433          704 :   for (int tabstop = 1; tabstop != num_tabstops; ++tabstop)
    4434              :     {
    4435          640 :       test_context dc;
    4436          640 :       dc.get_column_options ().m_tabstop = tabstop;
    4437          640 :       static const int small_width = 24;
    4438          640 :       auto &source_printing_opts = dc.get_source_printing_options ();
    4439          640 :       source_printing_opts.max_width = small_width - 4;
    4440          640 :       source_printing_opts.min_margin_width
    4441          640 :         = test_left_margin - test_linenum_sep + 1;
    4442          640 :       dc.show_line_numbers (true);
    4443          640 :       diagnostics::source_print_policy policy (dc);
    4444          640 :       layout test_layout (policy, richloc, nullptr);
    4445          640 :       colorizer col (*dc.get_reference_printer (),
    4446          640 :                      richloc, diagnostics::kind::error);
    4447          640 :       diagnostics::to_text text_or_html (*dc.get_reference_printer (), col);
    4448          640 :       layout_printer<diagnostics::to_text> lp
    4449          640 :         (text_or_html, test_layout, false);
    4450          640 :       lp.print (policy);
    4451              : 
    4452              :       /* We have arranged things so that two columns will be printed before
    4453              :          the caret.  If the tab results in more than one space, this should
    4454              :          produce two spaces in the output; otherwise, it will be a single space
    4455              :          preceded by the opening quote before the tab character.  */
    4456          640 :       const char *output1
    4457              :         = "   1 |   ' is a tab that occupies 1 byte and a variable number of "
    4458              :           "display columns, starting at column #103.\n"
    4459              :           "     |   ^\n";
    4460          640 :       const char *output2
    4461              :         = "   1 | ` ' is a tab that occupies 1 byte and a variable number of "
    4462              :           "display columns, starting at column #103.\n"
    4463              :           "     |   ^\n";
    4464          640 :       const char *expected_output = (extra_width[tabstop] ? output1 : output2);
    4465          640 :       ASSERT_STREQ (expected_output, pp_formatted_text (dc.get_reference_printer ()));
    4466          640 :     }
    4467           96 : }
    4468              : 
    4469              : 
    4470              : /* Verify that diagnostic_show_locus works sanely on UNKNOWN_LOCATION.  */
    4471              : 
    4472              : static void
    4473            4 : test_diagnostic_show_locus_unknown_location ()
    4474              : {
    4475            4 :   test_context dc;
    4476            4 :   rich_location richloc (line_table, UNKNOWN_LOCATION);
    4477            4 :   ASSERT_STREQ ("", dc.test_show_locus (richloc));
    4478            4 : }
    4479              : 
    4480              : /* Verify that diagnostic_show_locus works sanely for various
    4481              :    single-line cases.
    4482              : 
    4483              :    All of these work on the following 1-line source file:
    4484              :      .0000000001111111
    4485              :      .1234567890123456
    4486              :      "foo = bar.field;\n"
    4487              :    which is set up by test_diagnostic_show_locus_one_liner and calls
    4488              :    them.  */
    4489              : 
    4490              : /* Just a caret.  */
    4491              : 
    4492              : static void
    4493           64 : test_one_liner_simple_caret ()
    4494              : {
    4495           64 :   test_context dc;
    4496           64 :   location_t caret = linemap_position_for_column (line_table, 10);
    4497           64 :   rich_location richloc (line_table, caret);
    4498           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4499              :                 "          ^\n",
    4500              :                 dc.test_show_locus (richloc));
    4501           64 : }
    4502              : 
    4503              : /* No column information (column == 0).
    4504              :    No annotation line should be printed.  */
    4505              : 
    4506              : static void
    4507           64 : test_one_liner_no_column ()
    4508              : {
    4509           64 :   test_context dc;
    4510           64 :   location_t caret = linemap_position_for_column (line_table, 0);
    4511           64 :   rich_location richloc (line_table, caret);
    4512           64 :   ASSERT_STREQ (" foo = bar.field;\n",
    4513              :                 dc.test_show_locus (richloc));
    4514           64 : }
    4515              : 
    4516              : /* Caret and range.  */
    4517              : 
    4518              : static void
    4519           64 : test_one_liner_caret_and_range ()
    4520              : {
    4521           64 :   test_context dc;
    4522           64 :   location_t caret = linemap_position_for_column (line_table, 10);
    4523           64 :   location_t start = linemap_position_for_column (line_table, 7);
    4524           64 :   location_t finish = linemap_position_for_column (line_table, 15);
    4525           64 :   location_t loc = make_location (caret, start, finish);
    4526           64 :   rich_location richloc (line_table, loc);
    4527           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4528              :                 "       ~~~^~~~~~\n",
    4529              :                 dc.test_show_locus (richloc));
    4530              : 
    4531           64 :   {
    4532           64 :     test_context dc;
    4533           64 :     auto out = make_raw_html_for_locus (richloc, diagnostics::kind::error, dc);
    4534           64 :      ASSERT_STREQ
    4535              :        ("<table class=\"locus\">\n"
    4536              :         "  <tbody class=\"line-span\">\n"
    4537              :         "    <tr><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n"
    4538              :         "    <tr><td class=\"left-margin\"> </td><td class=\"annotation\">      ~~~^~~~~~</td></tr>\n"
    4539              :         "  </tbody>\n"
    4540              :         "</table>\n",
    4541              :         out.get ());
    4542           64 :   }
    4543           64 :   {
    4544           64 :     test_context dc;
    4545           64 :     dc.show_line_numbers (true);
    4546           64 :     auto out = make_raw_html_for_locus (richloc, diagnostics::kind::error, dc);
    4547           64 :      ASSERT_STREQ
    4548              :        ("<table class=\"locus\">\n"
    4549              :         "  <tbody class=\"line-span\">\n"
    4550              :         "    <tr><td class=\"linenum\">    1</td><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n"
    4551              :         "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">      ~~~^~~~~~</td></tr>\n"
    4552              :         "  </tbody>\n"
    4553              :         "</table>\n",
    4554              :         out.get ());
    4555           64 :   }
    4556           64 : }
    4557              : 
    4558              : /* Multiple ranges and carets.  */
    4559              : 
    4560              : static void
    4561           64 : test_one_liner_multiple_carets_and_ranges ()
    4562              : {
    4563           64 :   test_context dc;
    4564           64 :   location_t foo
    4565           64 :     = make_location (linemap_position_for_column (line_table, 2),
    4566              :                      linemap_position_for_column (line_table, 1),
    4567              :                      linemap_position_for_column (line_table, 3));
    4568           64 :   dc.set_caret_char (0, 'A');
    4569              : 
    4570           64 :   location_t bar
    4571           64 :     = make_location (linemap_position_for_column (line_table, 8),
    4572              :                      linemap_position_for_column (line_table, 7),
    4573              :                      linemap_position_for_column (line_table, 9));
    4574           64 :   dc.set_caret_char (1, 'B');
    4575              : 
    4576           64 :   location_t field
    4577           64 :     = make_location (linemap_position_for_column (line_table, 13),
    4578              :                      linemap_position_for_column (line_table, 11),
    4579              :                      linemap_position_for_column (line_table, 15));
    4580           64 :   dc.set_caret_char (2, 'C');
    4581              : 
    4582           64 :   rich_location richloc (line_table, foo);
    4583           64 :   richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
    4584           64 :   richloc.add_range (field, SHOW_RANGE_WITH_CARET);
    4585           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4586              :                 " ~A~   ~B~ ~~C~~\n",
    4587              :                 dc.test_show_locus (richloc));
    4588           64 : }
    4589              : 
    4590              : /* Insertion fix-it hint: adding an "&" to the front of "bar.field". */
    4591              : 
    4592              : static void
    4593           64 : test_one_liner_fixit_insert_before ()
    4594              : {
    4595           64 :   test_context dc;
    4596           64 :   location_t caret = linemap_position_for_column (line_table, 7);
    4597           64 :   rich_location richloc (line_table, caret);
    4598           64 :   richloc.add_fixit_insert_before ("&");
    4599           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4600              :                 "       ^\n"
    4601              :                 "       &\n",
    4602              :                 dc.test_show_locus (richloc));
    4603           64 : }
    4604              : 
    4605              : /* Insertion fix-it hint: adding a "[0]" after "foo". */
    4606              : 
    4607              : static void
    4608           64 : test_one_liner_fixit_insert_after ()
    4609              : {
    4610           64 :   test_context dc;
    4611           64 :   location_t start = linemap_position_for_column (line_table, 1);
    4612           64 :   location_t finish = linemap_position_for_column (line_table, 3);
    4613           64 :   location_t foo = make_location (start, start, finish);
    4614           64 :   rich_location richloc (line_table, foo);
    4615           64 :   richloc.add_fixit_insert_after ("[0]");
    4616           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4617              :                 " ^~~\n"
    4618              :                 "    [0]\n",
    4619              :                 dc.test_show_locus (richloc));
    4620           64 : }
    4621              : 
    4622              : /* Removal fix-it hint: removal of the ".field".
    4623              :    Also verify the interaction of pp_set_prefix with rulers and
    4624              :    fix-it hints.  */
    4625              : 
    4626              : static void
    4627           64 : test_one_liner_fixit_remove ()
    4628              : {
    4629           64 :   location_t start = linemap_position_for_column (line_table, 10);
    4630           64 :   location_t finish = linemap_position_for_column (line_table, 15);
    4631           64 :   location_t dot = make_location (start, start, finish);
    4632           64 :   rich_location richloc (line_table, dot);
    4633           64 :   richloc.add_fixit_remove ();
    4634              : 
    4635              :   /* Normal.  */
    4636           64 :   {
    4637           64 :     test_context dc;
    4638           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    4639              :                   "          ^~~~~~\n"
    4640              :                   "          ------\n",
    4641              :                   dc.test_show_locus (richloc));
    4642           64 :   }
    4643              : 
    4644              :   /* Test of adding a prefix.  */
    4645           64 :   {
    4646           64 :     test_context dc;
    4647           64 :     pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
    4648           64 :     pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
    4649           64 :     ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
    4650              :                   "TEST PREFIX:          ^~~~~~\n"
    4651              :                   "TEST PREFIX:          ------\n",
    4652              :                   dc.test_show_locus (richloc));
    4653           64 :   }
    4654              : 
    4655              :   /* Normal, with ruler.  */
    4656           64 :   {
    4657           64 :     test_context dc;
    4658           64 :     auto &source_printing_opts = dc.get_source_printing_options ();
    4659           64 :     dc.show_ruler (true);
    4660           64 :     source_printing_opts.max_width = 104;
    4661           64 :     ASSERT_STREQ ("          0         0         0         0         0         0         0         0         0         1    \n"
    4662              :                   "          1         2         3         4         5         6         7         8         9         0    \n"
    4663              :                   " 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\n"
    4664              :                   " foo = bar.field;\n"
    4665              :                   "          ^~~~~~\n"
    4666              :                   "          ------\n",
    4667              :                   dc.test_show_locus (richloc));
    4668           64 :   }
    4669              : 
    4670              :   /* Test of adding a prefix, with ruler.  */
    4671           64 :   {
    4672           64 :     test_context dc;
    4673           64 :     auto &source_printing_opts = dc.get_source_printing_options ();
    4674           64 :     dc.show_ruler (true);
    4675           64 :     source_printing_opts.max_width = 50;
    4676           64 :     pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
    4677           64 :     pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
    4678           64 :     ASSERT_STREQ ("TEST PREFIX:          1         2         3         4         5\n"
    4679              :                   "TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
    4680              :                   "TEST PREFIX: foo = bar.field;\n"
    4681              :                   "TEST PREFIX:          ^~~~~~\n"
    4682              :                   "TEST PREFIX:          ------\n",
    4683              :                   dc.test_show_locus (richloc));
    4684           64 :   }
    4685              : 
    4686              :   /* Test of adding a prefix, with ruler and line numbers.  */
    4687           64 :   {
    4688           64 :     test_context dc;
    4689           64 :     auto &source_printing_opts = dc.get_source_printing_options ();
    4690           64 :     dc.show_ruler (true);
    4691           64 :     source_printing_opts.max_width = 50;
    4692           64 :     dc.show_line_numbers (true);
    4693           64 :     pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
    4694           64 :     pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
    4695           64 :     ASSERT_STREQ ("TEST PREFIX:      |          1         2         3         4         5\n"
    4696              :                   "TEST PREFIX:      | 12345678901234567890123456789012345678901234567890\n"
    4697              :                   "TEST PREFIX:    1 | foo = bar.field;\n"
    4698              :                   "TEST PREFIX:      |          ^~~~~~\n"
    4699              :                   "TEST PREFIX:      |          ------\n",
    4700              :                   dc.test_show_locus (richloc));
    4701           64 :   }
    4702           64 : }
    4703              : 
    4704              : /* Replace fix-it hint: replacing "field" with "m_field". */
    4705              : 
    4706              : static void
    4707           64 : test_one_liner_fixit_replace ()
    4708              : {
    4709           64 :   test_context dc;
    4710           64 :   location_t start = linemap_position_for_column (line_table, 11);
    4711           64 :   location_t finish = linemap_position_for_column (line_table, 15);
    4712           64 :   location_t field = make_location (start, start, finish);
    4713           64 :   rich_location richloc (line_table, field);
    4714           64 :   richloc.add_fixit_replace ("m_field");
    4715           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4716              :                 "           ^~~~~\n"
    4717              :                 "           m_field\n",
    4718              :                 dc.test_show_locus (richloc));
    4719           64 : }
    4720              : 
    4721              : /* Replace fix-it hint: replacing "field" with "m_field",
    4722              :    but where the caret was elsewhere.  */
    4723              : 
    4724              : static void
    4725           64 : test_one_liner_fixit_replace_non_equal_range ()
    4726              : {
    4727           64 :   test_context dc;
    4728           64 :   location_t equals = linemap_position_for_column (line_table, 5);
    4729           64 :   location_t start = linemap_position_for_column (line_table, 11);
    4730           64 :   location_t finish = linemap_position_for_column (line_table, 15);
    4731           64 :   rich_location richloc (line_table, equals);
    4732           64 :   source_range range;
    4733           64 :   range.m_start = start;
    4734           64 :   range.m_finish = finish;
    4735           64 :   richloc.add_fixit_replace (range, "m_field");
    4736              :   /* The replacement range is not indicated in the annotation line, so
    4737              :      it should be indicated via an additional underline.  */
    4738           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4739              :                 "     ^\n"
    4740              :                 "           -----\n"
    4741              :                 "           m_field\n",
    4742              :                 dc.test_show_locus (richloc));
    4743           64 : }
    4744              : 
    4745              : /* Replace fix-it hint: replacing "field" with "m_field",
    4746              :    where the caret was elsewhere, but where a secondary range
    4747              :    exactly covers "field".  */
    4748              : 
    4749              : static void
    4750           64 : test_one_liner_fixit_replace_equal_secondary_range ()
    4751              : {
    4752           64 :   test_context dc;
    4753           64 :   location_t equals = linemap_position_for_column (line_table, 5);
    4754           64 :   location_t start = linemap_position_for_column (line_table, 11);
    4755           64 :   location_t finish = linemap_position_for_column (line_table, 15);
    4756           64 :   rich_location richloc (line_table, equals);
    4757           64 :   location_t field = make_location (start, start, finish);
    4758           64 :   richloc.add_range (field);
    4759           64 :   richloc.add_fixit_replace (field, "m_field");
    4760              :   /* The replacement range is indicated in the annotation line,
    4761              :      so it shouldn't be indicated via an additional underline.  */
    4762           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4763              :                 "     ^     ~~~~~\n"
    4764              :                 "           m_field\n",
    4765              :                 dc.test_show_locus (richloc));
    4766           64 : }
    4767              : 
    4768              : /* Verify that we can use ad-hoc locations when adding fixits to a
    4769              :    rich_location.  */
    4770              : 
    4771              : static void
    4772           64 : test_one_liner_fixit_validation_adhoc_locations ()
    4773              : {
    4774              :   /* Generate a range that's too long to be packed, so must
    4775              :      be stored as an ad-hoc location (given the defaults
    4776              :      of 5 or 7 bits or 0 bits of packed range); 150 columns > 2**7.  */
    4777           64 :   const location_t c7 = linemap_position_for_column (line_table, 7);
    4778           64 :   const location_t c157 = linemap_position_for_column (line_table, 157);
    4779           64 :   const location_t loc = make_location (c7, c7, c157);
    4780              : 
    4781           64 :   if (c157 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    4782              :     return;
    4783              : 
    4784           64 :   ASSERT_TRUE (IS_ADHOC_LOC (loc));
    4785              : 
    4786              :   /* Insert.  */
    4787           64 :   {
    4788           64 :     rich_location richloc (line_table, loc);
    4789           64 :     richloc.add_fixit_insert_before (loc, "test");
    4790              :     /* It should not have been discarded by the validator.  */
    4791           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    4792              : 
    4793           64 :     test_context dc;
    4794           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    4795              :                   "       ^~~~~~~~~~                               "
    4796              :                   "          "
    4797              :                   "          "
    4798              :                   "          "
    4799              :                   "          "
    4800              :                   "          "
    4801              :                   "          "
    4802              :                   "          "
    4803              :                   "          "
    4804              :                   "          "
    4805              :                   "          "
    4806              :                   "          \n"
    4807              :                   "       test\n",
    4808              :                   dc.test_show_locus (richloc));
    4809           64 :   }
    4810              : 
    4811              :   /* Remove.  */
    4812           64 :   {
    4813           64 :     rich_location richloc (line_table, loc);
    4814           64 :     source_range range = source_range::from_locations (loc, c157);
    4815           64 :     richloc.add_fixit_remove (range);
    4816              :     /* It should not have been discarded by the validator.  */
    4817           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    4818              : 
    4819           64 :     test_context dc;
    4820           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    4821              :                   "       ^~~~~~~~~~                               "
    4822              :                   "          "
    4823              :                   "          "
    4824              :                   "          "
    4825              :                   "          "
    4826              :                   "          "
    4827              :                   "          "
    4828              :                   "          "
    4829              :                   "          "
    4830              :                   "          "
    4831              :                   "          "
    4832              :                   "          \n"
    4833              :                   "       -----------------------------------------"
    4834              :                   "----------"
    4835              :                   "----------"
    4836              :                   "----------"
    4837              :                   "----------"
    4838              :                   "----------"
    4839              :                   "----------"
    4840              :                   "----------"
    4841              :                   "----------"
    4842              :                   "----------"
    4843              :                   "----------"
    4844              :                   "----------\n",
    4845              :                   dc.test_show_locus (richloc));
    4846           64 :   }
    4847              : 
    4848              :   /* Replace.  */
    4849           64 :   {
    4850           64 :     rich_location richloc (line_table, loc);
    4851           64 :     source_range range = source_range::from_locations (loc, c157);
    4852           64 :     richloc.add_fixit_replace (range, "test");
    4853              :     /* It should not have been discarded by the validator.  */
    4854           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    4855              : 
    4856           64 :     test_context dc;
    4857           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    4858              :                   "       ^~~~~~~~~~                               "
    4859              :                   "          "
    4860              :                   "          "
    4861              :                   "          "
    4862              :                   "          "
    4863              :                   "          "
    4864              :                   "          "
    4865              :                   "          "
    4866              :                   "          "
    4867              :                   "          "
    4868              :                   "          "
    4869              :                   "          \n"
    4870              :                   "       test\n",
    4871              :                   dc.test_show_locus (richloc));
    4872           64 :   }
    4873              : }
    4874              : 
    4875              : /* Test of consolidating insertions at the same location.  */
    4876              : 
    4877              : static void
    4878           64 : test_one_liner_many_fixits_1 ()
    4879              : {
    4880           64 :   test_context dc;
    4881           64 :   location_t equals = linemap_position_for_column (line_table, 5);
    4882           64 :   rich_location richloc (line_table, equals);
    4883         1344 :   for (int i = 0; i < 19; i++)
    4884         1216 :     richloc.add_fixit_insert_before ("a");
    4885           64 :   ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    4886           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4887              :                 "     ^\n"
    4888              :                 "     aaaaaaaaaaaaaaaaaaa\n",
    4889              :                 dc.test_show_locus (richloc));
    4890           64 : }
    4891              : 
    4892              : /* Ensure that we can add an arbitrary number of fix-it hints to a
    4893              :    rich_location, even if they are not consolidated.  */
    4894              : 
    4895              : static void
    4896           64 : test_one_liner_many_fixits_2 ()
    4897              : {
    4898           64 :   test_context dc;
    4899           64 :   location_t equals = linemap_position_for_column (line_table, 5);
    4900           64 :   rich_location richloc (line_table, equals);
    4901         1344 :   for (int i = 0; i < 19; i++)
    4902              :     {
    4903         1216 :       location_t loc = linemap_position_for_column (line_table, (i * 2) + 1);
    4904         1216 :       richloc.add_fixit_insert_before (loc, "a");
    4905              :     }
    4906           64 :   ASSERT_EQ (19, richloc.get_num_fixit_hints ());
    4907           64 :   ASSERT_STREQ (" foo = bar.field;\n"
    4908              :                 "     ^\n"
    4909              :                 " a a a a a a a a a a a a a a a a a a a\n",
    4910              :                 dc.test_show_locus (richloc));
    4911           64 : }
    4912              : 
    4913              : /* Test of labeling the ranges within a rich_location.  */
    4914              : 
    4915              : static void
    4916           64 : test_one_liner_labels ()
    4917              : {
    4918           64 :   location_t foo
    4919           64 :     = make_location (linemap_position_for_column (line_table, 1),
    4920              :                      linemap_position_for_column (line_table, 1),
    4921              :                      linemap_position_for_column (line_table, 3));
    4922           64 :   location_t bar
    4923           64 :     = make_location (linemap_position_for_column (line_table, 7),
    4924              :                      linemap_position_for_column (line_table, 7),
    4925              :                      linemap_position_for_column (line_table, 9));
    4926           64 :   location_t field
    4927           64 :     = make_location (linemap_position_for_column (line_table, 11),
    4928              :                      linemap_position_for_column (line_table, 11),
    4929              :                      linemap_position_for_column (line_table, 15));
    4930              : 
    4931              :   /* Example where all the labels fit on one line.  */
    4932           64 :   {
    4933           64 :     text_range_label label0 ("0");
    4934           64 :     text_range_label label1 ("1");
    4935           64 :     text_range_label label2 ("2");
    4936           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    4937           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    4938           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    4939              : 
    4940           64 :     {
    4941           64 :       test_context dc;
    4942           64 :       ASSERT_STREQ (" foo = bar.field;\n"
    4943              :                     " ^~~   ~~~ ~~~~~\n"
    4944              :                     " |     |   |\n"
    4945              :                     " 0     1   2\n",
    4946              :                     dc.test_show_locus (richloc));
    4947           64 :     }
    4948              : 
    4949              :     /* Verify that we can disable label-printing.  */
    4950           64 :     {
    4951           64 :       test_context dc;
    4952           64 :       dc.show_labels (false);
    4953           64 :       ASSERT_STREQ (" foo = bar.field;\n"
    4954              :                     " ^~~   ~~~ ~~~~~\n",
    4955              :                     dc.test_show_locus (richloc));
    4956           64 :     }
    4957           64 :   }
    4958              : 
    4959              :   /* Example where the labels need extra lines.  */
    4960           64 :   {
    4961           64 :     text_range_label label0 ("label 0");
    4962           64 :     text_range_label label1 ("label 1");
    4963           64 :     text_range_label label2 ("label 2");
    4964           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    4965           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    4966           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    4967              : 
    4968           64 :     test_context dc;
    4969           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    4970              :                   " ^~~   ~~~ ~~~~~\n"
    4971              :                   " |     |   |\n"
    4972              :                   " |     |   label 2\n"
    4973              :                   " |     label 1\n"
    4974              :                   " label 0\n",
    4975              :                   dc.test_show_locus (richloc));
    4976              : 
    4977           64 :     {
    4978           64 :       test_context dc;
    4979           64 :       dc.show_line_numbers (true);
    4980           64 :       auto out
    4981           64 :         = make_raw_html_for_locus (richloc, diagnostics::kind::error, dc);
    4982           64 :       ASSERT_STREQ
    4983              :         ("<table class=\"locus\">\n"
    4984              :          "  <tbody class=\"line-span\">\n"
    4985              :          "    <tr><td class=\"linenum\">    1</td><td class=\"left-margin\"> </td><td class=\"source\">foo = bar.field;</td></tr>\n"
    4986              :          "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">^~~   ~~~ ~~~~~</td></tr>\n"
    4987              :          "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">|     |   |</td></tr>\n"
    4988              :          "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">|     |   label 2</td></tr>\n"
    4989              :          "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">|     label 1</td></tr>\n"
    4990              :          "    <tr><td class=\"linenum\">     </td><td class=\"left-margin\"> </td><td class=\"annotation\">label 0</td></tr>\n"
    4991              :          "  </tbody>\n"
    4992              :          "</table>\n",
    4993              :          out.get ());
    4994           64 :     }
    4995           64 :   }
    4996              : 
    4997              :   /* Example of boundary conditions: label 0 and 1 have just enough clearance,
    4998              :      but label 1 just touches label 2.  */
    4999           64 :   {
    5000           64 :     text_range_label label0 ("aaaaa");
    5001           64 :     text_range_label label1 ("bbbb");
    5002           64 :     text_range_label label2 ("c");
    5003           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    5004           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5005           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    5006              : 
    5007           64 :     test_context dc;
    5008           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    5009              :                   " ^~~   ~~~ ~~~~~\n"
    5010              :                   " |     |   |\n"
    5011              :                   " |     |   c\n"
    5012              :                   " aaaaa bbbb\n",
    5013              :                   dc.test_show_locus (richloc));
    5014           64 :   }
    5015              : 
    5016              :   /* Example of out-of-order ranges (thus requiring a sort).  */
    5017           64 :   {
    5018           64 :     text_range_label label0 ("0");
    5019           64 :     text_range_label label1 ("1");
    5020           64 :     text_range_label label2 ("2");
    5021           64 :     gcc_rich_location richloc (field, &label0, nullptr);
    5022           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5023           64 :     richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label2);
    5024              : 
    5025           64 :     test_context dc;
    5026           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    5027              :                   " ~~~   ~~~ ^~~~~\n"
    5028              :                   " |     |   |\n"
    5029              :                   " 2     1   0\n",
    5030              :                   dc.test_show_locus (richloc));
    5031           64 :   }
    5032              : 
    5033              :   /* Ensure we don't ICE if multiple ranges with labels are on
    5034              :      the same point.  */
    5035           64 :   {
    5036           64 :     text_range_label label0 ("label 0");
    5037           64 :     text_range_label label1 ("label 1");
    5038           64 :     text_range_label label2 ("label 2");
    5039           64 :     gcc_rich_location richloc (bar, &label0, nullptr);
    5040           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5041           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label2);
    5042              : 
    5043           64 :     test_context dc;
    5044           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    5045              :                   "       ^~~\n"
    5046              :                   "       |\n"
    5047              :                   "       label 0\n"
    5048              :                   "       label 1\n"
    5049              :                   "       label 2\n",
    5050              :                   dc.test_show_locus (richloc));
    5051           64 :   }
    5052              : 
    5053              :   /* Example of out-of-order ranges (thus requiring a sort), where
    5054              :      they overlap, and there are multiple ranges on the same point.  */
    5055           64 :   {
    5056           64 :     text_range_label label_0a ("label 0a");
    5057           64 :     text_range_label label_1a ("label 1a");
    5058           64 :     text_range_label label_2a ("label 2a");
    5059           64 :     text_range_label label_0b ("label 0b");
    5060           64 :     text_range_label label_1b ("label 1b");
    5061           64 :     text_range_label label_2b ("label 2b");
    5062           64 :     text_range_label label_0c ("label 0c");
    5063           64 :     text_range_label label_1c ("label 1c");
    5064           64 :     text_range_label label_2c ("label 2c");
    5065           64 :     gcc_rich_location richloc (field, &label_0a, nullptr);
    5066           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1a);
    5067           64 :     richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2a);
    5068              : 
    5069           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0b);
    5070           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1b);
    5071           64 :     richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2b);
    5072              : 
    5073           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label_0c);
    5074           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label_1c);
    5075           64 :     richloc.add_range (foo, SHOW_RANGE_WITHOUT_CARET, &label_2c);
    5076              : 
    5077           64 :     test_context dc;
    5078           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    5079              :                   " ~~~   ~~~ ^~~~~\n"
    5080              :                   " |     |   |\n"
    5081              :                   " |     |   label 0a\n"
    5082              :                   " |     |   label 0b\n"
    5083              :                   " |     |   label 0c\n"
    5084              :                   " |     label 1a\n"
    5085              :                   " |     label 1b\n"
    5086              :                   " |     label 1c\n"
    5087              :                   " label 2a\n"
    5088              :                   " label 2b\n"
    5089              :                   " label 2c\n",
    5090              :                   dc.test_show_locus (richloc));
    5091           64 :   }
    5092              : 
    5093              :   /* Verify that a nullptr result from range_label::get_text is
    5094              :      handled gracefully.  */
    5095           64 :   {
    5096           64 :     text_range_label label (nullptr);
    5097           64 :     gcc_rich_location richloc (bar, &label, nullptr);
    5098              : 
    5099           64 :     test_context dc;
    5100           64 :     ASSERT_STREQ (" foo = bar.field;\n"
    5101              :                   "       ^~~\n",
    5102              :                   dc.test_show_locus (richloc));
    5103           64 :    }
    5104              : 
    5105              :   /* TODO: example of formatted printing (needs to be in
    5106              :      gcc-rich-location.cc due to Makefile.in issues).  */
    5107           64 : }
    5108              : 
    5109              : /* Run the various one-liner tests.  */
    5110              : 
    5111              : static void
    5112           96 : test_diagnostic_show_locus_one_liner (const line_table_case &case_)
    5113              : {
    5114              :   /* Create a tempfile and write some text to it.
    5115              :      ....................0000000001111111.
    5116              :      ....................1234567890123456.  */
    5117           96 :   const char *content = "foo = bar.field;\n";
    5118              : 
    5119           96 :   source_printing_fixture f (case_, content);
    5120              : 
    5121           96 :   location_t line_end = linemap_position_for_column (line_table, 16);
    5122              : 
    5123              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    5124           96 :   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5125           32 :     return;
    5126              : 
    5127           64 :   ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
    5128           64 :   ASSERT_EQ (1, LOCATION_LINE (line_end));
    5129           64 :   ASSERT_EQ (16, LOCATION_COLUMN (line_end));
    5130              : 
    5131           64 :   test_one_liner_simple_caret ();
    5132           64 :   test_one_liner_no_column ();
    5133           64 :   test_one_liner_caret_and_range ();
    5134           64 :   test_one_liner_multiple_carets_and_ranges ();
    5135           64 :   test_one_liner_fixit_insert_before ();
    5136           64 :   test_one_liner_fixit_insert_after ();
    5137           64 :   test_one_liner_fixit_remove ();
    5138           64 :   test_one_liner_fixit_replace ();
    5139           64 :   test_one_liner_fixit_replace_non_equal_range ();
    5140           64 :   test_one_liner_fixit_replace_equal_secondary_range ();
    5141           64 :   test_one_liner_fixit_validation_adhoc_locations ();
    5142           64 :   test_one_liner_many_fixits_1 ();
    5143           64 :   test_one_liner_many_fixits_2 ();
    5144           64 :   test_one_liner_labels ();
    5145           96 : }
    5146              : 
    5147              : /* Version of all one-liner tests exercising multibyte awareness.
    5148              :    These are all called from test_diagnostic_show_locus_one_liner,
    5149              :    which uses source_printing_fixture_one_liner_utf8 to create
    5150              :    the test file; see the notes in diagnostic-show-locus-selftest.h.
    5151              : 
    5152              :    Note: all of the below asserts would be easier to read if we used UTF-8
    5153              :    directly in the string constants, but it seems better not to demand the
    5154              :    host compiler support this, when it isn't otherwise necessary.  Instead,
    5155              :    whenever an extended character appears in a string, we put a line break
    5156              :    after it so that all succeeding characters can appear visually at the
    5157              :    correct display column.  */
    5158              : 
    5159              : /* Just a caret.  */
    5160              : 
    5161              : static void
    5162           64 : test_one_liner_simple_caret_utf8 ()
    5163              : {
    5164           64 :   test_context dc;
    5165           64 :   location_t caret = linemap_position_for_column (line_table, 18);
    5166           64 :   rich_location richloc (line_table, caret);
    5167           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5168              :                    "_foo = \xcf\x80"
    5169              :                            "_bar.\xf0\x9f\x98\x82"
    5170              :                                   "_field\xcf\x80"
    5171              :                                          ";\n"
    5172              :                 "               ^\n",
    5173              :                 dc.test_show_locus (richloc));
    5174           64 : }
    5175              : 
    5176              : /* Caret and range.  */
    5177              : static void
    5178           64 : test_one_liner_caret_and_range_utf8 ()
    5179              : {
    5180           64 :   test_context dc;
    5181           64 :   location_t caret = linemap_position_for_column (line_table, 18);
    5182           64 :   location_t start = linemap_position_for_column (line_table, 12);
    5183           64 :   location_t finish = linemap_position_for_column (line_table, 30);
    5184           64 :   location_t loc = make_location (caret, start, finish);
    5185           64 :   rich_location richloc (line_table, loc);
    5186           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5187              :                    "_foo = \xcf\x80"
    5188              :                            "_bar.\xf0\x9f\x98\x82"
    5189              :                                   "_field\xcf\x80"
    5190              :                                          ";\n"
    5191              :                 "          ~~~~~^~~~~~~~~~\n",
    5192              :                 dc.test_show_locus (richloc));
    5193           64 : }
    5194              : 
    5195              : /* Multiple ranges and carets.  */
    5196              : 
    5197              : static void
    5198           64 : test_one_liner_multiple_carets_and_ranges_utf8 ()
    5199              : {
    5200           64 :   test_context dc;
    5201           64 :   location_t foo
    5202           64 :     = make_location (linemap_position_for_column (line_table, 7),
    5203              :                      linemap_position_for_column (line_table, 1),
    5204              :                      linemap_position_for_column (line_table, 8));
    5205           64 :   dc.set_caret_char (0, 'A');
    5206              : 
    5207           64 :   location_t bar
    5208           64 :     = make_location (linemap_position_for_column (line_table, 16),
    5209              :                      linemap_position_for_column (line_table, 12),
    5210              :                      linemap_position_for_column (line_table, 17));
    5211           64 :   dc.set_caret_char (1, 'B');
    5212              : 
    5213           64 :   location_t field
    5214           64 :     = make_location (linemap_position_for_column (line_table, 26),
    5215              :                      linemap_position_for_column (line_table, 19),
    5216              :                      linemap_position_for_column (line_table, 30));
    5217           64 :   dc.set_caret_char (2, 'C');
    5218           64 :   rich_location richloc (line_table, foo);
    5219           64 :   richloc.add_range (bar, SHOW_RANGE_WITH_CARET);
    5220           64 :   richloc.add_range (field, SHOW_RANGE_WITH_CARET);
    5221           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5222              :                    "_foo = \xcf\x80"
    5223              :                            "_bar.\xf0\x9f\x98\x82"
    5224              :                                   "_field\xcf\x80"
    5225              :                                          ";\n"
    5226              :                 " ~~~~A~   ~~~B~ ~~~~~C~~~\n",
    5227              :                 dc.test_show_locus (richloc));
    5228           64 : }
    5229              : 
    5230              : /* Insertion fix-it hint: adding an "&" to the front of "P_bar.field". */
    5231              : 
    5232              : static void
    5233           64 : test_one_liner_fixit_insert_before_utf8 ()
    5234              : {
    5235           64 :   test_context dc;
    5236           64 :   location_t caret = linemap_position_for_column (line_table, 12);
    5237           64 :   rich_location richloc (line_table, caret);
    5238           64 :   richloc.add_fixit_insert_before ("&");
    5239           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5240              :                    "_foo = \xcf\x80"
    5241              :                            "_bar.\xf0\x9f\x98\x82"
    5242              :                                   "_field\xcf\x80"
    5243              :                                          ";\n"
    5244              :                 "          ^\n"
    5245              :                 "          &\n",
    5246              :                 dc.test_show_locus (richloc));
    5247           64 : }
    5248              : 
    5249              : /* Insertion fix-it hint: adding a "[0]" after "SS_foo". */
    5250              : 
    5251              : static void
    5252           64 : test_one_liner_fixit_insert_after_utf8 ()
    5253              : {
    5254           64 :   test_context dc;
    5255           64 :   location_t start = linemap_position_for_column (line_table, 1);
    5256           64 :   location_t finish = linemap_position_for_column (line_table, 8);
    5257           64 :   location_t foo = make_location (start, start, finish);
    5258           64 :   rich_location richloc (line_table, foo);
    5259           64 :   richloc.add_fixit_insert_after ("[0]");
    5260           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5261              :                    "_foo = \xcf\x80"
    5262              :                            "_bar.\xf0\x9f\x98\x82"
    5263              :                                   "_field\xcf\x80"
    5264              :                                          ";\n"
    5265              :                 " ^~~~~~\n"
    5266              :                 "       [0]\n",
    5267              :                 dc.test_show_locus (richloc));
    5268           64 : }
    5269              : 
    5270              : /* Removal fix-it hint: removal of the ".SS_fieldP". */
    5271              : 
    5272              : static void
    5273           64 : test_one_liner_fixit_remove_utf8 ()
    5274              : {
    5275           64 :   test_context dc;
    5276           64 :   location_t start = linemap_position_for_column (line_table, 18);
    5277           64 :   location_t finish = linemap_position_for_column (line_table, 30);
    5278           64 :   location_t dot = make_location (start, start, finish);
    5279           64 :   rich_location richloc (line_table, dot);
    5280           64 :   richloc.add_fixit_remove ();
    5281           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5282              :                    "_foo = \xcf\x80"
    5283              :                            "_bar.\xf0\x9f\x98\x82"
    5284              :                                   "_field\xcf\x80"
    5285              :                                          ";\n"
    5286              :                 "               ^~~~~~~~~~\n"
    5287              :                 "               ----------\n",
    5288              :                 dc.test_show_locus (richloc));
    5289           64 : }
    5290              : 
    5291              : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP". */
    5292              : 
    5293              : static void
    5294           64 : test_one_liner_fixit_replace_utf8 ()
    5295              : {
    5296           64 :   test_context dc;
    5297           64 :   location_t start = linemap_position_for_column (line_table, 19);
    5298           64 :   location_t finish = linemap_position_for_column (line_table, 30);
    5299           64 :   location_t field = make_location (start, start, finish);
    5300           64 :   rich_location richloc (line_table, field);
    5301           64 :   richloc.add_fixit_replace ("m_\xf0\x9f\x98\x82_field\xcf\x80");
    5302           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5303              :                    "_foo = \xcf\x80"
    5304              :                            "_bar.\xf0\x9f\x98\x82"
    5305              :                                   "_field\xcf\x80"
    5306              :                                          ";\n"
    5307              :                 "                ^~~~~~~~~\n"
    5308              :                 "                m_\xf0\x9f\x98\x82"
    5309              :                                     "_field\xcf\x80\n",
    5310              :                 dc.test_show_locus (richloc));
    5311           64 : }
    5312              : 
    5313              : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
    5314              :    but where the caret was elsewhere.  */
    5315              : 
    5316              : static void
    5317           64 : test_one_liner_fixit_replace_non_equal_range_utf8 ()
    5318              : {
    5319           64 :   test_context dc;
    5320           64 :   location_t equals = linemap_position_for_column (line_table, 10);
    5321           64 :   location_t start = linemap_position_for_column (line_table, 19);
    5322           64 :   location_t finish = linemap_position_for_column (line_table, 30);
    5323           64 :   rich_location richloc (line_table, equals);
    5324           64 :   source_range range;
    5325           64 :   range.m_start = start;
    5326           64 :   range.m_finish = finish;
    5327           64 :   richloc.add_fixit_replace (range, "m_\xf0\x9f\x98\x82_field\xcf\x80");
    5328              :   /* The replacement range is not indicated in the annotation line, so
    5329              :      it should be indicated via an additional underline.  */
    5330           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5331              :                    "_foo = \xcf\x80"
    5332              :                            "_bar.\xf0\x9f\x98\x82"
    5333              :                                   "_field\xcf\x80"
    5334              :                                          ";\n"
    5335              :                 "        ^\n"
    5336              :                 "                ---------\n"
    5337              :                 "                m_\xf0\x9f\x98\x82"
    5338              :                                     "_field\xcf\x80\n",
    5339              :                 dc.test_show_locus (richloc));
    5340           64 : }
    5341              : 
    5342              : /* Replace fix-it hint: replacing "SS_fieldP" with "m_SSfieldP",
    5343              :    where the caret was elsewhere, but where a secondary range
    5344              :    exactly covers "field".  */
    5345              : 
    5346              : static void
    5347           64 : test_one_liner_fixit_replace_equal_secondary_range_utf8 ()
    5348              : {
    5349           64 :   test_context dc;
    5350           64 :   location_t equals = linemap_position_for_column (line_table, 10);
    5351           64 :   location_t start = linemap_position_for_column (line_table, 19);
    5352           64 :   location_t finish = linemap_position_for_column (line_table, 30);
    5353           64 :   rich_location richloc (line_table, equals);
    5354           64 :   location_t field = make_location (start, start, finish);
    5355           64 :   richloc.add_range (field);
    5356           64 :   richloc.add_fixit_replace (field, "m_\xf0\x9f\x98\x82_field\xcf\x80");
    5357              :   /* The replacement range is indicated in the annotation line,
    5358              :      so it shouldn't be indicated via an additional underline.  */
    5359           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5360              :                    "_foo = \xcf\x80"
    5361              :                            "_bar.\xf0\x9f\x98\x82"
    5362              :                                   "_field\xcf\x80"
    5363              :                                          ";\n"
    5364              :                 "        ^       ~~~~~~~~~\n"
    5365              :                 "                m_\xf0\x9f\x98\x82"
    5366              :                                     "_field\xcf\x80\n",
    5367              :                 dc.test_show_locus (richloc));
    5368           64 : }
    5369              : 
    5370              : /* Verify that we can use ad-hoc locations when adding fixits to a
    5371              :    rich_location.  */
    5372              : 
    5373              : static void
    5374           64 : test_one_liner_fixit_validation_adhoc_locations_utf8 ()
    5375              : {
    5376              :   /* Generate a range that's too long to be packed, so must
    5377              :      be stored as an ad-hoc location (given the defaults
    5378              :      of 5 bits or 7 bits or 0 bits of packed range); 150 columns > 2**7.  */
    5379           64 :   const location_t c12 = linemap_position_for_column (line_table, 12);
    5380           64 :   const location_t c162 = linemap_position_for_column (line_table, 162);
    5381           64 :   const location_t loc = make_location (c12, c12, c162);
    5382              : 
    5383           64 :   if (c162 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5384              :     return;
    5385              : 
    5386           64 :   ASSERT_TRUE (IS_ADHOC_LOC (loc));
    5387              : 
    5388              :   /* Insert.  */
    5389           64 :   {
    5390           64 :     rich_location richloc (line_table, loc);
    5391           64 :     richloc.add_fixit_insert_before (loc, "test");
    5392              :     /* It should not have been discarded by the validator.  */
    5393           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    5394              : 
    5395           64 :     test_context dc;
    5396           64 :     ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5397              :                      "_foo = \xcf\x80"
    5398              :                              "_bar.\xf0\x9f\x98\x82"
    5399              :                                     "_field\xcf\x80"
    5400              :                                            ";\n"
    5401              :                   "          ^~~~~~~~~~~~~~~~                     "
    5402              :                   "          "
    5403              :                   "          "
    5404              :                   "          "
    5405              :                   "          "
    5406              :                   "          "
    5407              :                   "          "
    5408              :                   "          "
    5409              :                   "          "
    5410              :                   "          "
    5411              :                   "          "
    5412              :                   "          \n"
    5413              :                   "          test\n",
    5414              :                 dc.test_show_locus (richloc));
    5415           64 :   }
    5416              : 
    5417              :   /* Remove.  */
    5418           64 :   {
    5419           64 :     rich_location richloc (line_table, loc);
    5420           64 :     source_range range = source_range::from_locations (loc, c162);
    5421           64 :     richloc.add_fixit_remove (range);
    5422              :     /* It should not have been discarded by the validator.  */
    5423           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    5424              : 
    5425           64 :     test_context dc;
    5426           64 :     ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5427              :                      "_foo = \xcf\x80"
    5428              :                              "_bar.\xf0\x9f\x98\x82"
    5429              :                                     "_field\xcf\x80"
    5430              :                                            ";\n"
    5431              :                   "          ^~~~~~~~~~~~~~~~                     "
    5432              :                   "          "
    5433              :                   "          "
    5434              :                   "          "
    5435              :                   "          "
    5436              :                   "          "
    5437              :                   "          "
    5438              :                   "          "
    5439              :                   "          "
    5440              :                   "          "
    5441              :                   "          "
    5442              :                   "          \n"
    5443              :                   "          -------------------------------------"
    5444              :                   "----------"
    5445              :                   "----------"
    5446              :                   "----------"
    5447              :                   "----------"
    5448              :                   "----------"
    5449              :                   "----------"
    5450              :                   "----------"
    5451              :                   "----------"
    5452              :                   "----------"
    5453              :                   "----------"
    5454              :                   "----------\n",
    5455              :                 dc.test_show_locus (richloc));
    5456           64 :   }
    5457              : 
    5458              :   /* Replace.  */
    5459           64 :   {
    5460           64 :     rich_location richloc (line_table, loc);
    5461           64 :     source_range range = source_range::from_locations (loc, c162);
    5462           64 :     richloc.add_fixit_replace (range, "test");
    5463              :     /* It should not have been discarded by the validator.  */
    5464           64 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    5465              : 
    5466           64 :     test_context dc;
    5467           64 :     ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5468              :                      "_foo = \xcf\x80"
    5469              :                              "_bar.\xf0\x9f\x98\x82"
    5470              :                                     "_field\xcf\x80"
    5471              :                                            ";\n"
    5472              :                   "          ^~~~~~~~~~~~~~~~                     "
    5473              :                   "          "
    5474              :                   "          "
    5475              :                   "          "
    5476              :                   "          "
    5477              :                   "          "
    5478              :                   "          "
    5479              :                   "          "
    5480              :                   "          "
    5481              :                   "          "
    5482              :                   "          "
    5483              :                   "          \n"
    5484              :                   "          test\n",
    5485              :                 dc.test_show_locus (richloc));
    5486           64 :   }
    5487              : }
    5488              : 
    5489              : /* Test of consolidating insertions at the same location.  */
    5490              : 
    5491              : static void
    5492           64 : test_one_liner_many_fixits_1_utf8 ()
    5493              : {
    5494           64 :   test_context dc;
    5495           64 :   location_t equals = linemap_position_for_column (line_table, 10);
    5496           64 :   rich_location richloc (line_table, equals);
    5497         1344 :   for (int i = 0; i < 19; i++)
    5498         1856 :     richloc.add_fixit_insert_before (i & 1 ? "@" : "\xcf\x80");
    5499           64 :   ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    5500           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5501              :                    "_foo = \xcf\x80"
    5502              :                            "_bar.\xf0\x9f\x98\x82"
    5503              :                                   "_field\xcf\x80"
    5504              :                                          ";\n"
    5505              :                 "        ^\n"
    5506              :                 "        \xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@"
    5507              :                 "\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80@\xcf\x80\n",
    5508              :                 dc.test_show_locus (richloc));
    5509           64 : }
    5510              : 
    5511              : /* Ensure that we can add an arbitrary number of fix-it hints to a
    5512              :    rich_location, even if they are not consolidated.  */
    5513              : 
    5514              : static void
    5515           64 : test_one_liner_many_fixits_2_utf8 ()
    5516              : {
    5517           64 :   test_context dc;
    5518           64 :   location_t equals = linemap_position_for_column (line_table, 10);
    5519           64 :   rich_location richloc (line_table, equals);
    5520           64 :   const int nlocs = 19;
    5521           64 :   int locs[nlocs] = {1, 5, 7, 9, 11, 14, 16, 18, 23, 25, 27, 29, 32,
    5522              :                      34, 36, 38, 40, 42, 44};
    5523         1280 :   for (int i = 0; i != nlocs; ++i)
    5524              :     {
    5525         1216 :       location_t loc = linemap_position_for_column (line_table, locs[i]);
    5526         1856 :       richloc.add_fixit_insert_before (loc, i & 1 ? "@" : "\xcf\x80");
    5527              :     }
    5528              : 
    5529           64 :   ASSERT_EQ (nlocs, richloc.get_num_fixit_hints ());
    5530           64 :   ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5531              :                    "_foo = \xcf\x80"
    5532              :                            "_bar.\xf0\x9f\x98\x82"
    5533              :                                   "_field\xcf\x80"
    5534              :                                          ";\n"
    5535              :                 "        ^\n"
    5536              :                 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @  \xcf\x80 @"
    5537              :                 " \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80 @ \xcf\x80\n",
    5538              :                 dc.test_show_locus (richloc));
    5539           64 : }
    5540              : 
    5541              : /* Test of labeling the ranges within a rich_location.  */
    5542              : 
    5543              : static void
    5544           64 : test_one_liner_labels_utf8 ()
    5545              : {
    5546           64 :   location_t foo
    5547           64 :     = make_location (linemap_position_for_column (line_table, 1),
    5548              :                      linemap_position_for_column (line_table, 1),
    5549              :                      linemap_position_for_column (line_table, 8));
    5550           64 :   location_t bar
    5551           64 :     = make_location (linemap_position_for_column (line_table, 12),
    5552              :                      linemap_position_for_column (line_table, 12),
    5553              :                      linemap_position_for_column (line_table, 17));
    5554           64 :   location_t field
    5555           64 :     = make_location (linemap_position_for_column (line_table, 19),
    5556              :                      linemap_position_for_column (line_table, 19),
    5557              :                      linemap_position_for_column (line_table, 30));
    5558              : 
    5559              :   /* Example where all the labels fit on one line.  */
    5560           64 :   {
    5561              :     /* These three labels contain multibyte characters such that their byte
    5562              :        lengths are respectively (12, 10, 18), but their display widths are only
    5563              :        (6, 5, 9).  All three fit on the line when considering the display
    5564              :        widths, but not when considering the byte widths, so verify that we do
    5565              :        indeed put them all on one line.  */
    5566           64 :     text_range_label label0
    5567           64 :       ("\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80");
    5568           64 :     text_range_label label1
    5569           64 :       ("\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80");
    5570           64 :     text_range_label label2
    5571              :       ("\xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
    5572           64 :        "\xcf\x80");
    5573           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    5574           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5575           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    5576              : 
    5577           64 :     {
    5578           64 :       test_context dc;
    5579           64 :       ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5580              :                        "_foo = \xcf\x80"
    5581              :                                "_bar.\xf0\x9f\x98\x82"
    5582              :                                       "_field\xcf\x80"
    5583              :                                              ";\n"
    5584              :                     " ^~~~~~   ~~~~~ ~~~~~~~~~\n"
    5585              :                     " |        |     |\n"
    5586              :                     " \xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80\xcf\x80"
    5587              :                            "   \xf0\x9f\x98\x82\xf0\x9f\x98\x82\xcf\x80"
    5588              :                                    " \xf0\x9f\x98\x82\xcf\x80\xf0\x9f\x98\x82"
    5589              :                                          "\xf0\x9f\x98\x82\xcf\x80\xcf\x80\n",
    5590              :                     dc.test_show_locus (richloc));
    5591           64 :     }
    5592              : 
    5593           64 :   }
    5594              : 
    5595              :   /* Example where the labels need extra lines.  */
    5596           64 :   {
    5597           64 :     text_range_label label0 ("label 0\xf0\x9f\x98\x82");
    5598           64 :     text_range_label label1 ("label 1\xcf\x80");
    5599           64 :     text_range_label label2 ("label 2\xcf\x80");
    5600           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    5601           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5602           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    5603              : 
    5604           64 :     test_context dc;
    5605           64 :     ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5606              :                      "_foo = \xcf\x80"
    5607              :                              "_bar.\xf0\x9f\x98\x82"
    5608              :                                     "_field\xcf\x80"
    5609              :                                            ";\n"
    5610              :                   " ^~~~~~   ~~~~~ ~~~~~~~~~\n"
    5611              :                   " |        |     |\n"
    5612              :                   " |        |     label 2\xcf\x80\n"
    5613              :                   " |        label 1\xcf\x80\n"
    5614              :                   " label 0\xf0\x9f\x98\x82\n",
    5615              :                   dc.test_show_locus (richloc));
    5616           64 :   }
    5617              : 
    5618              :   /* Example of boundary conditions: label 0 and 1 have just enough clearance,
    5619              :      but label 1 just touches label 2.  */
    5620           64 :   {
    5621           64 :     text_range_label label0 ("aaaaa\xf0\x9f\x98\x82\xcf\x80");
    5622           64 :     text_range_label label1 ("bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82");
    5623           64 :     text_range_label label2 ("c");
    5624           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    5625           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5626           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    5627              : 
    5628           64 :     test_context dc;
    5629           64 :     ASSERT_STREQ (" \xf0\x9f\x98\x82"
    5630              :                      "_foo = \xcf\x80"
    5631              :                              "_bar.\xf0\x9f\x98\x82"
    5632              :                                     "_field\xcf\x80"
    5633              :                                            ";\n"
    5634              :                   " ^~~~~~   ~~~~~ ~~~~~~~~~\n"
    5635              :                   " |        |     |\n"
    5636              :                   " |        |     c\n"
    5637              :                   " aaaaa\xf0\x9f\x98\x82\xcf\x80"
    5638              :                            " bb\xf0\x9f\x98\x82\xf0\x9f\x98\x82\n",
    5639              :                   dc.test_show_locus (richloc));
    5640           64 :   }
    5641              : 
    5642              :   /* Example of escaping the source lines.  */
    5643           64 :   {
    5644           64 :     text_range_label label0 ("label 0\xf0\x9f\x98\x82");
    5645           64 :     text_range_label label1 ("label 1\xcf\x80");
    5646           64 :     text_range_label label2 ("label 2\xcf\x80");
    5647           64 :     gcc_rich_location richloc (foo, &label0, nullptr);
    5648           64 :     richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
    5649           64 :     richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
    5650           64 :     richloc.set_escape_on_output (true);
    5651              : 
    5652           64 :     {
    5653           64 :       test_context dc;
    5654           64 :       dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
    5655           64 :       ASSERT_STREQ (" <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
    5656              :                     " ^~~~~~~~~~~~~   ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
    5657              :                     " |               |            |\n"
    5658              :                     " label 0\xf0\x9f\x98\x82"
    5659              :                     /* ... */ "       label 1\xcf\x80"
    5660              :                     /* ...................*/ "     label 2\xcf\x80\n",
    5661              :                     dc.test_show_locus (richloc));
    5662           64 :     }
    5663           64 :     {
    5664           64 :       test_context dc;
    5665           64 :       dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
    5666           64 :       ASSERT_STREQ
    5667              :         (" <f0><9f><98><82>_foo = <cf><80>_bar.<f0><9f><98><82>_field<cf><80>;\n"
    5668              :          " ^~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
    5669              :          " |                      |            |\n"
    5670              :          " label 0\xf0\x9f\x98\x82"
    5671              :          /* ... */ "              label 1\xcf\x80"
    5672              :          /* ..........................*/ "     label 2\xcf\x80\n",
    5673              :          dc.test_show_locus (richloc));
    5674           64 :     }
    5675           64 :   }
    5676           64 : }
    5677              : 
    5678              : /* Make sure that colorization codes don't interrupt a multibyte
    5679              :    sequence, which would corrupt it.  */
    5680              : static void
    5681           64 : test_one_liner_colorized_utf8 ()
    5682              : {
    5683           64 :   test_context dc;
    5684           64 :   dc.colorize_source (true);
    5685           64 :   diagnostic_color_init (&dc, DIAGNOSTICS_COLOR_YES);
    5686           64 :   const location_t pi = linemap_position_for_column (line_table, 12);
    5687           64 :   rich_location richloc (line_table, pi);
    5688              : 
    5689              :   /* In order to avoid having the test depend on exactly how the colorization
    5690              :      was effected, just confirm there are two pi characters in the output.  */
    5691           64 :   const char *result = dc.test_show_locus (richloc);
    5692           64 :   const char *null_term = result + strlen (result);
    5693           64 :   const char *first_pi = strstr (result, "\xcf\x80");
    5694           64 :   ASSERT_TRUE (first_pi && first_pi <= null_term - 2);
    5695           64 :   ASSERT_STR_CONTAINS (first_pi + 2, "\xcf\x80");
    5696           64 : }
    5697              : 
    5698              : static const char * const one_liner_utf8_content
    5699              :   /* Display columns.
    5700              :      0000000000000000000000011111111111111111111111111111112222222222222
    5701              :      1111111122222222345678900000000123456666666677777777890123444444445  */
    5702              :   = "\xf0\x9f\x98\x82_foo = \xcf\x80_bar.\xf0\x9f\x98\x82_field\xcf\x80;\n";
    5703              :   /* 0000000000000000000001111111111111111111222222222222222222222233333
    5704              :      1111222233334444567890122223333456789999000011112222345678999900001
    5705              :      Byte columns.  */
    5706              : 
    5707          288 : source_printing_fixture_one_liner_utf8::
    5708              : source_printing_fixture_one_liner_utf8 (const line_table_case &case_)
    5709          288 : : source_printing_fixture (case_, one_liner_utf8_content)
    5710              : {
    5711          288 : }
    5712              : 
    5713              : /* Run the various one-liner tests.  */
    5714              : 
    5715              : static void
    5716           96 : test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_)
    5717              : {
    5718           96 :   source_printing_fixture_one_liner_utf8 f (case_);
    5719              : 
    5720           96 :   location_t line_end = linemap_position_for_column (line_table, 31);
    5721              : 
    5722              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    5723           96 :   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5724           32 :     return;
    5725              : 
    5726           64 :   ASSERT_STREQ (f.get_filename (), LOCATION_FILE (line_end));
    5727           64 :   ASSERT_EQ (1, LOCATION_LINE (line_end));
    5728           64 :   ASSERT_EQ (31, LOCATION_COLUMN (line_end));
    5729              : 
    5730           64 :   diagnostics::char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1);
    5731           64 :   ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (),
    5732              :                                     def_policy ()));
    5733           64 :   ASSERT_EQ (25, location_compute_display_column (f.m_fc,
    5734              :                                                   expand_location (line_end),
    5735              :                                                   def_policy ()));
    5736              : 
    5737           64 :   test_one_liner_simple_caret_utf8 ();
    5738           64 :   test_one_liner_caret_and_range_utf8 ();
    5739           64 :   test_one_liner_multiple_carets_and_ranges_utf8 ();
    5740           64 :   test_one_liner_fixit_insert_before_utf8 ();
    5741           64 :   test_one_liner_fixit_insert_after_utf8 ();
    5742           64 :   test_one_liner_fixit_remove_utf8 ();
    5743           64 :   test_one_liner_fixit_replace_utf8 ();
    5744           64 :   test_one_liner_fixit_replace_non_equal_range_utf8 ();
    5745           64 :   test_one_liner_fixit_replace_equal_secondary_range_utf8 ();
    5746           64 :   test_one_liner_fixit_validation_adhoc_locations_utf8 ();
    5747           64 :   test_one_liner_many_fixits_1_utf8 ();
    5748           64 :   test_one_liner_many_fixits_2_utf8 ();
    5749           64 :   test_one_liner_labels_utf8 ();
    5750           64 :   test_one_liner_colorized_utf8 ();
    5751           96 : }
    5752              : 
    5753              : /* Verify that gcc_rich_location::add_location_if_nearby works.  */
    5754              : 
    5755              : static void
    5756           96 : test_add_location_if_nearby (const line_table_case &case_)
    5757              : {
    5758              :   /* Create a tempfile and write some text to it.
    5759              :      ...000000000111111111122222222223333333333.
    5760              :      ...123456789012345678901234567890123456789.  */
    5761           96 :   const char *content
    5762              :     = ("struct same_line { double x; double y; ;\n" /* line 1.  */
    5763              :        "struct different_line\n"                    /* line 2.  */
    5764              :        "{\n"                                        /* line 3.  */
    5765              :        "  double x;\n"                              /* line 4.  */
    5766              :        "  double y;\n"                              /* line 5.  */
    5767              :        ";\n");                                      /* line 6.  */
    5768           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content, nullptr);
    5769           96 :   line_table_test ltt (case_);
    5770              : 
    5771           96 :   const line_map_ordinary *ord_map
    5772           96 :     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    5773              :                                            tmp.get_filename (), 0));
    5774              : 
    5775           96 :   linemap_line_start (line_table, 1, 100);
    5776              : 
    5777           96 :   const location_t final_line_end
    5778           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 6, 7);
    5779              : 
    5780              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    5781           96 :   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5782           40 :     return;
    5783              : 
    5784              :   /* Test of add_location_if_nearby on the same line as the
    5785              :      primary location.  */
    5786           56 :   {
    5787           56 :     test_context dc;
    5788           56 :     const location_t missing_close_brace_1_39
    5789           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 1, 39);
    5790           56 :     const location_t matching_open_brace_1_18
    5791           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
    5792           56 :     gcc_rich_location richloc (missing_close_brace_1_39);
    5793           56 :     bool added = richloc.add_location_if_nearby (dc,
    5794              :                                                  matching_open_brace_1_18);
    5795           56 :     ASSERT_TRUE (added);
    5796           56 :     ASSERT_EQ (2, richloc.get_num_locations ());
    5797           56 :     ASSERT_STREQ (" struct same_line { double x; double y; ;\n"
    5798              :                   "                  ~                    ^\n",
    5799              :                   dc.test_show_locus (richloc));
    5800           56 :   }
    5801              : 
    5802              :   /* Test of add_location_if_nearby on a different line to the
    5803              :      primary location.  */
    5804           56 :   {
    5805           56 :     test_context dc;
    5806           56 :     const location_t missing_close_brace_6_1
    5807           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 6, 1);
    5808           56 :     const location_t matching_open_brace_3_1
    5809           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 3, 1);
    5810           56 :     gcc_rich_location richloc (missing_close_brace_6_1);
    5811           56 :     bool added = richloc.add_location_if_nearby (dc,
    5812              :                                                  matching_open_brace_3_1);
    5813           56 :     ASSERT_FALSE (added);
    5814           56 :     ASSERT_EQ (1, richloc.get_num_locations ());
    5815           56 :   }
    5816           96 : }
    5817              : 
    5818              : /* Verify that we print fixits even if they only affect lines
    5819              :    outside those covered by the ranges in the rich_location.  */
    5820              : 
    5821              : static void
    5822           96 : test_diagnostic_show_locus_fixit_lines (const line_table_case &case_)
    5823              : {
    5824              :   /* Create a tempfile and write some text to it.
    5825              :      ...000000000111111111122222222223333333333.
    5826              :      ...123456789012345678901234567890123456789.  */
    5827           96 :   const char *content
    5828              :     = ("struct point { double x; double y; };\n" /* line 1.  */
    5829              :        "struct point origin = {x: 0.0,\n"        /* line 2.  */
    5830              :        "                       y\n"              /* line 3.  */
    5831              :        "\n"                                      /* line 4.  */
    5832              :        "\n"                                      /* line 5.  */
    5833              :        "                        : 0.0};\n");     /* line 6.  */
    5834           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
    5835           96 :   line_table_test ltt (case_);
    5836              : 
    5837           96 :   const line_map_ordinary *ord_map
    5838           96 :     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    5839              :                                            tmp.get_filename (), 0));
    5840              : 
    5841           96 :   linemap_line_start (line_table, 1, 100);
    5842              : 
    5843           96 :   const location_t final_line_end
    5844           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
    5845              : 
    5846              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    5847           96 :   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5848           40 :     return;
    5849              : 
    5850              :   /* A pair of tests for modernizing the initializers to C99-style.  */
    5851              : 
    5852              :   /* The one-liner case (line 2).  */
    5853           56 :   {
    5854           56 :     test_context dc;
    5855           56 :     const location_t x
    5856           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 2, 24);
    5857           56 :     const location_t colon
    5858           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 2, 25);
    5859           56 :     rich_location richloc (line_table, colon);
    5860           56 :     richloc.add_fixit_insert_before (x, ".");
    5861           56 :     richloc.add_fixit_replace (colon, "=");
    5862           56 :     ASSERT_STREQ (" struct point origin = {x: 0.0,\n"
    5863              :                   "                         ^\n"
    5864              :                   "                        .=\n",
    5865              :                   dc.test_show_locus (richloc));
    5866           56 :   }
    5867              : 
    5868              :   /* The multiline case.  The caret for the rich_location is on line 6;
    5869              :      verify that insertion fixit on line 3 is still printed (and that
    5870              :      span starts are printed due to the gap between the span at line 3
    5871              :      and that at line 6).  */
    5872           56 :   {
    5873           56 :     test_context dc;
    5874           56 :     const location_t y
    5875           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
    5876           56 :     const location_t colon
    5877           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
    5878           56 :     rich_location richloc (line_table, colon);
    5879           56 :     richloc.add_fixit_insert_before (y, ".");
    5880           56 :     richloc.add_fixit_replace (colon, "=");
    5881           56 :     ASSERT_STREQ ("FILENAME:3:24:\n"
    5882              :                   "                        y\n"
    5883              :                   "                        .\n"
    5884              :                   "FILENAME:6:25:\n"
    5885              :                   "                         : 0.0};\n"
    5886              :                   "                         ^\n"
    5887              :                   "                         =\n",
    5888              :                   dc.test_show_locus (richloc));
    5889           56 :   }
    5890              : 
    5891              :   /* As above, but verify the behavior of multiple line spans
    5892              :      with line-numbering enabled.  */
    5893           56 :   {
    5894           56 :     const location_t y
    5895           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 3, 24);
    5896           56 :     const location_t colon
    5897           56 :       = linemap_position_for_line_and_column (line_table, ord_map, 6, 25);
    5898           56 :     rich_location richloc (line_table, colon);
    5899           56 :     richloc.add_fixit_insert_before (y, ".");
    5900           56 :     richloc.add_fixit_replace (colon, "=");
    5901           56 :     test_context dc;
    5902           56 :     dc.show_line_numbers (true);
    5903           56 :     ASSERT_STREQ ("    3 |                        y\n"
    5904              :                   "      |                        .\n"
    5905              :                   "......\n"
    5906              :                   "    6 |                         : 0.0};\n"
    5907              :                   "      |                         ^\n"
    5908              :                   "      |                         =\n",
    5909              :                   dc.test_show_locus (richloc));
    5910           56 :   }
    5911           96 : }
    5912              : 
    5913              : 
    5914              : /* Verify that fix-it hints are appropriately consolidated.
    5915              : 
    5916              :    If any fix-it hints in a rich_location involve locations beyond
    5917              :    LINE_MAP_MAX_LOCATION_WITH_COLS, then we can't reliably apply
    5918              :    the fix-it as a whole, so there should be none.
    5919              : 
    5920              :    Otherwise, verify that consecutive "replace" and "remove" fix-its
    5921              :    are merged, and that other fix-its remain separate.   */
    5922              : 
    5923              : static void
    5924           96 : test_fixit_consolidation (const line_table_case &case_)
    5925              : {
    5926           96 :   line_table_test ltt (case_);
    5927              : 
    5928           96 :   linemap_add (line_table, LC_ENTER, false, "test.c", 1);
    5929              : 
    5930           96 :   const location_t c10 = linemap_position_for_column (line_table, 10);
    5931           96 :   const location_t c15 = linemap_position_for_column (line_table, 15);
    5932           96 :   const location_t c16 = linemap_position_for_column (line_table, 16);
    5933           96 :   const location_t c17 = linemap_position_for_column (line_table, 17);
    5934           96 :   const location_t c20 = linemap_position_for_column (line_table, 20);
    5935           96 :   const location_t c21 = linemap_position_for_column (line_table, 21);
    5936           96 :   const location_t caret = c10;
    5937              : 
    5938              :   /* Insert + insert. */
    5939           96 :   {
    5940           96 :     rich_location richloc (line_table, caret);
    5941           96 :     richloc.add_fixit_insert_before (c10, "foo");
    5942           96 :     richloc.add_fixit_insert_before (c15, "bar");
    5943              : 
    5944           96 :     if (c15 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5945              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    5946           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    5947              :     else
    5948              :       /* They should not have been merged.  */
    5949           64 :       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    5950           96 :   }
    5951              : 
    5952              :   /* Insert + replace. */
    5953           96 :   {
    5954           96 :     rich_location richloc (line_table, caret);
    5955           96 :     richloc.add_fixit_insert_before (c10, "foo");
    5956           96 :     richloc.add_fixit_replace (source_range::from_locations (c15, c17),
    5957              :                                "bar");
    5958              : 
    5959           96 :     if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5960              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    5961           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    5962              :     else
    5963              :       /* They should not have been merged.  */
    5964           64 :       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    5965           96 :   }
    5966              : 
    5967              :   /* Replace + non-consecutive insert. */
    5968           96 :   {
    5969           96 :     rich_location richloc (line_table, caret);
    5970           96 :     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
    5971              :                                "bar");
    5972           96 :     richloc.add_fixit_insert_before (c17, "foo");
    5973              : 
    5974           96 :     if (c17 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5975              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    5976           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    5977              :     else
    5978              :       /* They should not have been merged.  */
    5979           64 :       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    5980           96 :   }
    5981              : 
    5982              :   /* Replace + non-consecutive replace. */
    5983           96 :   {
    5984           96 :     rich_location richloc (line_table, caret);
    5985           96 :     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
    5986              :                                "foo");
    5987           96 :     richloc.add_fixit_replace (source_range::from_locations (c17, c20),
    5988              :                                "bar");
    5989              : 
    5990           96 :     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    5991              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    5992           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    5993              :     else
    5994              :       /* They should not have been merged.  */
    5995           64 :       ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    5996           96 :   }
    5997              : 
    5998              :   /* Replace + consecutive replace. */
    5999           96 :   {
    6000           96 :     rich_location richloc (line_table, caret);
    6001           96 :     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
    6002              :                                "foo");
    6003           96 :     richloc.add_fixit_replace (source_range::from_locations (c16, c20),
    6004              :                                "bar");
    6005              : 
    6006           96 :     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6007              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    6008           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    6009              :     else
    6010              :       {
    6011              :         /* They should have been merged into a single "replace".  */
    6012           64 :         ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    6013           64 :         const fixit_hint *hint = richloc.get_fixit_hint (0);
    6014           64 :         ASSERT_STREQ ("foobar", hint->get_string ());
    6015           64 :         ASSERT_EQ (c10, hint->get_start_loc ());
    6016           64 :         ASSERT_EQ (c21, hint->get_next_loc ());
    6017              :       }
    6018           96 :   }
    6019              : 
    6020              :   /* Replace + consecutive removal. */
    6021           96 :   {
    6022           96 :     rich_location richloc (line_table, caret);
    6023           96 :     richloc.add_fixit_replace (source_range::from_locations (c10, c15),
    6024              :                                "foo");
    6025           96 :     richloc.add_fixit_remove (source_range::from_locations (c16, c20));
    6026              : 
    6027           96 :     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6028              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    6029           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    6030              :     else
    6031              :       {
    6032              :         /* They should have been merged into a single replace, with the
    6033              :            range extended to cover that of the removal.  */
    6034           64 :         ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    6035           64 :         const fixit_hint *hint = richloc.get_fixit_hint (0);
    6036           64 :         ASSERT_STREQ ("foo", hint->get_string ());
    6037           64 :         ASSERT_EQ (c10, hint->get_start_loc ());
    6038           64 :         ASSERT_EQ (c21, hint->get_next_loc ());
    6039              :       }
    6040           96 :   }
    6041              : 
    6042              :   /* Consecutive removals. */
    6043           96 :   {
    6044           96 :     rich_location richloc (line_table, caret);
    6045           96 :     richloc.add_fixit_remove (source_range::from_locations (c10, c15));
    6046           96 :     richloc.add_fixit_remove (source_range::from_locations (c16, c20));
    6047              : 
    6048           96 :     if (c20 > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6049              :       /* Bogus column info for 2nd fixit, so no fixits.  */
    6050           32 :       ASSERT_EQ (0, richloc.get_num_fixit_hints ());
    6051              :     else
    6052              :       {
    6053              :         /* They should have been merged into a single "replace-with-empty".  */
    6054           64 :         ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    6055           64 :         const fixit_hint *hint = richloc.get_fixit_hint (0);
    6056           64 :         ASSERT_STREQ ("", hint->get_string ());
    6057           64 :         ASSERT_EQ (c10, hint->get_start_loc ());
    6058           64 :         ASSERT_EQ (c21, hint->get_next_loc ());
    6059              :       }
    6060           96 :   }
    6061           96 : }
    6062              : 
    6063              : /* Verify that the line_corrections machinery correctly prints
    6064              :    overlapping fixit-hints.  */
    6065              : 
    6066              : static void
    6067           96 : test_overlapped_fixit_printing (const line_table_case &case_)
    6068              : {
    6069              :   /* Create a tempfile and write some text to it.
    6070              :      ...000000000111111111122222222223333333333.
    6071              :      ...123456789012345678901234567890123456789.  */
    6072           96 :   const char *content
    6073              :     = ("  foo *f = (foo *)ptr->field;\n");
    6074           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
    6075           96 :   diagnostics::file_cache fc;
    6076           96 :   line_table_test ltt (case_);
    6077              : 
    6078           96 :   const line_map_ordinary *ord_map
    6079           96 :     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    6080              :                                            tmp.get_filename (), 0));
    6081              : 
    6082           96 :   linemap_line_start (line_table, 1, 100);
    6083              : 
    6084           96 :   const location_t final_line_end
    6085           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 6, 36);
    6086              : 
    6087              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    6088           96 :   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6089           40 :     return;
    6090              : 
    6091              :   /* A test for converting a C-style cast to a C++-style cast.  */
    6092           56 :   const location_t open_paren
    6093           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 12);
    6094           56 :   const location_t close_paren
    6095           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 18);
    6096           56 :   const location_t expr_start
    6097           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 19);
    6098           56 :   const location_t expr_finish
    6099           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 28);
    6100           56 :   const location_t expr = make_location (expr_start, expr_start, expr_finish);
    6101              : 
    6102              :   /* Various examples of fix-it hints that aren't themselves consolidated,
    6103              :      but for which the *printing* may need consolidation.  */
    6104              : 
    6105              :   /* Example where 3 fix-it hints are printed as one.  */
    6106           56 :   {
    6107           56 :     test_context dc;
    6108           56 :     rich_location richloc (line_table, expr);
    6109           56 :     richloc.add_fixit_replace (open_paren, "const_cast<");
    6110           56 :     richloc.add_fixit_replace (close_paren, "> (");
    6111           56 :     richloc.add_fixit_insert_after (")");
    6112              : 
    6113           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6114              :                   "                   ^~~~~~~~~~\n"
    6115              :                   "            -----------------\n"
    6116              :                   "            const_cast<foo *> (ptr->field)\n",
    6117              :                   dc.test_show_locus (richloc));
    6118              : 
    6119              :     /* Unit-test the line_corrections machinery.  */
    6120           56 :     char_display_policy policy (make_char_policy (dc, richloc));
    6121           56 :     ASSERT_EQ (3, richloc.get_num_fixit_hints ());
    6122           56 :     const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
    6123          112 :     ASSERT_EQ (column_range (12, 12),
    6124              :                get_affected_range (fc, policy, hint_0, CU_BYTES));
    6125          112 :     ASSERT_EQ (column_range (12, 12),
    6126              :                get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
    6127          112 :     ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
    6128           56 :     const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
    6129          112 :     ASSERT_EQ (column_range (18, 18),
    6130              :                get_affected_range (fc, policy, hint_1, CU_BYTES));
    6131          112 :     ASSERT_EQ (column_range (18, 18),
    6132              :                get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
    6133          112 :     ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
    6134           56 :     const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
    6135          112 :     ASSERT_EQ (column_range (29, 28),
    6136              :                get_affected_range (fc, policy, hint_2, CU_BYTES));
    6137          112 :     ASSERT_EQ (column_range (29, 28),
    6138              :                get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
    6139          112 :     ASSERT_EQ (column_range (29, 29), get_printed_columns (fc, policy, hint_2));
    6140              : 
    6141              :     /* Add each hint in turn to a line_corrections instance,
    6142              :        and verify that they are consolidated into one correction instance
    6143              :        as expected.  */
    6144           56 :     line_corrections lc (fc, policy, tmp.get_filename (), 1);
    6145              : 
    6146              :     /* The first replace hint by itself.  */
    6147           56 :     lc.add_hint (hint_0);
    6148           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6149          112 :     ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_bytes);
    6150          112 :     ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
    6151          112 :     ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
    6152           56 :     ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
    6153              : 
    6154              :     /* After the second replacement hint, they are printed together
    6155              :        as a replacement (along with the text between them).  */
    6156           56 :     lc.add_hint (hint_1);
    6157           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6158           56 :     ASSERT_STREQ ("const_cast<foo *> (", lc.m_corrections[0]->m_text);
    6159          112 :     ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_bytes);
    6160          112 :     ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
    6161          112 :     ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
    6162              : 
    6163              :     /* After the final insertion hint, they are all printed together
    6164              :        as a replacement (along with the text between them).  */
    6165           56 :     lc.add_hint (hint_2);
    6166           56 :     ASSERT_STREQ ("const_cast<foo *> (ptr->field)",
    6167              :                   lc.m_corrections[0]->m_text);
    6168           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6169          112 :     ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_bytes);
    6170          112 :     ASSERT_EQ (column_range (12, 28), lc.m_corrections[0]->m_affected_columns);
    6171          112 :     ASSERT_EQ (column_range (12, 41), lc.m_corrections[0]->m_printed_columns);
    6172           56 :   }
    6173              : 
    6174              :   /* Example where two are consolidated during printing.  */
    6175           56 :   {
    6176           56 :     test_context dc;
    6177           56 :     rich_location richloc (line_table, expr);
    6178           56 :     richloc.add_fixit_replace (open_paren, "CAST (");
    6179           56 :     richloc.add_fixit_replace (close_paren, ") (");
    6180           56 :     richloc.add_fixit_insert_after (")");
    6181              : 
    6182           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6183              :                   "                   ^~~~~~~~~~\n"
    6184              :                   "            -\n"
    6185              :                   "            CAST (-\n"
    6186              :                   "                  ) (        )\n",
    6187              :                   dc.test_show_locus (richloc));
    6188           56 :   }
    6189              : 
    6190              :   /* Example where none are consolidated during printing.  */
    6191           56 :   {
    6192           56 :     test_context dc;
    6193           56 :     rich_location richloc (line_table, expr);
    6194           56 :     richloc.add_fixit_replace (open_paren, "CST (");
    6195           56 :     richloc.add_fixit_replace (close_paren, ") (");
    6196           56 :     richloc.add_fixit_insert_after (")");
    6197              : 
    6198           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6199              :                   "                   ^~~~~~~~~~\n"
    6200              :                   "            -\n"
    6201              :                   "            CST ( -\n"
    6202              :                   "                  ) (        )\n",
    6203              :                   dc.test_show_locus (richloc));
    6204           56 :   }
    6205              : 
    6206              :   /* Example of deletion fix-it hints.  */
    6207           56 :   {
    6208           56 :     test_context dc;
    6209           56 :     rich_location richloc (line_table, expr);
    6210           56 :     richloc.add_fixit_insert_before (open_paren, "(bar *)");
    6211           56 :     source_range victim = {open_paren, close_paren};
    6212           56 :     richloc.add_fixit_remove (victim);
    6213              : 
    6214              :     /* This case is actually handled by fixit-consolidation,
    6215              :        rather than by line_corrections.  */
    6216           56 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    6217              : 
    6218           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6219              :                   "                   ^~~~~~~~~~\n"
    6220              :                   "            -------\n"
    6221              :                   "            (bar *)\n",
    6222              :                   dc.test_show_locus (richloc));
    6223           56 :   }
    6224              : 
    6225              :   /* Example of deletion fix-it hints that would overlap.  */
    6226           56 :   {
    6227           56 :     test_context dc;
    6228           56 :     rich_location richloc (line_table, expr);
    6229           56 :     richloc.add_fixit_insert_before (open_paren, "(longer *)");
    6230           56 :     source_range victim = {expr_start, expr_finish};
    6231           56 :     richloc.add_fixit_remove (victim);
    6232              : 
    6233              :     /* These fixits are not consolidated.  */
    6234           56 :     ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    6235              : 
    6236              :     /* But the corrections are.  */
    6237           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6238              :                   "                   ^~~~~~~~~~\n"
    6239              :                   "            -----------------\n"
    6240              :                   "            (longer *)(foo *)\n",
    6241              :                   dc.test_show_locus (richloc));
    6242           56 :   }
    6243              : 
    6244              :   /* Example of insertion fix-it hints that would overlap.  */
    6245           56 :   {
    6246           56 :     test_context dc;
    6247           56 :     rich_location richloc (line_table, expr);
    6248           56 :     richloc.add_fixit_insert_before (open_paren, "LONGER THAN THE CAST");
    6249           56 :     richloc.add_fixit_insert_after (close_paren, "TEST");
    6250              : 
    6251              :     /* The first insertion is long enough that if printed naively,
    6252              :        it would overlap with the second.
    6253              :        Verify that they are printed as a single replacement.  */
    6254           56 :     ASSERT_STREQ ("   foo *f = (foo *)ptr->field;\n"
    6255              :                   "                   ^~~~~~~~~~\n"
    6256              :                   "            -------\n"
    6257              :                   "            LONGER THAN THE CAST(foo *)TEST\n",
    6258              :                   dc.test_show_locus (richloc));
    6259           56 :   }
    6260           96 : }
    6261              : 
    6262              : /* Multibyte-aware version of preceding tests.  See comments above
    6263              :    test_one_liner_simple_caret_utf8() too, we use the same two multibyte
    6264              :    characters here.  */
    6265              : 
    6266              : static void
    6267           96 : test_overlapped_fixit_printing_utf8 (const line_table_case &case_)
    6268              : {
    6269              :   /* Create a tempfile and write some text to it.  */
    6270              : 
    6271           96 :   const char *content
    6272              :     /* Display columns.
    6273              :        00000000000000000000000111111111111111111111111222222222222222223
    6274              :        12344444444555555556789012344444444555555556789012345678999999990  */
    6275              :     = "  f\xf0\x9f\x98\x82 *f = (f\xf0\x9f\x98\x82 *)ptr->field\xcf\x80;\n";
    6276              :     /* 00000000000000000000011111111111111111111112222222222333333333333
    6277              :        12344445555666677778901234566667777888899990123456789012333344445
    6278              :        Byte columns.  */
    6279              : 
    6280           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".C", content);
    6281           96 :   line_table_test ltt (case_);
    6282              : 
    6283           96 :   const line_map_ordinary *ord_map
    6284           96 :     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    6285              :                                            tmp.get_filename (), 0));
    6286              : 
    6287           96 :   linemap_line_start (line_table, 1, 100);
    6288              : 
    6289           96 :   const location_t final_line_end
    6290           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 6, 50);
    6291              : 
    6292              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    6293           96 :   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6294           40 :     return;
    6295              : 
    6296              :   /* A test for converting a C-style cast to a C++-style cast.  */
    6297           56 :   const location_t open_paren
    6298           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 14);
    6299           56 :   const location_t close_paren
    6300           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 22);
    6301           56 :   const location_t expr_start
    6302           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
    6303           56 :   const location_t expr_finish
    6304           56 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 34);
    6305           56 :   const location_t expr = make_location (expr_start, expr_start, expr_finish);
    6306              : 
    6307              :   /* Various examples of fix-it hints that aren't themselves consolidated,
    6308              :      but for which the *printing* may need consolidation.  */
    6309              : 
    6310              :   /* Example where 3 fix-it hints are printed as one.  */
    6311           56 :   {
    6312           56 :     test_context dc;
    6313           56 :     diagnostics::file_cache &fc = dc.get_file_cache ();
    6314           56 :     rich_location richloc (line_table, expr);
    6315           56 :     richloc.add_fixit_replace (open_paren, "const_cast<");
    6316           56 :     richloc.add_fixit_replace (close_paren, "> (");
    6317           56 :     richloc.add_fixit_insert_after (")");
    6318              : 
    6319           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6320              :                         " *f = (f\xf0\x9f\x98\x82"
    6321              :                                   " *)ptr->field\xcf\x80"
    6322              :                                                 ";\n"
    6323              :                   "                   ^~~~~~~~~~~\n"
    6324              :                   "            ------------------\n"
    6325              :                   "            const_cast<f\xf0\x9f\x98\x82"
    6326              :                                             " *> (ptr->field\xcf\x80"
    6327              :                                                             ")\n",
    6328              :                   dc.test_show_locus (richloc));
    6329              : 
    6330              :     /* Unit-test the line_corrections machinery.  */
    6331           56 :     char_display_policy policy (make_char_policy (dc, richloc));
    6332           56 :     ASSERT_EQ (3, richloc.get_num_fixit_hints ());
    6333           56 :     const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
    6334          112 :     ASSERT_EQ (column_range (14, 14),
    6335              :                get_affected_range (fc, policy, hint_0, CU_BYTES));
    6336          112 :     ASSERT_EQ (column_range (12, 12),
    6337              :                get_affected_range (fc, policy, hint_0, CU_DISPLAY_COLS));
    6338          112 :     ASSERT_EQ (column_range (12, 22), get_printed_columns (fc, policy, hint_0));
    6339           56 :     const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
    6340          112 :     ASSERT_EQ (column_range (22, 22),
    6341              :                get_affected_range (fc, policy, hint_1, CU_BYTES));
    6342          112 :     ASSERT_EQ (column_range (18, 18),
    6343              :                get_affected_range (fc, policy, hint_1, CU_DISPLAY_COLS));
    6344          112 :     ASSERT_EQ (column_range (18, 20), get_printed_columns (fc, policy, hint_1));
    6345           56 :     const fixit_hint *hint_2 = richloc.get_fixit_hint (2);
    6346          112 :     ASSERT_EQ (column_range (35, 34),
    6347              :                get_affected_range (fc, policy, hint_2, CU_BYTES));
    6348          112 :     ASSERT_EQ (column_range (30, 29),
    6349              :                get_affected_range (fc, policy, hint_2, CU_DISPLAY_COLS));
    6350          112 :     ASSERT_EQ (column_range (30, 30), get_printed_columns (fc, policy, hint_2));
    6351              : 
    6352              :     /* Add each hint in turn to a line_corrections instance,
    6353              :        and verify that they are consolidated into one correction instance
    6354              :        as expected.  */
    6355           56 :     line_corrections lc (fc, policy, tmp.get_filename (), 1);
    6356              : 
    6357              :     /* The first replace hint by itself.  */
    6358           56 :     lc.add_hint (hint_0);
    6359           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6360          112 :     ASSERT_EQ (column_range (14, 14), lc.m_corrections[0]->m_affected_bytes);
    6361          112 :     ASSERT_EQ (column_range (12, 12), lc.m_corrections[0]->m_affected_columns);
    6362          112 :     ASSERT_EQ (column_range (12, 22), lc.m_corrections[0]->m_printed_columns);
    6363           56 :     ASSERT_STREQ ("const_cast<", lc.m_corrections[0]->m_text);
    6364              : 
    6365              :     /* After the second replacement hint, they are printed together
    6366              :        as a replacement (along with the text between them).  */
    6367           56 :     lc.add_hint (hint_1);
    6368           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6369           56 :     ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (",
    6370              :                   lc.m_corrections[0]->m_text);
    6371          112 :     ASSERT_EQ (column_range (14, 22), lc.m_corrections[0]->m_affected_bytes);
    6372          112 :     ASSERT_EQ (column_range (12, 18), lc.m_corrections[0]->m_affected_columns);
    6373          112 :     ASSERT_EQ (column_range (12, 30), lc.m_corrections[0]->m_printed_columns);
    6374              : 
    6375              :     /* After the final insertion hint, they are all printed together
    6376              :        as a replacement (along with the text between them).  */
    6377           56 :     lc.add_hint (hint_2);
    6378           56 :     ASSERT_STREQ ("const_cast<f\xf0\x9f\x98\x82 *> (ptr->field\xcf\x80)",
    6379              :                   lc.m_corrections[0]->m_text);
    6380           56 :     ASSERT_EQ (1, lc.m_corrections.length ());
    6381          112 :     ASSERT_EQ (column_range (14, 34), lc.m_corrections[0]->m_affected_bytes);
    6382          112 :     ASSERT_EQ (column_range (12, 29), lc.m_corrections[0]->m_affected_columns);
    6383          112 :     ASSERT_EQ (column_range (12, 42), lc.m_corrections[0]->m_printed_columns);
    6384           56 :   }
    6385              : 
    6386              :   /* Example where two are consolidated during printing.  */
    6387           56 :   {
    6388           56 :     test_context dc;
    6389           56 :     rich_location richloc (line_table, expr);
    6390           56 :     richloc.add_fixit_replace (open_paren, "CAST (");
    6391           56 :     richloc.add_fixit_replace (close_paren, ") (");
    6392           56 :     richloc.add_fixit_insert_after (")");
    6393              : 
    6394           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6395              :                         " *f = (f\xf0\x9f\x98\x82"
    6396              :                                   " *)ptr->field\xcf\x80"
    6397              :                                                 ";\n"
    6398              :                   "                   ^~~~~~~~~~~\n"
    6399              :                   "            -\n"
    6400              :                   "            CAST (-\n"
    6401              :                   "                  ) (         )\n",
    6402              :                   dc.test_show_locus (richloc));
    6403           56 :   }
    6404              : 
    6405              :   /* Example where none are consolidated during printing.  */
    6406           56 :   {
    6407           56 :     test_context dc;
    6408           56 :     rich_location richloc (line_table, expr);
    6409           56 :     richloc.add_fixit_replace (open_paren, "CST (");
    6410           56 :     richloc.add_fixit_replace (close_paren, ") (");
    6411           56 :     richloc.add_fixit_insert_after (")");
    6412              : 
    6413           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6414              :                         " *f = (f\xf0\x9f\x98\x82"
    6415              :                                   " *)ptr->field\xcf\x80"
    6416              :                                                 ";\n"
    6417              :                   "                   ^~~~~~~~~~~\n"
    6418              :                   "            -\n"
    6419              :                   "            CST ( -\n"
    6420              :                   "                  ) (         )\n",
    6421              :                   dc.test_show_locus (richloc));
    6422           56 :   }
    6423              : 
    6424              :   /* Example of deletion fix-it hints.  */
    6425           56 :   {
    6426           56 :     test_context dc;
    6427           56 :     rich_location richloc (line_table, expr);
    6428           56 :     richloc.add_fixit_insert_before (open_paren, "(bar\xf0\x9f\x98\x82 *)");
    6429           56 :     source_range victim = {open_paren, close_paren};
    6430           56 :     richloc.add_fixit_remove (victim);
    6431              : 
    6432              :     /* This case is actually handled by fixit-consolidation,
    6433              :        rather than by line_corrections.  */
    6434           56 :     ASSERT_EQ (1, richloc.get_num_fixit_hints ());
    6435              : 
    6436           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6437              :                         " *f = (f\xf0\x9f\x98\x82"
    6438              :                                   " *)ptr->field\xcf\x80"
    6439              :                                                 ";\n"
    6440              :                   "                   ^~~~~~~~~~~\n"
    6441              :                   "            -------\n"
    6442              :                   "            (bar\xf0\x9f\x98\x82"
    6443              :                                     " *)\n",
    6444              :                   dc.test_show_locus (richloc));
    6445           56 :   }
    6446              : 
    6447              :   /* Example of deletion fix-it hints that would overlap.  */
    6448           56 :   {
    6449           56 :     test_context dc;
    6450           56 :     rich_location richloc (line_table, expr);
    6451           56 :     richloc.add_fixit_insert_before (open_paren, "(long\xf0\x9f\x98\x82 *)");
    6452           56 :     source_range victim = {expr_start, expr_finish};
    6453           56 :     richloc.add_fixit_remove (victim);
    6454              : 
    6455              :     /* These fixits are not consolidated.  */
    6456           56 :     ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    6457              : 
    6458              :     /* But the corrections are.  */
    6459           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6460              :                         " *f = (f\xf0\x9f\x98\x82"
    6461              :                                   " *)ptr->field\xcf\x80"
    6462              :                                                 ";\n"
    6463              :                   "                   ^~~~~~~~~~~\n"
    6464              :                   "            ------------------\n"
    6465              :                   "            (long\xf0\x9f\x98\x82"
    6466              :                                      " *)(f\xf0\x9f\x98\x82"
    6467              :                                             " *)\n",
    6468              :                   dc.test_show_locus (richloc));
    6469           56 :   }
    6470              : 
    6471              :   /* Example of insertion fix-it hints that would overlap.  */
    6472           56 :   {
    6473           56 :     test_context dc;
    6474           56 :     rich_location richloc (line_table, expr);
    6475           56 :     richloc.add_fixit_insert_before
    6476           56 :       (open_paren, "L\xf0\x9f\x98\x82NGER THAN THE CAST");
    6477           56 :     richloc.add_fixit_insert_after (close_paren, "TEST");
    6478              : 
    6479              :     /* The first insertion is long enough that if printed naively,
    6480              :        it would overlap with the second.
    6481              :        Verify that they are printed as a single replacement.  */
    6482           56 :     ASSERT_STREQ ("   f\xf0\x9f\x98\x82"
    6483              :                         " *f = (f\xf0\x9f\x98\x82"
    6484              :                                   " *)ptr->field\xcf\x80"
    6485              :                                                 ";\n"
    6486              :                   "                   ^~~~~~~~~~~\n"
    6487              :                   "            -------\n"
    6488              :                   "            L\xf0\x9f\x98\x82"
    6489              :                                  "NGER THAN THE CAST(f\xf0\x9f\x98\x82"
    6490              :                                                        " *)TEST\n",
    6491              :                   dc.test_show_locus (richloc));
    6492           56 :   }
    6493           96 : }
    6494              : 
    6495              : /* Verify that the line_corrections machinery correctly prints
    6496              :    overlapping fixit-hints that have been added in the wrong
    6497              :    order.
    6498              :    Adapted from PR c/81405 seen on gcc.dg/init-excess-1.c*/
    6499              : 
    6500              : static void
    6501           96 : test_overlapped_fixit_printing_2 (const line_table_case &case_)
    6502              : {
    6503              :   /* Create a tempfile and write some text to it.
    6504              :      ...000000000111111111122222222223333333333.
    6505              :      ...123456789012345678901234567890123456789.  */
    6506           96 :   const char *content
    6507              :     = ("int a5[][0][0] = { 1, 2 };\n");
    6508           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
    6509           96 :   line_table_test ltt (case_);
    6510              : 
    6511           96 :   const line_map_ordinary *ord_map
    6512           96 :     = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    6513              :                                            tmp.get_filename (), 0));
    6514              : 
    6515           96 :   linemap_line_start (line_table, 1, 100);
    6516              : 
    6517           96 :   const location_t final_line_end
    6518           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 100);
    6519              : 
    6520              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    6521           96 :   if (final_line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6522           32 :     return;
    6523              : 
    6524           64 :   const location_t col_1
    6525           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
    6526           64 :   const location_t col_20
    6527           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 20);
    6528           64 :   const location_t col_21
    6529           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 21);
    6530           64 :   const location_t col_23
    6531           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 23);
    6532           64 :   const location_t col_25
    6533           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 25);
    6534              : 
    6535              :   /* Two insertions, in the wrong order.  */
    6536           64 :   {
    6537           64 :     test_context dc;
    6538           64 :     diagnostics::file_cache &fc = dc.get_file_cache ();
    6539              : 
    6540           64 :     rich_location richloc (line_table, col_20);
    6541           64 :     richloc.add_fixit_insert_before (col_23, "{");
    6542           64 :     richloc.add_fixit_insert_before (col_21, "}");
    6543              : 
    6544              :     /* These fixits should be accepted; they can't be consolidated.  */
    6545           64 :     char_display_policy policy (make_char_policy (dc, richloc));
    6546           64 :     ASSERT_EQ (2, richloc.get_num_fixit_hints ());
    6547           64 :     const fixit_hint *hint_0 = richloc.get_fixit_hint (0);
    6548          128 :     ASSERT_EQ (column_range (23, 22),
    6549              :                get_affected_range (fc, policy, hint_0, CU_BYTES));
    6550          128 :     ASSERT_EQ (column_range (23, 23), get_printed_columns (fc, policy, hint_0));
    6551           64 :     const fixit_hint *hint_1 = richloc.get_fixit_hint (1);
    6552          128 :     ASSERT_EQ (column_range (21, 20),
    6553              :                get_affected_range (fc, policy, hint_1, CU_BYTES));
    6554          128 :     ASSERT_EQ (column_range (21, 21), get_printed_columns (fc, policy, hint_1));
    6555              : 
    6556              :     /* Verify that they're printed correctly.  */
    6557           64 :     ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
    6558              :                   "                    ^\n"
    6559              :                   "                     } {\n",
    6560              :                   dc.test_show_locus (richloc));
    6561           64 :   }
    6562              : 
    6563              :   /* Various overlapping insertions, some occurring "out of order"
    6564              :      (reproducing the fix-it hints from PR c/81405).  */
    6565           64 :   {
    6566           64 :     test_context dc;
    6567           64 :     rich_location richloc (line_table, col_20);
    6568              : 
    6569           64 :     richloc.add_fixit_insert_before (col_20, "{{");
    6570           64 :     richloc.add_fixit_insert_before (col_21, "}}");
    6571           64 :     richloc.add_fixit_insert_before (col_23, "{");
    6572           64 :     richloc.add_fixit_insert_before (col_21, "}");
    6573           64 :     richloc.add_fixit_insert_before (col_23, "{{");
    6574           64 :     richloc.add_fixit_insert_before (col_25, "}");
    6575           64 :     richloc.add_fixit_insert_before (col_21, "}");
    6576           64 :     richloc.add_fixit_insert_before (col_1, "{");
    6577           64 :     richloc.add_fixit_insert_before (col_25, "}");
    6578              : 
    6579           64 :     ASSERT_STREQ (" int a5[][0][0] = { 1, 2 };\n"
    6580              :                   "                    ^\n"
    6581              :                   " {                  -----\n"
    6582              :                   "                    {{1}}}}, {{{2 }}\n",
    6583              :                   dc.test_show_locus (richloc));
    6584           64 :   }
    6585           96 : }
    6586              : 
    6587              : /* Insertion fix-it hint: adding a "break;" on a line by itself.  */
    6588              : 
    6589              : static void
    6590           96 : test_fixit_insert_containing_newline (const line_table_case &case_)
    6591              : {
    6592              :   /* Create a tempfile and write some text to it.
    6593              :      .........................0000000001111111.
    6594              :      .........................1234567890123456.  */
    6595           96 :   const char *old_content = ("    case 'a':\n" /* line 1. */
    6596              :                              "      x = a;\n"  /* line 2. */
    6597              :                              "    case 'b':\n" /* line 3. */
    6598              :                              "      x = b;\n");/* line 4. */
    6599              : 
    6600           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    6601           96 :   line_table_test ltt (case_);
    6602           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 3);
    6603              : 
    6604           96 :   location_t case_start = linemap_position_for_column (line_table, 5);
    6605           96 :   location_t case_finish = linemap_position_for_column (line_table, 13);
    6606           96 :   location_t case_loc = make_location (case_start, case_start, case_finish);
    6607           96 :   location_t line_start = linemap_position_for_column (line_table, 1);
    6608              : 
    6609           96 :   if (case_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6610           32 :     return;
    6611              : 
    6612              :   /* Add a "break;" on a line by itself before line 3 i.e. before
    6613              :      column 1 of line 3. */
    6614           64 :   {
    6615           64 :     rich_location richloc (line_table, case_loc);
    6616           64 :     richloc.add_fixit_insert_before (line_start, "      break;\n");
    6617              : 
    6618              :     /* Without line numbers.  */
    6619           64 :     {
    6620           64 :       test_context dc;
    6621           64 :       ASSERT_STREQ ("       x = a;\n"
    6622              :                     "+      break;\n"
    6623              :                     "     case 'b':\n"
    6624              :                     "     ^~~~~~~~~\n",
    6625              :                     dc.test_show_locus (richloc));
    6626           64 :     }
    6627              : 
    6628              :     /* With line numbers.  */
    6629           64 :     {
    6630           64 :       test_context dc;
    6631           64 :       dc.show_line_numbers (true);
    6632           64 :       ASSERT_STREQ ("    2 |       x = a;\n"
    6633              :                     "  +++ |+      break;\n"
    6634              :                     "    3 |     case 'b':\n"
    6635              :                     "      |     ^~~~~~~~~\n",
    6636              :                     dc.test_show_locus (richloc));
    6637           64 :     }
    6638           64 :   }
    6639              : 
    6640              :   /* Verify that attempts to add text with a newline fail when the
    6641              :      insertion point is *not* at the start of a line.  */
    6642           64 :   {
    6643           64 :     rich_location richloc (line_table, case_loc);
    6644           64 :     richloc.add_fixit_insert_before (case_start, "break;\n");
    6645           64 :     ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
    6646           64 :     test_context dc;
    6647           64 :     ASSERT_STREQ ("     case 'b':\n"
    6648              :                   "     ^~~~~~~~~\n",
    6649              :                   dc.test_show_locus (richloc));
    6650           64 :   }
    6651           96 : }
    6652              : 
    6653              : /* Insertion fix-it hint: adding a "#include <stdio.h>\n" to the top
    6654              :    of the file, where the fix-it is printed in a different line-span
    6655              :    to the primary range of the diagnostic.  */
    6656              : 
    6657              : static void
    6658           96 : test_fixit_insert_containing_newline_2 (const line_table_case &case_)
    6659              : {
    6660              :   /* Create a tempfile and write some text to it.
    6661              :      .........................0000000001111111.
    6662              :      .........................1234567890123456.  */
    6663           96 :   const char *old_content = ("test (int ch)\n"  /* line 1. */
    6664              :                              "{\n"              /* line 2. */
    6665              :                              " putchar (ch);\n" /* line 3. */
    6666              :                              "}\n");            /* line 4. */
    6667              : 
    6668           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    6669           96 :   line_table_test ltt (case_);
    6670              : 
    6671           96 :   const line_map_ordinary *ord_map = linemap_check_ordinary
    6672           96 :     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
    6673           96 :   linemap_line_start (line_table, 1, 100);
    6674              : 
    6675              :   /* The primary range is the "putchar" token.  */
    6676           96 :   location_t putchar_start
    6677           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 3, 2);
    6678           96 :   location_t putchar_finish
    6679           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 3, 8);
    6680           96 :   location_t putchar_loc
    6681           96 :     = make_location (putchar_start, putchar_start, putchar_finish);
    6682           96 :   rich_location richloc (line_table, putchar_loc);
    6683              : 
    6684              :   /* Add a "#include <stdio.h>" on a line by itself at the top of the file.  */
    6685           96 :   location_t file_start
    6686           96 :      = linemap_position_for_line_and_column (line_table, ord_map,  1, 1);
    6687           96 :   richloc.add_fixit_insert_before (file_start, "#include <stdio.h>\n");
    6688              : 
    6689           96 :   if (putchar_finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6690           36 :     return;
    6691              : 
    6692           60 :   {
    6693           60 :     test_context dc;
    6694           60 :     ASSERT_STREQ ("FILENAME:1:1:\n"
    6695              :                   "+#include <stdio.h>\n"
    6696              :                   " test (int ch)\n"
    6697              :                   "FILENAME:3:2:\n"
    6698              :                   "  putchar (ch);\n"
    6699              :                   "  ^~~~~~~\n",
    6700              :                   dc.test_show_locus (richloc));
    6701           60 :   }
    6702              : 
    6703              :   /* With line-numbering, the line spans are close enough to be
    6704              :      consolidated, since it makes little sense to skip line 2.  */
    6705           60 :   {
    6706           60 :     test_context dc;
    6707           60 :     dc.show_line_numbers (true);
    6708           60 :     ASSERT_STREQ ("  +++ |+#include <stdio.h>\n"
    6709              :                   "    1 | test (int ch)\n"
    6710              :                   "    2 | {\n"
    6711              :                   "    3 |  putchar (ch);\n"
    6712              :                   "      |  ^~~~~~~\n",
    6713              :                   dc.test_show_locus (richloc));
    6714           60 :   }
    6715           96 : }
    6716              : 
    6717              : /* Replacement fix-it hint containing a newline.
    6718              :    This will fail, as newlines are only supported when inserting at the
    6719              :    beginning of a line.  */
    6720              : 
    6721              : static void
    6722           96 : test_fixit_replace_containing_newline (const line_table_case &case_)
    6723              : {
    6724              :   /* Create a tempfile and write some text to it.
    6725              :     .........................0000000001111.
    6726              :     .........................1234567890123.  */
    6727           96 :   const char *old_content = "foo = bar ();\n";
    6728              : 
    6729           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    6730           96 :   line_table_test ltt (case_);
    6731           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
    6732              : 
    6733              :   /* Replace the " = " with "\n  = ", as if we were reformatting an
    6734              :      overly long line.  */
    6735           96 :   location_t start = linemap_position_for_column (line_table, 4);
    6736           96 :   location_t finish = linemap_position_for_column (line_table, 6);
    6737           96 :   location_t loc = linemap_position_for_column (line_table, 13);
    6738           96 :   rich_location richloc (line_table, loc);
    6739           96 :   source_range range = source_range::from_locations (start, finish);
    6740           96 :   richloc.add_fixit_replace (range, "\n =");
    6741              : 
    6742              :   /* Arbitrary newlines are not yet supported within fix-it hints, so
    6743              :      the fix-it should not be displayed.  */
    6744           96 :   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
    6745              : 
    6746           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6747           32 :     return;
    6748              : 
    6749           64 :   test_context dc;
    6750           64 :   ASSERT_STREQ (" foo = bar ();\n"
    6751              :                 "             ^\n",
    6752              :                 dc.test_show_locus (richloc));
    6753           96 : }
    6754              : 
    6755              : /* Fix-it hint, attempting to delete a newline.
    6756              :    This will fail, as we currently only support fix-it hints that
    6757              :    affect one line at a time.  */
    6758              : 
    6759              : static void
    6760           96 : test_fixit_deletion_affecting_newline (const line_table_case &case_)
    6761              : {
    6762              :   /* Create a tempfile and write some text to it.
    6763              :     ..........................0000000001111.
    6764              :     ..........................1234567890123.  */
    6765           96 :   const char *old_content = ("foo = bar (\n"
    6766              :                              "      );\n");
    6767              : 
    6768           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", old_content);
    6769           96 :   line_table_test ltt (case_);
    6770           96 :   const line_map_ordinary *ord_map = linemap_check_ordinary
    6771           96 :     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
    6772           96 :   linemap_line_start (line_table, 1, 100);
    6773              : 
    6774              :   /* Attempt to delete the " (\n...)".  */
    6775           96 :   location_t start
    6776           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 10);
    6777           96 :   location_t caret
    6778           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
    6779           96 :   location_t finish
    6780           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 2, 7);
    6781           96 :   location_t loc = make_location (caret, start, finish);
    6782           96 :   rich_location richloc (line_table, loc);
    6783           96 :   richloc. add_fixit_remove ();
    6784              : 
    6785              :   /* Fix-it hints that affect more than one line are not yet supported, so
    6786              :      the fix-it should not be displayed.  */
    6787           96 :   ASSERT_TRUE (richloc.seen_impossible_fixit_p ());
    6788              : 
    6789           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6790           32 :     return;
    6791              : 
    6792           64 :   test_context dc;
    6793           64 :   ASSERT_STREQ (" foo = bar (\n"
    6794              :                 "          ~^\n"
    6795              :                 "       );\n"
    6796              :                 "       ~    \n",
    6797              :                 dc.test_show_locus (richloc));
    6798           96 : }
    6799              : 
    6800              : static void
    6801           96 : test_tab_expansion (const line_table_case &case_)
    6802              : {
    6803              :   /* Create a tempfile and write some text to it.  This example uses a tabstop
    6804              :      of 8, as the column numbers attempt to indicate:
    6805              : 
    6806              :     .....................000.01111111111.22222333333  display
    6807              :     .....................123.90123456789.56789012345  columns  */
    6808           96 :   const char *content = "  \t   This: `\t' is a tab.\n";
    6809              :   /* ....................000 00000011111 11111222222  byte
    6810              :      ....................123 45678901234 56789012345  columns  */
    6811              : 
    6812           96 :   const int tabstop = 8;
    6813           96 :   cpp_char_column_policy policy (tabstop, cpp_wcwidth);
    6814           96 :   const int first_non_ws_byte_col = 7;
    6815           96 :   const int right_quote_byte_col = 15;
    6816           96 :   const int last_byte_col = 25;
    6817           96 :   ASSERT_EQ (35, cpp_display_width (content, last_byte_col, policy));
    6818              : 
    6819           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
    6820           96 :   line_table_test ltt (case_);
    6821           96 :   linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
    6822              : 
    6823              :   /* Don't attempt to run the tests if column data might be unavailable.  */
    6824           96 :   location_t line_end = linemap_position_for_column (line_table, last_byte_col);
    6825           96 :   if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6826           32 :     return;
    6827              : 
    6828              :   /* Check that the leading whitespace with mixed tabs and spaces is expanded
    6829              :      into 11 spaces.  Recall that print_line() also puts one space before
    6830              :      everything too.  */
    6831           64 :   {
    6832           64 :     test_context dc;
    6833           64 :     dc.get_column_options ().m_tabstop = tabstop;
    6834           64 :     rich_location richloc (line_table,
    6835              :                            linemap_position_for_column (line_table,
    6836           64 :                                                         first_non_ws_byte_col));
    6837           64 :     ASSERT_STREQ ("            This: `      ' is a tab.\n"
    6838              :                   "            ^\n",
    6839              :                   dc.test_show_locus (richloc));
    6840           64 :   }
    6841              : 
    6842              :   /* Confirm the display width was tracked correctly across the internal tab
    6843              :      as well.  */
    6844           64 :   {
    6845           64 :     test_context dc;
    6846           64 :     dc.get_column_options ().m_tabstop = tabstop;
    6847           64 :     rich_location richloc (line_table,
    6848              :                            linemap_position_for_column (line_table,
    6849           64 :                                                         right_quote_byte_col));
    6850           64 :     ASSERT_STREQ ("            This: `      ' is a tab.\n"
    6851              :                   "                         ^\n",
    6852              :                   dc.test_show_locus (richloc));
    6853           64 :   }
    6854           96 : }
    6855              : 
    6856              : /* Verify that the escaping machinery can cope with a variety of different
    6857              :    invalid bytes.  */
    6858              : 
    6859              : static void
    6860           96 : test_escaping_bytes_1 (const line_table_case &case_)
    6861              : {
    6862           96 :   const char content[] = "before\0\1\2\3\v\x80\xff""after\n";
    6863           96 :   const size_t sz = sizeof (content);
    6864           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
    6865           96 :   line_table_test ltt (case_);
    6866           96 :   const line_map_ordinary *ord_map = linemap_check_ordinary
    6867           96 :     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
    6868           96 :   linemap_line_start (line_table, 1, 100);
    6869              : 
    6870           96 :   location_t finish
    6871           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 1,
    6872              :                                             strlen (content));
    6873              : 
    6874           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6875           32 :     return;
    6876              : 
    6877              :   /* Locations of the NUL and \v bytes.  */
    6878           64 :   location_t nul_loc
    6879           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 7);
    6880           64 :   location_t v_loc
    6881           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 11);
    6882           64 :   gcc_rich_location richloc (nul_loc);
    6883           64 :   richloc.add_range (v_loc);
    6884              : 
    6885           64 :   {
    6886           64 :     test_context dc;
    6887           64 :     ASSERT_STREQ (" before \1\2\3\v\x80\xff""after\n"
    6888              :                   "       ^   ~\n",
    6889              :                   dc.test_show_locus (richloc));
    6890           64 :   }
    6891           64 :   richloc.set_escape_on_output (true);
    6892           64 :   {
    6893           64 :     test_context dc;
    6894           64 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
    6895           64 :     ASSERT_STREQ
    6896              :       (" before<U+0000><U+0001><U+0002><U+0003><U+000B><80><ff>after\n"
    6897              :        "       ^~~~~~~~                        ~~~~~~~~\n",
    6898              :        dc.test_show_locus (richloc));
    6899           64 :   }
    6900           64 :   {
    6901           64 :     test_context dc;
    6902           64 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
    6903           64 :     ASSERT_STREQ (" before<00><01><02><03><0b><80><ff>after\n"
    6904              :                   "       ^~~~            ~~~~\n",
    6905              :                   dc.test_show_locus (richloc));
    6906           64 :   }
    6907           96 : }
    6908              : 
    6909              : /* As above, but verify that we handle the initial byte of a line
    6910              :    correctly.  */
    6911              : 
    6912              : static void
    6913           96 : test_escaping_bytes_2 (const line_table_case &case_)
    6914              : {
    6915           96 :   const char content[]  = "\0after\n";
    6916           96 :   const size_t sz = sizeof (content);
    6917           96 :   temp_source_file tmp (SELFTEST_LOCATION, ".c", content, sz);
    6918           96 :   line_table_test ltt (case_);
    6919           96 :   const line_map_ordinary *ord_map = linemap_check_ordinary
    6920           96 :     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
    6921           96 :   linemap_line_start (line_table, 1, 100);
    6922              : 
    6923           96 :   location_t finish
    6924           96 :     = linemap_position_for_line_and_column (line_table, ord_map, 1,
    6925              :                                             strlen (content));
    6926              : 
    6927           96 :   if (finish > LINE_MAP_MAX_LOCATION_WITH_COLS)
    6928           32 :     return;
    6929              : 
    6930              :   /* Location of the NUL byte.  */
    6931           64 :   location_t nul_loc
    6932           64 :     = linemap_position_for_line_and_column (line_table, ord_map, 1, 1);
    6933           64 :   gcc_rich_location richloc (nul_loc);
    6934              : 
    6935           64 :   {
    6936           64 :     test_context dc;
    6937           64 :     ASSERT_STREQ ("  after\n"
    6938              :                   " ^\n",
    6939              :                   dc.test_show_locus (richloc));
    6940           64 :   }
    6941           64 :   richloc.set_escape_on_output (true);
    6942           64 :   {
    6943           64 :     test_context dc;
    6944           64 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_UNICODE);
    6945           64 :     ASSERT_STREQ (" <U+0000>after\n"
    6946              :                   " ^~~~~~~~\n",
    6947              :                   dc.test_show_locus (richloc));
    6948           64 :   }
    6949           64 :   {
    6950           64 :     test_context dc;
    6951           64 :     dc.set_escape_format (DIAGNOSTICS_ESCAPE_FORMAT_BYTES);
    6952           64 :     ASSERT_STREQ (" <00>after\n"
    6953              :                   " ^~~~\n",
    6954              :                   dc.test_show_locus (richloc));
    6955           64 :   }
    6956           96 : }
    6957              : 
    6958              : /* Verify that line numbers are correctly printed for the case of
    6959              :    a multiline range in which the width of the line numbers changes
    6960              :    (e.g. from "9" to "10").  */
    6961              : 
    6962              : static void
    6963            4 : test_line_numbers_multiline_range ()
    6964              : {
    6965              :   /* Create a tempfile and write some text to it.  */
    6966            4 :   pretty_printer pp;
    6967           88 :   for (int i = 0; i < 20; i++)
    6968              :     /* .........0000000001111111.
    6969              :    .............1234567890123456.  */
    6970           80 :     pp_printf (&pp, "this is line %i\n", i + 1);
    6971            4 :   temp_source_file tmp (SELFTEST_LOCATION, ".txt", pp_formatted_text (&pp));
    6972            4 :   line_table_test ltt;
    6973              : 
    6974            4 :   const line_map_ordinary *ord_map = linemap_check_ordinary
    6975            4 :     (linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 0));
    6976            4 :   linemap_line_start (line_table, 1, 100);
    6977              : 
    6978              :   /* Create a multi-line location, starting at the "line" of line 9, with
    6979              :      a caret on the "is" of line 10, finishing on the "this" line 11.  */
    6980              : 
    6981            4 :   location_t start
    6982            4 :     = linemap_position_for_line_and_column (line_table, ord_map, 9, 9);
    6983            4 :   location_t caret
    6984            4 :     = linemap_position_for_line_and_column (line_table, ord_map, 10, 6);
    6985            4 :   location_t finish
    6986            4 :     = linemap_position_for_line_and_column (line_table, ord_map, 11, 4);
    6987            4 :   location_t loc = make_location (caret, start, finish);
    6988              : 
    6989            4 :   test_context dc;
    6990            4 :   dc.show_line_numbers (true);
    6991            4 :   auto &source_printing_opts = dc.get_source_printing_options ();
    6992            4 :   source_printing_opts.min_margin_width = 0;
    6993            4 :   gcc_rich_location richloc (loc);
    6994            4 :   ASSERT_STREQ (" 9 | this is line 9\n"
    6995              :                 "   |         ~~~~~~\n"
    6996              :                 "10 | this is line 10\n"
    6997              :                 "   | ~~~~~^~~~~~~~~~\n"
    6998              :                 "11 | this is line 11\n"
    6999              :                 "   | ~~~~  \n",
    7000              :                 dc.test_show_locus (richloc));
    7001            4 : }
    7002              : 
    7003              : /* Run all of the selftests within this file.  */
    7004              : 
    7005              : void
    7006            4 : source_printing_cc_tests ()
    7007              : {
    7008            4 :   test_line_span ();
    7009              : 
    7010            4 :   test_layout_range_for_single_point ();
    7011            4 :   test_layout_range_for_single_line ();
    7012            4 :   test_layout_range_for_multiple_lines ();
    7013              : 
    7014            4 :   test_display_widths ();
    7015              : 
    7016            4 :   for_each_line_table_case (test_layout_x_offset_display_utf8);
    7017            4 :   for_each_line_table_case (test_layout_x_offset_display_tab);
    7018              : 
    7019            4 :   test_get_line_bytes_without_trailing_whitespace ();
    7020              : 
    7021            4 :   test_diagnostic_show_locus_unknown_location ();
    7022              : 
    7023            4 :   for_each_line_table_case (test_diagnostic_show_locus_one_liner);
    7024            4 :   for_each_line_table_case (test_diagnostic_show_locus_one_liner_utf8);
    7025            4 :   for_each_line_table_case (test_add_location_if_nearby);
    7026            4 :   for_each_line_table_case (test_diagnostic_show_locus_fixit_lines);
    7027            4 :   for_each_line_table_case (test_fixit_consolidation);
    7028            4 :   for_each_line_table_case (test_overlapped_fixit_printing);
    7029            4 :   for_each_line_table_case (test_overlapped_fixit_printing_utf8);
    7030            4 :   for_each_line_table_case (test_overlapped_fixit_printing_2);
    7031            4 :   for_each_line_table_case (test_fixit_insert_containing_newline);
    7032            4 :   for_each_line_table_case (test_fixit_insert_containing_newline_2);
    7033            4 :   for_each_line_table_case (test_fixit_replace_containing_newline);
    7034            4 :   for_each_line_table_case (test_fixit_deletion_affecting_newline);
    7035            4 :   for_each_line_table_case (test_tab_expansion);
    7036            4 :   for_each_line_table_case (test_escaping_bytes_1);
    7037            4 :   for_each_line_table_case (test_escaping_bytes_2);
    7038              : 
    7039            4 :   test_line_numbers_multiline_range ();
    7040            4 : }
    7041              : 
    7042              : } // namespace diagnostics::selftest
    7043              : } // namespace diagnostics
    7044              : 
    7045              : #endif /* #if CHECKING_P */
    7046              : 
    7047              : #if __GNUC__ >= 10
    7048              : #  pragma GCC diagnostic pop
    7049              : #endif
        

Generated by: LCOV version 2.5.0-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.