summaryrefslogtreecommitdiff
path: root/t/comp
diff options
context:
space:
mode:
authorFather Chrysostomos <sprout@cpan.org>2012-09-18 23:19:52 -0700
committerFather Chrysostomos <sprout@cpan.org>2012-09-19 06:06:52 -0700
commit3f33d153bd4aa2e98501ccbc4ae56fabdbe3d985 (patch)
tree2323fd9cd9091c5bfb3623b54c14ffaacfb26d92 /t/comp
parent4d539dcf0dc3ee5f41c62f0575055ad1f4e935d3 (diff)
downloadperl-3f33d153bd4aa2e98501ccbc4ae56fabdbe3d985.tar.gz
[perl #114942] Correct scoping for ‘for my $x(){} $x’
This was broken by commit 60ac52eb5d5. What that commit did was to merge two different queues that the lexer had for pending tokens. Since bison requires that yylex return exactly one token for each call, when the lexer sometimes has to set aside tokens in a queue and return them from the next few calls to yylex. Formerly, there were two mechanism: the forced token queue (used by force_next), and PL_pending_ident. PL_pending_ident was used for names that had to be looked up in the pads. $foo was handled like this: First call to yylex: 1. Put '$foo' in PL_tokenbuf. 2. Set PL_pending_ident. 3. Return a '$' token. Second call: PL_pending_ident is set, so call S_pending_ident, which looks up the name from PL_tokenbuf, and return the THING token containing the appropriate op. The forced token queue took precedence over PL_pending_ident. Chang- ing the order (necessary for parsing ‘our sub foo($)’) caused some XS::APItest tests to fail. So I concluded that the two queues needed to be merged. As a result, the $foo handling changed to this: First call to yylex: 1. Put '$foo' in PL_tokenbuf. 2. Call force_ident_maybe_lex (S_pending_ident renamed and modi- fied), which looks up the symbol and adds it to the forced token queue. 3. Return a '$' token. Second call: Return the token from the forced token queue. That had the unforeseen consequence of changing this: for my $x (...) { ... } $x; such that the $x was still visible after the for loop. It only hap- pened when the $ was the next token after the closing }: $ ./miniperl -e 'for my $x(()){} $x = 3; warn $x' Warning: something's wrong at -e line 1. $ ./miniperl -e 'for my $x(()){} ;$x = 3; warn $x' 3 at -e line 1. This broke Class::Declare. The name lookup in the pad must not happen before the '$' token is emitted. At that point, the parser has not yet created the for loop (which includes exiting its scope), as it does not yet know whether there is a continue block. (See the ‘FOR MY...’ branch of the barestmt rule in perly.y.) So we must delay the name lookup till the second call. So we rename force_ident_maybe_lex back to S_pending_ident, removing the force_next stuff. And we add a new force_ident_maybe_lex function that adds a special ‘pending ident’ token to the forced token queue. The part of yylex that handles pending tokens (case LEX_KNOWNEXT) is modified to account for these special ‘pending ident’ tokens and call S_pending_ident.
Diffstat (limited to 't/comp')
-rw-r--r--t/comp/parser.t5
1 files changed, 4 insertions, 1 deletions
diff --git a/t/comp/parser.t b/t/comp/parser.t
index a0f9a0c73b..7c0db7fa37 100644
--- a/t/comp/parser.t
+++ b/t/comp/parser.t
@@ -3,7 +3,7 @@
# Checks if the parser behaves correctly in edge cases
# (including weird syntax errors)
-print "1..153\n";
+print "1..154\n";
sub failed {
my ($got, $expected, $name) = @_;
@@ -446,6 +446,9 @@ is prototype "Hello::_he_said", '_', 'initial tick in sub declaration';
eval 'no if $] >= 5.17.4 warnings => "deprecated"';
is 1,1, ' no crash for "no ... syntax error"';
+for my $pkg(()){}
+$pkg = 3;
+is $pkg, 3, '[perl #114942] for my $foo()){} $foo';
# Add new tests HERE (above this line)