Skip to content

Commit

Permalink
Make B::Deparse qualify sub calls named after keywords
Browse files Browse the repository at this point in the history
While B::Deparse was correctly applying CORE:: as necessary to dis-
ambiguate between keywords and custom subroutines, it was not doing
likewise for subroutines whose names were keywords.  main::foo()
should be deparsed as main::foo() if ‘foo’ is a keyword.
  • Loading branch information
Father Chrysostomos committed Oct 5, 2014
1 parent 4c62787 commit a958cfb
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
10 changes: 8 additions & 2 deletions lib/B/Deparse-core.t
Expand Up @@ -132,6 +132,10 @@ sub do_infix_keyword {
testit $keyword, "(\$a CORE::$keyword \$b)", $exp;
testit $keyword, "(\$a $keyword \$b)", $exp;
if (!$strong) {
# B::Deparse fully qualifies any sub whose name is a keyword,
# imported or not, since the importedness may not be reproduced by
# the deparsed code. x is special.
$keyword =~ s/^(?!x\z)/test::/;
testit $keyword, "$keyword(\$a, \$b)", "$keyword(\$a, \$b);";
}
}
Expand All @@ -157,7 +161,9 @@ sub do_std_keyword {
$args = ((!$core && !$strong) || $parens)
? "($args)"
: @args ? " $args" : "";
push @code, (($core && !($do_exp && $strong)) ? "CORE::" : "")
push @code, (($core && !($do_exp && $strong))
? "CORE::"
: $do_exp && !$core && !$strong ? "test::" : "")
. "$keyword$args;";
}
testit $keyword, @code; # code[0]: to run; code[1]: expected
Expand Down Expand Up @@ -210,7 +216,7 @@ testit delete => 'delete $h{\'foo\'};', 'delete $h{\'foo\'};';
# do is listed as strong, but only do { block } is strong;
# do $file is weak, so test it separately here
testit do => 'CORE::do $a;';
testit do => 'do $a;', 'do($a);';
testit do => 'do $a;', 'test::do($a);';
testit do => 'CORE::do { 1 }',
"do {\n 1\n };";
testit do => 'do { 1 };',
Expand Down
37 changes: 28 additions & 9 deletions lib/B/Deparse.pm
Expand Up @@ -1870,20 +1870,23 @@ my %strong_proto_keywords = map { $_ => 1 } qw(
undef
);

sub keyword {
my $self = shift;
my $name = shift;
return $name if $name =~ /^CORE::/; # just in case
if (exists $feature_keywords{$name}) {
sub feature_enabled {
my($self,$name) = @_;
my $hh;
my $hints = $self->{hints} & $feature::hint_mask;
if ($hints && $hints != $feature::hint_mask) {
$hh = _features_from_bundle($hints);
}
elsif ($hints) { $hh = $self->{'hinthash'} }
return "CORE::$name"
if !$hh
|| !$hh->{"feature_$feature_keywords{$name}"}
return $hh && $hh->{"feature_$feature_keywords{$name}"}
}

sub keyword {
my $self = shift;
my $name = shift;
return $name if $name =~ /^CORE::/; # just in case
if (exists $feature_keywords{$name}) {
return "CORE::$name" if not $self->feature_enabled($name);
}
if ($strong_proto_keywords{$name}
|| ($name !~ /^(?:chom?p|do|exec|glob|s(?:elect|ystem))\z/
Expand Down Expand Up @@ -3882,8 +3885,24 @@ sub pp_entersub {
if (!$amper) {
if ($kid eq 'main::') {
$kid = '::';
} elsif ($kid !~ /^(?:\w|::)(?:[\w\d]|::(?!\z))*\z/) {
}
else {
if ($kid !~ /::/ && $kid ne 'x') {
# Fully qualify any sub name that is also a keyword. While
# we could check the import flag, we cannot guarantee that
# the code deparsed so far would set that flag, so we qual-
# ify the names regardless of importation.
my $fq;
if (exists $feature_keywords{$kid}) {
$fq++ if $self->feature_enabled($kid);
} elsif (eval { () = prototype "CORE::$kid"; 1 }) {
$fq++
}
$fq and substr $kid, 0, 0, = $self->{'curstash'}.'::';
}
if ($kid !~ /^(?:\w|::)(?:[\w\d]|::(?!\z))*\z/) {
$kid = single_delim("q", "'", $kid) . '->';
}
}
}
} elsif (is_scalar ($kid->first) && $kid->first->name ne 'rv2cv') {
Expand Down
7 changes: 7 additions & 0 deletions lib/B/Deparse.t
Expand Up @@ -1506,3 +1506,10 @@ my(@array, %hash, @a, @b, %c, %d);
() = \(@Foo::array);
() = \(%Foo::hash);
() = \(@Foo::a, (@Foo::b), (%Foo::c), %Foo::d);
####
# subs synonymous with keywords
main::our();
main::pop();
state();
use feature 'state';
main::state();

0 comments on commit a958cfb

Please sign in to comment.