extra: shuffle smatch_extra.h around some more
[smatch.git] / check_missing_break.c
blob174b6fb9bbe0e3c66ceb76d8e5eed6a6171c5957
1 /*
2 * sparse/check_missing_break.c
4 * Copyright (C) 2013 Oracle.
6 * Licensed under the Open Software License version 1.1
8 */
11 * The way I'm detecting missing breaks is if there is an assignment inside a
12 * switch statement which is over written.
16 #include "smatch.h"
17 #include "smatch_slist.h"
19 static int my_id;
20 static struct expression *skip_this;
23 * It goes like this:
24 * - Allocate a state which stores the switch expression. I wanted to
25 * just have a state &assigned but we need to know the switch statement where
26 * it was assigned.
27 * - If it gets used then we change it to &used.
28 * - For unmatched states we use &used (because of cleanness, not because we need
29 * to).
30 * - If we merge inside a case statement and one of the states is &assigned (or
31 * if it is &nobreak) then &nobreak is used.
33 * We print an error when we assign something to a &no_break symbol.
37 STATE(used);
38 STATE(no_break);
40 static struct smatch_state *alloc_my_state(struct expression *expr)
42 struct smatch_state *state;
43 char *name;
45 state = __alloc_smatch_state(0);
46 expr = strip_expr(expr);
47 name = get_variable_from_expr_complex(expr, NULL);
48 if (!name)
49 name = alloc_string("");
50 state->name = alloc_sname(name);
51 free_string(name);
52 state->data = expr;
53 return state;
56 static void match_assign(struct expression *expr)
58 struct expression *left;
60 if (expr->op != '=')
61 return;
62 if (!get_switch_expr())
63 return;
64 left = strip_expr(expr->left);
65 if (get_state_expr(my_id, left) == &no_break) {
66 char *name;
68 name = get_variable_from_expr(left, NULL);
69 sm_msg("warn: missing break? reassigning '%s'", name);
70 free_string(name);
73 set_state_expr(my_id, left, alloc_my_state(get_switch_expr()));
74 skip_this = left;
77 static void match_symbol(struct expression *expr)
79 expr = strip_expr(expr);
80 if (expr == skip_this)
81 return;
82 set_state_expr(my_id, expr, &used);
85 static struct smatch_state *unmatched_state(struct sm_state *sm)
87 return &used;
90 static int in_case;
91 struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
93 struct expression *switch_expr;
95 if (s1 == &no_break || s2 == &no_break)
96 return &no_break;
97 if (!in_case)
98 return &used;
99 switch_expr = get_switch_expr();
100 if (s1->data == switch_expr || s2->data == switch_expr)
101 return &no_break;
102 return &used;
105 static void match_stmt(struct statement *stmt)
107 if (stmt->type == STMT_CASE)
108 in_case = 1;
109 else
110 in_case = 0;
113 void check_missing_break(int id)
115 my_id = id;
117 add_unmatched_state_hook(my_id, &unmatched_state);
118 add_merge_hook(my_id, &merge_hook);
120 add_hook(&match_assign, ASSIGNMENT_HOOK);
121 add_hook(&match_symbol, SYM_HOOK);
122 add_hook(&match_stmt, STMT_HOOK);