db/fixup_kernel.sh: fix kstrtoint() functions
[smatch.git] / check_unreachable.c
blob0b0b28f5c5ced30b85bf1b2e1927348ce3fe278f
1 /*
2 * Copyright (C) 2014 Oracle.
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.
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.
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
18 #include "smatch.h"
20 static int my_id;
22 static int print_unreached = 1;
23 static struct string_list *turn_off_names;
24 static struct string_list *ignore_names;
26 static bool empty_statement(struct statement *stmt)
28 if (!stmt)
29 return false;
30 if (stmt->type == STMT_EXPRESSION && !stmt->expression)
31 return true;
32 return false;
35 static void print_unreached_initializers(struct symbol_list *sym_list)
37 struct symbol *sym;
39 FOR_EACH_PTR(sym_list, sym) {
40 if (sym->initializer && !(sym->ctype.modifiers & MOD_STATIC))
41 sm_msg("info: '%s' is not actually initialized (unreached code).",
42 (sym->ident ? sym->ident->name : "this variable"));
43 } END_FOR_EACH_PTR(sym);
46 static bool is_ignored_macro(struct statement *stmt)
48 char *name;
49 char *tmp;
51 name = get_macro_name(stmt->pos);
52 if (!name)
53 return false;
55 if (strncmp(name, "for_", 4) == 0)
56 return true;
58 FOR_EACH_PTR(ignore_names, tmp) {
59 if (strcmp(tmp, name) == 0)
60 return true;
61 } END_FOR_EACH_PTR(tmp);
63 return false;
66 static bool prev_line_was_endif(struct statement *stmt)
68 struct token *token;
69 struct position pos = stmt->pos;
71 pos.line--;
72 pos.pos = 2;
74 token = pos_get_token(pos);
75 if (token && token_type(token) == TOKEN_IDENT &&
76 strcmp(show_ident(token->ident), "endif") == 0)
77 return true;
79 pos.line--;
80 token = pos_get_token(pos);
81 if (token && token_type(token) == TOKEN_IDENT &&
82 strcmp(show_ident(token->ident), "endif") == 0)
83 return true;
85 return false;
88 static bool we_jumped_into_the_middle_of_a_loop(struct statement *stmt)
90 struct statement *prev;
93 * Smatch doesn't handle loops correctly and this is a hack. What we
94 * do is that if the first unreachable statement is a loop and the
95 * previous statement was a goto then it's probably code like this:
96 * goto first;
97 * for (;;) {
98 * frob();
99 * first:
100 * more_frob();
102 * Every statement is reachable but only on the second iteration.
105 if (stmt->type != STMT_ITERATOR)
106 return false;
107 prev = get_prev_statement();
108 if (prev && prev->type == STMT_GOTO)
109 return true;
110 return false;
113 static void unreachable_stmt(struct statement *stmt)
116 if (__inline_fn)
117 return;
119 if (!__path_is_null()) {
120 print_unreached = 1;
121 return;
124 /* if we hit a label then assume there is a matching goto */
125 if (stmt->type == STMT_LABEL)
126 print_unreached = 0;
127 if (prev_line_was_endif(stmt))
128 print_unreached = 0;
129 if (we_jumped_into_the_middle_of_a_loop(stmt))
130 print_unreached = 0;
132 if (!print_unreached)
133 return;
134 if (empty_statement(stmt))
135 return;
136 if (is_ignored_macro(stmt))
137 return;
139 switch (stmt->type) {
140 case STMT_COMPOUND: /* after a switch before a case stmt */
141 case STMT_RANGE:
142 case STMT_CASE:
143 return;
144 case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */
145 print_unreached_initializers(stmt->declaration);
146 return;
147 case STMT_RETURN: /* gcc complains if you don't have a return statement */
148 if (is_last_stmt(stmt))
149 return;
150 break;
151 case STMT_GOTO:
152 /* people put extra breaks inside switch statements */
153 if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
154 strcmp(stmt->goto_label->ident->name, "break") == 0)
155 return;
156 break;
157 default:
158 break;
160 sm_warning("ignoring unreachable code.");
161 print_unreached = 0;
164 static bool is_turn_off(char *name)
166 char *tmp;
168 if (!name)
169 return false;
171 FOR_EACH_PTR(turn_off_names, tmp) {
172 if (strcmp(tmp, name) == 0)
173 return true;
174 } END_FOR_EACH_PTR(tmp);
176 return false;
179 static char *get_function_name(struct statement *stmt)
181 struct expression *expr;
183 if (stmt->type != STMT_EXPRESSION)
184 return NULL;
185 expr = stmt->expression;
186 if (!expr || expr->type != EXPR_CALL)
187 return NULL;
188 if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
189 return NULL;
190 return expr->fn->symbol_name->name;
193 static void turn_off_unreachable(struct statement *stmt)
195 char *name;
197 name = get_macro_name(stmt->pos);
198 if (is_turn_off(name)) {
199 print_unreached = 0;
200 return;
203 if (stmt->type == STMT_IF &&
204 known_condition_true(stmt->if_conditional) && __path_is_null()) {
205 print_unreached = 0;
206 return;
209 name = get_function_name(stmt);
210 if (is_turn_off(name))
211 print_unreached = 0;
214 static void register_turn_off_macros(void)
216 struct token *token;
217 char *macro;
218 char name[256];
220 if (option_project == PROJ_NONE)
221 strcpy(name, "unreachable.turn_off");
222 else
223 snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
225 token = get_tokens_file(name);
226 if (!token)
227 return;
228 if (token_type(token) != TOKEN_STREAMBEGIN)
229 return;
230 token = token->next;
231 while (token_type(token) != TOKEN_STREAMEND) {
232 if (token_type(token) != TOKEN_IDENT)
233 return;
234 macro = alloc_string(show_ident(token->ident));
235 add_ptr_list(&turn_off_names, macro);
236 token = token->next;
238 clear_token_alloc();
241 static void register_ignored_macros(void)
243 struct token *token;
244 char *macro;
245 char name[256];
247 if (option_project == PROJ_NONE)
248 strcpy(name, "unreachable.ignore");
249 else
250 snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
252 token = get_tokens_file(name);
253 if (!token)
254 return;
255 if (token_type(token) != TOKEN_STREAMBEGIN)
256 return;
257 token = token->next;
258 while (token_type(token) != TOKEN_STREAMEND) {
259 if (token_type(token) != TOKEN_IDENT)
260 return;
261 macro = alloc_string(show_ident(token->ident));
262 add_ptr_list(&ignore_names, macro);
263 token = token->next;
265 clear_token_alloc();
268 void check_unreachable(int id)
270 my_id = id;
272 register_turn_off_macros();
273 register_ignored_macros();
274 add_hook(&unreachable_stmt, STMT_HOOK);
275 add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);