debug: add __smatch_about(var) to print what smatch knows about a variable
[smatch.git] / check_missing_break.c
blob72227679be2702526fccc49320adedaf38572e6e
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 = expr_to_str(expr);
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 struct expression *last_print_expr;
57 static void print_missing_break(struct expression *expr)
59 char *name;
61 if (get_switch_expr() == last_print_expr)
62 return;
63 last_print_expr = get_switch_expr();
65 name = expr_to_var(expr);
66 sm_msg("warn: missing break? reassigning '%s'", name);
67 free_string(name);
70 static void match_assign(struct expression *expr)
72 struct expression *left;
74 if (expr->op != '=')
75 return;
76 if (!get_switch_expr())
77 return;
78 left = strip_expr(expr->left);
79 if (get_state_expr(my_id, left) == &no_break)
80 print_missing_break(left);
82 set_state_expr(my_id, left, alloc_my_state(get_switch_expr()));
83 skip_this = left;
86 static void match_symbol(struct expression *expr)
88 if (outside_of_function())
89 return;
91 expr = strip_expr(expr);
92 if (expr == skip_this)
93 return;
94 set_state_expr(my_id, expr, &used);
97 static struct smatch_state *unmatched_state(struct sm_state *sm)
99 return &used;
102 static int in_case;
103 struct smatch_state *merge_hook(struct smatch_state *s1, struct smatch_state *s2)
105 struct expression *switch_expr;
107 if (s1 == &no_break || s2 == &no_break)
108 return &no_break;
109 if (!in_case)
110 return &used;
111 switch_expr = get_switch_expr();
112 if (s1->data == switch_expr || s2->data == switch_expr)
113 return &no_break;
114 return &used;
117 static void match_stmt(struct statement *stmt)
119 if (stmt->type == STMT_CASE)
120 in_case = 1;
121 else
122 in_case = 0;
125 void check_missing_break(int id)
127 my_id = id;
129 add_unmatched_state_hook(my_id, &unmatched_state);
130 add_merge_hook(my_id, &merge_hook);
132 add_hook(&match_assign, ASSIGNMENT_HOOK);
133 add_hook(&match_symbol, SYM_HOOK);
134 add_hook(&match_stmt, STMT_HOOK);