1 /* -Wnonnull-compare warning support.
2    Copyright (C) 2016-2021 Free Software Foundation, Inc.
3    Contributed by Jakub Jelinek <jakub@redhat.com>
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "backend.h"
25 #include "tree.h"
26 #include "gimple.h"
27 #include "tree-pass.h"
28 #include "ssa.h"
29 #include "diagnostic-core.h"
30 #include "tree-dfa.h"
31 
32 /* Warn about comparison of nonnull_arg_p argument initial values
33    with NULL.  */
34 
35 static void
do_warn_nonnull_compare(function * fun,tree arg)36 do_warn_nonnull_compare (function *fun, tree arg)
37 {
38   if (!POINTER_TYPE_P (TREE_TYPE (arg))
39       && TREE_CODE (TREE_TYPE (arg)) != OFFSET_TYPE)
40     return;
41 
42   if (!nonnull_arg_p (arg))
43     return;
44 
45   tree d = ssa_default_def (fun, arg);
46   if (d == NULL_TREE)
47     return;
48 
49   use_operand_p use_p;
50   imm_use_iterator iter;
51 
52   FOR_EACH_IMM_USE_FAST (use_p, iter, d)
53     {
54       gimple *stmt = USE_STMT (use_p);
55       tree op = NULL_TREE;
56       location_t loc = gimple_location (stmt);
57       if (gimple_code (stmt) == GIMPLE_COND)
58 	switch (gimple_cond_code (stmt))
59 	  {
60 	  case EQ_EXPR:
61 	  case NE_EXPR:
62 	    if (gimple_cond_lhs (stmt) == d)
63 	      op = gimple_cond_rhs (stmt);
64 	    break;
65 	  default:
66 	    break;
67 	  }
68       else if (is_gimple_assign (stmt))
69 	switch (gimple_assign_rhs_code (stmt))
70 	  {
71 	  case EQ_EXPR:
72 	  case NE_EXPR:
73 	    if (gimple_assign_rhs1 (stmt) == d)
74 	      op = gimple_assign_rhs2 (stmt);
75 	    break;
76 	  case COND_EXPR:
77 	    switch (TREE_CODE (gimple_assign_rhs1 (stmt)))
78 	      {
79 	      case EQ_EXPR:
80 	      case NE_EXPR:
81 		op = gimple_assign_rhs1 (stmt);
82 		if (TREE_OPERAND (op, 0) != d)
83 		  {
84 		    op = NULL_TREE;
85 		    break;
86 		  }
87 		loc = EXPR_LOC_OR_LOC (op, loc);
88 		op = TREE_OPERAND (op, 1);
89 		break;
90 	      default:
91 		break;
92 	      }
93 	    break;
94 	  default:
95 	    break;
96 	  }
97       if (op
98 	  && (POINTER_TYPE_P (TREE_TYPE (arg))
99 	      ? integer_zerop (op) : integer_minus_onep (op))
100 	  && !gimple_no_warning_p (stmt))
101 	warning_at (loc, OPT_Wnonnull_compare,
102 		    "%<nonnull%> argument %qD compared to NULL", arg);
103     }
104 }
105 
106 namespace {
107 
108 const pass_data pass_data_warn_nonnull_compare =
109 {
110   GIMPLE_PASS, /* type */
111   "*nonnullcmp", /* name */
112   OPTGROUP_NONE, /* optinfo_flags */
113   TV_NONE, /* tv_id */
114   PROP_ssa, /* properties_required */
115   0, /* properties_provided */
116   0, /* properties_destroyed */
117   0, /* todo_flags_start */
118   0, /* todo_flags_finish */
119 };
120 
121 class pass_warn_nonnull_compare : public gimple_opt_pass
122 {
123 public:
pass_warn_nonnull_compare(gcc::context * ctxt)124   pass_warn_nonnull_compare (gcc::context *ctxt)
125     : gimple_opt_pass (pass_data_warn_nonnull_compare, ctxt)
126   {}
127 
128   /* opt_pass methods: */
gate(function *)129   virtual bool gate (function *) { return warn_nonnull_compare; }
130 
131   virtual unsigned int execute (function *);
132 
133 }; // class pass_warn_nonnull_compare
134 
135 unsigned int
execute(function * fun)136 pass_warn_nonnull_compare::execute (function *fun)
137 {
138   if (fun->static_chain_decl)
139     do_warn_nonnull_compare (fun, fun->static_chain_decl);
140 
141   for (tree arg = DECL_ARGUMENTS (cfun->decl); arg; arg = DECL_CHAIN (arg))
142     do_warn_nonnull_compare (fun, arg);
143   return 0;
144 }
145 
146 } // anon namespace
147 
148 gimple_opt_pass *
make_pass_warn_nonnull_compare(gcc::context * ctxt)149 make_pass_warn_nonnull_compare (gcc::context *ctxt)
150 {
151   return new pass_warn_nonnull_compare (ctxt);
152 }
153