Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect unreachable code in Perl conditional which always evaluates to false?

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.

like image 493
Josh Greifer Avatar asked Apr 28 '14 08:04

Josh Greifer


2 Answers

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.

like image 160
choroba Avatar answered Sep 25 '22 03:09

choroba


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.

like image 22
tobyink Avatar answered Sep 23 '22 03:09

tobyink