expressions: add cast_expression()
[smatch.git] / check_direct_return_instead_of_goto.c
blobb075bc2f8691ac2de8e82305d4a449e0fadd4b1b
1 /*
2 * Copyright (C) 2022 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"
19 #include "smatch_extra.h"
20 #include "smatch_slist.h"
22 static int my_id;
24 static struct statement *goto_stmt, *next_goto;
25 static struct expression *return_expr;
27 static unsigned long set_label;
28 static unsigned long label_cnt;
30 static void reset(void)
32 goto_stmt = NULL;
33 next_goto = NULL;
34 return_expr = NULL;
37 static bool is_do_nothing_goto(struct statement *goto_stmt)
39 struct symbol *fn;
40 struct statement *stmt;
42 fn = get_base_type(cur_func_sym);
43 if (!fn)
44 return false;
45 stmt = fn->stmt;
46 if (!stmt)
47 stmt = fn->inline_stmt;
48 if (!stmt || stmt->type != STMT_COMPOUND)
49 return false;
50 stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
51 if (!stmt)
52 return false;
53 if (stmt->type != STMT_LABEL)
54 return false;
56 if (!stmt->label_identifier ||
57 stmt->label_identifier->type != SYM_LABEL ||
58 !stmt->label_identifier->ident)
59 return false;
61 if (strcmp(stmt->label_identifier->ident->name,
62 goto_stmt->goto_label->ident->name) == 0)
63 return true;
65 return false;
68 static bool is_printk_stmt(struct statement *stmt)
70 char *str;
72 if (!stmt)
73 return false;
75 str = pos_ident(stmt->pos);
76 if (!str)
77 return false;
79 if (strcmp(str, "dev_err") == 0 ||
80 strcmp(str, "dev_info") == 0 ||
81 strcmp(str, "dev_warn") == 0 ||
82 strcmp(str, "dev_warn") == 0 ||
83 strcmp(str, "dev_notice") == 0 ||
84 strcmp(str, "dev_dbg") == 0)
85 return true;
87 if (strcmp(str, "pr_err") == 0 ||
88 strcmp(str, "pr_info") == 0 ||
89 strcmp(str, "pr_warn") == 0 ||
90 strcmp(str, "pr_warn") == 0 ||
91 strcmp(str, "pr_notice") == 0 ||
92 strcmp(str, "pr_debug") == 0)
93 return true;
95 return false;
98 static bool label_name_matches(struct statement *goto_stmt,
99 struct statement *stmt)
101 if (!stmt)
102 return false;
104 if (stmt->type != STMT_LABEL)
105 return false;
107 if (!stmt->label_identifier ||
108 stmt->label_identifier->type != SYM_LABEL ||
109 !stmt->label_identifier->ident)
110 return false;
112 if (strcmp(stmt->label_identifier->ident->name,
113 goto_stmt->goto_label->ident->name) == 0)
114 return true;
116 return false;
119 static bool is_printk_goto(struct statement *goto_stmt)
121 struct symbol *fn;
122 struct statement *stmt, *tmp;
124 fn = get_base_type(cur_func_sym);
125 if (!fn)
126 return false;
127 stmt = fn->stmt;
128 if (!stmt)
129 stmt = fn->inline_stmt;
130 if (!stmt || stmt->type != STMT_COMPOUND)
131 return false;
133 FOR_EACH_PTR_REVERSE(stmt->stmts, tmp) {
134 if (tmp->type == STMT_RETURN)
135 continue;
136 if (tmp->type == STMT_LABEL &&
137 !is_printk_stmt(tmp->label_statement))
138 return false;
139 if (is_printk_stmt(tmp))
140 continue;
141 if (label_name_matches(goto_stmt, tmp))
142 return true;
143 return false;
144 } END_FOR_EACH_PTR_REVERSE(tmp);
146 return false;
149 static void match_goto(struct statement *stmt)
151 /* Find the first goto */
153 if (next_goto)
154 return;
156 if (stmt->type != STMT_GOTO ||
157 !stmt->goto_label ||
158 stmt->goto_label->type != SYM_LABEL ||
159 !stmt->goto_label->ident)
160 return;
162 if (is_do_nothing_goto(stmt))
163 return;
165 if (is_printk_goto(stmt))
166 return;
168 goto_stmt = stmt;
171 static void match_return(struct expression *expr)
173 struct range_list *rl;
175 if (next_goto)
176 return;
177 if (!goto_stmt)
178 return;
180 if (!expr ||
181 !get_implied_rl(expr, &rl) ||
182 success_fail_return(rl) != RET_FAIL) {
183 reset();
184 return;
187 return_expr = expr;
190 static void match_next_goto(struct statement *stmt)
192 if (next_goto)
193 return;
195 if (stmt->type != STMT_GOTO ||
196 !stmt->goto_label ||
197 stmt->goto_label->type != SYM_LABEL ||
198 !stmt->goto_label->ident)
199 return;
201 if (!goto_stmt || !return_expr) {
202 reset();
203 return;
206 next_goto = stmt;
209 static void add_label_cnt(struct statement *stmt)
211 if (!next_goto) {
212 reset();
213 return;
216 if (!stmt ||
217 !stmt->label_identifier ||
218 stmt->label_identifier->type != SYM_LABEL ||
219 !stmt->label_identifier->ident)
220 return;
222 if (strcmp(stmt->label_identifier->ident->name,
223 goto_stmt->goto_label->ident->name) == 0)
224 label_cnt++;
226 if (strcmp(stmt->label_identifier->ident->name,
227 next_goto->goto_label->ident->name) == 0)
228 label_cnt++;
231 static void match_label(struct statement *stmt)
233 sval_t sval;
235 if (stmt->type != STMT_LABEL)
236 return;
238 if (get_state(my_id, "cleanup", NULL) == &true_state) {
239 /* The second label in a cleanup block is still cleanup. */
240 set_label = true;
241 add_label_cnt(stmt);
242 return;
246 * If the cleanup block isn't preceded by a "return 0;" then it could
247 * be a success path.
250 if (__get_cur_stree())
251 return;
252 if (!__prev_stmt || __prev_stmt->type != STMT_RETURN)
253 return;
254 if (!get_implied_value(__prev_stmt->ret_value, &sval) || sval.value != 0)
255 return;
256 /* We can't set the state until after we've merged the gotos. */
257 set_label = true;
258 add_label_cnt(stmt);
261 static void match_label_after(struct statement *stmt)
263 if (stmt->type != STMT_LABEL)
264 return;
265 if (set_label) {
266 set_state(my_id, "cleanup", NULL, &true_state);
267 set_label = false;
271 static void match_final_return(struct expression *expr)
273 struct range_list *rl;
275 if (!expr)
276 return;
277 if (!next_goto)
278 return;
279 if (label_cnt != 2)
280 return;
282 if (get_state(my_id, "cleanup", NULL) != &true_state)
283 return;
285 if (!get_implied_rl(expr, &rl))
286 return;
287 if (success_fail_return(rl) != RET_FAIL)
288 return;
290 sm_warning_line(return_expr->pos.line, "missing unwind goto?");
293 void check_direct_return_instead_of_goto(int id)
295 my_id = id;
297 if (option_project != PROJ_KERNEL)
298 return;
300 add_function_data(&set_label);
301 add_function_data(&label_cnt);
303 add_function_data((unsigned long *)&goto_stmt);
304 add_function_data((unsigned long *)&return_expr);
305 add_function_data((unsigned long *)&next_goto);
307 // check them in reverse order
308 add_hook(&match_next_goto, STMT_HOOK);
309 add_hook(&match_return, RETURN_HOOK);
310 add_hook(&match_goto, STMT_HOOK);
311 add_hook(&match_final_return, RETURN_HOOK);
314 add_hook(&match_label, STMT_HOOK);
315 add_hook(&match_label_after, STMT_HOOK_AFTER);