I'm new to Perl, and am currently tasked with tidying and maintaining a large and pretty messy Perl project. I'm using perl-critic to help me detect issues in the code (and also to teach me best practices).
The existing code has places where the coder has created unreachable code. For instance, they added '&& 0' as a lazy way of commenting out some of the code branches:
if ($req->param('donut') && 0) {
unreachable code...
} else {
always branches to here...
}
I'd hoped that perl or Critic would warn me about unreachable code in such instances (where a conditional has a constant value evaluating to false), but it doesn't.
Is there a tool or a piece of script I could use which can reliably detect this kind of thing?
Obviously I could search for '&& 0' in the source but there are a number of ways that the coder could have created unreachable code besides appending '&& 0' to an if statement.
Using B::Deparse, you can detect unreachable code in some situations:
perl -MO=Deparse -e 'if (0 && $x) {print 1} else {print 2}'
do {
print 2
};
-e syntax OK
It's not so easy if the 0 is not the first condition, though:
perl -MO=Deparse -e 'if ($x && 0) {print 1} else {print 2}'
if ($x and 0) {
print 1;
}
else {
print 2;
}
-e syntax OK
Why is it different? Well, if 0 comes last, all the conditions before it must be checked. They can have side-effects which will still happen. Also, &&
forces a scalar context, so it can change the behaviour of the code called when evaluating the condition.
This doesn't explain why the block itself isn't compiled away, sorry. My guess would be it just seemed too complicated.
As per choroba's answer, B::Deparse will be able to show you cases where the code is so obviously unreachable that the Perl compiler optimizes it away. But, in the general case it's impossible to detect. The following code includes an effectively unreachable block.
use 5.006;
if ($] < 5) { ... }
Because $]
is a variable which returns the currently running version of Perl, which is guaranteed to be at least 5.006 by the use
line. But you'd need some pretty clever techniques to figure that out using static analysis of the source code. (As an aside, although an unusual thing to do, it is possible to alter the value of $]
at run-time — see Acme::Futuristic::Perl — in which case the code will become reachable.)
If you have a decent test suite for your code, Devel::Cover may be useful. You set the environment variable PERL5OPT
to -MDevel::Cover
, then run your test suite (note it will run a little slower than usual), then run the command cover
which will produce a pretty HTML report. This report will highlight which subs were not executed, which branches were never used, etc.
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