modification_hooks: handle PARAM_SET earlier
[smatch.git] / smatch_container_of.c
blob3acfa3245cbf83e9d051cec54ac7c6625b4a27df
1 /*
2 * Copyright (C) 2017 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 #include "smatch.h"
19 #include "smatch_slist.h"
21 static int my_id;
23 static struct stree *used_stree;
24 static struct stree_stack *saved_stack;
26 STATE(used);
28 int get_param_from_container_of(struct expression *expr)
30 struct expression *param_expr;
31 struct symbol *type;
32 sval_t sval;
33 int param;
36 type = get_type(expr);
37 if (!type || type->type != SYM_PTR)
38 return -1;
40 expr = strip_expr(expr);
41 if (expr->type != EXPR_BINOP || expr->op != '-')
42 return -1;
44 if (!get_value(expr->right, &sval))
45 return -1;
46 if (sval.value < 0 || sval.value > 4096)
47 return -1;
49 param_expr = get_assigned_expr(expr->left);
50 if (!param_expr)
51 return -1;
52 param = get_param_num(param_expr);
53 if (param < 0)
54 return -1;
56 return param;
59 int get_offset_from_container_of(struct expression *expr)
61 struct expression *param_expr;
62 struct symbol *type;
63 sval_t sval;
65 type = get_type(expr);
66 if (!type || type->type != SYM_PTR)
67 return -1;
69 expr = strip_expr(expr);
70 if (expr->type != EXPR_BINOP || expr->op != '-')
71 return -1;
73 if (!get_value(expr->right, &sval))
74 return -1;
75 if (sval.value < 0 || sval.value > 4096)
76 return -1;
78 param_expr = get_assigned_expr(expr->left);
79 if (!param_expr)
80 return -1;
82 return sval.value;
85 static int get_container_arg(struct symbol *sym)
87 struct expression *__mptr;
88 int param;
90 if (!sym || !sym->ident)
91 return -1;
93 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
94 param = get_param_from_container_of(__mptr);
96 return param;
99 static int get_container_offset(struct symbol *sym)
101 struct expression *__mptr;
102 int offset;
104 if (!sym || !sym->ident)
105 return -1;
107 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
108 offset = get_offset_from_container_of(__mptr);
110 return offset;
113 static char *get_container_name(struct sm_state *sm, int offset)
115 static char buf[256];
116 const char *name;
118 name = get_param_name(sm);
119 if (!name)
120 return NULL;
122 if (name[0] == '$')
123 snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
124 else if (name[0] == '*' || name[1] == '$')
125 snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
126 else
127 return NULL;
129 return buf;
132 static void get_state_hook(int owner, const char *name, struct symbol *sym)
134 int arg;
136 if (!option_info)
137 return;
138 if (__in_fake_assign)
139 return;
141 arg = get_container_arg(sym);
142 if (arg >= 0)
143 set_state_stree(&used_stree, my_id, name, sym, &used);
146 static void set_param_used(struct expression *call, struct expression *arg, char *key, char *unused)
148 struct symbol *sym;
149 char *name;
150 int arg_nr;
152 name = get_variable_from_key(arg, key, &sym);
153 if (!name || !sym)
154 goto free;
156 arg_nr = get_container_arg(sym);
157 if (arg_nr >= 0)
158 set_state(my_id, name, sym, &used);
159 free:
160 free_string(name);
163 static void process_states(void)
165 struct sm_state *tmp;
166 int arg, offset;
167 const char *name;
169 FOR_EACH_SM(used_stree, tmp) {
170 arg = get_container_arg(tmp->sym);
171 offset = get_container_offset(tmp->sym);
172 if (arg < 0 || offset < 0)
173 continue;
174 name = get_container_name(tmp, offset);
175 if (!name)
176 continue;
177 sql_insert_call_implies(CONTAINER, arg, name, "");
178 } END_FOR_EACH_SM(tmp);
180 free_stree(&used_stree);
183 static void match_function_def(struct symbol *sym)
185 free_stree(&used_stree);
188 static void match_save_states(struct expression *expr)
190 push_stree(&saved_stack, used_stree);
191 used_stree = NULL;
194 static void match_restore_states(struct expression *expr)
196 free_stree(&used_stree);
197 used_stree = pop_stree(&saved_stack);
200 static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
202 int offset;
203 int param;
204 char key[64];
205 char value[64];
207 param = get_param_from_container_of(expr);
208 if (param < 0)
209 return;
210 offset = get_offset_from_container_of(expr);
211 if (offset < 0)
212 return;
214 snprintf(key, sizeof(key), "%d", param);
215 snprintf(value, sizeof(value), "-%d", offset);
217 /* no need to add it to call_implies because it's not really param_used */
218 sql_insert_return_states(return_id, return_ranges, CONTAINER, -1,
219 key, value);
222 static void returns_container_of(struct expression *expr, int param, char *key, char *value)
224 struct expression *call, *arg;
225 int offset;
226 char buf[64];
228 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
229 return;
230 call = strip_expr(expr->right);
231 if (call->type != EXPR_CALL)
232 return;
233 if (param != -1)
234 return;
235 param = atoi(key);
236 offset = atoi(value);
238 arg = get_argument_from_call_expr(call->args, param);
239 if (!arg)
240 return;
241 if (arg->type != EXPR_SYMBOL)
242 return;
243 param = get_param_num(arg);
244 if (param < 0)
245 return;
246 snprintf(buf, sizeof(buf), "$(%d)", offset);
247 sql_insert_call_implies(CONTAINER, param, buf, "");
250 void register_container_of(int id)
252 my_id = id;
254 add_hook(&match_function_def, FUNC_DEF_HOOK);
256 add_get_state_hook(&get_state_hook);
258 add_hook(&match_save_states, INLINE_FN_START);
259 add_hook(&match_restore_states, INLINE_FN_END);
261 select_call_implies_hook(CONTAINER, &set_param_used);
262 all_return_states_hook(&process_states);
264 add_split_return_callback(&print_returns_container_of);
265 select_return_states_hook(CONTAINER, &returns_container_of);