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

Generated by: LCOV version 2.4-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.