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.
28 #include "smatch_slist.h"
29 #include "smatch_extra.h"
31 extern int check_assigned_expr_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
);
45 static void undef(struct sm_state
*sm
, struct expression
*mod_expr
)
47 if (__in_fake_parameter_assign
)
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
;
58 /* skip 'foo->'. This was checked in the caller. */
59 skip
= sym
->ident
->len
+ 2;
61 state
= get_state(my_id
, sym
->ident
->name
, sym
);
62 if (!state
|| !state
->data
)
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
)
75 if (sm
->state
->data
!= sym
)
77 len
= strlen(sm
->state
->name
);
78 if (strncmp(name
, sm
->state
->name
, len
) != 0)
83 if (!stack
&& name
[len
] != '-')
85 snprintf(buf
, sizeof(buf
), "%s%s", sm
->name
, name
+ len
);
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
;
97 orig_expr
= sm
->state
->data
;
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
)
111 orig_sym
= expr_to_sym(orig_expr
);
117 len
= strlen(sm
->state
->name
);
118 if (strncmp(name
, sm
->state
->name
, len
) != 0)
121 if (name
[len
] == '.')
123 if (!stack
&& name
[len
] != '-')
125 snprintf(buf
, sizeof(buf
), "%s%s", sm
->name
, name
+ len
);
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
)
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
);
153 if (sm
->owner
== check_assigned_expr_id
) {
154 ret
= map_assignment_long_to_short(sm
, name
, sym
, new_sym
, stack
);
159 } END_FOR_EACH_SM(sm
);
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
)
177 struct symbol
*start_sym
;
178 struct smatch_state
*state
;
182 name
= expr_to_str_sym(expr
, &start_sym
);
185 if (expr
->type
== EXPR_CALL
)
186 start_sym
= expr_to_sym(expr
->fn
);
188 state
= get_state(my_id
, name
, start_sym
);
190 if (!state
|| !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
;
202 struct expression
*arg
, *new;
204 struct symbol
*right_sym
;
207 while (*p
&& *p
!= '[')
215 snprintf(buf
, sizeof(buf
), "%s", p
);
216 close
= strchr(buf
, ']');
221 param
= atoi(buf
+ 1);
222 arg
= get_argument_from_call_expr(call
->args
, param
);
226 new = gen_expression_from_key(arg
, buf
);
230 right_name
= expr_to_var_sym(new, &right_sym
);
231 if (!right_name
|| !right_sym
)
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
);
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
)
252 call
= strip_expr(expr
->right
);
253 if (call
->type
!= EXPR_CALL
)
256 store_mapping_helper(left_name
, left_sym
, call
, return_string
);
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
);
267 left_name
= expr_to_str(call
);
271 store_mapping_helper(left_name
, left_sym
, call
, return_string
);
277 free_string(left_name
);
280 void register_return_to_param(int id
)
283 add_modification_hook(my_id
, &undef
);
286 void register_return_to_param_links(int id
)
289 set_up_link_functions(my_id
, link_id
);