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, 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::ConstantLeadingUnderscore;
18 40     40   34092 use 5.006;
  40         169  
19 40     40   243 use strict;
  40         96  
  40         863  
20 40     40   205 use warnings;
  40         91  
  40         1195  
21 40     40   229 use base 'Perl::Critic::Policy';
  40         102  
  40         5039  
22 40     40   180569 use Perl::Critic::Utils;
  40         109  
  40         834  
23 40     40   37915 use Perl::Critic::Pulp::Utils;
  40         139  
  40         1722  
24 40     40   828 use Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders;
  40         105  
  40         1366  
25 40     40   255 use version (); # but don't import qv()
  40         102  
  40         1619  
26              
27             # uncomment this to run the ### lines
28             # use Smart::Comments;
29              
30             our $VERSION = 99;
31              
32 40     40   234 use constant supported_parameters => ();
  40         97  
  40         2971  
33 40     40   274 use constant default_severity => $Perl::Critic::Utils::SEVERITY_MEDIUM;
  40         110  
  40         2881  
34 40     40   274 use constant default_themes => qw(pulp compatibility);
  40         97  
  40         2518  
35 40     40   269 use constant applies_to => 'PPI::Document';
  40         95  
  40         26779  
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 672654 my ($self, $elem, $document) = @_;
42              
43 28         94 my @violations;
44             my $perlver; # a "version" object
45 28         0 my $modver; # a "version" object
46              
47 28   100     84 my $aref = $document->find ('PPI::Statement::Include')
48             || return; # if no includes at all
49 27         368 foreach my $inc (@$aref) {
50              
51 47 100 66     2418 $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       1179 if (my $ver = $inc->version) {
57             # "use 5.006" etc perl version
58 10         296 $ver = version->new ($ver);
59 10 50 33     37 if (! defined $perlver || $ver > $perlver) {
60 10         17 $perlver = $ver; # maximum seen so-far
61              
62 10 100       84 if ($perlver >= $perl_ok_version) {
63             # adequate perl version demanded, stop here
64 7         16 last;
65             }
66             }
67 3         11 next;
68             }
69              
70 36 50 50     845 ($inc->module||'') eq 'constant' || next;
71              
72 36 100       854 if (my $ver = Perl::Critic::Pulp::Utils::include_module_version ($inc)) {
73             ### $ver
74             # PPI::Token::Number::Float
75 11         31 $ver = version->new ($ver->content);
76 11 50 33     122 if (! defined $modver || $ver > $modver) {
77 11         24 $modver = $ver;
78              
79 11 100       57 if ($modver >= $constant_ok_version) {
80             # adequate "constant" version demanded, stop here
81 5         16 last;
82             }
83             }
84             }
85              
86 31         114 my $name = _use_constant_single_name ($inc);
87 31 100 100     268 if (defined $name && $name =~ /^_/) {
88 20 100       146 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         2060 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   146561 my ($inc) = @_;
107 71   100     252 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       219 if ($arg->isa('PPI::Token::Word')) {
111             # use constant FOO ...
112 28         83 return $arg->content;
113             }
114 33 100 100     192 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         41 return $arg->literal;
119             }
120 23 100 100     123 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         49 my $str = $arg->string;
125 9 50       113 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         29 return $str;
130             }
131             }
132             # a hash or an expression or something unrecognised
133 14         35 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, 2021 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