File Coverage

blib/lib/Perl/Critic/Policy/Modules/ProhibitEvilModules.pm
Criterion Covered Total %
statement 85 87 97.7
branch 24 30 80.0
condition 12 16 75.0
subroutine 18 18 100.0
pod 5 6 83.3
total 144 157 91.7


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Modules::ProhibitEvilModules;
2              
3 40     40   27174 use 5.010001;
  40         158  
4 40     40   3025 use strict;
  40         171  
  40         983  
5 40     40   226 use warnings;
  40         83  
  40         1135  
6              
7 40     40   212 use English qw(-no_match_vars);
  40         95  
  40         248  
8 40     40   14470 use Readonly;
  40         89  
  40         2083  
9              
10             use Perl::Critic::Exception::Configuration::Option::Policy::ParameterValue
11 40     40   292 qw{ throw_policy_value };
  40         88  
  40         1078  
12 40         2091 use Perl::Critic::Utils qw{
13             :booleans :characters :severities :data_conversion
14 40     40   2381 };
  40         144  
15              
16 40     40   14419 use parent 'Perl::Critic::Policy';
  40         107  
  40         231  
17              
18             our $VERSION = '1.148';
19              
20             #-----------------------------------------------------------------------------
21              
22             Readonly::Scalar my $EXPL => q{Find an alternative module};
23              
24             Readonly::Scalar my $MODULE_NAME_REGEX =>
25             qr<
26             \b
27             [[:alpha:]_]
28             (?:
29             (?: \w | :: )*
30             \w
31             )?
32             \b
33             >xms;
34             Readonly::Scalar my $REGULAR_EXPRESSION_REGEX => qr< [/] ( [^/]+ ) [/] >xms;
35             Readonly::Scalar my $DESCRIPTION_REGEX => qr< [{] ( [^}]+ ) [}] >xms;
36              
37             # It's kind of unfortunate that I had to put capturing parentheses in the
38             # component regexes above, because they're not visible here and so make
39             # figuring out the positions of captures hard. Too bad we can't make the
40             # minimum perl version 5.10. :]
41             Readonly::Scalar my $MODULES_REGEX =>
42             qr<
43             \A
44             \s*
45             (?:
46             ( $MODULE_NAME_REGEX )
47             | $REGULAR_EXPRESSION_REGEX
48             )
49             (?: \s* $DESCRIPTION_REGEX )?
50             \s*
51             >xms;
52              
53             Readonly::Scalar my $MODULES_FILE_LINE_REGEX =>
54             qr<
55             \A
56             \s*
57             (?:
58             ( $MODULE_NAME_REGEX )
59             | $REGULAR_EXPRESSION_REGEX
60             )
61             \s*
62             ( \S (?: .* \S )? )?
63             \s*
64             \z
65             >xms;
66              
67             Readonly::Scalar my $DEFAULT_MODULES =>
68             join
69             $SPACE,
70             map { "$_ {Found use of $_. This module is deprecated by the Perl 5 Porters.}" }
71             qw< Class::ISA Pod::Plainer Shell Switch >;
72              
73             # Indexes in the arrays of regexes for the "modules" option.
74             Readonly::Scalar my $INDEX_REGEX => 0;
75             Readonly::Scalar my $INDEX_DESCRIPTION => 1;
76              
77             #-----------------------------------------------------------------------------
78              
79             sub supported_parameters {
80             return (
81             {
82 107     107 0 2582 name => 'modules',
83             description => 'The names of or patterns for modules to forbid.',
84             default_string => $DEFAULT_MODULES,
85             parser => \&_parse_modules,
86             },
87             {
88             name => 'modules_file',
89             description => 'A file containing names of or patterns for modules to forbid.',
90             default_string => $EMPTY,
91             parser => \&_parse_modules_file,
92             },
93             );
94             }
95              
96 100     100 1 427 sub default_severity { return $SEVERITY_HIGHEST }
97 74     74 1 372 sub default_themes { return qw( core bugs certrule ) }
98 49     49 1 148 sub applies_to { return 'PPI::Statement::Include' }
99              
100             #-----------------------------------------------------------------------------
101              
102             sub _parse_modules {
103 105     105   466 my ($self, $parameter, $config_string) = @_;
104              
105 105   66     747 my $module_specifications = $config_string // $parameter->get_default_string();
106              
107 105 100       540 return if not $module_specifications;
108 104 50       758 return if $module_specifications =~ m< \A \s* \z >xms;
109              
110 104         3892 while ( $module_specifications =~ s< $MODULES_REGEX ><>xms ) {
111 399         1816 my ($module, $regex_string, $description) = ($1, $2, $3);
112              
113 399         1399 $self->_handle_module_specification(
114             module => $module,
115             regex_string => $regex_string,
116             description => $description,
117             option_name => 'modules',
118             option_value => $config_string,
119             );
120             }
121              
122 103 50       617 if ($module_specifications) {
123 0         0 throw_policy_value
124             policy => $self->get_short_name(),
125             option_name => 'modules',
126             option_value => $config_string,
127             message_suffix =>
128             qq{contains unparseable data: "$module_specifications"};
129             }
130              
131 103         392 return;
132             }
133              
134             sub _parse_modules_file {
135 105     105   608 my ($self, $parameter, $config_string) = @_;
136              
137 105 100       446 return if not $config_string;
138 2 50       10 return if $config_string =~ m< \A \s* \z >xms;
139              
140 2 50       209 open my $handle, '<', $config_string
141             or throw_policy_value
142             policy => $self->get_short_name(),
143             option_name => 'modules_file',
144             option_value => $config_string,
145             message_suffix =>
146             qq<refers to a file that could not be opened: $OS_ERROR>;
147 2         11703 while ( my $line = <$handle> ) {
148 13         42 $self->_handle_module_specification_on_line($line, $config_string);
149             }
150 2 50       44 close $handle or warn qq<Could not close "$config_string": $OS_ERROR\n>;
151              
152 2         79 return;
153             }
154              
155             sub _handle_module_specification_on_line {
156 13     13   38 my ($self, $line, $config_string) = @_;
157              
158 13         59 $line =~ s< [#] .* \z ><>xms;
159 13         59 $line =~ s< \s+ \z ><>xms;
160 13         52 $line =~ s< \A \s+ ><>xms;
161              
162 13 100       46 return if not $line;
163              
164 6 50       234 if ( $line =~ s< $MODULES_FILE_LINE_REGEX ><>xms ) {
165 6         30 my ($module, $regex_string, $description) = ($1, $2, $3);
166              
167 6         20 $self->_handle_module_specification(
168             module => $module,
169             regex_string => $regex_string,
170             description => $description,
171             option_name => 'modules_file',
172             option_value => $config_string,
173             );
174             }
175             else {
176 0         0 throw_policy_value
177             policy => $self->get_short_name(),
178             option_name => 'modules_file',
179             option_value => $config_string,
180             message_suffix =>
181             qq{contains unparseable data: "$line"};
182             }
183              
184 6         55 return;
185             }
186              
187             sub _handle_module_specification {
188 405     405   1896 my ($self, %arguments) = @_;
189              
190 405   66     1266 my $description = $arguments{description} || $EMPTY;
191              
192 405 100       1007 if ( my $regex_string = $arguments{regex_string} ) {
193             # These are module name patterns (e.g. /Acme/)
194 11         18 my $actual_regex;
195              
196 11         200 eval { $actual_regex = qr/$regex_string/; 1 } ## no critic (ExtendedFormatting, LineBoundaryMatching, DotMatchAnything)
  10         35  
197             or throw_policy_value
198             policy => $self->get_short_name(),
199             option_name => $arguments{option_name},
200             option_value => $arguments{option_value},
201 11 100       20 message_suffix =>
202             qq{contains an invalid regular expression: "$regex_string"};
203              
204             # Can't use a hash due to stringification, so this is an AoA.
205 10   100     49 $self->{_evil_modules_regexes} ||= [];
206              
207             push
208 10         19 @{ $self->{_evil_modules_regexes} },
  10         35  
209             [ $actual_regex, $description ];
210             }
211             else {
212             # These are literal module names (e.g. Acme::Foo)
213 394   100     1309 $self->{_evil_modules} ||= {};
214 394         1348 $self->{_evil_modules}{ $arguments{module} } = $description;
215             }
216              
217 404         6346 return;
218             }
219              
220             #-----------------------------------------------------------------------------
221              
222             sub initialize_if_enabled {
223 85     85 1 344 my ($self, $config) = @_;
224              
225             # Disable if no modules are specified; there's no point in running if
226             # there aren't any.
227             return
228             exists $self->{_evil_modules}
229 85   66     528 || exists $self->{_evil_modules_regexes};
230             }
231              
232             #-----------------------------------------------------------------------------
233              
234             sub violates {
235 90     90 1 246 my ( $self, $elem, undef ) = @_;
236              
237 90         317 my $module = $elem->module();
238 90 100       2363 return if not $module;
239              
240 88         236 my $evil_modules = $self->{_evil_modules};
241 88         182 my $evil_modules_regexes = $self->{_evil_modules_regexes};
242 88         146 my $description;
243              
244 88 100       254 if ( exists $evil_modules->{$module} ) {
245 12         26 $description = $evil_modules->{ $module };
246             }
247             else {
248             REGEX:
249 76         142 foreach my $regex ( @{$evil_modules_regexes} ) {
  76         216  
250 20 100       128 if ( $module =~ $regex->[$INDEX_REGEX] ) {
251 14         32 $description = $regex->[$INDEX_DESCRIPTION];
252 14         29 last REGEX;
253             }
254             }
255             }
256              
257 88 100       252 if (defined $description) {
258 26   66     107 $description ||= qq<Prohibited module "$module" used>;
259              
260 26         81 return $self->violation( $description, $EXPL, $elem );
261             }
262              
263 62         194 return; # ok!
264             }
265              
266             1;
267              
268             __END__
269              
270             #-----------------------------------------------------------------------------
271              
272             =pod
273              
274             =head1 NAME
275              
276             Perl::Critic::Policy::Modules::ProhibitEvilModules - Ban modules that aren't blessed by your shop.
277              
278              
279             =head1 AFFILIATION
280              
281             This Policy is part of the core L<Perl::Critic|Perl::Critic>
282             distribution.
283              
284              
285             =head1 DESCRIPTION
286              
287             Use this policy if you wish to prohibit the use of specific modules.
288             These may be modules that you feel are deprecated, buggy, unsupported,
289             insecure, or just don't like.
290              
291              
292             =head1 CONFIGURATION
293              
294             The set of prohibited modules is configurable via the C<modules> and
295             C<modules_file> options.
296              
297             The value of C<modules> should be a string of space-delimited, fully
298             qualified module names and/or regular expressions. An example of
299             prohibiting two specific modules in a F<.perlcriticrc> file:
300              
301             [Modules::ProhibitEvilModules]
302             modules = Getopt::Std Autoload
303              
304             Regular expressions are identified by values beginning and ending with
305             slashes. Any module with a name that matches C<m/pattern/> will be
306             forbidden. For example:
307              
308             [Modules::ProhibitEvilModules]
309             modules = /Acme::/
310              
311             would cause all modules that match C<m/Acme::/> to be forbidden.
312              
313             In addition, you can override the default message ("Prohibited module
314             "I<module>" used") with your own, in order to give suggestions for
315             alternative action. To do so, put your message in curly braces after
316             the module name or regular expression. Like this:
317              
318             [Modules::ProhibitEvilModules]
319             modules = Fatal {Found use of Fatal. Use autodie instead} /Acme::/ {We don't use joke modules}
320              
321             Similarly, the C<modules_file> option gives the name of a file
322             containing specifications for prohibited modules. Only one module
323             specification is allowed per line and comments start with an octothorp
324             and run to end of line; no curly braces are necessary for delimiting
325             messages:
326              
327             Evil # Prohibit the "Evil" module and use the default message.
328              
329             # Prohibit the "Fatal" module and give a replacement message.
330             Fatal Found use of Fatal. Use autodie instead.
331              
332             # Use a regular expression.
333             /Acme::/ We don't use joke modules.
334              
335             By default, the modules that have been deprecated by the Perl 5 Porters are
336             reported; at the time of writing these are L<Class::ISA|Class::ISA>,
337             L<Pod::Plainer|Pod::Plainer>, L<Shell|Shell>, and L<Switch|Switch>.
338             Specifying a value for the C<modules> option will override this.
339              
340              
341             =head1 NOTES
342              
343             Note that this policy doesn't apply to pragmas.
344              
345              
346             =head1 AUTHOR
347              
348             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
349              
350              
351             =head1 COPYRIGHT
352              
353             Copyright (c) 2005-2011 Imaginative Software Systems. All rights reserved.
354              
355             This program is free software; you can redistribute it and/or modify
356             it under the same terms as Perl itself. The full text of this license
357             can be found in the LICENSE file included with this module.
358              
359             =cut
360              
361             # Local Variables:
362             # mode: cperl
363             # cperl-indent-level: 4
364             # fill-column: 78
365             # indent-tabs-mode: nil
366             # c-indentation-style: bsd
367             # End:
368             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :