Login
Always put parens on method calls.
[gknop/Perl-Critic.git] / lib / Perl / Critic / Document.pm
CommitLineData
6036a254 1##############################################################################
a73f4a71
JRT
2# $URL$
3# $Date$
4# $Author$
5# $Revision$
6036a254 6##############################################################################
5bf96118
CD
7
8package Perl::Critic::Document;
9
df6dee2b 10use 5.006001;
5bf96118 11use strict;
58a9e587 12use warnings;
267b39b4 13
d5835ca8 14use Carp qw< confess >;
2d2fd196 15
5bf96118 16use PPI::Document;
d5835ca8
JRT
17use PPI::Document::File;
18
19use List::Util qw< max >;
20use Scalar::Util qw< blessed weaken >;
267b39b4 21use version;
5bf96118 22
d5835ca8
JRT
23use Perl::Critic::Annotation;
24use Perl::Critic::Exception::Parse qw{ throw_parse };
25
6036a254 26#-----------------------------------------------------------------------------
58a9e587 27
fb1c69dd 28our $VERSION = '1.093_02';
5bf96118 29
6036a254 30#-----------------------------------------------------------------------------
5bf96118
CD
31
32our $AUTOLOAD;
937b8de0 33sub AUTOLOAD { ## no critic (ProhibitAutoloading,ArgUnpacking)
6e7d6c9f
CD
34 my ( $function_name ) = $AUTOLOAD =~ m/ ([^:\']+) \z /xms;
35 return if $function_name eq 'DESTROY';
36 my $self = shift;
37 return $self->{_doc}->$function_name(@_);
5bf96118
CD
38}
39
6036a254 40#-----------------------------------------------------------------------------
5bf96118 41
58a9e587 42sub new {
d5835ca8 43 my ($class, @args) = @_;
937b8de0 44 my $self = bless {}, $class;
d5835ca8
JRT
45 return $self->_init(@args);
46}
47
48#-----------------------------------------------------------------------------
49
50sub _init {
51
52 my ($self, $source_code) = @_;
53
54 # $source_code can be a file name, or a reference to a
55 # PPI::Document, or a reference to a scalar containing source
56 # code. In the last case, PPI handles the translation for us.
57
58 my $doc = _is_ppi_doc( $source_code ) ? $source_code
59 : ref $source_code ? PPI::Document->new($source_code)
60 : PPI::Document::File->new($source_code);
61
62 # Bail on error
63 if ( not defined $doc ) {
64 my $errstr = PPI::Document::errstr();
65 my $file = ref $source_code ? undef : $source_code;
66 throw_parse
67 message => qq<Can't parse code: $errstr>,
68 file_name => $file;
69 }
70
937b8de0 71 $self->{_doc} = $doc;
d5835ca8
JRT
72 $self->{_annotations} = [];
73 $self->{_suppressed_violations} = [];
74 $self->{_disabled_line_map} = {};
75 $self->index_locations();
76 $self->_disable_shebang_fix();
77
937b8de0 78 return $self;
5bf96118
CD
79}
80
6036a254 81#-----------------------------------------------------------------------------
58a9e587 82
d5835ca8
JRT
83sub _is_ppi_doc {
84 my ($ref) = @_;
85 return blessed($ref) && $ref->isa('PPI::Document');
86}
87
88#-----------------------------------------------------------------------------
89
2b6293b2
CD
90sub ppi_document {
91 my ($self) = @_;
92 return $self->{_doc};
93}
94
95#-----------------------------------------------------------------------------
96
47e1ff34 97sub isa {
6e7d6c9f
CD
98 my ($self, @args) = @_;
99 return $self->SUPER::isa(@args)
100 || ( (ref $self) && $self->{_doc} && $self->{_doc}->isa(@args) );
47e1ff34
CD
101}
102
6036a254 103#-----------------------------------------------------------------------------
47e1ff34 104
5bf96118 105sub find {
6e7d6c9f 106 my ($self, $wanted, @more_args) = @_;
5bf96118 107
58a9e587
JRT
108 # This method can only find elements by their class names. For
109 # other types of searches, delegate to the PPI::Document
5bf96118 110 if ( ( ref $wanted ) || !$wanted || $wanted !~ m/ \A PPI:: /xms ) {
6e7d6c9f 111 return $self->{_doc}->find($wanted, @more_args);
5bf96118 112 }
58a9e587
JRT
113
114 # Build the class cache if it doesn't exist. This happens at most
115 # once per Perl::Critic::Document instance. %elements of will be
116 # populated as a side-effect of calling the $finder_sub coderef
117 # that is produced by the caching_finder() closure.
5bf96118 118 if ( !$self->{_elements_of} ) {
389109ec 119
58a9e587 120 my %cache = ( 'PPI::Document' => [ $self ] );
389109ec
JRT
121
122 # The cache refers to $self, and $self refers to the cache. This
123 # creates a circular reference that leaks memory (i.e. $self is not
124 # destroyed until execution is complete). By weakening the reference,
125 # we allow perl to collect the garbage properly.
126 weaken( $cache{'PPI::Document'}->[0] );
127
58a9e587
JRT
128 my $finder_coderef = _caching_finder( \%cache );
129 $self->{_doc}->find( $finder_coderef );
130 $self->{_elements_of} = \%cache;
131 }
132
133 # find() must return false-but-defined on fail
134 return $self->{_elements_of}->{$wanted} || q{};
135}
136
6036a254 137#-----------------------------------------------------------------------------
58a9e587 138
fb21e21e 139sub find_first {
6e7d6c9f 140 my ($self, $wanted, @more_args) = @_;
fb21e21e
CD
141
142 # This method can only find elements by their class names. For
143 # other types of searches, delegate to the PPI::Document
144 if ( ( ref $wanted ) || !$wanted || $wanted !~ m/ \A PPI:: /xms ) {
6e7d6c9f 145 return $self->{_doc}->find_first($wanted, @more_args);
fb21e21e
CD
146 }
147
148 my $result = $self->find($wanted);
149 return $result ? $result->[0] : $result;
150}
151
6036a254 152#-----------------------------------------------------------------------------
fb21e21e 153
f5eeac3b 154sub find_any {
6e7d6c9f 155 my ($self, $wanted, @more_args) = @_;
f5eeac3b
CD
156
157 # This method can only find elements by their class names. For
158 # other types of searches, delegate to the PPI::Document
159 if ( ( ref $wanted ) || !$wanted || $wanted !~ m/ \A PPI:: /xms ) {
6e7d6c9f 160 return $self->{_doc}->find_any($wanted, @more_args);
f5eeac3b
CD
161 }
162
163 my $result = $self->find($wanted);
164 return $result ? 1 : $result;
165}
166
6036a254 167#-----------------------------------------------------------------------------
f5eeac3b 168
60108aef
CD
169sub filename {
170 my ($self) = @_;
c73ba7f3
JRT
171 my $doc = $self->{_doc};
172 return $doc->can('filename') ? $doc->filename() : undef;
60108aef
CD
173}
174
6036a254 175#-----------------------------------------------------------------------------
60108aef 176
267b39b4
ES
177sub highest_explicit_perl_version {
178 my ($self) = @_;
179
180 my $highest_explicit_perl_version =
181 $self->{_highest_explicit_perl_version};
182
183 if ( not exists $self->{_highest_explicit_perl_version} ) {
184 my $includes = $self->find( \&_is_a_version_statement );
185
186 if ($includes) {
1ebef5a9
ES
187 # Note: this will complain about underscores, e.g. "use
188 # 5.008_000". However, nothing important should be depending upon
189 # alpha perl versions and marking non-alpha versions as alpha is
190 # bad in and of itself. Note that this contradicts an example in
191 # perlfunc about "use".
267b39b4
ES
192 $highest_explicit_perl_version =
193 max map { version->new( $_->version() ) } @{$includes};
194 }
195 else {
196 $highest_explicit_perl_version = undef;
197 }
198
199 $self->{_highest_explicit_perl_version} =
200 $highest_explicit_perl_version;
201 }
202
203 return $highest_explicit_perl_version if $highest_explicit_perl_version;
204 return;
205}
206
937b8de0
JRT
207#-----------------------------------------------------------------------------
208
d5835ca8
JRT
209sub process_annotations {
210 my ($self) = @_;
937b8de0 211
d5835ca8
JRT
212 my @annotations = Perl::Critic::Annotation->create_annotations($self);
213 $self->add_annotation(@annotations);
937b8de0
JRT
214 return $self;
215}
216
217#-----------------------------------------------------------------------------
218
d5835ca8
JRT
219sub line_is_disabled_for_policy {
220 my ($self, $line, $policy) = @_;
221 my $policy_name = ref $policy || $policy;
2d2fd196
JRT
222
223 # HACK: This Policy is special. If it is active, it cannot be
d1237298 224 # disabled by a "## no critic" annotation. Rather than create a general
2d2fd196 225 # hook in Policy.pm for enabling this behavior, we chose to hack
d5835ca8 226 # it here, since this isn't the kind of thing that most policies do
2f4b6b33
JRT
227
228 return 0 if $policy_name eq
2d2fd196
JRT
229 'Perl::Critic::Policy::Miscellanea::ProhibitUnrestrictedNoCritic';
230
d5835ca8
JRT
231 return 1 if $self->{_disabled_line_map}->{$line}->{$policy_name};
232 return 1 if $self->{_disabled_line_map}->{$line}->{ALL};
937b8de0
JRT
233 return 0;
234}
235
236#-----------------------------------------------------------------------------
237
d5835ca8
JRT
238sub add_annotation {
239 my ($self, @annotations) = @_;
240
241 # Add annotation to our private map for quick lookup
242 for my $annotation (@annotations) {
243
244 my ($start, $end) = $annotation->effective_range();
245 my @affected_policies = $annotation->disables_all_policies ?
246 qw(ALL) : $annotation->disabled_policies();
247
248 # TODO: Find clever way to do this with hash slices
249 for my $line ($start .. $end) {
250 for my $policy (@affected_policies) {
251 $self->{_disabled_line_map}->{$line}->{$policy} = 1;
252 }
253 }
254 }
255
256 push @{ $self->{_annotations} }, @annotations;
2d2fd196
JRT
257 return $self;
258}
4880392e 259
2d2fd196 260#-----------------------------------------------------------------------------
4880392e 261
d5835ca8 262sub annotations {
2d2fd196 263 my ($self) = @_;
d5835ca8
JRT
264 return @{ $self->{_annotations} };
265}
4880392e 266
d5835ca8 267#-----------------------------------------------------------------------------
4880392e 268
d5835ca8
JRT
269sub add_suppressed_violation {
270 my ($self, $violation) = @_;
271 push @{$self->{_suppressed_violations}}, $violation;
272 return $self;
273}
4880392e 274
d5835ca8 275#-----------------------------------------------------------------------------
2d2fd196 276
d5835ca8
JRT
277sub suppressed_violations {
278 my ($self) = @_;
279 return @{ $self->{_suppressed_violations} };
95ebf9b0
JRT
280}
281
282#-----------------------------------------------------------------------------
d5835ca8 283# PRIVATE functions & methods
95ebf9b0 284
267b39b4
ES
285sub _is_a_version_statement {
286 my (undef, $element) = @_;
287
288 return 0 if not $element->isa('PPI::Statement::Include');
289 return 1 if $element->version();
290 return 0;
291}
292
293#-----------------------------------------------------------------------------
294
58a9e587
JRT
295sub _caching_finder {
296
297 my $cache_ref = shift; # These vars will persist for the life
298 my %isa_cache = (); # of the code ref that this sub returns
299
300
301 # Gather up all the PPI elements and sort by @ISA. Note: if any
302 # instances used multiple inheritance, this implementation would
303 # lead to multiple copies of $element in the $elements_of lists.
304 # However, PPI::* doesn't do multiple inheritance, so we are safe
305
306 return sub {
6e7d6c9f 307 my (undef, $element) = @_;
58a9e587
JRT
308 my $classes = $isa_cache{ref $element};
309 if ( !$classes ) {
310 $classes = [ ref $element ];
311 # Use a C-style loop because we append to the classes array inside
312 for ( my $i = 0; $i < @{$classes}; $i++ ) { ## no critic(ProhibitCStyleForLoops)
313 no strict 'refs'; ## no critic(ProhibitNoStrict)
314 push @{$classes}, @{"$classes->[$i]::ISA"};
315 $cache_ref->{$classes->[$i]} ||= [];
5bf96118 316 }
58a9e587
JRT
317 $isa_cache{$classes->[0]} = $classes;
318 }
5bf96118 319
58a9e587
JRT
320 for my $class ( @{$classes} ) {
321 push @{$cache_ref->{$class}}, $element;
322 }
5bf96118 323
58a9e587
JRT
324 return 0; # 0 tells find() to keep traversing, but not to store this $element
325 };
5bf96118
CD
326}
327
6036a254 328#-----------------------------------------------------------------------------
58a9e587 329
d5835ca8 330sub _disable_shebang_fix {
2d2fd196
JRT
331 my ($self) = @_;
332
937b8de0
JRT
333 # When you install a script using ExtUtils::MakeMaker or Module::Build, it
334 # inserts some magical code into the top of the file (just after the
335 # shebang). This code allows people to call your script using a shell,
336 # like `sh my_script`. Unfortunately, this code causes several Policy
d1237298 337 # violations, so we disable them as if they had "## no critic" annotations.
937b8de0 338
d5835ca8 339 my $first_stmnt = $self->schild(0) || return;
937b8de0
JRT
340
341 # Different versions of MakeMaker and Build use slightly different shebang
342 # fixing strings. This matches most of the ones I've found in my own Perl
343 # distribution, but it may not be bullet-proof.
344
66d796fa 345 my $fixin_rx = qr<^eval 'exec .* \$0 \${1\+"\$@"}'\s*[\r\n]\s*if.+;>ms; ## no critic (ExtendedFormatting)
937b8de0 346 if ( $first_stmnt =~ $fixin_rx ) {
d5835ca8
JRT
347 my $line = $first_stmnt->location->[0];
348 $self->{_disabled_line_map}->{$line}->{ALL} = 1;
349 $self->{_disabled_line_map}->{$line + 1}->{ALL} = 1;
937b8de0
JRT
350 }
351
2d2fd196 352 return $self;
937b8de0
JRT
353}
354
355#-----------------------------------------------------------------------------
356
5bf96118 3571;
58a9e587 358
5bf96118
CD
359__END__
360
a73f4a71
JRT
361=pod
362
363=for stopwords pre-caches
364
5bf96118
CD
365=head1 NAME
366
c728943a 367Perl::Critic::Document - Caching wrapper around a PPI::Document.
5bf96118 368
267b39b4 369
5bf96118
CD
370=head1 SYNOPSIS
371
372 use PPI::Document;
373 use Perl::Critic::Document;
374 my $doc = PPI::Document->new('Foo.pm');
375 $doc = Perl::Critic::Document->new($doc);
376 ## Then use the instance just like a PPI::Document
377
267b39b4 378
5bf96118
CD
379=head1 DESCRIPTION
380
381Perl::Critic does a lot of iterations over the PPI document tree via
382the C<PPI::Document::find()> method. To save some time, this class
383pre-caches a lot of the common C<find()> calls in a single traversal.
384Then, on subsequent requests we return the cached data.
385
386This is implemented as a facade, where method calls are handed to the
387stored C<PPI::Document> instance.
388
267b39b4 389
5bf96118
CD
390=head1 CAVEATS
391
392This facade does not implement the overloaded operators from
11f53956
ES
393L<PPI::Document|PPI::Document> (that is, the C<use overload ...>
394work). Therefore, users of this facade must not rely on that syntactic
395sugar. So, for example, instead of C<my $source = "$doc";> you should
396write C<my $source = $doc->content();>
5bf96118
CD
397
398Perhaps there is a CPAN module out there which implements a facade
399better than we do here?
400
267b39b4
ES
401
402=head1 CONSTRUCTOR
403
404=over
405
d5835ca8 406=item C<< new($source_code) >>
267b39b4 407
d5835ca8
JRT
408Create a new instance referencing a PPI::Document instance. The
409C<$source_code> can be the name of a file, a reference to a scalar
410containing actual source code, or a L<PPI::Document> or
411L<PPI::Document::File>.
267b39b4
ES
412
413=back
414
5bf96118
CD
415=head1 METHODS
416
417=over
418
267b39b4 419=item C<< ppi_document() >>
2b6293b2 420
11f53956
ES
421Accessor for the wrapped PPI::Document instance. Note that altering
422this instance in any way can cause unpredictable failures in
423Perl::Critic's subsequent analysis because some caches may fall out of
424date.
2b6293b2 425
5bf96118 426
267b39b4
ES
427=item C<< find($wanted) >>
428
429=item C<< find_first($wanted) >>
fb21e21e 430
267b39b4 431=item C<< find_any($wanted) >>
f5eeac3b 432
fb21e21e 433If C<$wanted> is a simple PPI class name, then the cache is employed.
f5eeac3b
CD
434Otherwise we forward the call to the corresponding method of the
435C<PPI::Document> instance.
5bf96118 436
267b39b4
ES
437
438=item C<< filename() >>
e7f2d995
CD
439
440Returns the filename for the source code if applicable
441(PPI::Document::File) or C<undef> otherwise (PPI::Document).
442
267b39b4
ES
443
444=item C<< isa( $classname ) >>
242f7b08 445
11f53956
ES
446To be compatible with other modules that expect to get a
447PPI::Document, the Perl::Critic::Document class masquerades as the
448PPI::Document class.
242f7b08 449
267b39b4
ES
450
451=item C<< highest_explicit_perl_version() >>
452
11f53956
ES
453Returns a L<version|version> object for the highest Perl version
454requirement declared in the document via a C<use> or C<require>
455statement. Returns nothing if there is no version statement.
267b39b4 456
d5835ca8 457=item C<< process_annotations() >>
267b39b4 458
d5835ca8
JRT
459Causes this Document to scan itself and mark which lines &
460policies are disabled by the C<"## no critic"> annotations.
937b8de0 461
d5835ca8 462=item C<< line_is_disabled_for_policy($line, $policy_object) >>
937b8de0 463
d5835ca8
JRT
464Returns true if the given C<$policy_object> or C<$policy_name> has
465been disabled for at C<$line> in this Document. Otherwise, returns false.
937b8de0 466
d5835ca8 467=item C<< add_annotation( $annotation ) >>
937b8de0 468
d5835ca8 469Adds an C<$annotation> object to this Document.
2d2fd196 470
d5835ca8 471=item C<< annotations() >>
2d2fd196 472
d5835ca8
JRT
473Returns a list containing all the L<Perl::Critic::Annotation> that
474were found in this Document.
2d2fd196 475
d5835ca8 476=item C<< add_suppressed_violation($violation) >>
2d2fd196 477
d5835ca8
JRT
478Informs this Document that a C<$violation> was found but not reported
479because it fell on a line that had been suppressed by a C<"## no critic">
480annotation. Returns C<$self>.
95ebf9b0 481
d5835ca8 482=item C<< suppressed_violations() >>
95ebf9b0 483
d5835ca8
JRT
484Returns a list of references to all the L<Perl::Critic::Violation>s
485that were found in this Document but were suppressed.
937b8de0 486
5bf96118
CD
487=back
488
489=head1 AUTHOR
490
2f4b6b33 491Chris Dolan <cdolan@cpan.org>
5bf96118
CD
492
493=head1 COPYRIGHT
494
20dfddeb 495Copyright (c) 2006-2008 Chris Dolan. All rights reserved.
5bf96118
CD
496
497This program is free software; you can redistribute it and/or modify
498it under the same terms as Perl itself. The full text of this license
499can be found in the LICENSE file included with this module.
500
501=cut
737d3b65 502
d5835ca8 503##############################################################################
737d3b65
CD
504# Local Variables:
505# mode: cperl
506# cperl-indent-level: 4
507# fill-column: 78
508# indent-tabs-mode: nil
509# c-indentation-style: bsd
510# End:
96fed375 511# ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :