—##############################################################################
# $Date: 2007-10-09 12:47:42 -0500 (Tue, 09 Oct 2007) $
# $Author: clonezone $
# $Revision: 1967 $
##############################################################################
use
strict;
use
warnings;
use
Readonly;
our
$VERSION
=
'1.079_001'
;
#-----------------------------------------------------------------------------
Readonly::Scalar
my
$PACKAGE_RX
=>
qr/::/
mx;
Readonly::Hash
my
%EXCEPTIONS
=> hashify(
qw(
$_
$ARG
@_
)
);
Readonly::Scalar
my
$DESC
=>
q{Magic variables should be assigned as "local"}
;
Readonly::Scalar
my
$EXPL
=> [ 81, 82 ];
#-----------------------------------------------------------------------------
sub
supported_parameters {
return
() }
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
ne
q{=}
;
my
$destination
=
$elem
->sprevious_sibling;
return
if
!
$destination
;
# huh? assignment in void context??
if
(_is_non_local_magic_dest(
$destination
)) {
return
$self
->violation(
$DESC
,
$EXPL
,
$elem
);
}
return
;
# OK
}
sub
_is_non_local_magic_dest {
my
$elem
=
shift
;
#print "Test dest $elem, @{[ref $elem]}\n";
# Quick exit if in good form
my
$modifier
=
$elem
->sprevious_sibling;
return
if
$modifier
&&
$modifier
->isa(
'PPI::Token::Word'
) &&
$modifier
eq
'local'
;
# Implementation note: Can't rely on PPI::Token::Magic,
# unfortunately, because we need English too
if
(
$elem
->isa(
'PPI::Token::Symbol'
)) {
return
_is_magic_var(
$elem
);
}
elsif
(
$elem
->isa(
'PPI::Structure::List'
) ||
$elem
->isa(
'PPI::Statement::Expression'
)) {
for
my
$child
(
$elem
->schildren) {
return
1
if
_is_non_local_magic_dest(
$child
);
}
}
return
;
}
#-----------------------------------------------------------------------------
sub
_is_magic_var {
my
$elem
=
shift
;
#print "checking $elem\n";
my
$variable_name
=
"$elem"
;
return
if
$EXCEPTIONS
{
$variable_name
};
return
1
if
$elem
->isa(
'PPI::Token::Magic'
);
# optimization(?), and helps with PPI 1.118 carat bug
return
if
! is_perl_global(
$elem
);
#print " MAGIC\n";
return
1;
}
1;
__END__
#-----------------------------------------------------------------------------
=pod
=head1 NAME
Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars
=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>!) 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> };
=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 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 :