1 /*
   2  * Copyright (C) 2016 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 struct statement_list *stmt_list;
  21 
  22 static int end_of_function(struct statement *stmt)
  23 {
  24         struct symbol *fn = get_base_type(cur_func_sym);
  25 
  26         /* err on the conservative side of things */
  27         if (!fn)
  28                 return 1;
  29         if (stmt == fn->stmt || stmt == fn->inline_stmt)
  30                 return 1;
  31         return 0;
  32 }
  33 
  34 /*
  35  * We're wasting a lot of time worrying about out of scope variables.
  36  * When we come to the end of a scope then just delete them all the out of
  37  * scope states.
  38  */
  39 static void match_end_of_block(struct statement *stmt)
  40 {
  41         struct statement *tmp;
  42         struct symbol *sym;
  43 
  44         if (end_of_function(stmt))
  45                 return;
  46 
  47         FOR_EACH_PTR(stmt->stmts, tmp) {
  48                 if (tmp->type != STMT_DECLARATION)
  49                         return;
  50 
  51                 FOR_EACH_PTR(tmp->declaration, sym) {
  52                         if (!sym->ident)
  53                                 continue;
  54                         __delete_all_states_sym(sym);
  55                 } END_FOR_EACH_PTR(sym);
  56         } END_FOR_EACH_PTR(tmp);
  57 }
  58 
  59 static int is_outer_stmt(struct statement *stmt)
  60 {
  61         struct symbol *fn;
  62 
  63         if (!cur_func_sym)
  64                 return 0;
  65         fn = get_base_type(cur_func_sym);
  66         if (!fn)
  67                 return 0;
  68         /*
  69          * There are times when ->parent is not set but it's set for
  70          * the outer statement so ignoring NULLs works as a work-around.
  71          */
  72         if (!stmt->parent)
  73                 return 0;
  74         if (stmt->parent == fn->stmt ||
  75             stmt->parent == fn->inline_stmt)
  76                 return 1;
  77         return 0;
  78 }
  79 
  80 static void match_stmt(struct statement *stmt)
  81 {
  82         struct statement *tmp;
  83 
  84         if (__inline_fn)
  85                 return;
  86 
  87         if (stmt->type == STMT_COMPOUND)
  88                 add_ptr_list(&stmt_list, stmt);
  89 
  90         if (!is_outer_stmt(stmt))
  91                 return;
  92 
  93         FOR_EACH_PTR(stmt_list, tmp) {
  94                 match_end_of_block(tmp);
  95         } END_FOR_EACH_PTR(tmp);
  96         free_ptr_list(&stmt_list);
  97 }
  98 
  99 static void match_end_func(struct symbol *sym)
 100 {
 101         if (__inline_fn)
 102                 return;
 103         free_ptr_list(&stmt_list);
 104 }
 105 
 106 void register_scope(int id)
 107 {
 108         add_hook(&match_stmt, STMT_HOOK_AFTER);
 109         add_hook(&match_end_func, AFTER_FUNC_HOOK);
 110 }