File Coverage

blib/lib/Perl/Critic/Policy/Compatibility/ConstantPragmaHash.pm
Criterion Covered Total %
statement 58 58 100.0
branch 19 22 86.3
condition 14 21 66.6
subroutine 13 13 100.0
pod 1 1 100.0
total 105 115 91.3


line stmt bran cond sub pod time code
1             # Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021 Kevin Ryde
2              
3             # Perl-Critic-Pulp is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by the
5             # Free Software Foundation; either version 3, or (at your option) any later
6             # version.
7             #
8             # Perl-Critic-Pulp is distributed in the hope that it will be useful, but
9             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11             # for more details.
12             #
13             # You should have received a copy of the GNU General Public License along
14             # with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
15              
16              
17             package Perl::Critic::Policy::Compatibility::ConstantPragmaHash;
18 40     40   36358 use 5.006;
  40         212  
19 40     40   253 use strict;
  40         103  
  40         976  
20 40     40   221 use warnings;
  40         95  
  40         1227  
21 40     40   230 use base 'Perl::Critic::Policy';
  40         110  
  40         5296  
22 40     40   200539 use Perl::Critic::Utils;
  40         124  
  40         914  
23 40     40   37586 use Perl::Critic::Pulp::Utils;
  40         119  
  40         1572  
24 40     40   248 use version (); # but don't import qv()
  40         104  
  40         1935  
25              
26             # uncomment this to run the ### lines
27             # use Smart::Comments;
28              
29             our $VERSION = 99;
30              
31 40     40   326 use constant supported_parameters => ();
  40         119  
  40         3195  
32 40     40   280 use constant default_severity => $Perl::Critic::Utils::SEVERITY_MEDIUM;
  40         122  
  40         2687  
33 40     40   284 use constant default_themes => qw(pulp compatibility);
  40         110  
  40         2477  
34 40     40   268 use constant applies_to => 'PPI::Document';
  40         157  
  40         19317  
