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