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   26283 use 5.010001;
  40         176  
4 40     40   245 use strict;
  40         112  
  40         851  
5 40     40   254 use warnings;
  40         124  
  40         1066  
6 40     40   276 use Readonly;
  40         158  
  40         2138  
7              
8 40     40   312 use List::SomeUtils qw(any);
  40         292  
  40         2057  
9              
10 40     40   283 use Perl::Critic::Utils qw{ :severities };
  40         122  
  40         2230  
11 40     40   5202 use parent 'Perl::Critic::Policy';
  40         134  
  40         268  
12              
13             our $VERSION = '1.150';
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 90     90 0 1617 sub supported_parameters { return () }
23 77     77 1 310 sub default_severity { return $SEVERITY_LOW }
24 86     86 1 388 sub default_themes { return qw(core pbp readability) }
25 30     30 1 83 sub applies_to { return 'PPI::Document' }
26              
27             #-----------------------------------------------------------------------------
28              
29             sub violates {
30 30     30 1 74 my ( $self, $elem, $doc ) = @_;
31              
32 30 100       141 return if $doc->find_first( \&_is_version_declaration );
33              
34             #If we get here, then no $VERSION was found
35 3         81 return $self->violation( $DESC, $EXPL, $doc );
36             }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub _is_version_declaration { ## no critic (ArgUnpacking)
41 575 100   575   5731 return 1 if _is_our_version(@_);
42 548 50       882 return 1 if _is_vars_version(@_);
43 548 50       2232 return 1 if _is_package_version(@_);
44 548 50       886 return 1 if _is_readonly_version(@_);
45 548 50       895 return 1 if _is_package_argument_version(@_);
46 548         870 return 0;
47             }
48              
49             #-----------------------------------------------------------------------------
50              
51             sub _is_our_version {
52 575     575   738 my (undef, $elem) = @_;
53 575 100       1759 $elem->isa('PPI::Statement::Variable') || return 0;
54 27 50       128 $elem->type() eq 'our' || return 0;
55 27     27   1266 return any { $_ eq '$VERSION' } $elem->variables(); ## no critic (RequireInterpolation)
  27         1572  
56             }
57              
58             #-----------------------------------------------------------------------------
59              
60             sub _is_vars_version {
61 548     548   729 my (undef, $elem) = @_;
62 548 100       1539 $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 548     548   762 my (undef, $elem) = @_;
71 548 100       1538 $elem->isa('PPI::Token::Symbol') || return 0;
72 5         14 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 548     548   731 my (undef, $elem) = @_;
88 548 100       1475 $elem->isa('PPI::Token::Symbol') || return 0;
89 5 50       13 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 548     548   754 my (undef, $elem) = @_;
102 548 100       1557 $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       140 my $ver = $elem->schild( 2 )
107             or return 0;
108 28         602 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 :