1 /* 2 * Copyright (C) 2013 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 /* 19 * The way I'm detecting missing breaks is if there is an assignment inside a 20 * switch statement which is over written. 21 * 22 */ 23 24 #include "smatch.h" 25 #include "smatch_slist.h" 26 27 static int my_id; 28 static struct expression *skip_this; 29 30 /* 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. 40 * 41 * We print an error when we assign something to a &no_break symbol. 42 * 43 */ 44 45 STATE(used); 46 STATE(no_break); 47 48 static int in_switch_stmt; 49 50 static struct smatch_state *alloc_my_state(struct expression *expr) 51 { 52 struct smatch_state *state; 53 char *name; 54 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; 64 } 65 66 struct expression *last_print_expr; 67 static void print_missing_break(struct expression *expr) 68 { 69 char *name; 70 71 if (get_switch_expr() == last_print_expr) 72 return; 73 last_print_expr = get_switch_expr(); 74 75 name = expr_to_var(expr); 76 sm_warning("missing break? reassigning '%s'", name); 77 free_string(name); 78 } 79 80 static void match_assign(struct expression *expr) 81 { 82 struct expression *left; 83 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); 91 92 set_state_expr(my_id, left, alloc_my_state(get_switch_expr())); 93 skip_this = left; 94 } 95 96 static void match_symbol(struct expression *expr) 97 { 98 if (outside_of_function()) 99 return; 100 if (!get_switch_expr()) 101 return; 102 103 expr = strip_expr(expr); 104 if (expr == skip_this) 105 return; 106 set_state_expr(my_id, expr, &used); 107 } 108 109 static struct smatch_state *unmatched_state(struct sm_state *sm) 110 { 111 return &used; 112 } 113 114 static int in_case; 115 static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2) 116 { 117 struct expression *switch_expr; 118 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; 127 } 128 129 static void match_stmt(struct statement *stmt) 130 { 131 if (stmt->type == STMT_CASE) 132 in_case = 1; 133 else 134 in_case = 0; 135 } 136 137 static void match_switch(struct statement *stmt) 138 { 139 if (stmt->type != STMT_SWITCH) 140 return; 141 142 in_switch_stmt++; 143 } 144 145 static void delete_my_states(int owner) 146 { 147 struct state_list *slist = NULL; 148 struct sm_state *sm; 149 150 FOR_EACH_MY_SM(owner, __get_cur_stree(), sm) { 151 add_ptr_list(&slist, sm); 152 } END_FOR_EACH_SM(sm); 153 154 FOR_EACH_PTR(slist, sm) { 155 delete_state(sm->owner, sm->name, sm->sym); 156 } END_FOR_EACH_PTR(sm); 157 158 free_slist(&slist); 159 } 160 161 static void match_switch_end(struct statement *stmt) 162 { 163 164 if (stmt->type != STMT_SWITCH) 165 return; 166 167 in_switch_stmt--; 168 169 if (!in_switch_stmt) 170 delete_my_states(my_id); 171 } 172 173 void check_missing_break(int id) 174 { 175 my_id = id; 176 177 if (!option_spammy) 178 return; 179 180 add_unmatched_state_hook(my_id, &unmatched_state); 181 add_merge_hook(my_id, &merge_hook); 182 183 add_hook(&match_assign, ASSIGNMENT_HOOK); 184 add_hook(&match_symbol, SYM_HOOK); 185 add_hook(&match_stmt, STMT_HOOK); 186 add_hook(&match_switch, STMT_HOOK); 187 add_hook(&match_switch_end, STMT_HOOK_AFTER); 188 }