unreachable: add some more macros to the .ignore and .turn_off files
[smatch.git] / check_unreachable.c
blob3b6f4d017d1e4731727f83e3269e9ffc4240573c
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 int empty_statement(struct statement *stmt)
28 if (!stmt)
29 return 0;
30 if (stmt->type == STMT_EXPRESSION && !stmt->expression)
31 return 1;
32 return 0;
35 static int is_last_stmt(struct statement *cur_stmt)
37 struct symbol *fn = get_base_type(cur_func_sym);
38 struct statement *stmt;
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;
53 static void print_unreached_initializers(struct symbol_list *sym_list)
55 struct symbol *sym;
57 FOR_EACH_PTR(sym_list, sym) {
58 if (sym->initializer)
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);
64 static int is_ignored_macro(struct statement *stmt)
66 char *name;
67 char *tmp;
69 name = get_macro_name(stmt->pos);
70 if (!name)
71 return 0;
73 FOR_EACH_PTR(ignore_names, tmp) {
74 if (strcmp(tmp, name) == 0)
75 return 1;
76 } END_FOR_EACH_PTR(tmp);
78 return 0;
81 static void unreachable_stmt(struct statement *stmt)
84 if (__inline_fn)
85 return;
87 if (!__path_is_null()) {
88 print_unreached = 1;
89 return;
91 if (!print_unreached)
92 return;
94 switch (stmt->type) {
95 case STMT_COMPOUND: /* after a switch before a case stmt */
96 case STMT_RANGE:
97 case STMT_CASE:
98 case STMT_LABEL:
99 return;
100 case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */
101 print_unreached_initializers(stmt->declaration);
102 return;
103 case STMT_RETURN: /* gcc complains if you don't have a return statement */
104 if (is_last_stmt(stmt))
105 return;
106 break;
107 case STMT_GOTO:
108 /* people put extra breaks inside switch statements */
109 if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
110 strcmp(stmt->goto_label->ident->name, "break") == 0)
111 return;
112 break;
113 default:
114 break;
116 if (empty_statement(stmt))
117 return;
118 if (is_ignored_macro(stmt))
119 return;
120 if (!option_spammy)
121 return;
122 sm_msg("info: ignoring unreachable code.");
123 print_unreached = 0;
126 static int is_turn_off(char *name)
128 char *tmp;
130 if (!name)
131 return 0;
133 FOR_EACH_PTR(turn_off_names, tmp) {
134 if (strcmp(tmp, name) == 0)
135 return 1;
136 } END_FOR_EACH_PTR(tmp);
138 return 0;
141 static char *get_function_name(struct statement *stmt)
143 struct expression *expr;
145 if (stmt->type != STMT_EXPRESSION)
146 return NULL;
147 expr = stmt->expression;
148 if (!expr || expr->type != EXPR_CALL)
149 return NULL;
150 if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
151 return NULL;
152 return expr->fn->symbol_name->name;
155 static void turn_off_unreachable(struct statement *stmt)
157 char *name;
159 name = get_macro_name(stmt->pos);
160 if (is_turn_off(name)) {
161 print_unreached = 0;
162 return;
165 if (stmt->type == STMT_IF &&
166 known_condition_true(stmt->if_conditional) && __path_is_null()) {
167 print_unreached = 0;
168 return;
171 name = get_function_name(stmt);
172 if (is_turn_off(name))
173 print_unreached = 0;
176 static void register_turn_off_macros(void)
178 struct token *token;
179 char *macro;
180 char name[256];
182 if (option_project == PROJ_NONE)
183 strcpy(name, "unreachable.turn_off");
184 else
185 snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
187 token = get_tokens_file(name);
188 if (!token)
189 return;
190 if (token_type(token) != TOKEN_STREAMBEGIN)
191 return;
192 token = token->next;
193 while (token_type(token) != TOKEN_STREAMEND) {
194 if (token_type(token) != TOKEN_IDENT)
195 return;
196 macro = alloc_string(show_ident(token->ident));
197 add_ptr_list(&turn_off_names, macro);
198 token = token->next;
200 clear_token_alloc();
203 static void register_ignored_macros(void)
205 struct token *token;
206 char *macro;
207 char name[256];
209 if (option_project == PROJ_NONE)
210 strcpy(name, "unreachable.ignore");
211 else
212 snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
214 token = get_tokens_file(name);
215 if (!token)
216 return;
217 if (token_type(token) != TOKEN_STREAMBEGIN)
218 return;
219 token = token->next;
220 while (token_type(token) != TOKEN_STREAMEND) {
221 if (token_type(token) != TOKEN_IDENT)
222 return;
223 macro = alloc_string(show_ident(token->ident));
224 add_ptr_list(&ignore_names, macro);
225 token = token->next;
227 clear_token_alloc();
230 void check_unreachable(int id)
232 my_id = id;
234 register_turn_off_macros();
235 register_ignored_macros();
236 add_hook(&unreachable_stmt, STMT_HOOK);
237 add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);