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 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 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 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 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 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 200 static void match_end_func(void) 201 { 202 if (__inline_fn) 203 return; 204 orig_pos = 0; 205 } 206 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 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 }