function_hooks: improve how function assignments are handled
[smatch.git] / check_unused_ret.c
blob01fafbe76a7a5d8de8d34bf0d6dc5d4269825687
1 /*
2 * Copyright (C) 2009 Dan Carpenter.
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 check is supposed to find places like this:
20 * err = foo();
21 * err = bar();
22 * if (err)
23 * return err;
24 * (the first assignment isn't used)
26 * How the check works is that every assignment gets an ID.
27 * We store that assignment ID in a list of assignments that
28 * haven't been used. We also set the state of 'err' from
29 * the example above to be. Then when we use 'err' we remove
30 * it from the list. At the end of the function we print
31 * a list of assignments that still haven't been used.
33 * Note that this check only works for assignments to
34 * EXPR_SYMBOL. Maybe it could be modified to cover other
35 * assignments later but then you would have to deal with
36 * scope issues.
38 * Also this state is quite tied to the order the callbacks
39 * are called in smatch_flow.c. (If the order changed it
40 * would break).
44 #include "smatch.h"
45 #include "smatch_slist.h"
46 #include "smatch_function_hashtable.h"
48 static int my_id;
50 struct assignment {
51 int assign_id;
52 char *name;
53 char *function;
54 int line;
56 ALLOCATOR(assignment, "assignment id");
57 DECLARE_PTR_LIST(assignment_list, struct assignment);
58 static struct assignment_list *assignment_list;
60 static struct expression *skip_this;
61 static int assign_id;
63 static DEFINE_HASHTABLE_INSERT(insert_func, char, int);
64 static DEFINE_HASHTABLE_SEARCH(search_func, char, int);
65 static struct hashtable *ignored_funcs;
67 static const char *kernel_ignored[] = {
68 "inb",
69 "inl",
70 "inw",
71 "readb",
72 "readl",
73 "readw",
76 static char *get_fn_name(struct expression *expr)
78 if (expr->type != EXPR_CALL)
79 return NULL;
80 if (expr->fn->type != EXPR_SYMBOL)
81 return NULL;
82 return expr_to_var(expr->fn);
85 static int ignored_function(struct expression *expr)
87 char *func;
88 int ret = 0;
90 func = get_fn_name(expr);
91 if (!func)
92 return 0;
93 if (search_func(ignored_funcs, func))
94 ret = 1;
95 free_string(func);
96 return ret;
99 static void match_assign_call(struct expression *expr)
101 struct expression *left;
102 struct assignment *assign;
104 if (final_pass)
105 return;
106 if (in_condition())
107 return;
108 if (expr->op != '=')
109 return;
110 if (unreachable())
111 return;
112 if (ignored_function(expr->right))
113 return;
114 left = strip_expr(expr->left);
115 if (!left || left->type != EXPR_SYMBOL)
116 return;
117 if (left->symbol->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC))
118 return;
120 skip_this = left;
122 set_state_expr(my_id, left, alloc_state_num(assign_id));
124 assign = __alloc_assignment(0);
125 assign->assign_id = assign_id++;
126 assign->name = expr_to_var(left);
127 assign->function = get_fn_name(expr->right);
128 assign->line = get_lineno();
129 add_ptr_list(&assignment_list, assign);
132 static void match_assign(struct expression *expr)
134 struct expression *left;
136 if (expr->op != '=')
137 return;
138 left = strip_expr(expr->left);
139 if (!left || left->type != EXPR_SYMBOL)
140 return;
141 set_state_expr(my_id, left, &undefined);
144 static void delete_used(int assign_id)
146 struct assignment *tmp;
148 FOR_EACH_PTR(assignment_list, tmp) {
149 if (tmp->assign_id == assign_id) {
150 DELETE_CURRENT_PTR(tmp);
151 return;
153 } END_FOR_EACH_PTR(tmp);
156 static void delete_used_symbols(struct state_list *possible)
158 struct sm_state *tmp;
160 FOR_EACH_PTR(possible, tmp) {
161 delete_used(PTR_INT(tmp->state->data));
162 } END_FOR_EACH_PTR(tmp);
165 static void match_symbol(struct expression *expr)
167 struct sm_state *sm;
169 expr = strip_expr(expr);
170 if (expr == skip_this)
171 return;
172 sm = get_sm_state_expr(my_id, expr);
173 if (!sm)
174 return;
175 delete_used_symbols(sm->possible);
176 set_state_expr(my_id, expr, &undefined);
179 static void match_end_func(struct symbol *sym)
181 struct assignment *tmp;
183 if (__inline_fn)
184 return;
185 FOR_EACH_PTR(assignment_list, tmp) {
186 sm_printf("%s:%d %s ", get_filename(), tmp->line, get_function());
187 sm_printf("warn: unused return: %s = %s()\n",
188 tmp->name, tmp->function);
189 } END_FOR_EACH_PTR(tmp);
190 clear_assignment_alloc();
191 __free_ptr_list((struct ptr_list **)&assignment_list);
194 void check_unused_ret(int id)
196 my_id = id;
198 /* It turns out that this test is worthless unless you use --two-passes. */
199 if (!option_two_passes)
200 return;
201 add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK);
202 add_hook(&match_assign, ASSIGNMENT_HOOK);
203 add_hook(&match_symbol, SYM_HOOK);
204 add_hook(&match_end_func, END_FUNC_HOOK);
205 ignored_funcs = create_function_hashtable(100);
206 if (option_project == PROJ_KERNEL) {
207 int i;
209 for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++)
210 insert_func(ignored_funcs, (char *)kernel_ignored[i], (int *)1);