File Coverage

blib/lib/Perl/ImportReport.pm
Criterion Covered Total %
statement 123 134 91.7
branch 34 44 77.2
condition 9 14 64.2
subroutine 11 12 91.6
pod 3 3 100.0
total 180 207 86.9


line stmt bran cond sub pod time code
1             package Perl::ImportReport;
2              
3 1     1   26437 use strict;
  1         2  
  1         39  
4 1     1   5 use warnings;
  1         1  
  1         29  
5              
6 1     1   1248 use PPI ();
  1         176648  
  1         21  
7 1     1   10 use PPI::Util ();
  1         2  
  1         516  
8              
9             $Perl::ImportReport::VERSION = '0.1';
10              
11             sub new {
12 2   100 2 1 234 my $ppi = PPI::Util::_Document( $_[1] ) || return;
13              
14 1         21630 return bless {
15             'ppi_document' => $ppi,
16             'import_report' => undef,
17             },
18             $_[0];
19             }
20              
21             sub get_ppi_document {
22 0     0 1 0 return $_[0]->{'ppi_document'};
23             }
24             *Document = *get_ppi_document; # to match Perl::MinimumVersion
25              
26             sub get_import_report {
27 1     1 1 418 my $iro = shift;
28              
29             # restart fresh
30 1         8 $iro->{'import_report'} = undef;
31              
32             # Create a map of all PPI::Statement::Package's so we can determine what package a given PPI::Statement::Include is in
33 1         8 my %pkg = ( 0 => [ { 'namespace' => 'main', 'column_number' => 0 } ] );
34             my $pkg_nodes = $iro->{'ppi_document'}->find(
35             sub {
36 85 100   85   818 if ( $_[1]->isa('PPI::Statement::Package') ) {
37 5 50       28 if ( my $ns = $_[1]->namespace() ) {
38 5         132 my $line_no = $_[1]->line_number();
39 5 100       5151 $pkg{$line_no} = [] if !exists $pkg{$line_no};
40              
41 5         6 push @{ $pkg{$line_no} },
  5         21  
42             {
43             'namespace' => $ns,
44             'column_number' => $_[1]->column_number(),
45             };
46 5         90 return 1;
47             }
48             }
49 80         123 return;
50             }
51 1         16 );
52              
53 1         16 $iro->{'import_report'}{'number_of_includes'} = 0;
54 1 50       10 if ( $iro->{'ppi_document'}->find_any('PPI::Statement::Include') ) {
55             my $inc_nodes = $iro->{'ppi_document'}->find(
56             sub {
57 202 50 66 202   1818 if ( $_[1]->isa('PPI::Statement::Include') && !$_[1]->pragma && $_[1]->module && $_[1]->type eq 'use' ) {
      66        
      33        
58 21         1318 return 1;
59             }
60 181         322 return;
61             }
62 1         328 );
63              
64 1 50       13 return $iro->{'import_report'} if ref($inc_nodes) ne 'ARRAY';
65              
66 1         2 my @incs;
67 1         2 for my $ppi_inc ( @{$inc_nodes} ) {
  1         4  
68            
69 21         28 my $parent_package;
70 21         101 for my $line_num ( sort { $a <=> $b } keys %pkg ) {
  126         222  
71 105 100       802 if ( $line_num <= $ppi_inc->line_number() ) {
72 52 100       1105 if ( $line_num == $ppi_inc->line_number() ) {
73 1         16 for my $ns_hr ( @{ $pkg{$line_num} } ) {
  1         3  
74 2 100       22 if ( $ns_hr->{'column_number'} < $ppi_inc->column_number() ) {
75 1         19 $parent_package = $ns_hr->{'namespace'};
76             }
77             }
78             }
79             else {
80 51         941 $parent_package = $pkg{$line_num}->[-1]{'namespace'};
81             }
82             }
83             }
84              
85 21         363 my %import_data = (
86             'raw_perl' => "$ppi_inc",
87             'module' => $ppi_inc->module(),
88             'module_version' => $ppi_inc->module_version(),
89             'arguments' => [ $ppi_inc->arguments() ],
90             'line_number' => $ppi_inc->line_number(),
91             'in_package' => $parent_package,
92             'exporter' => {},
93             );
94              
95 21         3326 my $module = $ppi_inc->module();
96 21 100       470 if ( !defined $ppi_inc->arguments() ) {
97 5         432 eval "require $module;"; # TODO: ? PPI $module instead so as not to run code ?...
98 1     1   5 no strict 'refs';
  1         2  
  1         212  
99 5         18888 $import_data{'exporter'}{'EXPORT'}{'error'} = $@;
100 5         9 $import_data{'exporter'}{'EXPORT'}{'count'} = @{"$module\::EXPORT"};
  5         30  
101 5         9 @{ $import_data{'exporter'}{'EXPORT'}{'data'} } = @{"$module\::EXPORT"};
  5         20  
  5         12  
102              
103 5         9 $import_data{'symbol_list'} = \@{"$module\::EXPORT"};
  5         21  
104 5         11 push @{ $iro->{'import_report'}{'imports'} }, \%import_data;
  5         18  
105 5         35 $iro->{'import_report'}{'number_of_includes'}++;
106             }
107             else {
108 16         587 my $list = join( '', map { $_->content() } $ppi_inc->arguments() );
  26         592  
109              
110 16 100       360 if ( $list !~ m/^\s*\(/ ) {
111 13         27 $list = "($list)";
112             }
113              
114 1     1   5 my @list = do { no strict; eval $list };
  1         2  
  1         76  
  16         20  
  16         965  
115 16         49 my @expanded = @list;
116              
117 16 100       53 if (@list) {
118              
119             # If any of the entries in an import list begins with !, : or / then the list is treated
120             # as a series of specifications which either add to or delete from the list of names to
121             # import. They are processed left to right. Specifications are in the form:
122             # [!]name This name only
123             # [!]:DEFAULT All names in @EXPORT
124             # [!]:tag All names in $EXPORT_TAGS{tag} anonymous list
125             # [!]/pattern/ All names in @EXPORT and @EXPORT_OK which match
126              
127             # TODO: @list contains only qr()
128 13 100       81 if ( grep m{^[!:/]}, @list ) {
129 7         361 eval "require $module;";
130 1     1   3 no strict 'refs';
  1         2  
  1         550  
131              
132 7         45 $import_data{'exporter'}{'EXPORT_OK'}{'error'} = $@;
133 7         10 @{ $import_data{'exporter'}{'EXPORT_OK'}{'data'} } = @{"$module\::EXPORT_OK"};
  7         23  
  7         27  
134              
135 7         22 $import_data{'exporter'}{'EXPORT_TAGS'}{'error'} = $@;
136 7         33 $import_data{'exporter'}{'EXPORT_TAGS'}{'data'} = \%{"$module\::EXPORT_TAGS"}; # TOOD: ? copy ?
  7         29  
137              
138 7         15 @expanded = ();
139              
140 7         14 for my $ent (@list) {
141 13         20 my $symbol = $ent;
142 13         25 my $remove = 0;
143 13 100       44 if ( $ent =~ m/^\!(.*)/ ) {
144 4         6 $remove = 1;
145 4         9 $symbol = $1;
146             }
147              
148 13         18 my @symbols;
149              
150 13 100 66     73 if ( substr( $symbol, 0, 1 ) eq ':' ) {
    100          
151 2 50       4 if ( exists ${"$module\::EXPORT_TAGS"}{$symbol} ) {
  2         10  
152 2         3 @symbols = @{ ${"$module\::EXPORT_TAGS"}{$symbol} };
  2         2  
  2         9  
153             }
154             else {
155 0         0 my $copy = $symbol;
156 0         0 $copy =~ s/^://;
157 0 0       0 if ( exists ${"$module\::EXPORT_TAGS"}{$copy} ) {
  0         0  
158 0         0 @symbols = @{ ${"$module\::EXPORT_TAGS"}{$copy} };
  0         0  
  0         0  
159             }
160             }
161              
162             }
163             elsif ( ref($symbol) eq 'Regexp' || substr( $symbol, 0, 1 ) eq '/' ) {
164 3         6 my $qr = $symbol;
165 3 50       10 if ( ref($symbol) ne 'Regexp' ) {
166 3         6 my $copy = $symbol;
167 3         14 $copy =~ s{^\/}{};
168 3         15 $copy =~ s{\/$}{};
169 3         36 $qr = qr($copy);
170             }
171              
172 3         8 @symbols = grep $qr, @{"$module\::EXPORT_OK"};
  3         10  
173 3 0       7 push @symbols, map { $_ =~ $qr ? @{ ${"$module\::EXPORT_TAGS"}{$_} } : () } keys %{"$module\::EXPORT_TAGS"};
  0         0  
  0         0  
  0         0  
  3         15  
174             }
175             else {
176 8         21 @symbols = ($symbol);
177             }
178              
179             # TODO: normalize sigil-prefixed names in some sensical manner
180 13 100       65 if ($remove) {
181 4         6 my %remove;
182 4         11 @remove{@symbols} = ();
183 4 100       6 @expanded = map { exists $remove{$_} ? () : ($_) } @expanded;
  7         32  
184             }
185             else {
186 9         27 push @expanded, @symbols;
187             }
188             }
189             }
190              
191 13         45 $import_data{'symbol_list'} = \@expanded;
192 13         21 push @{ $iro->{'import_report'}{'imports'} }, \%import_data;
  13         33  
193 13         48 $iro->{'import_report'}{'number_of_includes'}++;
194             }
195             }
196             }
197             }
198              
199 1         17 return $iro->{'import_report'};
200             }
201              
202             1;
203              
204             __END__