blob: 18c44e600c8702503842cd951ca1c77337ac3958 [file] [log] [blame]
/* 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 */