I want to scan a code base to identify all instances of undefined subroutines that are not presently reachable.
As an example:
use strict;
use warnings;
my $flag = 0;
if ( $flag ) {
undefined_sub();
}
When $flag
evaluates to true, the following warning is emitted:
Undefined subroutine &main::undefined_sub called at - line 6
I don't want to rely on warnings issued at run-time to identify undefined subroutines
The strict
and warnings
pragmas don't help here. use strict 'subs'
has no effect.
Even the following code snippet is silent
$ perl -Mstrict -we 'exit 0; undefined_sub()'
Perhaps Subroutines::ProhibitCallsToUndeclaredSubs policy from Perl::Critic can help
This Policy checks that every unqualified subroutine call has a matching subroutine declaration in the current file, or that it explicitly appears in the import list for one of the included modules.
This "policy" is a part of Perl::Critic::StricterSubs, which needs to be installed. There are a few more policies there. This is considered a severity 4 violation, so you can do
perlcritic -4 script.pl
and parse the output for neither declared nor explicitly imported
, or use
perlcritic -4 --single-policy ProhibitCallsToUndeclaredSubs script.pl
Some legitimate uses are still flagged, since it requires all subs to be imported explicitly.
This is a static analyzer, which I think should fit your purpose.
What you're asking for is in at least some sense impossible. Consider the following code snippet:
( rand()<0.5 ? *foo : *bar } = sub { say "Hello World!" };
foo();
There is a 50% chance that this will run OK, and a 50% chance that it will give an "Undefined subroutine" error. The decision is made at runtime, so it's not possible to tell before that what it will be. This is of course a contrived case to demonstrate a point, but runtime (or compile-time) generation of subroutines is not that uncommon in real code. For an example, look at how Moose
adds functions that create methods. Static source code analysis will never be able to fully analyze such code.
B::Lint
is probably about as good as something pre-runtime can get.
To find calls to subs that aren't defined at compile time, you can use B::Lint as follows:
a.pl
:
use List::Util qw( min );
sub defined_sub { }
sub defined_later;
sub undefined_sub;
defined_sub();
defined_later();
undefined_sub();
undeclared_sub();
min();
max(); # XXX Didn't import
List::Util::max();
List::Util::mac(); # XXX Typo!
sub defined_later { }
Test:
$ perl -MO=Lint,undefined-subs a.pl
Undefined subroutine 'undefined_sub' called at a.pl line 9
Nonexistent subroutine 'undeclared_sub' called at a.pl line 10
Nonexistent subroutine 'max' called at a.pl line 12
Nonexistent subroutine 'List::Util::mac' called at a.pl line 14
a.pl syntax OK
Note that this is just for sub calls. Method calls (such as Class->method
and method Class
) aren't checked. But you are asking about sub calls.
Note that foo $x
is a valid method call (using the indirect method call syntax) meaning $x->foo
if foo
isn't a valid function or sub, so B::Lint won't catch that. But it will catch foo($x)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With