1 /*
2  * Copyright (C) 2016 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 struct statement_list *stmt_list;
21 
end_of_function(struct statement * stmt)22 static int end_of_function(struct statement *stmt)
23 {
24 	struct symbol *fn = get_base_type(cur_func_sym);
25 
26 	/* err on the conservative side of things */
27 	if (!fn)
28 		return 1;
29 	if (stmt == fn->stmt || stmt == fn->inline_stmt)
30 		return 1;
31 	return 0;
32 }
33 
34 /*
35  * We're wasting a lot of time worrying about out of scope variables.
36  * When we come to the end of a scope then just delete them all the out of
37  * scope states.
38  */
match_end_of_block(struct statement * stmt)39 static void match_end_of_block(struct statement *stmt)
40 {
41 	struct statement *tmp;
42 	struct symbol *sym;
43 
44 	if (end_of_function(stmt))
45 		return;
46 
47 	FOR_EACH_PTR(stmt->stmts, tmp) {
48 		if (tmp->type != STMT_DECLARATION)
49 			return;
50 
51 		FOR_EACH_PTR(tmp->declaration, sym) {
52 			if (!sym->ident)
53 				continue;
54 			__delete_all_states_sym(sym);
55 		} END_FOR_EACH_PTR(sym);
56 	} END_FOR_EACH_PTR(tmp);
57 }
58 
is_outer_stmt(struct statement * stmt)59 static int is_outer_stmt(struct statement *stmt)
60 {
61 	struct symbol *fn;
62 
63 	if (!cur_func_sym)
64 		return 0;
65 	fn = get_base_type(cur_func_sym);
66 	if (!fn)
67 		return 0;
68 	/*
69 	 * There are times when ->parent is not set but it's set for
70 	 * the outer statement so ignoring NULLs works as a work-around.
71 	 */
72 	if (!stmt->parent)
73 		return 0;
74 	if (stmt->parent == fn->stmt ||
75 	    stmt->parent == fn->inline_stmt)
76 		return 1;
77 	return 0;
78 }
79 
match_stmt(struct statement * stmt)80 static void match_stmt(struct statement *stmt)
81 {
82 	struct statement *tmp;
83 
84 	if (__inline_fn)
85 		return;
86 
87 	if (stmt->type == STMT_COMPOUND)
88 		add_ptr_list(&stmt_list, stmt);
89 
90 	if (!is_outer_stmt(stmt))
91 		return;
92 
93 	FOR_EACH_PTR(stmt_list, tmp) {
94 		match_end_of_block(tmp);
95 	} END_FOR_EACH_PTR(tmp);
96 	free_ptr_list(&stmt_list);
97 }
98 
match_end_func(struct symbol * sym)99 static void match_end_func(struct symbol *sym)
100 {
101 	if (__inline_fn)
102 		return;
103 	free_ptr_list(&stmt_list);
104 }
105 
register_scope(int id)106 void register_scope(int id)
107 {
108 	add_hook(&match_stmt, STMT_HOOK_AFTER);
109 	add_hook(&match_end_func, AFTER_FUNC_HOOK);
110 }
111