2 * sparse/check_unused_ret.c
4 * Copyright (C) 2009 Dan Carpenter.
6 * Licensed under the Open Software License version 1.1
11 * This check is supposed to find places like this:
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
30 * Also this state is quite tied to the order the callbacks
31 * are called in smatch_flow.c. (If the order changed it
39 #include "smatch_slist.h"
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
;
55 static struct hsearch_data ignored_funcs
;
56 static const char *kernel_ignored
[] = {
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
);
70 state
->name
= alloc_string(buff
);
71 state
->data
= (void *) assign_id
;
75 static int ignored_function(struct expression
*func
)
79 func
= strip_expr(func
);
80 if (!func
|| func
->type
!= EXPR_CALL
)
83 if (!func
|| func
->type
!= EXPR_SYMBOL
)
86 e
.key
= func
->symbol_name
->name
;
87 hsearch_r(e
, FIND
, &ep
, &ignored_funcs
);
93 static void match_assign_call(struct expression
*expr
)
95 struct expression
*left
;
96 struct assignment
*assign
;
106 if (ignored_function(expr
->right
))
108 left
= strip_expr(expr
->left
);
109 if (!left
|| left
->type
!= EXPR_SYMBOL
)
111 if (left
->symbol
->ctype
.modifiers
& (MOD_TOPLEVEL
| MOD_EXTERN
| MOD_STATIC
))
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
;
131 left
= strip_expr(expr
->left
);
132 if (!left
|| left
->type
!= EXPR_SYMBOL
)
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
);
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
)
162 expr
= strip_expr(expr
);
163 if (expr
== skip_this
)
165 sm
= get_sm_state_expr(my_id
, expr
);
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
)
188 /* It turns out that this test is worthless unless you use --two-passes. */
189 if (!option_two_passes
)
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
) {
199 for (i
= 0; i
< ARRAY_SIZE(kernel_ignored
); i
++) {
202 e
.key
= (char *)kernel_ignored
[i
];
204 if (!hsearch_r(e
, ENTER
, &ep
, &ignored_funcs
)) {
205 printf("check_unused_ret.c broke.\n");