1 /* 2 * Copyright (C) 2009 Dan Carpenter. 3 * 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. 8 * 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. 13 * 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 16 */ 17 18 /* 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) 25 * 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. 32 * 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. 37 * 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). 41 * 42 */ 43 44 #include "smatch.h" 45 #include "smatch_slist.h" 46 #include "smatch_function_hashtable.h" 47 48 static int my_id; 49 50 struct assignment { 51 int assign_id; 52 char *name; 53 char *function; 54 int line; 55 }; 56 ALLOCATOR(assignment, "assignment id"); 57 DECLARE_PTR_LIST(assignment_list, struct assignment); 58 static struct assignment_list *assignment_list; 59 60 static struct expression *skip_this; 61 static int assign_id; 62 63 static DEFINE_HASHTABLE_INSERT(insert_func, char, int); 64 static DEFINE_HASHTABLE_SEARCH(search_func, char, int); 65 static struct hashtable *ignored_funcs; 66 67 static const char *kernel_ignored[] = { 68 "inb", 69 "inl", 70 "inw", 71 "readb", 72 "readl", 73 "readw", 74 }; 75 76 static char *get_fn_name(struct expression *expr) 77 { 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); 83 } 84 85 static int ignored_function(struct expression *expr) 86 { 87 char *func; 88 int ret = 0; 89 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; 97 } 98 99 static void match_assign_call(struct expression *expr) 100 { 101 struct expression *left; 102 struct assignment *assign; 103 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; 119 120 skip_this = left; 121 122 set_state_expr(my_id, left, alloc_state_num(assign_id)); 123 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); 130 } 131 132 static void match_assign(struct expression *expr) 133 { 134 struct expression *left; 135 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); 142 } 143 144 static void delete_used(int assign_id) 145 { 146 struct assignment *tmp; 147 148 FOR_EACH_PTR(assignment_list, tmp) { 149 if (tmp->assign_id == assign_id) { 150 DELETE_CURRENT_PTR(tmp); 151 return; 152 } 153 } END_FOR_EACH_PTR(tmp); 154 } 155 156 static void delete_used_symbols(struct state_list *possible) 157 { 158 struct sm_state *tmp; 159 160 FOR_EACH_PTR(possible, tmp) { 161 delete_used(PTR_INT(tmp->state->data)); 162 } END_FOR_EACH_PTR(tmp); 163 } 164 165 static void match_symbol(struct expression *expr) 166 { 167 struct sm_state *sm; 168 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); 177 } 178 179 static void match_end_func(struct symbol *sym) 180 { 181 struct assignment *tmp; 182 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 } 191 192 static void match_after_func(struct symbol *sym) 193 { 194 if (__inline_fn) 195 return; 196 clear_assignment_alloc(); 197 __free_ptr_list((struct ptr_list **)&assignment_list); 198 } 199 200 void check_unused_ret(int id) 201 { 202 my_id = id; 203 204 /* It turns out that this test is worthless unless you use --two-passes. */ 205 if (!option_two_passes) 206 return; 207 add_hook(&match_assign_call, CALL_ASSIGNMENT_HOOK); 208 add_hook(&match_assign, ASSIGNMENT_HOOK); 209 add_hook(&match_symbol, SYM_HOOK); 210 add_hook(&match_end_func, END_FUNC_HOOK); 211 add_hook(&match_after_func, AFTER_FUNC_HOOK); 212 ignored_funcs = create_function_hashtable(100); 213 if (option_project == PROJ_KERNEL) { 214 int i; 215 216 for (i = 0; i < ARRAY_SIZE(kernel_ignored); i++) 217 insert_func(ignored_funcs, (char *)kernel_ignored[i], (int *)1); 218 } 219 } 220