File Coverage

blib/lib/Perl/Critic/Policy/Modules/RequireVersionVar.pm
Criterion Covered Total %
statement 56 56 100.0
branch 31 32 96.8
condition 4 7 57.1
subroutine 19 19 100.0
pod 4 5 80.0
total 114 119 95.8


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Modules::RequireVersionVar;
2              
3 40     40   27306 use 5.010001;
  40         200  
4 40     40   255 use strict;
  40         102  
  40         808  
5 40     40   213 use warnings;
  40         103  
  40         1076  
6 40     40   238 use Readonly;
  40         106  
  40         2083  
7              
8 40     40   356 use List::SomeUtils qw(any);
  40         145  
  40         2038  
9              
10 40     40   269 use Perl::Critic::Utils qw{ :severities };
  40         148  
  40         2295  
11 40     40   5551 use parent 'Perl::Critic::Policy';
  40         105  
  40         242  
12              
13             our $VERSION = '1.146';
14              
15             #-----------------------------------------------------------------------------
16              
17             Readonly::Scalar my $DESC => q{No package-scoped "$VERSION" variable found}; ## no critic (RequireInterpolation)
18             Readonly::Scalar my $EXPL => [ 404 ];
19              
20             #-----------------------------------------------------------------------------
21              
22 104     104 0 1772 sub supported_parameters { return () }
23 83     83 1 475 sub default_severity { return $SEVERITY_LOW }
24 86     86 1 410 sub default_themes { return qw(core pbp readability) }
25 44     44 1 159 sub applies_to { return 'PPI::Document' }
26              
27             #-----------------------------------------------------------------------------
28              
29             sub violates {
30 44     44 1 161 my ( $self, $elem, $doc ) = @_;
31              
32 44 100       214 return if $doc->find_first( \&_is_version_declaration );
33              
34             #If we get here, then no $VERSION was found
35 9         181 return $self->violation( $DESC, $EXPL, $doc );
36             }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _is_version_declaration { ## no critic (ArgUnpacking)
41 653 100   653   6999 return 1 if _is_our_version(@_);
42 624 100       1226 return 1 if _is_vars_version(@_);
43 622 100       2968 return 1 if _is_package_version(@_);
44 621 100       1197 return 1 if _is_readonly_version(@_);
45 619 100       1233 return 1 if _is_package_argument_version(@_);
46 618         1215 return 0;
47             }
48              
49             #-----------------------------------------------------------------------------
50              
51             sub _is_our_version {
52 653     653   1076 my (undef, $elem) = @_;
53 653 100       2410 $elem->isa('PPI::Statement::Variable') || return 0;
54 31 100       220 $elem->type() eq 'our' || return 0;
55 30     30   1736 return any { $_ eq '$VERSION' } $elem->variables(); ## no critic (RequireInterpolation)
  30         2302  
56             }
57              
58             #-----------------------------------------------------------------------------
59              
60             sub _is_vars_version {
61 624     624   998 my (undef, $elem) = @_;
62 624 100       2015 $elem->isa('PPI::Statement::Include') || return 0;
63 57 100       267 $elem->pragma() eq 'vars' || return 0;
64 2         91 return $elem =~ m{ \$VERSION }xms; #Crude, but usually works
65             }
66              
67             #-----------------------------------------------------------------------------
68              
69             sub _is_package_version {
70 622     622   1026 my (undef, $elem) = @_;
71 622 100       2011 $elem->isa('PPI::Token::Symbol') || return 0;
72 11         31 return $elem =~ m{ \A \$ \S+ ::VERSION \z }xms;
73             #TODO: ensure that it is in _this_ package!
74             }
75              
76             #-----------------------------------------------------------------------------
77              
78             sub _is_readonly_version {
79              
80             #---------------------------------------------------------------
81             # Readonly VERSION statements usually come in one of two forms:
82             #
83             # Readonly our $VERSION = 1.0;
84             # Readonly::Scalar our $VERSION = 1.0;
85             #---------------------------------------------------------------
86              
87 621     621   987 my (undef, $elem) = @_;
88 621 100       1976 $elem->isa('PPI::Token::Symbol') || return 0;
89 10 100       41 return 0 if $elem !~ m{ \A \$VERSION \z }xms;
90              
91 4   50     36 my $psib = $elem->sprevious_sibling() || return 0;
92 4 100       170 return 0 if $psib ne 'our';
93              
94 2   50     48 my $ppsib = $psib->sprevious_sibling() || return 0;
95 2   66     49 return $ppsib eq 'Readonly' || $ppsib eq 'Readonly::Scalar';
96             }
97              
98             #-----------------------------------------------------------------------------
99              
100             sub _is_package_argument_version {
101 619     619   1040 my (undef, $elem) = @_;
102 619 100       2047 $elem->isa( 'PPI::Statement::Package' ) or return 0;
103             # Perldoc for 5.12.3 documents the statement as
104             # package NAMESPACE VERSION
105             # with no comma, and the compiler in fact does not accept one.
106 30 50       136 my $ver = $elem->schild( 2 )
107             or return 0;
108 30         842 return $ver->isa( 'PPI::Token::Number' );
109             }
110              
111             1;
112              
113             __END__
114              
115             #-----------------------------------------------------------------------------
116              
117             =pod
118              
119             =head1 NAME
120              
121             Perl::Critic::Policy::Modules::RequireVersionVar - Give every module a C<$VERSION> number.
122              
123              
124             =head1 AFFILIATION
125              
126             This Policy is part of the core L<Perl::Critic|Perl::Critic>
127             distribution.
128              
129              
130             =head1 DESCRIPTION
131              
132             Every Perl file (modules, libraries, and programs) should have a
133             package-scoped C<$VERSION> variable. The C<$VERSION> allows clients to
134             insist on a particular revision of your file like this:
135              
136             use SomeModule 2.4; #Only loads version 2.4
137              
138             This Policy scans your file for any package variable named
139             C<$VERSION>. I'm assuming that you are using C<strict>, so you'll
140             have to declare it like one of these:
141              
142             our $VERSION = 1.0611;
143             $MyPackage::VERSION = 1.061;
144             use vars qw($VERSION);
145             use version; our $VERSION = qv(1.0611);
146              
147             Perl's version system does not recognize lexical variables such as
148              
149             my $VERSION = 1.0611;
150              
151             so they are not accepted by this policy.
152              
153             A common practice is to use the C<$Revision$> keyword to
154             automatically define the C<$VERSION> variable like this:
155              
156             our ($VERSION) = '$Revision$' =~ m{ \$Revision: \s+ (\S+) }x;
157              
158              
159             =head1 CONFIGURATION
160              
161             This Policy is not configurable except for the standard options.
162              
163              
164             =head1 NOTES
165              
166             Conway recommends using the C<version> pragma instead of raw numbers
167             or 'v-strings.' However, this Policy only insists that the
168             C<$VERSION> be defined somehow. I may try to extend this in the
169             future.
170              
171              
172             =head1 TO DO
173              
174             Add check that C<$VERSION> is independently evaluatable. In
175             particular, prohibit this:
176              
177             our $VERSION = $Other::Module::VERSION;
178              
179             This doesn't work because PAUSE and other tools literally copy your
180             version declaration out of your module and evaluates it in isolation,
181             at which point there's nothing in C<Other::Module>, and so the
182             C<$VERSION> is undefined.
183              
184              
185             =head1 AUTHOR
186              
187             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
188              
189              
190             =head1 COPYRIGHT
191              
192             Copyright (c) 2005-2021 Imaginative Software Systems. All rights reserved.
193              
194             This program is free software; you can redistribute it and/or modify
195             it under the same terms as Perl itself. The full text of this license
196             can be found in the LICENSE file included with this module.
197              
198             =cut
199              
200             # Local Variables:
201             # mode: cperl
202             # cperl-indent-level: 4
203             # fill-column: 78
204             # indent-tabs-mode: nil
205             # c-indentation-style: bsd
206             # End:
207             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :