perl5 icon indicating copy to clipboard operation
perl5 copied to clipboard

[doc] perl -c vs. "outside"

Open jidanni opened this issue 1 year ago • 8 comments

man perlrun says

  -c   causes Perl to check the  syntax  of  the  program  and  then  exit
       without  executing  it.   Actually,  it  will  execute any "BEGIN",
       "UNITCHECK", or "CHECK" blocks and any "use" statements: these  are
       considered  as  occurring  outside  the  execution of your program.
       "INIT" and "END" blocks, however, will be skipped.

It seems the word "outside" needs to be changed to "inside", for the above paragraph to make sense.

Anyway, some people would just like to check the syntax of the single file in front of them, without needing to comment out any "use", or providing paths.

Just like if their program said open(my $fh, "<", "input.txt"), when not having such a input.txt ready yet. It shouldn't affect the syntax check.

So maybe an option could be added for that.

jidanni avatar Apr 13 '24 07:04 jidanni

"outside" is correct, the compilation is considered outside the execution, though this may better be replaced with "before". Syntax cannot be checked without executing these blocks and use statements, because these things can and commonly do modify the parser (the simplest example is "use strict").

Grinnz avatar Apr 13 '24 07:04 Grinnz

Conceptually, Perl programs have a compilation phase (where source code is parsed and translated to an internal representation) and a run-time phase (where the internal representation is executed). In practice, perl always performs these two steps together, one directly after the other, so for the most part, you don't have to care. But technically the steps are:

  • parsing (including BEGIN blocks and use statements)
  • "checking" (including CHECK blocks)
  • compile time ends
  • runtime begins
  • initialization (including INIT blocks)
  • main script starts running
  • ...
  • program calls exit or die or falls off the end of the main script
  • cleanup (including END blocks)
  • global objects are destroyed (so their destructors are called)

The -c switch stops after compile time ends, so all the runtime parts are skipped.

Not executing use would break a lot of code. Not just in obvious cases like use strict or use feature (which have a big impact on how the following code is parsed), but also seemingly harmless cases like:

use Scalar::Util qw(looks_like_number);
if (looks_like_number "31.42-1") {
    print "yay\n";
}

This code just imports and calls a subroutine. But without executing the use statement, we get:

String found where operator expected (Do you need to predeclare "looks_like_number"?) at - line 2, near "looks_like_number "31.42e-1""
syntax error at - line 2, near "looks_like_number "31.42e-1""
Execution of - aborted due to compilation errors.

mauke avatar Apr 13 '24 17:04 mauke

Okay okay okay. So let's just concentrate on that word "outside". Seems like it's just checking the things outside the program and not the things inside or something.

If we change it to "before" then where should we put the word "after" etc.?

I think somebody needs to do a professional redo on that paragraph. Not some amateur like me.

jidanni avatar Apr 13 '24 22:04 jidanni

@jidanni

I think it's also worth nothing that you can't just "focus on this one word in a sentence", you need to look at 'em all, and form a kind of parse tree, and then extract the meaning from that.

I'd like to propose that we just concentrate on "statements [that] are considered as occurring outside the execution of your program".

If you include those other words into your reading it becomes clearer that it's not checking the parts "considered as occurring outside the execution of your program", it's executing the parts "considered as occurring outside the execution of your program".

</@ mention>

To me it seems like an explanation of why these blocks need to be run/skipped is more than one can readily boil down into a single sentence, so it might be worth not including it in a summary/usage message and just describing the behaviour direclty:

 -c   causes Perl to check the  syntax  of  the  program  and  then  exit without  executing  it. 
      Security note: -c will run some code blocks (BEGIN/use/require/CHECK/etc)
                     and skip others (END/INIT). 
                     see L<perlrun/why-minus-c-runs-some-stuff> for more.

guest20 avatar Apr 15 '24 10:04 guest20

Hey that's a good idea. Just tell what it is going to do. No need to get into deep logic about it. OK, please make the edit. Thanks!

jidanni avatar Apr 17 '24 13:04 jidanni

We have noticed a behaviour change between v5.36.0 and v5.38.2

% more foo.pl
#!/usr/bin/perl

use Modern::Perl;

BEGIN {
    say "exit";
    exit;
};

say "hey!";

With perl v5.36.0

% perl -c foo.pl
exit
foo.pl syntax OK

With perl v5.38.2

% perl -c foo.pl
exit

We are using Test::Strict in our test suite and it is now failing on Ubuntu 24 (5.38.2) but passing in Debian 12 (5.36.0).

What's the expected behaviour here? Should I open a separate issue?

joubu avatar Sep 05 '24 08:09 joubu

https://perldoc.perl.org/perl5380delta#INIT-blocks-no-longer-run-after-an-exit()-in-BEGIN

Grinnz avatar Sep 05 '24 08:09 Grinnz

https://perldoc.perl.org/perl5380delta#INIT-blocks-no-longer-run-after-an-exit()-in-BEGIN

Thanks a lot, I missed that!

joubu avatar Sep 05 '24 09:09 joubu