File Coverage

blib/lib/Perl/Critic/Policy/Dynamic/ValidateAgainstSymbolTable.pm
Criterion Covered Total %
statement 150 161 93.1
branch 64 80 80.0
condition 8 13 61.5
subroutine 23 25 92.0
pod 5 6 83.3
total 250 285 87.7


line stmt bran cond sub pod time code
1             ##############################################################################
2             # $URL: http://perlcritic.tigris.org/svn/perlcritic/tags/Perl-Critic-Dynamic-0.05/lib/Perl/Critic/Policy/Dynamic/ValidateAgainstSymbolTable.pm $
3             # $Date: 2010-09-24 12:32:37 -0700 (Fri, 24 Sep 2010) $
4             # $Author: thaljef $
5             # $Revision: 3935 $
6             ##############################################################################
7              
8             package Perl::Critic::Policy::Dynamic::ValidateAgainstSymbolTable;
9              
10 56     56   10983115 use strict;
  55         71024  
  55         4840  
11 34     34   171 use warnings;
  34         68  
  34         891  
12              
13 34     34   243 use base 'Perl::Critic::DynamicPolicy';
  34         68  
  34         19580  
14              
15 34     34   474 use Carp qw(confess);
  34         35  
  34         2240  
16 34     34   172 use English qw(-no_match_vars);
  34         68  
  34         303  
17 34     34   19620 use Devel::Symdump ();
  34         68  
  34         740  
18 34     34   170 use Readonly ();
  34         68  
  34         855  
19              
20 34         2201 use Perl::Critic::Utils qw(
21             :severities
22             &hashify
23             &is_function_call
24             &is_perl_builtin
25             &policy_short_name
26 34     34   170 );
  34         35  
