File Coverage

blib/lib/Perl/Critic/Policy/Modules/RequireVersionVar.pm
Criterion Covered Total %
statement 51 56 91.0
branch 22 32 68.7
condition 0 7 0.0
subroutine 19 19 100.0
pod 4 5 80.0
total 96 119 80.6


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Modules::RequireVersionVar;
2              
3 40     40   24390 use 5.010001;
  40         139  
4 40     40   196 use strict;
  40         67  
  40         821  
5 40     40   163 use warnings;
  40         71  
  40         1444  
6 40     40   149 use Readonly;
  40         83  
  40         2128  
7              
8 40     40   295 use List::SomeUtils qw(any);
  40         115  
  40         1995  
9              
10 40     40   204 use Perl::Critic::Utils qw{ :severities };
  40         68  
  40         1938  
11 40     40   4498 use parent 'Perl::Critic::Policy';
  40         86  
  40         226  
12              
13             our $VERSION = '1.156';
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 91     91 0 625 sub supported_parameters { return () }
23 79     79 1 239 sub default_severity { return $SEVERITY_LOW }
24 86     86 1 231 sub default_themes { return qw(core pbp readability) }
25 31     31 1 76 sub applies_to { return 'PPI::Document' }
26              
27             #-----------------------------------------------------------------------------
28              
29             sub violates {
30 31     31 1 95 my ( $self, undef, $doc ) = @_;
31              
32 31 100       108 return if $doc->find_first( \&_is_version_declaration );
33              
34             #If we get here, then no $VERSION was found
35 4         74 return $self->violation( $DESC, $EXPL, $doc );
36             }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _is_version_declaration { ## no critic (ArgUnpacking)
41 592 100   592   4514 return 1 if _is_our_version(@_);
42 565 50       769 return 1 if _is_vars_version(@_);
43 565 50       2088 return 1 if _is_package_version(@_);
44 565 50       720 return 1 if _is_readonly_version(@_);
45 565 50       767 return 1 if _is_package_argument_version(@_);
46 565         778 return 0;
47             }
48              
49             #-----------------------------------------------------------------------------
50              
51             sub _is_our_version {
52 592     592   704 my (undef, $elem) = @_;
53 592 100       1606 $elem->isa('PPI::Statement::Variable') || return 0;
54 27 50       139 $elem->type() eq 'our' || return 0;
55 27     27   1238 return any { $_ eq '$VERSION' } $elem->variables(); ## no critic (RequireInterpolation)
  27         1321  
56             }
57              
58             #-----------------------------------------------------------------------------
59              
60             sub _is_vars_version {
61 565     565   625 my (undef, $elem) = @_;
62 565 100       1273 $elem->isa('PPI::Statement::Include') || return 0;
63 55 50       131 $elem->pragma() eq 'vars' || return 0;
64 0         0 return $elem =~ m{ \$VERSION }xms; #Crude, but usually works
65             }
66              
67             #-----------------------------------------------------------------------------
68              
69             sub _is_package_version {
70 565     565   629 my (undef, $elem) = @_;
71 565 100       1345 $elem->isa('PPI::Token::Symbol') || return 0;
72 6         21 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 565     565   589 my (undef, $elem) = @_;
88 565 100       1224 $elem->isa('PPI::Token::Symbol') || return 0;
89 6 50       17 return 0 if $elem !~ m{ \A \$VERSION \z }xms;
90              
91 0   0     0 my $psib = $elem->sprevious_sibling() || return 0;
92 0 0       0 return 0 if $psib ne 'our';
93              
94 0   0     0 my $ppsib = $psib->sprevious_sibling() || return 0;
95 0   0     0 return $ppsib eq 'Readonly' || $ppsib eq 'Readonly::Scalar';
96             }
97              
98             #-----------------------------------------------------------------------------
99              
100             sub _is_package_argument_version {
101 565     565   643 my (undef, $elem) = @_;
102 565 100       1274 $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 28 50       151 my $ver = $elem->schild( 2 )
107             or return 0;
108 28         530 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 :