aboutsummaryrefslogtreecommitdiff
path: root/gcc/analyzer/analyzer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/analyzer/analyzer.cc')
-rw-r--r--gcc/analyzer/analyzer.cc162
1 files changed, 158 insertions, 4 deletions
diff --git a/gcc/analyzer/analyzer.cc b/gcc/analyzer/analyzer.cc
index df8d881..f6e9c9d 100644
--- a/gcc/analyzer/analyzer.cc
+++ b/gcc/analyzer/analyzer.cc
@@ -60,6 +60,160 @@ get_stmt_location (const gimple *stmt, function *fun)
return stmt->location;
}
+static tree
+fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited);
+
+/* Attemp to generate a tree for the LHS of ASSIGN_STMT.
+ VISITED must be non-NULL; it is used to ensure termination. */
+
+static tree
+get_diagnostic_tree_for_gassign_1 (const gassign *assign_stmt,
+ hash_set<tree> *visited)
+{
+ enum tree_code code = gimple_assign_rhs_code (assign_stmt);
+
+ /* Reverse the effect of extract_ops_from_tree during
+ gimplification. */
+ switch (get_gimple_rhs_class (code))
+ {
+ default:
+ case GIMPLE_INVALID_RHS:
+ gcc_unreachable ();
+ case GIMPLE_TERNARY_RHS:
+ case GIMPLE_BINARY_RHS:
+ case GIMPLE_UNARY_RHS:
+ {
+ tree t = make_node (code);
+ TREE_TYPE (t) = TREE_TYPE (gimple_assign_lhs (assign_stmt));
+ unsigned num_rhs_args = gimple_num_ops (assign_stmt) - 1;
+ for (unsigned i = 0; i < num_rhs_args; i++)
+ {
+ tree op = gimple_op (assign_stmt, i + 1);
+ if (op)
+ {
+ op = fixup_tree_for_diagnostic_1 (op, visited);
+ if (op == NULL_TREE)
+ return NULL_TREE;
+ }
+ TREE_OPERAND (t, i) = op;
+ }
+ return t;
+ }
+ case GIMPLE_SINGLE_RHS:
+ {
+ tree op = gimple_op (assign_stmt, 1);
+ op = fixup_tree_for_diagnostic_1 (op, visited);
+ return op;
+ }
+ }
+}
+
+/* Subroutine of fixup_tree_for_diagnostic_1, called on SSA names.
+ Attempt to reconstruct a a tree expression for SSA_NAME
+ based on its def-stmt.
+ SSA_NAME must be non-NULL.
+ VISITED must be non-NULL; it is used to ensure termination.
+
+ Return NULL_TREE if there is a problem. */
+
+static tree
+maybe_reconstruct_from_def_stmt (tree ssa_name,
+ hash_set<tree> *visited)
+{
+ /* Ensure termination. */
+ if (visited->contains (ssa_name))
+ return NULL_TREE;
+ visited->add (ssa_name);
+
+ gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
+
+ switch (gimple_code (def_stmt))
+ {
+ default:
+ gcc_unreachable ();
+ case GIMPLE_ASM:
+ case GIMPLE_NOP:
+ case GIMPLE_PHI:
+ /* Can't handle these. */
+ return NULL_TREE;
+ case GIMPLE_ASSIGN:
+ return get_diagnostic_tree_for_gassign_1
+ (as_a <const gassign *> (def_stmt), visited);
+ case GIMPLE_CALL:
+ {
+ gcall *call_stmt = as_a <gcall *> (def_stmt);
+ tree return_type = gimple_call_return_type (call_stmt);
+ tree fn = fixup_tree_for_diagnostic_1 (gimple_call_fn (call_stmt),
+ visited);
+ if (fn == NULL_TREE)
+ return NULL_TREE;
+ unsigned num_args = gimple_call_num_args (call_stmt);
+ auto_vec<tree> args (num_args);
+ for (unsigned i = 0; i < num_args; i++)
+ {
+ tree arg = gimple_call_arg (call_stmt, i);
+ arg = fixup_tree_for_diagnostic_1 (arg, visited);
+ if (arg == NULL_TREE)
+ return NULL_TREE;
+ args.quick_push (arg);
+ }
+ gcc_assert (fn);
+ return build_call_array_loc (gimple_location (call_stmt),
+ return_type, fn,
+ num_args, args.address ());
+ }
+ break;
+ }
+}
+
+/* Subroutine of fixup_tree_for_diagnostic: attempt to fixup EXPR,
+ which can be NULL.
+ VISITED must be non-NULL; it is used to ensure termination. */
+
+static tree
+fixup_tree_for_diagnostic_1 (tree expr, hash_set<tree> *visited)
+{
+ if (expr
+ && TREE_CODE (expr) == SSA_NAME
+ && (SSA_NAME_VAR (expr) == NULL_TREE
+ || DECL_ARTIFICIAL (SSA_NAME_VAR (expr))))
+ {
+ if (tree var = SSA_NAME_VAR (expr))
+ if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
+ return DECL_DEBUG_EXPR (var);
+ if (tree expr2 = maybe_reconstruct_from_def_stmt (expr, visited))
+ return expr2;
+ }
+ return expr;
+}
+
+/* We don't want to print '<unknown>' in our diagnostics (PR analyzer/99771),
+ but sometimes we generate diagnostics involving an ssa name for a
+ temporary.
+
+ Work around this by attempting to reconstruct a tree expression for
+ such temporaries based on their def-stmts.
+
+ Otherwise return EXPR.
+
+ EXPR can be NULL. */
+
+tree
+fixup_tree_for_diagnostic (tree expr)
+{
+ hash_set<tree> visited;
+ return fixup_tree_for_diagnostic_1 (expr, &visited);
+}
+
+/* Attempt to generate a tree for the LHS of ASSIGN_STMT. */
+
+tree
+get_diagnostic_tree_for_gassign (const gassign *assign_stmt)
+{
+ hash_set<tree> visited;
+ return get_diagnostic_tree_for_gassign_1 (assign_stmt, &visited);
+}
+
} // namespace ana
/* Helper function for checkers. Is the CALL to the given function name,
@@ -90,7 +244,7 @@ is_special_named_call_p (const gcall *call, const char *funcname,
Compare with special_function_p in calls.c. */
bool
-is_named_call_p (tree fndecl, const char *funcname)
+is_named_call_p (const_tree fndecl, const char *funcname)
{
gcc_assert (fndecl);
gcc_assert (funcname);
@@ -142,7 +296,7 @@ is_std_function_p (const_tree fndecl)
/* Like is_named_call_p, but look for std::FUNCNAME. */
bool
-is_std_named_call_p (tree fndecl, const char *funcname)
+is_std_named_call_p (const_tree fndecl, const char *funcname)
{
gcc_assert (fndecl);
gcc_assert (funcname);
@@ -164,7 +318,7 @@ is_std_named_call_p (tree fndecl, const char *funcname)
arguments? */
bool
-is_named_call_p (tree fndecl, const char *funcname,
+is_named_call_p (const_tree fndecl, const char *funcname,
const gcall *call, unsigned int num_args)
{
gcc_assert (fndecl);
@@ -182,7 +336,7 @@ is_named_call_p (tree fndecl, const char *funcname,
/* Like is_named_call_p, but check for std::FUNCNAME. */
bool
-is_std_named_call_p (tree fndecl, const char *funcname,
+is_std_named_call_p (const_tree fndecl, const char *funcname,
const gcall *call, unsigned int num_args)
{
gcc_assert (fndecl);