LCOV - code coverage report
Current view: top level - gcc/analyzer - diagnostic-manager.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 86.4 % 1123 970
Test Date: 2026-02-28 14:20:25 Functions: 92.6 % 68 63
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Classes for saving, deduplicating, and emitting analyzer diagnostics.
       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
       8              : under the terms of the GNU General Public License as published by
       9              : the Free Software Foundation; either version 3, or (at your option)
      10              : any later version.
      11              : 
      12              : GCC is distributed in the hope that it will be useful, but
      13              : WITHOUT ANY WARRANTY; without even the implied warranty of
      14              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15              : General Public License 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 "analyzer/common.h"
      22              : 
      23              : #include "cfg.h"
      24              : #include "gimple-pretty-print.h"
      25              : #include "gimple-iterator.h"
      26              : #include "inlining-iterator.h"
      27              : #include "cgraph.h"
      28              : #include "digraph.h"
      29              : #include "gcc-rich-location.h"
      30              : #include "diagnostics/sarif-sink.h"
      31              : 
      32              : #include "analyzer/analyzer-logging.h"
      33              : #include "analyzer/sm.h"
      34              : #include "analyzer/pending-diagnostic.h"
      35              : #include "analyzer/diagnostic-manager.h"
      36              : #include "analyzer/call-string.h"
      37              : #include "analyzer/program-point.h"
      38              : #include "analyzer/store.h"
      39              : #include "analyzer/region-model.h"
      40              : #include "analyzer/constraint-manager.h"
      41              : #include "analyzer/supergraph.h"
      42              : #include "analyzer/program-state.h"
      43              : #include "analyzer/exploded-graph.h"
      44              : #include "analyzer/trimmed-graph.h"
      45              : #include "analyzer/feasible-graph.h"
      46              : #include "analyzer/checker-path.h"
      47              : #include "analyzer/reachability.h"
      48              : 
      49              : #if ENABLE_ANALYZER
      50              : 
      51              : namespace ana {
      52              : 
      53              : class feasible_worklist;
      54              : 
      55              : // struct pending_location
      56              : 
      57          921 : pending_location::pending_location ()
      58          921 : : m_enode (nullptr),
      59          921 :   m_event_loc_info (UNKNOWN_LOCATION, NULL_TREE, 0)
      60              : {
      61          921 : }
      62              : 
      63         7043 : pending_location::pending_location (exploded_node *enode)
      64         7043 : : m_enode (enode),
      65         7043 :   m_event_loc_info (enode)
      66              : {
      67         7043 : }
      68              : 
      69         3582 : pending_location::pending_location (exploded_node *enode,
      70         3582 :                                     location_t loc)
      71         3582 : : m_enode (enode),
      72         3582 :   m_event_loc_info (enode)
      73              : {
      74         3582 :   m_event_loc_info.m_loc = loc;
      75         3582 : }
      76              : 
      77              : std::unique_ptr<json::object>
      78           36 : pending_location::to_json () const
      79              : {
      80           36 :   auto ploc_obj = std::make_unique<json::object> ();
      81              : 
      82           36 :   if (m_enode)
      83           36 :     ploc_obj->set_integer ("enode", m_enode->m_index);
      84              :   // TODO: also store m_event_loc_info
      85           36 :   return ploc_obj;
      86              : }
      87              : 
      88              : /* State for finding the shortest feasible exploded_path for a
      89              :    saved_diagnostic.
      90              :    This is shared between all diagnostics, so that we avoid repeating work.  */
      91              : 
      92              : class epath_finder
      93              : {
      94              : public:
      95         1556 :   epath_finder (const exploded_graph &eg)
      96         1556 :   : m_eg (eg),
      97         1556 :     m_sep (nullptr)
      98              :   {
      99              :     /* This is shared by all diagnostics, but only needed if
     100              :        !flag_analyzer_feasibility.  */
     101         1556 :     if (!flag_analyzer_feasibility)
     102            4 :       m_sep = new shortest_exploded_paths (eg, eg.get_origin (),
     103            4 :                                            SPS_FROM_GIVEN_ORIGIN);
     104         1556 :   }
     105              : 
     106         1560 :   ~epath_finder () { delete m_sep; }
     107              : 
     108       153340 :   logger *get_logger () const { return m_eg.get_logger (); }
     109              : 
     110              :   std::unique_ptr<exploded_path>
     111              :   get_best_epath (const exploded_node *target_enode,
     112              :                   const pending_diagnostic &pd,
     113              :                   const char *desc, unsigned diag_idx,
     114              :                   std::unique_ptr<feasibility_problem> *out_problem);
     115              : 
     116              : private:
     117              :   DISABLE_COPY_AND_ASSIGN(epath_finder);
     118              : 
     119              :   std::unique_ptr<exploded_path>
     120              :   explore_feasible_paths (const exploded_node *target_enode,
     121              :                           const pending_diagnostic &pd,
     122              :                           const char *desc, unsigned diag_idx);
     123              :   bool
     124              :   process_worklist_item (feasible_worklist *worklist,
     125              :                          const trimmed_graph &tg,
     126              :                          feasible_graph *fg,
     127              :                          const exploded_node *target_enode,
     128              :                          const pending_diagnostic &pd,
     129              :                          unsigned diag_idx,
     130              :                          std::unique_ptr<exploded_path> *out_best_path) const;
     131              :   void dump_trimmed_graph (const exploded_node *target_enode,
     132              :                            const char *desc, unsigned diag_idx,
     133              :                            const trimmed_graph &tg,
     134              :                            const shortest_paths<eg_traits, exploded_path> &sep);
     135              :   void dump_feasible_graph (const exploded_node *target_enode,
     136              :                             const char *desc, unsigned diag_idx,
     137              :                             const feasible_graph &fg);
     138              :   void dump_feasible_path (const exploded_node *target_enode,
     139              :                            unsigned diag_idx,
     140              :                            const feasible_graph &fg,
     141              :                            const feasible_node &fnode) const;
     142              : 
     143              :   const exploded_graph &m_eg;
     144              :   shortest_exploded_paths *m_sep;
     145              : };
     146              : 
     147              : /* class epath_finder.  */
     148              : 
     149              : /* Get the "best" exploded_path for reaching TARGET_ENODE from the origin,
     150              :    returning ownership of it to the caller.
     151              : 
     152              :    Ideally we want to report the shortest feasible path.
     153              :    Return nullptr if we could not find a feasible path
     154              :    (when flag_analyzer_feasibility is true).
     155              : 
     156              :    If flag_analyzer_feasibility is false, then simply return the
     157              :    shortest path.
     158              : 
     159              :    Use DESC and DIAG_IDX when logging.
     160              : 
     161              :    Write any feasibility_problem to *OUT_PROBLEM.  */
     162              : 
     163              : std::unique_ptr<exploded_path>
     164         6375 : epath_finder::get_best_epath (const exploded_node *target_enode,
     165              :                               const pending_diagnostic &pd,
     166              :                               const char *desc, unsigned diag_idx,
     167              :                               std::unique_ptr<feasibility_problem> *out_problem)
     168              : {
     169         6375 :   logger *logger = get_logger ();
     170         6375 :   LOG_SCOPE (logger);
     171              : 
     172         6375 :   unsigned snode_id = target_enode->get_supernode ()->m_id;
     173         6375 :   if (logger)
     174            2 :     logger->log ("considering %qs at EN: %i, SN: %i (sd: %i)",
     175            2 :                  desc, target_enode->m_index, snode_id, diag_idx);
     176              : 
     177              :   /* State-merging means that not every path in the egraph corresponds
     178              :      to a feasible one w.r.t. states.
     179              : 
     180              :      We want to find the shortest feasible path from the origin to ENODE
     181              :      in the egraph.  */
     182              : 
     183         6375 :   if (flag_analyzer_feasibility)
     184              :     {
     185              :       /* Attempt to find the shortest feasible path using feasible_graph.  */
     186         6371 :       if (logger)
     187            2 :         logger->log ("trying to find shortest feasible path");
     188         6371 :       if (std::unique_ptr<exploded_path> epath
     189         6371 :             = explore_feasible_paths (target_enode, pd, desc, diag_idx))
     190              :         {
     191         6089 :           if (logger)
     192            2 :             logger->log ("accepting %qs at EN: %i, SN: %i (sd: %i)"
     193              :                          " with feasible path (length: %i)",
     194            2 :                          desc, target_enode->m_index, snode_id, diag_idx,
     195              :                          epath->length ());
     196         6089 :           return epath;
     197              :         }
     198              :       else
     199              :         {
     200          282 :           if (logger)
     201            0 :             logger->log ("rejecting %qs at EN: %i, SN: %i (sd: %i)"
     202              :                          " due to not finding feasible path",
     203            0 :                          desc, target_enode->m_index, snode_id, diag_idx);
     204          282 :           return nullptr;
     205         6371 :         }
     206              :     }
     207              :   else
     208              :     {
     209              :       /* As a crude approximation to shortest feasible path, simply find
     210              :          the shortest path, and note whether it is feasible.
     211              :          There could be longer feasible paths within the egraph, so this
     212              :          approach would lead to diagnostics being falsely rejected
     213              :          (PR analyzer/96374).  */
     214            4 :       if (logger)
     215            0 :         logger->log ("trying to find shortest path ignoring feasibility");
     216            4 :       gcc_assert (m_sep);
     217            4 :       auto epath
     218              :         = std::make_unique<exploded_path>
     219            4 :             (m_sep->get_shortest_path (target_enode));
     220            4 :       if (epath->feasible_p (logger, out_problem, m_eg.get_engine (), &m_eg))
     221              :         {
     222            0 :           if (logger)
     223            0 :             logger->log ("accepting %qs at EN: %i, SN: %i (sn: %i)"
     224              :                          " with feasible path (length: %i)",
     225            0 :                          desc, target_enode->m_index, snode_id, diag_idx,
     226              :                          epath->length ());
     227              :         }
     228              :       else
     229              :         {
     230            4 :           if (logger)
     231            0 :             logger->log ("accepting %qs at EN: %i, SN: %i (sn: %i) (length: %i)"
     232              :                          " despite infeasible path (due to %qs)",
     233            0 :                          desc, target_enode->m_index, snode_id, diag_idx,
     234              :                          epath->length (),
     235              :                          "-fno-analyzer-feasibility");
     236              :         }
     237            4 :       return epath;
     238            4 :     }
     239         6375 : }
     240              : 
     241              : /* A class for managing the worklist of feasible_nodes in
     242              :    epath_finder::explore_feasible_paths, prioritizing them
     243              :    so that shorter paths appear earlier in the queue.  */
     244              : 
     245        12742 : class feasible_worklist
     246              : {
     247              : public:
     248         6371 :   feasible_worklist (const shortest_paths<eg_traits, exploded_path> &sep)
     249        12742 :   : m_queue (key_t (*this, nullptr)),
     250         6371 :     m_sep (sep)
     251              :   {
     252         6371 :   }
     253              : 
     254       134219 :   feasible_node *take_next () { return m_queue.extract_min (); }
     255              : 
     256       137488 :   void add_node (feasible_node *fnode)
     257              :   {
     258       274976 :     m_queue.insert (key_t (*this, fnode), fnode);
     259       131117 :   }
     260              : 
     261              : private:
     262              :   struct key_t
     263              :   {
     264       143859 :     key_t (const feasible_worklist &w, feasible_node *fnode)
     265         6371 :     : m_worklist (w), m_fnode (fnode)
     266              :     {}
     267              : 
     268       147191 :     bool operator< (const key_t &other) const
     269              :     {
     270       147191 :       return cmp (*this, other) < 0;
     271              :     }
     272              : 
     273        45761 :     bool operator== (const key_t &other) const
     274              :     {
     275        45761 :       return cmp (*this, other) == 0;
     276              :     }
     277              : 
     278        45761 :     bool operator> (const key_t &other) const
     279              :     {
     280        45761 :       return !(*this == other || *this < other);
     281              :     }
     282              : 
     283              :   private:
     284       192952 :     static int cmp (const key_t &ka, const key_t &kb)
     285              :     {
     286              :       /* Choose the node for which if the remaining path were feasible,
     287              :          it would be the shortest path (summing the length of the
     288              :          known-feasible path so far with that of the remaining
     289              :          possibly-feasible path).  */
     290       192952 :       int ca = ka.m_worklist.get_estimated_cost (ka.m_fnode);
     291       192952 :       int cb = kb.m_worklist.get_estimated_cost (kb.m_fnode);
     292       192952 :       return ca - cb;
     293              :     }
     294              : 
     295              :     const feasible_worklist &m_worklist;
     296              :     feasible_node *m_fnode;
     297              :   };
     298              : 
     299              :   /* Get the estimated length of a path involving FNODE from
     300              :      the origin to the target enode.
     301              :      Sum the length of the known-feasible path so far with
     302              :      that of the remaining possibly-feasible path.  */
     303              : 
     304       385904 :   int get_estimated_cost (const feasible_node *fnode) const
     305              :   {
     306       385904 :     unsigned length_so_far = fnode->get_path_length ();
     307       385904 :     int shortest_remaining_path
     308       385904 :       = m_sep.get_shortest_distance (fnode->get_inner_node ());
     309              : 
     310       385904 :     gcc_assert (shortest_remaining_path >= 0);
     311              :     /* This should be true since we're only exploring nodes within
     312              :        the trimmed graph (and we anticipate it being much smaller
     313              :        than this, and thus not overflowing the sum).  */
     314       385904 :     gcc_assert (shortest_remaining_path < INT_MAX);
     315              : 
     316       385904 :     return length_so_far + shortest_remaining_path;
     317              :   }
     318              : 
     319              :   /* Priority queue, backed by a fibonacci_heap.  */
     320              :   typedef fibonacci_heap<key_t, feasible_node> queue_t;
     321              :   queue_t m_queue;
     322              :   const shortest_paths<eg_traits, exploded_path> &m_sep;
     323              : };
     324              : 
     325              : /* When we're building the exploded graph we want to simplify
     326              :    overly-complicated symbolic values down to "UNKNOWN" to try to avoid
     327              :    state explosions and unbounded chains of exploration.
     328              : 
     329              :    However, when we're building the feasibility graph for a diagnostic
     330              :    (actually a tree), we don't want UNKNOWN values, as conditions on them
     331              :    are also unknown: we don't want to have a contradiction such as a path
     332              :    where (VAL != 0) and then (VAL == 0) along the same path.
     333              : 
     334              :    Hence this is an RAII class for temporarily disabling complexity-checking
     335              :    in the region_model_manager, for use within
     336              :    epath_finder::explore_feasible_paths.
     337              : 
     338              :    We also disable the creation of unknown_svalue instances during feasibility
     339              :    checking, instead creating unique svalues, to avoid paradoxes in paths.  */
     340              : 
     341              : class auto_checking_feasibility
     342              : {
     343              : public:
     344         6371 :   auto_checking_feasibility (region_model_manager *mgr) : m_mgr (mgr)
     345              :   {
     346         6371 :     m_mgr->begin_checking_feasibility ();
     347         6371 :   }
     348         6371 :   ~auto_checking_feasibility ()
     349              :   {
     350         6371 :     m_mgr->end_checking_feasibility ();
     351              :   }
     352              : private:
     353              :   region_model_manager *m_mgr;
     354              : };
     355              : 
     356              : /* Attempt to find the shortest feasible path from the origin to
     357              :    TARGET_ENODE by iteratively building a feasible_graph, in which
     358              :    every path to a feasible_node is feasible by construction.
     359              : 
     360              :    We effectively explore the tree of feasible paths in order of shortest
     361              :    path until we either find a feasible path to TARGET_ENODE, or hit
     362              :    a limit and give up.
     363              : 
     364              :    Preliminaries:
     365              :    - Find the shortest path from each node to the TARGET_ENODE (without
     366              :    checking feasibility), so that we can prioritize our worklist.
     367              :    - Construct a trimmed_graph: the subset of nodes/edges that
     368              :    are on a path that eventually reaches TARGET_ENODE.  We will only need
     369              :    to consider these when considering the shortest feasible path.
     370              : 
     371              :    Build a feasible_graph, in which every path to a feasible_node
     372              :    is feasible by construction.
     373              :    We use a worklist to flatten the exploration into an iteration.
     374              :    Starting at the origin, find feasible out-edges within the trimmed graph.
     375              :    At each stage, choose the node for which if the remaining path were feasible,
     376              :    it would be the shortest path (summing the length of the known-feasible path
     377              :    so far with that of the remaining possibly-feasible path).
     378              :    This way, the first feasible path we find to TARGET_ENODE is the shortest.
     379              :    We start by trying the shortest possible path, but if that fails,
     380              :    we explore progressively longer paths, eventually trying iterations through
     381              :    loops.  The exploration is captured in the feasible_graph, which can be
     382              :    dumped as a .dot file to visualize the exploration.  The indices of the
     383              :    feasible_nodes show the order in which they were created.
     384              : 
     385              :    This is something of a brute-force approach, but the trimmed_graph
     386              :    hopefully keeps the complexity manageable.
     387              : 
     388              :    Terminate with failure when the number of infeasible edges exceeds
     389              :    a threshold (--param=analyzer-max-infeasible-edges=).
     390              :    This is guaranteed to eventually lead to terminatation, as
     391              :    we can't keep creating feasible nodes without eventually
     392              :    either reaching an infeasible edge, or reaching the
     393              :    TARGET_ENODE.  Specifically, there can't be a cycle of
     394              :    feasible edges that doesn't reach the target_enode without
     395              :    an out-edge that either fails feasibility or gets closer
     396              :    to the TARGET_ENODE: on each iteration we are either:
     397              :    - effectively getting closer to the TARGET_ENODE (which can't
     398              :      continue forever without reaching the target), or
     399              :    - getting monotonically closer to the termination threshold.  */
     400              : 
     401              : std::unique_ptr<exploded_path>
     402         6371 : epath_finder::explore_feasible_paths (const exploded_node *target_enode,
     403              :                                       const pending_diagnostic &pd,
     404              :                                       const char *desc, unsigned diag_idx)
     405              : {
     406         6371 :   logger *logger = get_logger ();
     407         6371 :   LOG_SCOPE (logger);
     408              : 
     409         6371 :   region_model_manager *mgr = m_eg.get_engine ()->get_model_manager ();
     410              : 
     411              :   /* Determine the shortest path to TARGET_ENODE from each node in
     412              :      the exploded graph.  */
     413         6371 :   shortest_paths<eg_traits, exploded_path> sep
     414         6371 :     (m_eg, target_enode, SPS_TO_GIVEN_TARGET);
     415              : 
     416              :   /* Construct a trimmed_graph: the subset of nodes/edges that
     417              :      are on a path that eventually reaches TARGET_ENODE.
     418              :      We only need to consider these when considering the shortest
     419              :      feasible path.  */
     420         6371 :   trimmed_graph tg (m_eg, target_enode);
     421              : 
     422         6371 :   if (flag_dump_analyzer_feasibility)
     423            4 :     dump_trimmed_graph (target_enode, desc, diag_idx, tg, sep);
     424              : 
     425         6371 :   feasible_graph fg;
     426         6371 :   feasible_worklist worklist (sep);
     427              : 
     428              :   /* Populate the worklist with the origin node.  */
     429         6371 :   {
     430         6371 :     feasibility_state init_state (mgr, m_eg.get_supergraph ());
     431         6371 :     feasible_node *origin = fg.add_node (m_eg.get_origin (), init_state, 0);
     432         6371 :     worklist.add_node (origin);
     433         6371 :   }
     434              : 
     435              :   /* Iteratively explore the tree of feasible paths in order of shortest
     436              :      path until we either find a feasible path to TARGET_ENODE, or hit
     437              :      a limit.  */
     438              : 
     439              :   /* Set this if we find a feasible path to TARGET_ENODE.  */
     440         6371 :   std::unique_ptr<exploded_path> best_path = nullptr;
     441              : 
     442         6371 :   {
     443         6371 :     auto_checking_feasibility sentinel (mgr);
     444              : 
     445       134219 :     while (process_worklist_item (&worklist, tg, &fg, target_enode,
     446              :                                   pd, diag_idx, &best_path))
     447              :       {
     448              :         /* Empty; the work is done within process_worklist_item.  */
     449              :       }
     450         6371 :   }
     451              : 
     452         6371 :   if (logger)
     453              :     {
     454            2 :       logger->log ("tg for sd: %i:", diag_idx);
     455            2 :       logger->inc_indent ();
     456            2 :       tg.log_stats (logger);
     457            2 :       logger->dec_indent ();
     458              : 
     459            2 :       logger->log ("fg for sd: %i:", diag_idx);
     460            2 :       logger->inc_indent ();
     461            2 :       fg.log_stats (logger);
     462            2 :       logger->dec_indent ();
     463              :     }
     464              : 
     465              :   /* Dump the feasible_graph.  */
     466         6371 :   if (flag_dump_analyzer_feasibility)
     467            4 :     dump_feasible_graph (target_enode, desc, diag_idx, fg);
     468              : 
     469         6371 :   return best_path;
     470        12742 : }
     471              : 
     472              : /* Process the next item in WORKLIST, potentially adding new items
     473              :    based on feasible out-edges, and extending FG accordingly.
     474              :    Use TG to ignore out-edges that don't lead to TARGET_ENODE.
     475              :    Return true if the worklist processing should continue.
     476              :    Return false if the processing of the worklist should stop
     477              :    (either due to reaching TARGET_ENODE, or hitting a limit).
     478              :    Write to *OUT_BEST_PATH if stopping due to finding a feasible path
     479              :    to TARGET_ENODE.
     480              :    Use PD to provide additional restrictions on feasibility of
     481              :    the final path in the feasible_graph before converting to
     482              :    an exploded_path.  */
     483              : 
     484              : bool
     485       134219 : epath_finder::
     486              : process_worklist_item (feasible_worklist *worklist,
     487              :                        const trimmed_graph &tg,
     488              :                        feasible_graph *fg,
     489              :                        const exploded_node *target_enode,
     490              :                        const pending_diagnostic &pd,
     491              :                        unsigned diag_idx,
     492              :                        std::unique_ptr<exploded_path> *out_best_path) const
     493              : {
     494       134219 :   logger *logger = get_logger ();
     495              : 
     496       134219 :   feasible_node *fnode = worklist->take_next ();
     497       134219 :   if (!fnode)
     498              :     {
     499           63 :       if (logger)
     500            0 :         logger->log ("drained worklist for sd: %i"
     501              :                      " without finding feasible path",
     502              :                      diag_idx);
     503           63 :       return false;
     504              :     }
     505              : 
     506       134156 :   log_scope s (logger, "fg worklist item",
     507              :                "considering FN: %i (EN: %i) for sd: %i",
     508       134156 :                fnode->get_index (), fnode->get_inner_node ()->m_index,
     509       134156 :                diag_idx);
     510              : 
     511              :   /* Iterate through all out-edges from this item.  */
     512       134156 :   unsigned i;
     513       134156 :   exploded_edge *succ_eedge;
     514       320710 :   FOR_EACH_VEC_ELT (fnode->get_inner_node ()->m_succs, i, succ_eedge)
     515              :     {
     516       192862 :       log_scope s (logger, "edge", "considering edge: EN:%i -> EN:%i",
     517       192862 :                    succ_eedge->m_src->m_index,
     518       192862 :                    succ_eedge->m_dest->m_index);
     519              :       /* Reject edges that aren't in the trimmed graph.  */
     520       192862 :       if (!tg.contains_p (succ_eedge))
     521              :         {
     522        53337 :           if (logger)
     523            1 :             logger->log ("rejecting: not in trimmed graph");
     524        53337 :           continue;
     525              :         }
     526              : 
     527       139525 :       feasibility_state succ_state (fnode->get_state ());
     528       139525 :       std::unique_ptr<rejected_constraint> rc;
     529       139525 :       if (succ_state.maybe_update_for_edge (logger, succ_eedge, nullptr, &rc))
     530              :         {
     531       137268 :           gcc_assert (rc == nullptr);
     532       137268 :           feasible_node *succ_fnode
     533       137268 :             = fg->add_node (succ_eedge->m_dest,
     534              :                             succ_state,
     535       137268 :                             fnode->get_path_length () + 1);
     536       137268 :           if (logger)
     537           14 :             logger->log ("accepting as FN: %i", succ_fnode->get_index ());
     538       137268 :           fg->add_edge (new feasible_edge (fnode, succ_fnode, succ_eedge));
     539              : 
     540              :           /* Have we reached TARGET_ENODE?  */
     541       137268 :           if (succ_fnode->get_inner_node () == target_enode)
     542              :             {
     543         6151 :               if (logger)
     544            2 :                 logger->log ("success: got feasible path to EN: %i (sd: %i)"
     545              :                              " (length: %i)",
     546            2 :                              target_enode->m_index, diag_idx,
     547              :                              succ_fnode->get_path_length ());
     548         6151 :               if (!pd.check_valid_fpath_p (*succ_fnode))
     549              :                 {
     550           62 :                   if (logger)
     551            0 :                     logger->log ("rejecting feasible path due to"
     552              :                                  " pending_diagnostic");
     553           62 :                   return false;
     554              :                 }
     555         6089 :               *out_best_path = fg->make_epath (succ_fnode);
     556         6089 :               if (flag_dump_analyzer_feasibility)
     557            4 :                 dump_feasible_path (target_enode, diag_idx, *fg, *succ_fnode);
     558              : 
     559              :               /* Success: stop the worklist iteration.  */
     560         6089 :               return false;
     561              :             }
     562              :           else
     563       131117 :             worklist->add_node (succ_fnode);
     564              :         }
     565              :       else
     566              :         {
     567         2257 :           if (logger)
     568            0 :             logger->log ("infeasible");
     569         2257 :           gcc_assert (rc);
     570         2257 :           fg->add_feasibility_problem (fnode,
     571              :                                        succ_eedge,
     572              :                                        std::move (rc));
     573              : 
     574              :           /* Give up if there have been too many infeasible edges.  */
     575         2257 :           if (fg->get_num_infeasible ()
     576         2257 :               > (unsigned)param_analyzer_max_infeasible_edges)
     577              :             {
     578          157 :               if (logger)
     579            0 :                 logger->log ("too many infeasible edges (%i); giving up",
     580              :                              fg->get_num_infeasible ());
     581          157 :               return false;
     582              :             }
     583              :         }
     584       192862 :     }
     585              : 
     586              :   /* Continue the worklist iteration.  */
     587              :   return true;
     588       134156 : }
     589              : 
     590              : /* Helper class for epath_finder::dump_trimmed_graph
     591              :    to dump extra per-node information.
     592              :    Use SEP to add the length of the shortest path from each
     593              :    node to the target node to each node's dump.  */
     594              : 
     595              : class dump_eg_with_shortest_path : public eg_traits::dump_args_t
     596              : {
     597              : public:
     598            4 :   dump_eg_with_shortest_path
     599              :     (const exploded_graph &eg,
     600              :      const shortest_paths<eg_traits, exploded_path> &sep)
     601            4 :   : dump_args_t (eg),
     602            4 :     m_sep (sep)
     603              :   {
     604              :   }
     605              : 
     606           74 :   void dump_extra_info (const exploded_node *enode,
     607              :                         pretty_printer *pp) const final override
     608              :   {
     609          144 :     pp_printf (pp, "sp: %i", m_sep.get_shortest_path (enode).length ());
     610           74 :     pp_newline (pp);
     611           74 :   }
     612              : 
     613              : private:
     614              :   const shortest_paths<eg_traits, exploded_path> &m_sep;
     615              : };
     616              : 
     617              : /* Dump TG to "BASE_NAME.DESC.DIAG_IDX.to-enN.tg.dot",
     618              :    annotating each node with the length of the shortest path
     619              :    from that node to TARGET_ENODE (using SEP).  */
     620              : 
     621              : void
     622            4 : epath_finder::
     623              : dump_trimmed_graph (const exploded_node *target_enode,
     624              :                     const char *desc, unsigned diag_idx,
     625              :                     const trimmed_graph &tg,
     626              :                     const shortest_paths<eg_traits, exploded_path> &sep)
     627              : {
     628            4 :   auto_timevar tv (TV_ANALYZER_DUMP);
     629            4 :   dump_eg_with_shortest_path inner_args (m_eg, sep);
     630            4 :   trimmed_graph::dump_args_t args (inner_args);
     631            4 :   pretty_printer pp;
     632            4 :   pp_printf (&pp, "%s.%s.%i.to-en%i.tg.dot",
     633            4 :              dump_base_name, desc, diag_idx, target_enode->m_index);
     634            4 :   char *filename = xstrdup (pp_formatted_text (&pp));
     635            4 :   tg.dump_dot (filename, nullptr, args);
     636            4 :   free (filename);
     637            4 : }
     638              : 
     639              : /* Dump FG to "BASE_NAME.DESC.DIAG_IDX.to-enN.fg.dot".  */
     640              : 
     641              : void
     642            4 : epath_finder::dump_feasible_graph (const exploded_node *target_enode,
     643              :                                    const char *desc, unsigned diag_idx,
     644              :                                    const feasible_graph &fg)
     645              : {
     646            4 :   auto_timevar tv (TV_ANALYZER_DUMP);
     647            4 :   exploded_graph::dump_args_t inner_args (m_eg);
     648            4 :   feasible_graph::dump_args_t args (inner_args);
     649            4 :   pretty_printer pp;
     650            4 :   pp_printf (&pp, "%s.%s.%i.to-en%i.fg.dot",
     651            4 :              dump_base_name, desc, diag_idx, target_enode->m_index);
     652            4 :   char *filename = xstrdup (pp_formatted_text (&pp));
     653            4 :   fg.dump_dot (filename, nullptr, args);
     654            4 :   free (filename);
     655            4 : }
     656              : 
     657              : /* Dump the path to FNODE to "BASE_NAME.DIAG_IDX.to-enN.fpath.txt".  */
     658              : 
     659              : void
     660            4 : epath_finder::dump_feasible_path (const exploded_node *target_enode,
     661              :                                   unsigned diag_idx,
     662              :                                   const feasible_graph &fg,
     663              :                                   const feasible_node &fnode) const
     664              : {
     665            4 :   auto_timevar tv (TV_ANALYZER_DUMP);
     666            4 :   pretty_printer pp;
     667            4 :   pp_printf (&pp, "%s.%i.to-en%i.fpath.txt",
     668            4 :              dump_base_name, diag_idx, target_enode->m_index);
     669            4 :   char *filename = xstrdup (pp_formatted_text (&pp));
     670            4 :   fg.dump_feasible_path (fnode, filename);
     671            4 :   free (filename);
     672            4 : }
     673              : 
     674              : /* class saved_diagnostic.  */
     675              : 
     676              : /* saved_diagnostic's ctor.  */
     677              : 
     678         6375 : saved_diagnostic::saved_diagnostic (const state_machine *sm,
     679              :                                     pending_location &&ploc,
     680              :                                     tree var,
     681              :                                     const svalue *sval,
     682              :                                     state_machine::state_t state,
     683              :                                     std::unique_ptr<pending_diagnostic> d,
     684         6375 :                                     unsigned idx)
     685         6375 : : m_sm (sm),
     686         6375 :   m_ploc (std::move (ploc)),
     687         6375 :   m_var (var), m_sval (sval), m_state (state),
     688         6375 :   m_d (std::move (d)), m_trailing_eedge (nullptr),
     689         6375 :   m_idx (idx),
     690         6375 :   m_best_epath (nullptr), m_problem (nullptr),
     691         6375 :   m_notes ()
     692              : {
     693              :   /* We must have an enode in order to be able to look for paths
     694              :      through the exploded_graph to this diagnostic.  */
     695         6375 :   gcc_assert (m_ploc.m_enode);
     696         6375 : }
     697              : 
     698              : const supernode *
     699       131526 : saved_diagnostic::get_supernode () const
     700              : {
     701       131526 :   return m_ploc.m_enode->get_supernode ();
     702              : }
     703              : 
     704              : bool
     705        44107 : saved_diagnostic::operator== (const saved_diagnostic &other) const
     706              : {
     707        46253 :   if (m_notes.length () != other.m_notes.length ())
     708              :     return false;
     709        43655 :   for (unsigned i = 0; i < m_notes.length (); i++)
     710          734 :     if (!m_notes[i]->equal_p (*other.m_notes[i]))
     711              :       return false;
     712              : 
     713              :   // Don't deduplicate dump_path_diagnostic instances
     714        42921 :   if (!strcmp (m_d->get_kind (), "dump_path_diagnostic"))
     715         1980 :     return this == &other;
     716              : 
     717        40941 :   return (m_sm == other.m_sm
     718              :           /* We don't compare m_enode.  */
     719        35812 :           && get_supernode () == other.get_supernode ()
     720         6770 :           && (m_ploc.m_event_loc_info.m_loc
     721         6770 :               == other.m_ploc.m_event_loc_info.m_loc)
     722         6506 :           && pending_diagnostic::same_tree_p (m_var, other.m_var)
     723         6052 :           && m_state == other.m_state
     724         5990 :           && m_d->equal_p (*other.m_d)
     725        46765 :           && m_trailing_eedge == other.m_trailing_eedge);
     726              : }
     727              : 
     728              : /* Add PN to this diagnostic, taking ownership of it.  */
     729              : 
     730              : void
     731          194 : saved_diagnostic::add_note (std::unique_ptr<pending_note> pn)
     732              : {
     733          194 :   gcc_assert (pn);
     734          194 :   m_notes.safe_push (pn.release ());
     735          194 : }
     736              : 
     737              : /* Add EVENT to this diagnostic.  */
     738              : 
     739              : void
     740          159 : saved_diagnostic::add_event (std::unique_ptr<checker_event> event)
     741              : {
     742          159 :   gcc_assert (event);
     743          159 :   m_saved_events.safe_push (event.release ());
     744          159 : }
     745              : 
     746              : /* Return a new json::object of the form
     747              :    {"sm": optional str,
     748              :     "ploc": {},
     749              :     "sval": optional str,
     750              :     "state": optional str,
     751              :     "path_length": optional int,
     752              :     "pending_diagnostic": str,
     753              :     "idx": int}.  */
     754              : 
     755              : std::unique_ptr<json::object>
     756            0 : saved_diagnostic::to_json () const
     757              : {
     758            0 :   auto sd_obj = std::make_unique<json::object> ();
     759              : 
     760            0 :   if (m_sm)
     761            0 :     sd_obj->set_string ("sm", m_sm->get_name ());
     762            0 :   sd_obj->set ("ploc", m_ploc.to_json ());
     763            0 :   if (m_sval)
     764            0 :     sd_obj->set ("sval", m_sval->to_json ());
     765            0 :   if (m_state)
     766            0 :     sd_obj->set ("state", m_state->to_json ());
     767            0 :   if (m_best_epath)
     768            0 :     sd_obj->set_integer ("path_length", get_epath_length ());
     769            0 :   sd_obj->set_string ("pending_diagnostic", m_d->get_kind ());
     770            0 :   sd_obj->set_integer ("idx", m_idx);
     771              : 
     772            0 :   return sd_obj;
     773              : }
     774              : 
     775              : /* Dump this to PP in a form suitable for use as an id in .dot output.  */
     776              : 
     777              : void
     778           16 : saved_diagnostic::dump_dot_id (pretty_printer *pp) const
     779              : {
     780           16 :   pp_printf (pp, "sd_%i", m_idx);
     781           16 : }
     782              : 
     783              : /* Dump this to PP in a form suitable for use as a node in .dot output.  */
     784              : 
     785              : void
     786            8 : saved_diagnostic::dump_as_dot_node (pretty_printer *pp) const
     787              : {
     788            8 :   dump_dot_id (pp);
     789            8 :   pp_printf (pp,
     790              :              " [shape=none,margin=0,style=filled,fillcolor=\"red\",label=\"");
     791            8 :   pp_write_text_to_stream (pp);
     792              : 
     793              :   /* Node label.  */
     794            8 :   pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)\n",
     795            8 :              m_d->get_kind (), m_idx);
     796            8 :   if (m_sm)
     797              :     {
     798            8 :       pp_printf (pp, "sm: %s", m_sm->get_name ());
     799            8 :       if (m_state)
     800              :         {
     801            8 :           pp_printf (pp, "; state: ");
     802            8 :           m_state->dump_to_pp (pp);
     803              :         }
     804            8 :       pp_newline (pp);
     805              :     }
     806            8 :   if (m_var)
     807            8 :     pp_printf (pp, "var: %qE\n", m_var);
     808            8 :   if (m_sval)
     809              :     {
     810            8 :       pp_string (pp, "sval: ");
     811            8 :       m_sval->dump_to_pp (pp, true);
     812            8 :       pp_newline (pp);
     813              :     }
     814            8 :   if (m_best_epath)
     815            0 :     pp_printf (pp, "path length: %i\n", get_epath_length ());
     816              : 
     817            8 :   pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
     818            8 :   pp_string (pp, "\"];\n\n");
     819              : 
     820              :   /* Show links to duplicates.  */
     821            8 :   for (auto iter : m_duplicates)
     822              :     {
     823            0 :       dump_dot_id (pp);
     824            0 :       pp_string (pp, " -> ");
     825            0 :       iter->dump_dot_id (pp);
     826            0 :       pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
     827            0 :       pp_newline (pp);
     828              :     }
     829            8 : }
     830              : 
     831              : /* Use PF to find the best exploded_path for this saved_diagnostic, if any,
     832              :    and store it in m_best_epath.
     833              :    Return true if a best path was found.  */
     834              : 
     835              : bool
     836         6375 : saved_diagnostic::calc_best_epath (epath_finder *pf)
     837              : {
     838         6375 :   logger *logger = pf->get_logger ();
     839         6375 :   LOG_SCOPE (logger);
     840         6375 :   m_problem = nullptr;
     841              : 
     842         6375 :   m_best_epath = pf->get_best_epath (m_ploc.m_enode,
     843         6375 :                                      *m_d, m_d->get_kind (), m_idx,
     844         6375 :                                      &m_problem);
     845              : 
     846              :   /* Handle failure to find a feasible path.  */
     847         6375 :   if (m_best_epath == nullptr)
     848          282 :     return false;
     849              : 
     850              :   gcc_assert (m_best_epath);
     851              : 
     852              :   return true;
     853         6375 : }
     854              : 
     855              : unsigned
     856         6622 : saved_diagnostic::get_epath_length () const
     857              : {
     858         6622 :   gcc_assert (m_best_epath);
     859         6622 :   return m_best_epath->length ();
     860              : }
     861              : 
     862              : /* Record that OTHER (and its duplicates) are duplicates
     863              :    of this saved_diagnostic.  */
     864              : 
     865              : void
     866         2064 : saved_diagnostic::add_duplicate (saved_diagnostic *other)
     867              : {
     868         2064 :   gcc_assert (other);
     869         2064 :   m_duplicates.reserve (m_duplicates.length ()
     870         2168 :                         + other->m_duplicates.length ()
     871              :                         + 1);
     872         2064 :   m_duplicates.splice (other->m_duplicates);
     873         2064 :   other->m_duplicates.truncate (0);
     874         2064 :   m_duplicates.safe_push (other);
     875         2064 : }
     876              : 
     877              : /* Walk up the sedges of each of the two paths.
     878              :    If the two sequences of sedges do not perfectly correspond,
     879              :    then paths are incompatible.
     880              :    If there is at least one sedge that either cannot be paired up
     881              :    or its counterpart is not equal, then the paths are incompatible
     882              :    and this function returns FALSE.
     883              :    Otherwise return TRUE.
     884              : 
     885              :    Incompatible paths:
     886              : 
     887              :        <cond Y>
     888              :        /      \
     889              :       /        \
     890              :     true      false
     891              :      |          |
     892              :     ...        ...
     893              :      |          |
     894              :     ...       stmt x
     895              :      |
     896              :    stmt x
     897              : 
     898              :    Both LHS_PATH and RHS_PATH final enodes should be
     899              :    over the same gimple statement.  */
     900              : 
     901              : static bool
     902           70 : compatible_epath_p (const exploded_path *lhs_path,
     903              :                     const exploded_path *rhs_path)
     904              : {
     905           70 :   gcc_assert (lhs_path);
     906           70 :   gcc_assert (rhs_path);
     907           70 :   gcc_assert (rhs_path->length () > 0);
     908           70 :   gcc_assert (rhs_path->length () > 0);
     909           70 :   int lhs_eedge_idx = lhs_path->length () - 1;
     910           70 :   int rhs_eedge_idx = rhs_path->length () - 1;
     911           70 :   const exploded_edge *lhs_eedge;
     912           70 :   const exploded_edge *rhs_eedge;
     913              : 
     914         1061 :   while (lhs_eedge_idx >= 0 && rhs_eedge_idx >= 0)
     915              :     {
     916         1130 :       while (lhs_eedge_idx >= 0)
     917              :         {
     918              :           /* Find LHS_PATH's next superedge.  */
     919         1092 :           lhs_eedge = lhs_path->m_edges[lhs_eedge_idx];
     920         1092 :           if (lhs_eedge->m_sedge)
     921              :             break;
     922              :           else
     923           69 :             lhs_eedge_idx--;
     924              :         }
     925         1130 :       while (rhs_eedge_idx >= 0)
     926              :         {
     927              :           /* Find RHS_PATH's next superedge.  */
     928         1092 :           rhs_eedge = rhs_path->m_edges[rhs_eedge_idx];
     929         1092 :           if (rhs_eedge->m_sedge)
     930              :             break;
     931              :           else
     932           69 :             rhs_eedge_idx--;
     933              :         }
     934              : 
     935         1061 :       if (lhs_eedge->m_sedge && rhs_eedge->m_sedge)
     936              :         {
     937         1023 :           if (lhs_eedge->m_sedge != rhs_eedge->m_sedge)
     938              :             /* Both superedges do not match.
     939              :                Superedges are not dependent on the exploded path, so even
     940              :                different epaths will have similar sedges if they follow
     941              :                the same outcome of a conditional node.  */
     942              :             return false;
     943              : 
     944          991 :           lhs_eedge_idx--;
     945          991 :           rhs_eedge_idx--;
     946          991 :           continue;
     947              :         }
     948           38 :       else if (lhs_eedge->m_sedge == nullptr && rhs_eedge->m_sedge == nullptr)
     949              :         /* Both paths were drained up entirely.
     950              :            No discriminant was found.  */
     951              :         return true;
     952              : 
     953              :       /* A superedge was found for only one of the two paths.  */
     954              :       return false;
     955              :     }
     956              : 
     957              :   /* A superedge was found for only one of the two paths.  */
     958            0 :   if (lhs_eedge_idx >= 0 || rhs_eedge_idx >= 0)
     959              :     return false;
     960              : 
     961              :   /* Both paths were drained up entirely.
     962              :      No discriminant was found.  */
     963              :   return true;
     964              : }
     965              : 
     966              : 
     967              : /* Return true if this diagnostic supercedes OTHER, and that OTHER should
     968              :    therefore not be emitted.  */
     969              : 
     970              : bool
     971        25958 : saved_diagnostic::supercedes_p (const saved_diagnostic &other) const
     972              : {
     973        25958 :   if (get_supernode () != other.get_supernode ())
     974              :     return false;
     975              : 
     976              :   /* return early if OTHER won't be superseded anyway.  */
     977         4907 :   if (!m_d->supercedes_p (*other.m_d))
     978              :     return false;
     979              : 
     980              :   /* If the two saved_diagnostics' path are not compatible
     981              :      then they cannot supersede one another.  */
     982           70 :   return compatible_epath_p (m_best_epath.get (), other.m_best_epath.get ());
     983              : }
     984              : 
     985              : /* Move any saved checker_events from this saved_diagnostic to
     986              :    the end of DST_PATH.  */
     987              : 
     988              : void
     989         3991 : saved_diagnostic::add_any_saved_events (checker_path &dst_path)
     990              : {
     991         4432 :   for (auto &event : m_saved_events)
     992              :     {
     993          147 :       dst_path.add_event (std::unique_ptr<checker_event> (event));
     994          147 :       event = nullptr;
     995              :     }
     996         3991 : }
     997              : 
     998              : /* Emit any pending notes owned by this diagnostic.  */
     999              : 
    1000              : void
    1001         3923 : saved_diagnostic::emit_any_notes () const
    1002              : {
    1003         4441 :   for (auto pn : m_notes)
    1004          178 :     pn->emit ();
    1005         3923 : }
    1006              : 
    1007              : /* For SARIF output, add additional properties to the "result" object
    1008              :    for this diagnostic.
    1009              :    This extra data is intended for use when debugging the analyzer.  */
    1010              : 
    1011              : void
    1012           36 : saved_diagnostic::
    1013              : maybe_add_sarif_properties (diagnostics::sarif_object &result_obj) const
    1014              : {
    1015           36 :   auto &props = result_obj.get_or_create_properties ();
    1016              : #define PROPERTY_PREFIX "gcc/analyzer/saved_diagnostic/"
    1017           36 :   if (m_sm)
    1018           16 :     props.set_string (PROPERTY_PREFIX "sm", m_sm->get_name ());
    1019           36 :   props.set (PROPERTY_PREFIX "ploc", m_ploc.to_json ());
    1020           36 :   if (m_var)
    1021           16 :     props.set (PROPERTY_PREFIX "var", tree_to_json (m_var));
    1022           36 :   if (m_sval)
    1023           16 :     props.set (PROPERTY_PREFIX "sval", m_sval->to_json ());
    1024           36 :   if (m_state)
    1025           16 :     props.set (PROPERTY_PREFIX "state", m_state->to_json ());
    1026              :   // TODO: m_best_epath
    1027           36 :   props.set_integer (PROPERTY_PREFIX "idx", m_idx);
    1028           36 :   if (m_duplicates.length () > 0)
    1029              :     {
    1030            4 :       auto duplicates_arr = std::make_unique<json::array> ();
    1031           16 :       for (auto iter : m_duplicates)
    1032              :         {
    1033            4 :           auto sd_obj = std::make_unique<diagnostics::sarif_object> ();
    1034            4 :           iter->maybe_add_sarif_properties (*sd_obj);
    1035            4 :           duplicates_arr->append (std::move (sd_obj));
    1036            4 :         }
    1037            4 :       props.set<json::array> (PROPERTY_PREFIX "duplicates",
    1038              :                               std::move (duplicates_arr));
    1039            4 :     }
    1040              : #undef PROPERTY_PREFIX
    1041              : 
    1042              : #define PROPERTY_PREFIX "gcc/analyzer/pending_diagnostic/"
    1043           36 :   props.set_string (PROPERTY_PREFIX "kind", m_d->get_kind ());
    1044              : #undef PROPERTY_PREFIX
    1045              : 
    1046              :   /* Potentially add pending_diagnostic-specific properties.  */
    1047           36 :   m_d->maybe_add_sarif_properties (result_obj);
    1048           36 : }
    1049              : 
    1050              : /* State for building a checker_path from a particular exploded_path.
    1051              :    In particular, this precomputes reachability information: the set of
    1052              :    source enodes for which a path be found to the diagnostic enode.  */
    1053              : 
    1054         3991 : class path_builder
    1055              : {
    1056              : public:
    1057         3991 :   path_builder (const exploded_graph &eg,
    1058              :                 const exploded_path &epath,
    1059              :                 const feasibility_problem *problem,
    1060              :                 const saved_diagnostic &sd)
    1061         3991 :   : m_eg (eg),
    1062         3991 :     m_diag_enode (epath.get_final_enode ()),
    1063         3991 :     m_sd (sd),
    1064         3991 :     m_reachability (eg, m_diag_enode),
    1065         3991 :     m_feasibility_problem (problem)
    1066         3991 :   {}
    1067              : 
    1068            0 :   const exploded_node *get_diag_node () const { return m_diag_enode; }
    1069              : 
    1070        52454 :   pending_diagnostic *get_pending_diagnostic () const
    1071              :   {
    1072          207 :     return m_sd.m_d.get ();
    1073              :   }
    1074              : 
    1075        30557 :   bool reachable_from_p (const exploded_node *src_enode) const
    1076              :   {
    1077        61114 :     return m_reachability.reachable_from_p (src_enode);
    1078              :   }
    1079              : 
    1080        42084 :   const extrinsic_state &get_ext_state () const { return m_eg.get_ext_state (); }
    1081              : 
    1082        40314 :   const feasibility_problem *get_feasibility_problem () const
    1083              :   {
    1084        40314 :     return m_feasibility_problem;
    1085              :   }
    1086              : 
    1087         4444 :   const state_machine *get_sm () const { return m_sd.m_sm; }
    1088              : 
    1089              : private:
    1090              :   typedef reachability<eg_traits> enode_reachability;
    1091              : 
    1092              :   const exploded_graph &m_eg;
    1093              : 
    1094              :   /* The enode where the diagnostic occurs.  */
    1095              :   const exploded_node *m_diag_enode;
    1096              : 
    1097              :   const saved_diagnostic &m_sd;
    1098              : 
    1099              :   /* Precompute all enodes from which the diagnostic is reachable.  */
    1100              :   enode_reachability m_reachability;
    1101              : 
    1102              :   const feasibility_problem *m_feasibility_problem;
    1103              : };
    1104              : 
    1105              : /* class diagnostic_manager.  */
    1106              : 
    1107              : /* diagnostic_manager's ctor.  */
    1108              : 
    1109         3377 : diagnostic_manager::diagnostic_manager (logger *logger, engine *eng,
    1110         3377 :                                         int verbosity)
    1111         3377 : : log_user (logger), m_eng (eng), m_verbosity (verbosity),
    1112         3377 :   m_num_disabled_diagnostics (0)
    1113              : {
    1114         3377 : }
    1115              : 
    1116              : /* Queue pending_diagnostic D at ENODE for later emission.
    1117              :    Return true/false signifying if the diagnostic was actually added.  */
    1118              : 
    1119              : bool
    1120        10478 : diagnostic_manager::add_diagnostic (const state_machine *sm,
    1121              :                                     pending_location &&ploc,
    1122              :                                     tree var,
    1123              :                                     const svalue *sval,
    1124              :                                     state_machine::state_t state,
    1125              :                                     std::unique_ptr<pending_diagnostic> d)
    1126              : {
    1127        10478 :   LOG_FUNC (get_logger ());
    1128              : 
    1129              :   /* We must have an enode in order to be able to look for paths
    1130              :      through the exploded_graph to the diagnostic.  */
    1131        10478 :   gcc_assert (ploc.m_enode);
    1132              : 
    1133              :   /* If this warning is ultimately going to be rejected by a -Wno-analyzer-*
    1134              :      flag, reject it now.  */
    1135        10478 :   {
    1136        10478 :     location_t loc = ploc.m_event_loc_info.m_loc;
    1137        10478 :     loc = d->fixup_location (loc, true);
    1138        10478 :     int option = d->get_controlling_option ();
    1139        10478 :     if (!warning_enabled_at (loc, option))
    1140              :       {
    1141         4103 :         if (get_logger ())
    1142            0 :           get_logger ()->log ("rejecting disabled warning %qs",
    1143            0 :                               d->get_kind ());
    1144         4103 :         m_num_disabled_diagnostics++;
    1145         4103 :         return false;
    1146              :       }
    1147              :   }
    1148              : 
    1149         6375 :   saved_diagnostic *sd
    1150              :     = new saved_diagnostic (sm,
    1151              :                             std::move (ploc),
    1152              :                             var,
    1153              :                             sval,
    1154              :                             state,
    1155              :                             std::move (d),
    1156        11194 :                             m_saved_diagnostics.length ());
    1157         6375 :   m_saved_diagnostics.safe_push (sd);
    1158         6375 :   sd->m_ploc.m_enode->add_diagnostic (sd);
    1159         6375 :   if (get_logger ())
    1160            4 :     log ("adding saved diagnostic %i at SN %i to EN %i: %qs",
    1161              :          sd->get_index (),
    1162            2 :          sd->get_supernode ()->m_id,
    1163            2 :          sd->m_ploc.m_enode->m_index,
    1164            2 :          sd->m_d->get_kind ());
    1165              :   return true;
    1166        10478 : }
    1167              : 
    1168              : /* Queue pending_diagnostic D at ENODE for later emission.
    1169              :    Return true/false signifying if the diagnostic was actually added.
    1170              :    Take ownership of D (or delete it).  */
    1171              : 
    1172              : bool
    1173         3597 : diagnostic_manager::add_diagnostic (pending_location &&ploc,
    1174              :                                     std::unique_ptr<pending_diagnostic> d)
    1175              : {
    1176         3597 :   gcc_assert (ploc.m_enode);
    1177         3597 :   return add_diagnostic (nullptr, std::move (ploc),
    1178         3597 :                          NULL_TREE, nullptr, 0, std::move (d));
    1179              : }
    1180              : 
    1181              : /* Add PN to the most recent saved_diagnostic.  */
    1182              : 
    1183              : void
    1184          194 : diagnostic_manager::add_note (std::unique_ptr<pending_note> pn)
    1185              : {
    1186          194 :   LOG_FUNC (get_logger ());
    1187          194 :   gcc_assert (pn);
    1188              : 
    1189              :   /* Get most recent saved_diagnostic.  */
    1190          194 :   gcc_assert (m_saved_diagnostics.length () > 0);
    1191          194 :   saved_diagnostic *sd = m_saved_diagnostics[m_saved_diagnostics.length () - 1];
    1192          194 :   sd->add_note (std::move (pn));
    1193          194 : }
    1194              : 
    1195              : /* Add EVENT to the most recent saved_diagnostic.  */
    1196              : 
    1197              : void
    1198          159 : diagnostic_manager::add_event (std::unique_ptr<checker_event> event)
    1199              : {
    1200          159 :   LOG_FUNC (get_logger ());
    1201          159 :   gcc_assert (event);
    1202              : 
    1203              :   /* Get most recent saved_diagnostic.  */
    1204          159 :   gcc_assert (m_saved_diagnostics.length () > 0);
    1205          159 :   saved_diagnostic *sd = m_saved_diagnostics[m_saved_diagnostics.length () - 1];
    1206          159 :   sd->add_event (std::move (event));
    1207          159 : }
    1208              : 
    1209              : /* Return a new json::object of the form
    1210              :    {"diagnostics"  : [obj for saved_diagnostic]}.  */
    1211              : 
    1212              : std::unique_ptr<json::object>
    1213            0 : diagnostic_manager::to_json () const
    1214              : {
    1215            0 :   auto dm_obj = std::make_unique<json::object> ();
    1216              : 
    1217            0 :   {
    1218            0 :     auto sd_arr = std::make_unique<json::array> ();
    1219            0 :     int i;
    1220            0 :     saved_diagnostic *sd;
    1221            0 :     FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
    1222            0 :       sd_arr->append (sd->to_json ());
    1223            0 :     dm_obj->set ("diagnostics", std::move (sd_arr));
    1224            0 :   }
    1225              : 
    1226            0 :   return dm_obj;
    1227              : }
    1228              : 
    1229              : /* A class for identifying sets of duplicated pending_diagnostic.
    1230              : 
    1231              :    We want to find the simplest saved_diagnostic amongst those that share a
    1232              :    dedupe_key.  */
    1233              : 
    1234              : class dedupe_key
    1235              : {
    1236              : public:
    1237         6093 :   dedupe_key (const saved_diagnostic &sd)
    1238         6093 :   : m_sd (sd),
    1239         6093 :     m_loc (sd.m_ploc.m_event_loc_info.m_loc)
    1240              :   {
    1241              :   }
    1242              : 
    1243        60221 :   hashval_t hash () const
    1244              :   {
    1245        60221 :     inchash::hash hstate;
    1246              :     // TODO: m_sd
    1247        60221 :     return hstate.end ();
    1248              :   }
    1249        44107 :   bool operator== (const dedupe_key &other) const
    1250              :   {
    1251        44107 :     return (m_sd == other.m_sd
    1252        44107 :             && m_loc == other.m_loc);
    1253              :   }
    1254              : 
    1255        28039 :   location_t get_location () const { return m_loc; }
    1256              : 
    1257              :   /* A qsort comparator for use by dedupe_winners::emit_best
    1258              :      to sort them into location_t order.  */
    1259              : 
    1260              :   static int
    1261        28039 :   comparator (const void *p1, const void *p2)
    1262              :   {
    1263        28039 :     const dedupe_key *pk1 = *(const dedupe_key * const *)p1;
    1264        28039 :     const dedupe_key *pk2 = *(const dedupe_key * const *)p2;
    1265              : 
    1266        28039 :     location_t loc1 = pk1->get_location ();
    1267        28039 :     location_t loc2 = pk2->get_location ();
    1268              : 
    1269        28039 :     if (int cmp = linemap_compare_locations (line_table, loc2, loc1))
    1270              :       return cmp;
    1271         1245 :     if (int cmp = ((int)pk1->m_sd.get_epath_length ()
    1272         1245 :                    - (int)pk2->m_sd.get_epath_length ()))
    1273              :       return cmp;
    1274          602 :     if (int cmp = strcmp (pk1->m_sd.m_d->get_kind (),
    1275          602 :                           pk2->m_sd.m_d->get_kind ()))
    1276              :       return cmp;
    1277              :     return 0;
    1278              :   }
    1279              : 
    1280              :   const saved_diagnostic &m_sd;
    1281              :   location_t m_loc;
    1282              : };
    1283              : 
    1284              : /* Traits for use by dedupe_winners.  */
    1285              : 
    1286              : class dedupe_hash_map_traits
    1287              : {
    1288              : public:
    1289              :   typedef const dedupe_key *key_type;
    1290              :   typedef saved_diagnostic *value_type;
    1291              :   typedef saved_diagnostic *compare_type;
    1292              : 
    1293        60221 :   static inline hashval_t hash (const key_type &v)
    1294              :   {
    1295        60221 :     return v->hash ();
    1296              :   }
    1297        44107 :   static inline bool equal_keys (const key_type &k1, const key_type &k2)
    1298              :   {
    1299        44107 :     return *k1 == *k2;
    1300              :   }
    1301              :   template <typename T>
    1302              :   static inline void remove (T &)
    1303              :   {
    1304              :     // TODO
    1305              :   }
    1306              :   template <typename T>
    1307           38 :   static inline void mark_deleted (T &entry)
    1308              :   {
    1309           38 :     entry.m_key = reinterpret_cast<key_type> (1);
    1310              :   }
    1311              :   template <typename T>
    1312            0 :   static inline void mark_empty (T &entry)
    1313              :   {
    1314            0 :     entry.m_key = nullptr;
    1315              :   }
    1316              :   template <typename T>
    1317       134780 :   static inline bool is_deleted (const T &entry)
    1318              :   {
    1319       134780 :     return entry.m_key == reinterpret_cast<key_type> (1);
    1320              :   }
    1321              :   template <typename T>
    1322       503837 :   static inline bool is_empty (const T &entry)
    1323              :   {
    1324       503837 :     return entry.m_key == nullptr;
    1325              :   }
    1326              :   static const bool empty_zero_p = true;
    1327              : };
    1328              : 
    1329              : /* A class for deduplicating diagnostics and finding (and emitting) the
    1330              :    best saved_diagnostic within each partition.  */
    1331              : 
    1332              : class dedupe_winners
    1333              : {
    1334              : public:
    1335         1556 :   ~dedupe_winners ()
    1336              :   {
    1337              :     /* Delete all keys, but not the saved_diagnostics.  */
    1338         5547 :     for (map_t::iterator iter = m_map.begin ();
    1339         5547 :          iter != m_map.end ();
    1340         3991 :          ++iter)
    1341         3991 :       delete (*iter).first;
    1342         1556 :   }
    1343              : 
    1344              :   /* Determine an exploded_path for SD using PF and, if it's feasible,
    1345              :      determine if SD is the best seen so far for its dedupe_key.
    1346              :      Record the winning SD for each dedupe_key.  */
    1347              : 
    1348         6375 :   void add (logger *logger,
    1349              :             epath_finder *pf,
    1350              :             saved_diagnostic *sd)
    1351              :   {
    1352              :     /* Determine best epath for SD.  */
    1353         6375 :     if (!sd->calc_best_epath (pf))
    1354          282 :       return;
    1355              : 
    1356         6093 :     const exploded_path *epath = sd->get_best_epath ();
    1357         6093 :     gcc_assert (epath);
    1358              : 
    1359              :     /* Now we have an exploded path, use it for pending_locations that are
    1360              :        affected by such things, and for deduplication.  */
    1361         6093 :     if (sd->m_ploc.m_fixer_for_epath)
    1362         1140 :       sd->m_ploc.m_fixer_for_epath->fixup_for_epath (*epath, sd->m_ploc);
    1363              : 
    1364         6093 :     dedupe_key *key = new dedupe_key (*sd);
    1365         6093 :     if (saved_diagnostic **slot = m_map.get (key))
    1366              :       {
    1367         2064 :         if (logger)
    1368            0 :           logger->log ("already have this dedupe_key");
    1369              : 
    1370         2064 :         saved_diagnostic *cur_best_sd = *slot;
    1371              : 
    1372         2064 :         if (sd->get_epath_length () < cur_best_sd->get_epath_length ())
    1373              :           {
    1374              :             /* We've got a shorter path for the key; replace
    1375              :                the current candidate, marking it as a duplicate of SD.  */
    1376          145 :             if (logger)
    1377            0 :               logger->log ("length %i is better than existing length %i;"
    1378              :                            " taking over this dedupe_key",
    1379              :                            sd->get_epath_length (),
    1380              :                            cur_best_sd->get_epath_length ());
    1381          145 :             sd->add_duplicate (cur_best_sd);
    1382          145 :             *slot = sd;
    1383              :           }
    1384              :         else
    1385              :           /* We haven't beaten the current best candidate; add SD
    1386              :              as a duplicate of it.  */
    1387              :           {
    1388         1919 :             if (logger)
    1389            0 :               logger->log ("length %i isn't better than existing length %i;"
    1390              :                            " dropping this candidate",
    1391              :                            sd->get_epath_length (),
    1392              :                            cur_best_sd->get_epath_length ());
    1393         1919 :             cur_best_sd->add_duplicate (sd);
    1394              :           }
    1395         2064 :         delete key;
    1396              :       }
    1397              :     else
    1398              :       {
    1399              :         /* This is the first candidate for this key.  */
    1400         4029 :         m_map.put (key, sd);
    1401         4029 :         if (logger)
    1402            2 :           logger->log ("first candidate for this dedupe_key");
    1403              :       }
    1404              :   }
    1405              : 
    1406              :   /* Handle interactions between the dedupe winners, so that some
    1407              :      diagnostics can supercede others (of different kinds).
    1408              : 
    1409              :      We want use-after-free to supercede use-of-unitialized-value,
    1410              :      so that if we have these at the same stmt, we don't emit
    1411              :      a use-of-uninitialized, just the use-after-free.  */
    1412              : 
    1413         1556 :   void handle_interactions (diagnostic_manager *dm)
    1414              :   {
    1415         1556 :     LOG_SCOPE (dm->get_logger ());
    1416         1556 :     auto_vec<const dedupe_key *> superceded;
    1417         5585 :     for (auto outer : m_map)
    1418              :       {
    1419         4029 :         const saved_diagnostic *outer_sd = outer.second;
    1420        59898 :         for (auto inner : m_map)
    1421              :           {
    1422        25958 :             const saved_diagnostic *inner_sd = inner.second;
    1423        25958 :             if (inner_sd->supercedes_p (*outer_sd))
    1424              :               {
    1425           38 :                 superceded.safe_push (outer.first);
    1426           38 :                 if (dm->get_logger ())
    1427            0 :                   dm->log ("sd[%i] \"%s\" superceded by sd[%i] \"%s\"",
    1428            0 :                            outer_sd->get_index (), outer_sd->m_d->get_kind (),
    1429            0 :                            inner_sd->get_index (), inner_sd->m_d->get_kind ());
    1430              :                 break;
    1431              :               }
    1432              :           }
    1433              :       }
    1434         1670 :     for (auto iter : superceded)
    1435           38 :       m_map.remove (iter);
    1436         1556 :   }
    1437              : 
    1438              :  /* Emit the simplest diagnostic within each set.  */
    1439              : 
    1440         1556 :   void emit_best (diagnostic_manager *dm,
    1441              :                   const exploded_graph &eg)
    1442              :   {
    1443         1556 :     LOG_SCOPE (dm->get_logger ());
    1444              : 
    1445              :     /* Get keys into a vec for sorting.  */
    1446         1556 :     auto_vec<const dedupe_key *> keys (m_map.elements ());
    1447         1556 :     for (map_t::iterator iter = m_map.begin ();
    1448         5547 :          iter != m_map.end ();
    1449         3991 :          ++iter)
    1450         3991 :       keys.quick_push ((*iter).first);
    1451              : 
    1452         3063 :     dm->log ("# keys after de-duplication: %i", keys.length ());
    1453              : 
    1454              :     /* Sort into a good emission order.  */
    1455         1556 :     keys.qsort (dedupe_key::comparator);
    1456              : 
    1457              :     /* Emit the best saved_diagnostics for each key.  */
    1458              :     int i;
    1459              :     const dedupe_key *key;
    1460         7054 :     FOR_EACH_VEC_ELT (keys, i, key)
    1461              :       {
    1462         3991 :         saved_diagnostic **slot = m_map.get (key);
    1463         3991 :         gcc_assert (*slot);
    1464         3991 :         saved_diagnostic *sd = *slot;
    1465         3991 :         dm->emit_saved_diagnostic (eg, *sd);
    1466              :       }
    1467         1556 :   }
    1468              : 
    1469              : private:
    1470              :   /* This maps from each dedupe_key to a current best saved_diagnostic.  */
    1471              : 
    1472              :   typedef hash_map<const dedupe_key *, saved_diagnostic *,
    1473              :                    dedupe_hash_map_traits> map_t;
    1474              :   map_t m_map;
    1475              : };
    1476              : 
    1477              : /* Emit all saved diagnostics.  */
    1478              : 
    1479              : void
    1480         3377 : diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
    1481              : {
    1482         3377 :   LOG_SCOPE (get_logger ());
    1483         3377 :   auto_timevar tv (TV_ANALYZER_DIAGNOSTICS);
    1484         4933 :   log ("# saved diagnostics: %i", m_saved_diagnostics.length ());
    1485         3377 :   log ("# disabled diagnostics: %i", m_num_disabled_diagnostics);
    1486         3377 :   if (get_logger ())
    1487              :     {
    1488              :       unsigned i;
    1489              :       saved_diagnostic *sd;
    1490            7 :       FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
    1491            2 :         log ("[%i] sd: %qs at EN: %i, SN: %i",
    1492            2 :              i, sd->m_d->get_kind (), sd->m_ploc.m_enode->m_index,
    1493            2 :              sd->get_supernode ()->m_id);
    1494              :     }
    1495              : 
    1496         3377 :   if (m_saved_diagnostics.length () == 0)
    1497         1821 :     return;
    1498              : 
    1499              :   /* Compute the shortest_paths once, sharing it between all diagnostics.  */
    1500         1556 :   epath_finder pf (eg);
    1501              : 
    1502              :   /* Iterate through all saved diagnostics, adding them to a dedupe_winners
    1503              :      instance.  This partitions the saved diagnostics by dedupe_key,
    1504              :      generating exploded_paths for them, and retaining the best one in each
    1505              :      partition.  */
    1506         1556 :   dedupe_winners best_candidates;
    1507              : 
    1508         1556 :   int i;
    1509         1556 :   saved_diagnostic *sd;
    1510         9487 :   FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
    1511         6375 :     best_candidates.add (get_logger (), &pf, sd);
    1512              : 
    1513         1556 :   best_candidates.handle_interactions (this);
    1514              : 
    1515              :   /* For each dedupe-key, call emit_saved_diagnostic on the "best"
    1516              :      saved_diagnostic.  */
    1517         1556 :   best_candidates.emit_best (this, eg);
    1518         3377 : }
    1519              : 
    1520              : /* Custom subclass of diagnostics::metadata which, for SARIF output,
    1521              :    populates the property bag of the diagnostic's "result" object
    1522              :    with information from the saved_diagnostic and the
    1523              :    pending_diagnostic.  */
    1524              : 
    1525         3991 : class pending_diagnostic_metadata : public diagnostics::metadata
    1526              : {
    1527              : public:
    1528         3991 :   pending_diagnostic_metadata (const saved_diagnostic &sd)
    1529         3991 :   : m_sd (sd)
    1530              :   {
    1531              :   }
    1532              : 
    1533              :   void
    1534           32 :   maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
    1535              :     const override
    1536              :   {
    1537           32 :     m_sd.maybe_add_sarif_properties (result_obj);
    1538           32 :   }
    1539              : 
    1540              : private:
    1541              :   const saved_diagnostic &m_sd;
    1542              : };
    1543              : 
    1544              : /* Given a saved_diagnostic SD with m_best_epath through EG,
    1545              :    create an checker_path of suitable events and use it to call
    1546              :    SD's underlying pending_diagnostic "emit" vfunc to emit a diagnostic.  */
    1547              : 
    1548              : void
    1549         3991 : diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
    1550              :                                            saved_diagnostic &sd)
    1551              : {
    1552         3991 :   LOG_SCOPE (get_logger ());
    1553         3991 :   log ("sd[%i]: %qs at SN: %i",
    1554         3991 :        sd.get_index (), sd.m_d->get_kind (), sd.get_supernode ()->m_id);
    1555         5371 :   log ("num dupes: %i", sd.get_num_dupes ());
    1556              : 
    1557         3991 :   const exploded_path *epath = sd.get_best_epath ();
    1558         3991 :   gcc_assert (epath);
    1559              : 
    1560              :   /* Precompute all enodes from which the diagnostic is reachable.  */
    1561         3991 :   path_builder pb (eg, *epath, sd.get_feasibility_problem (), sd);
    1562              : 
    1563              :   /* This is the diagnostics::paths::path subclass that will be built for
    1564              :      the diagnostic.  */
    1565         3991 :   checker_path emission_path (get_logical_location_manager (),
    1566              :                               eg.get_ext_state (),
    1567         3991 :                               get_logger ());
    1568              : 
    1569              :   /* Populate emission_path with a full description of EPATH.  */
    1570         3991 :   build_emission_path (pb, *epath, &emission_path);
    1571              : 
    1572              :   /* Now prune it to just cover the most pertinent events.  */
    1573         3991 :   prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state);
    1574              : 
    1575              :   /* Add any saved events to the path, giving contextual information
    1576              :      about what the analyzer was simulating as the diagnostic was
    1577              :      generated.  These don't get pruned, as they are probably pertinent.  */
    1578         3991 :   sd.add_any_saved_events (emission_path);
    1579              : 
    1580              :   /* Add a final event to the path, covering the diagnostic itself.  */
    1581         3991 :   {
    1582         3991 :     const exploded_node *const enode = epath->get_final_enode ();
    1583         3991 :     sd.m_d->add_final_event (sd.m_sm, enode, sd.m_ploc.m_event_loc_info,
    1584              :                              sd.m_var, sd.m_state, &emission_path);
    1585              :   }
    1586              : 
    1587              :   /* The "final" event might not be final; if the saved_diagnostic has a
    1588              :      trailing eedge stashed, add any events for it.  This is for use
    1589              :      in handling longjmp, to show where a longjmp is rewinding to.  */
    1590         3991 :   if (sd.m_trailing_eedge)
    1591            4 :     add_events_for_eedge (pb, *sd.m_trailing_eedge, &emission_path, nullptr);
    1592              : 
    1593         3991 :   emission_path.inject_any_inlined_call_events (get_logger ());
    1594              : 
    1595         3991 :   emission_path.prepare_for_emission (sd.m_d.get ());
    1596              : 
    1597         3991 :   location_t loc = sd.m_ploc.m_event_loc_info.m_loc;
    1598         3991 :   loc = sd.m_d->fixup_location (loc, true);
    1599              : 
    1600              :   /* Allow the pending_diagnostic to fix up the locations of events.  */
    1601         3991 :   emission_path.fixup_locations (sd.m_d.get ());
    1602              : 
    1603         3991 :   gcc_rich_location rich_loc (loc);
    1604         3991 :   rich_loc.set_path (&emission_path);
    1605              : 
    1606         3991 :   auto_diagnostic_group d;
    1607         3991 :   auto_cfun sentinel (sd.get_supernode ()->m_fun);
    1608         3991 :   pending_diagnostic_metadata m (sd);
    1609         3991 :   diagnostic_emission_context diag_ctxt (sd, rich_loc, m, get_logger ());
    1610         3991 :   if (sd.m_d->emit (diag_ctxt))
    1611              :     {
    1612         3923 :       sd.emit_any_notes ();
    1613              : 
    1614         3923 :       unsigned num_dupes = sd.get_num_dupes ();
    1615         3923 :       if (flag_analyzer_show_duplicate_count && num_dupes > 0)
    1616            4 :         inform_n (loc, num_dupes,
    1617              :                   "%i duplicate", "%i duplicates",
    1618              :                   num_dupes);
    1619         3923 :       if (flag_dump_analyzer_exploded_paths)
    1620              :         {
    1621            0 :           auto_timevar tv (TV_ANALYZER_DUMP);
    1622            0 :           pretty_printer pp;
    1623            0 :           pp_printf (&pp, "%s.%i.%s.epath.txt",
    1624            0 :                      dump_base_name, sd.get_index (), sd.m_d->get_kind ());
    1625            0 :           char *filename = xstrdup (pp_formatted_text (&pp));
    1626            0 :           epath->dump_to_file (filename, eg.get_ext_state ());
    1627            0 :           inform (loc, "exploded path written to %qs", filename);
    1628            0 :           free (filename);
    1629            0 :         }
    1630              :     }
    1631         3991 : }
    1632              : 
    1633              : const diagnostics::logical_locations::manager &
    1634         3991 : diagnostic_manager::get_logical_location_manager () const
    1635              : {
    1636         3991 :   gcc_assert (global_dc);
    1637         3991 :   auto mgr = global_dc->get_logical_location_manager ();
    1638         3991 :   gcc_assert (mgr);
    1639         3991 :   return *mgr;
    1640              : }
    1641              : 
    1642              : /* Emit a "path" of events to EMISSION_PATH describing the exploded path
    1643              :    EPATH within EG.  */
    1644              : 
    1645              : void
    1646         3991 : diagnostic_manager::build_emission_path (const path_builder &pb,
    1647              :                                          const exploded_path &epath,
    1648              :                                          checker_path *emission_path) const
    1649              : {
    1650         3991 :   LOG_SCOPE (get_logger ());
    1651              : 
    1652         3991 :   interesting_t interest;
    1653         3991 :   pb.get_pending_diagnostic ()->mark_interesting_stuff (&interest);
    1654              : 
    1655              :   /* Add region creation events for any globals of interest, at the
    1656              :      beginning of the path.  */
    1657         3991 :   {
    1658         7919 :     for (auto reg : interest.m_region_creation)
    1659         1328 :       switch (reg->get_memory_space ())
    1660              :         {
    1661         1112 :         default:
    1662         1112 :           continue;
    1663          216 :         case MEMSPACE_CODE:
    1664          216 :         case MEMSPACE_GLOBALS:
    1665          216 :         case MEMSPACE_READONLY_DATA:
    1666          216 :           {
    1667          216 :             const region *base_reg = reg->get_base_region ();
    1668          216 :             if (tree decl = base_reg->maybe_get_decl ())
    1669          207 :               if (DECL_P (decl)
    1670          207 :                   && useful_location_p (DECL_SOURCE_LOCATION (decl)))
    1671              :                 {
    1672          207 :                   emission_path->add_region_creation_events
    1673          207 :                     (pb.get_pending_diagnostic (),
    1674              :                      reg, nullptr,
    1675          207 :                      event_loc_info (DECL_SOURCE_LOCATION (decl),
    1676              :                                      NULL_TREE,
    1677          207 :                                      0),
    1678          207 :                      m_verbosity > 3);
    1679              :                 }
    1680              :           }
    1681         1112 :         }
    1682              :   }
    1683              : 
    1684              :   /* Walk EPATH, adding events as appropriate.  */
    1685        46071 :   for (unsigned i = 0; i < epath.m_edges.length (); i++)
    1686              :     {
    1687        42080 :       const exploded_edge *eedge = epath.m_edges[i];
    1688        42080 :       add_events_for_eedge (pb, *eedge, emission_path, &interest);
    1689              :     }
    1690         3991 :   add_event_on_final_node (pb, epath.get_final_enode (),
    1691              :                            emission_path, &interest);
    1692         3991 : }
    1693              : 
    1694              : /* Emit a region_creation_event when requested on the last statement in
    1695              :    the path.
    1696              : 
    1697              :    If a region_creation_event should be emitted on the last statement of the
    1698              :    path, we need to peek to the successors to get whether the final enode
    1699              :    created a region.
    1700              : */
    1701              : 
    1702              : void
    1703         3991 : diagnostic_manager::add_event_on_final_node (const path_builder &pb,
    1704              :                                              const exploded_node *final_enode,
    1705              :                                              checker_path *emission_path,
    1706              :                                              interesting_t *interest) const
    1707              : {
    1708         3991 :   const program_point &src_point = final_enode->get_point ();
    1709         3991 :   const int src_stack_depth = src_point.get_stack_depth ();
    1710         3991 :   const program_state &src_state = final_enode->get_state ();
    1711         3991 :   const region_model *src_model = src_state.m_region_model;
    1712              : 
    1713         3991 :   unsigned j;
    1714         3991 :   exploded_edge *e;
    1715         7189 :   FOR_EACH_VEC_ELT (final_enode->m_succs, j, e)
    1716              :   {
    1717         3275 :     exploded_node *dst = e->m_dest;
    1718         3275 :     const program_state &dst_state = dst->get_state ();
    1719         3275 :     const region_model *dst_model = dst_state.m_region_model;
    1720         3275 :     if (src_model->get_dynamic_extents ()
    1721         3275 :         != dst_model->get_dynamic_extents ())
    1722              :       {
    1723              :         unsigned i;
    1724              :         const region *reg;
    1725              :         bool emitted = false;
    1726          456 :         FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
    1727              :           {
    1728           92 :             const region *base_reg = reg->get_base_region ();
    1729           92 :             const svalue *old_extents
    1730           92 :         = src_model->get_dynamic_extents (base_reg);
    1731           92 :             const svalue *new_extents
    1732           92 :         = dst_model->get_dynamic_extents (base_reg);
    1733           92 :             if (old_extents == nullptr && new_extents != nullptr)
    1734           81 :               switch (base_reg->get_kind ())
    1735              :                 {
    1736              :                 default:
    1737              :                   break;
    1738           77 :                 case RK_HEAP_ALLOCATED:
    1739           77 :                 case RK_ALLOCA:
    1740           77 :                   emission_path->add_region_creation_events
    1741          154 :                     (pb.get_pending_diagnostic (),
    1742              :                      reg,
    1743              :                      dst_model,
    1744           77 :                      event_loc_info (src_point.get_location (),
    1745              :                                      src_point.get_fndecl (),
    1746          154 :                                      src_stack_depth),
    1747              :                      false);
    1748           77 :                   emitted = true;
    1749           77 :                   break;
    1750              :                 }
    1751              :           }
    1752          364 :         if (emitted)
    1753              :           break;
    1754              :       }
    1755              :   }
    1756         3991 : }
    1757              : 
    1758              : /* Subclass of state_change_visitor that creates state_change_event
    1759              :    instances.  */
    1760              : 
    1761        42084 : class state_change_event_creator : public state_change_visitor
    1762              : {
    1763              : public:
    1764        42084 :   state_change_event_creator (const path_builder &pb,
    1765              :                               const exploded_edge &eedge,
    1766              :                               checker_path *emission_path)
    1767        42084 :     : m_pb (pb),
    1768        42084 :       m_eedge (eedge),
    1769        42084 :       m_emission_path (emission_path)
    1770              :   {}
    1771              : 
    1772           98 :   bool on_global_state_change (const state_machine &sm,
    1773              :                                state_machine::state_t src_sm_val,
    1774              :                                state_machine::state_t dst_sm_val)
    1775              :     final override
    1776              :   {
    1777           98 :     if (&sm != m_pb.get_sm ())
    1778              :       return false;
    1779           55 :     const exploded_node *src_node = m_eedge.m_src;
    1780           55 :     const exploded_node *dst_node = m_eedge.m_dest;
    1781           55 :     const gimple *stmt = m_eedge.maybe_get_stmt ();
    1782           55 :     const program_state &dst_state = dst_node->get_state ();
    1783              : 
    1784           55 :     m_emission_path->add_event
    1785           55 :       (std::make_unique<state_change_event> (m_eedge.m_src,
    1786              :                                              stmt,
    1787              :                                              sm,
    1788          110 :                                              nullptr,
    1789              :                                              src_sm_val,
    1790              :                                              dst_sm_val,
    1791           55 :                                              nullptr,
    1792              :                                              dst_state,
    1793              :                                              src_node));
    1794           55 :     return false;
    1795              :   }
    1796              : 
    1797         4346 :   bool on_state_change (const state_machine &sm,
    1798              :                         state_machine::state_t src_sm_val,
    1799              :                         state_machine::state_t dst_sm_val,
    1800              :                         const svalue *sval,
    1801              :                         const svalue *dst_origin_sval) final override
    1802              :   {
    1803         4346 :     if (&sm != m_pb.get_sm ())
    1804              :       return false;
    1805              : 
    1806         3298 :     const exploded_node *src_node = m_eedge.m_src;
    1807         3298 :     const exploded_node *dst_node = m_eedge.m_dest;
    1808         3298 :     const gimple *stmt = m_eedge.maybe_get_stmt ();
    1809         3298 :     const program_state &dst_state = dst_node->get_state ();
    1810              : 
    1811         3298 :     m_emission_path->add_event
    1812         3298 :       (std::make_unique<state_change_event> (m_eedge.m_src,
    1813              :                                              stmt,
    1814              :                                              sm,
    1815              :                                              sval,
    1816              :                                              src_sm_val,
    1817              :                                              dst_sm_val,
    1818              :                                              dst_origin_sval,
    1819              :                                              dst_state,
    1820              :                                              src_node));
    1821         3298 :     return false;
    1822              :   }
    1823              : 
    1824              :   const path_builder &m_pb;
    1825              :   const exploded_edge &m_eedge;
    1826              :   checker_path *m_emission_path;
    1827              : };
    1828              : 
    1829              : /* Compare SRC_STATE and DST_STATE (which use EXT_STATE), and call
    1830              :    VISITOR's on_state_change for every sm-state change that occurs
    1831              :    to a tree, and on_global_state_change for every global state change
    1832              :    that occurs.
    1833              : 
    1834              :    This determines the state changes that ought to be reported to
    1835              :    the user: a combination of the effects of changes to sm_state_map
    1836              :    (which maps svalues to sm-states), and of region_model changes
    1837              :    (which map trees to svalues).
    1838              : 
    1839              :    Bail out early and return true if any call to on_global_state_change
    1840              :    or on_state_change returns true, otherwise return false.
    1841              : 
    1842              :    This is split out to make it easier to experiment with changes to
    1843              :    exploded_node granularity (so that we can observe what state changes
    1844              :    lead to state_change_events being emitted).  */
    1845              : 
    1846              : bool
    1847        42084 : for_each_state_change (const program_state &src_state,
    1848              :                        const program_state &dst_state,
    1849              :                        const extrinsic_state &ext_state,
    1850              :                        state_change_visitor *visitor)
    1851              : {
    1852        84168 :   gcc_assert (src_state.m_checker_states.length ()
    1853              :               == ext_state.get_num_checkers ());
    1854        84168 :   gcc_assert (dst_state.m_checker_states.length ()
    1855              :               == ext_state.get_num_checkers ());
    1856       335140 :   for (unsigned i = 0; i < ext_state.get_num_checkers (); i++)
    1857              :     {
    1858       293056 :       const state_machine &sm = ext_state.get_sm (i);
    1859       293056 :       const sm_state_map &src_smap = *src_state.m_checker_states[i];
    1860       293056 :       const sm_state_map &dst_smap = *dst_state.m_checker_states[i];
    1861              : 
    1862              :       /* Add events for any global state changes.  */
    1863       293056 :       if (src_smap.get_global_state () != dst_smap.get_global_state ())
    1864           98 :         if (visitor->on_global_state_change (sm,
    1865              :                                              src_smap.get_global_state (),
    1866              :                                              dst_smap.get_global_state ()))
    1867              :           return true;
    1868              : 
    1869              :       /* Add events for per-svalue state changes.  */
    1870       328067 :       for (sm_state_map::iterator_t iter = dst_smap.begin ();
    1871       621123 :            iter != dst_smap.end ();
    1872        35011 :            ++iter)
    1873              :         {
    1874        35011 :           const svalue *sval = (*iter).first;
    1875        35011 :           state_machine::state_t dst_sm_val = (*iter).second.m_state;
    1876        35011 :           state_machine::state_t src_sm_val
    1877        35011 :             = src_smap.get_state (sval, ext_state);
    1878        35011 :           if (dst_sm_val != src_sm_val)
    1879              :             {
    1880         4346 :               const svalue *origin_sval = (*iter).second.m_origin;
    1881         4346 :               if (visitor->on_state_change (sm, src_sm_val, dst_sm_val,
    1882              :                                             sval, origin_sval))
    1883            0 :                 return true;
    1884              :             }
    1885              :         }
    1886              :     }
    1887              :   return false;
    1888              : }
    1889              : 
    1890              : /* Subroutine of diagnostic_manager::build_emission_path.
    1891              :    Add any events for EEDGE to EMISSION_PATH.  */
    1892              : 
    1893              : void
    1894        42084 : diagnostic_manager::add_events_for_eedge (const path_builder &pb,
    1895              :                                           const exploded_edge &eedge,
    1896              :                                           checker_path *emission_path,
    1897              :                                           interesting_t *interest) const
    1898              : {
    1899        42084 :   const exploded_node *src_node = eedge.m_src;
    1900        42084 :   const program_point &src_point = src_node->get_point ();
    1901        42084 :   const int src_stack_depth = src_point.get_stack_depth ();
    1902        42084 :   const exploded_node *dst_node = eedge.m_dest;
    1903        42084 :   const program_point &dst_point = dst_node->get_point ();
    1904        42084 :   const int dst_stack_depth = dst_point.get_stack_depth ();
    1905        42084 :   if (get_logger ())
    1906              :     {
    1907           13 :       get_logger ()->start_log_line ();
    1908           13 :       pretty_printer *pp = get_logger ()->get_printer ();
    1909           13 :       pp_printf (pp, "EN %i -> EN %i: ",
    1910           13 :                  eedge.m_src->m_index,
    1911           13 :                  eedge.m_dest->m_index);
    1912           13 :       src_point.print (pp, format (false));
    1913           13 :       pp_string (pp, "-> ");
    1914           13 :       dst_point.print (pp, format (false));
    1915           13 :       get_logger ()->end_log_line ();
    1916              :     }
    1917        42084 :   const program_state &src_state = src_node->get_state ();
    1918        42084 :   const program_state &dst_state = dst_node->get_state ();
    1919              : 
    1920              :   /* Add state change events for the states that have changed.
    1921              :      We add these before events for superedges, so that if we have a
    1922              :      state_change_event due to following an edge, we'll get this sequence
    1923              :      of events:
    1924              : 
    1925              :       | if (!ptr)
    1926              :       |    ~
    1927              :       |    |
    1928              :       |    (1) assuming 'ptr' is non-NULL  (state_change_event)
    1929              :       |    (2) following 'false' branch... (start_cfg_edge_event)
    1930              :      ...
    1931              :       | do_something (ptr);
    1932              :       | ~~~~~~~~~~~~~^~~~~
    1933              :       |              |
    1934              :       |              (3) ...to here        (end_cfg_edge_event).  */
    1935        42084 :   state_change_event_creator visitor (pb, eedge, emission_path);
    1936        42084 :   for_each_state_change (src_state, dst_state, pb.get_ext_state (),
    1937              :                          &visitor);
    1938              : 
    1939              :   /* Give diagnostics an opportunity to inject extra events, or
    1940              :      to override the rest of this function.  */
    1941        42084 :   pending_diagnostic *pd = pb.get_pending_diagnostic ();
    1942        42084 :   if (pd->maybe_add_custom_events_for_eedge (eedge, emission_path))
    1943              :     return;
    1944              : 
    1945              :   /* Allow non-standard edges to add events, e.g. when rewinding from
    1946              :      longjmp to a setjmp.  */
    1947        41107 :   if (eedge.m_custom_info)
    1948         2320 :     eedge.m_custom_info->add_events_to_path (emission_path, eedge, *pd);
    1949              : 
    1950              :   /* Don't add events for insignificant edges at verbosity levels below 3.  */
    1951        41107 :   if (m_verbosity < 3)
    1952        40882 :     if (!significant_edge_p (pb, eedge))
    1953              :       return;
    1954              : 
    1955              :   /* Add events for operations.  */
    1956        40310 :   if (eedge.m_sedge)
    1957        35177 :     if (auto op = eedge.m_sedge->get_op ())
    1958        25872 :       op->add_any_events_for_eedge (eedge, *emission_path);
    1959              : 
    1960              :   /* Add events for function entry.  */
    1961        40310 :   if (dst_point.get_supernode ()->entry_p ())
    1962              :     {
    1963         5104 :       pb.get_pending_diagnostic ()->add_function_entry_event
    1964         5104 :         (eedge, emission_path);
    1965              :       /* Create region_creation_events for on-stack regions within
    1966              :          this frame.  */
    1967         5104 :       if (interest)
    1968              :         {
    1969              :           unsigned i;
    1970              :           const region *reg;
    1971         6572 :           FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
    1972         1468 :             if (const frame_region *frame = reg->maybe_get_frame_region ())
    1973         1948 :               if (frame->get_fndecl () == dst_point.get_fndecl ())
    1974              :                 {
    1975          855 :                   const region *base_reg = reg->get_base_region ();
    1976          855 :                   if (tree decl = base_reg->maybe_get_decl ())
    1977          754 :                     if (DECL_P (decl)
    1978          754 :                         && useful_location_p (DECL_SOURCE_LOCATION (decl)))
    1979              :                       {
    1980          750 :                         emission_path->add_region_creation_events
    1981         1500 :                           (pb.get_pending_diagnostic (),
    1982          750 :                            reg, dst_state.m_region_model,
    1983          750 :                            event_loc_info (DECL_SOURCE_LOCATION (decl),
    1984              :                                            dst_point.get_fndecl (),
    1985          750 :                                            dst_stack_depth),
    1986          750 :                            m_verbosity > 3);
    1987              :                       }
    1988              :                 }
    1989              :         }
    1990              :     }
    1991              : 
    1992              :   /* Look for changes in dynamic extents, which will identify
    1993              :      the creation of heap-based regions and alloca regions.  */
    1994        40310 :   if (interest)
    1995              :     {
    1996        40306 :       const region_model *src_model = src_state.m_region_model;
    1997        40306 :       const region_model *dst_model = dst_state.m_region_model;
    1998        40306 :       if (src_model->get_dynamic_extents ()
    1999        40306 :           != dst_model->get_dynamic_extents ())
    2000              :         {
    2001              :           unsigned i;
    2002              :           const region *reg;
    2003         2389 :           FOR_EACH_VEC_ELT (interest->m_region_creation, i, reg)
    2004              :             {
    2005          421 :               const region *base_reg = reg->get_base_region ();
    2006          421 :               const svalue *old_extents
    2007          421 :                 = src_model->get_dynamic_extents (base_reg);
    2008          421 :               const svalue *new_extents
    2009          421 :                 = dst_model->get_dynamic_extents (base_reg);
    2010          421 :               if (old_extents == nullptr && new_extents != nullptr)
    2011          277 :                 switch (base_reg->get_kind ())
    2012              :                   {
    2013              :                   default:
    2014              :                     break;
    2015          241 :                   case RK_HEAP_ALLOCATED:
    2016          241 :                   case RK_ALLOCA:
    2017          241 :                     emission_path->add_region_creation_events
    2018          241 :                       (pb.get_pending_diagnostic (),
    2019              :                        reg, dst_model,
    2020          241 :                        event_loc_info (src_point.get_location (),
    2021              :                                        src_point.get_fndecl (),
    2022          482 :                                        src_stack_depth),
    2023          241 :                        m_verbosity > 3);
    2024          241 :                     break;
    2025              :                   }
    2026              :             }
    2027              :         }
    2028              :     }
    2029              : 
    2030        40310 :   if (pb.get_feasibility_problem ()
    2031        40310 :       && &pb.get_feasibility_problem ()->m_eedge == &eedge)
    2032              :     {
    2033            4 :       pretty_printer pp;
    2034            4 :       pp_format_decoder (&pp) = default_tree_printer;
    2035            4 :       pp_string (&pp,
    2036              :                  "this path would have been rejected as infeasible"
    2037              :                  " at this edge: ");
    2038            4 :       pb.get_feasibility_problem ()->dump_to_pp (&pp);
    2039            4 :       emission_path->add_event
    2040            4 :         (std::make_unique<precanned_custom_event>
    2041            4 :            (event_loc_info (dst_point.get_location (),
    2042              :                             dst_point.get_fndecl (),
    2043            8 :                             dst_stack_depth),
    2044            4 :             pp_formatted_text (&pp)));
    2045            4 :     }
    2046        42084 : }
    2047              : 
    2048              : /* Return true if EEDGE is a significant edge in the path to the diagnostic
    2049              :    for PB.
    2050              : 
    2051              :    Consider all of the sibling out-eedges from the same source enode
    2052              :    as EEDGE.
    2053              :    If there's no path from the destinations of those eedges to the
    2054              :    diagnostic enode, then we have to take this eedge and thus it's
    2055              :    significant.
    2056              : 
    2057              :    Conversely if there is a path from the destination of any other sibling
    2058              :    eedge to the diagnostic enode, then this edge is insignificant.
    2059              : 
    2060              :    Example 1: redundant if-else:
    2061              : 
    2062              :      (A) if (...)            A
    2063              :      (B)   ...              / \
    2064              :          else              B   C
    2065              :      (C)   ...              \ /
    2066              :      (D) [DIAGNOSTIC]        D
    2067              : 
    2068              :      D is reachable by either B or C, so neither of these edges
    2069              :      are significant.
    2070              : 
    2071              :    Example 2: pertinent if-else:
    2072              : 
    2073              :      (A) if (...)                         A
    2074              :      (B)   ...                           / \
    2075              :          else                           B   C
    2076              :      (C)   [NECESSARY CONDITION]        |   |
    2077              :      (D) [POSSIBLE DIAGNOSTIC]          D1  D2
    2078              : 
    2079              :      D becomes D1 and D2 in the exploded graph, where the diagnostic occurs
    2080              :      at D2.  D2 is only reachable via C, so the A -> C edge is significant.
    2081              : 
    2082              :    Example 3: redundant loop:
    2083              : 
    2084              :      (A) while (...)          +-->A
    2085              :      (B)   ...                |  / \
    2086              :      (C) ...                  +-B  C
    2087              :      (D) [DIAGNOSTIC]              |
    2088              :                                    D
    2089              : 
    2090              :      D is reachable from both B and C, so the A->C edge is not significant.  */
    2091              : 
    2092              : bool
    2093        40882 : diagnostic_manager::significant_edge_p (const path_builder &pb,
    2094              :                                         const exploded_edge &eedge) const
    2095              : {
    2096        40882 :   int i;
    2097        40882 :   exploded_edge *sibling;
    2098       111147 :   FOR_EACH_VEC_ELT (eedge.m_src->m_succs, i, sibling)
    2099              :     {
    2100        71062 :       if (sibling == &eedge)
    2101        40505 :         continue;
    2102        30557 :       if (pb.reachable_from_p (sibling->m_dest))
    2103              :         {
    2104          797 :           if (get_logger ())
    2105            0 :             get_logger ()->log ("  edge EN: %i -> EN: %i is insignificant as"
    2106              :                                 " EN: %i is also reachable via"
    2107              :                                 " EN: %i -> EN: %i",
    2108            0 :                                 eedge.m_src->m_index, eedge.m_dest->m_index,
    2109            0 :                                 pb.get_diag_node ()->m_index,
    2110            0 :                                 sibling->m_src->m_index,
    2111              :                                 sibling->m_dest->m_index);
    2112          797 :           return false;
    2113              :         }
    2114              :     }
    2115              : 
    2116              :   return true;
    2117              : }
    2118              : 
    2119              : /* Prune PATH, based on the verbosity level, to the most pertinent
    2120              :    events for a diagnostic that involves VAR ending in state STATE
    2121              :    (for state machine SM).
    2122              : 
    2123              :    PATH is updated in place, and the redundant checker_events are deleted.
    2124              : 
    2125              :    As well as deleting events, call record_critical_state on events in
    2126              :    which state critical to the pending_diagnostic is being handled; see
    2127              :    the comment for diagnostic_manager::prune_for_sm_diagnostic.  */
    2128              : 
    2129              : void
    2130         3991 : diagnostic_manager::prune_path (checker_path *path,
    2131              :                                 const state_machine *sm,
    2132              :                                 const svalue *sval,
    2133              :                                 state_machine::state_t state) const
    2134              : {
    2135         3991 :   LOG_FUNC (get_logger ());
    2136         3991 :   path->maybe_log (get_logger (), "path");
    2137         3991 :   prune_for_sm_diagnostic (path, sm, sval, state);
    2138         3991 :   prune_interproc_events (path);
    2139         3991 :   if (! flag_analyzer_show_events_in_system_headers)
    2140         3989 :     prune_system_headers (path);
    2141         3991 :   consolidate_conditions (path);
    2142         3991 :   consolidate_unwind_events (path);
    2143         3991 :   finish_pruning (path);
    2144         3991 :   path->maybe_log (get_logger (), "pruned");
    2145         3991 : }
    2146              : 
    2147              : /* A cheap test to determine if EXPR can be the expression of interest in
    2148              :    an sm-diagnostic, so that we can reject cases where we have a non-lvalue.
    2149              :    We don't have always have a model when calling this, so we can't use
    2150              :    tentative_region_model_context, so there can be false positives.  */
    2151              : 
    2152              : static bool
    2153            0 : can_be_expr_of_interest_p (tree expr)
    2154              : {
    2155            0 :   if (!expr)
    2156              :     return false;
    2157              : 
    2158              :   /* Reject constants.  */
    2159            0 :   if (CONSTANT_CLASS_P (expr))
    2160            0 :     return false;
    2161              : 
    2162              :   /* Otherwise assume that it can be an lvalue.  */
    2163              :   return true;
    2164              : }
    2165              : 
    2166              : /* First pass of diagnostic_manager::prune_path: apply verbosity level,
    2167              :    pruning unrelated state change events.
    2168              : 
    2169              :    Iterate backwards through PATH, skipping state change events that aren't
    2170              :    VAR but update the pertinent VAR when state-copying occurs.
    2171              : 
    2172              :    As well as deleting events, call record_critical_state on events in
    2173              :    which state critical to the pending_diagnostic is being handled, so
    2174              :    that the event's get_desc vfunc can potentially supply a more precise
    2175              :    description of the event to the user.
    2176              :    e.g. improving
    2177              :      "calling 'foo' from 'bar'"
    2178              :    to
    2179              :      "passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1"
    2180              :    when the diagnostic relates to later dereferencing 'ptr'.  */
    2181              : 
    2182              : void
    2183         3991 : diagnostic_manager::prune_for_sm_diagnostic (checker_path *path,
    2184              :                                              const state_machine *sm,
    2185              :                                              const svalue *sval,
    2186              :                                              state_machine::state_t state) const
    2187              : {
    2188         3991 :   int idx = path->num_events () - 1;
    2189        67385 :   while (idx >= 0 && idx < (signed)path->num_events ())
    2190              :     {
    2191        31697 :       checker_event *base_event = path->get_checker_event (idx);
    2192        31697 :       if (get_logger ())
    2193              :         {
    2194            6 :           if (sm)
    2195              :             {
    2196            0 :               if (sval)
    2197              :                 {
    2198            0 :                   label_text sval_desc = sval->get_desc ();
    2199            0 :                   log ("considering event %i (%s), with sval: %qs, state: %qs",
    2200              :                        idx, event_kind_to_string (base_event->get_kind ()),
    2201              :                        sval_desc.get (), state->get_name ());
    2202            0 :                 }
    2203              :               else
    2204            0 :                 log ("considering event %i (%s), with global state: %qs",
    2205              :                      idx, event_kind_to_string (base_event->get_kind ()),
    2206              :                      state->get_name ());
    2207              :             }
    2208              :           else
    2209            6 :             log ("considering event %i", idx);
    2210              :         }
    2211              : 
    2212        31697 :       switch (base_event->get_kind ())
    2213              :         {
    2214            0 :         default:
    2215            0 :           gcc_unreachable ();
    2216              : 
    2217            0 :         case event_kind::debug:
    2218            0 :           if (m_verbosity < 4)
    2219              :             {
    2220            0 :               log ("filtering event %i: debug event", idx);
    2221            0 :               path->delete_event (idx);
    2222              :             }
    2223              :           break;
    2224              : 
    2225              :         case event_kind::custom:
    2226              :           /* Don't filter custom events.  */
    2227              :           break;
    2228              : 
    2229        13613 :         case event_kind::stmt:
    2230        13613 :           {
    2231        13613 :             if (m_verbosity < 4)
    2232              :               {
    2233        13613 :                 log ("filtering event %i: statement event", idx);
    2234        13613 :                 path->delete_event (idx);
    2235              :               }
    2236              :           }
    2237              :           break;
    2238              : 
    2239              :         case event_kind::region_creation:
    2240              :           /* Don't filter these.  */
    2241              :           break;
    2242              : 
    2243         5104 :         case event_kind::function_entry:
    2244         5104 :           if (m_verbosity < 1)
    2245              :             {
    2246           33 :               log ("filtering event %i: function entry", idx);
    2247           33 :               path->delete_event (idx);
    2248              :             }
    2249              :           break;
    2250              : 
    2251         3772 :         case event_kind::state_change:
    2252         3772 :           {
    2253         3772 :             state_change_event *state_change = (state_change_event *)base_event;
    2254         3772 :             gcc_assert (state_change->m_dst_state.m_region_model);
    2255              : 
    2256         3772 :             if (state_change->m_sval == sval)
    2257              :               {
    2258         2135 :                 if (state_change->m_origin)
    2259              :                   {
    2260            0 :                     if (get_logger ())
    2261              :                       {
    2262            0 :                         label_text sval_desc = sval->get_desc ();
    2263            0 :                         label_text origin_sval_desc
    2264            0 :                           = state_change->m_origin->get_desc ();
    2265            0 :                         log ("event %i:"
    2266              :                              " switching var of interest from %qs to %qs",
    2267              :                              idx, sval_desc.get (),
    2268              :                              origin_sval_desc.get ());
    2269            0 :                       }
    2270            0 :                     sval = state_change->m_origin;
    2271              :                   }
    2272         2135 :                 log ("event %i: switching state of interest from %qs to %qs",
    2273         2135 :                      idx, state_change->m_to->get_name (),
    2274         2135 :                      state_change->m_from->get_name ());
    2275         2135 :                 state = state_change->m_from;
    2276              :               }
    2277         1637 :             else if (m_verbosity < 4)
    2278              :               {
    2279         1637 :                 if (get_logger ())
    2280              :                   {
    2281            0 :                     if (state_change->m_sval)
    2282              :                       {
    2283            0 :                         label_text change_sval_desc
    2284            0 :                           = state_change->m_sval->get_desc ();
    2285            0 :                         if (sval)
    2286              :                           {
    2287            0 :                             label_text sval_desc = sval->get_desc ();
    2288            0 :                             log ("filtering event %i:"
    2289              :                                  " state change to %qs unrelated to %qs",
    2290              :                                  idx, change_sval_desc.get (),
    2291              :                                  sval_desc.get ());
    2292            0 :                           }
    2293              :                         else
    2294            0 :                           log ("filtering event %i: state change to %qs",
    2295              :                                idx, change_sval_desc.get ());
    2296            0 :                       }
    2297              :                     else
    2298            0 :                       log ("filtering event %i: global state change", idx);
    2299              :                   }
    2300         1637 :                 path->delete_event (idx);
    2301              :               }
    2302              :           }
    2303              :           break;
    2304              : 
    2305         2607 :         case event_kind::start_cfg_edge:
    2306         2607 :           {
    2307         2607 :             cfg_edge_event *event = (cfg_edge_event *)base_event;
    2308              : 
    2309              :             /* TODO: is this edge significant to var?
    2310              :                See if var can be in other states in the dest, but not
    2311              :                in other states in the src?
    2312              :                Must have multiple sibling edges.  */
    2313              : 
    2314         2607 :             if (event->should_filter_p (m_verbosity))
    2315              :               {
    2316           36 :                 log ("filtering events %i and %i: CFG edge", idx, idx + 1);
    2317           36 :                 path->delete_event (idx);
    2318              :                 /* Also delete the corresponding event_kind::end_cfg_edge.  */
    2319           36 :                 gcc_assert (path->get_checker_event (idx)->get_kind ()
    2320              :                             == event_kind::end_cfg_edge);
    2321           36 :                 path->delete_event (idx);
    2322              :               }
    2323              :           }
    2324              :           break;
    2325              : 
    2326              :         case event_kind::end_cfg_edge:
    2327              :           /* These come in pairs with event_kind::start_cfg_edge events and are
    2328              :              filtered when their start event is filtered.  */
    2329              :           break;
    2330              : 
    2331              :         case event_kind::catch_:
    2332              :         case event_kind::throw_:
    2333              :         case event_kind::unwind:
    2334              :           /* Don't filter these.  */
    2335              :           break;
    2336              : 
    2337         1193 :         case event_kind::call_:
    2338         1193 :           {
    2339         1193 :             call_event *event = (call_event *)base_event;
    2340         1193 :             const region_model *callee_model
    2341         1193 :               = event->m_eedge.m_dest->get_state ().m_region_model;
    2342         1193 :             const region_model *caller_model
    2343         1193 :               = event->m_eedge.m_src->get_state ().m_region_model;
    2344         1193 :             tree callee_var = callee_model->get_representative_tree (sval);
    2345         1193 :             callsite_expr expr;
    2346              : 
    2347         1193 :             tree caller_var;
    2348         1193 :             if (auto op = event->get_call_and_return_op ())
    2349              :               {
    2350         1193 :                 tree callee_fndecl
    2351         1193 :                   = event->m_eedge.m_dest->get_point ().get_fndecl ();
    2352         1193 :                 caller_var
    2353         1193 :                   = op->map_expr_from_callee_to_caller (callee_fndecl,
    2354              :                                                         callee_var,
    2355              :                                                         &expr);
    2356              :               }
    2357              :             else
    2358            0 :               caller_var = caller_model->get_representative_tree (sval);
    2359              : 
    2360         1193 :             if (caller_var)
    2361              :               {
    2362          215 :                 if (get_logger ())
    2363              :                   {
    2364            0 :                     label_text sval_desc = sval->get_desc ();
    2365            0 :                     log ("event %i:"
    2366              :                          " recording critical state for %qs at call"
    2367              :                          " from %qE in callee to %qE in caller",
    2368              :                          idx, sval_desc.get (), callee_var, caller_var);
    2369            0 :                   }
    2370          215 :                 if (expr.param_p ())
    2371          215 :                   event->record_critical_state (caller_var, state);
    2372              :               }
    2373              :           }
    2374         1193 :           break;
    2375              : 
    2376          601 :         case event_kind::return_:
    2377          601 :           {
    2378          601 :             if (sval)
    2379              :               {
    2380          431 :                 return_event *event = (return_event *)base_event;
    2381          431 :                 const region_model *caller_model
    2382          431 :                   = event->m_eedge.m_dest->get_state ().m_region_model;
    2383          431 :                 tree caller_var = caller_model->get_representative_tree (sval);
    2384          431 :                 const region_model *callee_model
    2385          431 :                   = event->m_eedge.m_src->get_state ().m_region_model;
    2386          431 :                 callsite_expr expr;
    2387              : 
    2388          431 :                 tree callee_var;
    2389              : 
    2390          431 :                 if (auto op = event->get_call_and_return_op ())
    2391              :                   {
    2392          431 :                     tree callee_fndecl
    2393          431 :                       = event->m_eedge.m_src->get_point ().get_fndecl ();
    2394          431 :                     callee_var
    2395          431 :                       = op->map_expr_from_caller_to_callee (callee_fndecl,
    2396              :                                                             caller_var,
    2397              :                                                             &expr);
    2398              :                   }
    2399              :                 else
    2400            0 :                   callee_var = callee_model->get_representative_tree (sval);
    2401              : 
    2402          431 :                 if (callee_var)
    2403              :                   {
    2404          185 :                     if (get_logger ())
    2405              :                       {
    2406            0 :                         label_text sval_desc = sval->get_desc ();
    2407            0 :                         log ("event %i:"
    2408              :                              " recording critical state for %qs at return"
    2409              :                              " from %qE in caller to %qE in callee",
    2410              :                              idx, sval_desc.get (), callee_var, callee_var);
    2411            0 :                       }
    2412          185 :                     if (expr.return_value_p ())
    2413           94 :                       event->record_critical_state (callee_var, state);
    2414              :                   }
    2415              :               }
    2416              :           }
    2417              :           break;
    2418              : 
    2419              :         case event_kind::inlined_call:
    2420              :           /* We don't expect to see these yet, as they're added later.
    2421              :              We'd want to keep them around.  */
    2422              :           break;
    2423              : 
    2424              :         case event_kind::setjmp_:
    2425              :           /* TODO: only show setjmp_events that matter i.e. those for which
    2426              :              there is a later rewind event using them.  */
    2427              :         case event_kind::rewind_from_longjmp:
    2428              :         case event_kind::rewind_to_setjmp:
    2429              :           break;
    2430              : 
    2431              :         case event_kind::warning:
    2432              :           /* Always show the final "warning" event in the path.  */
    2433              :           break;
    2434              :         }
    2435        31697 :       idx--;
    2436              :     }
    2437         3991 : }
    2438              : 
    2439              : /* Subroutine of diagnostic_manager::prune_for_sm_diagnostic.
    2440              :    If *EXPR is not suitable to be the expression of interest in
    2441              :    an sm-diagnostic, set *EXPR to NULL and log.  */
    2442              : 
    2443              : void
    2444            0 : diagnostic_manager::update_for_unsuitable_sm_exprs (tree *expr) const
    2445              : {
    2446            0 :   gcc_assert (expr);
    2447            0 :   if (*expr && !can_be_expr_of_interest_p (*expr))
    2448              :     {
    2449            0 :       log ("new var %qE is unsuitable; setting var to NULL", *expr);
    2450            0 :       *expr = NULL_TREE;
    2451              :     }
    2452            0 : }
    2453              : 
    2454              : /* Second pass of diagnostic_manager::prune_path: remove redundant
    2455              :    interprocedural information.
    2456              : 
    2457              :    For example, given:
    2458              :      (1)- calling "f2" from "f1"
    2459              :      (2)--- entry to "f2"
    2460              :      (3)--- calling "f3" from "f2"
    2461              :      (4)----- entry to "f3"
    2462              :      (5)--- returning to "f2" to "f3"
    2463              :      (6)- returning to "f1" to "f2"
    2464              :    with no other intervening events, then none of these events are
    2465              :    likely to be interesting to the user.
    2466              : 
    2467              :    Prune [..., call, function-entry, return, ...] triples repeatedly
    2468              :    until nothing has changed.  For the example above, this would
    2469              :    remove events (3, 4, 5), and then remove events (1, 2, 6).  */
    2470              : 
    2471              : void
    2472         3991 : diagnostic_manager::prune_interproc_events (checker_path *path) const
    2473              : {
    2474         3991 :   bool changed = false;
    2475         4208 :   do
    2476              :     {
    2477         4208 :       changed = false;
    2478         4208 :       int idx = (signed)path->num_events () - 1;
    2479        21812 :       while (idx >= 0)
    2480              :         {
    2481              :           /* Prune [..., call, function-entry, return, ...] triples.  */
    2482        17604 :           if (idx + 2 < (signed)path->num_events ()
    2483         9572 :               && path->get_checker_event (idx)->is_call_p ()
    2484          932 :               && path->get_checker_event (idx + 1)->is_function_entry_p ()
    2485        18528 :               && path->get_checker_event (idx + 2)->is_return_p ())
    2486              :             {
    2487          304 :               if (get_logger ())
    2488              :                 {
    2489            0 :                   label_text desc
    2490            0 :                     (path->get_checker_event (idx)->get_desc
    2491            0 :                        (*global_dc->get_reference_printer ()));
    2492            0 :                   log ("filtering events %i-%i:"
    2493              :                        " irrelevant call/entry/return: %s",
    2494              :                        idx, idx + 2, desc.get ());
    2495            0 :                 }
    2496          304 :               path->delete_event (idx + 2);
    2497          304 :               path->delete_event (idx + 1);
    2498          304 :               path->delete_event (idx);
    2499          304 :               changed = true;
    2500          304 :               idx--;
    2501          304 :               continue;
    2502          304 :             }
    2503              : 
    2504              :           /* Prune [..., call, return, ...] pairs
    2505              :              (for -fanalyzer-verbosity=0).  */
    2506        17300 :           if (idx + 1 < (signed)path->num_events ()
    2507        13046 :               && path->get_checker_event (idx)->is_call_p ()
    2508        18287 :               && path->get_checker_event (idx + 1)->is_return_p ())
    2509              :             {
    2510            4 :               if (get_logger ())
    2511              :                 {
    2512            0 :                   label_text desc
    2513            0 :                     (path->get_checker_event (idx)->get_desc
    2514            0 :                      (*global_dc->get_reference_printer ()));
    2515            0 :                   log ("filtering events %i-%i:"
    2516              :                        " irrelevant call/return: %s",
    2517              :                        idx, idx + 1, desc.get ());
    2518            0 :                 }
    2519            4 :               path->delete_event (idx + 1);
    2520            4 :               path->delete_event (idx);
    2521            4 :               changed = true;
    2522            4 :               idx--;
    2523            4 :               continue;
    2524            4 :             }
    2525              : 
    2526        17296 :           idx--;
    2527              :         }
    2528              : 
    2529              :     }
    2530              :   while (changed);
    2531         3991 : }
    2532              : 
    2533              : /* Remove everything within [call point, IDX]. For consistency,
    2534              :    IDX should represent the return event of the frame to delete,
    2535              :    or if there is none it should be the last event of the frame.
    2536              :    After this function, IDX designates the event prior to calling
    2537              :    this frame.  */
    2538              : 
    2539              : static void
    2540            0 : prune_frame (checker_path *path, int &idx)
    2541              : {
    2542            0 :   gcc_assert (idx >= 0);
    2543            0 :   int nesting = 1;
    2544            0 :   if (path->get_checker_event (idx)->is_return_p ())
    2545            0 :     nesting = 0;
    2546            0 :   do
    2547              :     {
    2548            0 :       if (path->get_checker_event (idx)->is_call_p ())
    2549            0 :         nesting--;
    2550            0 :       else if (path->get_checker_event (idx)->is_return_p ())
    2551            0 :         nesting++;
    2552              : 
    2553            0 :       path->delete_event (idx--);
    2554            0 :     } while (idx >= 0 && nesting != 0);
    2555            0 : }
    2556              : 
    2557              : /* This function is called when fanalyzer-show-events-in-system-headers
    2558              :    is disabled and will prune the diagnostic of all events within a
    2559              :    system header, only keeping the entry and exit events to the header.
    2560              :    This should be called after diagnostic_manager::prune_interproc_events
    2561              :    so that sucessive events [system header call, system header return]
    2562              :    are preserved thereafter.
    2563              : 
    2564              :    Given a diagnostics path diving into a system header in the form
    2565              :    [
    2566              :      prefix events...,
    2567              :      system header call,
    2568              :        system header entry,
    2569              :        events within system headers...,
    2570              :      system header return,
    2571              :      suffix events...
    2572              :    ]
    2573              : 
    2574              :    then transforms it into
    2575              :    [
    2576              :      prefix events...,
    2577              :      system header call,
    2578              :      system header return,
    2579              :      suffix events...
    2580              :    ].  */
    2581              : 
    2582              : void
    2583         3989 : diagnostic_manager::prune_system_headers (checker_path *path) const
    2584              : {
    2585         3989 :   int idx = (signed)path->num_events () - 1;
    2586        19409 :   while (idx >= 0)
    2587              :     {
    2588        15420 :       const checker_event *event = path->get_checker_event (idx);
    2589              :       /* Prune everything between
    2590              :          [..., system entry, (...), system return, ...].  */
    2591        15420 :       if (event->is_return_p ()
    2592        15420 :           && in_system_header_at (event->get_location ()))
    2593              :       {
    2594            0 :         int ret_idx = idx;
    2595            0 :         prune_frame (path, idx);
    2596              : 
    2597            0 :         if (get_logger ())
    2598              :         {
    2599            0 :           log ("filtering system headers events %i-%i:",
    2600              :                idx, ret_idx);
    2601              :         }
    2602              :         // Delete function entry within system headers.
    2603            0 :         if (idx >= 0)
    2604              :           {
    2605            0 :             event = path->get_checker_event (idx);
    2606            0 :             if (event->is_function_entry_p ()
    2607            0 :                 && in_system_header_at (event->get_location ()))
    2608              :               {
    2609            0 :                 if (get_logger ())
    2610              :                   {
    2611            0 :                     label_text desc
    2612            0 :                       (event->get_desc (*global_dc->get_reference_printer ()));
    2613            0 :                     log ("filtering event %i:"
    2614              :                          "system header entry event: %s",
    2615              :                          idx, desc.get ());
    2616            0 :                   }
    2617              : 
    2618            0 :                 path->delete_event (idx);
    2619              :               }
    2620              :           }
    2621              :       }
    2622              : 
    2623        15420 :       idx--;
    2624              :     }
    2625         3989 : }
    2626              : 
    2627              : /* Return true iff event IDX within PATH is on the same line as REF_EXP_LOC.  */
    2628              : 
    2629              : static bool
    2630         2712 : same_line_as_p (const expanded_location &ref_exp_loc,
    2631              :                 checker_path *path, unsigned idx)
    2632              : {
    2633         2712 :   const checker_event *ev = path->get_checker_event (idx);
    2634         2712 :   expanded_location idx_exp_loc = expand_location (ev->get_location ());
    2635         2712 :   gcc_assert (ref_exp_loc.file);
    2636         2712 :   if (idx_exp_loc.file == nullptr)
    2637              :     return false;
    2638         2689 :   if (strcmp (ref_exp_loc.file, idx_exp_loc.file))
    2639              :     return false;
    2640         2689 :   return ref_exp_loc.line == idx_exp_loc.line;
    2641              : }
    2642              : 
    2643              : /* This path-readability optimization reduces the verbosity of compound
    2644              :    conditional statements (without needing to reconstruct the AST, which
    2645              :    has already been lost).
    2646              : 
    2647              :    For example, it converts:
    2648              : 
    2649              :     |   61 |   if (cp[0] != '\0' && cp[0] != '#')
    2650              :     |      |      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    2651              :     |      |      |              |    |
    2652              :     |      |      |              |    (6) ...to here
    2653              :     |      |      |              (7) following ‘true’ branch...
    2654              :     |      |      (5) following ‘true’ branch...
    2655              :     |   62 |     {
    2656              :     |   63 |       alias = cp++;
    2657              :     |      |               ~~~~
    2658              :     |      |                 |
    2659              :     |      |                 (8) ...to here
    2660              : 
    2661              :    into:
    2662              : 
    2663              :     |   61 |   if (cp[0] != '\0' && cp[0] != '#')
    2664              :     |      |      ~
    2665              :     |      |      |
    2666              :     |      |      (5) following ‘true’ branch...
    2667              :     |   62 |     {
    2668              :     |   63 |       alias = cp++;
    2669              :     |      |               ~~~~
    2670              :     |      |                 |
    2671              :     |      |                 (6) ...to here
    2672              : 
    2673              :    by combining events 5-8 into new events 5-6.
    2674              : 
    2675              :    Find runs of consecutive (start_cfg_edge_event, end_cfg_edge_event) pairs
    2676              :    in which all events apart from the final end_cfg_edge_event are on the same
    2677              :    line, and for which either all the CFG edges are TRUE edges, or all are
    2678              :    FALSE edges.
    2679              : 
    2680              :    Consolidate each such run into a
    2681              :      (start_consolidated_cfg_edges_event, end_consolidated_cfg_edges_event)
    2682              :    pair.  */
    2683              : 
    2684              : void
    2685         3991 : diagnostic_manager::consolidate_conditions (checker_path *path) const
    2686              : {
    2687              :   /* Don't simplify edges if we're debugging them.  */
    2688         3991 :   if (flag_analyzer_verbose_edges)
    2689              :     return;
    2690              : 
    2691        11291 :   for (int start_idx = 0;
    2692        30473 :        start_idx < (signed)path->num_events () - 1;
    2693              :        start_idx++)
    2694              :     {
    2695        11291 :       if (path->cfg_edge_pair_at_p (start_idx))
    2696              :         {
    2697         2455 :           const checker_event *old_start_ev
    2698         2455 :             = path->get_checker_event (start_idx);
    2699         2455 :           expanded_location start_exp_loc
    2700         2455 :             = expand_location (old_start_ev->get_location ());
    2701         2455 :           if (start_exp_loc.file == nullptr)
    2702         2104 :             continue;
    2703         2455 :           if (!same_line_as_p (start_exp_loc, path, start_idx + 1))
    2704         2104 :             continue;
    2705              : 
    2706              :           /* Are we looking for a run of all TRUE edges, or all FALSE edges?  */
    2707          351 :           gcc_assert (old_start_ev->get_kind () == event_kind::start_cfg_edge);
    2708          351 :           const start_cfg_edge_event *old_start_cfg_ev
    2709              :             = (const start_cfg_edge_event *)old_start_ev;
    2710          351 :           bool edge_sense;
    2711          351 :           if (!old_start_cfg_ev->maybe_get_edge_sense (&edge_sense))
    2712            0 :             continue;
    2713              : 
    2714              :           /* Find a run of CFG start/end event pairs from
    2715              :                [start_idx, next_idx)
    2716              :              where all apart from the final event are on the same line,
    2717              :              and all are either TRUE or FALSE edges, matching the initial.  */
    2718          351 :           int next_idx = start_idx + 2;
    2719          351 :           while (path->cfg_edge_pair_at_p (next_idx)
    2720          467 :                  && same_line_as_p (start_exp_loc, path, next_idx))
    2721              :             {
    2722          156 :               const checker_event *iter_ev
    2723          156 :                 = path->get_checker_event (next_idx);
    2724          156 :               gcc_assert (iter_ev->get_kind () == event_kind::start_cfg_edge);
    2725          156 :               const start_cfg_edge_event *iter_cfg_ev
    2726              :                 = (const start_cfg_edge_event *)iter_ev;
    2727          156 :               bool iter_edge_sense;
    2728          156 :               if (!iter_cfg_ev->maybe_get_edge_sense (&iter_edge_sense))
    2729              :                 break;
    2730          156 :               if (iter_edge_sense != edge_sense)
    2731              :                 break;
    2732          116 :               next_idx += 2;
    2733              :             }
    2734              : 
    2735              :           /* If we have more than one pair in the run, consolidate.  */
    2736          351 :           if (next_idx > start_idx + 2)
    2737              :             {
    2738          108 :               const checker_event *old_end_ev
    2739          108 :                 = path->get_checker_event (next_idx - 1);
    2740          108 :               log ("consolidating CFG edge events %i-%i into %i-%i",
    2741              :                    start_idx, next_idx - 1, start_idx, start_idx +1);
    2742          108 :               start_consolidated_cfg_edges_event *new_start_ev
    2743              :                 = new start_consolidated_cfg_edges_event
    2744          108 :                 (event_loc_info (old_start_ev->get_location (),
    2745              :                                  old_start_ev->get_fndecl (),
    2746          108 :                                  old_start_ev->get_stack_depth ()),
    2747          108 :                  edge_sense);
    2748          108 :               checker_event *new_end_ev
    2749              :                 = new end_consolidated_cfg_edges_event
    2750          108 :                 (event_loc_info (old_end_ev->get_location (),
    2751              :                                  old_end_ev->get_fndecl (),
    2752          108 :                                  old_end_ev->get_stack_depth ()));
    2753          108 :               path->replace_event (start_idx, new_start_ev);
    2754          108 :               path->replace_event (start_idx + 1, new_end_ev);
    2755          108 :               path->delete_events (start_idx + 2, next_idx - (start_idx + 2));
    2756              :             }
    2757              :         }
    2758              :     }
    2759              : }
    2760              : 
    2761              : /* Consolidate runs of consecutive unwind_event.  */
    2762              : 
    2763              : void
    2764         3991 : diagnostic_manager::consolidate_unwind_events (checker_path *path) const
    2765              : {
    2766              :   /* Don't simplify edges if we're debugging them.  */
    2767         3991 :   if (flag_analyzer_verbose_edges)
    2768              :     return;
    2769              : 
    2770        11285 :   for (int start_idx = 0;
    2771        30461 :        start_idx < (signed)path->num_events () - 1;
    2772              :        start_idx++)
    2773              :     {
    2774              :       /* Find a run of consecutive unwind_event instances.  */
    2775        11285 :       if (path->get_checker_event (start_idx)->get_kind ()
    2776              :           != event_kind::unwind)
    2777        11276 :         continue;
    2778            9 :       int iter_idx = start_idx + 1;
    2779           15 :       while (iter_idx < (int)path->num_events ())
    2780           15 :         if (path->get_checker_event (iter_idx)->get_kind ()
    2781              :             == event_kind::unwind)
    2782            6 :           ++iter_idx;
    2783              :         else
    2784              :           break;
    2785              : 
    2786              :       /* iter_idx should now be one after the last unwind_event in the run.  */
    2787            9 :       const int last_idx = iter_idx - 1;
    2788            9 :       if (last_idx == start_idx)
    2789            3 :         continue;
    2790              : 
    2791            6 :       gcc_assert (last_idx > start_idx);
    2792              : 
    2793            6 :       log ("consolidating unwind events %i-%i into %i",
    2794              :            start_idx, last_idx, start_idx);
    2795              : 
    2796            6 :       unwind_event *first_event
    2797            6 :         = (unwind_event *)path->get_checker_event (start_idx);
    2798            6 :       const unwind_event *last_event
    2799            6 :         = (const unwind_event *)path->get_checker_event (last_idx);
    2800            6 :       first_event->m_num_frames += last_event->m_num_frames;
    2801            6 :       path->delete_events (start_idx + 1, last_idx - start_idx);
    2802              :     }
    2803              : }
    2804              : 
    2805              : /* Final pass of diagnostic_manager::prune_path.
    2806              : 
    2807              :    If all we're left with is in one function, then filter function entry
    2808              :    events.  */
    2809              : 
    2810              : void
    2811         3991 : diagnostic_manager::finish_pruning (checker_path *path) const
    2812              : {
    2813         3991 :   if (!path->interprocedural_p ())
    2814              :     {
    2815         3214 :       int idx = path->num_events () - 1;
    2816        23426 :       while (idx >= 0 && idx < (signed)path->num_events ())
    2817              :         {
    2818        10106 :           checker_event *base_event = path->get_checker_event (idx);
    2819        10106 :           if (base_event->get_kind () == event_kind::function_entry)
    2820              :             {
    2821         3118 :               log ("filtering event %i:"
    2822              :                    " function entry for purely intraprocedural path", idx);
    2823         3118 :               path->delete_event (idx);
    2824              :             }
    2825        10106 :           idx--;
    2826              :         }
    2827              :     }
    2828         3991 : }
    2829              : 
    2830              : } // namespace ana
    2831              : 
    2832              : #endif /* #if ENABLE_ANALYZER */
        

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.