35              
36             my $perl_ok_version = version->new('5.008');
37             my $constant_ok_version = version->new('1.03');
38              
39             sub violates {
40 26     26 1 720004 my ($self, $elem, $document) = @_;
41              
42 26         76 my @violations;
43             my $perlver; # a "version" object
44 26         0 my $modver; # a "version" object
45              
46 26   100     82 my $aref = $document->find ('PPI::Statement::Include')
47             || return; # if no includes at all
48 25         354 foreach my $inc (@$aref) {
49              
50 39 100 66     2070 $inc->type eq 'use'
      100        
51             || ($inc->type eq 'require'
52             && Perl::Critic::Pulp::Utils::elem_in_BEGIN($inc))
53             || next;
54              
55 37 100       939 if (my $ver = $inc->version) {
56             # "use 5.008" etc perl version
57 9         266 $ver = version->new ($ver);
58 9 50 33     34 if (! defined $perlver || $ver > $perlver) {
59 9         17 $perlver = $ver;
60              
61 9 100       76 if ($perlver >= $perl_ok_version) {
62             # adequate perl version demanded, stop here
63 8         20 last;
64             }
65             }
66 1         4 next;
67             }
68              
69 28 50 50     636 ($inc->module||'') eq 'constant' || next;
70              
71 28 100       659 if (my $ver = Perl::Critic::Pulp::Utils::include_module_version ($inc)) {
72             ### $ver
73             # PPI::Token::Number::Float
74 9         29 $ver = version->new ($ver->content);
75 9 50 33     103 if (! defined $modver || $ver > $modver) {
76 9         19 $modver = $ver;
77              
78 9 100       52 if ($modver >= $constant_ok_version) {
79             # adequate "constant" version demanded, stop here
80 7         18 last;
81             }
82             }
83             }
84              
85 21 100       54 if (_use_constant_is_multi ($inc)) {
86 15 100       115 push @violations, $self->violation
    100          
87             ("'use constant' with multi-constant hash requires perl 5.8 or constant 1.03 (at this point have "
88             . (defined $perlver ? "perl $perlver" : "no perl version")
89             . (defined $modver ? ", constant $modver)" : ", no constant version)"),
90             '',
91             $inc);
92             }
93             }
94              
95 25         2008 return @violations;
96             }
97              
98             # $inc is a PPI::Statement::Include with type "use" and module "constant".
99             # Return true if it has a multi-constant hash as its argument like
100             # "use constant { X => 1 };"
101             #
102             # The plain "use constant { x=>1 }" comes out as
103             #
104             # PPI::Statement::Include
105             # PPI::Token::Word 'use'
106             # PPI::Token::Word 'constant'
107             # PPI::Structure::Constructor { ... }
108             # PPI::Statement
109             # PPI::Token::Word 'x'
110             # PPI::Token::Operator '=>'
111             # PPI::Token::Number '1'
112             #
113             # Or as of PPI 1.203 with a version number "use constant 1.03 { x=>1 }" is
114             # different
115             #
116             # PPI::Statement::Include
117             # PPI::Token::Word 'use'
118             # PPI::Token::Word 'constant'
119             # PPI::Token::Number::Float '1.03'
120             # PPI::Structure::Block { ... }
121             # PPI::Statement
122             # PPI::Token::Word 'x'
123             # PPI::Token::Operator '=>'
124             # PPI::Token::Number '1'
125             #
126             sub _use_constant_is_multi {
127 49     49   131969 my ($inc) = @_;
128 49   100     228 my $arg = Perl::Critic::Pulp::Utils::include_module_first_arg ($inc)
129             || return 0; # empty "use constant" or version "use constant 1.05"
130 42   66     305 return ($arg->isa('PPI::Structure::Constructor') # without version number
131             || $arg->isa('PPI::Structure::Block')); # with version number
132             }
133              
134              
135             1;
136             __END__
137              
138             =for stopwords multi-constant CPAN perl ok ConstantPragmaHash backports prereqs Ryde
139              
140             =head1 NAME
141              
142             Perl::Critic::Policy::Compatibility::ConstantPragmaHash - new enough "constant" module for multiple constants
143              
144             =head1 DESCRIPTION
145              
146             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
147             add-on. It requires that when you use the hash style multiple constants of
148             C<use constant> that you explicitly declare either Perl 5.8 or C<constant>
149             1.03 or higher.
150              
151             use constant { AA => 1, BB => 2 }; # bad
152              
153             use 5.008;
154             use constant { CC => 1, DD => 2 }; # ok
155              
156             use constant 1.03;
157             use constant { EE => 1, FF => 2 }; # ok
158              
159             use constant 1.03 { GG => 1, HH => 2 }; # ok
160              
161             The idea is to keep you from using the multi-constant feature in code which
162             might run on Perl 5.6, or might in principle still run there. On that basis
163             this policy is under the "compatibility" theme (see L<Perl::Critic/POLICY
164             THEMES>).
165              
166             If you declare C<constant 1.03> then the code can still run on Perl 5.6 and
167             perhaps earlier if the user gets a suitably newer C<constant> module from
168             CPAN. Or of course for past compatibility just don't use the hash style at
169             all!
170              
171             =head2 Details
172              
173             A version declaration must be before the first multi-constant, so it's
174             checked before the multi-constant is attempted and gives an obscure error.
175              
176             use constant { X => 1, Y => 2 }; # bad
177             use 5.008;
178              
179             A C<require> for the perl version is not enough since C<use constant> is at
180             C<BEGIN> time, before plain code.
181              
182             require 5.008; # doesn't run early enough
183             use constant { X => 1, Y => 2 }; # bad
184              
185             But a C<require> within a C<BEGIN> block is ok (a past style, still found
186             occasionally).
187              
188             BEGIN { require 5.008 }
189             use constant { X => 1, Y => 2 }; # ok
190              
191             BEGIN {
192             require 5.008;
193             and_other_setups ...;
194             }
195             use constant { X => 1, Y => 2 }; # ok
196              
197             Currently C<ConstantPragmaHash> pays no attention to any conditionals within
198             the C<BEGIN>, it assumes any C<require> there always runs. It could be
199             tricked by some obscure tests but hopefully anything like that is rare or
200             does the right thing anyway.
201              
202             A quoted version number like
203              
204             use constant '1.03'; # no good
205              
206             is no good, only a bare number is recognised by C<use> and acted on by
207             ConstantPragmaHash. A string like that goes through to C<constant> as if a
208             name to define (which you'll see it objects to as soon as you try run it).
209              
210             =head2 Drawbacks
211              
212             Explicitly adding required version numbers in the code can be irritating,
213             especially if other things you're doing only run on 5.8 up anyway. But
214             declaring what code needs is accurate, it allows maybe for backports of
215             modules, and explicit versions can be grepped out to create or check
216             F<Makefile.PL> or F<Build.PL> prereqs.
217              
218             As always if you don't care about this or if you only ever use Perl 5.8
219             anyway then you can disable C<ConstantPragmaHash> from your F<.perlcriticrc>
220             in the usual way (see L<Perl::Critic/CONFIGURATION>),
221              
222             [-Compatibility::ConstantPragmaHash]
223              
224             =head1 SEE ALSO
225              
226             L<Perl::Critic::Pulp>,
227             L<Perl::Critic>,
228             L<Perl::Critic::Policy::Compatibility::ConstantLeadingUnderscore>,
229             L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma>,
230             L<Perl::Critic::Policy::Modules::RequirePerlVersion>
231              
232             L<constant>,
233             L<perlsub/Constant Functions>
234              
235             =head1 HOME PAGE
236              
237             L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>
238              
239             =head1 COPYRIGHT
240              
241             Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021 Kevin Ryde
242              
243             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
244             under the terms of the GNU General Public License as published by the Free
245             Software Foundation; either version 3, or (at your option) any later
246             version.
247              
248             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
249             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
250             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
251             more details.
252              
253             You should have received a copy of the GNU General Public License along with
254             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
255              
256             =cut