comparison: partially fix how links are updated
[smatch.git] / check_unused_ret.c
blobbd52a0cb3ba2d3b2dba894fa5d2271e0405eed4e
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 #include "smatch.h"
37 #include "smatch_slist.h"
38 #include "smatch_function_hashtable.h"
40 static int my_id;
42 struct assignment {
43 int assign_id;
44 char *name;
45 char *function;
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 DEFINE_HASHTABLE_INSERT(insert_func, char, int);
56 static DEFINE_HASHTABLE_SEARCH(search_func, char, int);
57 static struct hashtable *ignored_funcs;
59 static const char *kernel_ignored[] = {
60 "inb",
61 "inl",
62 "inw",
63 "readb",
64 "readl",
65 "readw",
68 static char *get_fn_name(struct expression *expr)
70 if (expr->type != EXPR_CALL)
71 return NULL;
72 if (expr->fn->type != EXPR_SYMBOL)
73 return NULL;
74 return expr_to_var(expr->fn);
77 static int ignored_function(struct expression *expr)
79 char *func;
80 int ret = 0;
82 func = get_fn_name(expr);
83 if (!func)
84 return 0;
85 if (search_func(ignored_funcs, func))
86 ret = 1;
87 free_string(func);
88 return ret;
91 static void match_assign_call(struct expression *expr)
93 struct expression *left;
94 struct assignment *assign;
96 if (final_pass)
97 return;
98 if (in_condition())
99 return;
100 if (expr->op != '=')
101 return;
102 if (unreachable())
103 return;
104 if (ignored_function(expr->right))
105 return;
106 left = strip_expr(expr->left);
107 if (!left || left->type != EXPR_SYMBOL)
108 return;
109 if (left->symbol->ctype.modifiers & (MOD_TOPLEVEL | MOD_EXTERN | MOD_STATIC))
110 return;
112 skip_this = left;
114 set_state_expr(my_id, left, alloc_state_num(assign_id));
116 assign = __alloc_assignment(0);
117 assign->assign_id = assign_id++;
118 assign->name = expr_to_var(left);
119 assign->function = get_fn_name(expr->right);
120 assign->line = get_lineno();
121 add_ptr_list(&assignment_list, assign);
124 static void match_assign(struct expression *expr)
126 struct expression *left;
128 if (expr->op != '=')
129 return;
130 left = strip_expr(expr->left);
131 if (!left || left->type != EXPR_SYMBOL)
132 return;
133 set_state_expr(my_id, left, &undefined);
136 static void delete_used(int assign_id)
138 struct assignment *tmp;
140 FOR_EACH_PTR(assignment_list, tmp) {
141 if (tmp->assign_id == assign_id) {
142 DELETE_CURRENT_PTR(tmp);
143 return;
145 } END_FOR_EACH_PTR(tmp);
148 static void delete_used_symbols(struct state_list *possible)
150 struct sm_state *tmp;
152 FOR_EACH_PTR(possible, tmp) {
153 delete_used(PTR_INT(tmp->state->data));
154 } END_FOR_EACH_PTR(tmp);
157 static void match_symbol(struct expression *expr)
159 struct sm_state *sm;
161 expr = strip_expr(expr);
162 if (expr == skip_this)
163 return;
164 sm = get_sm_state_expr(my_id, expr);
165 if (!sm)
166 return;
167 delete_used_symbols(sm->possible);
168 set_state_expr(my_id, expr, &undefined);
171 static void match_end_func(struct symbol *sym)
173 struct assignment *tmp;
175 if (__inline_fn)
176 return;
177 FOR_EACH_PTR(assignment_list, tmp) {
178 sm_printf("%s:%d %s ", get_filename(), tmp->line, get_function());
179 sm_printf("warn: unused return: %s = %s()\n",
180 tmp->name, tmp->function);
181 } END_FOR_EACH_PTR(tmp);
182 clear_assignment_alloc();
183 __free_ptr_list((struct ptr_list **)&assignment_list);
186 void check_unused_ret(int id)
188 my_id = id;
190 /* It turns out that this test is worthless unless you use --two-passes. */
191 if (!option_two_passes)
192 return;
193 add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK);
194 add_hook(&match_assign, ASSIGNMENT_HOOK);
195 add_hook(&match_symbol, SYM_HOOK);
196 add_hook(&match_end_func, END_FUNC_HOOK);
197 ignored_funcs = create_function_hashtable(100);
198 if (option_project == PROJ_KERNEL) {
199 int i;
201 for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++)
202 insert_func(ignored_funcs, (char *)kernel_ignored[i], (int *)1);