File Coverage

blib/lib/Perl/Critic/Policy/Compatibility/ConstantLeadingUnderscore.pm
Criterion Covered Total %
statement 70 72 97.2
branch 26 30 86.6
condition 21 27 77.7
subroutine 14 15 93.3
pod 1 1 100.0
total 132 145 91.0


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019 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::ConstantLeadingUnderscore;
18 40     40   29139 use 5.006;
  40         139  
19 40     40   192 use strict;
  40         105  
  40         790  
20 40     40   184 use warnings;
  40         92  
  40         1091  
21 40     40   193 use base 'Perl::Critic::Policy';
  40         83  
  40         4314  
22 40     40   198909 use Perl::Critic::Utils;
  40         80  
  40         574  
23 40     40   33056 use Perl::Critic::Pulp::Utils;
  40         88  
  40         1644  
24 40     40   789 use Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders;
  40         95  
  40         1234  
25 40     40   214 use version (); # but don't import qv()
  40         143  
  40         1318  
26              
27             # uncomment this to run the ### lines
28             # use Smart::Comments;
29              
30             our $VERSION = 97;
31              
32 40     40   199 use constant supported_parameters => ();
  40         106  
  40         2125  
33 40     40   239 use constant default_severity => $Perl::Critic::Utils::SEVERITY_MEDIUM;
  40         84  
  40         2271  
34 40     40   230 use constant default_themes => qw(pulp compatibility);
  40         78  
  40         2075  
35 40     40   214 use constant applies_to => 'PPI::Document';
  40         82  
  40         21718  
