(root)/
gcc-13.2.0/
gcc/
analyzer/
pending-diagnostic.h
       1  /* Classes for analyzer diagnostics.
       2     Copyright (C) 2019-2023 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  #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H
      22  #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H
      23  
      24  #include "diagnostic-path.h"
      25  #include "analyzer/sm.h"
      26  
      27  namespace ana {
      28  
      29  /* A bundle of information about things that are of interest to a
      30     pending_diagnostic.
      31  
      32     For now, merely the set of regions that are pertinent to the
      33     diagnostic, so that we can notify the user about when they
      34     were created.  */
      35  
      36  struct interesting_t
      37  {
      38    void add_region_creation (const region *reg);
      39  
      40    void dump_to_pp (pretty_printer *pp, bool simple) const;
      41  
      42    auto_vec<const region *> m_region_creation;
      43  };
      44  
      45  /* Various bundles of information used for generating more precise
      46     messages for events within a diagnostic_path, for passing to the
      47     various "describe_*" vfuncs of pending_diagnostic.  See those
      48     for more information.  */
      49  
      50  namespace evdesc {
      51  
      52  struct event_desc
      53  {
      54    event_desc (bool colorize) : m_colorize (colorize) {}
      55  
      56    label_text formatted_print (const char *fmt, ...) const
      57      ATTRIBUTE_GCC_DIAG(2,3);
      58  
      59    bool m_colorize;
      60  };
      61  
      62  /* For use by pending_diagnostic::describe_state_change.  */
      63  
      64  struct state_change : public event_desc
      65  {
      66    state_change (bool colorize,
      67  		tree expr,
      68  		tree origin,
      69  		state_machine::state_t old_state,
      70  		state_machine::state_t new_state,
      71  		diagnostic_event_id_t event_id,
      72  		const state_change_event &event)
      73    : event_desc (colorize),
      74      m_expr (expr), m_origin (origin),
      75      m_old_state (old_state), m_new_state (new_state),
      76      m_event_id (event_id), m_event (event)
      77    {}
      78  
      79    bool is_global_p () const { return m_expr == NULL_TREE; }
      80  
      81    tree m_expr;
      82    tree m_origin;
      83    state_machine::state_t m_old_state;
      84    state_machine::state_t m_new_state;
      85    diagnostic_event_id_t m_event_id;
      86    const state_change_event &m_event;
      87  };
      88  
      89  /* For use by pending_diagnostic::describe_call_with_state.  */
      90  
      91  struct call_with_state : public event_desc
      92  {
      93    call_with_state (bool colorize,
      94  		   tree caller_fndecl, tree callee_fndecl,
      95  		   tree expr, state_machine::state_t state)
      96    : event_desc (colorize),
      97      m_caller_fndecl (caller_fndecl),
      98      m_callee_fndecl (callee_fndecl),
      99      m_expr (expr),
     100      m_state (state)
     101    {
     102    }
     103  
     104    tree m_caller_fndecl;
     105    tree m_callee_fndecl;
     106    tree m_expr;
     107    state_machine::state_t m_state;
     108  };
     109  
     110  /* For use by pending_diagnostic::describe_return_of_state.  */
     111  
     112  struct return_of_state : public event_desc
     113  {
     114    return_of_state (bool colorize,
     115  		   tree caller_fndecl, tree callee_fndecl,
     116  		   state_machine::state_t state)
     117    : event_desc (colorize),
     118      m_caller_fndecl (caller_fndecl),
     119      m_callee_fndecl (callee_fndecl),
     120      m_state (state)
     121    {
     122    }
     123  
     124    tree m_caller_fndecl;
     125    tree m_callee_fndecl;
     126    state_machine::state_t m_state;
     127  };
     128  
     129  /* For use by pending_diagnostic::describe_final_event.  */
     130  
     131  struct final_event : public event_desc
     132  {
     133    final_event (bool colorize,
     134  	       tree expr, state_machine::state_t state,
     135  	       const warning_event &event)
     136    : event_desc (colorize),
     137      m_expr (expr), m_state (state), m_event (event)
     138    {}
     139  
     140    tree m_expr;
     141    state_machine::state_t m_state;
     142    const warning_event &m_event;
     143  };
     144  
     145  } /* end of namespace evdesc */
     146  
     147  /* An abstract base class for capturing information about a diagnostic in
     148     a form that is ready to emit at a later point (or be rejected).
     149     Each kind of diagnostic will have a concrete subclass of
     150     pending_diagnostic.
     151  
     152     Normally, gcc diagnostics are emitted using va_list, which can't be
     153     portably stored for later use, so we have to use an "emit" virtual
     154     function.
     155  
     156     This class also supports comparison, so that multiple pending_diagnostic
     157     instances can be de-duplicated.
     158  
     159     As well as emitting a diagnostic, the class has various "precision of
     160     wording" virtual functions, for generating descriptions for events
     161     within a diagnostic_path.  These are optional, but implementing these
     162     allows for more precise wordings than the more generic
     163     implementation.  */
     164  
     165  class pending_diagnostic
     166  {
     167   public:
     168    virtual ~pending_diagnostic () {}
     169  
     170    /* Vfunc to get the command-line option used when emitting the diagnostic,
     171       or zero if there is none.
     172       Used by diagnostic_manager for early rejection of diagnostics (to avoid
     173       having to generate feasible execution paths for them).  */
     174    virtual int get_controlling_option () const = 0;
     175  
     176    /* Vfunc to give the diagnostic the chance to terminate the execution
     177       path being explored.  By default, don't terminate the path.  */
     178    virtual bool terminate_path_p () const { return false; }
     179  
     180    /* Vfunc for emitting the diagnostic.  The rich_location will have been
     181       populated with a diagnostic_path.
     182       Return true if a diagnostic is actually emitted.  */
     183    virtual bool emit (rich_location *) = 0;
     184  
     185    /* Hand-coded RTTI: get an ID for the subclass.  */
     186    virtual const char *get_kind () const = 0;
     187  
     188    /* A vfunc for identifying "use of uninitialized value".  */
     189    virtual bool use_of_uninit_p () const { return false; }
     190  
     191    /* Compare for equality with OTHER, which might be of a different
     192       subclass.  */
     193  
     194    bool equal_p (const pending_diagnostic &other) const
     195    {
     196      /* Check for pointer equality on the IDs from get_kind.  */
     197      if (get_kind () != other.get_kind ())
     198        return false;
     199      /* Call vfunc now we know they have the same ID: */
     200      return subclass_equal_p (other);
     201    }
     202  
     203    /* A vfunc for testing for equality, where we've already
     204       checked they have the same ID.  See pending_diagnostic_subclass
     205       below for a convenience subclass for implementing this.  */
     206    virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0;
     207  
     208    /* Return true if T1 and T2 are "the same" for the purposes of
     209       diagnostic deduplication.  */
     210    static bool same_tree_p (tree t1, tree t2);
     211  
     212    /* Vfunc for fixing up locations, e.g. to avoid unwinding
     213       inside specific macros.  PRIMARY is true for the primary location
     214       for the diagnostic, and FALSE for events in their paths.  */
     215    virtual location_t fixup_location (location_t loc, bool primary) const;
     216  
     217    /* Precision-of-wording vfunc for describing a critical state change
     218       within the diagnostic_path.
     219  
     220       For example, a double-free diagnostic might use the descriptions:
     221       - "first 'free' happens here"
     222       - "second 'free' happens here"
     223       for the pertinent events, whereas a use-after-free might use the
     224       descriptions:
     225       - "freed here"
     226       - "use after free here"
     227       Note how in both cases the first event is a "free": the best
     228       description to use depends on the diagnostic.  */
     229  
     230    virtual label_text describe_state_change (const evdesc::state_change &)
     231    {
     232      /* Default no-op implementation.  */
     233      return label_text ();
     234    }
     235  
     236    /* Vfunc for implementing diagnostic_event::get_meaning for
     237       state_change_event.  */
     238    virtual diagnostic_event::meaning
     239    get_meaning_for_state_change (const evdesc::state_change &) const
     240    {
     241      /* Default no-op implementation.  */
     242      return diagnostic_event::meaning ();
     243    }
     244  
     245    /* Precision-of-wording vfunc for describing an interprocedural call
     246       carrying critial state for the diagnostic, from caller to callee.
     247  
     248       For example a double-free diagnostic might use:
     249       - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'"
     250       to make it clearer how the freed value moves from caller to
     251       callee.  */
     252  
     253    virtual label_text describe_call_with_state (const evdesc::call_with_state &)
     254    {
     255      /* Default no-op implementation.  */
     256      return label_text ();
     257    }
     258  
     259    /* Precision-of-wording vfunc for describing an interprocedural return
     260       within the diagnostic_path that carries critial state for the
     261       diagnostic, from callee back to caller.
     262  
     263       For example, a deref-of-unchecked-malloc diagnostic might use:
     264       - "returning possibly-NULL pointer to 'make_obj' from 'allocator'"
     265       to make it clearer how the unchecked value moves from callee
     266       back to caller.  */
     267  
     268    virtual label_text describe_return_of_state (const evdesc::return_of_state &)
     269    {
     270      /* Default no-op implementation.  */
     271      return label_text ();
     272    }
     273  
     274    /* Precision-of-wording vfunc for describing the final event within a
     275       diagnostic_path.
     276  
     277       For example a double-free diagnostic might use:
     278        - "second 'free' here; first 'free' was at (3)"
     279       and a use-after-free might use
     280        - "use after 'free' here; memory was freed at (2)".  */
     281  
     282    virtual label_text describe_final_event (const evdesc::final_event &)
     283    {
     284      /* Default no-op implementation.  */
     285      return label_text ();
     286    }
     287  
     288    /* End of precision-of-wording vfuncs.  */
     289  
     290    /* Vfunc for adding a function_entry_event to a checker_path, so that e.g.
     291       the infinite recursion diagnostic can add a custom event subclass
     292       that annotates recursively entering a function.  */
     293  
     294    virtual void
     295    add_function_entry_event (const exploded_edge &eedge,
     296  			    checker_path *emission_path);
     297  
     298    /* Vfunc for extending/overriding creation of the events for an
     299       exploded_edge that corresponds to a superedge, allowing for custom
     300       events to be created that are pertinent to a particular
     301       pending_diagnostic subclass.
     302  
     303       For example, the -Wanalyzer-stale-setjmp-buffer diagnostic adds a
     304       custom event showing when the pertinent stack frame is popped
     305       (and thus the point at which the jmp_buf becomes invalid).  */
     306  
     307    virtual bool maybe_add_custom_events_for_superedge (const exploded_edge &,
     308  						      checker_path *)
     309    {
     310      return false;
     311    }
     312  
     313    /* Vfunc for adding a call_event to a checker_path, so that e.g.
     314       the varargs diagnostics can add a custom event subclass that annotates
     315       the variadic arguments.  */
     316    virtual void add_call_event (const exploded_edge &,
     317  			       checker_path *);
     318  
     319    /* Vfunc for adding any events for the creation of regions identified
     320       by the mark_interesting_stuff vfunc.
     321       See the comment for class region_creation_event.  */
     322    virtual void add_region_creation_events (const region *reg,
     323  					   tree capacity,
     324  					   const event_loc_info &loc_info,
     325  					   checker_path &emission_path);
     326  
     327    /* Vfunc for adding the final warning_event to a checker_path, so that e.g.
     328       the infinite recursion diagnostic can have its diagnostic appear at
     329       the callsite, but the final event in the path be at the entrypoint
     330       of the called function.  */
     331    virtual void add_final_event (const state_machine *sm,
     332  				const exploded_node *enode,
     333  				const gimple *stmt,
     334  				tree var, state_machine::state_t state,
     335  				checker_path *emission_path);
     336  
     337    /* Vfunc for determining that this pending_diagnostic supercedes OTHER,
     338       and that OTHER should therefore not be emitted.
     339       They have already been tested for being at the same stmt.  */
     340  
     341    virtual bool
     342    supercedes_p (const pending_diagnostic &other ATTRIBUTE_UNUSED) const
     343    {
     344      return false;
     345    }
     346  
     347    /* Vfunc for registering additional information of interest to this
     348       diagnostic.  */
     349  
     350    virtual void mark_interesting_stuff (interesting_t *)
     351    {
     352      /* Default no-op implementation.  */
     353    }
     354  
     355    /* Vfunc to give diagnostic subclasses the opportunity to reject diagnostics
     356       by imposing their own additional feasibility checks on the path to a
     357       given feasible_node.  */
     358    virtual bool check_valid_fpath_p (const feasible_node &,
     359  				    const gimple *) const
     360    {
     361      /* Default implementation: accept this path.  */
     362      return true;
     363    }
     364  };
     365  
     366  /* A template to make it easier to make subclasses of pending_diagnostic.
     367  
     368     This uses the curiously-recurring template pattern, to implement
     369     pending_diagnostic::subclass_equal_p by casting and calling
     370     the operator==
     371  
     372     This assumes that BASE_OTHER has already been checked to have
     373     been of the same subclass (which pending_diagnostic::equal_p does).  */
     374  
     375  template <class Subclass>
     376  class pending_diagnostic_subclass : public pending_diagnostic
     377  {
     378   public:
     379    bool subclass_equal_p (const pending_diagnostic &base_other) const
     380      final override
     381    {
     382      const Subclass &other = (const Subclass &)base_other;
     383      return *(const Subclass*)this == other;
     384    }
     385  };
     386  
     387  /* An abstract base class for capturing additional notes that are to be
     388     emitted with a diagnostic.  */
     389  
     390  class pending_note
     391  {
     392  public:
     393    virtual ~pending_note () {}
     394  
     395    /* Hand-coded RTTI: get an ID for the subclass.  */
     396    virtual const char *get_kind () const = 0;
     397  
     398    /* Vfunc for emitting the note.  */
     399    virtual void emit () const = 0;
     400  
     401    bool equal_p (const pending_note &other) const
     402    {
     403      /* Check for pointer equality on the IDs from get_kind.  */
     404      if (get_kind () != other.get_kind ())
     405        return false;
     406      /* Call vfunc now we know they have the same ID: */
     407      return subclass_equal_p (other);
     408    }
     409  
     410    /* A vfunc for testing for equality, where we've already
     411       checked they have the same ID.  See pending_note_subclass
     412       below for a convenience subclass for implementing this.  */
     413    virtual bool subclass_equal_p (const pending_note &other) const = 0;
     414  };
     415  
     416  /* Analogous to pending_diagnostic_subclass, but for pending_note.  */
     417  
     418  template <class Subclass>
     419  class pending_note_subclass : public pending_note
     420  {
     421   public:
     422    bool subclass_equal_p (const pending_note &base_other) const
     423      final override
     424    {
     425      const Subclass &other = (const Subclass &)base_other;
     426      return *(const Subclass*)this == other;
     427    }
     428  };
     429  
     430  } // namespace ana
     431  
     432  #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */