#include "smatch.h"
#include "smatch_slist.h"
static int my_id;
static struct expression *skip_this;
STATE(used);
STATE(no_break);
static int in_switch_stmt;
static struct smatch_state *alloc_my_state(struct expression *expr)
{
struct smatch_state *state;
char *name;
state = __alloc_smatch_state(0);
expr = strip_expr(expr);
name = expr_to_str(expr);
if (!name)
name = alloc_string("");
state->name = alloc_sname(name);
free_string(name);
state->data = expr;
return state;
}
struct expression *last_print_expr;
static void print_missing_break(struct expression *expr)
{
char *name;
if (get_switch_expr() == last_print_expr)
return;
last_print_expr = get_switch_expr();
name = expr_to_var(expr);
sm_warning("missing break? reassigning '%s'", name);
free_string(name);
}
static void match_assign(struct expression *expr)
{
struct expression *left;
if (expr->op != '=')
return;
if (!get_switch_expr())
return;
left = strip_expr(expr->left);
if (get_state_expr(my_id, left) == &no_break)
print_missing_break(left);
set_state_expr(my_id, left, alloc_my_state(get_switch_expr()));
skip_this = left;
}
static void match_symbol(struct expression *expr)
{
if (outside_of_function())
return;
if (!get_switch_expr())
return;
expr = strip_expr(expr);
if (expr == skip_this)
return;
set_state_expr(my_id, expr, &used);
}
static struct smatch_state *unmatched_state(struct sm_state *sm)
{
return &used;
}
static int in_case;
static struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
{
struct expression *switch_expr;
if (s1 == &no_break || s2 == &no_break)
return &no_break;
if (!in_case)
return &used;
switch_expr = get_switch_expr();
if (s1->data == switch_expr || s2->data == switch_expr)
return &no_break;
return &used;
}
static void match_stmt(struct statement *stmt)
{
if (stmt->type == STMT_CASE)
in_case = 1;
else
in_case = 0;
}
static void match_switch(struct statement *stmt)
{
if (stmt->type != STMT_SWITCH)
return;
in_switch_stmt++;
}
static void delete_my_states(int owner)
{
struct state_list *slist = NULL;
struct sm_state *sm;
FOR_EACH_MY_SM(owner, __get_cur_stree(), sm) {
add_ptr_list(&slist, sm);
} END_FOR_EACH_SM(sm);
FOR_EACH_PTR(slist, sm) {
delete_state(sm->owner, sm->name, sm->sym);
} END_FOR_EACH_PTR(sm);
free_slist(&slist);
}
static void match_switch_end(struct statement *stmt)
{
if (stmt->type != STMT_SWITCH)
return;
in_switch_stmt--;
if (!in_switch_stmt)
delete_my_states(my_id);
}
void check_missing_break(int id)
{
my_id = id;
if (!option_spammy)
return;
set_dynamic_states(my_id);
add_unmatched_state_hook(my_id, &unmatched_state);
add_merge_hook(my_id, &merge_hook);
add_hook(&match_assign, ASSIGNMENT_HOOK);
add_hook(&match_symbol, SYM_HOOK);
add_hook(&match_stmt, STMT_HOOK);
add_hook(&match_switch, STMT_HOOK);
add_hook(&match_switch_end, STMT_HOOK_AFTER);
}