capped: fix handling of assignments
[smatch.git] / check_unreachable.c
blobf245f868507922c8f61df321ef958da7687ca72a
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 bool prev_stmt_false_if(void)
115 struct statement *prev;
117 /* true if statements are handled in turn_off_unreachable() */
119 if (!__path_is_null())
120 return false;
122 prev = get_prev_statement();
123 if (!prev || prev->type != STMT_IF)
124 return false;
126 if (known_condition_false(prev->if_conditional))
127 return true;
128 return false;
131 static void unreachable_stmt(struct statement *stmt)
134 if (__inline_fn)
135 return;
137 if (!__path_is_null()) {
138 print_unreached = 1;
139 return;
142 /* if we hit a label then assume there is a matching goto */
143 if (stmt->type == STMT_LABEL)
144 print_unreached = 0;
145 if (prev_line_was_endif(stmt))
146 print_unreached = 0;
147 if (we_jumped_into_the_middle_of_a_loop(stmt))
148 print_unreached = 0;
149 if (prev_stmt_false_if())
150 print_unreached = 0;
152 if (!print_unreached)
153 return;
154 if (empty_statement(stmt))
155 return;
156 if (is_ignored_macro(stmt))
157 return;
159 switch (stmt->type) {
160 case STMT_COMPOUND: /* after a switch before a case stmt */
161 case STMT_RANGE:
162 case STMT_CASE:
163 return;
164 case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */
165 print_unreached_initializers(stmt->declaration);
166 return;
167 case STMT_RETURN: /* gcc complains if you don't have a return statement */
168 if (is_last_stmt(stmt))
169 return;
170 break;
171 case STMT_GOTO:
172 /* people put extra breaks inside switch statements */
173 if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
174 strcmp(stmt->goto_label->ident->name, "break") == 0)
175 return;
176 break;
177 default:
178 break;
180 sm_warning("ignoring unreachable code.");
181 print_unreached = 0;
184 static bool is_turn_off(char *name)
186 char *tmp;
188 if (!name)
189 return false;
191 FOR_EACH_PTR(turn_off_names, tmp) {
192 if (strcmp(tmp, name) == 0)
193 return true;
194 } END_FOR_EACH_PTR(tmp);
196 return false;
199 static char *get_function_name(struct statement *stmt)
201 struct expression *expr;
203 if (stmt->type != STMT_EXPRESSION)
204 return NULL;
205 expr = stmt->expression;
206 if (!expr || expr->type != EXPR_CALL)
207 return NULL;
208 if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
209 return NULL;
210 return expr->fn->symbol_name->name;
213 static void turn_off_unreachable(struct statement *stmt)
215 char *name;
217 name = get_macro_name(stmt->pos);
218 if (is_turn_off(name)) {
219 print_unreached = 0;
220 return;
223 if (stmt->type == STMT_IF &&
224 known_condition_true(stmt->if_conditional) && __path_is_null()) {
225 print_unreached = 0;
226 return;
229 name = get_function_name(stmt);
230 if (is_turn_off(name))
231 print_unreached = 0;
234 static void register_turn_off_macros(void)
236 struct token *token;
237 char *macro;
238 char name[256];
240 if (option_project == PROJ_NONE)
241 strcpy(name, "unreachable.turn_off");
242 else
243 snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
245 token = get_tokens_file(name);
246 if (!token)
247 return;
248 if (token_type(token) != TOKEN_STREAMBEGIN)
249 return;
250 token = token->next;
251 while (token_type(token) != TOKEN_STREAMEND) {
252 if (token_type(token) != TOKEN_IDENT)
253 return;
254 macro = alloc_string(show_ident(token->ident));
255 add_ptr_list(&turn_off_names, macro);
256 token = token->next;
258 clear_token_alloc();
261 static void register_ignored_macros(void)
263 struct token *token;
264 char *macro;
265 char name[256];
267 if (option_project == PROJ_NONE)
268 strcpy(name, "unreachable.ignore");
269 else
270 snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
272 token = get_tokens_file(name);
273 if (!token)
274 return;
275 if (token_type(token) != TOKEN_STREAMBEGIN)
276 return;
277 token = token->next;
278 while (token_type(token) != TOKEN_STREAMEND) {
279 if (token_type(token) != TOKEN_IDENT)
280 return;
281 macro = alloc_string(show_ident(token->ident));
282 add_ptr_list(&ignore_names, macro);
283 token = token->next;
285 clear_token_alloc();
288 void check_unreachable(int id)
290 my_id = id;
292 register_turn_off_macros();
293 register_ignored_macros();
294 add_hook(&unreachable_stmt, STMT_HOOK);
295 add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);