1 /* 2 * Copyright (C) 2014 Oracle. 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 #include "smatch.h" 19 20 static int my_id; 21 22 static int print_unreached = 1; 23 static struct string_list *turn_off_names; 24 static struct string_list *ignore_names; 25 26 static int empty_statement(struct statement *stmt) 27 { 28 if (!stmt) 29 return 0; 30 if (stmt->type == STMT_EXPRESSION && !stmt->expression) 31 return 1; 32 return 0; 33 } 34 35 static int is_last_stmt(struct statement *cur_stmt) 36 { 37 struct symbol *fn = get_base_type(cur_func_sym); 38 struct statement *stmt; 39 40 if (!fn) 41 return 0; 42 stmt = fn->stmt; 43 if (!stmt) 44 stmt = fn->inline_stmt; 45 if (!stmt || stmt->type != STMT_COMPOUND) 46 return 0; 47 stmt = last_ptr_list((struct ptr_list *)stmt->stmts); 48 if (stmt == cur_stmt) 49 return 1; 50 return 0; 51 } 52 53 static void print_unreached_initializers(struct symbol_list *sym_list) 54 { 55 struct symbol *sym; 56 57 FOR_EACH_PTR(sym_list, sym) { 58 if (sym->initializer && !(sym->ctype.modifiers & MOD_STATIC)) 59 sm_msg("info: '%s' is not actually initialized (unreached code).", 60 (sym->ident ? sym->ident->name : "this variable")); 61 } END_FOR_EACH_PTR(sym); 62 } 63 64 static int is_ignored_macro(struct statement *stmt) 65 { 66 char *name; 67 char *tmp; 68 69 name = get_macro_name(stmt->pos); 70 if (!name) 71 return 0; 72 73 FOR_EACH_PTR(ignore_names, tmp) { 74 if (strcmp(tmp, name) == 0) 75 return 1; 76 } END_FOR_EACH_PTR(tmp); 77 78 return 0; 79 } 80 81 static int prev_line_was_endif(struct statement *stmt) 82 { 83 struct token *token; 84 struct position pos = stmt->pos; 85 86 pos.line--; 87 pos.pos = 2; 88 89 token = pos_get_token(pos); 90 if (token && token_type(token) == TOKEN_IDENT && 91 strcmp(show_ident(token->ident), "endif") == 0) 92 return 1; 93 94 pos.line--; 95 token = pos_get_token(pos); 96 if (token && token_type(token) == TOKEN_IDENT && 97 strcmp(show_ident(token->ident), "endif") == 0) 98 return 1; 99 100 return 0; 101 } 102 103 static int we_jumped_into_the_middle_of_a_loop(struct statement *stmt) 104 { 105 struct statement *prev; 106 107 /* 108 * Smatch doesn't handle loops correctly and this is a hack. What we 109 * do is that if the first unreachable statement is a loop and the 110 * previous statement was a goto then it's probably code like this: 111 * goto first; 112 * for (;;) { 113 * frob(); 114 * first: 115 * more_frob(); 116 * } 117 * Every statement is reachable but only on the second iteration. 118 */ 119 120 if (stmt->type != STMT_ITERATOR) 121 return 0; 122 prev = get_prev_statement(); 123 if (prev && prev->type == STMT_GOTO) 124 return 1; 125 return 0; 126 } 127 128 static void unreachable_stmt(struct statement *stmt) 129 { 130 131 if (__inline_fn) 132 return; 133 134 if (!__path_is_null()) { 135 print_unreached = 1; 136 return; 137 } 138 139 /* if we hit a label then assume there is a matching goto */ 140 if (stmt->type == STMT_LABEL) 141 print_unreached = 0; 142 if (prev_line_was_endif(stmt)) 143 print_unreached = 0; 144 if (we_jumped_into_the_middle_of_a_loop(stmt)) 145 print_unreached = 0; 146 147 if (!print_unreached) 148 return; 149 if (empty_statement(stmt)) 150 return; 151 if (is_ignored_macro(stmt)) 152 return; 153 154 switch (stmt->type) { 155 case STMT_COMPOUND: /* after a switch before a case stmt */ 156 case STMT_RANGE: 157 case STMT_CASE: 158 return; 159 case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */ 160 print_unreached_initializers(stmt->declaration); 161 return; 162 case STMT_RETURN: /* gcc complains if you don't have a return statement */ 163 if (is_last_stmt(stmt)) 164 return; 165 break; 166 case STMT_GOTO: 167 /* people put extra breaks inside switch statements */ 168 if (stmt->goto_label && stmt->goto_label->type == SYM_NODE && 169 strcmp(stmt->goto_label->ident->name, "break") == 0) 170 return; 171 break; 172 default: 173 break; 174 } 175 sm_msg("info: ignoring unreachable code."); 176 print_unreached = 0; 177 } 178 179 static int is_turn_off(char *name) 180 { 181 char *tmp; 182 183 if (!name) 184 return 0; 185 186 FOR_EACH_PTR(turn_off_names, tmp) { 187 if (strcmp(tmp, name) == 0) 188 return 1; 189 } END_FOR_EACH_PTR(tmp); 190 191 return 0; 192 } 193 194 static char *get_function_name(struct statement *stmt) 195 { 196 struct expression *expr; 197 198 if (stmt->type != STMT_EXPRESSION) 199 return NULL; 200 expr = stmt->expression; 201 if (!expr || expr->type != EXPR_CALL) 202 return NULL; 203 if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name) 204 return NULL; 205 return expr->fn->symbol_name->name; 206 } 207 208 static void turn_off_unreachable(struct statement *stmt) 209 { 210 char *name; 211 212 name = get_macro_name(stmt->pos); 213 if (is_turn_off(name)) { 214 print_unreached = 0; 215 return; 216 } 217 218 if (stmt->type == STMT_IF && 219 known_condition_true(stmt->if_conditional) && __path_is_null()) { 220 print_unreached = 0; 221 return; 222 } 223 224 name = get_function_name(stmt); 225 if (is_turn_off(name)) 226 print_unreached = 0; 227 } 228 229 static void register_turn_off_macros(void) 230 { 231 struct token *token; 232 char *macro; 233 char name[256]; 234 235 if (option_project == PROJ_NONE) 236 strcpy(name, "unreachable.turn_off"); 237 else 238 snprintf(name, 256, "%s.unreachable.turn_off", option_project_str); 239 240 token = get_tokens_file(name); 241 if (!token) 242 return; 243 if (token_type(token) != TOKEN_STREAMBEGIN) 244 return; 245 token = token->next; 246 while (token_type(token) != TOKEN_STREAMEND) { 247 if (token_type(token) != TOKEN_IDENT) 248 return; 249 macro = alloc_string(show_ident(token->ident)); 250 add_ptr_list(&turn_off_names, macro); 251 token = token->next; 252 } 253 clear_token_alloc(); 254 } 255 256 static void register_ignored_macros(void) 257 { 258 struct token *token; 259 char *macro; 260 char name[256]; 261 262 if (option_project == PROJ_NONE) 263 strcpy(name, "unreachable.ignore"); 264 else 265 snprintf(name, 256, "%s.unreachable.ignore", option_project_str); 266 267 token = get_tokens_file(name); 268 if (!token) 269 return; 270 if (token_type(token) != TOKEN_STREAMBEGIN) 271 return; 272 token = token->next; 273 while (token_type(token) != TOKEN_STREAMEND) { 274 if (token_type(token) != TOKEN_IDENT) 275 return; 276 macro = alloc_string(show_ident(token->ident)); 277 add_ptr_list(&ignore_names, macro); 278 token = token->next; 279 } 280 clear_token_alloc(); 281 } 282 283 void check_unreachable(int id) 284 { 285 my_id = id; 286 287 register_turn_off_macros(); 288 register_ignored_macros(); 289 add_hook(&unreachable_stmt, STMT_HOOK); 290 add_hook(&turn_off_unreachable, STMT_HOOK_AFTER); 291 }