File Coverage

blib/lib/Test/Vars.pm
Criterion Covered Total %
statement 266 276 96.3
branch 79 98 80.6
condition 38 48 79.1
subroutine 29 30 96.6
pod 3 3 100.0
total 415 455 91.2


line stmt bran cond sub pod time code
1             package Test::Vars;
2 45     45   5921628 use 5.010_000;
  45         162  
3 45     45   296 use strict;
  45         241  
  45         1099  
4 45     45   181 use warnings;
  45         77  
  45         4013  
5              
6             our $VERSION = '0.017';
7              
8             our @EXPORT = qw(all_vars_ok test_vars vars_ok);
9              
10 45     45   19170 use parent qw(Test::Builder::Module);
  45         13080  
  45         284  
11              
12 45     45   2951 use B ();
  45         88  
  45         941  
13 45     45   27977 use ExtUtils::Manifest qw(maniread);
  45         536969  
  45         3672  
14 45     45   19440 use IO::Pipe;
  45         391642  
  45         2326  
15 45     45   377 use List::Util 1.33 qw(all);
  45         1098  
  45         3692  
16 45     45   29108 use Storable qw(freeze thaw);
  45         213018  
  45         4301  
17 45     45   341 use Symbol qw(qualify_to_ref);
  45         83  
  45         3489  
18              
19 45   50 45   351 use constant _VERBOSE => ($ENV{TEST_VERBOSE} || 0);
  45         167  
  45         5715  
20 45     45   408 use constant _OPpLVAL_INTRO => 128;
  45         94  
  45         82613  
