/* Subclasses of diagnostic_event for analyzer diagnostics. Copyright (C) 2019-2024 Free Software Foundation, Inc. Contributed by David Malcolm . 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 . */ #ifndef GCC_ANALYZER_CHECKER_EVENT_H #define GCC_ANALYZER_CHECKER_EVENT_H #include "tree-logical-location.h" #include "analyzer/program-state.h" namespace ana { /* A bundle of location information for a checker_event. */ struct event_loc_info { event_loc_info (location_t loc, tree fndecl, int depth) : m_loc (loc), m_fndecl (fndecl), m_depth (depth) {} location_t m_loc; tree m_fndecl; int m_depth; }; /* 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; diagnostic_thread_id_t get_thread_id () const final override { return 0; } void maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) 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, const event_loc_info &loc_info); 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 (const event_loc_info &loc_info, const char *desc) : checker_event (EK_DEBUG, loc_info), 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 (const event_loc_info &loc_info) : checker_event (EK_CUSTOM, loc_info) { } }; /* A concrete custom_event subclass with a precanned message. */ class precanned_custom_event : public custom_event { public: precanned_custom_event (const event_loc_info &loc_info, const char *desc) : custom_event (loc_info), 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; }; /* An abstract event subclass describing the creation of a region that is significant for a diagnostic. 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. The events are created by pending_diagnostic's add_region_creation_events vfunc, which by default creates a region_creation_event_memory_space, and if a capacity is known, a region_creation_event_capacity, giving e.g.: (1) region created on stack here (2) capacity: 100 bytes but this vfunc can be overridden to create other events if other wordings are more appropriate foa a given pending_diagnostic. */ class region_creation_event : public checker_event { protected: region_creation_event (const event_loc_info &loc_info); }; /* Concrete subclass of region_creation_event. Generates a message based on the memory space of the region e.g. "region created on stack here". */ class region_creation_event_memory_space : public region_creation_event { public: region_creation_event_memory_space (enum memory_space mem_space, const event_loc_info &loc_info) : region_creation_event (loc_info), m_mem_space (mem_space) { } label_text get_desc (bool can_colorize) const final override; private: enum memory_space m_mem_space; }; /* Concrete subclass of region_creation_event. Generates a message based on the capacity of the region e.g. "capacity: 100 bytes". */ class region_creation_event_capacity : public region_creation_event { public: region_creation_event_capacity (tree capacity, const event_loc_info &loc_info) : region_creation_event (loc_info), m_capacity (capacity) { gcc_assert (m_capacity); } label_text get_desc (bool can_colorize) const final override; private: tree m_capacity; }; /* Concrete subclass of region_creation_event. Generates a message based on the capacity of the region e.g. "allocated 100 bytes here". */ class region_creation_event_allocation_size : public region_creation_event { public: region_creation_event_allocation_size (tree capacity, const event_loc_info &loc_info) : region_creation_event (loc_info), m_capacity (capacity) {} label_text get_desc (bool can_colorize) const final override; private: tree m_capacity; }; /* Concrete subclass of region_creation_event. Generates a debug message intended for analyzer developers. */ class region_creation_event_debug : public region_creation_event { public: region_creation_event_debug (const region *reg, tree capacity, const event_loc_info &loc_info) : region_creation_event (loc_info), m_reg (reg), m_capacity (capacity) { } label_text get_desc (bool can_colorize) const final override; private: const region *m_reg; tree m_capacity; }; /* An event subclass describing the entry to a function. */ class function_entry_event : public checker_event { public: function_entry_event (const event_loc_info &loc_info) : checker_event (EK_FUNCTION_ENTRY, loc_info) { } 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, const exploded_node *enode); label_text get_desc (bool can_colorize) const final override; meaning get_meaning () const override; const function *get_dest_function () const { return m_dst_state.get_current_function (); } const exploded_node *get_exploded_node () const { return m_enode; } 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; const exploded_node *m_enode; }; /* Subclass of checker_event; parent class for subclasses that relate to a superedge. */ class superedge_event : public checker_event { public: void maybe_add_sarif_properties (sarif_object &thread_flow_loc_obj) const override; /* 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, const event_loc_info &loc_info); 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, const event_loc_info &loc_info); }; /* 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, const event_loc_info &loc_info) : cfg_edge_event (EK_START_CFG_EDGE, eedge, loc_info) { } label_text get_desc (bool can_colorize) const override; protected: label_text maybe_describe_condition (bool can_colorize) const; private: 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, const event_loc_info &loc_info) : cfg_edge_event (EK_END_CFG_EDGE, eedge, loc_info) { } 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, const event_loc_info &loc_info); 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, const event_loc_info &loc_info); 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 (const event_loc_info &loc_info, bool edge_sense) : checker_event (EK_START_CONSOLIDATED_CFG_EDGES, loc_info), 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 (const event_loc_info &loc_info) : checker_event (EK_END_CONSOLIDATED_CFG_EDGES, loc_info) { } 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, event_loc_info (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 (const event_loc_info &loc_info, const exploded_node *enode, const gcall *setjmp_call) : checker_event (EK_SETJMP, loc_info), 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, const event_loc_info &loc_info, 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, const event_loc_info &loc_info, const rewind_info_t *rewind_info) : rewind_event (eedge, EK_REWIND_FROM_LONGJMP, loc_info, 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, const event_loc_info &loc_info, const rewind_info_t *rewind_info) : rewind_event (eedge, EK_REWIND_TO_SETJMP, loc_info, 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 (const event_loc_info &loc_info, const exploded_node *enode, const state_machine *sm, tree var, state_machine::state_t state) : checker_event (EK_WARNING, loc_info), m_enode (enode), m_sm (sm), m_var (var), m_state (state) { } label_text get_desc (bool can_colorize) const final override; meaning get_meaning () const override; const exploded_node *get_exploded_node () const { return m_enode; } private: const exploded_node *m_enode; const state_machine *m_sm; tree m_var; state_machine::state_t m_state; }; } // namespace ana #endif /* GCC_ANALYZER_CHECKER_EVENT_H */