kernel.return_fixes: add mipi_dsi_device_transfer(), timer_delete() and get_device()
[smatch.git] / smatch_return_to_param.c
blob7957d17d94d93db7dabfd0c16bd75987f2b3112a
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 static int my_id;
32 static int link_id;
34 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
36 struct smatch_state *state;
38 state = __alloc_smatch_state(0);
39 state->name = alloc_sname(name);
40 state->data = sym;
41 return state;
44 static void undef(struct sm_state *sm, struct expression *mod_expr)
46 if (__in_fake_parameter_assign)
47 return;
48 set_state(my_id, sm->name, sm->sym, &undefined);
51 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
53 struct smatch_state *state;
54 int skip;
55 char buf[256];
57 /* skip 'foo->'. This was checked in the caller. */
58 skip = sym->ident->len + 2;
60 state = get_state(my_id, sym->ident->name, sym);
61 if (!state || !state->data)
62 return NULL;
64 snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
65 *new_sym = state->data;
66 return alloc_string(buf);
69 static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
71 int len;
72 char buf[256];
74 if (sm->state->data != sym)
75 return NULL;
76 len = strlen(sm->state->name);
77 if (strncmp(name, sm->state->name, len) != 0)
78 return NULL;
80 if (name[len] == '.')
81 return NULL;
82 if (!stack && name[len] != '-')
83 return NULL;
84 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
85 *new_sym = sm->sym;
86 return alloc_string(buf);
90 * Normally, we expect people to consistently refer to variables by the shortest
91 * name. So they use "b->a" instead of "foo->bar.a" when both point to the
92 * same memory location. However, when we're dealing across function boundaries
93 * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we
94 * translate it to the shorter name. Smatch extra updates the shorter name,
95 * which in turn updates the longer name.
98 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
100 char *ret;
101 struct sm_state *sm;
103 *new_sym = NULL;
105 FOR_EACH_SM(__get_cur_stree(), sm) {
106 if (sm->owner == my_id) {
107 ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack);
108 if (ret)
109 return ret;
110 continue;
112 } END_FOR_EACH_SM(sm);
114 return NULL;
117 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
119 struct expression *fake;
120 char *name;
121 struct symbol *start_sym;
122 struct smatch_state *state;
124 *sym = NULL;
126 fake = expr_get_fake_parent_expr(expr);
127 if (fake && fake->type == EXPR_ASSIGNMENT && fake->op == '=')
128 expr = fake->left;
130 name = expr_to_str_sym(expr, &start_sym);
131 if (!name)
132 return NULL;
133 if (expr->type == EXPR_CALL)
134 start_sym = expr_to_sym(expr->fn);
136 state = get_state(my_id, name, start_sym);
137 free_string(name);
138 if (!state || !state->data)
139 return NULL;
141 *sym = state->data;
142 return alloc_string(state->name);
145 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
147 const char *p = return_string;
148 char *close;
149 int param;
150 struct expression *arg, *new;
151 char *right_name;
152 struct symbol *right_sym;
153 char buf[256];
155 while (*p && *p != '[')
156 p++;
157 if (!*p)
158 return;
159 p++;
160 if (*p != '$')
161 return;
163 snprintf(buf, sizeof(buf), "%s", p);
164 close = strchr(buf, ']');
165 if (!close)
166 return;
167 *close = '\0';
169 param = atoi(buf + 1);
170 arg = get_argument_from_call_expr(call->args, param);
171 if (!arg)
172 return;
174 new = gen_expression_from_key(arg, buf);
175 if (!new)
176 return;
178 right_name = expr_to_var_sym(new, &right_sym);
179 if (!right_name || !right_sym)
180 goto free;
182 set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym));
183 store_link(link_id, right_name, right_sym, left_name, left_sym);
185 free:
186 free_string(right_name);
189 static void return_str_to_param_mapping(struct expression *expr, const char *return_string)
191 struct expression *call;
192 char *left_name = NULL;
193 struct symbol *left_sym;
195 if (expr->type == EXPR_ASSIGNMENT) {
196 left_name = expr_to_var_sym(expr->left, &left_sym);
197 if (!left_name || !left_sym)
198 goto free;
200 call = strip_expr(expr->right);
201 if (call->type != EXPR_CALL)
202 goto free;
204 store_mapping_helper(left_name, left_sym, call, return_string);
205 goto free;
208 if (expr->type == EXPR_CALL &&
209 expr_get_parent_stmt(expr) &&
210 expr_get_parent_stmt(expr)->type == STMT_RETURN) {
211 call = strip_expr(expr);
212 left_sym = expr_to_sym(call->fn);
213 if (!left_sym)
214 return;
215 left_name = expr_to_str(call);
216 if (!left_name)
217 return;
219 store_mapping_helper(left_name, left_sym, call, return_string);
220 goto free;
224 free:
225 free_string(left_name);
228 void register_return_to_param(int id)
230 my_id = id;
231 set_dynamic_states(my_id);
232 add_return_string_hook(return_str_to_param_mapping);
233 add_modification_hook(my_id, &undef);
236 void register_return_to_param_links(int id)
238 link_id = id;
239 set_up_link_functions(my_id, link_id);