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 int print_unreached = 1;
  23 static struct string_list *turn_off_names;
  24 static struct string_list *ignore_names;
  25 
  26 static int empty_statement(struct statement *stmt)
  27 {
  28         if (!stmt)
  29                 return 0;
  30         if (stmt->type == STMT_EXPRESSION && !stmt->expression)
  31                 return 1;
  32         return 0;
  33 }
  34 
  35 static int is_last_stmt(struct statement *cur_stmt)
  36 {
  37         struct symbol *fn = get_base_type(cur_func_sym);
  38         struct statement *stmt;
  39 
  40         if (!fn)
  41                 return 0;
  42         stmt = fn->stmt;
  43         if (!stmt)
  44                 stmt = fn->inline_stmt;
  45         if (!stmt || stmt->type != STMT_COMPOUND)
  46                 return 0;
  47         stmt = last_ptr_list((struct ptr_list *)stmt->stmts);
  48         if (stmt == cur_stmt)
  49                 return 1;
  50         return 0;
  51 }
  52 
  53 static void print_unreached_initializers(struct symbol_list *sym_list)
  54 {
  55         struct symbol *sym;
  56 
  57         FOR_EACH_PTR(sym_list, sym) {
  58                 if (sym->initializer && !(sym->ctype.modifiers & MOD_STATIC))
  59                         sm_msg("info: '%s' is not actually initialized (unreached code).",
  60                                 (sym->ident ? sym->ident->name : "this variable"));
  61         } END_FOR_EACH_PTR(sym);
  62 }
  63 
  64 static int is_ignored_macro(struct statement *stmt)
  65 {
  66         char *name;
  67         char *tmp;
  68 
  69         name = get_macro_name(stmt->pos);
  70         if (!name)
  71                 return 0;
  72 
  73         FOR_EACH_PTR(ignore_names, tmp) {
  74                 if (strcmp(tmp, name) == 0)
  75                         return 1;
  76         } END_FOR_EACH_PTR(tmp);
  77 
  78         return 0;
  79 }
  80 
  81 static int prev_line_was_endif(struct statement *stmt)
  82 {
  83         struct token *token;
  84         struct position pos = stmt->pos;
  85 
  86         pos.line--;
  87         pos.pos = 2;
  88 
  89         token = pos_get_token(pos);
  90         if (token && token_type(token) == TOKEN_IDENT &&
  91             strcmp(show_ident(token->ident), "endif") == 0)
  92                 return 1;
  93 
  94         pos.line--;
  95         token = pos_get_token(pos);
  96         if (token && token_type(token) == TOKEN_IDENT &&
  97             strcmp(show_ident(token->ident), "endif") == 0)
  98                 return 1;
  99 
 100         return 0;
 101 }
 102 
 103 static int we_jumped_into_the_middle_of_a_loop(struct statement *stmt)
 104 {
 105         struct statement *prev;
 106 
 107         /*
 108          * Smatch doesn't handle loops correctly and this is a hack.  What we
 109          * do is that if the first unreachable statement is a loop and the
 110          * previous statement was a goto then it's probably code like this:
 111          *      goto first;
 112          *      for (;;) {
 113          *              frob();
 114          * first:
 115          *              more_frob();
 116          *      }
 117          * Every statement is reachable but only on the second iteration.
 118          */
 119 
 120         if (stmt->type != STMT_ITERATOR)
 121                 return 0;
 122         prev = get_prev_statement();
 123         if (prev && prev->type == STMT_GOTO)
 124                 return 1;
 125         return 0;
 126 }
 127 
 128 static void unreachable_stmt(struct statement *stmt)
 129 {
 130 
 131         if (__inline_fn)
 132                 return;
 133 
 134         if (!__path_is_null()) {
 135                 print_unreached = 1;
 136                 return;
 137         }
 138 
 139         /* if we hit a label then assume there is a matching goto */
 140         if (stmt->type == STMT_LABEL)
 141                 print_unreached = 0;
 142         if (prev_line_was_endif(stmt))
 143                 print_unreached = 0;
 144         if (we_jumped_into_the_middle_of_a_loop(stmt))
 145                 print_unreached = 0;
 146 
 147         if (!print_unreached)
 148                 return;
 149         if (empty_statement(stmt))
 150                 return;
 151         if (is_ignored_macro(stmt))
 152                 return;
 153 
 154         switch (stmt->type) {
 155         case STMT_COMPOUND: /* after a switch before a case stmt */
 156         case STMT_RANGE:
 157         case STMT_CASE:
 158                 return;
 159         case STMT_DECLARATION: /* switch (x) { int a; case foo: ... */
 160                 print_unreached_initializers(stmt->declaration);
 161                 return;
 162         case STMT_RETURN: /* gcc complains if you don't have a return statement */
 163                 if (is_last_stmt(stmt))
 164                         return;
 165                 break;
 166         case STMT_GOTO:
 167                 /* people put extra breaks inside switch statements */
 168                 if (stmt->goto_label && stmt->goto_label->type == SYM_NODE &&
 169                     strcmp(stmt->goto_label->ident->name, "break") == 0)
 170                         return;
 171                 break;
 172         default:
 173                 break;
 174         }
 175         sm_warning("ignoring unreachable code.");
 176         print_unreached = 0;
 177 }
 178 
 179 static int is_turn_off(char *name)
 180 {
 181         char *tmp;
 182 
 183         if (!name)
 184                 return 0;
 185 
 186         FOR_EACH_PTR(turn_off_names, tmp) {
 187                 if (strcmp(tmp, name) == 0)
 188                         return 1;
 189         } END_FOR_EACH_PTR(tmp);
 190 
 191         return 0;
 192 }
 193 
 194 static char *get_function_name(struct statement *stmt)
 195 {
 196         struct expression *expr;
 197 
 198         if (stmt->type != STMT_EXPRESSION)
 199                 return NULL;
 200         expr = stmt->expression;
 201         if (!expr || expr->type != EXPR_CALL)
 202                 return NULL;
 203         if (expr->fn->type != EXPR_SYMBOL || !expr->fn->symbol_name)
 204                 return NULL;
 205         return expr->fn->symbol_name->name;
 206 }
 207 
 208 static void turn_off_unreachable(struct statement *stmt)
 209 {
 210         char *name;
 211 
 212         name = get_macro_name(stmt->pos);
 213         if (is_turn_off(name)) {
 214                 print_unreached = 0;
 215                 return;
 216         }
 217 
 218         if (stmt->type == STMT_IF &&
 219             known_condition_true(stmt->if_conditional) &&  __path_is_null()) {
 220                 print_unreached = 0;
 221                 return;
 222         }
 223 
 224         name = get_function_name(stmt);
 225         if (is_turn_off(name))
 226                 print_unreached = 0;
 227 }
 228 
 229 static void register_turn_off_macros(void)
 230 {
 231         struct token *token;
 232         char *macro;
 233         char name[256];
 234 
 235         if (option_project == PROJ_NONE)
 236                 strcpy(name, "unreachable.turn_off");
 237         else
 238                 snprintf(name, 256, "%s.unreachable.turn_off", option_project_str);
 239 
 240         token = get_tokens_file(name);
 241         if (!token)
 242                 return;
 243         if (token_type(token) != TOKEN_STREAMBEGIN)
 244                 return;
 245         token = token->next;
 246         while (token_type(token) != TOKEN_STREAMEND) {
 247                 if (token_type(token) != TOKEN_IDENT)
 248                         return;
 249                 macro = alloc_string(show_ident(token->ident));
 250                 add_ptr_list(&turn_off_names, macro);
 251                 token = token->next;
 252         }
 253         clear_token_alloc();
 254 }
 255 
 256 static void register_ignored_macros(void)
 257 {
 258         struct token *token;
 259         char *macro;
 260         char name[256];
 261 
 262         if (option_project == PROJ_NONE)
 263                 strcpy(name, "unreachable.ignore");
 264         else
 265                 snprintf(name, 256, "%s.unreachable.ignore", option_project_str);
 266 
 267         token = get_tokens_file(name);
 268         if (!token)
 269                 return;
 270         if (token_type(token) != TOKEN_STREAMBEGIN)
 271                 return;
 272         token = token->next;
 273         while (token_type(token) != TOKEN_STREAMEND) {
 274                 if (token_type(token) != TOKEN_IDENT)
 275                         return;
 276                 macro = alloc_string(show_ident(token->ident));
 277                 add_ptr_list(&ignore_names, macro);
 278                 token = token->next;
 279         }
 280         clear_token_alloc();
 281 }
 282 
 283 void check_unreachable(int id)
 284 {
 285         my_id = id;
 286 
 287         register_turn_off_macros();
 288         register_ignored_macros();
 289         add_hook(&unreachable_stmt, STMT_HOOK);
 290         add_hook(&turn_off_unreachable, STMT_HOOK_AFTER);
 291 }