| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::perlimports::Include; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 74 |  |  | 74 |  | 1728 | use Moo; | 
|  | 74 |  |  |  |  | 9071 |  | 
|  | 74 |  |  |  |  | 679 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | our $VERSION = '0.000051'; | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 74 |  |  | 74 |  | 69892 | use Data::Dumper qw( Dumper ); | 
|  | 74 |  |  |  |  | 405635 |  | 
|  | 74 |  |  |  |  | 6357 |  | 
| 8 | 74 |  |  | 74 |  | 666 | use List::Util   qw( any none uniq ); | 
|  | 74 |  |  |  |  | 218 |  | 
|  | 74 |  |  |  |  | 6402 |  | 
| 9 | 74 |  |  | 74 |  | 48608 | use Memoize      qw( flush_cache memoize ); | 
|  | 74 |  |  |  |  | 188780 |  | 
|  | 74 |  |  |  |  | 4782 |  | 
| 10 | 74 |  |  | 74 |  | 31741 | use MooX::StrictConstructor; | 
|  | 74 |  |  |  |  | 358627 |  | 
|  | 74 |  |  |  |  | 454 |  | 
| 11 | 74 |  |  | 74 |  | 331578 | use PPI::Document               (); | 
|  | 74 |  |  |  |  | 8413621 |  | 
|  | 74 |  |  |  |  | 2991 |  | 
| 12 | 74 |  |  | 74 |  | 43637 | use PPIx::Utils::Classification qw( is_function_call is_perl_builtin ); | 
|  | 74 |  |  |  |  | 1012081 |  | 
|  | 74 |  |  |  |  | 7055 |  | 
| 13 | 74 |  |  | 74 |  | 38910 | use Ref::Util                   qw( is_plain_arrayref is_plain_hashref ); | 
|  | 74 |  |  |  |  | 43936 |  | 
|  | 74 |  |  |  |  | 5226 |  | 
| 14 | 74 |  |  | 74 |  | 1162 | use Sub::HandlesVia; | 
|  | 74 |  |  |  |  | 7265 |  | 
|  | 74 |  |  |  |  | 987 |  | 
| 15 | 74 |  |  | 74 |  | 213906 | use Try::Tiny       qw( catch try ); | 
|  | 74 |  |  |  |  | 248 |  | 
|  | 74 |  |  |  |  | 4461 |  | 
| 16 | 74 |  |  | 74 |  | 552 | use Types::Standard qw(ArrayRef Bool HashRef InstanceOf Maybe Object Str); | 
|  | 74 |  |  |  |  | 217 |  | 
|  | 74 |  |  |  |  | 838 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | with 'App::perlimports::Role::Logger'; | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  | memoize('is_function_call'); | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub BUILD { | 
| 23 | 50 |  |  | 50 | 0 | 233232 | flush_cache('is_function_call'); | 
| 24 |  |  |  |  |  |  | } | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | has _explicit_exports => ( | 
| 27 |  |  |  |  |  |  | is          => 'ro', | 
| 28 |  |  |  |  |  |  | isa         => HashRef, | 
| 29 |  |  |  |  |  |  | handles_via => 'Hash', | 
| 30 |  |  |  |  |  |  | handles     => { | 
| 31 |  |  |  |  |  |  | _delete_export         => 'delete', | 
| 32 |  |  |  |  |  |  | _explicit_export_count => 'count', | 
| 33 |  |  |  |  |  |  | _has_explicit_exports  => 'count', | 
| 34 |  |  |  |  |  |  | _import_name           => 'get', | 
| 35 |  |  |  |  |  |  | _is_importable         => 'exists', | 
| 36 |  |  |  |  |  |  | }, | 
| 37 |  |  |  |  |  |  | lazy    => 1, | 
| 38 |  |  |  |  |  |  | builder => '_build_explicit_exports', | 
| 39 |  |  |  |  |  |  | ); | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | has _document => ( | 
| 42 |  |  |  |  |  |  | is       => 'ro', | 
| 43 |  |  |  |  |  |  | isa      => InstanceOf ['App::perlimports::Document'], | 
| 44 |  |  |  |  |  |  | required => 1, | 
| 45 |  |  |  |  |  |  | init_arg => 'document', | 
| 46 |  |  |  |  |  |  | ); | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | has _export_inspector => ( | 
| 49 |  |  |  |  |  |  | is        => 'ro', | 
| 50 |  |  |  |  |  |  | isa       => InstanceOf ['App::perlimports::ExportInspector'], | 
| 51 |  |  |  |  |  |  | predicate => '_has_export_inspector',     # used in test | 
| 52 |  |  |  |  |  |  | lazy      => 1, | 
| 53 |  |  |  |  |  |  | builder   => '_build_export_inspector', | 
| 54 |  |  |  |  |  |  | ); | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | has formatted_ppi_statement => ( | 
| 57 |  |  |  |  |  |  | is      => 'ro', | 
| 58 |  |  |  |  |  |  | isa     => InstanceOf ['PPI::Statement::Include'], | 
| 59 |  |  |  |  |  |  | lazy    => 1, | 
| 60 |  |  |  |  |  |  | builder => '_build_formatted_ppi_statement', | 
| 61 |  |  |  |  |  |  | ); | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | has _ignored_modules => ( | 
| 64 |  |  |  |  |  |  | is        => 'ro', | 
| 65 |  |  |  |  |  |  | isa       => ArrayRef, | 
| 66 |  |  |  |  |  |  | init_arg  => 'ignored_modules', | 
| 67 |  |  |  |  |  |  | predicate => '_has_ignored_modules', | 
| 68 |  |  |  |  |  |  | ); | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | has _imports => ( | 
| 71 |  |  |  |  |  |  | is      => 'ro', | 
| 72 |  |  |  |  |  |  | isa     => ArrayRef, | 
| 73 |  |  |  |  |  |  | lazy    => 1, | 
| 74 |  |  |  |  |  |  | builder => '_build_imports', | 
| 75 |  |  |  |  |  |  | ); | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | has _include => ( | 
| 78 |  |  |  |  |  |  | is       => 'ro', | 
| 79 |  |  |  |  |  |  | isa      => InstanceOf ['PPI::Statement::Include'], | 
| 80 |  |  |  |  |  |  | init_arg => 'include', | 
| 81 |  |  |  |  |  |  | required => 1, | 
| 82 |  |  |  |  |  |  | ); | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | has _is_ignored => ( | 
| 85 |  |  |  |  |  |  | is      => 'ro', | 
| 86 |  |  |  |  |  |  | isa     => Bool, | 
| 87 |  |  |  |  |  |  | lazy    => 1, | 
| 88 |  |  |  |  |  |  | builder => '_build_is_ignored', | 
| 89 |  |  |  |  |  |  | ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | has _isa_test_builder_module => ( | 
| 92 |  |  |  |  |  |  | is      => 'ro', | 
| 93 |  |  |  |  |  |  | isa     => Bool, | 
| 94 |  |  |  |  |  |  | lazy    => 1, | 
| 95 |  |  |  |  |  |  | default => sub { shift->_export_inspector->isa_test_builder }, | 
| 96 |  |  |  |  |  |  | ); | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | has _is_translatable => ( | 
| 99 |  |  |  |  |  |  | is            => 'ro', | 
| 100 |  |  |  |  |  |  | isa           => Bool, | 
| 101 |  |  |  |  |  |  | lazy          => 1, | 
| 102 |  |  |  |  |  |  | builder       => '_build_is_translatable', | 
| 103 |  |  |  |  |  |  | documentation => 'Is this a require which can be converted to a use?', | 
| 104 |  |  |  |  |  |  | ); | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | has module_name => ( | 
| 107 |  |  |  |  |  |  | is      => 'ro', | 
| 108 |  |  |  |  |  |  | isa     => Maybe [Str], | 
| 109 |  |  |  |  |  |  | lazy    => 1, | 
| 110 |  |  |  |  |  |  | default => sub { shift->_include->module }, | 
| 111 |  |  |  |  |  |  | ); | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | has _original_imports => ( | 
| 114 |  |  |  |  |  |  | is          => 'ro', | 
| 115 |  |  |  |  |  |  | isa         => Maybe [ArrayRef], | 
| 116 |  |  |  |  |  |  | init_arg    => 'original_imports', | 
| 117 |  |  |  |  |  |  | handles_via => 'Array', | 
| 118 |  |  |  |  |  |  | handles     => { | 
| 119 |  |  |  |  |  |  | _all_original_imports => 'elements', | 
| 120 |  |  |  |  |  |  | _has_original_imports => 'count', | 
| 121 |  |  |  |  |  |  | }, | 
| 122 |  |  |  |  |  |  | ); | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | has _pad_imports => ( | 
| 125 |  |  |  |  |  |  | is       => 'ro', | 
| 126 |  |  |  |  |  |  | isa      => Bool, | 
| 127 |  |  |  |  |  |  | init_arg => 'pad_imports', | 
| 128 |  |  |  |  |  |  | default  => sub { 1 }, | 
| 129 |  |  |  |  |  |  | ); | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | has _tidy_whitespace => ( | 
| 132 |  |  |  |  |  |  | is       => 'ro', | 
| 133 |  |  |  |  |  |  | isa      => Bool, | 
| 134 |  |  |  |  |  |  | init_arg => 'tidy_whitespace', | 
| 135 |  |  |  |  |  |  | lazy     => 1, | 
| 136 |  |  |  |  |  |  | default  => sub { 1 }, | 
| 137 |  |  |  |  |  |  | ); | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | has _will_never_export => ( | 
| 140 |  |  |  |  |  |  | is      => 'ro', | 
| 141 |  |  |  |  |  |  | isa     => Bool, | 
| 142 |  |  |  |  |  |  | lazy    => 1, | 
| 143 |  |  |  |  |  |  | default => sub { | 
| 144 |  |  |  |  |  |  | my $self = shift; | 
| 145 |  |  |  |  |  |  | return exists $self->_document->never_exports->{ $self->module_name } | 
| 146 |  |  |  |  |  |  | || $self->_export_inspector->is_oo_class; | 
| 147 |  |  |  |  |  |  | }, | 
| 148 |  |  |  |  |  |  | ); | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | sub _build_export_inspector { | 
| 151 | 49 |  |  | 49 |  | 5019 | my $self = shift; | 
| 152 | 49 |  |  |  |  | 959 | return $self->_document->inspector_for( $self->module_name ); | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | # If we have implicit (but not explicit) exports,  we will make a best guess at | 
| 156 |  |  |  |  |  |  | # what gets exported by using the implicit list. | 
| 157 |  |  |  |  |  |  | sub _build_explicit_exports { | 
| 158 | 47 |  |  | 47 |  | 3689 | my $self = shift; | 
| 159 | 47 | 100 |  |  |  | 922 | return $self->_export_inspector->has_explicit_exports | 
| 160 |  |  |  |  |  |  | ? $self->_export_inspector->explicit_exports | 
| 161 |  |  |  |  |  |  | : $self->_export_inspector->implicit_exports; | 
| 162 |  |  |  |  |  |  | } | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | ## no critic (Subroutines::ProhibitExcessComplexity) | 
| 165 |  |  |  |  |  |  | sub _build_imports { | 
| 166 | 47 |  |  | 47 |  | 3161 | my $self = shift; | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | # This is not a real symbol, so we should never be looking for it to appear | 
| 169 |  |  |  |  |  |  | # in the code. | 
| 170 | 47 | 100 |  |  |  | 847 | $self->_delete_export('verbose') if $self->module_name eq 'Carp'; | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 47 |  |  |  |  | 1396 | my %found; | 
| 173 |  |  |  |  |  |  |  | 
| 174 |  |  |  |  |  |  | # Stolen from Perl::Critic::Policy::TooMuchCode::ProhibitUnfoundImport | 
| 175 | 47 |  |  |  |  | 131 | for my $word ( @{ $self->_document->possible_imports } ) { | 
|  | 47 |  |  |  |  | 1031 |  | 
| 176 | 726 | 50 |  |  |  | 3313 | next if exists $found{"$word"}; | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | # No need to keep looking if we've found everything that can be | 
| 179 |  |  |  |  |  |  | # imported | 
| 180 | 726 | 100 |  |  |  | 4161 | last unless $self->_imports_remain( \%found ); | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | # We don't want (for instance) pragma names to be confused with | 
| 183 |  |  |  |  |  |  | # functions. | 
| 184 |  |  |  |  |  |  | # | 
| 185 |  |  |  |  |  |  | # ie: | 
| 186 |  |  |  |  |  |  | # use warnings; | 
| 187 |  |  |  |  |  |  | # use Test::Warnings; # exports warnings() | 
| 188 |  |  |  |  |  |  | # | 
| 189 |  |  |  |  |  |  | # However, we also want to catch function calls in use statements, like | 
| 190 |  |  |  |  |  |  | # "use lib catfile( 't', 'lib');" | 
| 191 |  |  |  |  |  |  | # | 
| 192 |  |  |  |  |  |  | # or | 
| 193 |  |  |  |  |  |  | # | 
| 194 |  |  |  |  |  |  | # use Mojo::File qw( curfile ); | 
| 195 |  |  |  |  |  |  | # use lib curfile->sibling('lib')->to_string; | 
| 196 | 722 |  |  |  |  | 21852 | my $is_function_call = is_function_call($word); | 
| 197 | 722 | 100 | 66 |  |  | 123076 | if ( | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 198 |  |  |  |  |  |  | $word->parent | 
| 199 |  |  |  |  |  |  | && $word->parent->isa('PPI::Statement::Include') | 
| 200 |  |  |  |  |  |  | && (   !$is_function_call | 
| 201 |  |  |  |  |  |  | && !( $word->snext_sibling && $word->snext_sibling eq '->' ) ) | 
| 202 |  |  |  |  |  |  | ) { | 
| 203 | 210 |  |  |  |  | 15100 | next; | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | # Don't turn "use POSIX ();" into "use POSIX qw( sprintf );" | 
| 207 |  |  |  |  |  |  | # If it's a function and it's a builtin function and it's either not | 
| 208 |  |  |  |  |  |  | # included in original_imports or original imports are not implicit | 
| 209 |  |  |  |  |  |  | # then skip this. | 
| 210 | 512 | 100 | 100 |  |  | 8192 | if (   defined $self->_original_imports | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 211 | 292 |  |  | 292 |  | 2067 | && ( none { $_ eq $word } @{ $self->_original_imports } ) | 
|  | 236 |  |  |  |  | 946 |  | 
| 212 |  |  |  |  |  |  | && $is_function_call | 
| 213 |  |  |  |  |  |  | && is_perl_builtin($word) ) { | 
| 214 | 129 |  |  |  |  | 4082 | next; | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 383 |  |  |  |  | 2689 | my @found_import; | 
| 218 | 383 |  |  |  |  | 1254 | my $isa_symbol = $word->isa('PPI::Token::Symbol'); | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  | # Don't confuse my @Foo with a use of @Foo which is exported by a module. | 
| 221 | 383 | 100 | 100 |  |  | 1112 | if ( $isa_symbol && $word->content =~ m{\A(@|%|$)} ) { | 
| 222 | 25 |  |  |  |  | 226 | my $previous_sibling = $word->sprevious_sibling; | 
| 223 | 25 | 100 | 66 |  |  | 722 | if ( $previous_sibling && $previous_sibling->content eq 'my' ) { | 
| 224 | 9 |  |  |  |  | 60 | next; | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | # If a module exports %foo and we find $foo{bar}, $word->canonical | 
| 229 |  |  |  |  |  |  | # returns $foo and $word->symbol returns %foo | 
| 230 | 374 | 100 | 100 |  |  | 2319 | if (   $isa_symbol | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  | 100 | 33 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 100 |  |  |  |  | 
|  |  | 50 | 100 |  |  |  |  | 
|  |  | 50 | 66 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 231 |  |  |  |  |  |  | && $self->_is_importable( $word->symbol ) ) { | 
| 232 | 9 |  |  |  |  | 774 | @found_import = ( $word->symbol ); | 
| 233 |  |  |  |  |  |  | } | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | # Match on \&is_Str as is_Str | 
| 236 |  |  |  |  |  |  | elsif ($isa_symbol | 
| 237 |  |  |  |  |  |  | && $word->symbol_type eq '&' | 
| 238 |  |  |  |  |  |  | && $self->_is_importable( substr( $word->symbol, 1 ) ) ) { | 
| 239 | 0 |  |  |  |  | 0 | @found_import = ( substr( $word->symbol, 1 ) ); | 
| 240 |  |  |  |  |  |  | } | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | # Don't catch ${foo} here and mistake it for "foo". We deal with that | 
| 243 |  |  |  |  |  |  | # elsewhere. Don't catch @{ split_header $str }. | 
| 244 |  |  |  |  |  |  | elsif ( | 
| 245 |  |  |  |  |  |  | $self->_is_importable("$word") | 
| 246 |  |  |  |  |  |  | && !( | 
| 247 |  |  |  |  |  |  | $word =~ m{^\w} | 
| 248 |  |  |  |  |  |  | && $word->previous_token | 
| 249 |  |  |  |  |  |  | && $word->previous_token eq '{' | 
| 250 |  |  |  |  |  |  | && $word->previous_token->previous_token | 
| 251 |  |  |  |  |  |  | && $word->previous_token->previous_token eq '$' | 
| 252 |  |  |  |  |  |  | ) | 
| 253 |  |  |  |  |  |  | ) { | 
| 254 | 41 |  |  |  |  | 17841 | @found_import = ("$word"); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | # Maybe a subroutine ref has been exported. For instance, | 
| 258 |  |  |  |  |  |  | # Getopt::Long exports &GetOptions | 
| 259 |  |  |  |  |  |  | elsif ($is_function_call | 
| 260 |  |  |  |  |  |  | && $self->_is_importable( '&' . $word ) ) { | 
| 261 | 1 |  |  |  |  | 53 | @found_import = ( '&' . "$word" ); | 
| 262 |  |  |  |  |  |  | } | 
| 263 |  |  |  |  |  |  |  | 
| 264 |  |  |  |  |  |  | # Maybe this is an inner package referencing a function in main.  We | 
| 265 |  |  |  |  |  |  | # don't really deal with inner packages otherwise, so this could break | 
| 266 |  |  |  |  |  |  | # some things. | 
| 267 |  |  |  |  |  |  | elsif ($is_function_call | 
| 268 |  |  |  |  |  |  | && $word =~ m{^::\w+} | 
| 269 |  |  |  |  |  |  | && $self->_is_importable( substr( $word, 2 ) ) ) { | 
| 270 | 0 |  |  |  |  | 0 | @found_import = ( substr( $word, 2 ) ); | 
| 271 |  |  |  |  |  |  | } | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | # PPI can think that an imported function in a ternary is a label | 
| 274 |  |  |  |  |  |  | # my $foo = $enabled ? GEOIP_MEMORY_CACHE : 0; | 
| 275 |  |  |  |  |  |  | # The content of the $word will be "GEOIP_MEMORY_CACHE :" | 
| 276 |  |  |  |  |  |  | elsif ( $word->isa('PPI::Token::Label') ) { | 
| 277 | 0 | 0 |  |  |  | 0 | if ( $word->content =~ m{^(\w+)} ) { | 
| 278 | 0 |  |  |  |  | 0 | my $label = $1; | 
| 279 | 0 | 0 |  |  |  | 0 | if ( $self->_is_importable($label) ) { | 
| 280 | 0 |  |  |  |  | 0 | @found_import = ($label); | 
| 281 | 0 |  |  |  |  | 0 | $found{$label}++; | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | # Sometimes an import is only used to set a default value for a | 
| 287 |  |  |  |  |  |  | # variable in a signature. Without treating a prototype as a signature, | 
| 288 |  |  |  |  |  |  | # we would miss the import entirely.  I'm not particularly proud of | 
| 289 |  |  |  |  |  |  | # this, but since PPI doesn't yet support signatures, this will at | 
| 290 |  |  |  |  |  |  | # least help us cover some cases. If the prototype is actually a | 
| 291 |  |  |  |  |  |  | # prototype, then this just shouldn't find anything. | 
| 292 |  |  |  |  |  |  | elsif ( $word->isa('PPI::Token::Prototype') ) { | 
| 293 | 0 |  |  |  |  | 0 | my $prototype = $word->prototype; | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | # sometimes closing parens don't get included by PPI. | 
| 296 | 0 | 0 |  |  |  | 0 | if ( substr( $prototype, -1, 1 ) eq '(' ) { | 
| 297 | 0 |  |  |  |  | 0 | $prototype .= ')'; | 
| 298 |  |  |  |  |  |  | } | 
| 299 | 0 |  |  |  |  | 0 | $prototype =~ s{,}{;}g; | 
| 300 |  |  |  |  |  |  |  | 
| 301 | 0 |  |  |  |  | 0 | $prototype .= ';';    # Won't hurt if there's an extra ";" | 
| 302 | 0 |  |  |  |  | 0 | my $new   = PPI::Document->new( \$prototype ); | 
| 303 |  |  |  |  |  |  | my $words = $new->find( | 
| 304 |  |  |  |  |  |  | sub { | 
| 305 | 0 |  |  | 0 |  | 0 | $_[1]->isa('PPI::Token::Word') | 
| 306 |  |  |  |  |  |  | || $_[1]->isa('PPI::Token::Symbol'); | 
| 307 |  |  |  |  |  |  | } | 
| 308 | 0 |  | 0 |  |  | 0 | ) || []; | 
| 309 | 0 |  |  |  |  | 0 | for my $word ( @{$words} ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 310 | 0 | 0 |  |  |  | 0 | if ( $self->_is_importable("$word") ) { | 
| 311 | 0 |  |  |  |  | 0 | push @found_import, "$word"; | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  | } | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 | 374 |  |  |  |  | 71653 | for my $found (@found_import) { | 
| 317 | 51 | 100 |  |  |  | 215 | if ( !$self->_is_already_imported($found) ) { | 
| 318 | 45 |  |  |  |  | 181 | $found{$found}++; | 
| 319 |  |  |  |  |  |  | } | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  | } | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | #  A used import might be a variable interpolated into quotes. | 
| 324 | 47 | 100 |  |  |  | 491 | if ( $self->_imports_remain( \%found ) ) { | 
| 325 | 43 |  |  |  |  | 492 | for my $var ( keys %{ $self->_document->interpolated_symbols } ) { | 
|  | 43 |  |  |  |  | 1212 |  | 
| 326 | 9 | 100 |  |  |  | 285 | if ( $self->_is_importable($var) ) { | 
| 327 | 6 |  |  |  |  | 174 | $found{$var} = 1; | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | #  A used import might be just be a symbol that just gets exported.  ie. If | 
| 333 |  |  |  |  |  |  | #  it appears as @EXPORT = ( 'SOME_SYMBOL') we don't want to miss it. | 
| 334 | 47 | 50 | 100 |  |  | 1257 | if (   $self->_imports_remain( \%found ) | 
|  |  |  | 66 |  |  |  |  | 
| 335 |  |  |  |  |  |  | && $self->_document->my_own_inspector | 
| 336 |  |  |  |  |  |  | && $self->_document->my_own_inspector->is_exporter ) { | 
| 337 | 0 |  |  |  |  | 0 | for my $symbol ( | 
| 338 |  |  |  |  |  |  | uniq( | 
| 339 |  |  |  |  |  |  | $self->_document->my_own_inspector->implicit_export_names, | 
| 340 |  |  |  |  |  |  | $self->_document->my_own_inspector->explicit_export_names | 
| 341 |  |  |  |  |  |  | ) | 
| 342 |  |  |  |  |  |  | ) { | 
| 343 | 0 | 0 |  |  |  | 0 | if ( $self->_is_importable($symbol) ) { | 
| 344 | 0 |  |  |  |  | 0 | $found{$symbol} = 1; | 
| 345 |  |  |  |  |  |  | } | 
| 346 |  |  |  |  |  |  | } | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | # A used import might just be something that gets re-exported by | 
| 350 |  |  |  |  |  |  | # Sub::Exporter | 
| 351 | 47 | 100 |  |  |  | 1906 | if ( $self->_imports_remain( \%found ) ) { | 
| 352 | 43 |  |  |  |  | 938 | for my $func ( $self->_document->sub_exporter_export_list ) { | 
| 353 | 2 | 100 |  |  |  | 37 | if ( $self->_is_importable($func) ) { | 
| 354 | 1 |  |  |  |  | 30 | $found{$func}++; | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  | } | 
| 357 |  |  |  |  |  |  | } | 
| 358 |  |  |  |  |  |  |  | 
| 359 | 47 |  |  |  |  | 1336 | my @found = map { $self->_import_name($_) } keys %found; | 
|  | 52 |  |  |  |  | 430 |  | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | # Some modules have imports which are basically flags, rather than names of | 
| 362 |  |  |  |  |  |  | # symbols to export.  So if a flag is already in the import, we need to | 
| 363 |  |  |  |  |  |  | # preserve it, rather than risk altering the behaviour of the module. | 
| 364 | 47 | 100 |  |  |  | 1434 | if ( $self->_export_inspector->has_import_flags ) { | 
| 365 | 10 |  |  |  |  | 308 | for my $arg ( @{ $self->_export_inspector->import_flags } ) { | 
|  | 10 |  |  |  |  | 222 |  | 
| 366 | 10 | 100 | 100 |  |  | 539 | if ( defined $self->_original_imports | 
| 367 | 9 |  |  | 9 |  | 50 | && ( any { $_ eq $arg } @{ $self->_original_imports } ) ) { | 
|  | 6 |  |  |  |  | 37 |  | 
| 368 | 3 |  |  |  |  | 19 | push @found, $arg; | 
| 369 |  |  |  |  |  |  | } | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 47 |  |  |  |  | 1236 | @found = uniq _sort_symbols(@found); | 
| 374 | 47 | 100 |  |  |  | 286 | if ( $self->_original_imports ) { | 
| 375 | 18 |  |  |  |  | 43 | my @preserved = grep { m{\A[!_]} } @{ $self->_original_imports }; | 
|  | 28 |  |  |  |  | 111 |  | 
|  | 18 |  |  |  |  | 64 |  | 
| 376 | 18 |  |  |  |  | 100 | @found = uniq( @preserved, @found ); | 
| 377 |  |  |  |  |  |  | } | 
| 378 | 47 |  |  |  |  | 1120 | return \@found; | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | ## use critic | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | sub _build_is_ignored { | 
| 384 | 50 |  |  | 50 |  | 3728 | my $self = shift; | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 50 | 50 |  |  |  | 298 | if ( $self->_include->type eq 'require' ) { | 
| 387 | 0 | 0 |  |  |  | 0 | return 1 if !$self->_is_translatable; | 
| 388 |  |  |  |  |  |  | } | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | # This will be rewritten as "use Foo ();" | 
| 391 | 50 | 100 |  |  |  | 2589 | return 0 if $self->_will_never_export; | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 49 | 50 |  |  |  | 6065 | return 1 if $self->_export_inspector->has_fatal_error; | 
| 394 |  |  |  |  |  |  |  | 
| 395 | 49 | 50 |  |  |  | 5068 | return 0 if $self->_export_inspector->is_oo_class; | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 49 | 100 |  |  |  | 2241 | return 1 if $self->_export_inspector->is_moose_class; | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 48 | 50 |  |  |  | 6195 | return 1 if $self->_export_inspector->uses_moose; | 
| 400 |  |  |  |  |  |  |  | 
| 401 | 48 | 50 |  |  |  | 4498 | return 1 if $self->_export_inspector->is_moo_class; | 
| 402 |  |  |  |  |  |  |  | 
| 403 |  |  |  |  |  |  | return 1 | 
| 404 | 48 | 100 |  | 1 |  | 1894 | if any { $_ eq 'Moo::Object' } @{ $self->_export_inspector->pkg_isa }; | 
|  | 1 |  |  |  |  | 51 |  | 
|  | 48 |  |  |  |  | 1019 |  | 
| 405 |  |  |  |  |  |  |  | 
| 406 | 47 |  |  |  |  | 2519 | return 0; | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | sub _build_is_translatable { | 
| 410 | 47 |  |  | 47 |  | 1860 | my $self = shift; | 
| 411 |  |  |  |  |  |  |  | 
| 412 | 47 | 50 |  |  |  | 258 | return 0 if !$self->_include->type; | 
| 413 | 47 | 50 |  |  |  | 1500 | return 0 if $self->_include->type ne 'require'; | 
| 414 | 0 | 0 |  |  |  | 0 | return 0 if $self->module_name eq 'Exporter'; | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | # We can deal with a top level require. | 
| 417 |  |  |  |  |  |  | # require Foo; can be changed to use Foo (); | 
| 418 |  |  |  |  |  |  | # We don't want to touch requires which are inside any kind of a condition. | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | # If there is no parent, then it's likely just a single snippet | 
| 421 |  |  |  |  |  |  | # provided by a text editor. We can process the snippet. If it's part | 
| 422 |  |  |  |  |  |  | # of a larger document and the parent is not a PPI::Document, this | 
| 423 |  |  |  |  |  |  | # would appear not to be a top level require. | 
| 424 | 0 | 0 | 0 |  |  | 0 | if ( $self->_include->parent | 
| 425 |  |  |  |  |  |  | && !$self->_include->parent->isa('PPI::Document') ) { | 
| 426 | 0 |  |  |  |  | 0 | return 0; | 
| 427 |  |  |  |  |  |  | } | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | # Postfix conditions are a bit harder to find. If the significant | 
| 430 |  |  |  |  |  |  | # children amount to more than "require Module;", we'll just move on. | 
| 431 | 0 |  |  |  |  | 0 | my @children = $self->_include->schildren; | 
| 432 |  |  |  |  |  |  |  | 
| 433 | 0 |  |  |  |  | 0 | my $statement = join q{ }, @children[ 0 .. 2 ]; | 
| 434 | 0 | 0 |  |  |  | 0 | if ( $statement ne 'require ' . $self->module_name . ' ;' ) { | 
| 435 | 0 |  |  |  |  | 0 | return 0; | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | # Any other case of "require Foo;" should be translated to "use Foo ();" | 
| 439 |  |  |  |  |  |  | # as those are functionally equivalent. | 
| 440 | 0 |  |  |  |  | 0 | return 1; | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | ## no critic (Subroutines::ProhibitExcessComplexity) | 
| 444 |  |  |  |  |  |  | sub _build_formatted_ppi_statement { | 
| 445 | 50 |  |  | 50 |  | 7389 | my $self = shift; | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | # The following steps may seem a bit out of order, but we're trying to | 
| 448 |  |  |  |  |  |  | # short circuit if at all possible. That means not building an | 
| 449 |  |  |  |  |  |  | # ExportInspector object unless we really need to. | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | # Nothing to do here. Preserve the original statement. | 
| 452 | 50 | 100 |  |  |  | 1003 | return $self->_include if $self->_is_ignored; | 
| 453 |  |  |  |  |  |  |  | 
| 454 | 48 | 50 |  |  |  | 1733 | my $maybe_module_version | 
| 455 |  |  |  |  |  |  | = $self->_include->module_version | 
| 456 |  |  |  |  |  |  | ? q{ } . $self->_include->module_version | 
| 457 |  |  |  |  |  |  | : q{}; | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | # In this case we either have a module which we know will never export | 
| 460 |  |  |  |  |  |  | # symbols or a module which can export but for which we haven't found any | 
| 461 |  |  |  |  |  |  | # imported symbols. In both cases we'll want to rewrite with an empty list | 
| 462 |  |  |  |  |  |  | # of imports. | 
| 463 | 48 | 100 | 66 |  |  | 4164 | if (   $self->_will_never_export | 
|  |  |  | 100 |  |  |  |  | 
| 464 |  |  |  |  |  |  | || $self->_is_translatable | 
| 465 | 47 |  |  |  |  | 4045 | || !@{ $self->_imports } ) { | 
| 466 | 15 |  |  |  |  | 726 | return $self->_maybe_get_new_include( | 
| 467 |  |  |  |  |  |  | sprintf( | 
| 468 |  |  |  |  |  |  | 'use %s%s ();', $self->module_name, $maybe_module_version | 
| 469 |  |  |  |  |  |  | ) | 
| 470 |  |  |  |  |  |  | ); | 
| 471 |  |  |  |  |  |  | } | 
| 472 |  |  |  |  |  |  |  | 
| 473 | 33 |  |  |  |  | 1040 | my $statement; | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 33 |  |  |  |  | 255 | my @args = $self->_include->arguments; | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | # Don't touch a do { } block. | 
| 478 | 33 | 100 | 100 |  |  | 2064 | if ( $self->_isa_test_builder_module && @args && $args[0] eq 'do' ) { | 
|  |  |  | 66 |  |  |  |  | 
| 479 | 1 |  |  |  |  | 110 | return $self->_include; | 
| 480 |  |  |  |  |  |  | } | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | # Do some contortions to turn PPI objects back into a data structure so | 
| 483 |  |  |  |  |  |  | # that we can add or replace an import hash key and then end up with a new | 
| 484 |  |  |  |  |  |  | # list which is sorted on hash keys. This makes the assumption that the | 
| 485 |  |  |  |  |  |  | # same key won't get passed twice. This is pretty gross, but I was too lazy | 
| 486 |  |  |  |  |  |  | # to try to figure out how to do this with PPI and I think it should | 
| 487 |  |  |  |  |  |  | # *mostly* work. I don't like the formatting that Data::Dumper comes up | 
| 488 |  |  |  |  |  |  | # with, so we'll run it through perltidy. | 
| 489 |  |  |  |  |  |  |  | 
| 490 | 32 | 50 | 66 |  |  | 2741 | if (   $self->_isa_test_builder_module | 
| 491 |  |  |  |  |  |  | && @args ) { | 
| 492 | 0 |  |  |  |  | 0 | my $all; | 
| 493 |  |  |  |  |  |  |  | 
| 494 | 0 | 0 | 0 |  |  | 0 | if ( $args[0]->isa('PPI::Token::Word') ) { | 
|  |  | 0 |  |  |  |  |  | 
| 495 | 0 |  |  |  |  | 0 | $all = join q{ }, map { "$_" } @args; | 
|  | 0 |  |  |  |  | 0 |  | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | elsif ($args[0]->isa('PPI::Structure::List') | 
| 499 |  |  |  |  |  |  | && $args[0]->braces eq '()' ) { | 
| 500 | 0 |  |  |  |  | 0 | for my $child ( $args[0]->children ) { | 
| 501 | 0 |  |  |  |  | 0 | $all .= "$child"; | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | ## no critic (BuiltinFunctions::ProhibitStringyEval) | 
| 506 | 0 |  |  |  |  | 0 | my $args; | 
| 507 |  |  |  |  |  |  | my $error; | 
| 508 |  |  |  |  |  |  | try { | 
| 509 | 0 |  |  | 0 |  | 0 | $args = eval( '{' . $all . '}' ); | 
| 510 |  |  |  |  |  |  | } | 
| 511 |  |  |  |  |  |  | catch { | 
| 512 | 0 |  |  | 0 |  | 0 | $self->logger->info($_); | 
| 513 | 0 |  |  |  |  | 0 | $error = 1; | 
| 514 | 0 |  |  |  |  | 0 | }; | 
| 515 |  |  |  |  |  |  | ## use critic | 
| 516 |  |  |  |  |  |  |  | 
| 517 | 0 | 0 | 0 |  |  | 0 | if ( !$error && !is_plain_hashref($args) ) { | 
| 518 | 0 |  |  |  |  | 0 | $self->logger->info( 'Not a hashref: ' . Dumper($args) ); | 
| 519 | 0 |  |  |  |  | 0 | $error = 1; | 
| 520 |  |  |  |  |  |  | } | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | # We will replace these with our own parsed imports. | 
| 523 | 0 |  |  |  |  | 0 | delete $args->{import}; | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | # Ignore this line if we can't parse it. This will happen if the arg to | 
| 526 |  |  |  |  |  |  | # test is a do block, for example. | 
| 527 | 0 | 0 |  |  |  | 0 | return $self->_include if $error; | 
| 528 |  |  |  |  |  |  |  | 
| 529 | 0 |  |  |  |  | 0 | local $Data::Dumper::Terse         = 1; | 
| 530 | 0 |  |  |  |  | 0 | local $Data::Dumper::Indent        = 0; | 
| 531 | 0 |  |  |  |  | 0 | local $Data::Dumper::Sortkeys      = 1; | 
| 532 | 0 |  |  |  |  | 0 | local $Data::Dumper::Quotekeys     = 0; | 
| 533 | 0 |  |  |  |  | 0 | local $Data::Dumper::Useqq         = 0; | 
| 534 | 0 |  |  |  |  | 0 | local $Data::Dumper::Trailingcomma = 1; | 
| 535 | 0 |  |  |  |  | 0 | local $Data::Dumper::Deparse       = 1; | 
| 536 |  |  |  |  |  |  |  | 
| 537 | 0 |  |  |  |  | 0 | my $dumped = Dumper($args); | 
| 538 | 0 |  |  |  |  | 0 | my $non_import_args; | 
| 539 |  |  |  |  |  |  | my $import_arg; | 
| 540 |  |  |  |  |  |  |  | 
| 541 | 0 | 0 |  |  |  | 0 | if ( $dumped =~ m/^{(.*)}$/ ) { | 
| 542 | 0 |  |  |  |  | 0 | $non_import_args = $1; | 
| 543 |  |  |  |  |  |  | } | 
| 544 |  |  |  |  |  |  |  | 
| 545 | 0 | 0 |  |  |  | 0 | if ( $self->_imports ) { | 
| 546 |  |  |  |  |  |  | $import_arg = sprintf( | 
| 547 |  |  |  |  |  |  | 'import => [qw( %s )]', | 
| 548 | 0 |  |  |  |  | 0 | join( q{ }, @{ $self->_imports } ) | 
|  | 0 |  |  |  |  | 0 |  | 
| 549 |  |  |  |  |  |  | ); | 
| 550 |  |  |  |  |  |  | } | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | my $all_args = join ', ', | 
| 553 | 0 | 0 |  |  |  | 0 | grep { $_ && $_ =~ m{\w} } ( $import_arg, $non_import_args ); | 
|  | 0 |  |  |  |  | 0 |  | 
| 554 |  |  |  |  |  |  |  | 
| 555 | 0 |  |  |  |  | 0 | $statement = sprintf( | 
| 556 |  |  |  |  |  |  | 'use %s%s %s;', | 
| 557 |  |  |  |  |  |  | $self->module_name, | 
| 558 |  |  |  |  |  |  | $maybe_module_version, | 
| 559 |  |  |  |  |  |  | $all_args | 
| 560 |  |  |  |  |  |  | ); | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | # save ~60ms in cases where we don't need Perl::Tidy | 
| 563 | 0 |  |  |  |  | 0 | require Perl::Tidy;    ## no perlimports | 
| 564 | 0 |  |  |  |  | 0 | Perl::Tidy::perltidy( | 
| 565 |  |  |  |  |  |  | argv        => '-npro', | 
| 566 |  |  |  |  |  |  | source      => \$statement, | 
| 567 |  |  |  |  |  |  | destination => \$statement | 
| 568 |  |  |  |  |  |  | ); | 
| 569 |  |  |  |  |  |  | } | 
| 570 |  |  |  |  |  |  |  | 
| 571 |  |  |  |  |  |  | else { | 
| 572 | 32 | 100 |  |  |  | 549 | my $padding = $self->_pad_imports ? q{ } : q{}; | 
| 573 | 32 | 100 |  |  |  | 591 | my $template | 
| 574 |  |  |  |  |  |  | = $self->_isa_test_builder_module | 
| 575 |  |  |  |  |  |  | ? 'use %s%s import => [ qw(%s%s%s) ];' | 
| 576 |  |  |  |  |  |  | : 'use %s%s qw(%s%s%s);'; | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | $statement = sprintf( | 
| 579 |  |  |  |  |  |  | $template, | 
| 580 |  |  |  |  |  |  | $self->module_name, | 
| 581 |  |  |  |  |  |  | $maybe_module_version, | 
| 582 |  |  |  |  |  |  | $padding, | 
| 583 |  |  |  |  |  |  | join( | 
| 584 |  |  |  |  |  |  | q{ }, | 
| 585 | 32 |  |  |  |  | 921 | @{ $self->_imports } | 
|  | 32 |  |  |  |  | 851 |  | 
| 586 |  |  |  |  |  |  | ), | 
| 587 |  |  |  |  |  |  | $padding, | 
| 588 |  |  |  |  |  |  | ); | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | # Don't deal with Test::Builder classes here to keep it simple for now | 
| 592 | 32 | 50 | 33 |  |  | 612 | if ( length($statement) > 78 && !$self->_isa_test_builder_module ) { | 
| 593 | 0 |  |  |  |  | 0 | $statement = sprintf( | 
| 594 |  |  |  |  |  |  | "use %s%s qw(\n", | 
| 595 |  |  |  |  |  |  | $self->module_name, | 
| 596 |  |  |  |  |  |  | $maybe_module_version, | 
| 597 |  |  |  |  |  |  | ); | 
| 598 | 0 |  |  |  |  | 0 | for ( @{ $self->_imports } ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 599 | 0 |  |  |  |  | 0 | $statement .= "    $_\n"; | 
| 600 |  |  |  |  |  |  | } | 
| 601 | 0 |  |  |  |  | 0 | $statement .= ');'; | 
| 602 |  |  |  |  |  |  | } | 
| 603 |  |  |  |  |  |  |  | 
| 604 | 32 |  |  |  |  | 150 | return $self->_maybe_get_new_include($statement); | 
| 605 |  |  |  |  |  |  | } | 
| 606 |  |  |  |  |  |  |  | 
| 607 |  |  |  |  |  |  | ## use critic | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | sub _imports_remain { | 
| 610 | 867 |  |  | 867 |  | 1528 | my $self  = shift; | 
| 611 | 867 |  |  |  |  | 1230 | my $found = shift; | 
| 612 | 867 |  |  |  |  | 1231 | return keys %{$found} < $self->_explicit_export_count; | 
|  | 867 |  |  |  |  | 2706 |  | 
| 613 |  |  |  |  |  |  | } | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | sub _maybe_get_new_include { | 
| 616 | 47 |  |  | 47 |  | 321 | my $self      = shift; | 
| 617 | 47 |  |  |  |  | 110 | my $statement = shift; | 
| 618 | 47 |  |  |  |  | 370 | my $doc       = PPI::Document->new( \$statement ); | 
| 619 |  |  |  |  |  |  | my $includes | 
| 620 | 47 |  |  | 389 |  | 75122 | = $doc->find( sub { $_[1]->isa('PPI::Statement::Include'); } ); | 
|  | 389 |  |  |  |  | 5382 |  | 
| 621 | 47 |  |  |  |  | 1008 | my $rewrite = $includes->[0]->clone; | 
| 622 |  |  |  |  |  |  |  | 
| 623 | 47 |  |  |  |  | 6831 | my $a = $self->_include . q{}; | 
| 624 | 47 |  |  |  |  | 1450 | my $b = $rewrite . q{}; | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | # If the only difference is some whitespace before the quotes, we'll not | 
| 627 |  |  |  |  |  |  | # alter the include. This reduces some of the churn. What we want to avoid | 
| 628 |  |  |  |  |  |  | # is rewriting imports where the only change is to remove some whitespace | 
| 629 |  |  |  |  |  |  | # padding which was specifically added by perltidy. If we keep removing | 
| 630 |  |  |  |  |  |  | # changes made by perltidy this tool will be unfit to be used as a linter, | 
| 631 |  |  |  |  |  |  | # because it will either force a tidy after every run or it will introduce | 
| 632 |  |  |  |  |  |  | # tidying errors. | 
| 633 |  |  |  |  |  |  | # | 
| 634 |  |  |  |  |  |  | # So "use Foo     qw( bar );" should be considered equivalent to | 
| 635 |  |  |  |  |  |  | #    "use Foo qw( bar );" because it might be in the context of | 
| 636 |  |  |  |  |  |  | # | 
| 637 |  |  |  |  |  |  | #    use AAAAAAA qw( thing ); | 
| 638 |  |  |  |  |  |  | #    use Foo     qw( bar ); | 
| 639 |  |  |  |  |  |  | #    use FFFFFFF qw( other ); | 
| 640 |  |  |  |  |  |  | # | 
| 641 |  |  |  |  |  |  | #    If the existing include is something like | 
| 642 |  |  |  |  |  |  | #    "use Foo    123 qw( foo );" | 
| 643 |  |  |  |  |  |  | #    we should probably rewrite that since perltidy will likely rewrite | 
| 644 |  |  |  |  |  |  | #    this to | 
| 645 |  |  |  |  |  |  | #    "use Foo 123 qw( foo );" | 
| 646 |  |  |  |  |  |  |  | 
| 647 | 47 |  |  |  |  | 1676 | my $orig = $a; | 
| 648 | 47 | 100 |  |  |  | 208 | if ( _respace_include($orig) eq $b ) { | 
| 649 | 14 |  |  |  |  | 106 | return $self->_include; | 
| 650 |  |  |  |  |  |  | } | 
| 651 |  |  |  |  |  |  |  | 
| 652 | 33 | 100 |  |  |  | 927 | return $rewrite if $self->_tidy_whitespace; | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | # We will return the rewritten include if a newline has been added or | 
| 655 |  |  |  |  |  |  | # removed. This is a formatting change that we *probably* want. | 
| 656 |  |  |  |  |  |  |  | 
| 657 | 4 |  |  |  |  | 64 | $a =~ s{\s}{}g; | 
| 658 | 4 |  |  |  |  | 20 | $b =~ s{\s}{}g; | 
| 659 |  |  |  |  |  |  |  | 
| 660 | 4 | 50 |  |  |  | 32 | return ( $a eq $b ) ? $self->_include : $rewrite; | 
| 661 |  |  |  |  |  |  | } | 
| 662 |  |  |  |  |  |  |  | 
| 663 |  |  |  |  |  |  | # This function takes the original include and strips away the extra spaces | 
| 664 |  |  |  |  |  |  | # which might have been added as formatting by perltidy. This makes it easier | 
| 665 |  |  |  |  |  |  | # to compare the old include with the new and decide if we really need to | 
| 666 |  |  |  |  |  |  | # replace it. | 
| 667 |  |  |  |  |  |  | sub _respace_include { | 
| 668 | 50 |  |  | 50 |  | 1852 | my $include = shift; | 
| 669 | 50 |  |  |  |  | 418 | $include =~ s{\s+(qw|\()}{ $1}; | 
| 670 | 50 |  |  |  |  | 284 | return $include; | 
| 671 |  |  |  |  |  |  | } | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | # If there's a different module in this document which has already imported | 
| 674 |  |  |  |  |  |  | # a symbol of the same name in its original imports, the we should make | 
| 675 |  |  |  |  |  |  | # sure we don't accidentally create a duplicate import here. For example, | 
| 676 |  |  |  |  |  |  | # Path::Tiny and Test::TempDir::Tiny both export a tempdir() function. | 
| 677 |  |  |  |  |  |  | # Without this check we'd add a "tempdir" to both modules if we find it | 
| 678 |  |  |  |  |  |  | # being used in the document. | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | sub _is_already_imported { | 
| 681 | 51 |  |  | 51 |  | 107 | my $self      = shift; | 
| 682 | 51 |  |  |  |  | 98 | my $symbol    = shift; | 
| 683 | 51 |  |  |  |  | 94 | my $duplicate = 0; | 
| 684 |  |  |  |  |  |  |  | 
| 685 | 51 |  |  |  |  | 99 | foreach my $module ( | 
| 686 | 98 |  |  |  |  | 2391 | grep { $_ ne $self->module_name } | 
| 687 | 51 |  |  |  |  | 1292 | keys %{ $self->_document->original_imports } | 
| 688 |  |  |  |  |  |  | ) { | 
| 689 | 47 |  |  |  |  | 475 | $self->logger->debug( | 
| 690 |  |  |  |  |  |  | "checking $module for previous imports of $symbol"); | 
| 691 | 47 |  |  |  |  | 3095 | my @imports; | 
| 692 | 47 | 100 |  |  |  | 1014 | if ( | 
| 693 |  |  |  |  |  |  | is_plain_arrayref( | 
| 694 |  |  |  |  |  |  | $self->_document->original_imports->{$module} | 
| 695 |  |  |  |  |  |  | ) | 
| 696 |  |  |  |  |  |  | ) { | 
| 697 | 27 |  |  |  |  | 302 | @imports = @{ $self->_document->original_imports->{$module} }; | 
|  | 27 |  |  |  |  | 434 |  | 
| 698 | 27 |  |  |  |  | 329 | $self->logger->debug( | 
| 699 |  |  |  |  |  |  | 'Explicit imports found: ' . Dumper( [ sort @imports ] ) ); | 
| 700 |  |  |  |  |  |  | } | 
| 701 |  |  |  |  |  |  | else { | 
| 702 | 20 | 50 |  |  |  | 271 | if ( my $inspector = $self->_document->inspector_for($module) ) { | 
| 703 | 20 |  |  |  |  | 267 | @imports = $inspector->implicit_export_names; | 
| 704 | 20 |  |  |  |  | 564 | $self->logger->debug( 'Implicit imports found: ' | 
| 705 |  |  |  |  |  |  | . Dumper( [ sort @imports ] ) ); | 
| 706 |  |  |  |  |  |  | } | 
| 707 |  |  |  |  |  |  | } | 
| 708 |  |  |  |  |  |  |  | 
| 709 | 47 | 100 |  | 270 |  | 7013 | if ( any { $_ eq $symbol } @imports ) { | 
|  | 270 |  |  |  |  | 466 |  | 
| 710 | 6 |  |  |  |  | 12 | $duplicate = 1; | 
| 711 | 6 |  |  |  |  | 34 | $self->logger->debug("$symbol already imported via $module"); | 
| 712 | 6 |  |  |  |  | 233 | last; | 
| 713 |  |  |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | } | 
| 715 |  |  |  |  |  |  |  | 
| 716 | 51 |  |  |  |  | 459 | return $duplicate; | 
| 717 |  |  |  |  |  |  | } | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | sub _sort_symbols { | 
| 720 | 47 |  |  | 47 |  | 189 | my @list = @_; | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | ## no critic (BuiltinFunctions::RequireSimpleSortBlock) | 
| 723 |  |  |  |  |  |  | my @sorted = sort { | 
| 724 | 47 |  |  |  |  | 222 | my $A = _transform_before_cmp($a); | 
|  | 27 |  |  |  |  | 107 |  | 
| 725 | 27 |  |  |  |  | 85 | my $B = _transform_before_cmp($b); | 
| 726 | 27 |  |  |  |  | 124 | "\L$A" cmp "\L$B"; | 
| 727 |  |  |  |  |  |  | } @list; | 
| 728 | 47 |  |  |  |  | 328 | return @sorted; | 
| 729 |  |  |  |  |  |  | } | 
| 730 |  |  |  |  |  |  |  | 
| 731 |  |  |  |  |  |  | # This looks a little weird, but basically we want to maintain a stable sort | 
| 732 |  |  |  |  |  |  | # order with lists that look like (foo, $foo, @foo, %foo). We use "-" to begin | 
| 733 |  |  |  |  |  |  | # the suffix because it comes earliest in a sorted list of letters and digits. | 
| 734 |  |  |  |  |  |  | sub _transform_before_cmp { | 
| 735 | 54 |  |  | 54 |  | 101 | my $thing = shift; | 
| 736 | 54 | 100 |  |  |  | 283 | if ( $thing =~ m{\A[\$]} ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 737 | 11 |  |  |  |  | 38 | $thing = substr( $thing, 1 ) . '-0'; | 
| 738 |  |  |  |  |  |  | } | 
| 739 |  |  |  |  |  |  | elsif ( $thing =~ m{\A[@]} ) { | 
| 740 | 9 |  |  |  |  | 25 | $thing = substr( $thing, 1 ) . '-1'; | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  | elsif ( $thing =~ m{\A[%]} ) { | 
| 743 | 3 |  |  |  |  | 18 | $thing = substr( $thing, 1 ) . '-2'; | 
| 744 |  |  |  |  |  |  | } | 
| 745 | 54 |  |  |  |  | 133 | return $thing; | 
| 746 |  |  |  |  |  |  | } | 
| 747 |  |  |  |  |  |  |  | 
| 748 |  |  |  |  |  |  | 1; | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | # ABSTRACT: Encapsulate one use statement in a document | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | __END__ | 
| 753 |  |  |  |  |  |  |  | 
| 754 |  |  |  |  |  |  | =pod | 
| 755 |  |  |  |  |  |  |  | 
| 756 |  |  |  |  |  |  | =encoding UTF-8 | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | =head1 NAME | 
| 759 |  |  |  |  |  |  |  | 
| 760 |  |  |  |  |  |  | App::perlimports::Include - Encapsulate one use statement in a document | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  | =head1 VERSION | 
| 763 |  |  |  |  |  |  |  | 
| 764 |  |  |  |  |  |  | version 0.000051 | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  | =head1 METHODS | 
| 767 |  |  |  |  |  |  |  | 
| 768 |  |  |  |  |  |  | =head2 formatted_ppi_statement | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | Returns an L<PPI::Statement::Include> object. This can be stringified into an | 
| 771 |  |  |  |  |  |  | import statement or used to replace an existing L<PPI::Statement::Include>. | 
| 772 |  |  |  |  |  |  |  | 
| 773 |  |  |  |  |  |  | =head1 AUTHOR | 
| 774 |  |  |  |  |  |  |  | 
| 775 |  |  |  |  |  |  | Olaf Alders <olaf@wundercounter.com> | 
| 776 |  |  |  |  |  |  |  | 
| 777 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | This software is copyright (c) 2020 by Olaf Alders. | 
| 780 |  |  |  |  |  |  |  | 
| 781 |  |  |  |  |  |  | This is free software; you can redistribute it and/or modify it under | 
| 782 |  |  |  |  |  |  | the same terms as the Perl 5 programming language system itself. | 
| 783 |  |  |  |  |  |  |  | 
| 784 |  |  |  |  |  |  | =cut |