extra: don't overwrite non-null pointers because of a dereference
[smatch.git] / check_missing_break.c
blob434b7283fc9473c53de4edb3da55e97dee6f28dd
1 /*
2 * Copyright (C) 2013 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
19 * The way I'm detecting missing breaks is if there is an assignment inside a
20 * switch statement which is over written.
24 #include "smatch.h"
25 #include "smatch_slist.h"
27 static int my_id;
28 static struct expression *skip_this;
31 * It goes like this:
32 * - Allocate a state which stores the switch expression. I wanted to
33 * just have a state &assigned but we need to know the switch statement where
34 * it was assigned.
35 * - If it gets used then we change it to &used.
36 * - For unmatched states we use &used (because of cleanness, not because we need
37 * to).
38 * - If we merge inside a case statement and one of the states is &assigned (or
39 * if it is &nobreak) then &nobreak is used.
41 * We print an error when we assign something to a &no_break symbol.
45 STATE(used);
46 STATE(no_break);
48 static int in_switch_stmt;
50 static struct smatch_state *alloc_my_state(struct expression *expr)
52 struct smatch_state *state;
53 char *name;
55 state = __alloc_smatch_state(0);
56 expr = strip_expr(expr);
57 name = expr_to_str(expr);
58 if (!name)
59 name = alloc_string("");
60 state->name = alloc_sname(name);
61 free_string(name);
62 state->data = expr;
63 return state;
66 struct expression *last_print_expr;
67 static void print_missing_break(struct expression *expr)
69 char *name;
71 if (get_switch_expr() == last_print_expr)
72 return;
73 last_print_expr = get_switch_expr();
75 name = expr_to_var(expr);
76 sm_msg("warn: missing break? reassigning '%s'", name);
77 free_string(name);
80 static void match_assign(struct expression *expr)
82 struct expression *left;
84 if (expr->op != '=')
85 return;
86 if (!get_switch_expr())
87 return;
88 left = strip_expr(expr->left);
89 if (get_state_expr(my_id, left) == &no_break)
90 print_missing_break(left);
92 set_state_expr(my_id, left, alloc_my_state(get_switch_expr()));
93 skip_this = left;
96 static void match_symbol(struct expression *expr)
98 if (outside_of_function())
99 return;
100 if (!get_switch_expr())
101 return;
103 expr = strip_expr(expr);
104 if (expr == skip_this)
105 return;
106 set_state_expr(my_id, expr, &used);
109 static struct smatch_state *unmatched_state(struct sm_state *sm)
111 return &used;
114 static int in_case;
115 static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
117 struct expression *switch_expr;
119 if (s1 == &no_break || s2 == &no_break)
120 return &no_break;
121 if (!in_case)
122 return &used;
123 switch_expr = get_switch_expr();
124 if (s1->data == switch_expr || s2->data == switch_expr)
125 return &no_break;
126 return &used;
129 static void match_stmt(struct statement *stmt)
131 if (stmt->type == STMT_CASE)
132 in_case = 1;
133 else
134 in_case = 0;
137 static void match_switch(struct statement *stmt)
139 if (stmt->type != STMT_SWITCH)
140 return;
142 in_switch_stmt++;
145 static void delete_my_states(int owner)
147 struct state_list *slist = NULL;
148 struct sm_state *sm;
150 FOR_EACH_MY_SM(owner, __get_cur_stree(), sm) {
151 add_ptr_list(&slist, sm);
152 } END_FOR_EACH_SM(sm);
154 FOR_EACH_PTR(slist, sm) {
155 delete_state(sm->owner, sm->name, sm->sym);
156 } END_FOR_EACH_PTR(sm);
158 free_slist(&slist);
161 static void match_switch_end(struct statement *stmt)
164 if (stmt->type != STMT_SWITCH)
165 return;
167 in_switch_stmt--;
169 if (!in_switch_stmt)
170 delete_my_states(my_id);
173 void check_missing_break(int id)
175 my_id = id;
177 add_unmatched_state_hook(my_id, &unmatched_state);
178 add_merge_hook(my_id, &merge_hook);
180 add_hook(&match_assign, ASSIGNMENT_HOOK);
181 add_hook(&match_symbol, SYM_HOOK);
182 add_hook(&match_stmt, STMT_HOOK);
183 add_hook(&match_switch, STMT_HOOK);
184 add_hook(&match_switch_end, STMT_HOOK_AFTER);