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 }