File Coverage

blib/lib/Pod/Coverage.pm
Criterion Covered Total %
statement 0 119 0.0
branch 0 52 0.0
condition 0 28 0.0
subroutine 0 13 0.0
pod 5 5 100.0
total 5 217 2.3


line stmt bran cond sub pod time code
1             use strict;
2              
3             package Pod::Coverage;
4             use Devel::Symdump;
5             use B;
6             use Pod::Find qw(pod_where);
7              
8             BEGIN { defined &TRACE_ALL or eval 'sub TRACE_ALL () { 0 }' }
9              
10             use vars qw/ $VERSION /;
11             $VERSION = '0.23';
12              
13             =head1 NAME
14              
15             Pod::Coverage - Checks if the documentation of a module is comprehensive
16              
17             =head1 SYNOPSIS
18              
19             # in the beginnning...
20             perl -MPod::Coverage=Pod::Coverage -e666
21              
22             # all in one invocation
23             use Pod::Coverage package => 'Fishy';
24              
25             # straight OO
26             use Pod::Coverage;
27             my $pc = Pod::Coverage->new(package => 'Pod::Coverage');
28             print "We rock!" if $pc->coverage == 1;
29              
30              
31             =head1 DESCRIPTION
32              
33             Developers hate writing documentation. They'd hate it even more if
34             their computer tattled on them, but maybe they'll be even more
35             thankful in the long run. Even if not, F tells you to, so
36             you must obey.
37              
38             This module provides a mechanism for determining if the pod for a
39             given module is comprehensive.
40              
41             It expects to find either a C<< =head(n>1) >> or an C<=item> block documenting a
42             subroutine.
43              
44             Consider:
45             # an imaginary Foo.pm
46             package Foo;
47              
48             =item foo
49              
50             The foo sub
51              
52             = cut
53              
54             sub foo {}
55             sub bar {}
56              
57             1;
58             __END__
59              
60             In this example C is covered, but C is not, so the C
61             package is only 50% (0.5) covered
62              
63             =head2 Methods
64              
65             =over
66              
67             =item Pod::Coverage->new(package => $package)
68              
69             Creates a new Pod::Coverage object.
70              
71             C the name of the package to analyse
72              
73             C an array of regexen which define what symbols are regarded
74             as private (and so need not be documented) defaults to [ qr/^_/,
75             qr/^(un)?import$/, qr/^DESTROY$/, qr/^AUTOLOAD$/, qr/^bootstrap$/,
76             qr/^(TIE( SCALAR | ARRAY | HASH | HANDLE ) |
77             FETCH | STORE | UNTIE | FETCHSIZE | STORESIZE |
78             POP | PUSH | SHIFT | UNSHIFT | SPLICE | DELETE |
79             EXISTS | EXTEND | CLEAR | FIRSTKEY | NEXTKEY | PRINT | PRINTF |
80             WRITE | READLINE | GETC | READ | CLOSE | BINMODE | OPEN |
81             EOF | FILENO | SEEK | TELL | SCALAR )$/x,
82             qr/^( MODIFY | FETCH )_( REF | SCALAR | ARRAY | HASH | CODE |
83             GLOB | FORMAT | IO )_ATTRIBUTES$/x,
84             qr/^CLONE(_SKIP)?$/,
85             ]
86              
87             This should cover all the usual magical methods for tie()d objects,
88             attributes, generally all the methods that are typically not called by
89             a user, but instead being used internally by perl.
90              
91             C items are appended to the private list
92              
93             C an array of regexen which define what symbols you just want
94             us to assume are properly documented even if we can't find any docs
95             for them
96              
97             If C is supplied, that file is parsed for the documentation,
98             rather than using Pod::Find
99              
100             If C is supplied, then only POD sections which have
101             non-whitespace characters will count towards being documented.
102              
103             =cut
104              
105             sub new {
106 0     0 1   my $referent = shift;
107 0           my %args = @_;
108 0   0       my $class = ref $referent || $referent;
109              
110 0   0       my $private = $args{private} || [
111             qr/^_/,
112             qr/^(un)?import$/,
113             qr/^DESTROY$/,
114             qr/^AUTOLOAD$/,
115             qr/^bootstrap$/,
116             qr/^\(/,
117             qr/^(TIE( SCALAR | ARRAY | HASH | HANDLE ) |
118             FETCH | STORE | UNTIE | FETCHSIZE | STORESIZE |
119             POP | PUSH | SHIFT | UNSHIFT | SPLICE | DELETE |
120             EXISTS | EXTEND | CLEAR | FIRSTKEY | NEXTKEY | PRINT | PRINTF |
121             WRITE | READLINE | GETC | READ | CLOSE | BINMODE | OPEN |
122             EOF | FILENO | SEEK | TELL | SCALAR )$/x,
123             qr/^( MODIFY | FETCH )_( REF | SCALAR | ARRAY | HASH | CODE |
124             GLOB | FORMAT | IO)_ATTRIBUTES $/x,
125             qr/^CLONE(_SKIP)?$/,
126             ];
127 0 0         push @$private, @{ $args{also_private} || [] };
  0            
128 0   0       my $trustme = $args{trustme} || [];
129 0   0       my $nonwhitespace = $args{nonwhitespace} || undef;
130              
131 0           my $self = bless {
132             @_,
133             private => $private,
134             trustme => $trustme,
135             nonwhitespace => $nonwhitespace
136             }, $class;
137             }
138              
139             =item $object->coverage
140              
141             Gives the coverage as a value in the range 0 to 1
142              
143             =cut
144              
145             sub coverage {
146 0     0 1   my $self = shift;
147              
148 0           my $package = $self->{package};
149 0           my $pods = $self->_get_pods;
150 0 0         return unless $pods;
151              
152 0           my %symbols = map { $_ => 0 } $self->_get_syms($package);
  0            
153              
154 0 0 0       if (!%symbols && $self->{why_unrated}) {
155             # _get_syms failed violently
156 0           return;
157             }
158              
159             print "tying shoelaces\n" if TRACE_ALL;
160 0           for my $pod (@$pods) {
161 0 0         $symbols{$pod} = 1 if exists $symbols{$pod};
162             }
163              
164 0           foreach my $sym ( keys %symbols ) {
165 0 0         $symbols{$sym} = 1 if $self->_trustme_check($sym);
166             }
167              
168             # stash the results for later
169 0           $self->{symbols} = \%symbols;
170              
171             if (TRACE_ALL) {
172             require Data::Dumper;
173             print Data::Dumper::Dumper($self);
174             }
175              
176 0           my $symbols = scalar keys %symbols;
177 0           my $documented = scalar grep {$_} values %symbols;
  0            
178 0 0         unless ($symbols) {
179 0           $self->{why_unrated} = "no public symbols defined";
180 0           return;
181             }
182 0           return $documented / $symbols;
183             }
184              
185             =item $object->why_unrated
186              
187             C<< $object->coverage >> may return C, to indicate that it was
188             unable to deduce coverage for a package. If this happens you should
189             be able to check C to get a useful excuse.
190              
191             =cut
192              
193             sub why_unrated {
194 0     0 1   my $self = shift;
195 0           $self->{why_unrated};
196             }
197              
198             =item $object->naked/$object->uncovered
199              
200             Returns a list of uncovered routines, will implicitly call coverage if
201             it's not already been called.
202              
203             Note, private and 'trustme' identifiers will be skipped.
204              
205             =cut
206              
207             sub naked {
208 0     0 1   my $self = shift;
209 0 0         $self->{symbols} or $self->coverage;
210 0 0         return unless $self->{symbols};
211 0           return grep { !$self->{symbols}{$_} } keys %{ $self->{symbols} };
  0            
  0            
212             }
213              
214             *uncovered = \&naked;
215              
216             =item $object->covered
217              
218             Returns a list of covered routines, will implicitly call coverage if
219             it's not previously been called.
220              
221             As with C, private and 'trustme' identifiers will be skipped.
222              
223             =cut
224              
225             sub covered {
226 0     0 1   my $self = shift;
227 0 0         $self->{symbols} or $self->coverage;
228 0 0         return unless $self->{symbols};
229 0           return grep { $self->{symbols}{$_} } keys %{ $self->{symbols} };
  0            
  0            
230             }
231              
232             sub import {
233 0     0     my $self = shift;
234 0 0         return unless @_;
235              
236             # one argument - just a package
237 0 0         scalar @_ == 1 and unshift @_, 'package';
238              
239             # we were called with arguments
240 0           my $pc = $self->new(@_);
241 0           my $rating = $pc->coverage;
242 0 0         $rating = 'unrated (' . $pc->why_unrated . ')'
243             unless defined $rating;
244 0           print $pc->{package}, " has a $self rating of $rating\n";
245 0           my @looky_here = $pc->naked;
246 0 0         if ( @looky_here > 1 ) {
    0          
247 0           print "The following are uncovered: ", join( ", ", sort @looky_here ),
248             "\n";
249             } elsif (@looky_here) {
250 0           print "'$looky_here[0]' is uncovered\n";
251             }
252             }
253              
254             =back
255              
256             =head2 Debugging support
257              
258             In order to allow internals debugging, while allowing the optimiser to
259             do its thang, C uses constant subs to define how it traces.
260              
261             Use them like so
262              
263             sub Pod::Coverage::TRACE_ALL () { 1 }
264             use Pod::Coverage;
265              
266             Supported constants are:
267              
268             =over
269              
270             =item TRACE_ALL
271              
272             Trace everything.
273              
274             Well that's all there is so far, are you glad you came?
275              
276             =back
277              
278             =head2 Inheritance interface
279              
280             These abstract methods while functional in C may make
281             your life easier if you want to extend C to fit your
282             house style more closely.
283              
284             B Please consider this interface as in a state of flux until
285             this comment goes away.
286              
287             =over
288              
289             =item $object->_CvGV($symbol)
290              
291             Return the GV for the coderef supplied. Used by C<_get_syms> to identify
292             locally defined code.
293              
294             You probably won't need to override this one.
295              
296             =item $object->_get_syms($package)
297              
298             return a list of symbols to check for from the specified packahe
299              
300             =cut
301              
302             # this one walks the symbol tree
303             sub _get_syms {
304 0     0     my $self = shift;
305 0           my $package = shift;
306              
307             print "requiring '$package'\n" if TRACE_ALL;
308 0           eval qq{ require $package };
309 0 0         if ($@) {
310             print "require failed with $@\n" if TRACE_ALL;
311 0           $self->{why_unrated} = "requiring '$package' failed";
312 0           return;
313             }
314              
315             print "walking symbols\n" if TRACE_ALL;
316 0           my $syms = Devel::Symdump->new($package);
317              
318 0           my @symbols;
319 0           for my $sym ( $syms->functions ) {
320              
321             # see if said method wasn't just imported from elsewhere
322 0           my $glob = do { no strict 'refs'; \*{$sym} };
  0            
  0            
323 0           my $o = B::svref_2object($glob);
324              
325             # in 5.005 this flag is not exposed via B, though it exists
326 0   0       my $imported_cv = eval { B::GVf_IMPORTED_CV() } || 0x80;
327 0 0         next if $o->GvFLAGS & $imported_cv;
328              
329             # check if it's on the whitelist
330 0           $sym =~ s/$self->{package}:://;
331 0 0         next if $self->_private_check($sym);
332              
333 0           push @symbols, $sym;
334             }
335 0           return @symbols;
336             }
337              
338             =item _get_pods
339              
340             Extract pod markers from the currently active package.
341              
342             Return an arrayref or undef on fail.
343              
344             =cut
345              
346             sub _get_pods {
347 0     0     my $self = shift;
348              
349 0           my $package = $self->{package};
350              
351             print "getting pod location for '$package'\n" if TRACE_ALL;
352 0   0       $self->{pod_from} ||= pod_where( { -inc => 1 }, $package );
353              
354 0           my $pod_from = $self->{pod_from};
355 0 0         unless ($pod_from) {
356 0           $self->{why_unrated} = "couldn't find pod";
357 0           return;
358             }
359              
360             print "parsing '$pod_from'\n" if TRACE_ALL;
361 0           my $pod = Pod::Coverage::Extractor->new;
362 0           $pod->{nonwhitespace} = $self->{nonwhitespace};
363 0           $pod->parse_from_file( $pod_from, '/dev/null' );
364              
365 0   0       return $pod->{identifiers} || [];
366             }
367              
368             =item _private_check($symbol)
369              
370             return true if the symbol should be considered private
371              
372             =cut
373              
374             sub _private_check {
375 0     0     my $self = shift;
376 0           my $sym = shift;
377 0           return grep { $sym =~ /$_/ } @{ $self->{private} };
  0            
  0            
378             }
379              
380             =item _trustme_check($symbol)
381              
382             return true if the symbol is a 'trustme' symbol
383              
384             =cut
385              
386             sub _trustme_check {
387 0     0     my ( $self, $sym ) = @_;
388 0           return grep { $sym =~ /$_/ } @{ $self->{trustme} };
  0            
  0            
389             }
390              
391             sub _CvGV {
392 0     0     my $self = shift;
393 0           my $cv = shift;
394 0           my $b_cv = B::svref_2object($cv);
395              
396             # perl 5.6.2's B doesn't have an object_2svref. in 5.8 you can
397             # just do this:
398             # return *{ $b_cv->GV->object_2svref };
399             # but for backcompat we're forced into this uglyness:
400             no strict 'refs';
401 0           return *{ $b_cv->GV->STASH->NAME . "::" . $b_cv->GV->NAME };
  0            
402             }
403              
404             package Pod::Coverage::Extractor;
405             use Pod::Parser;
406             use base 'Pod::Parser';
407              
408             use constant debug => 0;
409              
410             # extract subnames from a pod stream
411             sub command {
412 0     0     my $self = shift;
413 0           my ( $command, $text, $line_num ) = @_;
414 0 0 0       if ( $command eq 'item' || $command =~ /^head(?:2|3|4)/ ) {
415              
416             # take a closer look
417 0           my @pods = ( $text =~ /\s*([^\s\|,\/]+)/g );
418 0           $self->{recent} = [];
419              
420 0           foreach my $pod (@pods) {
421             print "Considering: '$pod'\n" if debug;
422              
423             # it's dressed up like a method cal
424 0 0         $pod =~ /-E<\s*gt\s*>(.*)/ and $pod = $1;
425 0 0         $pod =~ /->(.*)/ and $pod = $1;
426              
427             # it's used as a (bare) fully qualified name
428 0 0         $pod =~ /\w+(?:::\w+)*::(\w+)/ and $pod = $1;
429              
430             # it's wrapped in a pod style B<>
431 0           $pod =~ s/[A-Z]
432 0           $pod =~ s/>//g;
433              
434             # has arguments, or a semicolon
435 0 0         $pod =~ /(\w+)\s*[;\(]/ and $pod = $1;
436              
437             print "Adding: '$pod'\n" if debug;
438 0 0         push @{ $self->{ $self->{nonwhitespace}
  0            
439             ? "recent"
440             : "identifiers" } }, $pod;
441             }
442             }
443             }
444              
445             sub textblock {
446 0     0     my $self = shift;
447 0           my ( $text, $line_num ) = shift;
448 0 0 0       if ( $self->{nonwhitespace} and $text =~ /\S/ and $self->{recent} ) {
      0        
449 0           push @{ $self->{identifiers} }, @{ $self->{recent} };
  0            
  0            
450 0           $self->{recent} = [];
451             }
452             }
453              
454             1;
455              
456             __END__