Prevent compiler execution of BEGIN / UNITCHECK / CHECK / INIT blocks
Asked Answered
D

1

1

I want to detect grammar errors in my perl code. I've found that perlcritic misses many of them, (eg a random else inserted before any if, so trying to compile with perl -cw looks like the only viable option.

However, I don't want to open myself up for executing code when checking for errors.

This perlmonks post shows that in BEGIN, INIT, UNITCHECK, and CHECK blocks can / do get executed when compiling.

Can I grammar check perl code without running any of it?

What about removing or renaming the block which could cause execution?

Disadvantaged answered 4/9, 2020 at 12:32 Comment(2)
perlcritic and perl-tidy don't run the code,Rabiah
@Rabiah I tried linting with perlcritic and all seemed well... until I tried compiling the code and got a whole bunch more errors.Disadvantaged
S
2

Neither perlcritic nor perltidy execute any of the code they analyse/manipulate.


To properly parse Perl code, part of it needs to be executed.

For example,

BEGIN {
   if (rand() < 0.5) {
      *f = sub { 5 };
   } else {
      *f = sub() { 5 };
   }
}

print f + 2;

randomly outputs 5 or 7 because the last statement is randomly compiled as one of the following:

print( f( +2 ) );   # rand() >= 0.5
print( f() + 2 );   # rand() < 0.5

Ok, so that's pretty far-fetched. Or is it? How is that different than

use Module qw( f );   # No different than a BEGIN block.

print f + 2;

Ok, so prototypes are discouraged. But what about

say "foo";   # This is a sub call.

use feature qw( say );

say "foo";   # This isn't a sub call; this is the say operator.

This means that correctly parsing code that uses the say operator (not counting CORE::say) requires executing code. A lot code uses the say operator.

But, if you account for a few common special cases, and if you accept a certain amount of imprecision (like whether say is a sub call or the say operator), one could parse Perl code fairly accurately without executing any of it. This is the idea behind PPI.

  • perlcritic uses PPI. It doesn't execute any of the code it analyses.
  • perltidy uses its own parser. It doesn't execute any of the code it analyses.
  • perl -c will execute BEGIN blocks (including use statements) and the like.
Scan answered 4/9, 2020 at 13:20 Comment(2)
Thanks for this excellent explanation. What about grammar errors? Surely they can be detected without execution? I'm currently using both perlcritic and (grudgingly) perl -cw in my vim linters, as the latter seems to be required for grammar errors.Disadvantaged
You mean syntax errors? No. I used an example the produced two different syntaxes based on a random number. I could easily have made them incompatible syntaxes (e.g. by using (&) instead of ()).Scan

© 2022 - 2024 — McMap. All rights reserved.