21              
22             #use Devel::Peek;
23             #use Data::Dumper;
24             #$Data::Dumper::Indent = 1;
25              
26             sub all_vars_ok {
27 2     2 1 507168 my(%args) = @_;
28              
29 2         44 my $builder = __PACKAGE__->builder;
30              
31 2 50       112 if(not -f $ExtUtils::Manifest::MANIFEST){
32 0         0 $builder->plan(skip_all => "No $ExtUtils::Manifest::MANIFEST ready");
33             }
34 2         18 my $manifest = maniread();
35 2         2094 my @libs = grep{ m{\A lib/ .* [.]pm \z}xms } keys %{$manifest};
  108         204  
  2         28  
36              
37 2 50       18 if (! @libs) {
38 0         0 $builder->plan(skip_all => "not lib/");
39             }
40              
41 2         22 $builder->plan(tests => scalar @libs);
42              
43 2         2520 local $Test::Builder::Level = $Test::Builder::Level + 1;
44 2         8 my $fail = 0;
45 2         8 foreach my $lib(@libs){
46 2 50       14 _vars_ok(\&_results_as_tests, $lib, \%args) or $fail++;
47             }
48              
49 1         130 return $fail == 0;
50             }
51              
52             sub _results_as_tests {
53 134     134   2338 my($file, $exit_code, $results) = @_;
54              
55 134         1361 local $Test::Builder::Level = $Test::Builder::Level + 1;
56              
57 134         6263 my $builder = __PACKAGE__->builder;
58 134         7470 my $is_ok = $builder->ok($exit_code == 0, $file);
59              
60 134         163789 for my $result (@$results) {
61 166         2920 my ($method, $message) = @$result;
62 166         2416 $builder->$method($message);
63             }
64              
65 134         113402 return $is_ok;
66             }
67              
68             sub test_vars {
69 13     13 1 3958628 my($lib, $result_handler, %args) = @_;
70 13         98 return _vars_ok($result_handler, $lib, \%args);
71             }
72              
73             sub vars_ok {
74 161     161 1 7467778 my($lib, %args) = @_;
75 161         465 local $Test::Builder::Level = $Test::Builder::Level + 1;
76 161         966 return _vars_ok(\&_results_as_tests, $lib, \%args);
77             }
78              
79             sub _vars_ok {
80 176     176   517 my($result_handler, $file, $args) = @_;
81              
82             # Perl sometimes produces Unix style paths even on Windows, which can lead
83             # to us producing error messages with a path like "lib\foo/bar.pm", which
84             # is really confusing. It's simpler to just use Unix style everywhere
85             # internally.
86 176         1176 $file =~ s{\\}{/}g;
87              
88 176         2993 my $pipe = IO::Pipe->new;
89 176         567217 my $pid = fork();
90 176 50       18973 if(defined $pid){
91 176 100       8123 if($pid != 0) { # self
92 142         18020 $pipe->reader;
93 142         1276269 my $results = thaw(join('', <$pipe>));
94 142         53232856 waitpid $pid, 0;
95              
96 142         5093 return $result_handler->($file, $?, $results);
97             }
98             else { # child
99 34         6362 $pipe->writer;
100 34         13668 exit !_check_vars($file, $args, $pipe);
101             }
102             }
103             else {
104 0         0 die "fork failed: $!";
105             }
106             }
107              
108             sub _check_vars {
109 34     34   1105 my($file, $args, $pipe) = @_;
110              
111 34         772 my @results;
112              
113 34         727 my $package = $file;
114              
115             # Looks like a file name. Turn it into a package name.
116 34 100       3051 if($file =~ /\./){
117 33         2739 $package =~ s{\A .* \b lib/ }{}xms;
118 33         1224 $package =~ s{[.]pm \z}{}xms;
119 33         1187 $package =~ s{/}{::}g;
120             }
121              
122             # Looks like a package name. Make a file name from it.
123             else{
124 1         46 $file .= '.pm';
125 1         65 $file =~ s{::}{/}g;
126             }
127              
128 34 100       1922 if(ref $args->{ignore_vars} eq 'ARRAY'){
129 1         20 $args->{ignore_vars} = { map{ $_ => 1 } @{$args->{ignore_vars}} };
  1         72  
  1         40  
130             }
131              
132 34 100       2379 if(not exists $args->{ignore_vars}{'$self'}){
133 33         1159 $args->{ignore_vars}{'$self'}++;
134             }
135              
136             # ensure library loaded
137             {
138 34     0   719 local $SIG{__WARN__} = sub{ }; # ignore warnings
  34         4682  
139              
140             # set PERLDB flags; see also perlvar
141 34         1935 local $^P = $^P | 0x200; # NAMEANON
142              
143 34         3328 local @INC = @INC;
144 34 100       1451 if($file =~ s{\A (.*\b lib)/}{}xms){
145 33         1457 unshift @INC, $1;
146             }
147 34         547 eval { require $file };
  34         59349  
148              
149 34 100       35722 if($@){
150 2         21 $@ =~ s/\n .*//xms;
151 2         52 push @results, [diag => "Test::Vars ignores $file because: $@"];
152 2         40 _pipe_results($pipe, @results);
153 2         931 return 1;
154             }
155             }
156              
157 32         1341 push @results, [note => "checking $package in $file ..."];
158             my $check_result = _check_into_stash(
159 32         144 *{qualify_to_ref('', $package)}{HASH}, $file, $args, \@results);
  32         1483  
160              
161 32         195 _pipe_results($pipe, @results);
162 32         16946 return $check_result;
163             }
164              
165             sub _check_into_stash {
166 32     32   3960 my($stash, $file, $args, $results) = @_;
167 32         300 my $fail = 0;
168              
169 32         207 foreach my $key(sort keys %{$stash}){
  32         1993  
170 186         1100 my $ref = \$stash->{$key};
171              
172 186 50       337 if (ref ${$ref} eq 'CODE') {
  186         696  
173             # Reify the glob and let perl figure out what to put in
174             # GvFILE. This is needed for the optimization added in 5.27.6 that
175             # stores coderefs directly in the stash instead of in a typeglob
176             # in the stash.
177 45     45   414 no strict 'refs';
  45         88  
  45         46277  
178 0         0 () = *{B::svref_2object($stash)->NAME . "::$key"};
  0         0  
179             }
180              
181 186 100       846 next if ref($ref) ne 'GLOB';
182              
183 185         2628 my $gv = B::svref_2object($ref);
184              
185 185         347 my $hashref = *{$ref}{HASH};
  185         419  
186 185         383 my $coderef = *{$ref}{CODE};
  185         963  
187              
188 185 100 66     6729 if(($hashref || $coderef) && $gv->FILE =~ /\Q$file\E\z/xms){
      100        
189 74 50 33     620 if($hashref && B::svref_2object($hashref)->NAME){ # stash
    50          
190 0 0       0 if(not _check_into_stash(
191             $hashref, $file, $args, $results)){
192 0         0 $fail++;
193             }
194             }
195             elsif($coderef){
196 74 100       677 if(not _check_into_code($coderef, $args, $results)){
197 10         37 $fail++;
198             }
199             }
200             }
201             }
202              
203 32         271 return $fail == 0;
204             }
205              
206             sub _check_into_code {
207 74     74   949 my($coderef, $args, $results) = @_;
208              
209 74         552 my $cv = B::svref_2object($coderef);
210              
211             # If ROOT is null then the sub is a stub, and has no body for us to check.
212 74 50 33     7755 if($cv->XSUB || $cv->ROOT->isa('B::NULL')){
213 0         0 return 1;
214             }
215              
216 74         475 my %info;
217 74         914 _count_padvars($cv, \%info, $results);
218              
219 74         222 my $fail = 0;
220              
221 74         517 foreach my $cv_info(map { $info{$_} } sort keys %info){
  90         371  
222 90         185 my $pad = $cv_info->{pad};
223              
224 90         129 push @$results, [note => "looking into $cv_info->{name}"] if _VERBOSE > 1;
225              
226 90         124 foreach my $p(@{$pad}){
  90         898  
227 1293 100 100     3927 next if !( defined $p && !$p->{outside} );
228              
229 406 100       828 if(! $p->{count}){
230 17 100       113 next if $args->{ignore_vars}{$p->{name}};
231              
232 13 100       118 if(my $cb = $args->{ignore_if}){
233 2         28 local $_ = $p->{name};
234 2 50       35 next if $cb->($_);
235             }
236              
237 11   50     38 my $c = $p->{context} || '';
238 11         170 push @$results, [diag => "$p->{name} is used once in $cv_info->{name} $c"];
239 11         42 $fail++;
240             }
241 0         0 elsif(_VERBOSE > 1){
242             push @$results, [note => "$p->{name} is used $p->{count} times"];
243             }
244             }
245             }
246              
247 74         533 return $fail == 0;
248              
249             }
250              
251             sub _pipe_results {
252 34     34   154 my ($pipe, @messages) = @_;
253 34         2733 print $pipe freeze(\@messages);
254 34         19201 close $pipe;
255             }
256              
257             my @padops;
258             my $op_anoncode;
259             my $op_enteriter;
260             my $op_entereval; # string eval
261             my $op_null;
262             my @op_svusers;
263             my $padsv_store;
264             my $aelemfastlex_store;
265             BEGIN{
266 45     45   289 foreach my $op(qw(padsv padav padhv padcv match multideref subst)){
267 315         1643 $padops[B::opnumber($op)]++;
268             }
269             # blead commit 93bad3fd55489cbd split aelemfast into two ops.
270             # Prior to that, 'aelemfast' handled lexicals too.
271 45         226 my $aelemfast = B::opnumber('aelemfast_lex');
272 45 50       793 $padops[$aelemfast == -1 ? B::opnumber('aelemfast') : $aelemfast]++;
273              
274             # The 5.37 development cycle introduced two new ops to account for.
275 45         181 $padsv_store = B::opnumber('padsv_store');
276 45 50       208 if ($padsv_store != -1) {
277 45         105 $padops[$padsv_store]++;
278 45         107 $op_svusers[$padsv_store]++;
279             }
280 45         272 $aelemfastlex_store = B::opnumber('aelemfastlex_store');
281 45 50       186 if ($aelemfastlex_store != -1) {
282 45         406 $padops[$aelemfastlex_store]++;
283 45         247 $op_svusers[$aelemfastlex_store]++;
284             }
285              
286 45         160 $op_anoncode = B::opnumber('anoncode');
287 45         96 $padops[$op_anoncode]++;
288              
289 45         214 $op_enteriter = B::opnumber('enteriter');
290 45         78 $padops[$op_enteriter]++;
291              
292 45         272 $op_entereval = B::opnumber('entereval');
293 45         86 $padops[$op_entereval]++;
294              
295 45         163 $op_null = B::opnumber('null');
296              
297 45         108 foreach my $op(qw(srefgen refgen sassign aassign)){
298 180         46890 $op_svusers[B::opnumber($op)]++;
299             }
300             }
301              
302             sub _count_padvars {
303 90     90   239 my($cv, $global_info, $results) = @_;
304              
305 90         228 my %info;
306              
307 90         1166 my $padlist = $cv->PADLIST;
308              
309 90         928 my $padvars = $padlist->ARRAYelt(1);
310              
311 90         220 my @pad;
312 90         194 my $ix = 0;
313 90         1928 foreach my $padname($padlist->ARRAYelt(0)->ARRAY){
314 1653 50       5170 if($padname->can('PVX')){
315 1653         2991 my $pv = $padname->PVX;
316              
317             # Under Perl 5.22.0+, $pv can end up as undef in some cases. With
318             # a threaded Perl, instead of undef we see an empty string.
319             #
320             # $pv can also end up as just '$' or '&'.
321 1653 100 66     10469 if(defined $pv && length $pv && $pv ne '&' && $pv ne '$' && !($padname->FLAGS & B::SVpad_OUR)){
      100        
      66        
      100        
322 515         704 my %p;
323              
324 515         1516 $p{name} = $pv;
325 515 100       5399 $p{outside} = $padname->FLAGS & B::SVf_FAKE ? 1 : 0;
326 515 100       1083 if($p{outside}){
327 112         248 $p{outside_padix} = $padname->PARENT_PAD_INDEX;
328             }
329 515         1101 $p{padix} = $ix;
330              
331 515         1622 $pad[$ix] = \%p;
332             }
333             }
334 1653         2930 $ix++;
335             }
336              
337 90         1272 my ( $cop_scan, $op_scan ) = _make_scan_subs(\@pad, $cv, $padvars, $global_info, $results, \%info);
338 90         457 local *B::COP::_scan_unused_vars;
339 90         1137 *B::COP::_scan_unused_vars = $cop_scan;
340              
341 90         290 local *B::OP::_scan_unused_vars;
342 90         2325 *B::OP::_scan_unused_vars = $op_scan;
343              
344 90         1607 my $name = sprintf('&%s::%s', $cv->GV->STASH->NAME, $cv->GV->NAME);
345              
346 90         468 my $root = $cv->ROOT;
347 90 50       179 if(${$root}){
  90         438  
348 90         948 B::walkoptree($root, '_scan_unused_vars');
349             }
350             else{
351 0         0 push @$results, [note => "NULL body subroutine $name found"];
352             }
353              
354 90         2034 %info = (
355             pad => \@pad,
356             name => $name,
357             );
358              
359 90         203 return $global_info->{ ${$cv} } = \%info;
  90         2013  
360             }
361              
362             sub _make_scan_subs {
363 90     90   375 my ($pad, $cv, $padvars, $global_info, $results, $info) = @_;
364              
365 90         151 my $cop;
366             my $cop_scan = sub {
367 931     931   3680 ($cop) = @_;
368 90         892 };
369              
370 90         174 my $stringy_eval_seen = 0;
371             my $op_scan = sub {
372 7009     7009   10371 my($op) = @_;
373              
374 7009 100       12520 return if $stringy_eval_seen;
375              
376 7006         16101 my $optype = $op->type;
377 7006 100       25527 return if !defined $padops[ $optype ];
378             # stringy eval could refer all the my variables
379 1374 100       2845 if($optype == $op_entereval){
380 1         11 foreach my $p(@$pad){
381 5         18 $p->{count}++;
382             }
383 1         2 $stringy_eval_seen = 1;
384 1         3 return;
385             }
386              
387             # In Perl 5.22+, pad variables can be referred to in ops like
388             # MULTIDEREF, which show up as a B::UNOP_AUX object. This object can
389             # refer to multiple pad variables.
390 1373 100       5435 if($op->isa('B::UNOP_AUX')) {
391 150         709 foreach my $i(grep {!ref}$ op->aux_list($cv)) {
  458         941  
392             # There is a bug in 5.24 with multideref aux_list where it can
393             # contain a value which is completely broken. It numifies to
394             # undef when used as an array index but "defined $i" will be
395             # true! We can detect it by comparing its stringified value to
396             # an empty string. This has been fixed in blead.
397 312 50       372 next unless do {
398 45     45   375 no warnings;
  45         97  
  45         38214  
399 312         1876 "$i" ne q{};
400             };
401 312 100       776 $pad->[$i]{count}++
402             if $pad->[$i];
403             }
404 150         583 return;
405             }
406              
407 1223         3387 my $targ = $op->targ;
408 1223 100       2263 return if $targ == 0; # maybe foreach (...)
409              
410 1207         1877 my $p = $pad->[$targ];
411 1207   100     5277 $p->{count} ||= 0;
412              
413 1207 100 100     6810 if($optype == $op_anoncode){
    100          
    100          
414 16         63 my $anon_cv = $padvars->ARRAYelt($targ);
415 16 50       100 if($anon_cv->CvFLAGS & B::CVf_CLONE){
416 16         182 my $my_info = _count_padvars($anon_cv, $global_info, $results);
417              
418 16         65 $my_info->{outside} = $info;
419              
420 16         23 foreach my $p(@{$my_info->{pad}}){
  16         48  
421 336 100 100     903 if(defined $p && $p->{outside_padix}){
422 80         1363 $pad->[ $p->{outside_padix} ]{count}++;
423             }
424             }
425             }
426 16         152 return;
427             }
428             elsif($optype == $op_enteriter or ($op->flags & B::OPf_WANT) == B::OPf_WANT_VOID){
429             # if $op is in void context, it is considered "not used"
430 263 100       1335 if(_ckwarn_once($cop)){
431 261         2087 $p->{context} = sprintf 'at %s line %d', $cop->file, $cop->line;
432 261         1153 return; # skip
433             }
434             }
435             elsif($op->private & _OPpLVAL_INTRO){
436             # my($var) = @_;
437             # ^^^^ padsv/non-void context
438             # ^ sassign/void context
439             #
440             # We gather all of the sibling ops that are not NULL. If all of
441             # them are SV-using OPs (see the BEGIN block earlier) _and_ all of
442             # them are in VOID context, then the variable from the first op is
443             # being used once.
444 168         214 my @ops;
445 168   66     462 for(my $o = $op->next; ${$o} && ref($o) ne 'B::COP'; $o = $o->next){
  670         3624  
446 502 100       2715 push @ops, $o
447             unless $o->type == $op_null;
448             }
449              
450 168 100 100     1294 if (all {$op_svusers[$_->type] && (($_->flags & B::OPf_WANT) == B::OPf_WANT_VOID)
  168 100 100     3180  
451             && ($_->type != $padsv_store) && ($_->type != $aelemfastlex_store) } @ops){
452 67 50       159 if(_ckwarn_once($cop)){
453 67         615 $p->{context} = sprintf 'at %s line %d',
454             $cop->file, $cop->line;
455 67         596 return; # unused, but ok
456             }
457             }
458             }
459              
460 863         4616 $p->{count}++;
461 90         1976 };
462              
463 90         674 return ($cop_scan, $op_scan);
464             }
465              
466             sub _ckwarn_once {
467 330     330   534 my($cop) = @_;
468              
469 330         1379 my $w = $cop->warnings;
470 330 100       815 if(ref($w) eq 'B::SPECIAL'){
471 320         550 return $B::specialsv_name[ ${$w} ] !~ /WARN_NONE/;
  320         1706  
472             }
473             else {
474 10         129 my $bits = ${$w->object_2svref};
  10         266  
475             # see warnings::__chk() and warnings::enabled()
476 10         77 return vec($bits, $warnings::Offsets{once}, 1);
477             }
478             }
479              
480             1;
481             __END__
482              
483             =head1 NAME
484              
485             Test::Vars - Detects unused variables in perl modules
486              
487             =head1 VERSION
488              
489             This document describes Test::Vars version 0.017.
490              
491             =head1 SYNOPSIS
492              
493             use Test::Vars;
494              
495             # Check all libs that are listed in the MANIFEST file
496             all_vars_ok();
497              
498             # Check an arbitrary file
499             vars_ok('t/lib/MyLib.pm');
500              
501             # Ignore some variables while checking
502             vars_ok 't/lib/MyLib2.pm', ignore_vars => [ '$an_unused_var' ];
503              
504             =head1 DESCRIPTION
505              
506             Test::Vars provides test functions to report unused variables either in an
507             entire distribution or in some files of your choice in order to keep your
508             source code tidy.
509              
510             =head1 INTERFACE
511              
512             =head2 Exported
513              
514             =head3 all_vars_ok(%args)
515              
516             Tests libraries in your distribution with I<%args>.
517              
518             I<libraries> are collected from the F<MANIFEST> file.
519              
520             If you want to ignore variables, for example C<$foo>, you can
521             tell it to the test routines:
522              
523             =over 4
524              
525             =item C<< ignore_vars => { '$foo' => 1 } >>
526              
527             =item C<< ignore_vars => [qw($foo)] >>
528              
529             =item C<< ignore_if => sub{ $_ eq '$foo' } >>
530              
531             =back
532              
533             Note that C<$self> will be ignored by default unless you pass
534             explicitly C<< { '$self' => 0 } >> to C<ignore_vars>.
535              
536             =head3 vars_ok($lib, %args)
537              
538             Tests I<$lib> with I<%args>.
539              
540             See C<all_vars_ok>.
541              
542             =head2 test_vars($lib, $result_handler, %args)
543              
544             This subroutine tests variables, but instead of outputting TAP, calls the
545             C<$result_handler> subroutine reference provided with the results of the test.
546              
547             The C<$result_handler> sub will be called once, with the following arguments:
548              
549             =over 4
550              
551             =item * $filename
552              
553             The file that was checked for unused variables.
554              
555             =item * $exit_code
556              
557             The value of C<$?> from the child process that actually did the tests. This
558             will be 0 if the tests passed.
559              
560             =item * $results
561              
562             This is an array reference which in turn contains zero or more array
563             references. Each of those references contains two elements, a L<Test::Builder>
564             method, either C<diag> or C<note>, and a message.
565              
566             If the method is C<diag>, the message contains an actual error. If the method
567             is C<notes>, the message contains extra information about the test, but is not
568             indicative of an error.
569              
570             =back
571              
572             =head1 MECHANISM
573              
574             C<Test::Vars> is similar to a part of C<Test::Perl::Critic>, but the mechanism
575             is different.
576              
577             While C<Perl::Critic>, the backend of C<Test::Perl::Critic>, scans the source
578             code as text, this modules scans the compiled opcodes (or AST: abstract syntax
579             tree) using the C<B> module. See also C<B> and its submodules.
580              
581             =head1 CONFIGURATION
582              
583             C<TEST_VERBOSE = 1 | 2 > shows the way this module works.
584              
585             =head1 CAVEATS
586              
587             Over time there have been reported a number of cases where Test-Vars fails to
588             report unused variables. You can review most of these cases by going to our
589             issue tracker on GitHub and selecting issues with the C<Bug> label:
590             L<https://github.com/houseabsolute/p5-Test-Vars/issues?q=is%3Aopen+is%3Aissue+label%3ABug>
591              
592             =head1 DEPENDENCIES
593              
594             Perl 5.10.0 or later.
595              
596             =head1 BUGS
597              
598             Please report new issues at our issue tracker on GitHub:
599             L<https://github.com/houseabsolute/p5-Test-Vars/issues>. We no longer use
600             rt.cpan.org for bug reports.
601              
602             =head1 SEE ALSO
603              
604             L<Perl::Critic>
605              
606             L<warnings::unused>
607              
608             L<B>
609              
610             L<Test::Builder::Module>
611              
612             =head1 AUTHOR
613              
614             Goro Fuji (gfx) E<lt>gfuji(at)cpan.orgE<gt>
615              
616             =head1 LICENSE AND COPYRIGHT
617              
618             Copyright (c) 2010, Goro Fuji (gfx). All rights reserved.
619              
620             This library is free software; you can redistribute it and/or modify
621             it under the same terms as Perl itself. See L<perlartistic> for details.
622              
623             =cut