File Coverage

inc/Pod/Coverage.pm
Criterion Covered Total %
statement 0 115 0.0
branch 0 50 0.0
condition 0 25 0.0
subroutine 0 13 0.0
pod 5 5 100.0
total 5 208 2.4


line stmt bran cond sub pod time code
1             #line 1
2             use strict;
3              
4             package Pod::Coverage;
5             use Devel::Symdump;
6             use B;
7             use Pod::Find qw(pod_where);
8              
9             BEGIN { defined &TRACE_ALL or eval 'sub TRACE_ALL () { 0 }' }
10              
11             use vars qw/ $VERSION /;
12             $VERSION = '0.19';
13              
14             #line 103
15              
16             sub new {
17             my $referent = shift;
18             my %args = @_;
19             my $class = ref $referent || $referent;
20              
21             my $private = $args{private} || [
22             qr/^_/,
23             qr/^import$/,
24             qr/^DESTROY$/,
25             qr/^AUTOLOAD$/,
26             qr/^bootstrap$/,
27             qr/^\(/,
28             qr/^(TIE( SCALAR | ARRAY | HASH | HANDLE ) |
29             FETCH | STORE | UNTIE | FETCHSIZE | STORESIZE |
30             POP | PUSH | SHIFT | UNSHIFT | SPLICE | DELETE |
31             EXISTS | EXTEND | CLEAR | FIRSTKEY | NEXTKEY | PRINT | PRINTF |
32             WRITE | READLINE | GETC | READ | CLOSE | BINMODE | OPEN |
33             EOF | FILENO | SEEK | TELL)$/x,
34             qr/^( MODIFY | FETCH )_( REF | SCALAR | ARRAY | HASH | CODE |
35             GLOB | FORMAT | IO)_ATTRIBUTES $/x,
36             qr/^CLONE(_SKIP)?$/,
37             ];
38             push @$private, @{ $args{also_private} || [] };
39             my $trustme = $args{trustme} || [];
40             my $nonwhitespace = $args{nonwhitespace} || undef;
41              
42             my $self = bless {
43             @_,
44             private => $private,
45             trustme => $trustme,
46             nonwhitespace => $nonwhitespace
47             }, $class;
48             }
49              
50             #line 143
51              
52             sub coverage {
53             my $self = shift;
54              
55             my $package = $self->{package};
56             my $pods = $self->_get_pods;
57             return unless $pods;
58              
59             my %symbols = map { $_ => 0 } $self->_get_syms($package);
60              
61             print "tying shoelaces\n" if TRACE_ALL;
62             for my $pod (@$pods) {
63             $symbols{$pod} = 1 if exists $symbols{$pod};
64             }
65              
66             foreach my $sym ( keys %symbols ) {
67             $symbols{$sym} = 1 if $self->_trustme_check($sym);
68             }
69              
70             # stash the results for later
71             $self->{symbols} = \%symbols;
72              
73             if (TRACE_ALL) {
74             require Data::Dumper;
75             print Data::Dumper::Dumper($self);
76             }
77              
78             my $symbols = scalar keys %symbols;
79             my $documented = scalar grep {$_} values %symbols;
80             unless ($symbols) {
81             $self->{why_unrated} = "no public symbols defined";
82             return;
83             }
84             return $documented / $symbols;
85             }
86              
87             #line 186
88              
89             sub why_unrated {
90             my $self = shift;
91             $self->{why_unrated};
92             }
93              
94             #line 200
95              
96             sub naked {
97             my $self = shift;
98             $self->{symbols} or $self->coverage;
99             return unless $self->{symbols};
100             return grep { !$self->{symbols}{$_} } keys %{ $self->{symbols} };
101             }
102              
103             *uncovered = \&naked;
104              
105 0     0 1   #line 218
106 0            
107 0   0       sub covered {
108             my $self = shift;
109 0   0       $self->{symbols} or $self->coverage;
110             return unless $self->{symbols};
111             return grep { $self->{symbols}{$_} } keys %{ $self->{symbols} };
112             }
113              
114             sub import {
115             my $self = shift;
116             return unless @_;
117              
118             # one argument - just a package
119             scalar @_ == 1 and unshift @_, 'package';
120              
121             # we were called with arguments
122             my $pc = $self->new(@_);
123             my $rating = $pc->coverage;
124             $rating = 'unrated (' . $pc->why_unrated . ')'
125             unless defined $rating;
126 0 0         print $pc->{package}, " has a $self rating of $rating\n";
  0            
127 0   0       my @looky_here = $pc->naked;
128 0   0       if ( @looky_here > 1 ) {
129             print "The following are uncovered: ", join( ", ", sort @looky_here ),
130 0           "\n";
131             } elsif (@looky_here) {
132             print "'$looky_here[0]' is uncovered\n";
133             }
134             }
135              
136             #line 295
137              
138             # this one walks the symbol tree
139             sub _get_syms {
140             my $self = shift;
141             my $package = shift;
142              
143             print "requiring '$package'\n" if TRACE_ALL;
144             eval qq{ require $package };
145 0     0 1   print "require failed with $@\n" if TRACE_ALL and $@;
146             return if $@;
147 0            
148 0           print "walking symbols\n" if TRACE_ALL;
149 0 0         my $syms = Devel::Symdump->new($package);
150              
151 0           my @symbols;
  0            
152             for my $sym ( $syms->functions ) {
153              
154 0           # see if said method wasn't just imported from elsewhere
155 0 0         my $glob = do { no strict 'refs'; \*{$sym} };
156             my $o = B::svref_2object($glob);
157              
158 0           # in 5.005 this flag is not exposed via B, though it exists
159 0 0         my $imported_cv = eval { B::GVf_IMPORTED_CV() } || 0x80;
160             next if $o->GvFLAGS & $imported_cv;
161              
162             # check if it's on the whitelist
163 0           $sym =~ s/$self->{package}:://;
164             next if $self->_private_check($sym);
165              
166             push @symbols, $sym;
167             }
168             return @symbols;
169             }
170 0            
171 0           #line 336
  0            
172 0 0          
173 0           sub _get_pods {
174 0           my $self = shift;
175              
176 0           my $package = $self->{package};
177              
178             print "getting pod location for '$package'\n" if TRACE_ALL;
179             $self->{pod_from} ||= pod_where( { -inc => 1 }, $package );
180              
181             my $pod_from = $self->{pod_from};
182             unless ($pod_from) {
183             $self->{why_unrated} = "couldn't find pod";
184             return;
185             }
186              
187             print "parsing '$pod_from'\n" if TRACE_ALL;
188 0     0 1   my $pod = Pod::Coverage::Extractor->new;
189 0           $pod->{nonwhitespace} = $self->{nonwhitespace};
190             $pod->parse_from_file( $pod_from, '/dev/null' );
191              
192             return $pod->{identifiers} || [];
193             }
194              
195             #line 364
196              
197             sub _private_check {
198             my $self = shift;
199             my $sym = shift;
200             return grep { $sym =~ /$_/ } @{ $self->{private} };
201             }
202 0     0 1    
203 0 0         #line 376
204 0 0          
205 0           sub _trustme_check {
  0            
  0            
206             my ( $self, $sym ) = @_;
207             return grep { $sym =~ /$_/ } @{ $self->{trustme} };
208             }
209              
210             sub _CvGV {
211             my $self = shift;
212             my $cv = shift;
213             my $b_cv = B::svref_2object($cv);
214              
215             # perl 5.6.2's B doesn't have an object_2svref. in 5.8 you can
216             # just do this:
217             # return *{ $b_cv->GV->object_2svref };
218             # but for backcompat we're forced into this uglyness:
219             no strict 'refs';
220 0     0 1   return *{ $b_cv->GV->STASH->NAME . "::" . $b_cv->GV->NAME };
221 0 0         }
222 0 0          
223 0           package Pod::Coverage::Extractor;
  0            
  0            
224             use Pod::Parser;
225             use base 'Pod::Parser';
226              
227 0     0     use constant debug => 0;
228 0 0          
229             # extract subnames from a pod stream
230             sub command {
231 0 0         my $self = shift;
232             my ( $command, $text, $line_num ) = @_;
233             if ( $command eq 'item' || $command =~ /^head(?:2|3|4)/ ) {
234 0            
235 0           # take a closer look
236 0 0         my @pods = ( $text =~ /\s*([^\s\|,\/]+)/g );
237             $self->{recent} = [];
238 0            
239 0           foreach my $pod (@pods) {
240 0 0         print "Considering: '$pod'\n" if debug;
    0          
241 0            
242             # it's dressed up like a method cal
243             $pod =~ /-E<\s*gt\s*>(.*)/ and $pod = $1;
244 0           $pod =~ /->(.*)/ and $pod = $1;
245              
246             # it's used as a (bare) fully qualified name
247             $pod =~ /\w+(?:::\w+)*::(\w+)/ and $pod = $1;
248              
249             # it's wrapped in a pod style B<>
250             $pod =~ s/[A-Z]
251             $pod =~ s/>//g;
252              
253             # has arguments, or a semicolon
254             $pod =~ /(\w+)\s*[;\(]/ and $pod = $1;
255              
256             print "Adding: '$pod'\n" if debug;
257             push @{ $self->{ $self->{nonwhitespace}
258             ? "recent"
259             : "identifiers" } }, $pod;
260             }
261             }
262             }
263              
264             sub textblock {
265             my $self = shift;
266             my ( $text, $line_num ) = shift;
267             if ( $self->{nonwhitespace} and $text =~ /\S/ and $self->{recent} ) {
268             push @{ $self->{identifiers} }, @{ $self->{recent} };
269             $self->{recent} = [];
270             }
271             }
272              
273             1;
274              
275             __END__