# Copyright 2008, 2009 Kevin Ryde
# Perl-Critic-Pulp is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Perl-Critic-Pulp is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Perl-Critic-Pulp. If not, see .
package Perl::Critic::Policy::Compatibility::ConstantPragmaHash;
use 5.006;
use strict;
use warnings;
use base 'Perl::Critic::Policy';
use Perl::Critic::Utils qw(:severities);
use version;
use Perl::Critic::Pulp;
our $VERSION = 22;
use constant DEBUG => 0;
sub supported_parameters { return; }
sub default_severity { return $SEVERITY_MEDIUM; }
sub default_themes { return qw(pulp compatibility); }
sub applies_to { return 'PPI::Document'; }
my $perl_ok_version = version->new('5.008');
my $constant_ok_version = version->new('1.03');
sub violates {
my ($self, $elem, $document) = @_;
my @violations;
my $perlver; # a "version" object
my $modver; # a "version" object
my $aref = $document->find ('PPI::Statement::Include')
|| return; # if no includes at all
foreach my $inc (@$aref) {
$inc->type eq 'use'
|| ($inc->type eq 'require' && _in_BEGIN($inc))
|| next;
if (my $ver = $inc->version) {
# "use 5.008" etc perl version
$ver = version->new ($ver);
if (! defined $perlver || $ver > $perlver) {
$perlver = $ver;
if ($perlver >= $perl_ok_version) {
# adequate perl version demanded, stop here
last;
}
}
next;
}
($inc->module||'') eq 'constant' || next;
if (my $ver = Perl::Critic::Pulp::include_module_version ($inc)) {
$ver = version->new ($ver);
if (! defined $modver || $ver > $modver) {
$modver = $ver;
if ($modver >= $constant_ok_version) {
# adequate "constant" version demanded, stop here
last;
}
}
}
if (_use_constant_is_multi ($inc)) {
push @violations, $self->violation
("'use constant' with multi-constant hash requires perl 5.8 or constant 1.03 (at this point have "
. (defined $perlver ? "perl $perlver" : "no perl version")
. (defined $modver ? ", constant $modver)" : ", no constant version)"),
'',
$inc);
}
}
return @violations;
}
# $inc is a PPI::Statement::Include with type "use" and module "constant".
# Return true if it has a multi-constant hash as its argument like
# "use constant { X => 1 };"
#
# The plain "use constant { x=>1 }" comes out as
#
# PPI::Statement::Include
# PPI::Token::Word 'use'
# PPI::Token::Word 'constant'
# PPI::Structure::Constructor { ... }
# PPI::Statement
# PPI::Token::Word 'x'
# PPI::Token::Operator '=>'
# PPI::Token::Number '1'
#
# Or as of PPI 1.203 with a version number "use constant 1.03 { x=>1 }" is
# different
#
# PPI::Statement::Include
# PPI::Token::Word 'use'
# PPI::Token::Word 'constant'
# PPI::Token::Number::Float '1.03'
# PPI::Structure::Block { ... }
# PPI::Statement
# PPI::Token::Word 'x'
# PPI::Token::Operator '=>'
# PPI::Token::Number '1'
#
sub _use_constant_is_multi {
my ($inc) = @_;
my $arg = Perl::Critic::Pulp::include_module_first_arg ($inc)
|| return 0; # empty "use constant" or version "use constant 1.05"
return ($arg->isa('PPI::Structure::Constructor') # without version number
|| $arg->isa('PPI::Structure::Block')); # with version number
}
# return true if $elem is somewhere within a BEGIN block
sub _in_BEGIN {
my ($elem) = @_;
while ($elem = $elem->parent) {
if ($elem->isa('PPI::Statement::Scheduled')) {
return ($elem->type eq 'BEGIN');
}
}
return 0;
}
1;
__END__
=head1 NAME
Perl::Critic::Policy::Compatibility::ConstantPragmaHash - new enough "constant" module for multiple constants
=head1 DESCRIPTION
This policy is part of the L|Perl::Critic::Pulp>
addon. It requires that when you use the hash style multiple constants with
C