36              
37             my $perl_ok_version = version->new('5.006');
38             my $constant_ok_version = version->new('1.02');
39              
40             sub violates {
41 28     28 1 775272 my ($self, $elem, $document) = @_;
42              
43 28         79 my @violations;
44             my $perlver; # a "version" object
45 28         0 my $modver; # a "version" object
46              
47 28   100     139 my $aref = $document->find ('PPI::Statement::Include')
48             || return; # if no includes at all
49 27         321 foreach my $inc (@$aref) {
50              
51 47 100 66     2148 $inc->type eq 'use'
      100        
52             || ($inc->type eq 'require'
53             && Perl::Critic::Pulp::Utils::elem_in_BEGIN($inc))
54             || next;
55              
56 46 100       1192 if (my $ver = $inc->version) {
57             # "use 5.006" etc perl version
58 10         252 $ver = version->new ($ver);
59 10 50 33     38 if (! defined $perlver || $ver > $perlver) {
60 10         113 $perlver = $ver; # maximum seen so-far
61              
62 10 100       82 if ($perlver >= $perl_ok_version) {
63             # adequate perl version demanded, stop here
64 7         127 last;
65             }
66             }
67 3         8 next;
68             }
69              
70 36 50 50     693 ($inc->module||'') eq 'constant' || next;
71              
72 36 100       860 if (my $ver = Perl::Critic::Pulp::Utils::include_module_version ($inc)) {
73             ### $ver
74             # PPI::Token::Number::Float
75 11         25 $ver = version->new ($ver->content);
76 11 50 33     99 if (! defined $modver || $ver > $modver) {
77 11         19 $modver = $ver;
78              
79 11 100       50 if ($modver >= $constant_ok_version) {
80             # adequate "constant" version demanded, stop here
81 5         10 last;
82             }
83             }
84             }
85              
86 31         70 my $name = _use_constant_single_name ($inc);
87 31 100 100     236 if (defined $name && $name =~ /^_/) {
88 20 100       131 push @violations, $self->violation
    100          
89             ("'use constant' with leading underscore requires perl 5.6 or constant 1.02 (at this point have "
90             . (defined $perlver ? "perl $perlver" : "no perl version")
91             . (defined $modver ? ", constant $modver)" : ", no constant version)"),
92             '',
93             $inc);
94             }
95             }
96              
97 27         1729 return @violations;
98             }
99              
100             # $inc is a PPI::Statement::Include with type "use" and module "constant".
101             # If it's a single-name "use constant foo => ..." then return the name
102             # string "foo". If it's a multi-constant or something unrecognised then
103             # return undef..
104             #
105             sub _use_constant_single_name {
106 71     71   207535 my ($inc) = @_;
107 71   100     197 my $arg = Perl::Critic::Pulp::Utils::include_module_first_arg ($inc)
108             || return undef; # empty "use constant" or version "use constant 1.05"
109              
110 61 100       188 if ($arg->isa('PPI::Token::Word')) {
111             # use constant FOO ...
112 28         62 return $arg->content;
113             }
114 33 100 100     180 if ($arg->isa('PPI::Token::Quote::Single')
115             || $arg->isa('PPI::Token::Quote::Literal')) {
116             # use constant 'FOO', ...
117             # use constant q{FOO}, ...
118 10         38 return $arg->literal;
119             }
120 23 100 100     115 if ($arg->isa('PPI::Token::Quote::Double')
121             || $arg->isa('PPI::Token::Quote::Interpolate')) {
122             # ENHANCE-ME: use $arg->interpolations() when available also on
123             # PPI::Token::Quote::Interpolate
124 9         31 my $str = $arg->string;
125 9 50       104 if (! Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders::_string_any_vars($str)) {
126             # use constant "FOO", ...
127             # use constant qq{FOO}, ...
128             # not quite right, but often close enough
129 9         26 return $str;
130             }
131             }
132             # a hash or an expression or something unrecognised
133 14         118 return undef;
134             }
135              
136             # $str is the contents of a "" or qq{} string
137             # return true if it has any $ or @ interpolation forms
138             sub _string_any_vars {
139 0     0     my ($str) = @_;
140 0           return ($str =~ /(^|[^\\])(\\\\)*[\$@]/);
141             }
142              
143             1;
144             __END__
145              
146             =for stopwords multi-constant multi-constants CPAN perl ok ConstantLeadingUnderscore backports prereqs Ryde subr inlined
147              
148             =head1 NAME
149              
150             Perl::Critic::Policy::Compatibility::ConstantLeadingUnderscore - new enough "constant" module for leading underscores
151              
152             =head1 DESCRIPTION
153              
154             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
155             add-on. It asks that if you have a constant with a leading underscore,
156              
157             use constant _FOO ... # leading underscore on name
158              
159             then you explicitly declare C<use 5.6> or C<use constant 1.02>, or higher,
160             since C<constant.pm> before that did not allow leading underscores.
161              
162             use constant _FOO => 123; # bad
163              
164             use 5.006;
165             use constant _FOO => 123; # ok
166              
167             use constant 1.02;
168             use constant _FOO => 123; # ok
169              
170             use constant 1.02 _FOO => 123; # ok
171              
172             The idea is to avoid trouble in code which might run on Perl 5.005, or might
173             in principle still run there. On that basis this policy is under the
174             "compatibility" theme (see L<Perl::Critic/POLICY THEMES>).
175              
176             Asking for the new enough module C<use constant 1.02> is suggested, since
177             it's the module feature which is required and the code might then still run
178             on Perl 5.005 or earlier if the user has a suitable C<constant.pm> from
179             CPAN.
180              
181             =head2 Details
182              
183             A version declaration must be before the first leading underscore, so it's
184             checked before the underscore is attempted (and would give an error).
185              
186             use constant _FOO => 123; # bad
187             use 5.006;
188              
189             A C<require> for the Perl version is not enough since C<use constant> is at
190             C<BEGIN> time, before plain code.
191              
192             require 5.006; # doesn't run early enough
193             use constant _FOO => 123; # bad
194              
195             But a C<require> within a C<BEGIN> block is ok (a past style, still found
196             occasionally).
197              
198             BEGIN { require 5.006 }
199             use constant _FOO => 123; # ok
200              
201             BEGIN {
202             require 5.006;
203             and_other_setups ...;
204             }
205             use constant _FOO => 123; # ok
206              
207             Currently C<ConstantLeadingUnderscore> pays no attention to any conditionals
208             within the C<BEGIN>, it assumes any C<require> there always runs. It might
209             be tricked by obscure tests but hopefully anything like that is rare or does
210             the right thing anyway.
211              
212             A quoted version number like
213              
214             use constant '1.02'; # no good
215              
216             is no good, only a bare number is recognised by the C<use> statement as a
217             version check. A string like that in fact goes through to C<constant> as a
218             name to define, and which it will reject.
219              
220             Leading underscores in a multi-constant hash are not flagged, since new
221             enough C<constant.pm> to have multi-constants is new enough to have
222             underscores. See
223             L<Compatibility::ConstantPragmaHash|Perl::Critic::Policy::Compatibility::ConstantPragmaHash>
224             for multi-constants version check.
225              
226             use constant { _FOO => 1 }; # not checked
227              
228             Leading double-underscore is disallowed by all versions of C<constant.pm>.
229             That's not reported by this policy since the code won't run at all.
230              
231             use constant __FOO => 123; # not allowed by any constant.pm
232              
233             =head2 Drawbacks
234              
235             Explicitly adding required version numbers in the code can be irritating,
236             especially if other things you're doing only run on 5.6 up anyway. But
237             declaring what code needs is accurate, it allows maybe for backports of
238             modules, and explicit versions can be grepped out to create or check
239             F<Makefile.PL> or F<Build.PL> prereqs.
240              
241             As always, if you don't care about this or if you only ever use Perl 5.6
242             anyway then you can disable C<ConstantLeadingUnderscore> from your
243             F<.perlcriticrc> in the usual way (see L<Perl::Critic/CONFIGURATION>),
244              
245             [-Compatibility::ConstantLeadingUnderscore]
246              
247             =head1 OTHER WAYS TO DO IT
248              
249             It's easy to write your own constant subr and it can have any name at all
250             (anything acceptable to Perl), bypassing the sanity checks or restrictions
251             in C<constant.pm>. Only the C<()> prototype is a bit obscure.
252              
253             sub _FOO () { return 123 }
254              
255             The key benefit of subs like this, whether from C<constant.pm> or
256             explicitly, is that the value is inlined and can be constant-folded in an
257             arithmetic expression etc (see L<perlsub/Constant Functions>).
258              
259             print 2*_FOO; # folded to 246 at compile-time
260              
261             The purpose of a leading underscore is normally a hint that the sub is meant
262             to be private to the module and/or its friends. If you don't need the
263             constant folding then a C<my> scalar is even more private, being invisible
264             to anything outside relevant scope,
265              
266             my $FOO = 123; # more private
267             # ...
268             do_something ($FOO); # nothing to constant-fold anyway
269              
270             The scalar returned from C<constant.pm> subs is flagged read-only, which
271             might prevent accidental mis-use when passed around. The C<Readonly> module
272             gives the same effect on variables. If you have C<Readonly::XS> then it's
273             just a flag too (no performance penalty on using the value).
274              
275             use Readonly;
276             Readonly::Scalar my $FOO => 123;
277              
278             =head1 SEE ALSO
279              
280             L<Perl::Critic::Pulp>,
281             L<Perl::Critic>,
282             L<Perl::Critic::Policy::Compatibility::ConstantPragmaHash>,
283             L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitConstantPragma>,
284             L<Perl::Critic::Policy::Modules::RequirePerlVersion>
285              
286             L<constant>, L<perlsub/Constant Functions>
287              
288             =head1 HOME PAGE
289              
290             L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>
291              
292             =head1 COPYRIGHT
293              
294             Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019 Kevin Ryde
295              
296             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
297             under the terms of the GNU General Public License as published by the Free
298             Software Foundation; either version 3, or (at your option) any later
299             version.
300              
301             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
302             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
303             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
304             more details.
305              
306             You should have received a copy of the GNU General Public License along with
307             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
308              
309             =cut