27              
28             #-----------------------------------------------------------------------------
29              
30             our $VERSION = 0.05;
31              
32             #-----------------------------------------------------------------------------
33              
34             Readonly::Scalar my $AMPERSAND => q{&};
35             Readonly::Scalar my $FAKE_NAMESPACE => '__FAKE_NAMESPACE__';
36             Readonly::Scalar my $CONFIG_PATH_SPLIT_REGEX => qr/ \s* [|] \s* /xms;
37             Readonly::Hash my %GLOBAL_PACKAGES => hashify(qw(UNIVERSAL CORE));
38              
39             #-----------------------------------------------------------------------------
40              
41 38     38 1 969 sub default_severity { return $SEVERITY_HIGH }
42 0     0 1 0 sub default_themes { return qw( dynamic bugs ) }
43 560     560 1 5539979 sub applies_to { return 'PPI::Document' }
44              
45             sub supported_parameters {
46 0     0 0 0 return qw(
47             at_inc
48             at_inc_prefix
49             at_inc_suffix
50             max_recursion
51             inspect_autoloaders
52             inspect_required_modules
53             );
54             }
55              
56             #-----------------------------------------------------------------------------
57              
58             sub new {
59              
60 561     561 1 4294640 my ( $class, %args ) = @_;
61 561         2996 my $self = bless {}, $class;
62              
63             # Configure @INC list...
64 561 100       8147 my @at_inc_prefix = defined $args{at_inc_prefix} ?
65             split $CONFIG_PATH_SPLIT_REGEX, $args{at_inc_prefix} : ();
66              
67 561 100       2487 my @at_inc_suffix = defined $args{at_inc_suffix} ?
68             split $CONFIG_PATH_SPLIT_REGEX, $args{at_inc_suffix} : ();
69              
70 561 100       6093 my @at_inc = defined $args{at_inc} ?
71             split $CONFIG_PATH_SPLIT_REGEX, $args{at_inc} : @INC;
72              
73 561         10834 $self->{_inc} = [@at_inc_prefix, @at_inc, @at_inc_suffix];
74              
75              
76             # Other configurations...
77 561 100       6969 $self->{_max_recursion} = defined $args{max_recursion} ?
78             $args{max_recursion} : 0;
79              
80 561 100       2364 $self->{_inspect_autoloaders} = defined $args{inspect_autoloaders} ?
81             $args{inspect_autoloaders} : 0;
82              
83 561 100       2832 $self->{_inspect_required_modules} = defined $args{inspect_required_modules} ?
84             $args{inspect_required_modules} : 0;
85              
86              
87 561         3127 return $self;
88             }
89              
90             #-----------------------------------------------------------------------------
91              
92             sub violates_dynamic {
93              
94 32     32 1 1576 my ($self, undef, $doc) = @_;
95              
96 32         3916 my $all_elements = $doc->find('PPI::Element');
97 32 50       7383 return if not $all_elements;
98              
99              
100 32         4582 my @wanted_namespaces = $self->_find_wanted_namespaces( $doc );
101 32         1311 my %wanted_namespaces = hashify(@wanted_namespaces);
102              
103              
104 32         2865 $self->_compile_document($doc);
105 31         250 my $symbols_of = $self->_hashify_symbol_table(@wanted_namespaces);
106              
107              
108 31         220 my @violations = ();
109 31         92 my $current_ns = $FAKE_NAMESPACE;
110              
111              
112             # TODO: Factor this if/elsif block into a dispatch table
113 31         398 ELEMENT:
114 31         87 for my $elem ( @{$all_elements} ) {
115              
116              
117 1017 100       30633 if ($elem->isa('PPI::Statement::Package') ) {
118 9         53 $current_ns = $elem->namespace();
119 9         507 next ELEMENT;
120             }
121              
122              
123              
124 1008 100       5973 if ($elem->isa('PPI::Statement::Include') ) {
125              
126 24 100       165 next if $elem->type() ne 'require';
127 3 100       177 next if not $self->{_inspect_required_modules};
128              
129 2   50     15 my $module = $elem->module() || next;
130 2         76 $self->_require_module($current_ns, $module);
131              
132 2         6 $wanted_namespaces{$module} = 1;
133 2         12 @wanted_namespaces = keys %wanted_namespaces;
134 2         11 $symbols_of = $self->_hashify_symbol_table(@wanted_namespaces);
135 2         168 next ELEMENT;
136             }
137              
138              
139              
140 984 100       13802 if ( $elem->isa('PPI::Token::Symbol') ) {
    100          
141              
142 42 50       2117 next if $elem->isa('PPI::Token::Magic');
143              
144 42         208 push @violations,
145             $self->_check_symbol($elem, $symbols_of,
146             \%wanted_namespaces, $current_ns);
147             }
148             elsif( $elem->isa('PPI::Token::Word') ) {
149              
150 134 100       1254 next if not is_function_call($elem);
151 82 100       35311 next if is_perl_builtin($elem);
152              
153 35         1418 push @violations,
154             $self->_check_bareword($elem, $symbols_of,
155             \%wanted_namespaces, $current_ns);
156             }
157             }
158              
159 31         16259 return @violations;
160             }
161              
162              
163             #-----------------------------------------------------------------------------
164              
165             sub _check_symbol {
166              
167 42     42   143 my ($self, $symbol, $symbols_of, $included_modules, $current_ns) = @_;
168              
169              
170             # Normalize and parse symbol
171             # TODO: Document the regexes used here
172 42         695 my $canon = $symbol->canonical();
173              
174 42 50       1965 $canon =~ m{ \A [\$@%&*] (.*?) (?: ::)? ([^:]*) \z }xms
175             or confess "Unexpected symbol format: $symbol";
176              
177 42         176 my ($pkg, $sym_name) = ($1, $2);
178 42         443 my $sigil = $symbol->symbol_type();
179              
180              
181             # Unqualified symbols are exempt because lexicals aren't in the symbol
182             # table. However, subroutines are. So we do want things like "&foo()" and
183             # "$code_ref = \&foo";
184              
185 42 100 100     1843 return if $sigil ne $AMPERSAND && !$pkg;
186 33 100 66     300 if ( !$pkg && $sigil eq $AMPERSAND ) {$pkg = $current_ns}
  8         33  
187              
188              
189             # If asked, skip calls to packages with AUTOLOAD
190 33 50       204 if ( $symbols_of->{$sigil}->{"${pkg}::AUTOLOAD"} ) {
191 0 0       0 return if not $self->{_inspect_autoloaders};
192             }
193              
194              
195             # Ignore stuff from global packages
196 33 50       170 return if exists $GLOBAL_PACKAGES{$pkg};
197              
198              
199             # Check if is in the symbol table
200 33 100       435 return if exists $symbols_of->{$sigil}->{"${pkg}::${sym_name}"};
201              
202              
203             # If we get here, there must be a violation
204 22         61 my $desc = qq{Symbol "$canon" does not appear to be defined};
205 22         98 my $expl = qq{Perhaps you forgot to load "$pkg"};
206              
207 22         508 return $self->violation($desc, $expl, $symbol);
208             }
209              
210             #-----------------------------------------------------------------------------
211              
212             sub _check_bareword {
213              
214 35     35   115 my ($self, $bareword, $symbols_of, $included_modules, $current_ns) = @_;
215              
216              
217              
218             # Normalize and parse bareword
219             # TODO: Document the regexes used here
220 35         158 my $canon = _canonicalize_bareword($bareword->content(), $current_ns);
221 35 50       758 $canon =~ m{ (.+) :: ([^:]+) \z }xms
222             or confess "Unexpected bareword format: $canon";
223 35         336 my ($sigil, $pkg, $sub_name) = ($AMPERSAND, $1, $2);
224              
225              
226             # Ignore stuff from global packages
227 35 100       229 return if exists $GLOBAL_PACKAGES{$pkg};
228              
229              
230             # If asked, skip calls to packages with AUTOLOAD
231 32 100       542 if ( $symbols_of->{$sigil}->{"${pkg}::AUTOLOAD"} ) {
232 2 100       13 return if not $self->{_inspect_autoloaders};
233             }
234              
235              
236             # Check if barewords is in the symbol table. It could be a
237             # a subroutine, or just a file-handle. I can't tell the diff.
238 31 100       152 return if exists $symbols_of->{$sigil}->{$canon};
239 16 50       71 return if exists $symbols_of->{ios}->{$canon};
240              
241              
242              
243             # If we get here, there must be a violation
244 16         61 my $desc = qq{Subroutine "$bareword" does not appear to be defined};
245 16 100       195 my $expl = $included_modules->{$pkg} ?
246             qq{Perhaps "$sub_name" is misspelled}
247             : qq{Perhaps you forgot to load "$pkg"};
248              
249              
250 16         978 return $self->violation($desc, $expl, $bareword);
251             }
252              
253             #-----------------------------------------------------------------------------
254              
255             sub _require_module {
256              
257 2     2   6 my ($self, $current_ns, $module) = @_;
258              
259 2         9 my $code = <<"END_CODE";
260              
261             package $current_ns;
262             require $module;
263              
264             END_CODE
265              
266 2         6 local @INC = @{ $self->{_inc} };
  2         23  
267 2         329 eval $code; ## no critic (Eval)
268              
269 2 50       15 if ($EVAL_ERROR) {
270 0         0 my $policy = policy_short_name(__PACKAGE__);
271 0         0 die qq($policy: Couldn't require "$module": $EVAL_ERROR\n);
272             }
273              
274 2         9 return 1;
275             }
276              
277             #-----------------------------------------------------------------------------
278              
279             sub _find_wanted_namespaces {
280              
281 32     32   478 my ($self, $doc) = @_;
282              
283 32         1841 my @declared_packages = $self->_find_declared_packages($doc);
284 32         604 my @included_modules = $self->_find_included_modules($doc);
285              
286             return (
287 32         1278 'main',
288             $FAKE_NAMESPACE,
289             @declared_packages,
290             @included_modules,
291             );
292             }
293              
294             #-----------------------------------------------------------------------------
295              
296             sub _compile_document {
297              
298 32     32   101 my ($self, $doc) = @_;
299              
300             # The $doc could be a script or a library or just an arbitrary block of
301             # code. I use "eval" to compile the code and populate the symbol table,
302             # but I don't want to execute anything. So I ineject code to die before
303             # anything is executed. Also, I want to protect myself from the chance
304             # that "die" has been overridden, so I call CORE::die directly. Perhaps
305             # there is a more elegant way to do this, but I haven't figured it out.
306              
307 32         537 my $suicide_note = '__execution_aborted__';
308 32         547 my $code_header = <<"END_HEADER";
309              
310             package $FAKE_NAMESPACE;
311              
312             no strict;
313             no warnings;
314             CORE::die("$suicide_note\\n");
315              
316             END_HEADER
317              
318             # As of version 1.118, PPI has trouble accurately parsing & reproducing
319             # files that contain HEREDOCs. So if $doc is a file, we'll try and read
320             # the code from the source. Otherwise, we'll use PPI's translation of it.
321              
322 32         419 my $source_code = q{};
323 32         1616 my $filename = $doc->filename();
324 32 50 33     3375 if (defined $filename && -f $filename) {
325 0 0       0 open my $fh, '<', $filename
326             or confess qq{Can't open "$filename" for reading: $OS_ERROR};
327 0         0 $source_code = do {local $INPUT_RECORD_SEPARATOR = undef; <$fh>; };
  0         0  
  0         0  
328 0 0       0 close $fh or confess qq{Can't close "$filename": $OS_ERROR};
329             }
330             else {
331 32         5125 $source_code = $doc->content();
332             }
333              
334             # Prepend our special header to the source
335 32         13878 $source_code = $code_header . $source_code;
336              
337             # Now eval the code, using the @INC paths that has been configured. If
338             # all goes well, it to die with a very particular error message.
339              
340 32         468 local @INC = @{ $self->{_inc} };
  32         3630  
341 32     32   23243 eval $source_code; ## no critic (Eval)
  32     32   857  
  32         214  
  32         6095  
  32         347  
  32         173  
  32         26875  
342 32 100       408 return 1 if $EVAL_ERROR eq "$suicide_note\n";
343              
344              
345             # Something went wrong then...
346 1   50     6 my $file = $doc->filename() || 'unknown file';
347 1         64 my $policy = policy_short_name(__PACKAGE__);
348              
349 1 50       52 if ($EVAL_ERROR) {
350 1         33 die qq($policy: Compilation of "$file" failed: $EVAL_ERROR\n);
351             }
352             else {
353 0         0 confess qq($policy: PANIC - "$file" did not commit suicide);
354             }
355             }
356              
357             #-----------------------------------------------------------------------------
358              
359             sub _find_declared_packages {
360              
361 32     32   536 my ($self, $doc) = @_;
362              
363 32         504 my $package_declarations = $doc->find('PPI::Statement::Package');
364 32 100       2662 return if not $package_declarations;
365              
366 9         47 my @declared_packages = map { $_->namespace() } @{$package_declarations};
  9         549  
  9         235  
367 9         3776 return @declared_packages;
368             }
369              
370             #-----------------------------------------------------------------------------
371              
372             sub _find_included_modules {
373              
374 32     32   374 my ($self, $doc) = @_;
375              
376 32         650 my $includes = $doc->find('PPI::Statement::Include');
377 32 100       1758 return if not $includes;
378              
379              
380 25         585 my @include_statements = grep {$_->type() =~ m/(?:use|no)/xms} @{$includes};
  25         1410  
  25         697  
381              
382             # I'm assuming that the name of the module, is the namespace where its
383             # symbols are going to be declared. But that isn't always true. Might be
384             # able to accommodate that by using Devel::Symdump in recursive mode.
385              
386 25         6581 my @included_modules = map { $_->module() } @include_statements;
  22         322  
387 25         1715 return @included_modules;
388             }
389              
390             #-----------------------------------------------------------------------------
391              
392             sub _hashify_symbol_table {
393              
394 33     33   1143 my ($self, @wanted_packages) = @_;
395              
396 33         510 local $Devel::Symdump::MAX_RECURSION = $self->{_max_recursion};
397 33         970187 my $symbol_table = Devel::Symdump->rnew(@wanted_packages);
398              
399             # This is kinda lame. Consider creating an OO interface for this, or just
400             # use Devel::Symdump directly. Or maybe there's a better module on CPAN.
401              
402             ## no critic (NoisyQuotes)
403 33         6051 my %symbols_by_sigil = (
404             'ios' => { hashify( $symbol_table->ios() ) },
405             '@' => { hashify( $symbol_table->arrays() ) },
406             '%' => { hashify( $symbol_table->hashes() ) },
407             '$' => { hashify( $symbol_table->scalars() ) },
408             '&' => { hashify( $symbol_table->functions() ) },
409             );
410              
411 33         258970 return \%symbols_by_sigil;
412             }
413              
414             #-----------------------------------------------------------------------------
415              
416             sub _canonicalize_bareword {
417              
418 35     35   210 my ($bareword_as_string, $current_ns) = @_;
419 35 100       674 return $bareword_as_string if $bareword_as_string =~ m/\A .+ ::/xms;
420 19 100       216 return 'main' . $bareword_as_string if $bareword_as_string =~ m/\A ::/xms;
421 17         72 return $current_ns. q{::} . $bareword_as_string;
422             }
423              
424             #-----------------------------------------------------------------------------
425              
426             1;
427              
428             __END__
429              
430             =pod
431              
432             =head1 NAME
433              
434             Perl::Critic::Policy::Dynamic::ValidateAgainstSymbolTable
435              
436             =head1 DESCRIPTION
437              
438             This Policy warns if any subroutine call that appears in your code is not
439             defined in the symbol table at compile-time. The intent is to detect typos in
440             the names of packages and subroutines, and possible failures to import,
441             declare, or include all the static subroutines that are invoked by your code..
442              
443             B<VERY IMPORTANT:> Most L<Perl::Critic> Policies (including all the ones that
444             ship with Perl::Critic> use pure static analysis -- they never compile nor
445             execute any of the code that they analyze. However, this policy is very
446             different. It actually attempts to compile your code and then compares the
447             subroutines mentioned in your code to those found in the symbol table.
448             Therefore you should B<not> use this Policy on any code that you do not trust,
449             or may have undesirable side-effects at compile-time (such as connecting to
450             the network or mutating files).
451              
452             For these reasons, this Policy (and any other Policy that inherits from
453             L<Perl::Critic::DynamicPolicy>) is marked as "unsafe" and usually ignored by
454             both L<Perl::Critic> and L<perlcritic>. So to use this Policy, you must set
455             the C<-allow-unsafe> switch in the L<Perl::Critic> contstructor or on the
456             L<perlcritic> command line.
457              
458             For this Policy to work, all the modules included in your code must be
459             installed locally, and must compile without error. See L<"CONFIGURATION"> for
460             information about controlling where this Policy searches for the included
461             modules.
462              
463             =head1 LIMITATIONS
464              
465             This Policy will not detect subroutines that are declared at run-time or
466             through direct manipulation of the symbol table, which may lead to false
467             warnings. The most common examples of this are modules that use C<AUTOLOAD>.
468              
469             Sophisticated code often use the C<require> function to postpone loading
470             modules or only load modules under certain conditions. If you set the
471             C<inspect_required_modules> option, the Policy will attempt to load all
472             modules that are C<require'd> at any point in your code. However, this Policy
473             does not know whether the module would have been loaded during normal
474             execution of your program. This may cause the Policy to overlook potential
475             violations.
476              
477             This Policy only examines static subroutine calls -- method calls are not
478             covered. Indirect method calls such as C<"my $fh = new FileHandle"> also tend
479             to trigger false warnings.
480              
481             This Policy compiles your code into the same symbol table as Perl::Critic
482             itself. So to maintain integrity in the symbol table, this Policy forks
483             itself before analyzing each file. On some systems, this may be slow and
484             consume a lot of resources.
485              
486             =head1 CONFIGURATION
487              
488             This Policy supports the following configuration parameters. See below for
489             example of how to set these parameters in you f<.perlcriticrc> file.
490              
491             =over
492              
493             =item C<at_inc_prefix>
494              
495             Prepends an arrayref of directories to the front of the current C<@INC> list.
496             This affects where the Policy will find dependent modules when it compiles
497             your code.
498              
499             =item C<at_inc_suffix>
500              
501             Appends an arrayref of directories to the end of the current C<@INC> list.
502             This affects where the Policy will find dependent modules when it compiles
503             your code.
504              
505             =item C<at_inc>
506              
507             Sets the C<@INC> list outright. This affects where the Policy will find
508             dependent modules when it compiles your code.
509              
510             =item C<inspect_required_modules>
511              
512             By default, this Policy only examines modules that are loaded by your code at
513             compile-time. If C<inspect_required_modules> is set to a true value, this Policy
514             will also compile all the modules that are C<require>'d in your code at
515             runtime. Note that this Policy does not know if these modules will actually
516             be loaded when your program runs, nor does it try to invoke the C<import>
517             method on those modules.
518              
519             =item C<inspect_autoloaders>
520              
521             By default, this Policy does not attempt to validate a function call into a
522             package that has an C<AUTOLOAD> method. Such packages usually define
523             functions at run-time, so this Policy has no chance of knowing what functions
524             that package might have. But if C<inspect_autoloaders> is set to a true
525             value, the Policy will check to see if a function exists in such packages.
526              
527             =item C<max_recursion>
528              
529             By default, this Policy only looks at the symbol tables for the namespaces
530             that are directly C<use'd> (or C<require'd>) by your code. However, some
531             modules contain multiple namespaces which may lead to false violations. But
532             if you set C<max_recursion> to a positive integer, this Policy will recurse
533             into those other namespaces. Beware, however, that using a deep recursion can
534             mask other violations. Setting C<max_recursion> to 1 or 2 is usually
535             sufficient.
536              
537             =back
538              
539             You can set these configuration parameters but putting any or all of the
540             following in your F<.perlcriticrc> file.
541              
542             [Dynamic::ValidateAgainstSymbolTable]
543              
544             at_inc_prefix = some/directory/path | another/directory/path
545             at_inc_suffix = some/directory/path | another/directory/path
546             at_inc = some/directory/path | another/directory/path
547              
548             inspect_required_modules = 1
549             inspect_autoloaders = 1
550              
551             =head1 AUTHOR
552              
553             Jeffrey Ryan Thalhammer <thaljef@cpan.org>
554              
555             =head1 COPYRIGHT
556              
557             Copyright (c) 2007 Jeffrey Ryan Thalhammer. All rights reserved.
558              
559             This program is free software; you can redistribute it and/or modify it under
560             the same terms as Perl itself. The full text of this license can be found in
561             the LICENSE file included with this module.
562              
563             =cut
564              
565             ##############################################################################
566             # Local Variables:
567             # mode: cperl
568             # cperl-indent-level: 4
569             # fill-column: 78
570             # indent-tabs-mode: nil
571             # c-indentation-style: bsd
572             # End:
573             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab :