LCOV - code coverage report
Current view: top level - gcc/diagnostics - paths-output.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 98.4 % 1011 995
Test Date: 2026-02-28 14:20:25 Functions: 100.0 % 46 46
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Printing paths through the code associated with a diagnostic.
       2              :    Copyright (C) 2019-2026 Free Software Foundation, Inc.
       3              :    Contributed by David Malcolm <dmalcolm@redhat.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_ALGORITHM
      23              : #define INCLUDE_MAP
      24              : #define INCLUDE_STRING
      25              : #define INCLUDE_VECTOR
      26              : #include "system.h"
      27              : #include "coretypes.h"
      28              : #include "diagnostics/macro-unwinding.h"
      29              : #include "intl.h"
      30              : #include "diagnostics/paths.h"
      31              : #include "gcc-rich-location.h"
      32              : #include "diagnostics/color.h"
      33              : #include "diagnostics/file-cache.h"
      34              : #include "diagnostics/event-id.h"
      35              : #include "diagnostics/source-printing-effects.h"
      36              : #include "pretty-print-markup.h"
      37              : #include "selftest.h"
      38              : #include "diagnostics/selftest-context.h"
      39              : #include "diagnostics/selftest-paths.h"
      40              : #include "text-art/theme.h"
      41              : #include "diagnostics/text-sink.h"
      42              : #include "diagnostics/html-sink.h"
      43              : #include "xml.h"
      44              : #include "xml-printer.h"
      45              : 
      46              : /* Disable warnings about missing quoting in GCC diagnostics for the print
      47              :    calls below.  */
      48              : #if __GNUC__ >= 10
      49              : #  pragma GCC diagnostic push
      50              : #  pragma GCC diagnostic ignored "-Wformat-diag"
      51              : #endif
      52              : 
      53              : /* Anonymous namespace for path-printing code.  */
      54              : 
      55              : namespace {
      56              : 
      57              : using namespace diagnostics;
      58              : using namespace diagnostics::paths;
      59              : 
      60              : /* A bundle of state for printing a path.  */
      61              : 
      62              : class path_print_policy
      63              : {
      64              : public:
      65         1513 :   path_print_policy (const diagnostics::text_sink &text_output)
      66         1513 :   : m_source_policy (text_output.get_context ())
      67              :   {
      68              :   }
      69              : 
      70            3 :   path_print_policy (const diagnostics::context &dc)
      71            3 :   : m_source_policy (dc)
      72              :   {
      73              :   }
      74              : 
      75              :   text_art::theme *
      76         2651 :   get_diagram_theme () const
      77              :   {
      78         2651 :     return m_source_policy.get_diagram_theme ();
      79              :   }
      80              : 
      81              :   const diagnostics::source_print_policy &
      82         2301 :   get_source_policy () const { return m_source_policy; }
      83              : 
      84              : private:
      85              :   diagnostics::source_print_policy m_source_policy;
      86              : };
      87              : 
      88              : /* Subclass of range_label for showing a particular event
      89              :    when showing a consecutive run of events within a diagnostic path as
      90              :    labelled ranges within one gcc_rich_location.  */
      91              : 
      92         2651 : class path_label : public range_label
      93              : {
      94              :  public:
      95         2651 :   path_label (const path &path_,
      96              :               const pretty_printer &ref_pp,
      97              :               unsigned start_idx,
      98              :               bool colorize,
      99              :               bool allow_emojis)
     100         2651 :   : m_path (path_),
     101         2651 :     m_ref_pp (ref_pp),
     102         2651 :     m_start_idx (start_idx), m_effects (*this),
     103         2651 :     m_colorize (colorize), m_allow_emojis (allow_emojis)
     104              :   {}
     105              : 
     106         4669 :   label_text get_text (unsigned range_idx) const final override
     107              :   {
     108         4669 :     unsigned event_idx = m_start_idx + range_idx;
     109         4669 :     const event &ev = m_path.get_event (event_idx);
     110              : 
     111         4669 :     const event::meaning meaning (ev.get_meaning ());
     112              : 
     113         4669 :     auto pp = m_ref_pp.clone ();
     114         4669 :     pp_show_color (pp.get ()) = m_colorize;
     115         4669 :     event_id_t event_id (event_idx);
     116         4669 :     pp_printf (pp.get (), "%@", &event_id);
     117         4669 :     pp_space (pp.get ());
     118              : 
     119         4669 :     if (meaning.m_verb == event::verb::danger
     120          200 :         && m_allow_emojis)
     121              :       {
     122            2 :         pp_unicode_character (pp.get (), 0x26A0); /* U+26A0 WARNING SIGN.  */
     123              :         /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
     124              :            variation of the char.  */
     125            2 :         pp_unicode_character (pp.get (), 0xFE0F);
     126              :         /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
     127              :            emoji variant is printed (by vte at least) with a 2nd half
     128              :            overlapping the next char.  Hence we add two spaces here: a space
     129              :            to be covered by this overlap, plus another space of padding.  */
     130            2 :         pp_string (pp.get (), "  ");
     131              :       }
     132              : 
     133         4669 :     ev.print_desc (*pp.get ());
     134              : 
     135         4669 :     label_text result
     136         4669 :       = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
     137         9338 :     return result;
     138         4669 :   }
     139              : 
     140              :   const diagnostics::label_effects *
     141         9338 :   get_effects (unsigned /*range_idx*/) const final override
     142              :   {
     143         9338 :     return &m_effects;
     144              :   }
     145              : 
     146              :  private:
     147         2651 :   class path_label_effects : public label_effects
     148              :   {
     149              :   public:
     150         2651 :     path_label_effects (const path_label &path_label)
     151         2651 :     : m_path_label (path_label)
     152              :     {
     153              :     }
     154         4669 :     bool has_in_edge (unsigned range_idx) const final override
     155              :     {
     156         9338 :       if (const event *prev_event
     157         4669 :             = m_path_label.get_prev_event (range_idx))
     158         3254 :         return prev_event->connect_to_next_event_p ();
     159              :       return false;
     160              :     }
     161         4669 :     bool has_out_edge (unsigned range_idx) const final override
     162              :     {
     163         9338 :       const event &ev = m_path_label.get_event (range_idx);
     164         4669 :       return ev.connect_to_next_event_p ();
     165              :     }
     166              : 
     167              :   private:
     168              :     const path_label &m_path_label;
     169              :   };
     170              : 
     171         4669 :   const event &get_event (unsigned range_idx) const
     172              :   {
     173         4669 :     unsigned event_idx = m_start_idx + range_idx;
     174         4669 :     return m_path.get_event (event_idx);
     175              :   }
     176              : 
     177         4669 :   const event *get_prev_event (unsigned range_idx) const
     178              :   {
     179         4669 :     if (m_start_idx + range_idx == 0)
     180              :       return nullptr;
     181         3254 :     unsigned event_idx = m_start_idx + range_idx - 1;
     182         3254 :     return &m_path.get_event (event_idx);
     183              :   }
     184              : 
     185              :   const path &m_path;
     186              :   const pretty_printer &m_ref_pp;
     187              :   unsigned m_start_idx;
     188              :   path_label_effects m_effects;
     189              :   const bool m_colorize;
     190              :   const bool m_allow_emojis;
     191              : };
     192              : 
     193              : /* Return true if E1 and E2 can be consolidated into the same run of events
     194              :    when printing a diagnostic path.  */
     195              : 
     196              : static bool
     197         3568 : can_consolidate_events (const path &p,
     198              :                         const event &e1,
     199              :                         unsigned ev1_idx,
     200              :                         const event &e2,
     201              :                         unsigned ev2_idx,
     202              :                         bool check_locations)
     203              : {
     204         3568 :   if (e1.get_thread_id () != e2.get_thread_id ())
     205              :     return false;
     206              : 
     207         3565 :   if (!p.same_function_p (ev1_idx, ev2_idx))
     208              :     return false;
     209              : 
     210         3158 :   if (e1.get_stack_depth () != e2.get_stack_depth ())
     211              :     return false;
     212              : 
     213         3126 :   if (check_locations)
     214              :     {
     215         3002 :       location_t loc1 = e1.get_location ();
     216         3002 :       location_t loc2 = e2.get_location ();
     217              : 
     218         3002 :       if (loc1 < RESERVED_LOCATION_COUNT
     219         3002 :           || loc2 < RESERVED_LOCATION_COUNT)
     220              :         return false;
     221              : 
     222              :       /* Neither can be macro-based.  */
     223         2876 :       if (linemap_location_from_macro_expansion_p (line_table, loc1))
     224              :         return false;
     225         2842 :       if (linemap_location_from_macro_expansion_p (line_table, loc2))
     226              :         return false;
     227              :     }
     228              : 
     229              :   /* Passed all the tests.  */
     230              :   return true;
     231              : }
     232              : 
     233              : struct event_range;
     234              : struct path_summary;
     235              : class thread_event_printer;
     236              : 
     237              : /* A bundle of information about all of the events in a diagnostic path
     238              :    relating to a specific path, for use by path_summary.  */
     239              : 
     240              : class per_thread_summary
     241              : {
     242              : public:
     243         1508 :   per_thread_summary (const path &path_,
     244              :                       const logical_locations::manager &logical_loc_mgr,
     245              :                       label_text name, unsigned swimlane_idx)
     246         1508 :   : m_path (path_),
     247         1508 :     m_logical_loc_mgr (logical_loc_mgr),
     248         1508 :     m_name (std::move (name)),
     249         1508 :     m_swimlane_idx (swimlane_idx),
     250         1508 :     m_last_event (nullptr),
     251         1508 :     m_min_depth (INT_MAX),
     252         1508 :     m_max_depth (INT_MIN)
     253              :   {}
     254              : 
     255         5075 :   void update_depth_limits (int stack_depth)
     256              :   {
     257         5075 :     if (stack_depth < m_min_depth)
     258         1512 :       m_min_depth = stack_depth;
     259         5075 :     if (stack_depth > m_max_depth)
     260         1715 :       m_max_depth = stack_depth;
     261              :   }
     262              : 
     263            4 :   const char *get_name () const { return m_name.get (); }
     264         2651 :   unsigned get_swimlane_index () const { return m_swimlane_idx; }
     265              : 
     266              :   bool interprocedural_p () const;
     267              : 
     268              : private:
     269              :   friend struct path_summary;
     270              :   friend class thread_event_printer;
     271              :   friend struct event_range;
     272              : 
     273              :   const path &m_path;
     274              :   const logical_locations::manager &m_logical_loc_mgr;
     275              : 
     276              :   const label_text m_name;
     277              : 
     278              :   /* The "swimlane index" is the order in which this per_thread_summary
     279              :      was created, for use when printing the events.  */
     280              :   const unsigned m_swimlane_idx;
     281              : 
     282              :   // The event ranges specific to this thread:
     283              :   auto_vec<event_range *> m_event_ranges;
     284              : 
     285              :   const event *m_last_event;
     286              : 
     287              :   int m_min_depth;
     288              :   int m_max_depth;
     289              : };
     290              : 
     291              : /* A stack frame for use in HTML output, holding child stack frames,
     292              :    and event ranges. */
     293              : 
     294              : struct stack_frame
     295              : {
     296            7 :   stack_frame (std::unique_ptr<stack_frame> parent,
     297              :                logical_locations::key logical_loc,
     298              :                int stack_depth)
     299            7 :   : m_parent (std::move (parent)),
     300            7 :     m_logical_loc (logical_loc),
     301            7 :     m_stack_depth (stack_depth)
     302              :   {}
     303              : 
     304              :   std::unique_ptr<stack_frame> m_parent;
     305              :   logical_locations::key m_logical_loc;
     306              :   const int m_stack_depth;
     307              : };
     308              : 
     309              : /* Begin emitting content relating to a new stack frame within PARENT.
     310              :    Allocated a new stack_frame and return it.  */
     311              : 
     312              : static std::unique_ptr<stack_frame>
     313            7 : begin_html_stack_frame (xml::printer &xp,
     314              :                         std::unique_ptr<stack_frame> parent,
     315              :                         logical_locations::key logical_loc,
     316              :                         int stack_depth,
     317              :                         const logical_locations::manager *logical_loc_mgr)
     318              : {
     319            7 :   if (logical_loc)
     320              :     {
     321            6 :       gcc_assert (logical_loc_mgr);
     322            6 :       xp.push_tag_with_class ("table", "stack-frame-with-margin", false);
     323            6 :       xp.push_tag ("tr", false);
     324            6 :       {
     325            6 :         xp.push_tag_with_class ("td", "interprocmargin", false);
     326            6 :         xp.set_attr ("style", "padding-left: 100px");
     327            6 :         xp.pop_tag ("td");
     328              :       }
     329            6 :       xp.push_tag_with_class ("td", "stack-frame", false);
     330            6 :       label_text funcname
     331            6 :         = logical_loc_mgr->get_name_for_path_output (logical_loc);
     332            6 :       if (funcname.get ())
     333              :         {
     334            6 :           xp.push_tag_with_class ("div", "frame-funcname", false);
     335            6 :           xp.push_tag ("span", true);
     336            6 :           xp.add_text (funcname.get ());
     337            6 :           xp.pop_tag ("span");
     338            6 :           xp.pop_tag ("div");
     339              :         }
     340            6 :     }
     341            7 :   return std::make_unique<stack_frame> (std::move (parent),
     342              :                                         logical_loc,
     343            7 :                                         stack_depth);
     344              : }
     345              : 
     346              : /* Finish emitting content for FRAME and delete it.
     347              :    Return parent.  */
     348              : 
     349              : static std::unique_ptr<stack_frame>
     350            7 : end_html_stack_frame (xml::printer &xp,
     351              :                       std::unique_ptr<stack_frame> frame)
     352              : {
     353            7 :   auto parent = std::move (frame->m_parent);
     354            7 :   if (frame->m_logical_loc)
     355              :     {
     356            6 :       xp.pop_tag ("td");
     357            6 :       xp.pop_tag ("tr");
     358            6 :       xp.pop_tag ("table");
     359              :     }
     360            7 :   return parent;
     361              : }
     362              : 
     363              : /* Append an HTML <div> element to XP containing an SVG arrow representing
     364              :    a change in stack depth from OLD_DEPTH to NEW_DEPTH.  */
     365              : 
     366              : static void
     367            4 : emit_svg_arrow (xml::printer &xp, int old_depth, int new_depth)
     368              : {
     369            4 :   const int pixels_per_depth = 100;
     370            4 :   const int min_depth = MIN (old_depth, new_depth);
     371            4 :   const int base_x = 20;
     372            4 :   const int excess = 30;
     373            4 :   const int last_x
     374            4 :     = base_x + (old_depth - min_depth) * pixels_per_depth;
     375            4 :   const int this_x
     376            4 :     = base_x + (new_depth - min_depth) * pixels_per_depth;
     377            4 :   pretty_printer tmp_pp;
     378            4 :   pretty_printer *pp = &tmp_pp;
     379            5 :   pp_printf (pp, "<div class=\"%s\">\n",
     380              :              old_depth < new_depth
     381              :              ? "between-ranges-call" : "between-ranges-return");
     382            4 :   pp_printf (pp, "  <svg height=\"30\" width=\"%i\">\n",
     383              :              MAX (last_x, this_x) + excess);
     384            4 :   pp_string
     385            4 :     (pp,
     386              :      "    <defs>\n"
     387              :      "      <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\"\n"
     388              :      "              refX=\"0\" refY=\"3.5\" orient=\"auto\" stroke=\"#0088ce\" fill=\"#0088ce\">\n"
     389              :      "      <polygon points=\"0 0, 10 3.5, 0 7\"/>\n"
     390              :      "      </marker>\n"
     391              :      "    </defs>\n");
     392            4 :   pp_printf (pp,
     393              :              "    <polyline points=\"%i,0 %i,10 %i,10 %i,20\"\n",
     394              :              last_x, last_x, this_x, this_x);
     395            4 :   pp_string
     396            4 :     (pp,
     397              :      "              style=\"fill:none;stroke: #0088ce\"\n"
     398              :      "              marker-end=\"url(#arrowhead)\"/>\n"
     399              :      "  </svg>\n"
     400              :      "</div>\n\n");
     401            4 :   xp.add_raw (pp_formatted_text (pp));
     402            4 : }
     403              : 
     404              : /* A range of consecutive events within a diagnostic path, all within the
     405              :    same thread, and with the same fndecl and stack_depth, and which are suitable
     406              :    to print with a single call to diagnostic_show_locus.  */
     407         2651 : struct event_range
     408              : {
     409              :   /* A struct for tracking the mergability of labels on a particular
     410              :      source line.  In particular, track information about links between
     411              :      labels to ensure that we only consolidate events involving links
     412              :      that the source-printing code is able to handle (splitting them
     413              :      otherwise).  */
     414              :   struct per_source_line_info
     415              :   {
     416         3805 :     void init (int line)
     417              :     {
     418         3805 :       m_line = line;
     419         3805 :       m_has_in_edge = false;
     420         3805 :       m_has_out_edge = false;
     421         3805 :       m_min_label_source_column = INT_MAX;
     422         3805 :       m_max_label_source_column = INT_MIN;
     423         3805 :     }
     424              : 
     425              :     /* Return true if our source-printing/labelling/linking code can handle
     426              :        the events already on this source line, *and* a new event at COLUMN.  */
     427              :     bool
     428         2287 :     can_add_label_for_event_p (bool has_in_edge,
     429              :                                const event *prev_event,
     430              :                                bool has_out_edge,
     431              :                                int column) const
     432              :     {
     433              :       /* Any existing in-edge has to be the left-most label on its
     434              :          source line.  */
     435         2287 :       if (m_has_in_edge && column < m_min_label_source_column)
     436              :         return false;
     437              :       /* Any existing out-edge has to be the right-most label on its
     438              :          source line.  */
     439         2171 :       if (m_has_out_edge && column > m_max_label_source_column)
     440              :         return false;
     441              :       /* Can't have more than one in-edge.  */
     442         2051 :       if (m_has_in_edge && has_in_edge)
     443              :         return false;
     444              :       /* Can't have more than one out-edge.  */
     445         2051 :       if (m_has_out_edge && has_out_edge)
     446              :         return false;
     447              : 
     448         1995 :       if (has_in_edge)
     449              :         {
     450              :           /* Any new in-edge needs to be the left-most label on its
     451              :              source line.  */
     452         1186 :           if (column > m_min_label_source_column)
     453          228 :             return false;
     454              : 
     455         1186 :           gcc_assert (prev_event);
     456         1186 :           const location_t prev_loc = prev_event->get_location ();
     457         1186 :           expanded_location prev_exploc
     458              :             = linemap_client_expand_location_to_spelling_point
     459         1186 :                 (line_table, prev_loc, location_aspect::caret);
     460              :           /* The destination in-edge's line number has to be <= the
     461              :              source out-edge's line number (if any).  */
     462         1186 :           if (prev_exploc.line >= m_line)
     463              :             return false;
     464              :         }
     465              : 
     466              :       /* Any new out-edge needs to be the right-most label on its
     467              :          source line.  */
     468         1767 :       if (has_out_edge)
     469          291 :         if (column < m_max_label_source_column)
     470              :           return false;
     471              : 
     472              :       /* All checks passed; we can add the new event at COLUMN.  */
     473              :       return true;
     474              :     }
     475              : 
     476              :     void
     477         3806 :     add_label_for_event (bool has_in_edge, bool has_out_edge, int column)
     478              :     {
     479         3806 :       if (has_in_edge)
     480         1486 :         m_has_in_edge = true;
     481         3806 :       if (has_out_edge)
     482         1486 :         m_has_out_edge = true;
     483         3806 :       m_min_label_source_column = std::min (m_min_label_source_column, column);
     484         3806 :       m_max_label_source_column = std::max (m_max_label_source_column, column);
     485              :     }
     486              : 
     487              :     int m_line;
     488              :     bool m_has_in_edge;
     489              :     bool m_has_out_edge;
     490              :     int m_min_label_source_column;
     491              :     int m_max_label_source_column;
     492              :   };
     493              : 
     494         2651 :   event_range (const path &path_,
     495              :                const pretty_printer &ref_pp,
     496              :                unsigned start_idx,
     497              :                const event &initial_event,
     498              :                per_thread_summary &t,
     499              :                bool show_event_links,
     500              :                bool colorize_labels,
     501              :                bool allow_emojis)
     502         2651 :   : m_path (path_),
     503         2651 :     m_initial_event (initial_event),
     504         2651 :     m_logical_loc (initial_event.get_logical_location ()),
     505         2651 :     m_stack_depth (initial_event.get_stack_depth ()),
     506         2651 :     m_start_idx (start_idx), m_end_idx (start_idx),
     507         2651 :     m_path_label (path_, ref_pp,
     508              :                   start_idx, colorize_labels, allow_emojis),
     509         2651 :     m_richloc (initial_event.get_location (), &m_path_label, nullptr),
     510         2651 :     m_thread_id (initial_event.get_thread_id ()),
     511         2651 :     m_per_thread_summary (t),
     512         2651 :     m_show_event_links (show_event_links)
     513              :   {
     514         2651 :     if (m_show_event_links)
     515              :       {
     516         2039 :         expanded_location exploc
     517              :           = linemap_client_expand_location_to_spelling_point
     518         2039 :               (line_table, initial_event.get_location (),
     519              :                location_aspect::caret);
     520         2039 :         per_source_line_info &source_line_info
     521         2039 :           = get_per_source_line_info (exploc.line);
     522              : 
     523         2039 :         const event *prev_thread_event = t.m_last_event;
     524         2039 :         const bool has_in_edge
     525              :           = (prev_thread_event
     526         2039 :              ? prev_thread_event->connect_to_next_event_p ()
     527         2039 :              : false);
     528         2039 :         const bool has_out_edge = initial_event.connect_to_next_event_p ();
     529              : 
     530         2039 :         source_line_info.add_label_for_event
     531         4078 :           (has_in_edge, has_out_edge, exploc.column);
     532              :       }
     533         2651 :   }
     534              : 
     535              :   per_source_line_info &
     536         4984 :   get_per_source_line_info (int source_line)
     537              :   {
     538         4984 :     bool existed = false;
     539         4984 :     per_source_line_info &result
     540         4984 :       = m_source_line_info_map.get_or_insert (source_line, &existed);
     541         4984 :     if (!existed)
     542         3805 :       result.init (source_line);
     543         4984 :     return result;
     544              :   }
     545              : 
     546         3568 :   bool maybe_add_event (const path_print_policy &policy,
     547              :                         const event &new_ev,
     548              :                         unsigned new_ev_idx,
     549              :                         bool check_rich_locations)
     550              :   {
     551         3568 :     if (!can_consolidate_events (m_path,
     552              :                                  m_initial_event, m_start_idx,
     553              :                                  new_ev, new_ev_idx,
     554              :                                  check_rich_locations))
     555              :       return false;
     556              : 
     557              :     /* Verify compatibility of the new label and existing labels
     558              :        with respect to the link-printing code.  */
     559         2945 :     expanded_location exploc
     560              :       = linemap_client_expand_location_to_spelling_point
     561         2945 :       (line_table, new_ev.get_location (), location_aspect::caret);
     562         2945 :     per_source_line_info &source_line_info
     563         2945 :       = get_per_source_line_info (exploc.line);
     564         2945 :     const event *prev_event = nullptr;
     565         2945 :     if (new_ev_idx > 0)
     566         2945 :       prev_event = &m_path.get_event (new_ev_idx - 1);
     567         2945 :     const bool has_in_edge = (prev_event
     568         2945 :                               ? prev_event->connect_to_next_event_p ()
     569         2945 :                               : false);
     570         2945 :     const bool has_out_edge = new_ev.connect_to_next_event_p ();
     571         2945 :     if (m_show_event_links)
     572         2287 :       if (!source_line_info.can_add_label_for_event_p
     573         2287 :           (has_in_edge, prev_event,
     574              :            has_out_edge, exploc.column))
     575              :         return false;
     576              : 
     577              :     /* Potentially verify that the locations are sufficiently close.  */
     578         2425 :     if (check_rich_locations)
     579         2301 :       if (!m_richloc.add_location_if_nearby (policy.get_source_policy (),
     580         2301 :                                              new_ev.get_location (),
     581              :                                              false, &m_path_label))
     582              :         return false;
     583              : 
     584         2424 :     m_end_idx = new_ev_idx;
     585         2424 :     m_per_thread_summary.m_last_event = &new_ev;
     586              : 
     587         2424 :     if (m_show_event_links)
     588         1767 :       source_line_info.add_label_for_event
     589         3534 :         (has_in_edge, has_out_edge, exploc.column);
     590              : 
     591              :     return true;
     592              :   }
     593              : 
     594              :   /* Print the events in this range to PP, typically as a single
     595              :      call to diagnostic_show_locus.  */
     596              : 
     597         2644 :   void print_as_text (pretty_printer &pp,
     598              :                       diagnostics::text_sink &text_output,
     599              :                       diagnostics::source_effect_info *effect_info)
     600              :   {
     601         2644 :     location_t initial_loc = m_initial_event.get_location ();
     602              : 
     603         2644 :     diagnostics::context &dc = text_output.get_context ();
     604              : 
     605              :     /* Emit a span indicating the filename (and line/column) if the
     606              :        line has changed relative to the last call to
     607              :        diagnostic_show_locus.  */
     608         2644 :     if (dc.get_source_printing_options ().enabled)
     609              :       {
     610         2644 :         expanded_location exploc
     611              :           = linemap_client_expand_location_to_spelling_point
     612         2644 :           (line_table, initial_loc, location_aspect::caret);
     613         2644 :         if (exploc.file != LOCATION_FILE (dc.m_last_location))
     614              :           {
     615         1330 :             diagnostics::location_print_policy loc_policy (text_output);
     616         1330 :             loc_policy.print_text_span_start (dc, pp, exploc);
     617              :           }
     618              :       }
     619              : 
     620              :     /* Ideally we will print events as labelled ranges within the
     621              :        quoted source.  But if there is no source, or we can't find it,
     622              :        we need a fallback, or the events won't show up. Fail more
     623              :        gracefully in this case by showing the event index and text.  */
     624         2644 :     if (!can_print_source_p (dc))
     625              :       {
     626          686 :         for (unsigned i = m_start_idx; i <= m_end_idx; i++)
     627              :           {
     628          405 :             const event &iter_event = m_path.get_event (i);
     629          405 :             location_t event_loc = iter_event.get_location ();
     630          405 :             if (get_pure_location (event_loc) > BUILTINS_LOCATION)
     631              :               {
     632              :                 // TODO: this implicitly uses "line_table"
     633            0 :                 gcc_assert (line_table);
     634            0 :                 expanded_location exploc (expand_location (event_loc));
     635              : 
     636            0 :                 pp_string (&pp, text_output.get_location_text (exploc).get ());
     637              :               }
     638          405 :             diagnostic_event_id_t event_id (i);
     639          405 :             pp_printf (&pp, " %@: ", &event_id);
     640          405 :             iter_event.print_desc (pp);
     641          405 :             pp_newline (&pp);
     642              :           }
     643              :         return;
     644              :       }
     645              : 
     646              :     /* Call diagnostic_show_locus to show the events using labels.  */
     647         2363 :     diagnostic_show_locus (&dc, text_output.get_source_printing_options (),
     648              :                            &m_richloc, diagnostics::kind::path, &pp,
     649              :                            effect_info);
     650              : 
     651              :     /* If we have a macro expansion, show the expansion to the user.  */
     652         2363 :     if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
     653              :       {
     654           44 :         gcc_assert (m_start_idx == m_end_idx);
     655           44 :         maybe_unwind_expanded_macro_loc (text_output, initial_loc);
     656              :       }
     657              :   }
     658              : 
     659              :   /* Print the events in this range to XP, typically as a single
     660              :      call to diagnostic_show_locus_as_html.  */
     661              : 
     662            7 :   void print_as_html (xml::printer &xp,
     663              :                       diagnostics::context &dc,
     664              :                       diagnostics::source_effect_info *effect_info,
     665              :                       html_label_writer *event_label_writer)
     666              :   {
     667            7 :     location_t initial_loc = m_initial_event.get_location ();
     668              : 
     669              :     /* Emit a span indicating the filename (and line/column) if the
     670              :        line has changed relative to the last call to
     671              :        diagnostic_show_locus.  */
     672            7 :     if (dc.get_source_printing_options ().enabled)
     673              :       {
     674            7 :         expanded_location exploc
     675              :           = linemap_client_expand_location_to_spelling_point
     676            7 :           (line_table, initial_loc, location_aspect::caret);
     677            7 :         if (exploc.file != LOCATION_FILE (dc.m_last_location))
     678              :           {
     679            1 :             diagnostics::location_print_policy loc_policy (dc);
     680            1 :             loc_policy.print_html_span_start (dc, xp, exploc);
     681              :           }
     682              :       }
     683              : 
     684              :     /* Ideally we will print events as labelled ranges within the
     685              :        quoted source.  But if there is no source, or we can't find it,
     686              :        we need a fallback, or the events won't show up. Fail more
     687              :        gracefully in this case by showing the event index and text.  */
     688            7 :     if (!can_print_source_p (dc))
     689              :       {
     690            2 :         for (unsigned i = m_start_idx; i <= m_end_idx; i++)
     691              :           {
     692            1 :             const event &iter_event = m_path.get_event (i);
     693              : 
     694            1 :             xml::auto_print_element p (xp, "p");
     695              : 
     696            1 :             if (event_label_writer)
     697            1 :               event_label_writer->begin_label ();
     698              : 
     699            1 :             location_t event_loc = iter_event.get_location ();
     700            1 :             if (get_pure_location (event_loc) > BUILTINS_LOCATION)
     701              :               {
     702              :                 // TODO: this implicitly uses "line_table"
     703            0 :                 gcc_assert (line_table);
     704            0 :                 expanded_location exploc (expand_location (event_loc));
     705              : 
     706            0 :                 location_print_policy policy (dc);
     707            0 :                 policy.print_html_span_start (dc, xp, exploc);
     708              :               }
     709            1 :             {
     710            1 :               diagnostic_event_id_t event_id (i);
     711            1 :               pretty_printer pp;
     712            1 :               pp_printf (&pp, " %@: ", &event_id);
     713            1 :               xp.push_tag_with_class ("span", "event-id");
     714            1 :               xp.add_text_from_pp (pp);
     715            1 :               xp.pop_tag ("span");
     716            1 :             }
     717            1 :             {
     718            1 :               pretty_printer pp;
     719            1 :               iter_event.print_desc (pp);
     720            1 :               xp.push_tag_with_class ("span", "event-text");
     721            1 :               xp.add_text_from_pp (pp);
     722            1 :               xp.pop_tag ("span");
     723            1 :             }
     724            1 :             if (event_label_writer)
     725            1 :               event_label_writer->end_label ();
     726            1 :           }
     727              :         return;
     728              :       }
     729              : 
     730              :     /* Call diagnostic_show_locus_as_html to show the source,
     731              :        showing events using labels.  */
     732            6 :     diagnostic_show_locus_as_html (&dc, dc.get_source_printing_options (),
     733              :                                    &m_richloc, diagnostics::kind::path, xp,
     734              :                                    effect_info, event_label_writer);
     735              : 
     736              :     // TODO: show macro expansions
     737              :   }
     738              : 
     739              :   const path &m_path;
     740              :   const event &m_initial_event;
     741              :   logical_locations::key m_logical_loc;
     742              :   int m_stack_depth;
     743              :   unsigned m_start_idx;
     744              :   unsigned m_end_idx;
     745              :   path_label m_path_label;
     746              :   gcc_rich_location m_richloc;
     747              :   thread_id_t m_thread_id;
     748              :   per_thread_summary &m_per_thread_summary;
     749              :   hash_map<int_hash<int, -1, -2>,
     750              :            per_source_line_info> m_source_line_info_map;
     751              :   bool m_show_event_links;
     752              : 
     753              : private:
     754              :   /* Return true if we can print source code for the primary location
     755              :      for the initial event.
     756              : 
     757              :      If we can't then the labels won't be printed, and thus we'll have
     758              :      to fall back to printing the events directly for them to be
     759              :      printed.  */
     760         2651 :   bool can_print_source_p (diagnostics::context &dc) const
     761              :   {
     762         2651 :     location_t initial_loc = m_initial_event.get_location ();
     763         2651 :     if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
     764              :       return false;
     765              : 
     766              :     // TODO: this implicitly uses "line_table"
     767         2369 :     expanded_location exploc (expand_location (initial_loc));
     768              : 
     769         2369 :     auto line_content
     770         2369 :       = dc.get_file_cache ().get_source_line (exploc.file, exploc.line);
     771         2369 :     if (!line_content)
     772              :       return false;
     773              : 
     774              :     return true;
     775              :   }
     776              : };
     777              : 
     778              : /* A struct for grouping together the events in a path into
     779              :    ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
     780              :    and stack depth).  */
     781              : 
     782              : struct path_summary
     783              : {
     784              :   path_summary (const path_print_policy &policy,
     785              :                 const pretty_printer &ref_pp,
     786              :                 const path &path_,
     787              :                 bool check_rich_locations,
     788              :                 bool colorize = false,
     789              :                 bool show_event_links = true);
     790              : 
     791         2644 :   const logical_locations::manager &get_logical_location_manager () const
     792              :   {
     793         2644 :     return m_logical_loc_mgr;
     794              :   }
     795           40 :   unsigned get_num_ranges () const { return m_ranges.length (); }
     796         7932 :   bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
     797              : 
     798              :   const per_thread_summary &get_events_for_thread_id (thread_id_t tid)
     799              :   {
     800              :     per_thread_summary **slot = m_thread_id_to_events.get (tid);
     801              :     gcc_assert (slot);
     802              :     gcc_assert (*slot);
     803              :     return **slot;
     804              :   }
     805              : 
     806              :   const logical_locations::manager &m_logical_loc_mgr;
     807              :   auto_delete_vec <event_range> m_ranges;
     808              :   auto_delete_vec <per_thread_summary> m_per_thread_summary;
     809              :   hash_map<int_hash<thread_id_t, -1, -2>,
     810              :            per_thread_summary *> m_thread_id_to_events;
     811              : 
     812              : private:
     813              :   per_thread_summary &
     814         5075 :   get_or_create_events_for_thread_id (const path &path_,
     815              :                                       thread_id_t tid)
     816              :   {
     817         5075 :     if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
     818         3567 :       return **slot;
     819              : 
     820         1508 :     const thread &thread = path_.get_thread (tid);
     821         1508 :     per_thread_summary *pts
     822              :       = new per_thread_summary (path_,
     823              :                                 m_logical_loc_mgr,
     824         3016 :                                 thread.get_name (false),
     825         1509 :                                 m_per_thread_summary.length ());
     826         1508 :     m_thread_id_to_events.put (tid, pts);
     827         1508 :     m_per_thread_summary.safe_push (pts);
     828         1508 :     return *pts;
     829              :   }
     830              : };
     831              : 
     832              : /* Return true iff there is more than one stack frame used by the events
     833              :    of this thread.  */
     834              : 
     835              : bool
     836         2644 : per_thread_summary::interprocedural_p () const
     837              : {
     838         4677 :   if (m_event_ranges.is_empty ())
     839              :     return false;
     840         2644 :   int first_stack_depth = m_event_ranges[0]->m_stack_depth;
     841         6962 :   for (auto range : m_event_ranges)
     842              :     {
     843         4929 :       if (!m_path.same_function_p (m_event_ranges[0]->m_start_idx,
     844         4929 :                                    range->m_start_idx))
     845              :           return true;
     846         4373 :       if (range->m_stack_depth != first_stack_depth)
     847              :         return true;
     848              :     }
     849              :   return false;
     850              : }
     851              : 
     852              : /* path_summary's ctor.  */
     853              : 
     854         1516 : path_summary::path_summary (const path_print_policy &policy,
     855              :                             const pretty_printer &ref_pp,
     856              :                             const path &path_,
     857              :                             bool check_rich_locations,
     858              :                             bool colorize,
     859         1516 :                             bool show_event_links)
     860         1516 : : m_logical_loc_mgr (path_.get_logical_location_manager ())
     861              : {
     862         1516 :   const unsigned num_events = path_.num_events ();
     863              : 
     864         1516 :   event_range *cur_event_range = nullptr;
     865         6591 :   for (unsigned idx = 0; idx < num_events; idx++)
     866              :     {
     867         5075 :       const event &ev = path_.get_event (idx);
     868         5075 :       const thread_id_t thread_id = ev.get_thread_id ();
     869         5075 :       per_thread_summary &pts
     870         5075 :         = get_or_create_events_for_thread_id (path_, thread_id);
     871              : 
     872         5075 :       pts.update_depth_limits (ev.get_stack_depth ());
     873              : 
     874         5075 :       if (cur_event_range)
     875         3568 :         if (cur_event_range->maybe_add_event (policy,
     876              :                                               ev,
     877              :                                               idx, check_rich_locations))
     878         2424 :           continue;
     879              : 
     880         2651 :       auto theme = policy.get_diagram_theme ();
     881         2651 :       const bool allow_emojis = theme ? theme->emojis_p () : false;
     882         5302 :       cur_event_range = new event_range (path_, ref_pp,
     883              :                                          idx, ev, pts,
     884              :                                          show_event_links,
     885              :                                          colorize,
     886         2651 :                                          allow_emojis);
     887         2651 :       m_ranges.safe_push (cur_event_range);
     888         2651 :       pts.m_event_ranges.safe_push (cur_event_range);
     889         2651 :       pts.m_last_event = &ev;
     890              :     }
     891         1516 : }
     892              : 
     893              : /* Write SPACES to PP.  */
     894              : 
     895              : static void
     896         4697 : write_indent (pretty_printer *pp, int spaces)
     897              : {
     898        32127 :   for (int i = 0; i < spaces; i++)
     899        27430 :     pp_space (pp);
     900            0 : }
     901              : 
     902              : static const int base_indent = 2;
     903              : static const int per_frame_indent = 2;
     904              : 
     905              : /* A bundle of state for printing event_range instances for a particular
     906              :    thread.  */
     907              : 
     908         1509 : class thread_event_printer
     909              : {
     910              : public:
     911         1508 :   thread_event_printer (const per_thread_summary &t, bool show_depths)
     912         1508 :   : m_per_thread_summary (t),
     913         1508 :     m_show_depths (show_depths),
     914         1508 :     m_cur_indent (base_indent),
     915         1508 :     m_vbar_column_for_depth (),
     916         1508 :     m_num_printed (0)
     917              :   {
     918         1508 :   }
     919              : 
     920              :   /* Get the previous event_range within this thread, if any.  */
     921         2644 :   const event_range *get_any_prev_range () const
     922              :   {
     923         2644 :     if (m_num_printed > 0)
     924         1139 :       return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
     925              :     else
     926              :       return nullptr;
     927              :   }
     928              : 
     929              :   /* Get the next event_range within this thread, if any.  */
     930         2644 :   const event_range *get_any_next_range () const
     931              :   {
     932         5288 :     if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
     933         1139 :       return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
     934              :     else
     935              :       return nullptr;
     936              :   }
     937              : 
     938              :   void
     939         2644 :   print_swimlane_for_event_range_as_text (diagnostics::text_sink &text_output,
     940              :                                           pretty_printer *pp,
     941              :                                           const logical_locations::manager &logical_loc_mgr,
     942              :                                           event_range *range,
     943              :                                           diagnostics::source_effect_info *effect_info)
     944              :   {
     945         2644 :     gcc_assert (pp);
     946         2644 :     const char *const line_color = "path";
     947         2644 :     const char *start_line_color
     948         2644 :       = colorize_start (pp_show_color (pp), line_color);
     949         2644 :     const char *end_line_color = colorize_stop (pp_show_color (pp));
     950              : 
     951         2644 :     text_art::ascii_theme fallback_theme;
     952         2644 :     text_art::theme *theme = text_output.get_diagram_theme ();
     953         2644 :     if (!theme)
     954          588 :       theme = &fallback_theme;
     955              : 
     956         2644 :     cppchar_t depth_marker_char = theme->get_cppchar
     957         2644 :       (text_art::theme::cell_kind::INTERPROCEDURAL_DEPTH_MARKER);
     958              :     /* e.g. "|".  */
     959              : 
     960         2644 :     const bool interprocedural_p = m_per_thread_summary.interprocedural_p ();
     961              : 
     962         2644 :     write_indent (pp, m_cur_indent);
     963         2644 :     if (const event_range *prev_range = get_any_prev_range ())
     964              :       {
     965         1139 :         if (range->m_stack_depth > prev_range->m_stack_depth)
     966              :           {
     967          322 :             gcc_assert (interprocedural_p);
     968              :             /* Show pushed stack frame(s).  */
     969          322 :             cppchar_t left = theme->get_cppchar
     970          322 :               (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_LEFT);
     971          322 :             cppchar_t middle = theme->get_cppchar
     972          322 :               (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_MIDDLE);
     973          322 :             cppchar_t right = theme->get_cppchar
     974          322 :               (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_RIGHT);
     975              :             /* e.g. "+--> ".  */
     976          322 :             pp_string (pp, start_line_color);
     977          322 :             pp_unicode_character (pp, left);
     978          322 :             pp_unicode_character (pp, middle);
     979          322 :             pp_unicode_character (pp, middle);
     980          322 :             pp_unicode_character (pp, right);
     981          322 :             pp_space (pp);
     982          322 :             pp_string (pp, end_line_color);
     983          322 :             m_cur_indent += 5;
     984              :           }
     985              :       }
     986         2644 :     if (range->m_logical_loc)
     987              :       {
     988          745 :         label_text name
     989          745 :           (logical_loc_mgr.get_name_for_path_output (range->m_logical_loc));
     990          745 :         if (name.get ())
     991          745 :           pp_printf (pp, "%qs: ", name.get ());
     992          745 :       }
     993         2644 :     if (range->m_start_idx == range->m_end_idx)
     994         1103 :       pp_printf (pp, "event %i",
     995              :                  range->m_start_idx + 1);
     996              :     else
     997         1541 :       pp_printf (pp, "events %i-%i",
     998              :                  range->m_start_idx + 1, range->m_end_idx + 1);
     999         2644 :     if (m_show_depths)
    1000          325 :       pp_printf (pp, " (depth %i)", range->m_stack_depth);
    1001         2644 :     pp_newline (pp);
    1002              : 
    1003              :     /* Print a run of events.  */
    1004         2644 :     if (interprocedural_p)
    1005              :       {
    1006          611 :         write_indent (pp, m_cur_indent + per_frame_indent);
    1007          611 :         pp_string (pp, start_line_color);
    1008          611 :         pp_unicode_character (pp, depth_marker_char);
    1009          611 :         pp_string (pp, end_line_color);
    1010          611 :         pp_newline (pp);
    1011              : 
    1012          611 :         char *saved_prefix = pp_take_prefix (pp);
    1013          611 :         char *prefix;
    1014          611 :         {
    1015          611 :           pretty_printer tmp_pp;
    1016          611 :           write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
    1017          611 :           pp_string (&tmp_pp, start_line_color);
    1018          611 :           pp_unicode_character (&tmp_pp, depth_marker_char);
    1019          611 :           pp_string (&tmp_pp, end_line_color);
    1020          611 :           prefix = xstrdup (pp_formatted_text (&tmp_pp));
    1021          611 :         }
    1022          611 :         pp_set_prefix (pp, prefix);
    1023          611 :         pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
    1024          611 :         range->print_as_text (*pp, text_output, effect_info);
    1025          611 :         pp_set_prefix (pp, saved_prefix);
    1026              : 
    1027          611 :         write_indent (pp, m_cur_indent + per_frame_indent);
    1028          611 :         pp_string (pp, start_line_color);
    1029          611 :         pp_unicode_character (pp, depth_marker_char);
    1030          611 :         pp_string (pp, end_line_color);
    1031          611 :         pp_newline (pp);
    1032              :       }
    1033              :     else
    1034         2033 :       range->print_as_text (*pp, text_output, effect_info);
    1035              : 
    1036         2644 :     if (const event_range *next_range = get_any_next_range ())
    1037              :       {
    1038         1139 :         if (range->m_stack_depth > next_range->m_stack_depth)
    1039              :           {
    1040          113 :             if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
    1041              :               {
    1042              :                 /* Show returning from stack frame(s), by printing
    1043              :                    something like:
    1044              :                    "                   |\n"
    1045              :                    "     <-------------+\n"
    1046              :                    "     |\n".  */
    1047          110 :                 gcc_assert (interprocedural_p);
    1048          110 :                 cppchar_t left = theme->get_cppchar
    1049          110 :                   (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_LEFT);
    1050          110 :                 cppchar_t middle = theme->get_cppchar
    1051          110 :                   (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_MIDDLE);
    1052          110 :                 cppchar_t right = theme->get_cppchar
    1053          110 :                   (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT);
    1054          110 :                 int vbar_for_next_frame
    1055          110 :                   = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
    1056              : 
    1057          110 :                 int indent_for_next_frame
    1058              :                   = vbar_for_next_frame - per_frame_indent;
    1059          110 :                 write_indent (pp, vbar_for_next_frame);
    1060          110 :                 pp_string (pp, start_line_color);
    1061          110 :                 pp_unicode_character (pp, left);
    1062         1022 :                 for (int i = indent_for_next_frame + per_frame_indent;
    1063         1022 :                      i < m_cur_indent + per_frame_indent - 1; i++)
    1064          912 :                   pp_unicode_character (pp, middle);
    1065          110 :                 pp_unicode_character (pp, right);
    1066          110 :                 pp_string (pp, end_line_color);
    1067          110 :                 pp_newline (pp);
    1068          110 :                 m_cur_indent = indent_for_next_frame;
    1069              : 
    1070          110 :                 write_indent (pp, vbar_for_next_frame);
    1071          110 :                 pp_string (pp, start_line_color);
    1072          110 :                 pp_unicode_character (pp, depth_marker_char);
    1073          110 :                 pp_string (pp, end_line_color);
    1074          110 :                 pp_newline (pp);
    1075              :               }
    1076              :             else
    1077              :               {
    1078              :                 /* Handle disjoint paths (e.g. a callback at some later
    1079              :                    time).  */
    1080            3 :                 m_cur_indent = base_indent;
    1081              :               }
    1082              :           }
    1083         1026 :         else if (range->m_stack_depth < next_range->m_stack_depth)
    1084              :           {
    1085              :             /* Prepare to show pushed stack frame.  */
    1086          322 :             gcc_assert (interprocedural_p);
    1087          322 :             gcc_assert (range->m_stack_depth != EMPTY);
    1088          322 :             gcc_assert (range->m_stack_depth != DELETED);
    1089          322 :             m_vbar_column_for_depth.put (range->m_stack_depth,
    1090          322 :                                          m_cur_indent + per_frame_indent);
    1091          322 :             m_cur_indent += per_frame_indent;
    1092              :           }
    1093              :       }
    1094              : 
    1095         2644 :     m_num_printed++;
    1096         2644 :   }
    1097              : 
    1098              :   void
    1099            7 :   print_swimlane_for_event_range_as_html (diagnostics::context &dc,
    1100              :                                           xml::printer &xp,
    1101              :                                           html_label_writer *event_label_writer,
    1102              :                                           event_range *range,
    1103              :                                           diagnostics::source_effect_info *effect_info)
    1104              :   {
    1105            7 :     range->print_as_html (xp, dc, effect_info, event_label_writer);
    1106            7 :     m_num_printed++;
    1107              :   }
    1108              : 
    1109              :   int get_cur_indent () const { return m_cur_indent; }
    1110              : 
    1111              : private:
    1112              :   const per_thread_summary &m_per_thread_summary;
    1113              :   bool m_show_depths;
    1114              : 
    1115              :   /* Print the ranges.  */
    1116              :   int m_cur_indent;
    1117              : 
    1118              :   /* Keep track of column numbers of existing '|' characters for
    1119              :      stack depths we've already printed.  */
    1120              :   static const int EMPTY = -1;
    1121              :   static const int DELETED = -2;
    1122              :   typedef int_hash <int, EMPTY, DELETED> vbar_hash;
    1123              :   hash_map <vbar_hash, int> m_vbar_column_for_depth;
    1124              : 
    1125              :   /* How many event ranges within this swimlane have we printed.
    1126              :      This is the index of the next event_range to print.  */
    1127              :   unsigned  m_num_printed;
    1128              : };
    1129              : 
    1130              : /* Print path_summary PS to TEXT_OUTPUT, giving an overview of the
    1131              :    interprocedural calls and returns.
    1132              : 
    1133              :    Print the event descriptions in a nested form, printing the event
    1134              :    descriptions within calls to diagnostic_show_locus, using labels to
    1135              :    show the events:
    1136              : 
    1137              :    'foo' (events 1-2)
    1138              :      | NN |
    1139              :      |    |
    1140              :      +--> 'bar' (events 3-4)
    1141              :             | NN |
    1142              :             |    |
    1143              :             +--> 'baz' (events 5-6)
    1144              :                    | NN |
    1145              :                    |    |
    1146              :      <------------ +
    1147              :      |
    1148              :    'foo' (events 7-8)
    1149              :      | NN |
    1150              :      |    |
    1151              :      +--> 'bar' (events 9-10)
    1152              :             | NN |
    1153              :             |    |
    1154              :             +--> 'baz' (events 11-12)
    1155              :                    | NN |
    1156              :                    |    |
    1157              : 
    1158              :    If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
    1159              :    of events.
    1160              : 
    1161              :    For events with UNKNOWN_LOCATION, print a summary of each the event.  */
    1162              : 
    1163              : static void
    1164         1513 : print_path_summary_as_text (const path_summary &ps,
    1165              :                             diagnostics::text_sink &text_output,
    1166              :                             bool show_depths)
    1167              : {
    1168         1513 :   pretty_printer *const pp = text_output.get_printer ();
    1169              : 
    1170         1513 :   std::vector<thread_event_printer> thread_event_printers;
    1171         6026 :   for (auto t : ps.m_per_thread_summary)
    1172         1505 :     thread_event_printers.push_back (thread_event_printer (*t, show_depths));
    1173              : 
    1174              :   unsigned i;
    1175              :   event_range *range;
    1176              :   int last_out_edge_column = -1;
    1177         4157 :   FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
    1178              :     {
    1179         2644 :       const int swimlane_idx
    1180         2644 :         = range->m_per_thread_summary.get_swimlane_index ();
    1181         2644 :       if (ps.multithreaded_p ())
    1182            6 :         if (i == 0 || ps.m_ranges[i - 1]->m_thread_id != range->m_thread_id)
    1183              :           {
    1184            4 :             if (i > 0)
    1185            3 :               pp_newline (pp);
    1186            4 :             pp_printf (pp, "Thread: %qs",
    1187            4 :                        range->m_per_thread_summary.get_name ());
    1188            4 :             pp_newline (pp);
    1189              :           }
    1190         2644 :       thread_event_printer &tep = thread_event_printers[swimlane_idx];
    1191              :       /* Wire up any trailing out-edge from previous range to leading in-edge
    1192              :          of this range.  */
    1193         2644 :       diagnostics::source_effect_info effect_info;
    1194         2644 :       effect_info.m_leading_in_edge_column = last_out_edge_column;
    1195         2644 :       tep.print_swimlane_for_event_range_as_text
    1196         2644 :         (text_output, pp,
    1197              :          ps.get_logical_location_manager (),
    1198              :          range, &effect_info);
    1199         2644 :       last_out_edge_column = effect_info.m_trailing_out_edge_column;
    1200              :     }
    1201         1513 : }
    1202              : 
    1203              : /* Print PS as HTML to XP, using DC and, if non-null EVENT_LABEL_WRITER.  */
    1204              : 
    1205              : static void
    1206            3 : print_path_summary_as_html (const path_summary &ps,
    1207              :                             diagnostics::context &dc,
    1208              :                             xml::printer &xp,
    1209              :                             html_label_writer *event_label_writer,
    1210              :                             bool show_depths)
    1211              : {
    1212            3 :   std::vector<thread_event_printer> thread_event_printers;
    1213           12 :   for (auto t : ps.m_per_thread_summary)
    1214            3 :     thread_event_printers.push_back (thread_event_printer (*t, show_depths));
    1215              : 
    1216            3 :   const logical_locations::manager *logical_loc_mgr
    1217            3 :     = dc.get_logical_location_manager ();
    1218              : 
    1219            3 :   xp.push_tag_with_class ("div", "event-ranges", false);
    1220              : 
    1221              :   /* Group the ranges into stack frames.  */
    1222            3 :   std::unique_ptr<stack_frame> curr_frame;
    1223            3 :   unsigned i;
    1224            3 :   event_range *range;
    1225            3 :   int last_out_edge_column = -1;
    1226           10 :   FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
    1227              :     {
    1228            7 :       const int swimlane_idx
    1229            7 :         = range->m_per_thread_summary.get_swimlane_index ();
    1230              : 
    1231            7 :       const logical_locations::key this_logical_loc = range->m_logical_loc;
    1232            7 :       const int this_depth = range->m_stack_depth;
    1233            7 :       if (curr_frame)
    1234              :         {
    1235            4 :           int old_stack_depth = curr_frame->m_stack_depth;
    1236            4 :           if (this_depth > curr_frame->m_stack_depth)
    1237              :             {
    1238            3 :               emit_svg_arrow (xp, old_stack_depth, this_depth);
    1239            3 :               curr_frame
    1240            6 :                 = begin_html_stack_frame (xp,
    1241              :                                           std::move (curr_frame),
    1242              :                                           range->m_logical_loc,
    1243              :                                           range->m_stack_depth,
    1244            3 :                                           logical_loc_mgr);
    1245              :             }
    1246              :           else
    1247              :             {
    1248            2 :               while (this_depth < curr_frame->m_stack_depth
    1249            2 :                      || this_logical_loc != curr_frame->m_logical_loc)
    1250              :                 {
    1251            2 :                   curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
    1252            2 :                   if (curr_frame == nullptr)
    1253              :                     {
    1254            1 :                       curr_frame
    1255            1 :                         = begin_html_stack_frame (xp,
    1256            2 :                                                   nullptr,
    1257              :                                                   range->m_logical_loc,
    1258              :                                                   range->m_stack_depth,
    1259            1 :                                                   logical_loc_mgr);
    1260            1 :                       break;
    1261              :                     }
    1262              :                 }
    1263            1 :               emit_svg_arrow (xp, old_stack_depth, this_depth);
    1264              :             }
    1265              :         }
    1266              :       else
    1267              :         {
    1268            3 :           curr_frame = begin_html_stack_frame (xp,
    1269            6 :                                                nullptr,
    1270              :                                                range->m_logical_loc,
    1271              :                                                range->m_stack_depth,
    1272            3 :                                                logical_loc_mgr);
    1273              :         }
    1274              : 
    1275            7 :       xp.push_tag_with_class ("table", "event-range-with-margin", false);
    1276            7 :       xp.push_tag ("tr", false);
    1277            7 :       xp.push_tag_with_class ("td", "event-range", false);
    1278            7 :       xp.push_tag_with_class ("div", "events-hdr", true);
    1279            7 :       if (range->m_logical_loc)
    1280              :         {
    1281            6 :           gcc_assert (logical_loc_mgr);
    1282            6 :           label_text funcname
    1283            6 :             = logical_loc_mgr->get_name_for_path_output (range->m_logical_loc);
    1284            6 :           if (funcname.get ())
    1285              :             {
    1286            6 :               xp.push_tag_with_class ("span", "funcname", true);
    1287            6 :               xp.add_text (funcname.get ());
    1288            6 :               xp.pop_tag ("span");
    1289            6 :               xp.add_text (": ");
    1290              :             }
    1291            6 :         }
    1292            7 :       {
    1293            7 :         xp.push_tag_with_class ("span", "event-ids", true);
    1294            7 :         pretty_printer pp;
    1295            7 :         if (range->m_start_idx == range->m_end_idx)
    1296            2 :           pp_printf (&pp, "event %i",
    1297              :                      range->m_start_idx + 1);
    1298              :         else
    1299            5 :           pp_printf (&pp, "events %i-%i",
    1300              :                      range->m_start_idx + 1, range->m_end_idx + 1);
    1301            7 :         xp.add_text_from_pp (pp);
    1302            7 :         xp.pop_tag ("span");
    1303            7 :       }
    1304            7 :       if (show_depths)
    1305              :         {
    1306            0 :           xp.add_text (" ");
    1307            0 :           xp.push_tag_with_class ("span", "depth", true);
    1308            0 :           pretty_printer pp;
    1309            0 :           pp_printf (&pp, "(depth %i)", range->m_stack_depth);
    1310            0 :           xp.add_text_from_pp (pp);
    1311            0 :           xp.pop_tag ("span");
    1312            0 :         }
    1313            7 :       xp.pop_tag ("div");
    1314              : 
    1315              :       /* Print a run of events.  */
    1316            7 :       thread_event_printer &tep = thread_event_printers[swimlane_idx];
    1317              :       /* Wire up any trailing out-edge from previous range to leading in-edge
    1318              :          of this range.  */
    1319            7 :       diagnostics::source_effect_info effect_info;
    1320            7 :       effect_info.m_leading_in_edge_column = last_out_edge_column;
    1321            7 :       tep.print_swimlane_for_event_range_as_html (dc, xp, event_label_writer,
    1322              :                                                   range, &effect_info);
    1323            7 :       last_out_edge_column = effect_info.m_trailing_out_edge_column;
    1324              : 
    1325            7 :       xp.pop_tag ("td");
    1326            7 :       xp.pop_tag ("tr");
    1327            7 :       xp.pop_tag ("table");
    1328              :     }
    1329              : 
    1330              :   /* Close outstanding frames.  */
    1331            8 :   while (curr_frame)
    1332            5 :     curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
    1333              : 
    1334            3 :   xp.pop_tag ("div");
    1335            3 : }
    1336              : 
    1337              : } /* end of anonymous namespace for path-printing code.  */
    1338              : 
    1339        14820 : class element_event_desc : public pp_element
    1340              : {
    1341              : public:
    1342        14820 :   element_event_desc (const event &event_)
    1343        14820 :   : m_event (event_)
    1344              :   {
    1345              :   }
    1346              : 
    1347        14911 :   void add_to_phase_2 (pp_markup::context &ctxt) final override
    1348              :   {
    1349        14911 :     auto pp = ctxt.m_pp.clone ();
    1350        14911 :     m_event.print_desc (*pp.get ());
    1351        14911 :     pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
    1352        14911 :   }
    1353              : 
    1354              : private:
    1355              :   const event &m_event;
    1356              : };
    1357              : 
    1358              : /* Print PATH according to the context's path_format.  */
    1359              : 
    1360              : void
    1361         9675 : diagnostics::text_sink::print_path (const path &path_)
    1362              : {
    1363         9675 :   const unsigned num_events = path_.num_events ();
    1364              : 
    1365         9675 :   switch (get_context ().get_path_format ())
    1366              :     {
    1367              :     case DPF_NONE:
    1368              :       /* Do nothing.  */
    1369              :       return;
    1370              : 
    1371         9437 :     case DPF_SEPARATE_EVENTS:
    1372         9437 :       {
    1373              :         /* A note per event.  */
    1374         9437 :         auto &logical_loc_mgr = path_.get_logical_location_manager ();
    1375        24257 :         for (unsigned i = 0; i < num_events; i++)
    1376              :           {
    1377        14820 :             const event &ev = path_.get_event (i);
    1378        14820 :             element_event_desc e_event_desc (ev);
    1379        14820 :             diagnostic_event_id_t event_id (i);
    1380        14820 :             if (get_context ().show_path_depths_p ())
    1381              :               {
    1382          172 :                 int stack_depth = ev.get_stack_depth ();
    1383              :                 /* -fdiagnostics-path-format=separate-events doesn't print
    1384              :                    fndecl information, so with -fdiagnostics-show-path-depths
    1385              :                    print the fndecls too, if any.  */
    1386          172 :                 if (logical_locations::key logical_loc
    1387          172 :                       = ev.get_logical_location ())
    1388              :                   {
    1389          172 :                     label_text name
    1390          172 :                       (logical_loc_mgr.get_name_for_path_output (logical_loc));
    1391          172 :                     inform (ev.get_location (),
    1392              :                             "%@ %e (fndecl %qs, depth %i)",
    1393              :                             &event_id, &e_event_desc,
    1394              :                             name.get (), stack_depth);
    1395          172 :                   }
    1396              :                 else
    1397            0 :                   inform (ev.get_location (),
    1398              :                           "%@ %e (depth %i)",
    1399              :                           &event_id, &e_event_desc,
    1400              :                           stack_depth);
    1401              :               }
    1402              :             else
    1403        14648 :               inform (ev.get_location (),
    1404              :                       "%@ %e", &event_id, &e_event_desc);
    1405        14820 :           }
    1406              :       }
    1407              :       break;
    1408              : 
    1409          229 :     case DPF_INLINE_EVENTS:
    1410          229 :       {
    1411              :         /* Consolidate related events.  */
    1412          229 :         path_print_policy policy (*this);
    1413          229 :         pretty_printer *const pp = get_printer ();
    1414          229 :         const bool check_rich_locations = true;
    1415          229 :         const bool colorize = pp_show_color (pp);
    1416          229 :         const bool show_event_links = m_source_printing.show_event_links_p;
    1417          229 :         path_summary summary (policy,
    1418              :                               *pp,
    1419              :                               path_,
    1420              :                               check_rich_locations,
    1421              :                               colorize,
    1422          229 :                               show_event_links);
    1423          229 :         char *saved_prefix = pp_take_prefix (pp);
    1424          229 :         pp_set_prefix (pp, nullptr);
    1425          229 :         print_path_summary_as_text (summary, *this,
    1426          229 :                                     get_context ().show_path_depths_p ());
    1427          229 :         pp_flush (pp);
    1428          229 :         pp_set_prefix (pp, saved_prefix);
    1429          229 :       }
    1430          229 :       break;
    1431              :     }
    1432              : }
    1433              : 
    1434              : /* Print PATH_ as HTML to XP, using DC and DSPP for settings.
    1435              :    If non-null, use EVENT_LABEL_WRITER when writing events.  */
    1436              : 
    1437              : void
    1438            3 : diagnostics::print_path_as_html (xml::printer &xp,
    1439              :                                  const path &path_,
    1440              :                                  context &dc,
    1441              :                                  html_label_writer *event_label_writer,
    1442              :                                  const source_print_policy &dspp)
    1443              : {
    1444            3 :   path_print_policy policy (dc);
    1445            3 :   const bool check_rich_locations = true;
    1446            3 :   const bool colorize = false;
    1447            3 :   const source_printing_options &source_printing_opts
    1448            3 :     = dspp.get_options ();
    1449            3 :   const bool show_event_links = source_printing_opts.show_event_links_p;
    1450            3 :   path_summary summary (policy,
    1451            3 :                         *dc.get_reference_printer (),
    1452              :                         path_,
    1453              :                         check_rich_locations,
    1454              :                         colorize,
    1455            3 :                         show_event_links);
    1456            3 :   print_path_summary_as_html (summary, dc, xp, event_label_writer,
    1457            3 :                               dc.show_path_depths_p ());
    1458            3 : }
    1459              : 
    1460              : #if CHECKING_P
    1461              : 
    1462              : namespace diagnostics {
    1463              : namespace paths {
    1464              : namespace selftest {
    1465              : 
    1466              : using location = ::selftest::location;
    1467              : using line_table_case = ::selftest::line_table_case;
    1468              : using line_table_test = ::selftest::line_table_test;
    1469              : using temp_source_file = ::selftest::temp_source_file;
    1470              : 
    1471              : using test_context = diagnostics::selftest::test_context;
    1472              : 
    1473              : /* Return true iff all events in PATH_ have locations for which column data
    1474              :    is available, so that selftests that require precise string output can
    1475              :    bail out for awkward line_table cases.  */
    1476              : 
    1477              : static bool
    1478         1632 : path_events_have_column_data_p (const path &path_)
    1479              : {
    1480         4388 :   for (unsigned idx = 0; idx < path_.num_events (); idx++)
    1481              :     {
    1482         3416 :       location_t event_loc = path_.get_event (idx).get_location ();
    1483         3416 :       if (line_table->get_pure_location (event_loc)
    1484              :           > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1485              :         return false;
    1486         2756 :       if (line_table->get_start (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1487              :         return false;
    1488         2756 :       if (line_table->get_finish (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
    1489              :         return false;
    1490              :     }
    1491              :   return true;
    1492              : }
    1493              : 
    1494              : /* Verify that empty paths are handled gracefully.  */
    1495              : 
    1496              : static void
    1497            4 : test_empty_path (pretty_printer *event_pp)
    1498              : {
    1499            4 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1500            4 :   test_path path (logical_loc_mgr, event_pp);
    1501            4 :   ASSERT_FALSE (path.interprocedural_p ());
    1502              : 
    1503            4 :   test_context dc;
    1504            4 :   text_sink text_output (dc);
    1505            4 :   path_print_policy policy (text_output);
    1506            4 :   path_summary summary (policy, *event_pp, path, false);
    1507            4 :   ASSERT_EQ (summary.get_num_ranges (), 0);
    1508              : 
    1509            4 :   print_path_summary_as_text (summary, text_output, true);
    1510            4 :   ASSERT_STREQ ("",
    1511              :                 pp_formatted_text (text_output.get_printer ()));
    1512            4 : }
    1513              : 
    1514              : /* Verify that print_path_summary works on a purely intraprocedural path.  */
    1515              : 
    1516              : static void
    1517            4 : test_intraprocedural_path (pretty_printer *event_pp)
    1518              : {
    1519            4 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1520            4 :   test_path path (logical_loc_mgr, event_pp);
    1521            4 :   const char *const funcname = "foo";
    1522            4 :   path.add_event (UNKNOWN_LOCATION, funcname, 0, "first %qs", "free");
    1523            4 :   path.add_event (UNKNOWN_LOCATION, funcname, 0, "double %qs", "free");
    1524              : 
    1525            4 :   ASSERT_FALSE (path.interprocedural_p ());
    1526              : 
    1527            4 :   selftest::test_context dc;
    1528            4 :   text_sink text_output (dc);
    1529            4 :   path_print_policy policy (text_output);
    1530            4 :   path_summary summary (policy, *event_pp, path, false, false, false);
    1531            4 :   ASSERT_EQ (summary.get_num_ranges (), 1);
    1532              : 
    1533            4 :   print_path_summary_as_text (summary, text_output, true);
    1534            4 :   ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n"
    1535              :                 " (1): first `free'\n"
    1536              :                 " (2): double `free'\n",
    1537              :                 pp_formatted_text (text_output.get_printer ()));
    1538            4 : }
    1539              : 
    1540              : /* Verify that print_path_summary works on an interprocedural path.  */
    1541              : 
    1542              : static void
    1543            4 : test_interprocedural_path_1 (pretty_printer *event_pp)
    1544              : {
    1545            4 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1546            4 :   test_path path (logical_loc_mgr, event_pp);
    1547            4 :   path.add_entry ("test", 0);
    1548            4 :   path.add_call ("test", 0, "make_boxed_int");
    1549            4 :   path.add_call ("make_boxed_int", 1, "wrapped_malloc");
    1550            4 :   path.add_event (UNKNOWN_LOCATION,
    1551              :                   "wrapped_malloc", 2, "calling malloc");
    1552            4 :   path.add_return ("test", 0);
    1553            4 :   path.add_call ("test", 0, "free_boxed_int");
    1554            4 :   path.add_call ("free_boxed_int", 1, "wrapped_free");
    1555            4 :   path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
    1556            4 :   path.add_return ("test", 0);
    1557            4 :   path.add_call ("test", 0, "free_boxed_int");
    1558            4 :   path.add_call ("free_boxed_int", 1, "wrapped_free");
    1559            4 :   path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
    1560            4 :   ASSERT_EQ (path.num_events (), 18);
    1561              : 
    1562            4 :   ASSERT_TRUE (path.interprocedural_p ());
    1563              : 
    1564            4 :   {
    1565            4 :     selftest::test_context dc;
    1566            4 :     text_sink text_output (dc, nullptr, false);
    1567            4 :     path_print_policy policy (text_output);
    1568            4 :     path_summary summary (policy, *event_pp, path, false);
    1569            4 :     ASSERT_EQ (summary.get_num_ranges (), 9);
    1570              : 
    1571            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1572            4 :     print_path_summary_as_text (summary, text_output, true);
    1573            4 :     ASSERT_STREQ
    1574              :       ("  `test': events 1-2 (depth 0)\n"
    1575              :        "    |\n"
    1576              :        "    | (1): entering `test'\n"
    1577              :        "    | (2): calling `make_boxed_int'\n"
    1578              :        "    |\n"
    1579              :        "    +--> `make_boxed_int': events 3-4 (depth 1)\n"
    1580              :        "           |\n"
    1581              :        "           | (3): entering `make_boxed_int'\n"
    1582              :        "           | (4): calling `wrapped_malloc'\n"
    1583              :        "           |\n"
    1584              :        "           +--> `wrapped_malloc': events 5-6 (depth 2)\n"
    1585              :        "                  |\n"
    1586              :        "                  | (5): entering `wrapped_malloc'\n"
    1587              :        "                  | (6): calling malloc\n"
    1588              :        "                  |\n"
    1589              :        "    <-------------+\n"
    1590              :        "    |\n"
    1591              :        "  `test': events 7-8 (depth 0)\n"
    1592              :        "    |\n"
    1593              :        "    | (7): returning to `test'\n"
    1594              :        "    | (8): calling `free_boxed_int'\n"
    1595              :        "    |\n"
    1596              :        "    +--> `free_boxed_int': events 9-10 (depth 1)\n"
    1597              :        "           |\n"
    1598              :        "           | (9): entering `free_boxed_int'\n"
    1599              :        "           | (10): calling `wrapped_free'\n"
    1600              :        "           |\n"
    1601              :        "           +--> `wrapped_free': events 11-12 (depth 2)\n"
    1602              :        "                  |\n"
    1603              :        "                  | (11): entering `wrapped_free'\n"
    1604              :        "                  | (12): calling free\n"
    1605              :        "                  |\n"
    1606              :        "    <-------------+\n"
    1607              :        "    |\n"
    1608              :        "  `test': events 13-14 (depth 0)\n"
    1609              :        "    |\n"
    1610              :        "    | (13): returning to `test'\n"
    1611              :        "    | (14): calling `free_boxed_int'\n"
    1612              :        "    |\n"
    1613              :        "    +--> `free_boxed_int': events 15-16 (depth 1)\n"
    1614              :        "           |\n"
    1615              :        "           | (15): entering `free_boxed_int'\n"
    1616              :        "           | (16): calling `wrapped_free'\n"
    1617              :        "           |\n"
    1618              :        "           +--> `wrapped_free': events 17-18 (depth 2)\n"
    1619              :        "                  |\n"
    1620              :        "                  | (17): entering `wrapped_free'\n"
    1621              :        "                  | (18): calling free\n"
    1622              :        "                  |\n",
    1623              :        pp_formatted_text (text_output.get_printer ()));
    1624            4 :   }
    1625            4 :   {
    1626            4 :     selftest::test_context dc;
    1627            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
    1628            4 :     text_sink text_output (dc);
    1629            4 :     path_print_policy policy (text_output);
    1630            4 :     path_summary summary (policy, *event_pp, path, false);
    1631            4 :     print_path_summary_as_text (summary, text_output, true);
    1632            4 :     ASSERT_STREQ
    1633              :       ("  `test': events 1-2 (depth 0)\n"
    1634              :        "    │\n"
    1635              :        "    │ (1): entering `test'\n"
    1636              :        "    │ (2): calling `make_boxed_int'\n"
    1637              :        "    │\n"
    1638              :        "    └──> `make_boxed_int': events 3-4 (depth 1)\n"
    1639              :        "           │\n"
    1640              :        "           │ (3): entering `make_boxed_int'\n"
    1641              :        "           │ (4): calling `wrapped_malloc'\n"
    1642              :        "           │\n"
    1643              :        "           └──> `wrapped_malloc': events 5-6 (depth 2)\n"
    1644              :        "                  │\n"
    1645              :        "                  │ (5): entering `wrapped_malloc'\n"
    1646              :        "                  │ (6): calling malloc\n"
    1647              :        "                  │\n"
    1648              :        "    <─────────────┘\n"
    1649              :        "    │\n"
    1650              :        "  `test': events 7-8 (depth 0)\n"
    1651              :        "    │\n"
    1652              :        "    │ (7): returning to `test'\n"
    1653              :        "    │ (8): calling `free_boxed_int'\n"
    1654              :        "    │\n"
    1655              :        "    └──> `free_boxed_int': events 9-10 (depth 1)\n"
    1656              :        "           │\n"
    1657              :        "           │ (9): entering `free_boxed_int'\n"
    1658              :        "           │ (10): calling `wrapped_free'\n"
    1659              :        "           │\n"
    1660              :        "           └──> `wrapped_free': events 11-12 (depth 2)\n"
    1661              :        "                  │\n"
    1662              :        "                  │ (11): entering `wrapped_free'\n"
    1663              :        "                  │ (12): calling free\n"
    1664              :        "                  │\n"
    1665              :        "    <─────────────┘\n"
    1666              :        "    │\n"
    1667              :        "  `test': events 13-14 (depth 0)\n"
    1668              :        "    │\n"
    1669              :        "    │ (13): returning to `test'\n"
    1670              :        "    │ (14): calling `free_boxed_int'\n"
    1671              :        "    │\n"
    1672              :        "    └──> `free_boxed_int': events 15-16 (depth 1)\n"
    1673              :        "           │\n"
    1674              :        "           │ (15): entering `free_boxed_int'\n"
    1675              :        "           │ (16): calling `wrapped_free'\n"
    1676              :        "           │\n"
    1677              :        "           └──> `wrapped_free': events 17-18 (depth 2)\n"
    1678              :        "                  │\n"
    1679              :        "                  │ (17): entering `wrapped_free'\n"
    1680              :        "                  │ (18): calling free\n"
    1681              :        "                  │\n",
    1682              :        pp_formatted_text (text_output.get_printer ()));
    1683            4 :   }
    1684              : 
    1685            4 : }
    1686              : 
    1687              : /* Example where we pop the stack to an intermediate frame, rather than the
    1688              :    initial one.  */
    1689              : 
    1690              : static void
    1691            4 : test_interprocedural_path_2 (pretty_printer *event_pp)
    1692              : {
    1693            4 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1694            4 :   test_path path (logical_loc_mgr, event_pp);
    1695            4 :   path.add_entry ("foo", 0);
    1696            4 :   path.add_call ("foo", 0, "bar");
    1697            4 :   path.add_call ("bar", 1, "baz");
    1698            4 :   path.add_return ("bar", 1);
    1699            4 :   path.add_call ("bar", 1, "baz");
    1700            4 :   ASSERT_EQ (path.num_events (), 8);
    1701              : 
    1702            4 :   ASSERT_TRUE (path.interprocedural_p ());
    1703              : 
    1704            4 :   {
    1705            4 :     selftest::test_context dc;
    1706            4 :     text_sink text_output (dc);
    1707            4 :     path_print_policy policy (text_output);
    1708            4 :     path_summary summary (policy, *event_pp, path, false);
    1709            4 :     ASSERT_EQ (summary.get_num_ranges (), 5);
    1710            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1711            4 :     print_path_summary_as_text (summary, text_output, true);
    1712            4 :     ASSERT_STREQ
    1713              :       ("  `foo': events 1-2 (depth 0)\n"
    1714              :        "    |\n"
    1715              :        "    | (1): entering `foo'\n"
    1716              :        "    | (2): calling `bar'\n"
    1717              :        "    |\n"
    1718              :        "    +--> `bar': events 3-4 (depth 1)\n"
    1719              :        "           |\n"
    1720              :        "           | (3): entering `bar'\n"
    1721              :        "           | (4): calling `baz'\n"
    1722              :        "           |\n"
    1723              :        "           +--> `baz': event 5 (depth 2)\n"
    1724              :        "                  |\n"
    1725              :        "                  | (5): entering `baz'\n"
    1726              :        "                  |\n"
    1727              :        "           <------+\n"
    1728              :        "           |\n"
    1729              :        "         `bar': events 6-7 (depth 1)\n"
    1730              :        "           |\n"
    1731              :        "           | (6): returning to `bar'\n"
    1732              :        "           | (7): calling `baz'\n"
    1733              :        "           |\n"
    1734              :        "           +--> `baz': event 8 (depth 2)\n"
    1735              :        "                  |\n"
    1736              :        "                  | (8): entering `baz'\n"
    1737              :        "                  |\n",
    1738              :        pp_formatted_text (text_output.get_printer ()));
    1739            4 :   }
    1740            4 :   {
    1741            4 :     selftest::test_context dc;
    1742            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
    1743            4 :     text_sink text_output (dc);
    1744            4 :     path_print_policy policy (text_output);
    1745            4 :     path_summary summary (policy, *event_pp, path, false);
    1746            4 :     print_path_summary_as_text (summary, text_output, true);
    1747            4 :     ASSERT_STREQ
    1748              :       ("  `foo': events 1-2 (depth 0)\n"
    1749              :        "    │\n"
    1750              :        "    │ (1): entering `foo'\n"
    1751              :        "    │ (2): calling `bar'\n"
    1752              :        "    │\n"
    1753              :        "    └──> `bar': events 3-4 (depth 1)\n"
    1754              :        "           │\n"
    1755              :        "           │ (3): entering `bar'\n"
    1756              :        "           │ (4): calling `baz'\n"
    1757              :        "           │\n"
    1758              :        "           └──> `baz': event 5 (depth 2)\n"
    1759              :        "                  │\n"
    1760              :        "                  │ (5): entering `baz'\n"
    1761              :        "                  │\n"
    1762              :        "           <──────┘\n"
    1763              :        "           │\n"
    1764              :        "         `bar': events 6-7 (depth 1)\n"
    1765              :        "           │\n"
    1766              :        "           │ (6): returning to `bar'\n"
    1767              :        "           │ (7): calling `baz'\n"
    1768              :        "           │\n"
    1769              :        "           └──> `baz': event 8 (depth 2)\n"
    1770              :        "                  │\n"
    1771              :        "                  │ (8): entering `baz'\n"
    1772              :        "                  │\n",
    1773              :        pp_formatted_text (text_output.get_printer ()));
    1774            4 :   }
    1775            4 : }
    1776              : 
    1777              : /* Verify that print_path_summary is sane in the face of a recursive
    1778              :    diagnostic path.  */
    1779              : 
    1780              : static void
    1781            4 : test_recursion (pretty_printer *event_pp)
    1782              : {
    1783            4 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1784            4 :   test_path path (logical_loc_mgr, event_pp);
    1785            4 :   path.add_entry ("factorial", 0);
    1786           16 :   for (int depth = 0; depth < 3; depth++)
    1787           12 :     path.add_call ("factorial", depth, "factorial");
    1788            4 :   ASSERT_EQ (path.num_events (), 7);
    1789              : 
    1790            4 :   ASSERT_TRUE (path.interprocedural_p ());
    1791              : 
    1792            4 :   {
    1793            4 :     selftest::test_context dc;
    1794            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1795              : 
    1796            4 :     text_sink text_output (dc);
    1797            4 :     path_print_policy policy (text_output);
    1798            4 :     path_summary summary (policy, *event_pp, path, false);
    1799            4 :     ASSERT_EQ (summary.get_num_ranges (), 4);
    1800              : 
    1801            4 :     print_path_summary_as_text (summary, text_output, true);
    1802            4 :     ASSERT_STREQ
    1803              :       ("  `factorial': events 1-2 (depth 0)\n"
    1804              :        "    |\n"
    1805              :        "    | (1): entering `factorial'\n"
    1806              :        "    | (2): calling `factorial'\n"
    1807              :        "    |\n"
    1808              :        "    +--> `factorial': events 3-4 (depth 1)\n"
    1809              :        "           |\n"
    1810              :        "           | (3): entering `factorial'\n"
    1811              :        "           | (4): calling `factorial'\n"
    1812              :        "           |\n"
    1813              :        "           +--> `factorial': events 5-6 (depth 2)\n"
    1814              :        "                  |\n"
    1815              :        "                  | (5): entering `factorial'\n"
    1816              :        "                  | (6): calling `factorial'\n"
    1817              :        "                  |\n"
    1818              :        "                  +--> `factorial': event 7 (depth 3)\n"
    1819              :        "                         |\n"
    1820              :        "                         | (7): entering `factorial'\n"
    1821              :        "                         |\n",
    1822              :        pp_formatted_text (text_output.get_printer ()));
    1823            4 :   }
    1824            4 :   {
    1825            4 :     selftest::test_context dc;
    1826            4 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
    1827              : 
    1828            4 :     text_sink text_output (dc);
    1829            4 :     path_print_policy policy (text_output);
    1830            4 :     path_summary summary (policy, *event_pp, path, false);
    1831            4 :     print_path_summary_as_text (summary, text_output, true);
    1832            4 :     ASSERT_STREQ
    1833              :       ("  `factorial': events 1-2 (depth 0)\n"
    1834              :        "    │\n"
    1835              :        "    │ (1): entering `factorial'\n"
    1836              :        "    │ (2): calling `factorial'\n"
    1837              :        "    │\n"
    1838              :        "    └──> `factorial': events 3-4 (depth 1)\n"
    1839              :        "           │\n"
    1840              :        "           │ (3): entering `factorial'\n"
    1841              :        "           │ (4): calling `factorial'\n"
    1842              :        "           │\n"
    1843              :        "           └──> `factorial': events 5-6 (depth 2)\n"
    1844              :        "                  │\n"
    1845              :        "                  │ (5): entering `factorial'\n"
    1846              :        "                  │ (6): calling `factorial'\n"
    1847              :        "                  │\n"
    1848              :        "                  └──> `factorial': event 7 (depth 3)\n"
    1849              :        "                         │\n"
    1850              :        "                         │ (7): entering `factorial'\n"
    1851              :        "                         │\n",
    1852              :        pp_formatted_text (text_output.get_printer ()));
    1853            4 :   }
    1854            4 : }
    1855              : 
    1856              : /* Helper class for writing tests of control flow visualization.  */
    1857              : 
    1858          576 : class control_flow_test
    1859              : {
    1860              : public:
    1861          576 :   control_flow_test (const selftest::location &loc,
    1862              :                      const line_table_case &case_,
    1863              :                      const char *content)
    1864          576 :   : m_tmp_file (loc, ".c", content,
    1865              :                 /* gcc_rich_location::add_location_if_nearby implicitly
    1866              :                    uses global_dc's file_cache, so we need to evict
    1867              :                    tmp when we're done.  */
    1868          576 :                 &global_dc->get_file_cache ()),
    1869          576 :     m_ltt (case_)
    1870              :   {
    1871          576 :     m_ord_map
    1872          576 :       = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
    1873              :                                              m_tmp_file.get_filename (), 0));
    1874          576 :     linemap_line_start (line_table, 1, 100);
    1875          576 :   }
    1876              : 
    1877        10272 :   location_t get_line_and_column (int line, int column)
    1878              :   {
    1879        10272 :     return linemap_position_for_line_and_column (line_table, m_ord_map,
    1880              :                                                  line, column);
    1881              :   }
    1882              : 
    1883         2592 :   location_t get_line_and_columns (int line, int first_column, int last_column)
    1884              :   {
    1885         2592 :     return get_line_and_columns (line,
    1886              :                                  first_column, first_column, last_column);
    1887              :   }
    1888              : 
    1889         3264 :   location_t get_line_and_columns (int line,
    1890              :                                    int first_column,
    1891              :                                    int caret_column,
    1892              :                                    int last_column)
    1893              :   {
    1894         3264 :     return make_location (get_line_and_column (line, caret_column),
    1895              :                           get_line_and_column (line, first_column),
    1896         3264 :                           get_line_and_column (line, last_column));
    1897              :   }
    1898              : 
    1899              : private:
    1900              :   temp_source_file m_tmp_file;
    1901              :   line_table_test m_ltt;
    1902              :   const line_map_ordinary *m_ord_map;
    1903              : };
    1904              : 
    1905              : /* Example of event edges where all events can go in the same layout,
    1906              :    testing the 6 combinations of:
    1907              :    - ASCII vs Unicode vs event links off
    1908              :    - line numbering on and off.  */
    1909              : 
    1910              : static void
    1911           96 : test_control_flow_1 (const line_table_case &case_,
    1912              :                      pretty_printer *event_pp)
    1913              : {
    1914              :   /* Create a tempfile and write some text to it.
    1915              :      ...000000000111111111122222222223333333333.
    1916              :      ...123456789012345678901234567890123456789.  */
    1917           96 :   const char *content
    1918              :     = ("int test (int *p)\n" /* line 1.  */
    1919              :        "{\n"                 /* line 2.  */
    1920              :        "  if (p)\n"          /* line 3.  */
    1921              :        "    return 0;\n"     /* line 4.  */
    1922              :        "  return *p;\n"      /* line 5.  */
    1923              :        "}\n");               /* line 6.  */
    1924              : 
    1925           96 :   control_flow_test t (SELFTEST_LOCATION, case_, content);
    1926              : 
    1927           96 :   const location_t conditional = t.get_line_and_column (3, 7);
    1928           96 :   const location_t cfg_dest = t.get_line_and_column (5, 10);
    1929              : 
    1930           96 :   logical_locations::selftest::test_manager logical_loc_mgr;
    1931           96 :   test_path path (logical_loc_mgr, event_pp);
    1932           96 :   path.add_event (conditional, nullptr, 0,
    1933              :                   "following %qs branch (when %qs is NULL)...",
    1934              :                   "false", "p");
    1935           96 :   path.connect_to_next_event ();
    1936              : 
    1937           96 :   path.add_event (cfg_dest, nullptr, 0,
    1938              :                   "...to here");
    1939           96 :   path.add_event (cfg_dest, nullptr, 0,
    1940              :                   "dereference of NULL %qs",
    1941              :                   "p");
    1942              : 
    1943           96 :   if (!path_events_have_column_data_p (path))
    1944           40 :     return;
    1945              : 
    1946              : 
    1947           56 :   {
    1948           56 :     selftest::test_context dc;
    1949           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1950           56 :     dc.show_event_links (true);
    1951           56 :     text_sink text_output (dc);
    1952           56 :     path_print_policy policy (text_output);
    1953           56 :     path_summary summary (policy, *event_pp, path, true);
    1954           56 :     print_path_summary_as_text (summary, text_output, false);
    1955           56 :     ASSERT_STREQ
    1956              :       ("  events 1-3\n"
    1957              :        "FILENAME:3:7:\n"
    1958              :        "   if (p)\n"
    1959              :        "       ^\n"
    1960              :        "       |\n"
    1961              :        "       (1) following `false' branch (when `p' is NULL)... ->-+\n"
    1962              :        "                                                             |\n"
    1963              :        "FILENAME:5:10:\n"
    1964              :        "                                                             |\n"
    1965              :        "+------------------------------------------------------------+\n"
    1966              :        "|  return *p;\n"
    1967              :        "|         ~\n"
    1968              :        "|         |\n"
    1969              :        "+-------->(2) ...to here\n"
    1970              :        "          (3) dereference of NULL `p'\n",
    1971              :        pp_formatted_text (text_output.get_printer ()));
    1972           56 :   }
    1973           56 :   {
    1974           56 :     selftest::test_context dc;
    1975           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1976           56 :     dc.show_event_links (false);
    1977           56 :     text_sink text_output (dc);
    1978           56 :     path_print_policy policy (text_output);
    1979           56 :     path_summary summary (policy, *event_pp, path, true);
    1980           56 :     print_path_summary_as_text (summary, text_output, false);
    1981           56 :     ASSERT_STREQ
    1982              :       ("  events 1-3\n"
    1983              :        "FILENAME:3:7:\n"
    1984              :        "   if (p)\n"
    1985              :        "       ^\n"
    1986              :        "       |\n"
    1987              :        "       (1) following `false' branch (when `p' is NULL)...\n"
    1988              :        "FILENAME:5:10:\n"
    1989              :        "   return *p;\n"
    1990              :        "          ~\n"
    1991              :        "          |\n"
    1992              :        "          (2) ...to here\n"
    1993              :        "          (3) dereference of NULL `p'\n",
    1994              :        pp_formatted_text (text_output.get_printer ()));
    1995           56 :   }
    1996           56 :   {
    1997           56 :     selftest::test_context dc;
    1998           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    1999           56 :     dc.show_line_numbers (true);
    2000           56 :     dc.show_event_links (true);
    2001           56 :     text_sink text_output (dc);
    2002           56 :     path_print_policy policy (text_output);
    2003           56 :     path_summary summary (policy, *event_pp, path, true);
    2004           56 :     print_path_summary_as_text (summary, text_output, false);
    2005           56 :     ASSERT_STREQ
    2006              :       ("  events 1-3\n"
    2007              :        "FILENAME:3:7:\n"
    2008              :        "    3 |   if (p)\n"
    2009              :        "      |       ^\n"
    2010              :        "      |       |\n"
    2011              :        "      |       (1) following `false' branch (when `p' is NULL)... ->-+\n"
    2012              :        "      |                                                             |\n"
    2013              :        "      |                                                             |\n"
    2014              :        "      |+------------------------------------------------------------+\n"
    2015              :        "    4 ||    return 0;\n"
    2016              :        "    5 ||  return *p;\n"
    2017              :        "      ||         ~\n"
    2018              :        "      ||         |\n"
    2019              :        "      |+-------->(2) ...to here\n"
    2020              :        "      |          (3) dereference of NULL `p'\n",
    2021              :        pp_formatted_text (text_output.get_printer ()));
    2022           56 :   }
    2023           56 :   {
    2024           56 :     selftest::test_context dc;
    2025           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2026           56 :     dc.show_line_numbers (true);
    2027           56 :     dc.show_event_links (false);
    2028           56 :     text_sink text_output (dc);
    2029           56 :     path_print_policy policy (text_output);
    2030           56 :     path_summary summary (policy, *event_pp, path, true);
    2031           56 :     print_path_summary_as_text (summary, text_output, false);
    2032           56 :     ASSERT_STREQ
    2033              :       ("  events 1-3\n"
    2034              :        "FILENAME:3:7:\n"
    2035              :        "    3 |   if (p)\n"
    2036              :        "      |       ^\n"
    2037              :        "      |       |\n"
    2038              :        "      |       (1) following `false' branch (when `p' is NULL)...\n"
    2039              :        "    4 |     return 0;\n"
    2040              :        "    5 |   return *p;\n"
    2041              :        "      |          ~\n"
    2042              :        "      |          |\n"
    2043              :        "      |          (2) ...to here\n"
    2044              :        "      |          (3) dereference of NULL `p'\n",
    2045              :        pp_formatted_text (text_output.get_printer ()));
    2046           56 :   }
    2047           56 :   {
    2048           56 :     selftest::test_context dc;
    2049           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
    2050           56 :     dc.show_event_links (true);
    2051           56 :     text_sink text_output (dc);
    2052           56 :     path_print_policy policy (text_output);
    2053           56 :     path_summary summary (policy, *event_pp, path, true);
    2054           56 :     print_path_summary_as_text (summary, text_output, false);
    2055           56 :     ASSERT_STREQ
    2056              :       ("  events 1-3\n"
    2057              :        "FILENAME:3:7:\n"
    2058              :        "   if (p)\n"
    2059              :        "       ^\n"
    2060              :        "       |\n"
    2061              :        "       (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
    2062              :        "                                                             │\n"
    2063              :        "FILENAME:5:10:\n"
    2064              :        "                                                             │\n"
    2065              :        "┌────────────────────────────────────────────────────────────┘\n"
    2066              :        "│  return *p;\n"
    2067              :        "│         ~\n"
    2068              :        "│         |\n"
    2069              :        "└────────>(2) ...to here\n"
    2070              :        "          (3) dereference of NULL `p'\n",
    2071              :        pp_formatted_text (text_output.get_printer ()));
    2072           56 :   }
    2073           56 :   {
    2074           56 :     selftest::test_context dc;
    2075           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
    2076           56 :     dc.show_event_links (true);
    2077           56 :     dc.show_line_numbers (true);
    2078           56 :     text_sink text_output (dc);
    2079           56 :     path_print_policy policy (text_output);
    2080           56 :     path_summary summary (policy, *event_pp, path, true);
    2081           56 :     print_path_summary_as_text (summary, text_output, false);
    2082           56 :     ASSERT_STREQ
    2083              :       ("  events 1-3\n"
    2084              :        "FILENAME:3:7:\n"
    2085              :        "    3 |   if (p)\n"
    2086              :        "      |       ^\n"
    2087              :        "      |       |\n"
    2088              :        "      |       (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
    2089              :        "      |                                                             │\n"
    2090              :        "      |                                                             │\n"
    2091              :        "      |┌────────────────────────────────────────────────────────────┘\n"
    2092              :        "    4 |│    return 0;\n"
    2093              :        "    5 |│  return *p;\n"
    2094              :        "      |│         ~\n"
    2095              :        "      |│         |\n"
    2096              :        "      |└────────>(2) ...to here\n"
    2097              :        "      |          (3) dereference of NULL `p'\n",
    2098              :        pp_formatted_text (text_output.get_printer ()));
    2099           56 :   }
    2100          192 : }
    2101              : 
    2102              : /* Complex example involving a backedge.  */
    2103              : 
    2104              : static void
    2105           96 : test_control_flow_2 (const line_table_case &case_,
    2106              :                      pretty_printer *event_pp)
    2107              : {
    2108              :   /* Create a tempfile and write some text to it.
    2109              :      ...000000000111111111122222222223333333333.
    2110              :      ...123456789012345678901234567890123456789.  */
    2111           96 :   const char *content
    2112              :     = ("int for_loop_noop_next (struct node *n)\n" /* <--------- line 1.  */
    2113              :        "{\n" /* <----------------------------------------------- line 2.  */
    2114              :        "  int sum = 0;\n" /* <---------------------------------- line 3.  */
    2115              :        "  for (struct node *iter = n; iter; iter->next)\n" /* <- line 4.  */
    2116              :        "    sum += n->val;\n" /* <------------------------------ line 5.  */
    2117              :        "  return sum;\n" /* <----------------------------------- line 6.  */
    2118              :        "}\n");  /* <-------------------------------------------- line 7.  */
    2119              :   /* Adapted from infinite-loop-linked-list.c where
    2120              :      "iter->next" should be "iter = iter->next".  */
    2121              : 
    2122           96 :   control_flow_test t (SELFTEST_LOCATION, case_, content);
    2123              : 
    2124           96 :   const location_t iter_test = t.get_line_and_columns (4, 31, 34);
    2125           96 :   const location_t loop_body_start = t.get_line_and_columns (5, 12, 17);
    2126           96 :   const location_t loop_body_end = t.get_line_and_columns (5, 5, 9, 17);
    2127              : 
    2128           96 :   logical_locations::selftest::test_manager logical_loc_mgr;
    2129           96 :   test_path path (logical_loc_mgr, event_pp);
    2130           96 :   path.add_event (iter_test, nullptr, 0, "infinite loop here");
    2131              : 
    2132           96 :   path.add_event (iter_test, nullptr, 0, "looping from here...");
    2133           96 :   path.connect_to_next_event ();
    2134              : 
    2135           96 :   path.add_event (loop_body_start, nullptr, 0, "...to here");
    2136              : 
    2137           96 :   path.add_event (loop_body_end, nullptr, 0, "looping back...");
    2138           96 :   path.connect_to_next_event ();
    2139              : 
    2140           96 :   path.add_event (iter_test, nullptr, 0, "...to here");
    2141              : 
    2142           96 :   if (!path_events_have_column_data_p (path))
    2143           40 :     return;
    2144              : 
    2145           56 :   {
    2146           56 :     selftest::test_context dc;
    2147           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2148           56 :     dc.show_event_links (true);
    2149           56 :     dc.show_line_numbers (true);
    2150           56 :     text_sink text_output (dc);
    2151           56 :     path_print_policy policy (text_output);
    2152           56 :     path_summary summary (policy, *event_pp, path, true);
    2153           56 :     print_path_summary_as_text (summary, text_output, false);
    2154           56 :     ASSERT_STREQ
    2155              :       ("  events 1-3\n"
    2156              :        "FILENAME:4:31:\n"
    2157              :        "    4 |   for (struct node *iter = n; iter; iter->next)\n"
    2158              :        "      |                               ^~~~\n"
    2159              :        "      |                               |\n"
    2160              :        "      |                               (1) infinite loop here\n"
    2161              :        "      |                               (2) looping from here... ->-+\n"
    2162              :        "      |                                                           |\n"
    2163              :        "      |                                                           |\n"
    2164              :        "      |+----------------------------------------------------------+\n"
    2165              :        "    5 ||    sum += n->val;\n"
    2166              :        "      ||           ~~~~~~              \n"
    2167              :        "      ||           |\n"
    2168              :        "      |+---------->(3) ...to here\n"
    2169              :        /* We need to start an new event_range here as event (4) is to the
    2170              :           left of event (3), and thus (4) would mess up the in-edge to (3).  */
    2171              :        "  event 4\n"
    2172              :        "    5 |     sum += n->val;\n"
    2173              :        "      |     ~~~~^~~~~~~~~\n"
    2174              :        "      |         |\n"
    2175              :        "      |         (4) looping back... ->-+\n"
    2176              :        "      |                                |\n"
    2177              :        /* We need to start an new event_range here as event (4) with an
    2178              :           out-edge is on a later line (line 5) than its destination event (5),
    2179              :           on line 4.  */
    2180              :        "  event 5\n"
    2181              :        "      |                                |\n"
    2182              :        "      |+-------------------------------+\n"
    2183              :        "    4 ||  for (struct node *iter = n; iter; iter->next)\n"
    2184              :        "      ||                              ^~~~\n"
    2185              :        "      ||                              |\n"
    2186              :        "      |+----------------------------->(5) ...to here\n",
    2187              :        pp_formatted_text (text_output.get_printer ()));
    2188           56 :   }
    2189          192 : }
    2190              : 
    2191              : /* Complex example involving a backedge and both an in-edge and out-edge
    2192              :    on the same line.  */
    2193              : 
    2194              : static void
    2195           96 : test_control_flow_3 (const line_table_case &case_,
    2196              :                      pretty_printer *event_pp)
    2197              : {
    2198              :   /* Create a tempfile and write some text to it.
    2199              :      ...000000000111111111122222222223333333333.
    2200              :      ...123456789012345678901234567890123456789.  */
    2201           96 :   const char *content
    2202              :     = ("void test_missing_comparison_in_for_condition_1 (int n)\n"
    2203              :        "{\n" /* <------------------------- line 2.  */
    2204              :        "  for (int i = 0; n; i++)\n" /* <- line 3.  */
    2205              :        "    {\n" /* <--------------------- line 4.  */
    2206              :        "    }\n" /* <--------------------- line 5.  */
    2207              :        "}\n"); /* <----------------------- line 6.  */
    2208              :   /* Adapted from infinite-loop-1.c where the condition should have been
    2209              :      "i < n", rather than just "n".  */
    2210              : 
    2211           96 :   control_flow_test t (SELFTEST_LOCATION, case_, content);
    2212              : 
    2213           96 :   const location_t iter_test = t.get_line_and_column (3, 19);
    2214           96 :   const location_t iter_next = t.get_line_and_columns (3, 22, 24);
    2215              : 
    2216           96 :   logical_locations::selftest::test_manager logical_loc_mgr;
    2217           96 :   test_path path (logical_loc_mgr, event_pp);
    2218           96 :   path.add_event (iter_test, nullptr, 0, "infinite loop here");
    2219              : 
    2220           96 :   path.add_event (iter_test, nullptr, 0, "looping from here...");
    2221           96 :   path.connect_to_next_event ();
    2222              : 
    2223           96 :   path.add_event (iter_next, nullptr, 0, "...to here");
    2224              : 
    2225           96 :   path.add_event (iter_next, nullptr, 0, "looping back...");
    2226           96 :   path.connect_to_next_event ();
    2227              : 
    2228           96 :   path.add_event (iter_test, nullptr, 0, "...to here");
    2229              : 
    2230           96 :   if (!path_events_have_column_data_p (path))
    2231           36 :     return;
    2232              : 
    2233           60 :   {
    2234           60 :     selftest::test_context dc;
    2235           60 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2236           60 :     dc.show_event_links (true);
    2237           60 :     dc.show_line_numbers (true);
    2238           60 :     text_sink text_output (dc);
    2239           60 :     path_print_policy policy (text_output);
    2240           60 :     path_summary summary (policy, *event_pp, path, true);
    2241           60 :     print_path_summary_as_text (summary, text_output, false);
    2242           60 :     ASSERT_STREQ
    2243              :       ("  events 1-2\n"
    2244              :        "FILENAME:3:19:\n"
    2245              :        "    3 |   for (int i = 0; n; i++)\n"
    2246              :        "      |                   ^\n"
    2247              :        "      |                   |\n"
    2248              :        "      |                   (1) infinite loop here\n"
    2249              :        "      |                   (2) looping from here... ->-+\n"
    2250              :        "      |                                               |\n"
    2251              :        "  events 3-4\n"
    2252              :        "      |                                               |\n"
    2253              :        "      |+----------------------------------------------+\n"
    2254              :        "    3 ||  for (int i = 0; n; i++)\n"
    2255              :        "      ||                     ^~~\n"
    2256              :        "      ||                     |\n"
    2257              :        "      |+-------------------->(3) ...to here\n"
    2258              :        "      |                      (4) looping back... ->-+\n"
    2259              :        "      |                                             |\n"
    2260              :        /* We need to start an new event_range here as event (4) with an
    2261              :           out-edge is on the same line as its destination event (5), but
    2262              :           to the right, which we can't handle as a single event_range. */
    2263              :        "  event 5\n"
    2264              :        "      |                                             |\n"
    2265              :        "      |+--------------------------------------------+\n"
    2266              :        "    3 ||  for (int i = 0; n; i++)\n"
    2267              :        "      ||                  ^\n"
    2268              :        "      ||                  |\n"
    2269              :        "      |+----------------->(5) ...to here\n",
    2270              :        pp_formatted_text (text_output.get_printer ()));
    2271           60 :   }
    2272          192 : }
    2273              : 
    2274              : /* Implementation of ASSERT_CFG_EDGE_PATH_STREQ.  */
    2275              : 
    2276              : static void
    2277         1152 : assert_cfg_edge_path_streq (const location &loc,
    2278              :                             pretty_printer *event_pp,
    2279              :                             const location_t src_loc,
    2280              :                             const location_t dst_loc,
    2281              :                             const char *expected_str)
    2282              : {
    2283         1152 :   logical_locations::selftest::test_manager logical_loc_mgr;
    2284         1152 :   test_path path (logical_loc_mgr, event_pp);
    2285         1152 :   path.add_event (src_loc, nullptr, 0, "from here...");
    2286         1152 :   path.connect_to_next_event ();
    2287              : 
    2288         1152 :   path.add_event (dst_loc, nullptr, 0, "...to here");
    2289              : 
    2290         1152 :   if (!path_events_have_column_data_p (path))
    2291          464 :     return;
    2292              : 
    2293          688 :   selftest::test_context dc;
    2294          688 :   dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2295          688 :   dc.show_event_links (true);
    2296          688 :   dc.show_line_numbers (true);
    2297          688 :   text_sink text_output (dc);
    2298          688 :   path_print_policy policy (text_output);
    2299          688 :   path_summary summary (policy, *event_pp, path, true);
    2300          688 :   print_path_summary_as_text (summary, text_output, false);
    2301          688 :   ASSERT_STREQ_AT (loc, expected_str,
    2302              :                    pp_formatted_text (text_output.get_printer ()));
    2303         1152 : }
    2304              : 
    2305              : /* Assert that if we make a path with an event with "from here..." at SRC_LOC
    2306              :    leading to an event "...to here" at DST_LOC that we print the path
    2307              :    as EXPECTED_STR.  */
    2308              : 
    2309              : #define ASSERT_CFG_EDGE_PATH_STREQ(SRC_LOC, DST_LOC, EXPECTED_STR) \
    2310              :   assert_cfg_edge_path_streq ((SELFTEST_LOCATION), (event_pp),     \
    2311              :                               (SRC_LOC), (DST_LOC), (EXPECTED_STR))
    2312              : 
    2313              : /* Various examples of edge, trying to cover all combinations of:
    2314              :    - relative x positive of src label and dst label
    2315              :    - relative y position of labels:
    2316              :      - on same line
    2317              :      - on next line
    2318              :      - on line after next
    2319              :      - big gap, where src is before dst
    2320              :      - big gap, where src is after dst
    2321              :    and other awkward cases.  */
    2322              : 
    2323              : static void
    2324           96 : test_control_flow_4 (const line_table_case &case_,
    2325              :                      pretty_printer *event_pp)
    2326              : {
    2327           96 :   std::string many_lines;
    2328         9696 :   for (int i = 1; i <= 100; i++)
    2329              :     /* ............000000000111
    2330              :        ............123456789012.  */
    2331         9600 :     many_lines += "LHS      RHS\n";
    2332           96 :   control_flow_test t (SELFTEST_LOCATION, case_, many_lines.c_str ());
    2333              : 
    2334              :   /* Same line.  */
    2335           96 :   {
    2336              :     /* LHS -> RHS.  */
    2337           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2338              :       (t.get_line_and_columns (3, 1, 3),
    2339              :        t.get_line_and_columns (3, 10, 12),
    2340              :        ("  event 1\n"
    2341              :         "FILENAME:3:1:\n"
    2342              :         "    3 | LHS      RHS\n"
    2343              :         "      | ^~~\n"
    2344              :         "      | |\n"
    2345              :         "      | (1) from here... ->-+\n"
    2346              :         "      |                     |\n"
    2347              :         "  event 2\n"
    2348              :         "      |                     |\n"
    2349              :         "      |+--------------------+\n"
    2350              :         "    3 ||LHS      RHS\n"
    2351              :         "      ||         ^~~\n"
    2352              :         "      ||         |\n"
    2353              :         "      |+-------->(2) ...to here\n"));
    2354              : 
    2355              :     /*  RHS -> LHS.  */
    2356           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2357              :       (t.get_line_and_columns (3, 10, 12),
    2358              :        t.get_line_and_columns (3, 1, 3),
    2359              :        ("  event 1\n"
    2360              :         "FILENAME:3:10:\n"
    2361              :         "    3 | LHS      RHS\n"
    2362              :         "      |          ^~~\n"
    2363              :         "      |          |\n"
    2364              :         "      |          (1) from here... ->-+\n"
    2365              :         "      |                              |\n"
    2366              :         "  event 2\n"
    2367              :         "      |                              |\n"
    2368              :         "      |+-----------------------------+\n"
    2369              :         "    3 ||LHS      RHS\n"
    2370              :         "      ||^~~\n"
    2371              :         "      |||\n"
    2372              :         "      |+(2) ...to here\n"));
    2373              :   }
    2374              : 
    2375              :   /* Next line.  */
    2376           96 :   {
    2377              :     /* LHS -> RHS.  */
    2378           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2379              :       (t.get_line_and_columns (3, 1, 3),
    2380              :        t.get_line_and_columns (4, 5, 7),
    2381              :        ("  events 1-2\n"
    2382              :         "FILENAME:3:1:\n"
    2383              :         "    3 | LHS      RHS\n"
    2384              :         "      | ^~~\n"
    2385              :         "      | |\n"
    2386              :         "      | (1) from here... ->-+\n"
    2387              :         "      |                     |\n"
    2388              :         "      |                     |\n"
    2389              :         "      |+--------------------+\n"
    2390              :         "    4 ||LHS      RHS\n"
    2391              :         "      ||    ~~~\n"
    2392              :         "      ||    |\n"
    2393              :         "      |+--->(2) ...to here\n"));
    2394              : 
    2395              :     /*  RHS -> LHS.  */
    2396           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2397              :       (t.get_line_and_columns (3, 10, 12),
    2398              :        t.get_line_and_columns (4, 1, 3),
    2399              :        ("  events 1-2\n"
    2400              :         "FILENAME:3:10:\n"
    2401              :         "    3 | LHS      RHS\n"
    2402              :         "      |          ^~~\n"
    2403              :         "      |          |\n"
    2404              :         "      |          (1) from here... ->-+\n"
    2405              :         "      |                              |\n"
    2406              :         "      |                              |\n"
    2407              :         "      |+-----------------------------+\n"
    2408              :         "    4 ||LHS      RHS\n"
    2409              :         "      ||~~~       \n"
    2410              :         "      |||\n"
    2411              :         "      |+(2) ...to here\n"));
    2412              :   }
    2413              : 
    2414              :   /* Line after next.  */
    2415           96 :   {
    2416              :     /* LHS -> RHS.  */
    2417           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2418              :       (t.get_line_and_columns (3, 1, 3),
    2419              :        t.get_line_and_columns (5, 10, 12),
    2420              :        ("  events 1-2\n"
    2421              :         "FILENAME:3:1:\n"
    2422              :         "    3 | LHS      RHS\n"
    2423              :         "      | ^~~\n"
    2424              :         "      | |\n"
    2425              :         "      | (1) from here... ->-+\n"
    2426              :         "      |                     |\n"
    2427              :         "      |                     |\n"
    2428              :         "      |+--------------------+\n"
    2429              :         "    4 ||LHS      RHS\n"
    2430              :         "    5 ||LHS      RHS\n"
    2431              :         "      ||         ~~~\n"
    2432              :         "      ||         |\n"
    2433              :         "      |+-------->(2) ...to here\n"));
    2434              : 
    2435              :     /*  RHS -> LHS.  */
    2436           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2437              :       (t.get_line_and_columns (3, 10, 12),
    2438              :        t.get_line_and_columns (5, 1, 3),
    2439              :        ("  events 1-2\n"
    2440              :         "FILENAME:3:10:\n"
    2441              :         "    3 | LHS      RHS\n"
    2442              :         "      |          ^~~\n"
    2443              :         "      |          |\n"
    2444              :         "      |          (1) from here... ->-+\n"
    2445              :         "      |                              |\n"
    2446              :         "      |                              |\n"
    2447              :         "      |+-----------------------------+\n"
    2448              :         "    4 ||LHS      RHS\n"
    2449              :         "    5 ||LHS      RHS\n"
    2450              :         "      ||~~~       \n"
    2451              :         "      |||\n"
    2452              :         "      |+(2) ...to here\n"));
    2453              :   }
    2454              : 
    2455              :   /* Big gap, increasing line number.  */
    2456           96 :   {
    2457              :     /* LHS -> RHS.  */
    2458           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2459              :       (t.get_line_and_columns (3, 1, 3),
    2460              :        t.get_line_and_columns (97, 10, 12),
    2461              :        ("  events 1-2\n"
    2462              :         "FILENAME:3:1:\n"
    2463              :         "    3 | LHS      RHS\n"
    2464              :         "      | ^~~\n"
    2465              :         "      | |\n"
    2466              :         "      | (1) from here... ->-+\n"
    2467              :         "      |                     |\n"
    2468              :         "......\n"
    2469              :         "      |                     |\n"
    2470              :         "      |+--------------------+\n"
    2471              :         "   97 ||LHS      RHS\n"
    2472              :         "      ||         ~~~\n"
    2473              :         "      ||         |\n"
    2474              :         "      |+-------->(2) ...to here\n"));
    2475              : 
    2476              :     /*  RHS -> LHS.  */
    2477           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2478              :       (t.get_line_and_columns (3, 10, 12),
    2479              :        t.get_line_and_columns (97, 1, 3),
    2480              :        ("  events 1-2\n"
    2481              :         "FILENAME:3:10:\n"
    2482              :         "    3 | LHS      RHS\n"
    2483              :         "      |          ^~~\n"
    2484              :         "      |          |\n"
    2485              :         "      |          (1) from here... ->-+\n"
    2486              :         "      |                              |\n"
    2487              :         "......\n"
    2488              :         "      |                              |\n"
    2489              :         "      |+-----------------------------+\n"
    2490              :         "   97 ||LHS      RHS\n"
    2491              :         "      ||~~~       \n"
    2492              :         "      |||\n"
    2493              :         "      |+(2) ...to here\n"));
    2494              :   }
    2495              : 
    2496              :   /* Big gap, decreasing line number.  */
    2497           96 :   {
    2498              :     /* LHS -> RHS.  */
    2499           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2500              :       (t.get_line_and_columns (97, 1, 3),
    2501              :        t.get_line_and_columns (3, 10, 12),
    2502              :        ("  event 1\n"
    2503              :         "FILENAME:97:1:\n"
    2504              :         "   97 | LHS      RHS\n"
    2505              :         "      | ^~~\n"
    2506              :         "      | |\n"
    2507              :         "      | (1) from here... ->-+\n"
    2508              :         "      |                     |\n"
    2509              :         "  event 2\n"
    2510              :         "      |                     |\n"
    2511              :         "      |+--------------------+\n"
    2512              :         "    3 ||LHS      RHS\n"
    2513              :         "      ||         ^~~\n"
    2514              :         "      ||         |\n"
    2515              :         "      |+-------->(2) ...to here\n"));
    2516              : 
    2517              :     /*  RHS -> LHS.  */
    2518           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2519              :       (t.get_line_and_columns (97, 10, 12),
    2520              :        t.get_line_and_columns (3, 1, 3),
    2521              :        ("  event 1\n"
    2522              :         "FILENAME:97:10:\n"
    2523              :         "   97 | LHS      RHS\n"
    2524              :         "      |          ^~~\n"
    2525              :         "      |          |\n"
    2526              :         "      |          (1) from here... ->-+\n"
    2527              :         "      |                              |\n"
    2528              :         "  event 2\n"
    2529              :         "      |                              |\n"
    2530              :         "      |+-----------------------------+\n"
    2531              :         "    3 ||LHS      RHS\n"
    2532              :         "      ||^~~\n"
    2533              :         "      |||\n"
    2534              :         "      |+(2) ...to here\n"));
    2535              :   }
    2536              : 
    2537              :   /* Unknown src.  */
    2538           96 :   {
    2539           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2540              :       (UNKNOWN_LOCATION,
    2541              :        t.get_line_and_columns (3, 10, 12),
    2542              :        ("  event 1\n"
    2543              :         " (1): from here...\n"
    2544              :         "  event 2\n"
    2545              :         "FILENAME:3:10:\n"
    2546              :         "    3 | LHS      RHS\n"
    2547              :         "      |          ^~~\n"
    2548              :         "      |          |\n"
    2549              :         "      |+-------->(2) ...to here\n"));
    2550              :   }
    2551              : 
    2552              :   /* Unknown dst.  */
    2553           96 :   {
    2554           96 :     ASSERT_CFG_EDGE_PATH_STREQ
    2555              :       (t.get_line_and_columns (3, 1, 3),
    2556              :        UNKNOWN_LOCATION,
    2557              :        ("  event 1\n"
    2558              :         "FILENAME:3:1:\n"
    2559              :         "    3 | LHS      RHS\n"
    2560              :         "      | ^~~\n"
    2561              :         "      | |\n"
    2562              :         "      | (1) from here... ->-+\n"
    2563              :         "      |                     |\n"
    2564              :         "  event 2\n"
    2565              :         "FILENAME:\n"
    2566              :         " (2): ...to here\n"));
    2567              :   }
    2568           96 : }
    2569              : 
    2570              : /* Another complex example, adapted from data-model-20.c.  */
    2571              : 
    2572              : static void
    2573           96 : test_control_flow_5 (const line_table_case &case_,
    2574              :                      pretty_printer *event_pp)
    2575              : {
    2576              :   /* Create a tempfile and write some text to it.
    2577              :      ...000000000111111111122222222223333333333444444444455555555556666666666.
    2578              :      ...123456789012345678901234567890123456789012345678901234567890123456789.  */
    2579           96 :   const char *content
    2580              :     = ("  if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
    2581              :        "    return NULL;\n" /* <------------------------- line 2.  */
    2582              :        "\n" /* <----------------------------------------- line 3.  */
    2583              :        "  for (i = 0; i < n; i++) {\n" /* <-------------- line 4.  */
    2584              :        "    if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n");
    2585              : 
    2586           96 :   control_flow_test t (SELFTEST_LOCATION, case_, content);
    2587              : 
    2588           96 :   logical_locations::selftest::test_manager logical_loc_mgr;
    2589           96 :   test_path path (logical_loc_mgr, event_pp);
    2590              :   /* (1) */
    2591           96 :   path.add_event (t.get_line_and_column (1, 6), nullptr, 0,
    2592              :                   "following %qs branch (when %qs is non-NULL)...",
    2593              :                   "false", "arr");
    2594           96 :   path.connect_to_next_event ();
    2595              : 
    2596              :   /* (2) */
    2597           96 :   path.add_event (t.get_line_and_columns (4, 8, 10, 12), nullptr, 0,
    2598              :                   "...to here");
    2599              : 
    2600              :   /* (3) */
    2601           96 :   path.add_event (t.get_line_and_columns (4, 15, 17, 19), nullptr, 0,
    2602              :                   "following %qs branch (when %qs)...",
    2603              :                   "true", "i < n");
    2604           96 :   path.connect_to_next_event ();
    2605              : 
    2606              :   /* (4) */
    2607           96 :   path.add_event (t.get_line_and_column (5, 13), nullptr, 0,
    2608              :                   "...to here");
    2609              : 
    2610              :   /* (5) */
    2611           96 :   path.add_event (t.get_line_and_columns (5, 33, 58), nullptr, 0,
    2612              :                   "allocated here");
    2613              : 
    2614           96 :   if (!path_events_have_column_data_p (path))
    2615           40 :     return;
    2616              : 
    2617           56 :   {
    2618           56 :     selftest::test_context dc;
    2619           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2620           56 :     dc.show_event_links (true);
    2621           56 :     dc.show_line_numbers (true);
    2622           56 :     text_sink text_output (dc);
    2623           56 :     path_print_policy policy (text_output);
    2624           56 :     path_summary summary (policy, *event_pp, path, true);
    2625           56 :     print_path_summary_as_text (summary, text_output, false);
    2626           56 :     ASSERT_STREQ
    2627              :       ("  events 1-5\n"
    2628              :        "FILENAME:1:6:\n"
    2629              :        "    1 |   if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
    2630              :        "      |      ^\n"
    2631              :        "      |      |\n"
    2632              :        "      |      (1) following `false' branch (when `arr' is non-NULL)... ->-+\n"
    2633              :        "      |                                                                  |\n"
    2634              :        "......\n"
    2635              :        "      |                                                                  |\n"
    2636              :        "      |+-----------------------------------------------------------------+\n"
    2637              :        "    4 ||  for (i = 0; i < n; i++) {\n"
    2638              :        "      ||       ~~~~~  ~~~~~\n"
    2639              :        "      ||         |      |\n"
    2640              :        "      ||         |      (3) following `true' branch (when `i < n')... ->-+\n"
    2641              :        "      |+-------->(2) ...to here                                          |\n"
    2642              :        "      |                                                                  |\n"
    2643              :        "      |                                                                  |\n"
    2644              :        "      |+-----------------------------------------------------------------+\n"
    2645              :        "    5 ||    if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n"
    2646              :        "      ||            ~                   ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
    2647              :        "      ||            |                   |\n"
    2648              :        "      |+----------->(4) ...to here      (5) allocated here\n",
    2649              :        pp_formatted_text (text_output.get_printer ()));
    2650           56 :   }
    2651          192 : }
    2652              : 
    2653              : /* Another complex example, adapted from loop-3.c.  */
    2654              : 
    2655              : static void
    2656           96 : test_control_flow_6 (const line_table_case &case_,
    2657              :                      pretty_printer *event_pp)
    2658              : {
    2659              :   /* Create a tempfile and write some text to it.
    2660              :      ...000000000111111111122222222223333333.
    2661              :      ...123456789012345678901234567890123456.  */
    2662           96 :   const char *content
    2663              :     = ("#include <stdlib.h>\n" /* <------------------ line 1.  */
    2664              :        "\n" /* <------------------------------------- line 2.  */
    2665              :        "void test(int c)\n" /* <--------------------- line 3.  */
    2666              :        "{\n" /* <------------------------------------ line 4.  */
    2667              :        "  int i;\n" /* <----------------------------- line 5.  */
    2668              :        "  char *buffer = (char*)malloc(256);\n" /* <- line 6.  */
    2669              :        "\n" /* <------------------------------------- line 7.  */
    2670              :        "  for (i=0; i<255; i++) {\n" /* <------------ line 8.  */
    2671              :        "    buffer[i] = c;\n" /* <------------------- line 9.  */
    2672              :        "\n" /* <------------------------------------- line 10.  */
    2673              :        "    free(buffer);\n" /* <-------------------- line 11.  */
    2674              :        "  }\n"); /* <-------------------------------- line 12.  */
    2675              : 
    2676           96 :   control_flow_test t (SELFTEST_LOCATION, case_, content);
    2677              : 
    2678           96 :   logical_locations::selftest::test_manager logical_loc_mgr;
    2679           96 :   test_path path (logical_loc_mgr, event_pp);
    2680              :   /* (1) */
    2681           96 :   path.add_event (t.get_line_and_columns (6, 25, 35), nullptr, 0,
    2682              :                   "allocated here");
    2683              : 
    2684              :   /* (2) */
    2685           96 :   path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
    2686              :                   "following %qs branch (when %qs)...",
    2687              :                   "true", "i <= 254");
    2688           96 :   path.connect_to_next_event ();
    2689              : 
    2690              :   /* (3) */
    2691           96 :   path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
    2692              :                   "...to here");
    2693              : 
    2694              :   /* (4) */
    2695           96 :   path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
    2696              :                   "following %qs branch (when %qs)...",
    2697              :                   "true", "i <= 254");
    2698           96 :   path.connect_to_next_event ();
    2699              : 
    2700              :   /* (5) */
    2701           96 :   path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
    2702              :                   "...to here");
    2703              : 
    2704           96 :   if (!path_events_have_column_data_p (path))
    2705           40 :     return;
    2706              : 
    2707           56 :   {
    2708           56 :     selftest::test_context dc;
    2709           56 :     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
    2710           56 :     dc.show_event_links (true);
    2711           56 :     dc.show_line_numbers (true);
    2712           56 :     text_sink text_output (dc);
    2713           56 :     path_print_policy policy (text_output);
    2714           56 :     path_summary summary (policy, *event_pp, path, true);
    2715           56 :     print_path_summary_as_text (summary, text_output, false);
    2716           56 :     ASSERT_STREQ
    2717              :       ("  events 1-3\n"
    2718              :        "FILENAME:6:25:\n"
    2719              :        "    6 |   char *buffer = (char*)malloc(256);\n"
    2720              :        "      |                         ^~~~~~~~~~~\n"
    2721              :        "      |                         |\n"
    2722              :        "      |                         (1) allocated here\n"
    2723              :        "    7 | \n"
    2724              :        "    8 |   for (i=0; i<255; i++) {\n"
    2725              :        "      |             ~~~~~        \n"
    2726              :        "      |              |\n"
    2727              :        "      |              (2) following `true' branch (when `i <= 254')... ->-+\n"
    2728              :        "      |                                                                  |\n"
    2729              :        "      |                                                                  |\n"
    2730              :        "      |+-----------------------------------------------------------------+\n"
    2731              :        "    9 ||    buffer[i] = c;\n"
    2732              :        "      ||    ~~~~~~~~~~~~~        \n"
    2733              :        "      ||              |\n"
    2734              :        "      |+------------->(3) ...to here\n"
    2735              :        "  events 4-5\n"
    2736              :        "    8 |   for (i=0; i<255; i++) {\n"
    2737              :        "      |             ~^~~~\n"
    2738              :        "      |              |\n"
    2739              :        "      |              (4) following `true' branch (when `i <= 254')... ->-+\n"
    2740              :        "      |                                                                  |\n"
    2741              :        "      |                                                                  |\n"
    2742              :        "      |+-----------------------------------------------------------------+\n"
    2743              :        "    9 ||    buffer[i] = c;\n"
    2744              :        "      ||    ~~~~~~~~~~~~~\n"
    2745              :        "      ||              |\n"
    2746              :        "      |+------------->(5) ...to here\n",
    2747              :        pp_formatted_text (text_output.get_printer ()));
    2748           56 :   }
    2749          192 : }
    2750              : 
    2751              : static void
    2752           96 : control_flow_tests (const line_table_case &case_)
    2753              : {
    2754           96 :   pretty_printer pp;
    2755           96 :   pp_show_color (&pp) = false;
    2756              : 
    2757           96 :   test_control_flow_1 (case_, &pp);
    2758           96 :   test_control_flow_2 (case_, &pp);
    2759           96 :   test_control_flow_3 (case_, &pp);
    2760           96 :   test_control_flow_4 (case_, &pp);
    2761           96 :   test_control_flow_5 (case_, &pp);
    2762           96 :   test_control_flow_6 (case_, &pp);
    2763           96 : }
    2764              : 
    2765              : } // namespace diagnostics::paths::selftest
    2766              : } // namespace diagnostics::paths
    2767              : 
    2768              : namespace selftest { // diagnostics::selftest
    2769              : 
    2770              : /* Run all of the selftests within this file.  */
    2771              : 
    2772              : void
    2773            4 : paths_output_cc_tests ()
    2774              : {
    2775            4 :   pretty_printer pp;
    2776            4 :   pp_show_color (&pp) = false;
    2777              : 
    2778            4 :   ::selftest::auto_fix_quotes fix_quotes;
    2779            4 :   diagnostics::paths::selftest::test_empty_path (&pp);
    2780            4 :   diagnostics::paths::selftest::test_intraprocedural_path (&pp);
    2781            4 :   diagnostics::paths::selftest::test_interprocedural_path_1 (&pp);
    2782            4 :   diagnostics::paths::selftest::test_interprocedural_path_2 (&pp);
    2783            4 :   diagnostics::paths::selftest::test_recursion (&pp);
    2784            4 :   for_each_line_table_case (diagnostics::paths::selftest::control_flow_tests);
    2785            4 : }
    2786              : 
    2787              : } // namespace diagnostics::selftest
    2788              : } // namespace diagnostics
    2789              : 
    2790              : #if __GNUC__ >= 10
    2791              : #  pragma GCC diagnostic pop
    2792              : #endif
    2793              : 
    2794              : #endif /* #if CHECKING_P */
        

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.