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
empty_statement(struct statement * stmt)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
is_last_stmt(struct statement * cur_stmt)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
print_unreached_initializers(struct symbol_list * sym_list)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
is_ignored_macro(struct statement * stmt)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
prev_line_was_endif(struct statement * stmt)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
we_jumped_into_the_middle_of_a_loop(struct statement * stmt)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
unreachable_stmt(struct statement * stmt)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_warning("ignoring unreachable code.");
176 print_unreached = 0;
177 }
178
is_turn_off(char * name)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
get_function_name(struct statement * stmt)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
turn_off_unreachable(struct statement * stmt)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
register_turn_off_macros(void)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
register_ignored_macros(void)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
check_unreachable(int id)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 }
292