xref: /illumos-gate/usr/src/tools/smatch/src/check_missing_break.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1*1f5207b7SJohn Levon /*
2*1f5207b7SJohn Levon  * Copyright (C) 2013 Oracle.
3*1f5207b7SJohn Levon  *
4*1f5207b7SJohn Levon  * This program is free software; you can redistribute it and/or
5*1f5207b7SJohn Levon  * modify it under the terms of the GNU General Public License
6*1f5207b7SJohn Levon  * as published by the Free Software Foundation; either version 2
7*1f5207b7SJohn Levon  * of the License, or (at your option) any later version.
8*1f5207b7SJohn Levon  *
9*1f5207b7SJohn Levon  * This program is distributed in the hope that it will be useful,
10*1f5207b7SJohn Levon  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11*1f5207b7SJohn Levon  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12*1f5207b7SJohn Levon  * GNU General Public License for more details.
13*1f5207b7SJohn Levon  *
14*1f5207b7SJohn Levon  * You should have received a copy of the GNU General Public License
15*1f5207b7SJohn Levon  * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16*1f5207b7SJohn Levon  */
17*1f5207b7SJohn Levon 
18*1f5207b7SJohn Levon /*
19*1f5207b7SJohn Levon  * The way I'm detecting missing breaks is if there is an assignment inside a
20*1f5207b7SJohn Levon  * switch statement which is over written.
21*1f5207b7SJohn Levon  *
22*1f5207b7SJohn Levon  */
23*1f5207b7SJohn Levon 
24*1f5207b7SJohn Levon #include "smatch.h"
25*1f5207b7SJohn Levon #include "smatch_slist.h"
26*1f5207b7SJohn Levon 
27*1f5207b7SJohn Levon static int my_id;
28*1f5207b7SJohn Levon static struct expression *skip_this;
29*1f5207b7SJohn Levon 
30*1f5207b7SJohn Levon /*
31*1f5207b7SJohn Levon  * It goes like this:
32*1f5207b7SJohn Levon  * - Allocate a state which stores the switch expression.  I wanted to
33*1f5207b7SJohn Levon  *   just have a state &assigned but we need to know the switch statement where
34*1f5207b7SJohn Levon  *   it was assigned.
35*1f5207b7SJohn Levon  * - If it gets used then we change it to &used.
36*1f5207b7SJohn Levon  * - For unmatched states we use &used (because of cleanness, not because we need
37*1f5207b7SJohn Levon  *   to).
38*1f5207b7SJohn Levon  * - If we merge inside a case statement and one of the states is &assigned (or
39*1f5207b7SJohn Levon  *   if it is &nobreak) then &nobreak is used.
40*1f5207b7SJohn Levon  *
41*1f5207b7SJohn Levon  * We print an error when we assign something to a &no_break symbol.
42*1f5207b7SJohn Levon  *
43*1f5207b7SJohn Levon  */
44*1f5207b7SJohn Levon 
45*1f5207b7SJohn Levon STATE(used);
46*1f5207b7SJohn Levon STATE(no_break);
47*1f5207b7SJohn Levon 
48*1f5207b7SJohn Levon static int in_switch_stmt;
49*1f5207b7SJohn Levon 
50*1f5207b7SJohn Levon static struct smatch_state *alloc_my_state(struct expression *expr)
51*1f5207b7SJohn Levon {
52*1f5207b7SJohn Levon 	struct smatch_state *state;
53*1f5207b7SJohn Levon 	char *name;
54*1f5207b7SJohn Levon 
55*1f5207b7SJohn Levon 	state = __alloc_smatch_state(0);
56*1f5207b7SJohn Levon 	expr = strip_expr(expr);
57*1f5207b7SJohn Levon 	name = expr_to_str(expr);
58*1f5207b7SJohn Levon 	if (!name)
59*1f5207b7SJohn Levon 		name = alloc_string("");
60*1f5207b7SJohn Levon 	state->name = alloc_sname(name);
61*1f5207b7SJohn Levon 	free_string(name);
62*1f5207b7SJohn Levon 	state->data = expr;
63*1f5207b7SJohn Levon 	return state;
64*1f5207b7SJohn Levon }
65*1f5207b7SJohn Levon 
66*1f5207b7SJohn Levon struct expression *last_print_expr;
67*1f5207b7SJohn Levon static void print_missing_break(struct expression *expr)
68*1f5207b7SJohn Levon {
69*1f5207b7SJohn Levon 	char *name;
70*1f5207b7SJohn Levon 
71*1f5207b7SJohn Levon 	if (get_switch_expr() == last_print_expr)
72*1f5207b7SJohn Levon 		return;
73*1f5207b7SJohn Levon 	last_print_expr = get_switch_expr();
74*1f5207b7SJohn Levon 
75*1f5207b7SJohn Levon 	name = expr_to_var(expr);
76*1f5207b7SJohn Levon 	sm_warning("missing break? reassigning '%s'", name);
77*1f5207b7SJohn Levon 	free_string(name);
78*1f5207b7SJohn Levon }
79*1f5207b7SJohn Levon 
80*1f5207b7SJohn Levon static void match_assign(struct expression *expr)
81*1f5207b7SJohn Levon {
82*1f5207b7SJohn Levon 	struct expression *left;
83*1f5207b7SJohn Levon 
84*1f5207b7SJohn Levon 	if (expr->op != '=')
85*1f5207b7SJohn Levon 		return;
86*1f5207b7SJohn Levon 	if (!get_switch_expr())
87*1f5207b7SJohn Levon 		return;
88*1f5207b7SJohn Levon 	left = strip_expr(expr->left);
89*1f5207b7SJohn Levon 	if (get_state_expr(my_id, left) == &no_break)
90*1f5207b7SJohn Levon 		print_missing_break(left);
91*1f5207b7SJohn Levon 
92*1f5207b7SJohn Levon 	set_state_expr(my_id, left, alloc_my_state(get_switch_expr()));
93*1f5207b7SJohn Levon 	skip_this = left;
94*1f5207b7SJohn Levon }
95*1f5207b7SJohn Levon 
96*1f5207b7SJohn Levon static void match_symbol(struct expression *expr)
97*1f5207b7SJohn Levon {
98*1f5207b7SJohn Levon 	if (outside_of_function())
99*1f5207b7SJohn Levon 		return;
100*1f5207b7SJohn Levon 	if (!get_switch_expr())
101*1f5207b7SJohn Levon 		return;
102*1f5207b7SJohn Levon 
103*1f5207b7SJohn Levon 	expr = strip_expr(expr);
104*1f5207b7SJohn Levon 	if (expr == skip_this)
105*1f5207b7SJohn Levon 		return;
106*1f5207b7SJohn Levon 	set_state_expr(my_id, expr, &used);
107*1f5207b7SJohn Levon }
108*1f5207b7SJohn Levon 
109*1f5207b7SJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm)
110*1f5207b7SJohn Levon {
111*1f5207b7SJohn Levon 	return &used;
112*1f5207b7SJohn Levon }
113*1f5207b7SJohn Levon 
114*1f5207b7SJohn Levon static int in_case;
115*1f5207b7SJohn Levon static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
116*1f5207b7SJohn Levon {
117*1f5207b7SJohn Levon 	struct expression *switch_expr;
118*1f5207b7SJohn Levon 
119*1f5207b7SJohn Levon 	if (s1 == &no_break || s2 == &no_break)
120*1f5207b7SJohn Levon 		return &no_break;
121*1f5207b7SJohn Levon 	if (!in_case)
122*1f5207b7SJohn Levon 		return &used;
123*1f5207b7SJohn Levon 	switch_expr = get_switch_expr();
124*1f5207b7SJohn Levon 	if (s1->data == switch_expr || s2->data == switch_expr)
125*1f5207b7SJohn Levon 		return &no_break;
126*1f5207b7SJohn Levon 	return &used;
127*1f5207b7SJohn Levon }
128*1f5207b7SJohn Levon 
129*1f5207b7SJohn Levon static void match_stmt(struct statement *stmt)
130*1f5207b7SJohn Levon {
131*1f5207b7SJohn Levon 	if (stmt->type == STMT_CASE)
132*1f5207b7SJohn Levon 		in_case = 1;
133*1f5207b7SJohn Levon 	else
134*1f5207b7SJohn Levon 		in_case = 0;
135*1f5207b7SJohn Levon }
136*1f5207b7SJohn Levon 
137*1f5207b7SJohn Levon static void match_switch(struct statement *stmt)
138*1f5207b7SJohn Levon {
139*1f5207b7SJohn Levon 	if (stmt->type != STMT_SWITCH)
140*1f5207b7SJohn Levon 		return;
141*1f5207b7SJohn Levon 
142*1f5207b7SJohn Levon 	in_switch_stmt++;
143*1f5207b7SJohn Levon }
144*1f5207b7SJohn Levon 
145*1f5207b7SJohn Levon static void delete_my_states(int owner)
146*1f5207b7SJohn Levon {
147*1f5207b7SJohn Levon 	struct state_list *slist = NULL;
148*1f5207b7SJohn Levon 	struct sm_state *sm;
149*1f5207b7SJohn Levon 
150*1f5207b7SJohn Levon 	FOR_EACH_MY_SM(owner, __get_cur_stree(), sm) {
151*1f5207b7SJohn Levon 		add_ptr_list(&slist, sm);
152*1f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
153*1f5207b7SJohn Levon 
154*1f5207b7SJohn Levon 	FOR_EACH_PTR(slist, sm) {
155*1f5207b7SJohn Levon 		delete_state(sm->owner, sm->name, sm->sym);
156*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR(sm);
157*1f5207b7SJohn Levon 
158*1f5207b7SJohn Levon 	free_slist(&slist);
159*1f5207b7SJohn Levon }
160*1f5207b7SJohn Levon 
161*1f5207b7SJohn Levon static void match_switch_end(struct statement *stmt)
162*1f5207b7SJohn Levon {
163*1f5207b7SJohn Levon 
164*1f5207b7SJohn Levon 	if (stmt->type != STMT_SWITCH)
165*1f5207b7SJohn Levon 		return;
166*1f5207b7SJohn Levon 
167*1f5207b7SJohn Levon 	in_switch_stmt--;
168*1f5207b7SJohn Levon 
169*1f5207b7SJohn Levon 	if (!in_switch_stmt)
170*1f5207b7SJohn Levon 		delete_my_states(my_id);
171*1f5207b7SJohn Levon }
172*1f5207b7SJohn Levon 
173*1f5207b7SJohn Levon void check_missing_break(int id)
174*1f5207b7SJohn Levon {
175*1f5207b7SJohn Levon 	my_id = id;
176*1f5207b7SJohn Levon 
177*1f5207b7SJohn Levon 	if (!option_spammy)
178*1f5207b7SJohn Levon 		return;
179*1f5207b7SJohn Levon 
180*1f5207b7SJohn Levon 	add_unmatched_state_hook(my_id, &unmatched_state);
181*1f5207b7SJohn Levon 	add_merge_hook(my_id, &merge_hook);
182*1f5207b7SJohn Levon 
183*1f5207b7SJohn Levon 	add_hook(&match_assign, ASSIGNMENT_HOOK);
184*1f5207b7SJohn Levon 	add_hook(&match_symbol, SYM_HOOK);
185*1f5207b7SJohn Levon 	add_hook(&match_stmt, STMT_HOOK);
186*1f5207b7SJohn Levon 	add_hook(&match_switch, STMT_HOOK);
187*1f5207b7SJohn Levon 	add_hook(&match_switch_end, STMT_HOOK_AFTER);
188*1f5207b7SJohn Levon }
189