Add illumos projects
[smatch.git] / smatch_return_to_param.c
blob220d24f1f91e1e09d6a7f2da72c2a20fe66be807
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
19 * This is for smatch_extra.c to use. It sort of like check_assigned_expr.c but
20 * more limited. Say a function returns "64min-s64max[$0->data]" and the caller
21 * does "struct whatever *p = get_data(dev);" then we want to record that p is
22 * now the same as "dev->data". Then if we update "p->foo" it means we can
23 * update "dev->data->foo" as well.
27 #include "smatch.h"
28 #include "smatch_slist.h"
29 #include "smatch_extra.h"
31 extern int check_assigned_expr_id;
32 static int my_id;
33 static int link_id;
35 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
37 struct smatch_state *state;
39 state = __alloc_smatch_state(0);
40 state->name = alloc_sname(name);
41 state->data = sym;
42 return state;
45 static void undef(struct sm_state *sm, struct expression *mod_expr)
47 if (__in_fake_parameter_assign)
48 return;
49 set_state(my_id, sm->name, sm->sym, &undefined);
52 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
54 struct smatch_state *state;
55 int skip;
56 char buf[256];
58 /* skip 'foo->'. This was checked in the caller. */
59 skip = strlen(sym->ident->name) + 2;
61 state = get_state(my_id, sym->ident->name, sym);
62 if (!state || !state->data)
63 return NULL;
65 snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
66 *new_sym = state->data;
67 return alloc_string(buf);
70 static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
72 int len;
73 char buf[256];
75 if (sm->state->data != sym)
76 return NULL;
77 len = strlen(sm->state->name);
78 if (strncmp(name, sm->state->name, len) != 0)
79 return NULL;
81 if (name[len] == '.')
82 return NULL;
83 if (!stack && name[len] != '-')
84 return NULL;
85 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
86 *new_sym = sm->sym;
87 return alloc_string(buf);
90 static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
92 struct expression *orig_expr;
93 struct symbol *orig_sym;
94 int len;
95 char buf[256];
97 orig_expr = sm->state->data;
98 if (!orig_expr)
99 return NULL;
102 * Say we have an assignment like:
103 * foo->bar->my_ptr = my_ptr;
104 * We still expect the function to carry on using "my_ptr" as the
105 * shorter name. That's not a long to short mapping.
108 if (orig_expr->type == EXPR_SYMBOL)
109 return NULL;
111 orig_sym = expr_to_sym(orig_expr);
112 if (!orig_sym)
113 return NULL;
114 if (sym != orig_sym)
115 return NULL;
117 len = strlen(sm->state->name);
118 if (strncmp(name, sm->state->name, len) != 0)
119 return NULL;
121 if (name[len] == '.')
122 return NULL;
123 if (!stack && name[len] != '-')
124 return NULL;
125 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
126 *new_sym = sm->sym;
127 return alloc_string(buf);
131 * Normally, we expect people to consistently refer to variables by the shortest
132 * name. So they use "b->a" instead of "foo->bar.a" when both point to the
133 * same memory location. However, when we're dealing across function boundaries
134 * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we
135 * translate it to the shorter name. Smatch extra updates the shorter name,
136 * which in turn updates the longer name.
139 static char *map_long_to_short_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
141 char *ret;
142 struct sm_state *sm;
144 *new_sym = NULL;
146 FOR_EACH_SM(__get_cur_stree(), sm) {
147 if (sm->owner == my_id) {
148 ret = map_my_state_long_to_short(sm, name, sym, new_sym, stack);
149 if (ret)
150 return ret;
151 continue;
153 if (sm->owner == check_assigned_expr_id) {
154 ret = map_assignment_long_to_short(sm, name, sym, new_sym, stack);
155 if (ret)
156 return ret;
157 continue;
159 } END_FOR_EACH_SM(sm);
161 return NULL;
164 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
166 return map_long_to_short_name_sym_helper(name, sym, new_sym, 1);
169 char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym)
171 return map_long_to_short_name_sym_helper(name, sym, new_sym, 0);
174 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
176 char *name;
177 struct symbol *start_sym;
178 struct smatch_state *state;
180 *sym = NULL;
182 name = expr_to_str_sym(expr, &start_sym);
183 if (!name)
184 return NULL;
185 if (expr->type == EXPR_CALL)
186 start_sym = expr_to_sym(expr->fn);
188 state = get_state(my_id, name, start_sym);
189 free_string(name);
190 if (!state || !state->data)
191 return NULL;
193 *sym = state->data;
194 return alloc_string(state->name);
197 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
199 const char *p = return_string;
200 char *close;
201 int param;
202 struct expression *arg, *new;
203 char *right_name;
204 struct symbol *right_sym;
205 char buf[256];
207 while (*p && *p != '[')
208 p++;
209 if (!*p)
210 return;
211 p++;
212 if (*p != '$')
213 return;
215 snprintf(buf, sizeof(buf), "%s", p);
216 close = strchr(buf, ']');
217 if (!close)
218 return;
219 *close = '\0';
221 param = atoi(buf + 1);
222 arg = get_argument_from_call_expr(call->args, param);
223 if (!arg)
224 return;
226 new = gen_expression_from_key(arg, buf);
227 if (!new)
228 return;
230 right_name = expr_to_var_sym(new, &right_sym);
231 if (!right_name || !right_sym)
232 goto free;
234 set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym));
235 store_link(link_id, right_name, right_sym, left_name, left_sym);
237 free:
238 free_string(right_name);
241 void __add_return_to_param_mapping(struct expression *expr, const char *return_string)
243 struct expression *call;
244 char *left_name = NULL;
245 struct symbol *left_sym;
247 if (expr->type == EXPR_ASSIGNMENT) {
248 left_name = expr_to_var_sym(expr->left, &left_sym);
249 if (!left_name || !left_sym)
250 goto free;
252 call = strip_expr(expr->right);
253 if (call->type != EXPR_CALL)
254 goto free;
256 store_mapping_helper(left_name, left_sym, call, return_string);
257 goto free;
260 if (expr->type == EXPR_CALL &&
261 expr_get_parent_stmt(expr) &&
262 expr_get_parent_stmt(expr)->type == STMT_RETURN) {
263 call = strip_expr(expr);
264 left_sym = expr_to_sym(call->fn);
265 if (!left_sym)
266 return;
267 left_name = expr_to_str(call);
268 if (!left_name)
269 return;
271 store_mapping_helper(left_name, left_sym, call, return_string);
272 goto free;
276 free:
277 free_string(left_name);
280 void register_return_to_param(int id)
282 my_id = id;
283 add_modification_hook(my_id, &undef);
286 void register_return_to_param_links(int id)
288 link_id = id;
289 set_up_link_functions(my_id, link_id);