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

2

8

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.

Chouinard answered 28/4, 2014 at 8:29 Comment(0)
A
9

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.

Alicea answered 28/4, 2014 at 8:47 Comment(1)
+1 for informing me about B::Deparse. Although the side-effect of the first part of the conditional was clear to me (meaning that the conditional expression itself can't be compiled away), I hadn't realised about the changing of the context introduced by the second part of the conditional.Chouinard
I
5

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.

Ironmaster answered 28/4, 2014 at 10:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.