--- /dev/null
+/* 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/>. */
+
+#include "config.h"
+#define INCLUDE_MEMORY
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "diagnostic-core.h"
+#include "gimple-pretty-print.h"
+#include "fold-const.h"
+#include "diagnostic-path.h"
+#include "options.h"
+#include "cgraph.h"
+#include "cfg.h"
+#include "digraph.h"
+#include "diagnostic-event-id.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "analyzer/sm.h"
+#include "sbitmap.h"
+#include "bitmap.h"
+#include "ordered-hash-map.h"
+#include "analyzer/call-string.h"
+#include "analyzer/program-point.h"
+#include "analyzer/store.h"
+#include "analyzer/region-model.h"
+#include "analyzer/program-state.h"
+#include "analyzer/checker-path.h"
+#include "gimple-iterator.h"
+#include "inlining-iterator.h"
+#include "analyzer/supergraph.h"
+#include "analyzer/pending-diagnostic.h"
+#include "analyzer/diagnostic-manager.h"
+#include "analyzer/constraint-manager.h"
+#include "analyzer/checker-event.h"
+#include "analyzer/exploded-graph.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* Get a string for EK. */
+
+const char *
+event_kind_to_string (enum event_kind ek)
+{
+ switch (ek)
+ {
+ default:
+ gcc_unreachable ();
+ case EK_DEBUG:
+ return "EK_DEBUG";
+ case EK_CUSTOM:
+ return "EK_CUSTOM";
+ case EK_STMT:
+ return "EK_STMT";
+ case EK_REGION_CREATION:
+ return "EK_REGION_CREATION";
+ case EK_FUNCTION_ENTRY:
+ return "EK_FUNCTION_ENTRY";
+ case EK_STATE_CHANGE:
+ return "EK_STATE_CHANGE";
+ case EK_START_CFG_EDGE:
+ return "EK_START_CFG_EDGE";
+ case EK_END_CFG_EDGE:
+ return "EK_END_CFG_EDGE";
+ case EK_CALL_EDGE:
+ return "EK_CALL_EDGE";
+ case EK_RETURN_EDGE:
+ return "EK_RETURN_EDGE";
+ case EK_START_CONSOLIDATED_CFG_EDGES:
+ return "EK_START_CONSOLIDATED_CFG_EDGES";
+ case EK_END_CONSOLIDATED_CFG_EDGES:
+ return "EK_END_CONSOLIDATED_CFG_EDGES";
+ case EK_INLINED_CALL:
+ return "EK_INLINED_CALL";
+ case EK_SETJMP:
+ return "EK_SETJMP";
+ case EK_REWIND_FROM_LONGJMP:
+ return "EK_REWIND_FROM_LONGJMP";
+ case EK_REWIND_TO_SETJMP:
+ return "EK_REWIND_TO_SETJMP";
+ case EK_WARNING:
+ return "EK_WARNING";
+ }
+}
+
+/* A class for fixing up fndecls and stack depths in checker_event, based
+ on inlining records.
+
+ The early inliner runs before the analyzer, which can lead to confusing
+ output.
+
+ Tne base fndecl and depth within a checker_event are from call strings
+ in program_points, which reflect the call strings after inlining.
+ This class lets us offset the depth and fix up the reported fndecl and
+ stack depth to better reflect the user's original code. */
+
+class inlining_info
+{
+public:
+ inlining_info (location_t loc)
+ {
+ inlining_iterator iter (loc);
+ m_inner_fndecl = iter.get_fndecl ();
+ int num_frames = 0;
+ while (!iter.done_p ())
+ {
+ m_outer_fndecl = iter.get_fndecl ();
+ num_frames++;
+ iter.next ();
+ }
+ if (num_frames > 1)
+ m_extra_frames = num_frames - 1;
+ else
+ m_extra_frames = 0;
+ }
+
+ tree get_inner_fndecl () const { return m_inner_fndecl; }
+ int get_extra_frames () const { return m_extra_frames; }
+
+private:
+ tree m_outer_fndecl;
+ tree m_inner_fndecl;
+ int m_extra_frames;
+};
+
+/* class checker_event : public diagnostic_event. */
+
+/* checker_event's ctor. */
+
+checker_event::checker_event (enum event_kind kind,
+ location_t loc, tree fndecl, int depth)
+: m_kind (kind), m_loc (loc),
+ m_original_fndecl (fndecl), m_effective_fndecl (fndecl),
+ m_original_depth (depth), m_effective_depth (depth),
+ m_pending_diagnostic (NULL), m_emission_id (),
+ m_logical_loc (fndecl)
+{
+ /* Update effective fndecl and depth if inlining has been recorded. */
+ if (flag_analyzer_undo_inlining)
+ {
+ inlining_info info (loc);
+ if (info.get_inner_fndecl ())
+ {
+ m_effective_fndecl = info.get_inner_fndecl ();
+ m_effective_depth += info.get_extra_frames ();
+ m_logical_loc = tree_logical_location (m_effective_fndecl);
+ }
+ }
+}
+
+/* No-op implementation of diagnostic_event::get_meaning vfunc for
+ checker_event: checker events have no meaning by default. */
+
+diagnostic_event::meaning
+checker_event::get_meaning () const
+{
+ return meaning ();
+}
+
+/* Dump this event to PP (for debugging/logging purposes). */
+
+void
+checker_event::dump (pretty_printer *pp) const
+{
+ label_text event_desc (get_desc (false));
+ pp_printf (pp, "\"%s\" (depth %i",
+ event_desc.get (), m_effective_depth);
+
+ if (m_effective_depth != m_original_depth)
+ pp_printf (pp, " corrected from %i",
+ m_original_depth);
+ if (m_effective_fndecl)
+ {
+ pp_printf (pp, ", fndecl %qE", m_effective_fndecl);
+ if (m_effective_fndecl != m_original_fndecl)
+ pp_printf (pp, " corrected from %qE", m_original_fndecl);
+ }
+ pp_printf (pp, ", m_loc=%x)",
+ get_location ());
+}
+
+/* Dump this event to stderr (for debugging/logging purposes). */
+
+DEBUG_FUNCTION void
+checker_event::debug () const
+{
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_show_color (&pp) = pp_show_color (global_dc->printer);
+ pp.buffer->stream = stderr;
+ dump (&pp);
+ pp_newline (&pp);
+ pp_flush (&pp);
+}
+
+/* Hook for being notified when this event has its final id EMISSION_ID
+ and is about to emitted for PD.
+
+ Base implementation of checker_event::prepare_for_emission vfunc;
+ subclasses that override this should chain up to it.
+
+ Record PD and EMISSION_ID, and call the get_desc vfunc, so that any
+ side-effects of the call to get_desc take place before
+ pending_diagnostic::emit is called.
+
+ For example, state_change_event::get_desc can call
+ pending_diagnostic::describe_state_change; free_of_non_heap can use this
+ to tweak the message (TODO: would be neater to simply capture the
+ pertinent data within the sm-state). */
+
+void
+checker_event::prepare_for_emission (checker_path *,
+ pending_diagnostic *pd,
+ diagnostic_event_id_t emission_id)
+{
+ m_pending_diagnostic = pd;
+ m_emission_id = emission_id;
+
+ label_text desc = get_desc (false);
+}
+
+/* class debug_event : public checker_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ debug_event.
+ Use the saved string as the event's description. */
+
+label_text
+debug_event::get_desc (bool) const
+{
+ return label_text::borrow (m_desc);
+}
+
+/* class precanned_custom_event : public custom_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ precanned_custom_event.
+ Use the saved string as the event's description. */
+
+label_text
+precanned_custom_event::get_desc (bool) const
+{
+ return label_text::borrow (m_desc);
+}
+
+/* class statement_event : public checker_event. */
+
+/* statement_event's ctor. */
+
+statement_event::statement_event (const gimple *stmt, tree fndecl, int depth,
+ const program_state &dst_state)
+: checker_event (EK_STMT, gimple_location (stmt), fndecl, depth),
+ m_stmt (stmt),
+ m_dst_state (dst_state)
+{
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ statement_event.
+ Use the statement's dump form as the event's description. */
+
+label_text
+statement_event::get_desc (bool) const
+{
+ pretty_printer pp;
+ pp_string (&pp, "stmt: ");
+ pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0);
+ return label_text::take (xstrdup (pp_formatted_text (&pp)));
+}
+
+/* class region_creation_event : public checker_event. */
+
+region_creation_event::region_creation_event (const region *reg,
+ tree capacity,
+ enum rce_kind kind,
+ location_t loc,
+ tree fndecl,
+ int depth)
+: checker_event (EK_REGION_CREATION, loc, fndecl, depth),
+ m_reg (reg),
+ m_capacity (capacity),
+ m_rce_kind (kind)
+{
+ if (m_rce_kind == RCE_CAPACITY)
+ gcc_assert (capacity);
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ region_creation_event.
+ There are effectively 3 kinds of region_region_event, to
+ avoid combinatorial explosion by trying to convy the
+ information in a single message. */
+
+label_text
+region_creation_event::get_desc (bool can_colorize) const
+{
+ if (m_pending_diagnostic)
+ {
+ label_text custom_desc
+ = m_pending_diagnostic->describe_region_creation_event
+ (evdesc::region_creation (can_colorize, m_reg));
+ if (custom_desc.get ())
+ return custom_desc;
+ }
+
+ switch (m_rce_kind)
+ {
+ default:
+ gcc_unreachable ();
+
+ case RCE_MEM_SPACE:
+ switch (m_reg->get_memory_space ())
+ {
+ default:
+ return label_text::borrow ("region created here");
+ case MEMSPACE_STACK:
+ return label_text::borrow ("region created on stack here");
+ case MEMSPACE_HEAP:
+ return label_text::borrow ("region created on heap here");
+ }
+ break;
+
+ case RCE_CAPACITY:
+ gcc_assert (m_capacity);
+ if (TREE_CODE (m_capacity) == INTEGER_CST)
+ {
+ unsigned HOST_WIDE_INT hwi = tree_to_uhwi (m_capacity);
+ if (hwi == 1)
+ return make_label_text (can_colorize,
+ "capacity: %wu byte", hwi);
+ else
+ return make_label_text (can_colorize,
+ "capacity: %wu bytes", hwi);
+ }
+ else
+ return make_label_text (can_colorize,
+ "capacity: %qE bytes", m_capacity);
+
+ case RCE_DEBUG:
+ {
+ pretty_printer pp;
+ pp_format_decoder (&pp) = default_tree_printer;
+ pp_string (&pp, "region creation: ");
+ m_reg->dump_to_pp (&pp, true);
+ if (m_capacity)
+ pp_printf (&pp, " capacity: %qE", m_capacity);
+ return label_text::take (xstrdup (pp_formatted_text (&pp)));
+ }
+ break;
+ }
+}
+
+/* class function_entry_event : public checker_event. */
+
+function_entry_event::function_entry_event (const program_point &dst_point)
+: checker_event (EK_FUNCTION_ENTRY,
+ dst_point.get_supernode ()->get_start_location (),
+ dst_point.get_fndecl (),
+ dst_point.get_stack_depth ())
+{
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ function_entry_event.
+
+ Use a string such as "entry to 'foo'" as the event's description. */
+
+label_text
+function_entry_event::get_desc (bool can_colorize) const
+{
+ return make_label_text (can_colorize, "entry to %qE", m_effective_fndecl);
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function entry. */
+
+diagnostic_event::meaning
+function_entry_event::get_meaning () const
+{
+ return meaning (VERB_enter, NOUN_function);
+}
+
+/* class state_change_event : public checker_event. */
+
+/* state_change_event's ctor. */
+
+state_change_event::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)
+: checker_event (EK_STATE_CHANGE,
+ stmt->location, node->m_fun->decl,
+ stack_depth),
+ m_node (node), m_stmt (stmt), m_sm (sm),
+ m_sval (sval), m_from (from), m_to (to),
+ m_origin (origin),
+ m_dst_state (dst_state)
+{
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ state_change_event.
+
+ Attempt to generate a nicer human-readable description.
+ For greatest precision-of-wording, give the pending diagnostic
+ a chance to describe this state change (in terms of the
+ diagnostic).
+ Note that we only have a pending_diagnostic set on the event once
+ the diagnostic is about to being emitted, so the description for
+ an event can change. */
+
+label_text
+state_change_event::get_desc (bool can_colorize) const
+{
+ if (m_pending_diagnostic)
+ {
+ region_model *model = m_dst_state.m_region_model;
+ tree var = model->get_representative_tree (m_sval);
+ tree origin = model->get_representative_tree (m_origin);
+ label_text custom_desc
+ = m_pending_diagnostic->describe_state_change
+ (evdesc::state_change (can_colorize, var, origin,
+ m_from, m_to, m_emission_id, *this));
+ if (custom_desc.get ())
+ {
+ if (flag_analyzer_verbose_state_changes)
+ {
+ /* Get any "meaning" of event. */
+ diagnostic_event::meaning meaning = get_meaning ();
+ pretty_printer meaning_pp;
+ meaning.dump_to_pp (&meaning_pp);
+
+ /* Append debug version. */
+ if (m_origin)
+ return make_label_text
+ (can_colorize,
+ "%s (state of %qE: %qs -> %qs, origin: %qE, meaning: %s)",
+ custom_desc.get (),
+ var,
+ m_from->get_name (),
+ m_to->get_name (),
+ origin,
+ pp_formatted_text (&meaning_pp));
+ else
+ return make_label_text
+ (can_colorize,
+ "%s (state of %qE: %qs -> %qs, NULL origin, meaning: %s)",
+ custom_desc.get (),
+ var,
+ m_from->get_name (),
+ m_to->get_name (),
+ pp_formatted_text (&meaning_pp));
+ }
+ else
+ return custom_desc;
+ }
+ }
+
+ /* Fallback description. */
+ if (m_sval)
+ {
+ label_text sval_desc = m_sval->get_desc ();
+ if (m_origin)
+ {
+ label_text origin_desc = m_origin->get_desc ();
+ return make_label_text
+ (can_colorize,
+ "state of %qs: %qs -> %qs (origin: %qs)",
+ sval_desc.get (),
+ m_from->get_name (),
+ m_to->get_name (),
+ origin_desc.get ());
+ }
+ else
+ return make_label_text
+ (can_colorize,
+ "state of %qs: %qs -> %qs (NULL origin)",
+ sval_desc.get (),
+ m_from->get_name (),
+ m_to->get_name ());
+ }
+ else
+ {
+ gcc_assert (m_origin == NULL);
+ return make_label_text
+ (can_colorize,
+ "global state: %qs -> %qs",
+ m_from->get_name (),
+ m_to->get_name ());
+ }
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ state change events: delegate to the pending_diagnostic to
+ get any meaning. */
+
+diagnostic_event::meaning
+state_change_event::get_meaning () const
+{
+ if (m_pending_diagnostic)
+ {
+ region_model *model = m_dst_state.m_region_model;
+ tree var = model->get_representative_tree (m_sval);
+ tree origin = model->get_representative_tree (m_origin);
+ return m_pending_diagnostic->get_meaning_for_state_change
+ (evdesc::state_change (false, var, origin,
+ m_from, m_to, m_emission_id, *this));
+ }
+ else
+ return meaning ();
+}
+
+/* class superedge_event : public checker_event. */
+
+/* Get the callgraph_superedge for this superedge_event, which must be
+ for an interprocedural edge, rather than a CFG edge. */
+
+const callgraph_superedge&
+superedge_event::get_callgraph_superedge () const
+{
+ gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE);
+ return *m_sedge->dyn_cast_callgraph_superedge ();
+}
+
+/* Determine if this event should be filtered at the given verbosity
+ level. */
+
+bool
+superedge_event::should_filter_p (int verbosity) const
+{
+ switch (m_sedge->m_kind)
+ {
+ case SUPEREDGE_CFG_EDGE:
+ {
+ if (verbosity < 2)
+ return true;
+
+ if (verbosity < 4)
+ {
+ /* Filter events with empty descriptions. This ought to filter
+ FALLTHRU, but retain true/false/switch edges. */
+ label_text desc = get_desc (false);
+ gcc_assert (desc.get ());
+ if (desc.get ()[0] == '\0')
+ return true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* superedge_event's ctor. */
+
+superedge_event::superedge_event (enum event_kind kind,
+ const exploded_edge &eedge,
+ location_t loc, tree fndecl, int depth)
+: checker_event (kind, loc, fndecl, depth),
+ m_eedge (eedge), m_sedge (eedge.m_sedge),
+ m_var (NULL_TREE), m_critical_state (0)
+{
+}
+
+/* class cfg_edge_event : public superedge_event. */
+
+/* Get the cfg_superedge for this cfg_edge_event. */
+
+const cfg_superedge &
+cfg_edge_event::get_cfg_superedge () const
+{
+ return *m_sedge->dyn_cast_cfg_superedge ();
+}
+
+/* cfg_edge_event's ctor. */
+
+cfg_edge_event::cfg_edge_event (enum event_kind kind,
+ const exploded_edge &eedge,
+ location_t loc, tree fndecl, int depth)
+: superedge_event (kind, eedge, loc, fndecl, depth)
+{
+ gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ CFG edge events. */
+
+diagnostic_event::meaning
+cfg_edge_event::get_meaning () const
+{
+ const cfg_superedge& cfg_sedge = get_cfg_superedge ();
+ if (cfg_sedge.true_value_p ())
+ return meaning (VERB_branch, PROPERTY_true);
+ else if (cfg_sedge.false_value_p ())
+ return meaning (VERB_branch, PROPERTY_false);
+ else
+ return meaning ();
+}
+
+/* class start_cfg_edge_event : public cfg_edge_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ start_cfg_edge_event.
+
+ If -fanalyzer-verbose-edges, then generate low-level descriptions, such
+ as
+ "taking 'true' edge SN:7 -> SN:8".
+
+ Otherwise, generate strings using the label of the underlying CFG if
+ any, such as:
+ "following 'true' branch..." or
+ "following 'case 3' branch..."
+ "following 'default' branch..."
+
+ For conditionals, attempt to supply a description of the condition that
+ holds, such as:
+ "following 'false' branch (when 'ptr' is non-NULL)..."
+
+ Failing that, return an empty description (which will lead to this event
+ being filtered). */
+
+label_text
+start_cfg_edge_event::get_desc (bool can_colorize) const
+{
+ bool user_facing = !flag_analyzer_verbose_edges;
+ label_text edge_desc (m_sedge->get_description (user_facing));
+ if (user_facing)
+ {
+ if (edge_desc.get () && strlen (edge_desc.get ()) > 0)
+ {
+ label_text cond_desc = maybe_describe_condition (can_colorize);
+ label_text result;
+ if (cond_desc.get ())
+ return make_label_text (can_colorize,
+ "following %qs branch (%s)...",
+ edge_desc.get (), cond_desc.get ());
+ else
+ return make_label_text (can_colorize,
+ "following %qs branch...",
+ edge_desc.get ());
+ }
+ else
+ return label_text::borrow ("");
+ }
+ else
+ {
+ if (strlen (edge_desc.get ()) > 0)
+ return make_label_text (can_colorize,
+ "taking %qs edge SN:%i -> SN:%i",
+ edge_desc.get (),
+ m_sedge->m_src->m_index,
+ m_sedge->m_dest->m_index);
+ else
+ return make_label_text (can_colorize,
+ "taking edge SN:%i -> SN:%i",
+ m_sedge->m_src->m_index,
+ m_sedge->m_dest->m_index);
+ }
+}
+
+/* Attempt to generate a description of any condition that holds at this edge.
+
+ The intent is to make the user-facing messages more clear, especially for
+ cases where there's a single or double-negative, such as
+ when describing the false branch of an inverted condition.
+
+ For example, rather than printing just:
+
+ | if (!ptr)
+ | ~
+ | |
+ | (1) following 'false' branch...
+
+ it's clearer to spell out the condition that holds:
+
+ | if (!ptr)
+ | ~
+ | |
+ | (1) following 'false' branch (when 'ptr' is non-NULL)...
+ ^^^^^^^^^^^^^^^^^^^^^^
+
+ In the above example, this function would generate the highlighted
+ string: "when 'ptr' is non-NULL".
+
+ If the edge is not a condition, or it's not clear that a description of
+ the condition would be helpful to the user, return NULL. */
+
+label_text
+start_cfg_edge_event::maybe_describe_condition (bool can_colorize) const
+{
+ const cfg_superedge& cfg_sedge = get_cfg_superedge ();
+
+ if (cfg_sedge.true_value_p () || cfg_sedge.false_value_p ())
+ {
+ const gimple *last_stmt = m_sedge->m_src->get_last_stmt ();
+ if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
+ {
+ enum tree_code op = gimple_cond_code (cond_stmt);
+ tree lhs = gimple_cond_lhs (cond_stmt);
+ tree rhs = gimple_cond_rhs (cond_stmt);
+ if (cfg_sedge.false_value_p ())
+ op = invert_tree_comparison (op, false /* honor_nans */);
+ return maybe_describe_condition (can_colorize,
+ lhs, op, rhs);
+ }
+ }
+ return label_text::borrow (NULL);
+}
+
+/* Subroutine of maybe_describe_condition above.
+
+ Attempt to generate a user-facing description of the condition
+ LHS OP RHS, but only if it is likely to make it easier for the
+ user to understand a condition. */
+
+label_text
+start_cfg_edge_event::maybe_describe_condition (bool can_colorize,
+ tree lhs,
+ enum tree_code op,
+ tree rhs)
+{
+ /* In theory we could just build a tree via
+ fold_build2 (op, boolean_type_node, lhs, rhs)
+ and print it with %qE on it, but this leads to warts such as
+ parenthesizing vars, such as '(i) <= 9', and uses of '<unknown>'. */
+
+ /* Special-case: describe testing the result of strcmp, as figuring
+ out what the "true" or "false" path is can be confusing to the user. */
+ if (TREE_CODE (lhs) == SSA_NAME
+ && zerop (rhs))
+ {
+ if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs)))
+ if (is_special_named_call_p (call, "strcmp", 2))
+ {
+ if (op == EQ_EXPR)
+ return label_text::borrow ("when the strings are equal");
+ if (op == NE_EXPR)
+ return label_text::borrow ("when the strings are non-equal");
+ }
+ }
+
+ /* Only attempt to generate text for sufficiently simple expressions. */
+ if (!should_print_expr_p (lhs))
+ return label_text::borrow (NULL);
+ if (!should_print_expr_p (rhs))
+ return label_text::borrow (NULL);
+
+ /* Special cases for pointer comparisons against NULL. */
+ if (POINTER_TYPE_P (TREE_TYPE (lhs))
+ && POINTER_TYPE_P (TREE_TYPE (rhs))
+ && zerop (rhs))
+ {
+ if (op == EQ_EXPR)
+ return make_label_text (can_colorize, "when %qE is NULL",
+ lhs);
+ if (op == NE_EXPR)
+ return make_label_text (can_colorize, "when %qE is non-NULL",
+ lhs);
+ }
+
+ return make_label_text (can_colorize, "when %<%E %s %E%>",
+ lhs, op_symbol_code (op), rhs);
+}
+
+/* Subroutine of maybe_describe_condition.
+
+ Return true if EXPR is we will get suitable user-facing output
+ from %E on it. */
+
+bool
+start_cfg_edge_event::should_print_expr_p (tree expr)
+{
+ if (TREE_CODE (expr) == SSA_NAME)
+ {
+ if (SSA_NAME_VAR (expr))
+ return should_print_expr_p (SSA_NAME_VAR (expr));
+ else
+ return false;
+ }
+
+ if (DECL_P (expr))
+ return true;
+
+ if (CONSTANT_CLASS_P (expr))
+ return true;
+
+ return false;
+}
+
+/* class call_event : public superedge_event. */
+
+/* call_event's ctor. */
+
+call_event::call_event (const exploded_edge &eedge,
+ location_t loc, tree fndecl, int depth)
+: superedge_event (EK_CALL_EDGE, eedge, loc, fndecl, depth)
+{
+ if (eedge.m_sedge)
+ gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL);
+
+ m_src_snode = eedge.m_src->get_supernode ();
+ m_dest_snode = eedge.m_dest->get_supernode ();
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ call_event.
+
+ If this call event passes critical state for an sm-based warning,
+ allow the diagnostic to generate a precise description, such as:
+
+ "passing freed pointer 'ptr' in call to 'foo' from 'bar'"
+
+ Otherwise, generate a description of the form
+ "calling 'foo' from 'bar'". */
+
+label_text
+call_event::get_desc (bool can_colorize) const
+{
+ if (m_critical_state && m_pending_diagnostic)
+ {
+ gcc_assert (m_var);
+ tree var = fixup_tree_for_diagnostic (m_var);
+ label_text custom_desc
+ = m_pending_diagnostic->describe_call_with_state
+ (evdesc::call_with_state (can_colorize,
+ m_src_snode->m_fun->decl,
+ m_dest_snode->m_fun->decl,
+ var,
+ m_critical_state));
+ if (custom_desc.get ())
+ return custom_desc;
+ }
+
+ return make_label_text (can_colorize,
+ "calling %qE from %qE",
+ get_callee_fndecl (),
+ get_caller_fndecl ());
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function call events. */
+
+diagnostic_event::meaning
+call_event::get_meaning () const
+{
+ return meaning (VERB_call, NOUN_function);
+}
+
+/* Override of checker_event::is_call_p for calls. */
+
+bool
+call_event::is_call_p () const
+{
+ return true;
+}
+
+tree
+call_event::get_caller_fndecl () const
+{
+ return m_src_snode->m_fun->decl;
+}
+
+tree
+call_event::get_callee_fndecl () const
+{
+ return m_dest_snode->m_fun->decl;
+}
+
+/* class return_event : public superedge_event. */
+
+/* return_event's ctor. */
+
+return_event::return_event (const exploded_edge &eedge,
+ location_t loc, tree fndecl, int depth)
+: superedge_event (EK_RETURN_EDGE, eedge, loc, fndecl, depth)
+{
+ if (eedge.m_sedge)
+ gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN);
+
+ m_src_snode = eedge.m_src->get_supernode ();
+ m_dest_snode = eedge.m_dest->get_supernode ();
+}
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ return_event.
+
+ If this return event returns critical state for an sm-based warning,
+ allow the diagnostic to generate a precise description, such as:
+
+ "possible of NULL to 'foo' from 'bar'"
+
+ Otherwise, generate a description of the form
+ "returning to 'foo' from 'bar'. */
+
+label_text
+return_event::get_desc (bool can_colorize) const
+{
+ /* For greatest precision-of-wording, if this is returning the
+ state involved in the pending diagnostic, give the pending
+ diagnostic a chance to describe this return (in terms of
+ itself). */
+ if (m_critical_state && m_pending_diagnostic)
+ {
+ label_text custom_desc
+ = m_pending_diagnostic->describe_return_of_state
+ (evdesc::return_of_state (can_colorize,
+ m_dest_snode->m_fun->decl,
+ m_src_snode->m_fun->decl,
+ m_critical_state));
+ if (custom_desc.get ())
+ return custom_desc;
+ }
+ return make_label_text (can_colorize,
+ "returning to %qE from %qE",
+ m_dest_snode->m_fun->decl,
+ m_src_snode->m_fun->decl);
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ function return events. */
+
+diagnostic_event::meaning
+return_event::get_meaning () const
+{
+ return meaning (VERB_return, NOUN_function);
+}
+
+/* Override of checker_event::is_return_p for returns. */
+
+bool
+return_event::is_return_p () const
+{
+ return true;
+}
+
+/* class start_consolidated_cfg_edges_event : public checker_event. */
+
+label_text
+start_consolidated_cfg_edges_event::get_desc (bool can_colorize) const
+{
+ return make_label_text (can_colorize,
+ "following %qs branch...",
+ m_edge_sense ? "true" : "false");
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ start_consolidated_cfg_edges_event. */
+
+diagnostic_event::meaning
+start_consolidated_cfg_edges_event::get_meaning () const
+{
+ return meaning (VERB_branch,
+ (m_edge_sense ? PROPERTY_true : PROPERTY_false));
+}
+
+/* class inlined_call_event : public checker_event. */
+
+label_text
+inlined_call_event::get_desc (bool can_colorize) const
+{
+ return make_label_text (can_colorize,
+ "inlined call to %qE from %qE",
+ m_apparent_callee_fndecl,
+ m_apparent_caller_fndecl);
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ reconstructed inlined function calls. */
+
+diagnostic_event::meaning
+inlined_call_event::get_meaning () const
+{
+ return meaning (VERB_call, NOUN_function);
+}
+
+/* class setjmp_event : public checker_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ setjmp_event. */
+
+label_text
+setjmp_event::get_desc (bool can_colorize) const
+{
+ return make_label_text (can_colorize,
+ "%qs called here",
+ get_user_facing_name (m_setjmp_call));
+}
+
+/* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event.
+
+ Record this setjmp's event ID into the path, so that rewind events can
+ use it. */
+
+void
+setjmp_event::prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostic_event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ path->record_setjmp_event (m_enode, emission_id);
+}
+
+/* class rewind_event : public checker_event. */
+
+/* Get the fndecl containing the site of the longjmp call. */
+
+tree
+rewind_event::get_longjmp_caller () const
+{
+ return m_eedge->m_src->get_function ()->decl;
+}
+
+/* Get the fndecl containing the site of the setjmp call. */
+
+tree
+rewind_event::get_setjmp_caller () const
+{
+ return m_eedge->m_dest->get_function ()->decl;
+}
+
+/* rewind_event's ctor. */
+
+rewind_event::rewind_event (const exploded_edge *eedge,
+ enum event_kind kind,
+ location_t loc, tree fndecl, int depth,
+ const rewind_info_t *rewind_info)
+: checker_event (kind, loc, fndecl, depth),
+ m_rewind_info (rewind_info),
+ m_eedge (eedge)
+{
+ gcc_assert (m_eedge->m_custom_info.get () == m_rewind_info);
+}
+
+/* class rewind_from_longjmp_event : public rewind_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ rewind_from_longjmp_event. */
+
+label_text
+rewind_from_longjmp_event::get_desc (bool can_colorize) const
+{
+ const char *src_name
+ = get_user_facing_name (m_rewind_info->get_longjmp_call ());
+
+ if (get_longjmp_caller () == get_setjmp_caller ())
+ /* Special-case: purely intraprocedural rewind. */
+ return make_label_text (can_colorize,
+ "rewinding within %qE from %qs...",
+ get_longjmp_caller (),
+ src_name);
+ else
+ return make_label_text (can_colorize,
+ "rewinding from %qs in %qE...",
+ src_name,
+ get_longjmp_caller ());
+}
+
+/* class rewind_to_setjmp_event : public rewind_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ rewind_to_setjmp_event. */
+
+label_text
+rewind_to_setjmp_event::get_desc (bool can_colorize) const
+{
+ const char *dst_name
+ = get_user_facing_name (m_rewind_info->get_setjmp_call ());
+
+ /* If we can, identify the ID of the setjmp_event. */
+ if (m_original_setjmp_event_id.known_p ())
+ {
+ if (get_longjmp_caller () == get_setjmp_caller ())
+ /* Special-case: purely intraprocedural rewind. */
+ return make_label_text (can_colorize,
+ "...to %qs (saved at %@)",
+ dst_name,
+ &m_original_setjmp_event_id);
+ else
+ return make_label_text (can_colorize,
+ "...to %qs in %qE (saved at %@)",
+ dst_name,
+ get_setjmp_caller (),
+ &m_original_setjmp_event_id);
+ }
+ else
+ {
+ if (get_longjmp_caller () == get_setjmp_caller ())
+ /* Special-case: purely intraprocedural rewind. */
+ return make_label_text (can_colorize,
+ "...to %qs",
+ dst_name,
+ get_setjmp_caller ());
+ else
+ return make_label_text (can_colorize,
+ "...to %qs in %qE",
+ dst_name,
+ get_setjmp_caller ());
+ }
+}
+
+/* Implementation of checker_event::prepare_for_emission vfunc for
+ rewind_to_setjmp_event.
+
+ Attempt to look up the setjmp event ID that recorded the jmp_buf
+ for this rewind. */
+
+void
+rewind_to_setjmp_event::prepare_for_emission (checker_path *path,
+ pending_diagnostic *pd,
+ diagnostic_event_id_t emission_id)
+{
+ checker_event::prepare_for_emission (path, pd, emission_id);
+ path->get_setjmp_event (m_rewind_info->get_enode_origin (),
+ &m_original_setjmp_event_id);
+}
+
+/* class warning_event : public checker_event. */
+
+/* Implementation of diagnostic_event::get_desc vfunc for
+ warning_event.
+
+ If the pending diagnostic implements describe_final_event, use it,
+ generating a precise description e.g.
+ "second 'free' here; first 'free' was at (7)"
+
+ Otherwise generate a generic description. */
+
+label_text
+warning_event::get_desc (bool can_colorize) const
+{
+ if (m_pending_diagnostic)
+ {
+ tree var = fixup_tree_for_diagnostic (m_var);
+ label_text ev_desc
+ = m_pending_diagnostic->describe_final_event
+ (evdesc::final_event (can_colorize, var, m_state));
+ if (ev_desc.get ())
+ {
+ if (m_sm && flag_analyzer_verbose_state_changes)
+ {
+ if (var)
+ return make_label_text (can_colorize,
+ "%s (%qE is in state %qs)",
+ ev_desc.get (),
+ var, m_state->get_name ());
+ else
+ return make_label_text (can_colorize,
+ "%s (in global state %qs)",
+ ev_desc.get (),
+ m_state->get_name ());
+ }
+ else
+ return ev_desc;
+ }
+ }
+
+ if (m_sm)
+ {
+ if (m_var)
+ return make_label_text (can_colorize,
+ "here (%qE is in state %qs)",
+ m_var, m_state->get_name ());
+ else
+ return make_label_text (can_colorize,
+ "here (in global state %qs)",
+ m_state->get_name ());
+ }
+ else
+ return label_text::borrow ("here");
+}
+
+/* Implementation of diagnostic_event::get_meaning vfunc for
+ warning_event. */
+
+diagnostic_event::meaning
+warning_event::get_meaning () const
+{
+ return meaning (VERB_danger, NOUN_unknown);
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
-/* Subclasses of diagnostic_path and diagnostic_event for analyzer diagnostics.
+/* Subclass of diagnostic_path for analyzer diagnostics.
Copyright (C) 2019-2022 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>.
namespace ana {
-/* Get a string for EK. */
-
-const char *
-event_kind_to_string (enum event_kind ek)
-{
- switch (ek)
- {
- default:
- gcc_unreachable ();
- case EK_DEBUG:
- return "EK_DEBUG";
- case EK_CUSTOM:
- return "EK_CUSTOM";
- case EK_STMT:
- return "EK_STMT";
- case EK_REGION_CREATION:
- return "EK_REGION_CREATION";
- case EK_FUNCTION_ENTRY:
- return "EK_FUNCTION_ENTRY";
- case EK_STATE_CHANGE:
- return "EK_STATE_CHANGE";
- case EK_START_CFG_EDGE:
- return "EK_START_CFG_EDGE";
- case EK_END_CFG_EDGE:
- return "EK_END_CFG_EDGE";
- case EK_CALL_EDGE:
- return "EK_CALL_EDGE";
- case EK_RETURN_EDGE:
- return "EK_RETURN_EDGE";
- case EK_START_CONSOLIDATED_CFG_EDGES:
- return "EK_START_CONSOLIDATED_CFG_EDGES";
- case EK_END_CONSOLIDATED_CFG_EDGES:
- return "EK_END_CONSOLIDATED_CFG_EDGES";
- case EK_INLINED_CALL:
- return "EK_INLINED_CALL";
- case EK_SETJMP:
- return "EK_SETJMP";
- case EK_REWIND_FROM_LONGJMP:
- return "EK_REWIND_FROM_LONGJMP";
- case EK_REWIND_TO_SETJMP:
- return "EK_REWIND_TO_SETJMP";
- case EK_WARNING:
- return "EK_WARNING";
- }
-}
-
-/* A class for fixing up fndecls and stack depths in checker_event, based
- on inlining records.
-
- The early inliner runs before the analyzer, which can lead to confusing
- output.
-
- Tne base fndecl and depth within a checker_event are from call strings
- in program_points, which reflect the call strings after inlining.
- This class lets us offset the depth and fix up the reported fndecl and
- stack depth to better reflect the user's original code. */
-
-class inlining_info
-{
-public:
- inlining_info (location_t loc)
- {
- inlining_iterator iter (loc);
- m_inner_fndecl = iter.get_fndecl ();
- int num_frames = 0;
- while (!iter.done_p ())
- {
- m_outer_fndecl = iter.get_fndecl ();
- num_frames++;
- iter.next ();
- }
- if (num_frames > 1)
- m_extra_frames = num_frames - 1;
- else
- m_extra_frames = 0;
- }
-
- tree get_inner_fndecl () const { return m_inner_fndecl; }
- int get_extra_frames () const { return m_extra_frames; }
-
-private:
- tree m_outer_fndecl;
- tree m_inner_fndecl;
- int m_extra_frames;
-};
-
-/* class checker_event : public diagnostic_event. */
-
-/* checker_event's ctor. */
-
-checker_event::checker_event (enum event_kind kind,
- location_t loc, tree fndecl, int depth)
-: m_kind (kind), m_loc (loc),
- m_original_fndecl (fndecl), m_effective_fndecl (fndecl),
- m_original_depth (depth), m_effective_depth (depth),
- m_pending_diagnostic (NULL), m_emission_id (),
- m_logical_loc (fndecl)
-{
- /* Update effective fndecl and depth if inlining has been recorded. */
- if (flag_analyzer_undo_inlining)
- {
- inlining_info info (loc);
- if (info.get_inner_fndecl ())
- {
- m_effective_fndecl = info.get_inner_fndecl ();
- m_effective_depth += info.get_extra_frames ();
- m_logical_loc = tree_logical_location (m_effective_fndecl);
- }
- }
-}
-
-/* No-op implementation of diagnostic_event::get_meaning vfunc for
- checker_event: checker events have no meaning by default. */
-
-diagnostic_event::meaning
-checker_event::get_meaning () const
-{
- return meaning ();
-}
-
-/* Dump this event to PP (for debugging/logging purposes). */
-
-void
-checker_event::dump (pretty_printer *pp) const
-{
- label_text event_desc (get_desc (false));
- pp_printf (pp, "\"%s\" (depth %i",
- event_desc.get (), m_effective_depth);
-
- if (m_effective_depth != m_original_depth)
- pp_printf (pp, " corrected from %i",
- m_original_depth);
- if (m_effective_fndecl)
- {
- pp_printf (pp, ", fndecl %qE", m_effective_fndecl);
- if (m_effective_fndecl != m_original_fndecl)
- pp_printf (pp, " corrected from %qE", m_original_fndecl);
- }
- pp_printf (pp, ", m_loc=%x)",
- get_location ());
-}
-
-/* Dump this event to stderr (for debugging/logging purposes). */
-
-DEBUG_FUNCTION void
-checker_event::debug () const
-{
- pretty_printer pp;
- pp_format_decoder (&pp) = default_tree_printer;
- pp_show_color (&pp) = pp_show_color (global_dc->printer);
- pp.buffer->stream = stderr;
- dump (&pp);
- pp_newline (&pp);
- pp_flush (&pp);
-}
-
-/* Hook for being notified when this event has its final id EMISSION_ID
- and is about to emitted for PD.
-
- Base implementation of checker_event::prepare_for_emission vfunc;
- subclasses that override this should chain up to it.
-
- Record PD and EMISSION_ID, and call the get_desc vfunc, so that any
- side-effects of the call to get_desc take place before
- pending_diagnostic::emit is called.
-
- For example, state_change_event::get_desc can call
- pending_diagnostic::describe_state_change; free_of_non_heap can use this
- to tweak the message (TODO: would be neater to simply capture the
- pertinent data within the sm-state). */
-
-void
-checker_event::prepare_for_emission (checker_path *,
- pending_diagnostic *pd,
- diagnostic_event_id_t emission_id)
-{
- m_pending_diagnostic = pd;
- m_emission_id = emission_id;
-
- label_text desc = get_desc (false);
-}
-
-/* class debug_event : public checker_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- debug_event.
- Use the saved string as the event's description. */
-
-label_text
-debug_event::get_desc (bool) const
-{
- return label_text::borrow (m_desc);
-}
-
-/* class precanned_custom_event : public custom_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- precanned_custom_event.
- Use the saved string as the event's description. */
-
-label_text
-precanned_custom_event::get_desc (bool) const
-{
- return label_text::borrow (m_desc);
-}
-
-/* class statement_event : public checker_event. */
-
-/* statement_event's ctor. */
-
-statement_event::statement_event (const gimple *stmt, tree fndecl, int depth,
- const program_state &dst_state)
-: checker_event (EK_STMT, gimple_location (stmt), fndecl, depth),
- m_stmt (stmt),
- m_dst_state (dst_state)
-{
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- statement_event.
- Use the statement's dump form as the event's description. */
-
-label_text
-statement_event::get_desc (bool) const
-{
- pretty_printer pp;
- pp_string (&pp, "stmt: ");
- pp_gimple_stmt_1 (&pp, m_stmt, 0, (dump_flags_t)0);
- return label_text::take (xstrdup (pp_formatted_text (&pp)));
-}
-
-/* class region_creation_event : public checker_event. */
-
-region_creation_event::region_creation_event (const region *reg,
- tree capacity,
- enum rce_kind kind,
- location_t loc,
- tree fndecl,
- int depth)
-: checker_event (EK_REGION_CREATION, loc, fndecl, depth),
- m_reg (reg),
- m_capacity (capacity),
- m_rce_kind (kind)
-{
- if (m_rce_kind == RCE_CAPACITY)
- gcc_assert (capacity);
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- region_creation_event.
- There are effectively 3 kinds of region_region_event, to
- avoid combinatorial explosion by trying to convy the
- information in a single message. */
-
-label_text
-region_creation_event::get_desc (bool can_colorize) const
-{
- if (m_pending_diagnostic)
- {
- label_text custom_desc
- = m_pending_diagnostic->describe_region_creation_event
- (evdesc::region_creation (can_colorize, m_reg));
- if (custom_desc.get ())
- return custom_desc;
- }
-
- switch (m_rce_kind)
- {
- default:
- gcc_unreachable ();
-
- case RCE_MEM_SPACE:
- switch (m_reg->get_memory_space ())
- {
- default:
- return label_text::borrow ("region created here");
- case MEMSPACE_STACK:
- return label_text::borrow ("region created on stack here");
- case MEMSPACE_HEAP:
- return label_text::borrow ("region created on heap here");
- }
- break;
-
- case RCE_CAPACITY:
- gcc_assert (m_capacity);
- if (TREE_CODE (m_capacity) == INTEGER_CST)
- {
- unsigned HOST_WIDE_INT hwi = tree_to_uhwi (m_capacity);
- if (hwi == 1)
- return make_label_text (can_colorize,
- "capacity: %wu byte", hwi);
- else
- return make_label_text (can_colorize,
- "capacity: %wu bytes", hwi);
- }
- else
- return make_label_text (can_colorize,
- "capacity: %qE bytes", m_capacity);
-
- case RCE_DEBUG:
- {
- pretty_printer pp;
- pp_format_decoder (&pp) = default_tree_printer;
- pp_string (&pp, "region creation: ");
- m_reg->dump_to_pp (&pp, true);
- if (m_capacity)
- pp_printf (&pp, " capacity: %qE", m_capacity);
- return label_text::take (xstrdup (pp_formatted_text (&pp)));
- }
- break;
- }
-}
-
-/* class function_entry_event : public checker_event. */
-
-function_entry_event::function_entry_event (const program_point &dst_point)
-: checker_event (EK_FUNCTION_ENTRY,
- dst_point.get_supernode ()->get_start_location (),
- dst_point.get_fndecl (),
- dst_point.get_stack_depth ())
-{
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- function_entry_event.
-
- Use a string such as "entry to 'foo'" as the event's description. */
-
-label_text
-function_entry_event::get_desc (bool can_colorize) const
-{
- return make_label_text (can_colorize, "entry to %qE", m_effective_fndecl);
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- function entry. */
-
-diagnostic_event::meaning
-function_entry_event::get_meaning () const
-{
- return meaning (VERB_enter, NOUN_function);
-}
-
-/* class state_change_event : public checker_event. */
-
-/* state_change_event's ctor. */
-
-state_change_event::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)
-: checker_event (EK_STATE_CHANGE,
- stmt->location, node->m_fun->decl,
- stack_depth),
- m_node (node), m_stmt (stmt), m_sm (sm),
- m_sval (sval), m_from (from), m_to (to),
- m_origin (origin),
- m_dst_state (dst_state)
-{
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- state_change_event.
-
- Attempt to generate a nicer human-readable description.
- For greatest precision-of-wording, give the pending diagnostic
- a chance to describe this state change (in terms of the
- diagnostic).
- Note that we only have a pending_diagnostic set on the event once
- the diagnostic is about to being emitted, so the description for
- an event can change. */
-
-label_text
-state_change_event::get_desc (bool can_colorize) const
-{
- if (m_pending_diagnostic)
- {
- region_model *model = m_dst_state.m_region_model;
- tree var = model->get_representative_tree (m_sval);
- tree origin = model->get_representative_tree (m_origin);
- label_text custom_desc
- = m_pending_diagnostic->describe_state_change
- (evdesc::state_change (can_colorize, var, origin,
- m_from, m_to, m_emission_id, *this));
- if (custom_desc.get ())
- {
- if (flag_analyzer_verbose_state_changes)
- {
- /* Get any "meaning" of event. */
- diagnostic_event::meaning meaning = get_meaning ();
- pretty_printer meaning_pp;
- meaning.dump_to_pp (&meaning_pp);
-
- /* Append debug version. */
- if (m_origin)
- return make_label_text
- (can_colorize,
- "%s (state of %qE: %qs -> %qs, origin: %qE, meaning: %s)",
- custom_desc.get (),
- var,
- m_from->get_name (),
- m_to->get_name (),
- origin,
- pp_formatted_text (&meaning_pp));
- else
- return make_label_text
- (can_colorize,
- "%s (state of %qE: %qs -> %qs, NULL origin, meaning: %s)",
- custom_desc.get (),
- var,
- m_from->get_name (),
- m_to->get_name (),
- pp_formatted_text (&meaning_pp));
- }
- else
- return custom_desc;
- }
- }
-
- /* Fallback description. */
- if (m_sval)
- {
- label_text sval_desc = m_sval->get_desc ();
- if (m_origin)
- {
- label_text origin_desc = m_origin->get_desc ();
- return make_label_text
- (can_colorize,
- "state of %qs: %qs -> %qs (origin: %qs)",
- sval_desc.get (),
- m_from->get_name (),
- m_to->get_name (),
- origin_desc.get ());
- }
- else
- return make_label_text
- (can_colorize,
- "state of %qs: %qs -> %qs (NULL origin)",
- sval_desc.get (),
- m_from->get_name (),
- m_to->get_name ());
- }
- else
- {
- gcc_assert (m_origin == NULL);
- return make_label_text
- (can_colorize,
- "global state: %qs -> %qs",
- m_from->get_name (),
- m_to->get_name ());
- }
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- state change events: delegate to the pending_diagnostic to
- get any meaning. */
-
-diagnostic_event::meaning
-state_change_event::get_meaning () const
-{
- if (m_pending_diagnostic)
- {
- region_model *model = m_dst_state.m_region_model;
- tree var = model->get_representative_tree (m_sval);
- tree origin = model->get_representative_tree (m_origin);
- return m_pending_diagnostic->get_meaning_for_state_change
- (evdesc::state_change (false, var, origin,
- m_from, m_to, m_emission_id, *this));
- }
- else
- return meaning ();
-}
-
-/* class superedge_event : public checker_event. */
-
-/* Get the callgraph_superedge for this superedge_event, which must be
- for an interprocedural edge, rather than a CFG edge. */
-
-const callgraph_superedge&
-superedge_event::get_callgraph_superedge () const
-{
- gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE);
- return *m_sedge->dyn_cast_callgraph_superedge ();
-}
-
-/* Determine if this event should be filtered at the given verbosity
- level. */
-
-bool
-superedge_event::should_filter_p (int verbosity) const
-{
- switch (m_sedge->m_kind)
- {
- case SUPEREDGE_CFG_EDGE:
- {
- if (verbosity < 2)
- return true;
-
- if (verbosity < 4)
- {
- /* Filter events with empty descriptions. This ought to filter
- FALLTHRU, but retain true/false/switch edges. */
- label_text desc = get_desc (false);
- gcc_assert (desc.get ());
- if (desc.get ()[0] == '\0')
- return true;
- }
- }
- break;
-
- default:
- break;
- }
- return false;
-}
-
-/* superedge_event's ctor. */
-
-superedge_event::superedge_event (enum event_kind kind,
- const exploded_edge &eedge,
- location_t loc, tree fndecl, int depth)
-: checker_event (kind, loc, fndecl, depth),
- m_eedge (eedge), m_sedge (eedge.m_sedge),
- m_var (NULL_TREE), m_critical_state (0)
-{
-}
-
-/* class cfg_edge_event : public superedge_event. */
-
-/* Get the cfg_superedge for this cfg_edge_event. */
-
-const cfg_superedge &
-cfg_edge_event::get_cfg_superedge () const
-{
- return *m_sedge->dyn_cast_cfg_superedge ();
-}
-
-/* cfg_edge_event's ctor. */
-
-cfg_edge_event::cfg_edge_event (enum event_kind kind,
- const exploded_edge &eedge,
- location_t loc, tree fndecl, int depth)
-: superedge_event (kind, eedge, loc, fndecl, depth)
-{
- gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- CFG edge events. */
-
-diagnostic_event::meaning
-cfg_edge_event::get_meaning () const
-{
- const cfg_superedge& cfg_sedge = get_cfg_superedge ();
- if (cfg_sedge.true_value_p ())
- return meaning (VERB_branch, PROPERTY_true);
- else if (cfg_sedge.false_value_p ())
- return meaning (VERB_branch, PROPERTY_false);
- else
- return meaning ();
-}
-
-/* class start_cfg_edge_event : public cfg_edge_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- start_cfg_edge_event.
-
- If -fanalyzer-verbose-edges, then generate low-level descriptions, such
- as
- "taking 'true' edge SN:7 -> SN:8".
-
- Otherwise, generate strings using the label of the underlying CFG if
- any, such as:
- "following 'true' branch..." or
- "following 'case 3' branch..."
- "following 'default' branch..."
-
- For conditionals, attempt to supply a description of the condition that
- holds, such as:
- "following 'false' branch (when 'ptr' is non-NULL)..."
-
- Failing that, return an empty description (which will lead to this event
- being filtered). */
-
-label_text
-start_cfg_edge_event::get_desc (bool can_colorize) const
-{
- bool user_facing = !flag_analyzer_verbose_edges;
- label_text edge_desc (m_sedge->get_description (user_facing));
- if (user_facing)
- {
- if (edge_desc.get () && strlen (edge_desc.get ()) > 0)
- {
- label_text cond_desc = maybe_describe_condition (can_colorize);
- label_text result;
- if (cond_desc.get ())
- return make_label_text (can_colorize,
- "following %qs branch (%s)...",
- edge_desc.get (), cond_desc.get ());
- else
- return make_label_text (can_colorize,
- "following %qs branch...",
- edge_desc.get ());
- }
- else
- return label_text::borrow ("");
- }
- else
- {
- if (strlen (edge_desc.get ()) > 0)
- return make_label_text (can_colorize,
- "taking %qs edge SN:%i -> SN:%i",
- edge_desc.get (),
- m_sedge->m_src->m_index,
- m_sedge->m_dest->m_index);
- else
- return make_label_text (can_colorize,
- "taking edge SN:%i -> SN:%i",
- m_sedge->m_src->m_index,
- m_sedge->m_dest->m_index);
- }
-}
-
-/* Attempt to generate a description of any condition that holds at this edge.
-
- The intent is to make the user-facing messages more clear, especially for
- cases where there's a single or double-negative, such as
- when describing the false branch of an inverted condition.
-
- For example, rather than printing just:
-
- | if (!ptr)
- | ~
- | |
- | (1) following 'false' branch...
-
- it's clearer to spell out the condition that holds:
-
- | if (!ptr)
- | ~
- | |
- | (1) following 'false' branch (when 'ptr' is non-NULL)...
- ^^^^^^^^^^^^^^^^^^^^^^
-
- In the above example, this function would generate the highlighted
- string: "when 'ptr' is non-NULL".
-
- If the edge is not a condition, or it's not clear that a description of
- the condition would be helpful to the user, return NULL. */
-
-label_text
-start_cfg_edge_event::maybe_describe_condition (bool can_colorize) const
-{
- const cfg_superedge& cfg_sedge = get_cfg_superedge ();
-
- if (cfg_sedge.true_value_p () || cfg_sedge.false_value_p ())
- {
- const gimple *last_stmt = m_sedge->m_src->get_last_stmt ();
- if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
- {
- enum tree_code op = gimple_cond_code (cond_stmt);
- tree lhs = gimple_cond_lhs (cond_stmt);
- tree rhs = gimple_cond_rhs (cond_stmt);
- if (cfg_sedge.false_value_p ())
- op = invert_tree_comparison (op, false /* honor_nans */);
- return maybe_describe_condition (can_colorize,
- lhs, op, rhs);
- }
- }
- return label_text::borrow (NULL);
-}
-
-/* Subroutine of maybe_describe_condition above.
-
- Attempt to generate a user-facing description of the condition
- LHS OP RHS, but only if it is likely to make it easier for the
- user to understand a condition. */
-
-label_text
-start_cfg_edge_event::maybe_describe_condition (bool can_colorize,
- tree lhs,
- enum tree_code op,
- tree rhs)
-{
- /* In theory we could just build a tree via
- fold_build2 (op, boolean_type_node, lhs, rhs)
- and print it with %qE on it, but this leads to warts such as
- parenthesizing vars, such as '(i) <= 9', and uses of '<unknown>'. */
-
- /* Special-case: describe testing the result of strcmp, as figuring
- out what the "true" or "false" path is can be confusing to the user. */
- if (TREE_CODE (lhs) == SSA_NAME
- && zerop (rhs))
- {
- if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs)))
- if (is_special_named_call_p (call, "strcmp", 2))
- {
- if (op == EQ_EXPR)
- return label_text::borrow ("when the strings are equal");
- if (op == NE_EXPR)
- return label_text::borrow ("when the strings are non-equal");
- }
- }
-
- /* Only attempt to generate text for sufficiently simple expressions. */
- if (!should_print_expr_p (lhs))
- return label_text::borrow (NULL);
- if (!should_print_expr_p (rhs))
- return label_text::borrow (NULL);
-
- /* Special cases for pointer comparisons against NULL. */
- if (POINTER_TYPE_P (TREE_TYPE (lhs))
- && POINTER_TYPE_P (TREE_TYPE (rhs))
- && zerop (rhs))
- {
- if (op == EQ_EXPR)
- return make_label_text (can_colorize, "when %qE is NULL",
- lhs);
- if (op == NE_EXPR)
- return make_label_text (can_colorize, "when %qE is non-NULL",
- lhs);
- }
-
- return make_label_text (can_colorize, "when %<%E %s %E%>",
- lhs, op_symbol_code (op), rhs);
-}
-
-/* Subroutine of maybe_describe_condition.
-
- Return true if EXPR is we will get suitable user-facing output
- from %E on it. */
-
-bool
-start_cfg_edge_event::should_print_expr_p (tree expr)
-{
- if (TREE_CODE (expr) == SSA_NAME)
- {
- if (SSA_NAME_VAR (expr))
- return should_print_expr_p (SSA_NAME_VAR (expr));
- else
- return false;
- }
-
- if (DECL_P (expr))
- return true;
-
- if (CONSTANT_CLASS_P (expr))
- return true;
-
- return false;
-}
-
-/* class call_event : public superedge_event. */
-
-/* call_event's ctor. */
-
-call_event::call_event (const exploded_edge &eedge,
- location_t loc, tree fndecl, int depth)
-: superedge_event (EK_CALL_EDGE, eedge, loc, fndecl, depth)
-{
- if (eedge.m_sedge)
- gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL);
-
- m_src_snode = eedge.m_src->get_supernode ();
- m_dest_snode = eedge.m_dest->get_supernode ();
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- call_event.
-
- If this call event passes critical state for an sm-based warning,
- allow the diagnostic to generate a precise description, such as:
-
- "passing freed pointer 'ptr' in call to 'foo' from 'bar'"
-
- Otherwise, generate a description of the form
- "calling 'foo' from 'bar'". */
-
-label_text
-call_event::get_desc (bool can_colorize) const
-{
- if (m_critical_state && m_pending_diagnostic)
- {
- gcc_assert (m_var);
- tree var = fixup_tree_for_diagnostic (m_var);
- label_text custom_desc
- = m_pending_diagnostic->describe_call_with_state
- (evdesc::call_with_state (can_colorize,
- m_src_snode->m_fun->decl,
- m_dest_snode->m_fun->decl,
- var,
- m_critical_state));
- if (custom_desc.get ())
- return custom_desc;
- }
-
- return make_label_text (can_colorize,
- "calling %qE from %qE",
- get_callee_fndecl (),
- get_caller_fndecl ());
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- function call events. */
-
-diagnostic_event::meaning
-call_event::get_meaning () const
-{
- return meaning (VERB_call, NOUN_function);
-}
-
-/* Override of checker_event::is_call_p for calls. */
-
-bool
-call_event::is_call_p () const
-{
- return true;
-}
-
-tree
-call_event::get_caller_fndecl () const
-{
- return m_src_snode->m_fun->decl;
-}
-
-tree
-call_event::get_callee_fndecl () const
-{
- return m_dest_snode->m_fun->decl;
-}
-
-/* class return_event : public superedge_event. */
-
-/* return_event's ctor. */
-
-return_event::return_event (const exploded_edge &eedge,
- location_t loc, tree fndecl, int depth)
-: superedge_event (EK_RETURN_EDGE, eedge, loc, fndecl, depth)
-{
- if (eedge.m_sedge)
- gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN);
-
- m_src_snode = eedge.m_src->get_supernode ();
- m_dest_snode = eedge.m_dest->get_supernode ();
-}
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- return_event.
-
- If this return event returns critical state for an sm-based warning,
- allow the diagnostic to generate a precise description, such as:
-
- "possible of NULL to 'foo' from 'bar'"
-
- Otherwise, generate a description of the form
- "returning to 'foo' from 'bar'. */
-
-label_text
-return_event::get_desc (bool can_colorize) const
-{
- /* For greatest precision-of-wording, if this is returning the
- state involved in the pending diagnostic, give the pending
- diagnostic a chance to describe this return (in terms of
- itself). */
- if (m_critical_state && m_pending_diagnostic)
- {
- label_text custom_desc
- = m_pending_diagnostic->describe_return_of_state
- (evdesc::return_of_state (can_colorize,
- m_dest_snode->m_fun->decl,
- m_src_snode->m_fun->decl,
- m_critical_state));
- if (custom_desc.get ())
- return custom_desc;
- }
- return make_label_text (can_colorize,
- "returning to %qE from %qE",
- m_dest_snode->m_fun->decl,
- m_src_snode->m_fun->decl);
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- function return events. */
-
-diagnostic_event::meaning
-return_event::get_meaning () const
-{
- return meaning (VERB_return, NOUN_function);
-}
-
-/* Override of checker_event::is_return_p for returns. */
-
-bool
-return_event::is_return_p () const
-{
- return true;
-}
-
-/* class start_consolidated_cfg_edges_event : public checker_event. */
-
-label_text
-start_consolidated_cfg_edges_event::get_desc (bool can_colorize) const
-{
- return make_label_text (can_colorize,
- "following %qs branch...",
- m_edge_sense ? "true" : "false");
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- start_consolidated_cfg_edges_event. */
-
-diagnostic_event::meaning
-start_consolidated_cfg_edges_event::get_meaning () const
-{
- return meaning (VERB_branch,
- (m_edge_sense ? PROPERTY_true : PROPERTY_false));
-}
-
-/* class inlined_call_event : public checker_event. */
-
-label_text
-inlined_call_event::get_desc (bool can_colorize) const
-{
- return make_label_text (can_colorize,
- "inlined call to %qE from %qE",
- m_apparent_callee_fndecl,
- m_apparent_caller_fndecl);
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- reconstructed inlined function calls. */
-
-diagnostic_event::meaning
-inlined_call_event::get_meaning () const
-{
- return meaning (VERB_call, NOUN_function);
-}
-
-/* class setjmp_event : public checker_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- setjmp_event. */
-
-label_text
-setjmp_event::get_desc (bool can_colorize) const
-{
- return make_label_text (can_colorize,
- "%qs called here",
- get_user_facing_name (m_setjmp_call));
-}
-
-/* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event.
-
- Record this setjmp's event ID into the path, so that rewind events can
- use it. */
-
-void
-setjmp_event::prepare_for_emission (checker_path *path,
- pending_diagnostic *pd,
- diagnostic_event_id_t emission_id)
-{
- checker_event::prepare_for_emission (path, pd, emission_id);
- path->record_setjmp_event (m_enode, emission_id);
-}
-
-/* class rewind_event : public checker_event. */
-
-/* Get the fndecl containing the site of the longjmp call. */
-
-tree
-rewind_event::get_longjmp_caller () const
-{
- return m_eedge->m_src->get_function ()->decl;
-}
-
-/* Get the fndecl containing the site of the setjmp call. */
-
-tree
-rewind_event::get_setjmp_caller () const
-{
- return m_eedge->m_dest->get_function ()->decl;
-}
-
-/* rewind_event's ctor. */
-
-rewind_event::rewind_event (const exploded_edge *eedge,
- enum event_kind kind,
- location_t loc, tree fndecl, int depth,
- const rewind_info_t *rewind_info)
-: checker_event (kind, loc, fndecl, depth),
- m_rewind_info (rewind_info),
- m_eedge (eedge)
-{
- gcc_assert (m_eedge->m_custom_info.get () == m_rewind_info);
-}
-
-/* class rewind_from_longjmp_event : public rewind_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- rewind_from_longjmp_event. */
-
-label_text
-rewind_from_longjmp_event::get_desc (bool can_colorize) const
-{
- const char *src_name
- = get_user_facing_name (m_rewind_info->get_longjmp_call ());
-
- if (get_longjmp_caller () == get_setjmp_caller ())
- /* Special-case: purely intraprocedural rewind. */
- return make_label_text (can_colorize,
- "rewinding within %qE from %qs...",
- get_longjmp_caller (),
- src_name);
- else
- return make_label_text (can_colorize,
- "rewinding from %qs in %qE...",
- src_name,
- get_longjmp_caller ());
-}
-
-/* class rewind_to_setjmp_event : public rewind_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- rewind_to_setjmp_event. */
-
-label_text
-rewind_to_setjmp_event::get_desc (bool can_colorize) const
-{
- const char *dst_name
- = get_user_facing_name (m_rewind_info->get_setjmp_call ());
-
- /* If we can, identify the ID of the setjmp_event. */
- if (m_original_setjmp_event_id.known_p ())
- {
- if (get_longjmp_caller () == get_setjmp_caller ())
- /* Special-case: purely intraprocedural rewind. */
- return make_label_text (can_colorize,
- "...to %qs (saved at %@)",
- dst_name,
- &m_original_setjmp_event_id);
- else
- return make_label_text (can_colorize,
- "...to %qs in %qE (saved at %@)",
- dst_name,
- get_setjmp_caller (),
- &m_original_setjmp_event_id);
- }
- else
- {
- if (get_longjmp_caller () == get_setjmp_caller ())
- /* Special-case: purely intraprocedural rewind. */
- return make_label_text (can_colorize,
- "...to %qs",
- dst_name,
- get_setjmp_caller ());
- else
- return make_label_text (can_colorize,
- "...to %qs in %qE",
- dst_name,
- get_setjmp_caller ());
- }
-}
-
-/* Implementation of checker_event::prepare_for_emission vfunc for
- rewind_to_setjmp_event.
-
- Attempt to look up the setjmp event ID that recorded the jmp_buf
- for this rewind. */
-
-void
-rewind_to_setjmp_event::prepare_for_emission (checker_path *path,
- pending_diagnostic *pd,
- diagnostic_event_id_t emission_id)
-{
- checker_event::prepare_for_emission (path, pd, emission_id);
- path->get_setjmp_event (m_rewind_info->get_enode_origin (),
- &m_original_setjmp_event_id);
-}
-
-/* class warning_event : public checker_event. */
-
-/* Implementation of diagnostic_event::get_desc vfunc for
- warning_event.
-
- If the pending diagnostic implements describe_final_event, use it,
- generating a precise description e.g.
- "second 'free' here; first 'free' was at (7)"
-
- Otherwise generate a generic description. */
-
-label_text
-warning_event::get_desc (bool can_colorize) const
-{
- if (m_pending_diagnostic)
- {
- tree var = fixup_tree_for_diagnostic (m_var);
- label_text ev_desc
- = m_pending_diagnostic->describe_final_event
- (evdesc::final_event (can_colorize, var, m_state));
- if (ev_desc.get ())
- {
- if (m_sm && flag_analyzer_verbose_state_changes)
- {
- if (var)
- return make_label_text (can_colorize,
- "%s (%qE is in state %qs)",
- ev_desc.get (),
- var, m_state->get_name ());
- else
- return make_label_text (can_colorize,
- "%s (in global state %qs)",
- ev_desc.get (),
- m_state->get_name ());
- }
- else
- return ev_desc;
- }
- }
-
- if (m_sm)
- {
- if (m_var)
- return make_label_text (can_colorize,
- "here (%qE is in state %qs)",
- m_var, m_state->get_name ());
- else
- return make_label_text (can_colorize,
- "here (in global state %qs)",
- m_state->get_name ());
- }
- else
- return label_text::borrow ("here");
-}
-
-/* Implementation of diagnostic_event::get_meaning vfunc for
- warning_event. */
-
-diagnostic_event::meaning
-warning_event::get_meaning () const
-{
- return meaning (VERB_danger, NOUN_unknown);
-}
-
/* Print a single-line representation of this path to PP. */
void