call_tree.pl: make it an interactive program
[smatch.git] / check_unused_ret.c
blob9992d2a8dfd22edef0adb84b2133843a63f8f9a1
1 /*
2 * sparse/check_unused_ret.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
8 */
11 * This check is supposed to find places like this:
12 * err = foo();
13 * err = bar();
14 * if (err)
15 * return err;
16 * (the first assignment isn't used)
18 * How the check works is that every assignment gets an ID.
19 * We store that assignment ID in a list of assignments that
20 * haven't been used. We also set the state of 'err' from
21 * the example above to be. Then when we use 'err' we remove
22 * it from the list. At the end of the function we print
23 * a list of assignments that still haven't been used.
25 * Note that this check only works for assignments to
26 * EXPR_SYMBOL. Maybe it could be modified to cover other
27 * assignments later but then you would have to deal with
28 * scope issues.
30 * Also this state is quite tied to the order the callbacks
31 * are called in smatch_flow.c. (If the order changed it
32 * would break).
36 #define _GNU_SOURCE
37 #include <search.h>
38 #include "smatch.h"
39 #include "smatch_slist.h"
41 static int my_id;
43 struct assignment {
44 int assign_id;
45 char *name;
46 int line;
48 ALLOCATOR(assignment, "assignment id");
49 DECLARE_PTR_LIST(assignment_list, struct assignment);
50 static struct assignment_list *assignment_list;
52 static struct expression *skip_this;
53 static int assign_id;
55 static struct hsearch_data ignored_funcs;
56 static const char *kernel_ignored[] = {
57 "inb",
58 "inl",
59 "inw",
62 static struct smatch_state *my_alloc_state(int assign_id)
64 struct smatch_state *state;
65 static char buff[256];
67 state = __alloc_smatch_state(0);
68 snprintf(buff, 255, "assign_%d", assign_id);
69 buff[255] = '\0';
70 state->name = alloc_string(buff);
71 state->data = (void *) assign_id;
72 return state;
75 static int ignored_function(struct expression *func)
77 ENTRY e, *ep;
79 func = strip_expr(func);
80 if (!func || func->type != EXPR_CALL)
81 return 0;
82 func = func->fn;
83 if (!func || func->type != EXPR_SYMBOL)
84 return 0;
86 e.key = func->symbol_name->name;
87 hsearch_r(e, FIND, &ep, &ignored_funcs);
88 if (!ep)
89 return 0;
90 return 1;
93 static void match_assign_call(struct expression *expr)
95 struct expression *left;
96 struct assignment *assign;
98 if (final_pass)
99 return;
100 if (in_condition())
101 return;
102 if (expr->op != '=')
103 return;
104 if (unreachable())
105 return;
106 if (ignored_function(expr->right))
107 return;
108 left = strip_expr(expr->left);
109 if (!left || left->type != EXPR_SYMBOL)
110 return;
111 if (left->symbol->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC))
112 return;
114 skip_this = left;
116 set_state_expr(my_id, left, my_alloc_state(assign_id));
118 assign = __alloc_assignment(0);
119 assign->assign_id = assign_id++;
120 assign->name = get_variable_from_expr(left, NULL);
121 assign->line = get_lineno();
122 add_ptr_list(&assignment_list, assign);
125 static void match_assign(struct expression *expr)
127 struct expression *left;
129 if (expr->op != '=')
130 return;
131 left = strip_expr(expr->left);
132 if (!left || left->type != EXPR_SYMBOL)
133 return;
134 delete_state_expr(my_id, left);
137 static void delete_used(int assign_id)
139 struct assignment *tmp;
141 FOR_EACH_PTR(assignment_list, tmp) {
142 if (tmp->assign_id == assign_id) {
143 DELETE_CURRENT_PTR(tmp);
144 return;
146 } END_FOR_EACH_PTR(tmp);
149 static void delete_used_symbols(struct state_list *possible)
151 struct sm_state *tmp;
153 FOR_EACH_PTR(possible, tmp) {
154 delete_used((int)tmp->state->data);
155 } END_FOR_EACH_PTR(tmp);
158 static void match_symbol(struct expression *expr)
160 struct sm_state *sm;
162 expr = strip_expr(expr);
163 if (expr == skip_this)
164 return;
165 sm = get_sm_state_expr(my_id, expr);
166 if (!sm)
167 return;
168 delete_used_symbols(sm->possible);
169 delete_state_expr(my_id, expr);
172 static void match_end_func(struct symbol *sym)
174 struct assignment *tmp;
176 FOR_EACH_PTR(assignment_list, tmp) {
177 sm_printf("%s +%d %s ", get_filename(), tmp->line, get_function());
178 sm_printf("warning: assignment to '%s' was never used\n", tmp->name);
179 } END_FOR_EACH_PTR(tmp);
180 clear_assignment_alloc();
181 __free_ptr_list((struct ptr_list **)&assignment_list);
184 void check_unused_ret(int id)
186 my_id = id;
188 /* It turns out that this test is worthless unless you use --two-passes. */
189 if (!option_two_passes)
190 return;
191 add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK);
192 add_hook(&match_assign, ASSIGNMENT_HOOK);
193 add_hook(&match_symbol, SYM_HOOK);
194 add_hook(&match_end_func, END_FUNC_HOOK);
195 hcreate_r(1000, &ignored_funcs);
196 if (option_project == PROJ_KERNEL) {
197 int i;
199 for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++) {
200 ENTRY e, *ep;
202 e.key = (char *)kernel_ignored[i];
203 e.data = (void *)1;
204 if (!hsearch_r(e, ENTER, &ep, &ignored_funcs)) {
205 printf("check_unused_ret.c broke.\n");
206 exit(1);