1/* This plugin contains an analysis pass that detects and warns about
2   self-assignment statements.  */
3/* { dg-options "-O" } */
4
5#include "gcc-plugin.h"
6#include "config.h"
7#include "system.h"
8#include "coretypes.h"
9#include "tm.h"
10#include "tree.h"
11#include "stringpool.h"
12#include "toplev.h"
13#include "basic-block.h"
14#include "hash-table.h"
15#include "vec.h"
16#include "ggc.h"
17#include "basic-block.h"
18#include "tree-ssa-alias.h"
19#include "internal-fn.h"
20#include "gimple-fold.h"
21#include "tree-eh.h"
22#include "gimple-expr.h"
23#include "is-a.h"
24#include "gimple.h"
25#include "gimple-iterator.h"
26#include "tree.h"
27#include "tree-pass.h"
28#include "intl.h"
29#include "plugin-version.h"
30#include "diagnostic.h"
31#include "context.h"
32
33int plugin_is_GPL_compatible;
34
35/* Indicate whether to check overloaded operator '=', which is performed by
36   default. To disable it, use -fplugin-arg-NAME-no-check-operator-eq.  */
37bool check_operator_eq = true;
38
39/* Given a rhs EXPR of a gimple assign statement, if it is
40   - SSA_NAME : returns its var decl, or, if it is a temp variable,
41                returns the rhs of its SSA def statement.
42   - VAR_DECL, PARM_DECL, FIELD_DECL, or a reference expression :
43                returns EXPR itself.
44   - any other expression : returns NULL_TREE.  */
45
46static tree
47get_real_ref_rhs (tree expr)
48{
49  switch (TREE_CODE (expr))
50    {
51      case SSA_NAME:
52        {
53          /* Given a self-assign statement, say foo.x = foo.x,
54             the IR (after SSA) looks like:
55
56             D.1797_14 = foo.x;
57             foo.x ={v} D.1797_14;
58
59             So if the rhs EXPR is an SSA_NAME of a temp variable,
60             e.g. D.1797_14, we need to grab the rhs of its SSA def
61             statement (i.e. foo.x).  */
62          tree vdecl = SSA_NAME_VAR (expr);
63          if ((!vdecl || DECL_ARTIFICIAL (vdecl))
64              && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
65            {
66              gimple def_stmt = SSA_NAME_DEF_STMT (expr);
67              /* We are only interested in an assignment with a single
68                 rhs operand because if it is not, the original assignment
69                 will not possibly be a self-assignment.  */
70              if (gimple_assign_single_p (def_stmt))
71                return get_real_ref_rhs (gimple_assign_rhs1 (def_stmt));
72              else
73                return NULL_TREE;
74            }
75          else
76            return vdecl;
77        }
78      case VAR_DECL:
79      case PARM_DECL:
80      case FIELD_DECL:
81      case COMPONENT_REF:
82      case MEM_REF:
83      case ARRAY_REF:
84        return expr;
85      default:
86        return NULL_TREE;
87    }
88}
89
90/* Given an expression tree, EXPR, that may contains SSA names, returns an
91   equivalent tree with the SSA names converted to var/parm/field decls
92   so that it can be used with '%E' format modifier when emitting warning
93   messages.
94
95   This function currently only supports VAR/PARM/FIELD_DECL, reference
96   expressions (COMPONENT_REF, INDIRECT_REF, ARRAY_REF), integer constant,
97   and SSA_NAME. If EXPR contains any other tree nodes (e.g. an arithmetic
98   expression appears in array index), NULL_TREE is returned.  */
99
100static tree
101get_non_ssa_expr (tree expr)
102{
103  if (!expr)
104    return NULL_TREE;
105  switch (TREE_CODE (expr))
106    {
107      case VAR_DECL:
108      case PARM_DECL:
109      case FIELD_DECL:
110        {
111          if (DECL_NAME (expr))
112            return expr;
113          else
114            return NULL_TREE;
115        }
116      case COMPONENT_REF:
117        {
118          tree base, orig_base = TREE_OPERAND (expr, 0);
119          tree component, orig_component = TREE_OPERAND (expr, 1);
120          base = get_non_ssa_expr (orig_base);
121          if (!base)
122            return NULL_TREE;
123          component = get_non_ssa_expr (orig_component);
124          if (!component)
125            return NULL_TREE;
126          /* If either BASE or COMPONENT is converted, build a new
127             component reference tree.  */
128          if (base != orig_base || component != orig_component)
129            return build3 (COMPONENT_REF, TREE_TYPE (component),
130                           base, component, NULL_TREE);
131          else
132            return expr;
133        }
134      case MEM_REF:
135        {
136          tree orig_base = TREE_OPERAND (expr, 0);
137	  if (TREE_CODE (orig_base) == SSA_NAME)
138	    {
139	      tree base = get_non_ssa_expr (orig_base);
140	      if (!base)
141		return NULL_TREE;
142	      return fold_build2 (MEM_REF, TREE_TYPE (expr),
143				  base, TREE_OPERAND (expr, 1));
144	    }
145	  return expr;
146        }
147      case ARRAY_REF:
148        {
149          tree array, orig_array = TREE_OPERAND (expr, 0);
150          tree index, orig_index = TREE_OPERAND (expr, 1);
151          array = get_non_ssa_expr (orig_array);
152          if (!array)
153            return NULL_TREE;
154          index = get_non_ssa_expr (orig_index);
155          if (!index)
156            return NULL_TREE;
157          /* If either ARRAY or INDEX is converted, build a new array
158             reference tree.  */
159          if (array != orig_array || index != orig_index)
160            return build4 (ARRAY_REF, TREE_TYPE (expr), array, index,
161                           TREE_OPERAND (expr, 2), TREE_OPERAND (expr, 3));
162          else
163            return expr;
164        }
165      case SSA_NAME:
166        {
167          tree vdecl = SSA_NAME_VAR (expr);
168          if ((!vdecl || DECL_ARTIFICIAL (vdecl))
169              && !gimple_nop_p (SSA_NAME_DEF_STMT (expr)))
170            {
171              gimple def_stmt = SSA_NAME_DEF_STMT (expr);
172              if (gimple_assign_single_p (def_stmt))
173                vdecl = gimple_assign_rhs1 (def_stmt);
174            }
175          return get_non_ssa_expr (vdecl);
176        }
177      case INTEGER_CST:
178        return expr;
179      default:
180        /* Return NULL_TREE for any other kind of tree nodes.  */
181        return NULL_TREE;
182    }
183}
184
185/* Given the LHS and (real) RHS of a gimple assign statement, STMT, check if
186   they are the same. If so, print a warning message about self-assignment.  */
187
188static void
189compare_and_warn (gimple stmt, tree lhs, tree rhs)
190{
191  if (operand_equal_p (lhs, rhs, OEP_PURE_SAME))
192    {
193      location_t location;
194      location = (gimple_has_location (stmt)
195                  ? gimple_location (stmt)
196                  : (DECL_P (lhs)
197                     ? DECL_SOURCE_LOCATION (lhs)
198                     : input_location));
199      /* If LHS contains any tree node not currently supported by
200         get_non_ssa_expr, simply emit a generic warning without
201         specifying LHS in the message.  */
202      lhs = get_non_ssa_expr (lhs);
203      if (lhs)
204        warning_at (location, 0, G_("%qE is assigned to itself"), lhs);
205      else
206        warning_at (location, 0, G_("self-assignment detected"));
207    }
208}
209
210/* Check and warn if STMT is a self-assign statement.  */
211
212static void
213warn_self_assign (gimple stmt)
214{
215  tree rhs, lhs;
216
217  /* Check assigment statement.  */
218  if (gimple_assign_single_p (stmt))
219    {
220      rhs = get_real_ref_rhs (gimple_assign_rhs1 (stmt));
221      if (!rhs)
222        return;
223
224      lhs = gimple_assign_lhs (stmt);
225      if (TREE_CODE (lhs) == SSA_NAME)
226        {
227          lhs = SSA_NAME_VAR (lhs);
228          if (!lhs || DECL_ARTIFICIAL (lhs))
229            return;
230        }
231
232      compare_and_warn (stmt, lhs, rhs);
233    }
234  /* Check overloaded operator '=' (if enabled).  */
235  else if (check_operator_eq && is_gimple_call (stmt))
236    {
237      tree fdecl = gimple_call_fndecl (stmt);
238      if (fdecl && (DECL_NAME (fdecl) == maybe_get_identifier ("operator=")))
239        {
240          /* If 'operator=' takes reference operands, the arguments will be
241             ADDR_EXPR trees. In this case, just remove the address-taken
242             operator before we compare the lhs and rhs.  */
243          lhs = gimple_call_arg (stmt, 0);
244          if (TREE_CODE (lhs) == ADDR_EXPR)
245            lhs = TREE_OPERAND (lhs, 0);
246          rhs = gimple_call_arg (stmt, 1);
247          if (TREE_CODE (rhs) == ADDR_EXPR)
248            rhs = TREE_OPERAND (rhs, 0);
249
250          compare_and_warn (stmt, lhs, rhs);
251        }
252    }
253}
254
255namespace {
256
257const pass_data pass_data_warn_self_assign =
258{
259  GIMPLE_PASS, /* type */
260  "warn_self_assign", /* name */
261  OPTGROUP_NONE, /* optinfo_flags */
262  TV_NONE, /* tv_id */
263  PROP_ssa, /* properties_required */
264  0, /* properties_provided */
265  0, /* properties_destroyed */
266  0, /* todo_flags_start */
267  0, /* todo_flags_finish */
268};
269
270class pass_warn_self_assign : public gimple_opt_pass
271{
272public:
273  pass_warn_self_assign(gcc::context *ctxt)
274    : gimple_opt_pass(pass_data_warn_self_assign, ctxt)
275  {}
276
277  /* opt_pass methods: */
278  virtual unsigned int execute (function *);
279
280}; // class pass_warn_self_assign
281
282unsigned int
283pass_warn_self_assign::execute (function *fun)
284{
285  gimple_stmt_iterator gsi;
286  basic_block bb;
287
288  FOR_EACH_BB_FN (bb, fun)
289    {
290      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
291        warn_self_assign (gsi_stmt (gsi));
292    }
293
294  return 0;
295}
296
297} // anon namespace
298
299static gimple_opt_pass *
300make_pass_warn_self_assign (gcc::context *ctxt)
301{
302  return new pass_warn_self_assign (ctxt);
303}
304
305/* The initialization routine exposed to and called by GCC. The spec of this
306   function is defined in gcc/gcc-plugin.h.
307
308   PLUGIN_NAME - name of the plugin (useful for error reporting)
309   ARGC        - the size of the ARGV array
310   ARGV        - an array of key-value argument pair
311
312   Returns 0 if initialization finishes successfully.
313
314   Note that this function needs to be named exactly "plugin_init".  */
315
316int
317plugin_init (struct plugin_name_args *plugin_info,
318             struct plugin_gcc_version *version)
319{
320  struct register_pass_info pass_info;
321  const char *plugin_name = plugin_info->base_name;
322  int argc = plugin_info->argc;
323  struct plugin_argument *argv = plugin_info->argv;
324  bool enabled = true;
325  int i;
326
327  if (!plugin_default_version_check (version, &gcc_version))
328    return 1;
329
330  /* Self-assign detection should happen after SSA is constructed.  */
331  pass_info.pass = make_pass_warn_self_assign (g);
332  pass_info.reference_pass_name = "ssa";
333  pass_info.ref_pass_instance_number = 1;
334  pass_info.pos_op = PASS_POS_INSERT_AFTER;
335
336  /* Process the plugin arguments. This plugin takes the following arguments:
337     check-operator-eq, no-check-operator-eq, enable, and disable.
338     By default, the analysis is enabled with 'operator=' checked.  */
339  for (i = 0; i < argc; ++i)
340    {
341      if (!strcmp (argv[i].key, "check-operator-eq"))
342        {
343          if (argv[i].value)
344            warning (0, G_("option '-fplugin-arg-%s-check-operator-eq=%s'"
345                           " ignored (superfluous '=%s')"),
346                     plugin_name, argv[i].value, argv[i].value);
347          else
348            check_operator_eq = true;
349        }
350      else if (!strcmp (argv[i].key, "no-check-operator-eq"))
351        {
352          if (argv[i].value)
353            warning (0, G_("option '-fplugin-arg-%s-no-check-operator-eq=%s'"
354                           " ignored (superfluous '=%s')"),
355                     plugin_name, argv[i].value, argv[i].value);
356          else
357            check_operator_eq = false;
358        }
359      else if (!strcmp (argv[i].key, "enable"))
360        {
361          if (argv[i].value)
362            warning (0, G_("option '-fplugin-arg-%s-enable=%s' ignored"
363                           " (superfluous '=%s')"),
364                     plugin_name, argv[i].value, argv[i].value);
365          else
366            enabled = true;
367        }
368      else if (!strcmp (argv[i].key, "disable"))
369        {
370          if (argv[i].value)
371            warning (0, G_("option '-fplugin-arg-%s-disable=%s' ignored"
372                           " (superfluous '=%s')"),
373                     plugin_name, argv[i].value, argv[i].value);
374          else
375            enabled = false;
376        }
377      else
378        warning (0, G_("plugin %qs: unrecognized argument %qs ignored"),
379                 plugin_name, argv[i].key);
380    }
381
382  /* Register this new pass with GCC if the analysis is enabled.  */
383  if (enabled)
384    register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
385                       &pass_info);
386
387  return 0;
388}
389