clear ->my_lists for &ignore.
[smatch.git] / smatch_implied.c
blobec7da0a811221894fb93ff1caedd302f5f8f840d
1 /*
2 * sparse/smatch_implied.c
4 * Copyright (C) 2008 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
11 * Imagine we have this code:
12 * foo = 0;
13 * if (bar)
14 * foo = 1;
15 * // <-- point #1
16 * else
17 * frob();
18 * // <-- point #2
19 * if (foo == 1) // <-- point #3
20 * bar->baz; // <-- point #4
22 * Currently (Oct 2008) in smatch when we merge bar states
23 * null and nonnull, at point #2, the state becomes undefined.
24 * As a result we get an error at point #3.
26 * The idea behind "implied state pools" is to fix that.
28 * The implied pools get created in merge_slist(). Whatever
29 * is unique to one slist being merged gets put into a pool.
31 * If we set a state that removes it from all pools.
33 * When we come to an if statement where "foo" has some pools
34 * associated we take all the pools where "foo == 1" and keep
35 * all the states that are consistent across those pools.
37 * The point of doing this is to turn an undefined state into
38 * a defined state. This hopefully gets rid of some false positives.
39 * What it doesn't do is find new errors that were
40 * missed before.
42 * There are quite a few implementation details I haven't figured
43 * out. How do you create implied state pools inside a
44 * complex condition? How do you determine what is implied
45 * from a complex condition? The initial patch is extremely rudimentary...
48 #include "smatch.h"
49 #include "smatch_slist.h"
50 #include "smatch_extra.h"
52 #define EQUALS 0
53 #define NOTEQUALS 1
55 int debug_implied_states = 0;
56 int option_no_implied = 0;
58 static int pool_in_pools(struct state_list_stack *pools,
59 struct state_list *pool)
61 struct state_list *tmp;
63 FOR_EACH_PTR(pools, tmp) {
64 if (tmp == pool)
65 return 1;
66 } END_FOR_EACH_PTR(tmp);
67 return 0;
70 static struct state_list *clone_states_in_pool(struct state_list *pool,
71 struct state_list *cur_slist)
73 struct sm_state *state;
74 struct sm_state *cur_state;
75 struct sm_state *tmp;
76 struct state_list *to_slist = NULL;
78 FOR_EACH_PTR(pool, state) {
79 cur_state = get_sm_state_slist(cur_slist, state->name,
80 state->owner, state->sym);
81 if (!cur_state)
82 continue;
83 if (state == cur_state)
84 continue;
85 if (pool_in_pools(cur_state->all_pools, pool)) {
86 tmp = clone_state(state);
87 add_ptr_list(&to_slist, tmp);
89 } END_FOR_EACH_PTR(state);
90 return to_slist;
94 * merge_implied() takes an implied state and another possibly implied state
95 * from another pool. It checks that the second pool is reachable from
96 * cur_slist then merges the two states and returns the result.
98 static struct sm_state *merge_implied(struct sm_state *one,
99 struct sm_state *two,
100 struct state_list *pool,
101 struct state_list *cur_slist)
103 struct sm_state *cur_state;
105 cur_state = get_sm_state_slist(cur_slist, two->name, two->owner,
106 two->sym);
107 if (!cur_state)
108 return NULL; /* this can't actually happen */
109 if (!pool_in_pools(cur_state->all_pools, pool))
110 return NULL;
111 return merge_sm_states(one, two);
115 * filter() is used to find what states are the same across
116 * a series of slists.
117 * It takes a **slist and a *filter.
118 * It removes everything from **slist that isn't in *filter.
119 * The reason you would want to do this is if you want to
120 * know what other states are true if one state is true. (smatch_implied).
122 static void filter(struct state_list **slist, struct state_list *filter,
123 struct state_list *cur_slist)
125 struct sm_state *s_one, *s_two;
126 struct state_list *results = NULL;
127 struct sm_state *tmp;
129 PREPARE_PTR_LIST(*slist, s_one);
130 PREPARE_PTR_LIST(filter, s_two);
131 for (;;) {
132 if (!s_one || !s_two)
133 break;
134 if (cmp_tracker(s_one, s_two) < 0) {
135 DIMPLIED("removed %s\n", s_one->name);
136 NEXT_PTR_LIST(s_one);
137 } else if (cmp_tracker(s_one, s_two) == 0) {
138 tmp = merge_implied(s_one, s_two, filter, cur_slist);
139 if (tmp)
140 add_ptr_list(&results, tmp);
141 else
142 DIMPLIED("removed %s\n", s_one->name);
143 NEXT_PTR_LIST(s_one);
144 NEXT_PTR_LIST(s_two);
145 } else {
146 NEXT_PTR_LIST(s_two);
149 FINISH_PTR_LIST(s_two);
150 FINISH_PTR_LIST(s_one);
152 free_slist(slist);
153 *slist = results;
156 static struct state_list *filter_stack(struct state_list_stack *stack)
158 struct state_list *tmp;
159 struct state_list *ret = NULL;
160 int i = 0;
162 FOR_EACH_PTR(stack, tmp) {
163 if (!i++) {
164 ret = clone_states_in_pool(tmp, __get_cur_slist());
165 if (debug_implied_states) {
166 printf("The first implied pool is:\n");
167 __print_slist(ret);
169 } else {
170 filter(&ret, tmp, __get_cur_slist());
171 DIMPLIED("filtered\n");
173 } END_FOR_EACH_PTR(tmp);
174 return ret;
177 static void get_eq_neq(struct sm_state *sm_state, int comparison, int num,
178 int left, struct state_list **true_states,
179 struct state_list **false_states)
181 struct state_list *list;
182 struct sm_state *s;
183 struct state_list_stack *true_stack = NULL;
184 struct state_list_stack *false_stack = NULL;
186 if (debug_implied_states || debug_states) {
187 if (left)
188 smatch_msg("checking implications: (%s %s %d)",
189 sm_state->name, show_special(comparison), num);
190 else
191 smatch_msg("checking implications: (%d %s %s)",
192 num, show_special(comparison), sm_state->name);
195 FOR_EACH_PTR(sm_state->my_pools, list) {
196 int istrue, isfalse;
197 s = get_sm_state_slist(list, sm_state->name, sm_state->owner,
198 sm_state->sym);
199 istrue = possibly_true(comparison,
200 (struct data_info *)s->state->data, num,
201 left);
202 isfalse = possibly_false(comparison,
203 (struct data_info *)s->state->data,
204 num, left);
205 if (debug_implied_states || debug_states) {
206 if (istrue && isfalse) {
207 printf("'%s = %s' from %d could be true or "
208 "false.\n", s->name,
209 show_state(s->state), s->line);
210 } else if (istrue) {
211 printf("'%s = %s' from %d is true.\n",
212 s->name, show_state(s->state),
213 s->line);
214 } else if (isfalse) {
215 printf("'%s = %s' from %d is false.\n",
216 s->name, show_state(s->state),
217 s->line);
218 } else {
219 printf("'%s = %s' from %d does not exist.\n",
220 s->name, show_state(s->state),
221 s->line);
224 if (istrue) {
225 push_slist(&true_stack, list);
227 if (isfalse) {
228 push_slist(&false_stack, list);
230 } END_FOR_EACH_PTR(list);
231 DIMPLIED("filtering true stack.\n");
232 *true_states = filter_stack(true_stack);
233 DIMPLIED("filtering false stack.\n");
234 *false_states = filter_stack(false_stack);
235 free_stack(&true_stack);
236 free_stack(&false_stack);
237 if (debug_implied_states || debug_states) {
238 printf("These are the implied states for the true path:\n");
239 __print_slist(*true_states);
240 printf("These are the implied states for the false path:\n");
241 __print_slist(*false_states);
245 static void handle_comparison(struct expression *expr,
246 struct state_list **implied_true,
247 struct state_list **implied_false)
249 struct symbol *sym;
250 char *name;
251 struct sm_state *state;
252 int value;
253 int left = 0;
255 value = get_value(expr->left);
256 if (value == UNDEFINED) {
257 value = get_value(expr->right);
258 if (value == UNDEFINED)
259 return;
260 left = 1;
262 if (left)
263 name = get_variable_from_expr(expr->left, &sym);
264 else
265 name = get_variable_from_expr(expr->right, &sym);
266 if (!name || !sym) {
267 free_string(name);
268 return;
270 state = get_sm_state(name, SMATCH_EXTRA, sym);
271 free_string(name);
272 if (!state)
273 return;
274 if (!state->my_pools) {
275 DIMPLIED("%d '%s' has no pools.\n", get_lineno(), state->name);
276 return;
278 get_eq_neq(state, expr->op, value, left, implied_true, implied_false);
281 static void get_tf_states(struct expression *expr,
282 struct state_list **implied_true,
283 struct state_list **implied_false)
285 struct symbol *sym;
286 char *name;
287 struct sm_state *state;
289 if (expr->type == EXPR_COMPARE) {
290 handle_comparison(expr, implied_true, implied_false);
291 return;
293 if (expr->type == EXPR_ASSIGNMENT) {
294 /* most of the time ->my_pools will be empty here because we
295 just set the state, but if have assigned a conditional
296 function there are implications. */
297 expr = expr->left;
300 name = get_variable_from_expr(expr, &sym);
301 if (!name || !sym) {
302 free_string(name);
303 return;
305 state = get_sm_state(name, SMATCH_EXTRA, sym);
306 free_string(name);
307 if (!state)
308 return;
309 if (!state->my_pools) {
310 DIMPLIED("%d '%s' has no pools.\n", get_lineno(), state->name);
311 return;
313 get_eq_neq(state, SPECIAL_NOTEQUAL, 0, 1, implied_true, implied_false);
316 static void implied_states_hook(struct expression *expr)
318 struct sm_state *state;
319 struct state_list *implied_true = NULL;
320 struct state_list *implied_false = NULL;
322 if (option_no_implied)
323 return;
325 get_tf_states(expr, &implied_true, &implied_false);
327 FOR_EACH_PTR(implied_true, state) {
328 __set_true_false_sm(state, NULL);
329 } END_FOR_EACH_PTR(state);
330 free_slist(&implied_true);
332 FOR_EACH_PTR(implied_false, state) {
333 __set_true_false_sm(NULL, state);
334 } END_FOR_EACH_PTR(state);
335 free_slist(&implied_false);
338 void get_implications(char *name, struct symbol *sym, int comparison, int num,
339 struct state_list **true_states,
340 struct state_list **false_states)
342 struct sm_state *sm;
344 sm = get_sm_state(name, SMATCH_EXTRA, sym);
345 if (!sm)
346 return;
347 if (slist_has_state(sm->possible, &undefined))
348 return;
349 get_eq_neq(sm, comparison, num, 1, true_states, false_states);
352 void __extra_match_condition(struct expression *expr);
353 void register_implications(int id)
355 add_hook(&implied_states_hook, CONDITION_HOOK);
356 add_hook(&__extra_match_condition, CONDITION_HOOK);