From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

##############################################################################
# $Date: 2009-06-27 20:02:58 -0400 (Sat, 27 Jun 2009) $
# $Author: clonezone $
# $Revision: 3373 $
##############################################################################
use 5.006001;
use strict;
use Perl::Critic::Utils qw{ :severities :classification $EMPTY hashify};
our $VERSION = '1.099_002';
#-----------------------------------------------------------------------------
Readonly::Scalar my $PACKAGE_RX => qr/::/xms;
Readonly::Hash my %EXCEPTIONS => hashify(qw(
$_
$ARG
@_
));
Readonly::Scalar my $DESC => q{Magic variable "%s" should be assigned as "local"};
Readonly::Scalar my $EXPL => [ 81, 82 ];
#-----------------------------------------------------------------------------
sub supported_parameters {
return (
{
name => 'allow',
description =>
q<Global variables to exclude from this policy.>,
default_string => $EMPTY,
behavior => 'string list',
list_always_present_values => [ qw< $_ $ARG @_ > ],
},
);
}
sub default_severity { return $SEVERITY_HIGH }
sub default_themes { return qw(core pbp bugs) }
sub applies_to { return 'PPI::Token::Operator' }
#-----------------------------------------------------------------------------
sub violates {
my ( $self, $elem, undef ) = @_;
return if $elem->content() ne q{=};
my $destination = $elem->sprevious_sibling;
return if !$destination; # huh? assignment in void context??
while ($destination->isa('PPI::Structure::Subscript')) {
$destination = $destination->sprevious_sibling()
or return;
}
if (my $var = $self->_is_non_local_magic_dest($destination)) {
return $self->violation( sprintf( $DESC, $var ), $EXPL, $elem );
}
return; # OK
}
sub _is_non_local_magic_dest {
my ($self, $elem) = @_;
# Quick exit if in good form
my $modifier = $elem->sprevious_sibling;
return
if
$modifier
&& $modifier->isa('PPI::Token::Word')
&& ($modifier->content() eq 'local' || $modifier->content() eq 'my');
# Implementation note: Can't rely on PPI::Token::Magic,
# unfortunately, because we need English too
if ($elem->isa('PPI::Token::Symbol')) {
return $self->_is_magic_var($elem) ? $elem : undef;
}
elsif (
$elem->isa('PPI::Structure::List')
or $elem->isa('PPI::Statement::Expression')
) {
for my $child ($elem->schildren) {
my $var = $self->_is_non_local_magic_dest($child);
return $var if $var;
}
}
return;
}
#-----------------------------------------------------------------------------
sub _is_magic_var {
my ($self, $elem) = @_;
my $variable_name = $elem->symbol();
return if $self->{_allow}{$variable_name};
return 1 if $elem->isa('PPI::Token::Magic'); # optimization(?), and helps with PPI 1.118 carat bug
return if not is_perl_global( $elem );
return 1;
}
1;
__END__
#-----------------------------------------------------------------------------
=pod
=head1 NAME
Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars - Magic variables should be assigned as "local".
=head1 AFFILIATION
This Policy is part of the core L<Perl::Critic|Perl::Critic>
distribution.
=head1 DESCRIPTION
Punctuation variables (and their English.pm equivalents) are global
variables. Messing with globals is dangerous in a complex program as
it can lead to very subtle and hard to fix bugs. If you must change a
magic variable in a non-trivial program, do it in a local scope.
For example, to slurp a filehandle into a scalar, it's common to set
the record separator to undef instead of a newline. If you choose to
do this (instead of using L<File::Slurp|File::Slurp>!) then be sure to
localize the global and change it for as short a time as possible.
# BAD:
$/ = undef;
my $content = <$fh>;
# BETTER:
my $content;
{
local $/ = undef;
$content = <$fh>;
}
# A popular idiom:
my $content = do { local $/ = undef; <$fh> };
This policy also allows the use of C<my>. Perl prevents using C<my>
with "proper" punctuation variables, but allows C<$a>, C<@ARGV>, the
names declared by L<English|English>, etc. This is not a good coding
practice, however it is not the concern of this specific policy to
complain about that.
There are exemptions for C<$_> and C<@_>, and the English equivalent
C<$ARG>.
=head1 CONFIGURATION
You can configure your own exemptions using the C<allow> option:
[Variables::RequireLocalizedPunctuationVars]
allow = @ARGV $ARGV
These are added to the default exemptions.
=head1 CAVEATS
The current PPI (v1.118) has a bug where $^ variables absorb following
whitespace by mistake. This makes it harder to spot those as magic
variables. Hopefully this will be fixed by PPI 1.200. In the
meantime, we have a workaround in this module.
Additionally, PPI v1.118 fails to recognize %! and %^H as magic
variables. PPI instead sees the "%" as a modulus operator. We have
no workaround for that bug right now.
=head1 CREDITS
Initial development of this policy was supported by a grant from the
Perl Foundation.
=head1 AUTHOR
Chris Dolan <cdolan@cpan.org>
=head1 COPYRIGHT
Copyright (c) 2007-2009 Chris Dolan. Many rights reserved.
This program is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. The full text of this license
can be found in the LICENSE file included with this module.
=cut
# Local Variables:
# mode: cperl
# cperl-indent-level: 4
# fill-column: 78
# indent-tabs-mode: nil
# c-indentation-style: bsd
# End:
# ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :