| /* Subclasses of diagnostic_event for analyzer diagnostics. |
| Copyright (C) 2019-2022 Free Software Foundation, Inc. |
| Contributed by David Malcolm <dmalcolm@redhat.com>. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it |
| under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #ifndef GCC_ANALYZER_CHECKER_EVENT_H |
| #define GCC_ANALYZER_CHECKER_EVENT_H |
| |
| #include "tree-logical-location.h" |
| |
| namespace ana { |
| |
| /* An enum for discriminating between the concrete subclasses of |
| checker_event. */ |
| |
| enum event_kind |
| { |
| EK_DEBUG, |
| EK_CUSTOM, |
| EK_STMT, |
| EK_REGION_CREATION, |
| EK_FUNCTION_ENTRY, |
| EK_STATE_CHANGE, |
| EK_START_CFG_EDGE, |
| EK_END_CFG_EDGE, |
| EK_CALL_EDGE, |
| EK_RETURN_EDGE, |
| EK_START_CONSOLIDATED_CFG_EDGES, |
| EK_END_CONSOLIDATED_CFG_EDGES, |
| EK_INLINED_CALL, |
| EK_SETJMP, |
| EK_REWIND_FROM_LONGJMP, |
| EK_REWIND_TO_SETJMP, |
| EK_WARNING |
| }; |
| |
| extern const char *event_kind_to_string (enum event_kind ek); |
| |
| /* Event subclasses. |
| |
| The class hierarchy looks like this (using indentation to show |
| inheritance, and with event_kinds shown for the concrete subclasses): |
| |
| diagnostic_event |
| checker_event |
| debug_event (EK_DEBUG) |
| custom_event (EK_CUSTOM) |
| precanned_custom_event |
| statement_event (EK_STMT) |
| region_creation_event (EK_REGION_CREATION) |
| function_entry_event (EK_FUNCTION_ENTRY) |
| state_change_event (EK_STATE_CHANGE) |
| superedge_event |
| cfg_edge_event |
| start_cfg_edge_event (EK_START_CFG_EDGE) |
| end_cfg_edge_event (EK_END_CFG_EDGE) |
| call_event (EK_CALL_EDGE) |
| return_edge (EK_RETURN_EDGE) |
| start_consolidated_cfg_edges_event (EK_START_CONSOLIDATED_CFG_EDGES) |
| end_consolidated_cfg_edges_event (EK_END_CONSOLIDATED_CFG_EDGES) |
| inlined_call_event (EK_INLINED_CALL) |
| setjmp_event (EK_SETJMP) |
| rewind_event |
| rewind_from_longjmp_event (EK_REWIND_FROM_LONGJMP) |
| rewind_to_setjmp_event (EK_REWIND_TO_SETJMP) |
| warning_event (EK_WARNING). */ |
| |
| /* Abstract subclass of diagnostic_event; the base class for use in |
| checker_path (the analyzer's diagnostic_path subclass). */ |
| |
| class checker_event : public diagnostic_event |
| { |
| public: |
| /* Implementation of diagnostic_event. */ |
| |
| location_t get_location () const final override { return m_loc; } |
| tree get_fndecl () const final override { return m_effective_fndecl; } |
| int get_stack_depth () const final override { return m_effective_depth; } |
| const logical_location *get_logical_location () const final override |
| { |
| if (m_effective_fndecl) |
| return &m_logical_loc; |
| else |
| return NULL; |
| } |
| meaning get_meaning () const override; |
| |
| /* Additional functionality. */ |
| |
| int get_original_stack_depth () const { return m_original_depth; } |
| |
| virtual void prepare_for_emission (checker_path *, |
| pending_diagnostic *pd, |
| diagnostic_event_id_t emission_id); |
| virtual bool is_call_p () const { return false; } |
| virtual bool is_function_entry_p () const { return false; } |
| virtual bool is_return_p () const { return false; } |
| |
| /* For use with %@. */ |
| const diagnostic_event_id_t *get_id_ptr () const |
| { |
| return &m_emission_id; |
| } |
| |
| void dump (pretty_printer *pp) const; |
| void debug () const; |
| |
| void set_location (location_t loc) { m_loc = loc; } |
| |
| protected: |
| checker_event (enum event_kind kind, |
| location_t loc, tree fndecl, int depth); |
| |
| public: |
| const enum event_kind m_kind; |
| protected: |
| location_t m_loc; |
| tree m_original_fndecl; |
| tree m_effective_fndecl; |
| int m_original_depth; |
| int m_effective_depth; |
| pending_diagnostic *m_pending_diagnostic; |
| diagnostic_event_id_t m_emission_id; // only set once all pruning has occurred |
| tree_logical_location m_logical_loc; |
| }; |
| |
| /* A concrete event subclass for a purely textual event, for use in |
| debugging path creation and filtering. */ |
| |
| class debug_event : public checker_event |
| { |
| public: |
| debug_event (location_t loc, tree fndecl, int depth, |
| const char *desc) |
| : checker_event (EK_DEBUG, loc, fndecl, depth), |
| m_desc (xstrdup (desc)) |
| { |
| } |
| ~debug_event () |
| { |
| free (m_desc); |
| } |
| |
| label_text get_desc (bool) const final override; |
| |
| private: |
| char *m_desc; |
| }; |
| |
| /* An abstract event subclass for custom events. These are not filtered, |
| as they are likely to be pertinent to the diagnostic. */ |
| |
| class custom_event : public checker_event |
| { |
| protected: |
| custom_event (location_t loc, tree fndecl, int depth) |
| : checker_event (EK_CUSTOM, loc, fndecl, depth) |
| { |
| } |
| }; |
| |
| /* A concrete custom_event subclass with a precanned message. */ |
| |
| class precanned_custom_event : public custom_event |
| { |
| public: |
| precanned_custom_event (location_t loc, tree fndecl, int depth, |
| const char *desc) |
| : custom_event (loc, fndecl, depth), |
| m_desc (xstrdup (desc)) |
| { |
| } |
| ~precanned_custom_event () |
| { |
| free (m_desc); |
| } |
| |
| label_text get_desc (bool) const final override; |
| |
| private: |
| char *m_desc; |
| }; |
| |
| /* A concrete event subclass describing the execution of a gimple statement, |
| for use at high verbosity levels when debugging paths. */ |
| |
| class statement_event : public checker_event |
| { |
| public: |
| statement_event (const gimple *stmt, tree fndecl, int depth, |
| const program_state &dst_state); |
| |
| label_text get_desc (bool) const final override; |
| |
| const gimple * const m_stmt; |
| const program_state m_dst_state; |
| }; |
| |
| /* There are too many combinations to express region creation in one message, |
| so we emit multiple region_creation_event instances when each pertinent |
| region is created. |
| |
| This enum distinguishes between the different messages. */ |
| |
| enum rce_kind |
| { |
| /* Generate a message based on the memory space of the region |
| e.g. "region created on stack here". */ |
| RCE_MEM_SPACE, |
| |
| /* Generate a message based on the capacity of the region |
| e.g. "capacity: 100 bytes". */ |
| RCE_CAPACITY, |
| |
| /* Generate a debug message. */ |
| RCE_DEBUG |
| }; |
| |
| /* A concrete event subclass describing the creation of a region that |
| is significant for a diagnostic. */ |
| |
| class region_creation_event : public checker_event |
| { |
| public: |
| region_creation_event (const region *reg, |
| tree capacity, |
| enum rce_kind kind, |
| location_t loc, tree fndecl, int depth); |
| |
| label_text get_desc (bool can_colorize) const final override; |
| |
| private: |
| const region *m_reg; |
| tree m_capacity; |
| enum rce_kind m_rce_kind; |
| }; |
| |
| /* An event subclass describing the entry to a function. */ |
| |
| class function_entry_event : public checker_event |
| { |
| public: |
| function_entry_event (location_t loc, tree fndecl, int depth) |
| : checker_event (EK_FUNCTION_ENTRY, loc, fndecl, depth) |
| { |
| } |
| |
| function_entry_event (const program_point &dst_point); |
| |
| label_text get_desc (bool can_colorize) const override; |
| meaning get_meaning () const override; |
| |
| bool is_function_entry_p () const final override { return true; } |
| }; |
| |
| /* Subclass of checker_event describing a state change. */ |
| |
| class state_change_event : public checker_event |
| { |
| public: |
| state_change_event (const supernode *node, const gimple *stmt, |
| int stack_depth, |
| const state_machine &sm, |
| const svalue *sval, |
| state_machine::state_t from, |
| state_machine::state_t to, |
| const svalue *origin, |
| const program_state &dst_state); |
| |
| label_text get_desc (bool can_colorize) const final override; |
| meaning get_meaning () const override; |
| |
| function *get_dest_function () const |
| { |
| return m_dst_state.get_current_function (); |
| } |
| |
| const supernode *m_node; |
| const gimple *m_stmt; |
| const state_machine &m_sm; |
| const svalue *m_sval; |
| state_machine::state_t m_from; |
| state_machine::state_t m_to; |
| const svalue *m_origin; |
| program_state m_dst_state; |
| }; |
| |
| /* Subclass of checker_event; parent class for subclasses that relate to |
| a superedge. */ |
| |
| class superedge_event : public checker_event |
| { |
| public: |
| /* Mark this edge event as being either an interprocedural call or |
| return in which VAR is in STATE, and that this is critical to the |
| diagnostic (so that get_desc can attempt to get a better description |
| from any pending_diagnostic). */ |
| void record_critical_state (tree var, state_machine::state_t state) |
| { |
| m_var = var; |
| m_critical_state = state; |
| } |
| |
| const callgraph_superedge& get_callgraph_superedge () const; |
| |
| bool should_filter_p (int verbosity) const; |
| |
| protected: |
| superedge_event (enum event_kind kind, const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth); |
| |
| public: |
| const exploded_edge &m_eedge; |
| const superedge *m_sedge; |
| tree m_var; |
| state_machine::state_t m_critical_state; |
| }; |
| |
| /* An abstract event subclass for when a CFG edge is followed; it has two |
| subclasses, representing the start of the edge and the end of the |
| edge, which come in pairs. */ |
| |
| class cfg_edge_event : public superedge_event |
| { |
| public: |
| meaning get_meaning () const override; |
| |
| const cfg_superedge& get_cfg_superedge () const; |
| |
| protected: |
| cfg_edge_event (enum event_kind kind, const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth); |
| }; |
| |
| /* A concrete event subclass for the start of a CFG edge |
| e.g. "following 'false' branch...'. */ |
| |
| class start_cfg_edge_event : public cfg_edge_event |
| { |
| public: |
| start_cfg_edge_event (const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth) |
| : cfg_edge_event (EK_START_CFG_EDGE, eedge, loc, fndecl, depth) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| |
| private: |
| label_text maybe_describe_condition (bool can_colorize) const; |
| |
| static label_text maybe_describe_condition (bool can_colorize, |
| tree lhs, |
| enum tree_code op, |
| tree rhs); |
| static bool should_print_expr_p (tree); |
| }; |
| |
| /* A concrete event subclass for the end of a CFG edge |
| e.g. "...to here'. */ |
| |
| class end_cfg_edge_event : public cfg_edge_event |
| { |
| public: |
| end_cfg_edge_event (const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth) |
| : cfg_edge_event (EK_END_CFG_EDGE, eedge, loc, fndecl, depth) |
| { |
| } |
| |
| label_text get_desc (bool /*can_colorize*/) const final override |
| { |
| return label_text::borrow ("...to here"); |
| } |
| }; |
| |
| /* A concrete event subclass for an interprocedural call. */ |
| |
| class call_event : public superedge_event |
| { |
| public: |
| call_event (const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth); |
| |
| label_text get_desc (bool can_colorize) const override; |
| meaning get_meaning () const override; |
| |
| bool is_call_p () const final override; |
| |
| protected: |
| tree get_caller_fndecl () const; |
| tree get_callee_fndecl () const; |
| |
| const supernode *m_src_snode; |
| const supernode *m_dest_snode; |
| }; |
| |
| /* A concrete event subclass for an interprocedural return. */ |
| |
| class return_event : public superedge_event |
| { |
| public: |
| return_event (const exploded_edge &eedge, |
| location_t loc, tree fndecl, int depth); |
| |
| label_text get_desc (bool can_colorize) const final override; |
| meaning get_meaning () const override; |
| |
| bool is_return_p () const final override; |
| |
| const supernode *m_src_snode; |
| const supernode *m_dest_snode; |
| }; |
| |
| /* A concrete event subclass for the start of a consolidated run of CFG |
| edges all either TRUE or FALSE e.g. "following 'false' branch...'. */ |
| |
| class start_consolidated_cfg_edges_event : public checker_event |
| { |
| public: |
| start_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth, |
| bool edge_sense) |
| : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth), |
| m_edge_sense (edge_sense) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| meaning get_meaning () const override; |
| |
| private: |
| bool m_edge_sense; |
| }; |
| |
| /* A concrete event subclass for the end of a consolidated run of |
| CFG edges e.g. "...to here'. */ |
| |
| class end_consolidated_cfg_edges_event : public checker_event |
| { |
| public: |
| end_consolidated_cfg_edges_event (location_t loc, tree fndecl, int depth) |
| : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc, fndecl, depth) |
| { |
| } |
| |
| label_text get_desc (bool /*can_colorize*/) const final override |
| { |
| return label_text::borrow ("...to here"); |
| } |
| }; |
| |
| /* A concrete event subclass for describing an inlined call event |
| e.g. "inlined call to 'callee' from 'caller'". */ |
| |
| class inlined_call_event : public checker_event |
| { |
| public: |
| inlined_call_event (location_t loc, |
| tree apparent_callee_fndecl, |
| tree apparent_caller_fndecl, |
| int actual_depth, |
| int stack_depth_adjustment) |
| : checker_event (EK_INLINED_CALL, loc, |
| apparent_caller_fndecl, |
| actual_depth + stack_depth_adjustment), |
| m_apparent_callee_fndecl (apparent_callee_fndecl), |
| m_apparent_caller_fndecl (apparent_caller_fndecl) |
| { |
| gcc_assert (LOCATION_BLOCK (loc) == NULL); |
| } |
| |
| label_text get_desc (bool /*can_colorize*/) const final override; |
| meaning get_meaning () const override; |
| |
| private: |
| tree m_apparent_callee_fndecl; |
| tree m_apparent_caller_fndecl; |
| }; |
| |
| /* A concrete event subclass for a setjmp or sigsetjmp call. */ |
| |
| class setjmp_event : public checker_event |
| { |
| public: |
| setjmp_event (location_t loc, const exploded_node *enode, |
| tree fndecl, int depth, const gcall *setjmp_call) |
| : checker_event (EK_SETJMP, loc, fndecl, depth), |
| m_enode (enode), m_setjmp_call (setjmp_call) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| |
| void prepare_for_emission (checker_path *path, |
| pending_diagnostic *pd, |
| diagnostic_event_id_t emission_id) final override; |
| |
| private: |
| const exploded_node *m_enode; |
| const gcall *m_setjmp_call; |
| }; |
| |
| /* An abstract event subclass for rewinding from a longjmp to a setjmp |
| (or siglongjmp to sigsetjmp). |
| |
| Base class for two from/to subclasses, showing the two halves of the |
| rewind. */ |
| |
| class rewind_event : public checker_event |
| { |
| public: |
| tree get_longjmp_caller () const; |
| tree get_setjmp_caller () const; |
| const exploded_edge *get_eedge () const { return m_eedge; } |
| |
| protected: |
| rewind_event (const exploded_edge *eedge, |
| enum event_kind kind, |
| location_t loc, tree fndecl, int depth, |
| const rewind_info_t *rewind_info); |
| const rewind_info_t *m_rewind_info; |
| |
| private: |
| const exploded_edge *m_eedge; |
| }; |
| |
| /* A concrete event subclass for rewinding from a longjmp to a setjmp, |
| showing the longjmp (or siglongjmp). */ |
| |
| class rewind_from_longjmp_event : public rewind_event |
| { |
| public: |
| rewind_from_longjmp_event (const exploded_edge *eedge, |
| location_t loc, tree fndecl, int depth, |
| const rewind_info_t *rewind_info) |
| : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc, fndecl, depth, |
| rewind_info) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| }; |
| |
| /* A concrete event subclass for rewinding from a longjmp to a setjmp, |
| showing the setjmp (or sigsetjmp). */ |
| |
| class rewind_to_setjmp_event : public rewind_event |
| { |
| public: |
| rewind_to_setjmp_event (const exploded_edge *eedge, |
| location_t loc, tree fndecl, int depth, |
| const rewind_info_t *rewind_info) |
| : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc, fndecl, depth, |
| rewind_info) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| |
| void prepare_for_emission (checker_path *path, |
| pending_diagnostic *pd, |
| diagnostic_event_id_t emission_id) final override; |
| |
| private: |
| diagnostic_event_id_t m_original_setjmp_event_id; |
| }; |
| |
| /* Concrete subclass of checker_event for use at the end of a path: |
| a repeat of the warning message at the end of the path (perhaps with |
| references to pertinent events that occurred on the way), at the point |
| where the problem occurs. */ |
| |
| class warning_event : public checker_event |
| { |
| public: |
| warning_event (location_t loc, tree fndecl, int depth, |
| const state_machine *sm, |
| tree var, state_machine::state_t state) |
| : checker_event (EK_WARNING, loc, fndecl, depth), |
| m_sm (sm), m_var (var), m_state (state) |
| { |
| } |
| |
| label_text get_desc (bool can_colorize) const final override; |
| meaning get_meaning () const override; |
| |
| private: |
| const state_machine *m_sm; |
| tree m_var; |
| state_machine::state_t m_state; |
| }; |
| |
| } // namespace ana |
| |
| #endif /* GCC_ANALYZER_CHECKER_EVENT_H */ |