units: move checks to check_ file and hide under the --spammy option
[smatch.git] / check_double_checking.c
blobe1dc805308713600a2c952a2042532027aa7a6cb
1 /*
2 * Copyright (C) 2014 Oracle.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
18 #define _GNU_SOURCE
19 #include <string.h>
20 #include "smatch.h"
21 #include "smatch_slist.h"
23 static int my_id;
25 STATE(checked);
26 STATE(modified);
28 struct stree *to_check;
30 static struct statement *get_cur_stmt(void)
32 return last_ptr_list((struct ptr_list *)big_statement_stack);
35 static void set_modified(struct sm_state *sm, struct expression *mod_expr)
37 set_state(my_id, sm->name, sm->sym, &modified);
40 static struct expression *strip_condition(struct expression *expr)
42 expr = strip_expr(expr);
44 if (expr->type == EXPR_PREOP && expr->op == '!')
45 return strip_condition(expr->unop);
47 if (expr->type == EXPR_COMPARE &&
48 (expr->op == SPECIAL_EQUAL ||
49 expr->op == SPECIAL_NOTEQUAL)) {
50 if (expr_is_zero(expr->left))
51 return strip_condition(expr->right);
52 if (expr_is_zero(expr->right))
53 return strip_condition(expr->left);
56 return expr;
59 static int conditions_match(struct expression *cond, struct expression *prev)
61 prev = strip_condition(prev);
63 if (prev == cond)
64 return 1;
66 if (prev->type == EXPR_LOGICAL) {
67 if (conditions_match(cond, prev->left) ||
68 conditions_match(cond, prev->right))
69 return 1;
72 return 0;
76 * People like to do "if (foo) { ... } else if (!foo) { ... }". Don't
77 * complain when they do that even though it is nonsense.
79 static int is_obvious_else(struct expression *cond)
81 struct statement *parent;
82 struct expression *prev;
84 if (!get_cur_stmt())
85 return 0;
86 parent = get_cur_stmt()->parent;
87 if (!parent)
88 return 0;
90 if (parent->type != STMT_IF)
91 return 0;
93 if (!parent->if_false)
94 return 0;
95 if (parent->if_false != get_cur_stmt())
96 return 0;
98 prev = strip_condition(parent->if_conditional);
100 return conditions_match(cond, prev);
103 static int name_means_synchronize(const char *name)
105 if (!name)
106 return 0;
108 if (strcasestr(name, "wait"))
109 return 1;
110 if (strcasestr(name, "down"))
111 return 1;
112 if (strcasestr(name, "lock") && !strcasestr(name, "unlock"))
113 return 1;
114 if (strcasestr(name, "delay"))
115 return 1;
116 if (strcasestr(name, "schedule"))
117 return 1;
118 if (strcmp(name, "smp_rmb") == 0)
119 return 1;
120 if (strcmp(name, "mb") == 0)
121 return 1;
122 if (strcmp(name, "barrier") == 0)
123 return 1;
124 return 0;
127 static int previous_statement_was_synchronize(void)
129 struct statement *stmt;
130 struct position pos;
131 struct position prev_pos;
132 char *ident;
134 if (!__cur_stmt)
135 return 0;
137 if (__prev_stmt) {
138 prev_pos = __prev_stmt->pos;
139 prev_pos.line -= 3;
140 } else {
141 prev_pos = __cur_stmt->pos;
142 prev_pos.line -= 5;
145 FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) {
146 if (stmt->pos.line < prev_pos.line)
147 return 0;
148 pos = stmt->pos;
149 ident = get_macro_name(pos);
150 if (name_means_synchronize(ident))
151 return 1;
152 ident = pos_ident(pos);
153 if (!ident)
154 continue;
155 if (strcmp(ident, "if") == 0) {
156 pos.pos += 4;
157 ident = pos_ident(pos);
158 if (!ident)
159 continue;
161 if (name_means_synchronize(ident))
162 return 1;
163 } END_FOR_EACH_PTR_REVERSE(stmt);
164 return 0;
167 static void match_condition(struct expression *expr)
169 struct smatch_state *state;
170 sval_t dummy;
171 char *name;
173 if (inside_loop())
174 return;
176 if (get_value(expr, &dummy))
177 return;
179 if (get_macro_name(expr->pos))
180 return;
182 state = get_stored_condition(expr);
183 if (!state || !state->data)
184 return;
185 if (get_macro_name(((struct expression *)state->data)->pos))
186 return;
189 * we allow double checking for NULL because people do this all the time
190 * and trying to stop them is a losers' battle.
192 if (is_pointer(expr) && implied_condition_true(expr))
193 return;
195 if (definitely_inside_loop()) {
196 struct symbol *sym;
198 if (__inline_fn)
199 return;
201 name = expr_to_var_sym(expr, &sym);
202 if (!name)
203 return;
204 set_state_expr(my_id, expr, &checked);
205 set_state_stree(&to_check, my_id, name, sym, &checked);
206 free_string(name);
207 return;
210 if (is_obvious_else(state->data))
211 return;
214 * It's common to test something, then take a lock and test if it is
215 * still true.
217 if (previous_statement_was_synchronize())
218 return;
220 name = expr_to_str(expr);
221 sm_warning("we tested '%s' before and it was '%s'", name, state->name);
222 free_string(name);
225 int get_check_line(struct sm_state *sm)
227 struct sm_state *tmp;
229 FOR_EACH_PTR(sm->possible, tmp) {
230 if (tmp->state == &checked)
231 return tmp->line;
232 } END_FOR_EACH_PTR(tmp);
234 return get_lineno();
237 static void after_loop(struct statement *stmt)
239 struct sm_state *check, *sm;
241 if (!stmt || stmt->type != STMT_ITERATOR)
242 return;
243 if (definitely_inside_loop())
244 return;
245 if (__inline_fn)
246 return;
248 FOR_EACH_SM(to_check, check) {
249 continue;
250 sm = get_sm_state(my_id, check->name, check->sym);
251 continue;
252 if (!sm)
253 continue;
254 if (slist_has_state(sm->possible, &modified))
255 continue;
257 sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function());
258 sm_printf("warn: we tested '%s' already\n", check->name);
259 } END_FOR_EACH_SM(check);
261 free_stree(&to_check);
264 static void match_func_end(struct symbol *sym)
266 if (__inline_fn)
267 return;
268 if (to_check)
269 sm_msg("debug: odd... found an function without an end.");
270 free_stree(&to_check);
273 void check_double_checking(int id)
275 my_id = id;
277 if (!option_spammy)
278 return;
280 add_hook(&match_condition, CONDITION_HOOK);
281 add_modification_hook(my_id, &set_modified);
282 add_hook(after_loop, STMT_HOOK_AFTER);
283 add_hook(&match_func_end, AFTER_FUNC_HOOK);