1 /*
2  * Copyright (C) 2014 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 #include "smatch.h"
19 
20 static int my_id;
21 
22 static struct string_list *ignored_macros;
23 
in_ignored_macro(struct statement * stmt)24 static int in_ignored_macro(struct statement *stmt)
25 {
26 	const char *macro;
27 	char *tmp;
28 
29 	macro = get_macro_name(stmt->pos);
30 	if (!macro)
31 		return 0;
32 
33 	FOR_EACH_PTR(ignored_macros, tmp) {
34 		if (!strcmp(tmp, macro))
35 			return 1;
36 	} END_FOR_EACH_PTR(tmp);
37 	return 0;
38 }
39 
missing_curly_braces(struct statement * stmt)40 static int missing_curly_braces(struct statement *stmt)
41 {
42 	int inside_pos;
43 
44 	if (stmt->pos.pos == __prev_stmt->pos.pos)
45 		return 0;
46 
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;
59 	}
60 
61 	if (stmt->pos.pos != inside_pos)
62 		return 0;
63 
64 	sm_warning("curly braces intended?");
65 	return 1;
66 }
67 
prev_lines_say_endif(struct statement * stmt)68 static int prev_lines_say_endif(struct statement *stmt)
69 {
70 	struct token *token;
71 	struct position pos = stmt->pos;
72 	int i;
73 
74 	pos.pos = 2;
75 
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;
82 	}
83 
84 	return 0;
85 }
86 
is_pre_or_post_statement(struct statement * stmt)87 static int is_pre_or_post_statement(struct statement *stmt)
88 {
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;
97 }
98 
99 /*
100  * If we go out of position, then warn, but don't warn when we go back
101  * into the correct position.
102  */
103 static int orig_pos;
104 
105 /*
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.
109  */
110 static struct position ignore_prev;
111 static struct position ignore_prev_inline;
112 
match_stmt(struct statement * stmt)113 static void match_stmt(struct statement *stmt)
114 {
115 	if (stmt != __cur_stmt)
116 		return;
117 	if (!__prev_stmt)
118 		return;
119 
120 	if (prev_lines_say_endif(stmt))
121 		return;
122 
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;
130 
131 	if (__prev_stmt->type == STMT_LABEL || __prev_stmt->type == STMT_CASE)
132 		return;
133 	/*
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.
141 	 */
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;
147 	}
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;
153 	}
154 
155 	if (missing_curly_braces(stmt))
156 		return;
157 
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;
164 	}
165 	if (stmt->pos.pos == __prev_stmt->pos.pos)
166 		return;
167 
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;
176 		/*
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.
180 		 */
181 		if (!__next_stmt)
182 			return;
183 	}
184 
185 	if (cmp_pos(__prev_stmt->pos, ignore_prev) == 0 ||
186 	    cmp_pos(__prev_stmt->pos, ignore_prev_inline) == 0)
187 		return;
188 
189 	if (in_ignored_macro(stmt))
190 		return;
191 
192 	if (stmt->pos.pos == orig_pos) {
193 		orig_pos = 0;
194 		return;
195 	}
196 	sm_warning("inconsistent indenting");
197 	orig_pos = __prev_stmt->pos.pos;
198 }
199 
match_end_func(void)200 static void match_end_func(void)
201 {
202 	if (__inline_fn)
203 		return;
204 	orig_pos = 0;
205 }
206 
register_ignored_macros(void)207 static void register_ignored_macros(void)
208 {
209 	struct token *token;
210 	char *macro;
211 	char name[256];
212 
213 	snprintf(name, 256, "%s.ignore_macro_indenting", option_project_str);
214 
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;
227 	}
228 	clear_token_alloc();
229 }
230 
check_indenting(int id)231 void check_indenting(int id)
232 {
233 	my_id = id;
234 	add_hook(&match_stmt, STMT_HOOK);
235 	add_hook(&match_end_func, END_FUNC_HOOK);
236 	register_ignored_macros();
237 }
238