flow, conditions: handle label statements correctly
[smatch.git] / check_indenting.c
blob7fc2287ffb179df71bfffde63ade223f1604f4b7
1 /*
2 * Copyright (C) 2014 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
18 #include "smatch.h"
20 static int my_id;
22 static struct string_list *ignored_macros;
24 static int in_ignored_macro(struct statement *stmt)
26 const char *macro;
27 char *tmp;
29 macro = get_macro_name(stmt->pos);
30 if (!macro)
31 return 0;
33 FOR_EACH_PTR(ignored_macros, tmp) {
34 if (!strcmp(tmp, macro))
35 return 1;
36 } END_FOR_EACH_PTR(tmp);
37 return 0;
40 static int missing_curly_braces(struct statement *stmt)
42 int inside_pos;
44 if (stmt->pos.pos == __prev_stmt->pos.pos)
45 return 0;
47 if (__prev_stmt->type == STMT_IF) {
48 if (__prev_stmt->if_true->type == STMT_COMPOUND)
49 return 0;
50 inside_pos = __prev_stmt->if_true->pos.pos;
51 } else if (__prev_stmt->type == STMT_ITERATOR) {
52 if (!__prev_stmt->iterator_pre_condition)
53 return 0;
54 if (__prev_stmt->iterator_statement->type == STMT_COMPOUND)
55 return 0;
56 inside_pos = __prev_stmt->iterator_statement->pos.pos;
57 } else {
58 return 0;
61 if (stmt->pos.pos != inside_pos)
62 return 0;
64 sm_msg("warn: curly braces intended?");
65 return 1;
68 static int prev_lines_say_endif(struct statement *stmt)
70 struct token *token;
71 struct position pos = stmt->pos;
72 int i;
74 pos.pos = 2;
76 for (i = 0; i < 4; i++) {
77 pos.line--;
78 token = pos_get_token(pos);
79 if (token && token_type(token) == TOKEN_IDENT &&
80 strcmp(show_ident(token->ident), "endif") == 0)
81 return 1;
84 return 0;
87 static int is_pre_or_post_statement(struct statement *stmt)
89 if (!stmt->parent)
90 return 0;
91 if (stmt->parent->type != STMT_ITERATOR)
92 return 0;
93 if (stmt->parent->iterator_pre_statement == stmt ||
94 stmt->parent->iterator_post_statement == stmt)
95 return 1;
96 return 0;
100 * If we go out of position, then warn, but don't warn when we go back
101 * into the correct position.
103 static int orig_pos;
106 * If the code has two statements on the same line then don't complain
107 * on the following line. This is a bit of hack because it relies on the
108 * quirk that we don't process nested inline functions.
110 static struct position ignore_prev;
111 static struct position ignore_prev_inline;
113 static void match_stmt(struct statement *stmt)
115 if (stmt != __cur_stmt)
116 return;
117 if (!__prev_stmt)
118 return;
120 if (prev_lines_say_endif(stmt))
121 return;
123 if (is_pre_or_post_statement(stmt))
124 return;
125 /* ignore empty statements if (foo) frob();; */
126 if (stmt->type == STMT_EXPRESSION && !stmt->expression)
127 return;
128 if (__prev_stmt->type == STMT_EXPRESSION && !__prev_stmt->expression)
129 return;
131 if (__prev_stmt->type == STMT_LABEL || __prev_stmt->type == STMT_CASE)
132 return;
134 * This is sort of ugly. The first statement after a case/label is
135 * special. Probably we should handle this in smatch_flow.c so that
136 * this is not a special case. Anyway it's like this:
137 * "foo: one++; two++;" The code is on the same line.
138 * Also there is still a false positive here, if the first case
139 * statement has two statements on the same line. I'm not sure what the
140 * deal is with that.
142 if (stmt->type == STMT_CASE) {
143 if (__next_stmt &&
144 __next_stmt->pos.line == stmt->case_statement->pos.line)
145 ignore_prev = __next_stmt->pos;
146 return;
148 if (stmt->type == STMT_LABEL) {
149 if (__next_stmt &&
150 __next_stmt->pos.line == stmt->label_statement->pos.line)
151 ignore_prev = __next_stmt->pos;
152 return;
155 if (missing_curly_braces(stmt))
156 return;
158 if (stmt->pos.line == __prev_stmt->pos.line) {
159 if (__inline_fn)
160 ignore_prev_inline = stmt->pos;
161 else
162 ignore_prev = stmt->pos;
163 return;
165 if (stmt->pos.pos == __prev_stmt->pos.pos)
166 return;
168 /* some people like to line up their break and case statements. */
169 if (stmt->type == STMT_GOTO && stmt->goto_label &&
170 stmt->goto_label->type == SYM_NODE &&
171 strcmp(stmt->goto_label->ident->name, "break") == 0) {
172 if (__next_stmt && __next_stmt->type == STMT_CASE &&
173 (stmt->pos.line == __next_stmt->pos.line ||
174 stmt->pos.pos == __next_stmt->pos.pos))
175 return;
177 * If we have a compound and the last statement is a break then
178 * it's probably intentional. This is most likely inside a
179 * case statement.
181 if (!__next_stmt)
182 return;
185 if (cmp_pos(__prev_stmt->pos, ignore_prev) == 0 ||
186 cmp_pos(__prev_stmt->pos, ignore_prev_inline) == 0)
187 return;
189 if (in_ignored_macro(stmt))
190 return;
192 if (stmt->pos.pos == orig_pos) {
193 orig_pos = 0;
194 return;
196 sm_msg("warn: inconsistent indenting");
197 orig_pos = __prev_stmt->pos.pos;
200 static void match_end_func(void)
202 if (__inline_fn)
203 return;
204 orig_pos = 0;
207 static void register_ignored_macros(void)
209 struct token *token;
210 char *macro;
211 char name[256];
213 snprintf(name, 256, "%s.ignore_macro_indenting", option_project_str);
215 token = get_tokens_file(name);
216 if (!token)
217 return;
218 if (token_type(token) != TOKEN_STREAMBEGIN)
219 return;
220 token = token->next;
221 while (token_type(token) != TOKEN_STREAMEND) {
222 if (token_type(token) != TOKEN_IDENT)
223 return;
224 macro = alloc_string(show_ident(token->ident));
225 add_ptr_list(&ignored_macros, macro);
226 token = token->next;
228 clear_token_alloc();
231 void check_indenting(int id)
233 my_id = id;
234 add_hook(&match_stmt, STMT_HOOK);
235 add_hook(&match_end_func, END_FUNC_HOOK);
236 register_ignored_macros();