1 /* 2 * Copyright (C) 2012 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 "scope.h" 19 #include "smatch.h" 20 #include "smatch_slist.h" 21 #include "smatch_expression_stacks.h" 22 23 static int my_id; 24 25 static struct string_list *ignored_macros; 26 static struct position old_pos; 27 28 static struct smatch_state *alloc_my_state(struct expression *expr) 29 { 30 struct smatch_state *state; 31 char *name; 32 33 state = __alloc_smatch_state(0); 34 expr = strip_expr(expr); 35 name = expr_to_str(expr); 36 state->name = alloc_sname(name); 37 free_string(name); 38 state->data = expr; 39 return state; 40 } 41 42 static int defined_inside_macro(struct position macro_pos, struct expression *expr) 43 { 44 char *name; 45 struct symbol *sym; 46 int ret = 0; 47 48 name = expr_to_var_sym(expr, &sym); 49 if (!name || !sym) 50 goto free; 51 if (!sym->scope || !sym->scope->token) 52 goto free; 53 if (positions_eq(macro_pos, sym->scope->token->pos)) 54 ret = 1; 55 free: 56 free_string(name); 57 return ret; 58 } 59 60 static int affected_inside_macro_before(struct expression *expr) 61 { 62 struct sm_state *sm; 63 struct sm_state *tmp; 64 struct expression *old_mod; 65 66 sm = get_sm_state_expr(my_id, expr); 67 if (!sm) 68 return 0; 69 70 FOR_EACH_PTR(sm->possible, tmp) { 71 old_mod = tmp->state->data; 72 if (!old_mod) 73 continue; 74 if (positions_eq(old_mod->pos, expr->pos)) 75 return 1; 76 } END_FOR_EACH_PTR(tmp); 77 return 0; 78 } 79 80 static int is_ignored_macro(const char *macro) 81 { 82 char *tmp; 83 84 FOR_EACH_PTR(ignored_macros, tmp) { 85 if (!strcmp(tmp, macro)) 86 return 1; 87 } END_FOR_EACH_PTR(tmp); 88 return 0; 89 } 90 91 static void match_unop(struct expression *raw_expr) 92 { 93 struct expression *expr; 94 char *macro, *name; 95 96 if (raw_expr->op != SPECIAL_INCREMENT && raw_expr->op != SPECIAL_DECREMENT) 97 return; 98 99 macro = get_macro_name(raw_expr->pos); 100 if (!macro) 101 return; 102 103 expr = strip_expr(raw_expr->unop); 104 105 if (defined_inside_macro(expr->pos, expr)) 106 return; 107 108 if (is_ignored_macro(macro)) 109 return; 110 111 if (!affected_inside_macro_before(expr)) { 112 set_state_expr(my_id, expr, alloc_my_state(expr)); 113 old_pos = expr->pos; 114 return; 115 } 116 117 if (!positions_eq(old_pos, expr->pos)) 118 return; 119 120 name = expr_to_str(raw_expr); 121 sm_warning("side effect in macro '%s' doing '%s'", 122 macro, name); 123 free_string(name); 124 } 125 126 static void match_stmt(struct statement *stmt) 127 { 128 if (!positions_eq(old_pos, stmt->pos)) 129 old_pos.line = 0; 130 } 131 132 static void register_ignored_macros(void) 133 { 134 struct token *token; 135 char *macro; 136 char name[256]; 137 138 snprintf(name, 256, "%s.ignore_side_effects", option_project_str); 139 140 token = get_tokens_file(name); 141 if (!token) 142 return; 143 if (token_type(token) != TOKEN_STREAMBEGIN) 144 return; 145 token = token->next; 146 while (token_type(token) != TOKEN_STREAMEND) { 147 if (token_type(token) != TOKEN_IDENT) 148 return; 149 macro = alloc_string(show_ident(token->ident)); 150 add_ptr_list(&ignored_macros, macro); 151 token = token->next; 152 } 153 clear_token_alloc(); 154 } 155 156 void check_macro_side_effects(int id) 157 { 158 my_id = id; 159 160 if (!option_spammy) 161 return; 162 163 add_hook(&match_unop, OP_HOOK); 164 add_hook(&match_stmt, STMT_HOOK); 165 register_ignored_macros(); 166 }