summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--parse.y27
-rw-r--r--test/ruby/test_parse.rb4
3 files changed, 34 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index c8485eff2f..05ef5ded8a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Thu Dec 25 12:47:44 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * parse.y (gettable_gen): warn possible reference to a local
+ variable defined in a past scope.
+
Thu Dec 25 10:09:14 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
* ext/io/console/console.c (console_dev): id_console is not a
diff --git a/parse.y b/parse.y
index 20345085b5..bfc685a05f 100644
--- a/parse.y
+++ b/parse.y
@@ -113,6 +113,7 @@ struct local_vars {
struct vtable *args;
struct vtable *vars;
struct vtable *used;
+ struct vtable *past;
struct local_vars *prev;
stack_type cmdargs;
};
@@ -8827,6 +8828,17 @@ match_op_gen(struct parser_params *parser, NODE *node1, NODE *node2)
return NEW_CALL(node1, tMATCH, NEW_LIST(node2));
}
+static int
+past_dvar_p(struct parser_params *parser, ID id)
+{
+ struct vtable *past = lvtbl->past;
+ while (past) {
+ if (vtable_included(past, id)) return 1;
+ past = past->prev;
+ }
+ return 0;
+}
+
static NODE*
gettable_gen(struct parser_params *parser, ID id)
{
@@ -8860,6 +8872,9 @@ gettable_gen(struct parser_params *parser, ID id)
}
return NEW_LVAR(id);
}
+ if (!in_defined && RTEST(ruby_verbose) && past_dvar_p(parser, id)) {
+ rb_warningV("possible reference to past scope - %"PRIsVALUE, rb_id2str(id));
+ }
/* method call without arguments */
return NEW_VCALL(id);
case ID_GLOBAL:
@@ -9978,6 +9993,7 @@ local_push_gen(struct parser_params *parser, int inherit_dvars)
local->used = !(inherit_dvars &&
(ifndef_ripper(compile_for_eval || e_option_supplied(parser))+0)) &&
RTEST(ruby_verbose) ? vtable_alloc(0) : 0;
+ local->past = 0;
local->cmdargs = cmdarg_stack;
cmdarg_stack = 0;
lvtbl = local;
@@ -9991,6 +10007,11 @@ local_pop_gen(struct parser_params *parser)
warn_unused_var(parser, lvtbl);
vtable_free(lvtbl->used);
}
+ while (lvtbl->past) {
+ struct vtable *past = lvtbl->past;
+ lvtbl->past = past->prev;
+ vtable_free(past);
+ }
vtable_free(lvtbl->args);
vtable_free(lvtbl->vars);
cmdarg_stack = lvtbl->cmdargs;
@@ -10090,10 +10111,12 @@ dyna_pop_1(struct parser_params *parser)
}
tmp = lvtbl->args;
lvtbl->args = lvtbl->args->prev;
- vtable_free(tmp);
+ tmp->prev = lvtbl->past;
+ lvtbl->past = tmp;
tmp = lvtbl->vars;
lvtbl->vars = lvtbl->vars->prev;
- vtable_free(tmp);
+ tmp->prev = lvtbl->past;
+ lvtbl->past = tmp;
}
static void
diff --git a/test/ruby/test_parse.rb b/test/ruby/test_parse.rb
index 70b38a2a7e..38dca83053 100644
--- a/test/ruby/test_parse.rb
+++ b/test/ruby/test_parse.rb
@@ -873,4 +873,8 @@ x = __ENCODING__
a = "\u{3042}"
assert_warning(/#{a}/) {eval("#{a} = 1; /(?<#{a}>)/ =~ ''")}
end
+
+ def test_past_scope_variable
+ assert_warning(/past scope/) {catch {|tag| eval("BEGIN{throw tag}; tap {a = 1}; a")}}
+ end
end