Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

use warnings; use strict; use 5.008; sub monk($) {} sub go() { mink(123); }
It would be nice if the typo ("mink") was caught earlier (at compile-time, i.e. by running "perl -c foo.pl"). I don't think this is possible in Perl, but maybe some wise monk knows a way.

Replies are listed 'Best First'.
Re: Possible to catch undefined sub at "compile" time?
by tinita (Parson) on Oct 10, 2007 at 11:54 UTC
    perl can't know if the sub won't be defined because you can define subs at runtime.
    use warnings; use strict; use 5.008; sub monk($) {} some_code_that_defines_a_sub_mink(); sub go() { mink(123); }
    how should this behave with your suggestion?
Re: Possible to catch undefined sub at "compile" time?
by shmem (Chancellor) on Oct 10, 2007 at 12:06 UTC
    Short answer: no.

    Long answer: perl doesn't know at compile time whether the typeglob CODE slot it allocates at compile time will be actually filled with code at run time.

    #!/usr/bin/perl # use warnings; use strict; use 5.008; sub monk($) {} sub go() { mink(123); } BEGIN { print "$_\n" for grep { /^m/ } sort keys %main:: } go(); __END__ main:: mink monk Undefined subroutine &main::mink called at sub.pl line 5.

    Mentioning a subroutine somewhere in the code has (as far as typeglob allocation goes) a similar effect as subroutine forward declarations:

    #!/usr/bin/perl # use warnings; use strict; use 5.008; sub mink; # sub mink still undefined, but typeglob allocated. sub monk($) {} sub go() { mink(123); } BEGIN { print "$_\n" for grep { /^m/ } sort keys %main:: } go(); __END__ main:: mink monk Undefined subroutine &main::mink called at sub.pl line 6.

    AFAIK there's no pragma which enforces allocating the code reference *) in the CODE slot along with typeglob allocation.

    *) Actually it is not just the code reference (which is what you get when you look into the CODE slot) but something deeper in perl's data structures. That is why

    use strict; $SIG{CHLD} = \&mywait; # value in the %SIG hash is an empty code ref

    compiles without problem, even if the sub mywait { } never gets defined, and the coderef therefor is never populated with code proper.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Possible to catch undefined sub at "compile" time?
by andreas1234567 (Vicar) on Oct 10, 2007 at 11:11 UTC
    One possible solution to this would be to create test scripts (e.g. using Test::More) for the module in question:
    package foo; use warnings; use strict; sub monk { return 42; } sub go { return mink(123); } 1; __END__
    use strict; use warnings; use Test::More tests => 3; use lib qw (.); BEGIN { use_ok('foo'); } # ------ monk ------ cmp_ok(foo::monk(), q{==}, 42, q{Expect monk to return 42}); # ------ go ------ cmp_ok(foo::go(), q{==}, 42, q{Expect go to return 42}); __END__
    Running the test yields:
    $ prove foo.t foo....ok 1/3Undefined subroutine &foo::mink called at foo.pm line 8. # Looks like you planned 3 tests but only ran 2. # Looks like your test died just after 2. foo....dubious Test returned status 255 (wstat 65280, 0xff00) DIED. FAILED test 3 Failed 1/3 tests, 66.67% okay Failed Test Stat Wstat Total Fail List of Failed ---------------------------------------------------------------------- +--------- foo.t 255 65280 3 2 3 Failed 1/1 test scripts. 1/3 subtests failed. Files=1, Tests=3, 1 wallclock secs ( 0.04 cusr + 0.00 csys = 0.04 C +PU) Failed 1/1 test programs. 1/3 subtests failed.
    --
    Andreas
      Andreas, thanks, but this is catching the problem at runtime. Ideally what I would like is a warning/error when referencing a sub which is not already defined. I'm sure this isn't possible in the general case, but perhaps it is possible for code similar to my simple example?
      --Ben

        The problem is, it may require running the script to determine if a function really isn't defined. You may have two scripts that declare functions into the same namespace, use AUTOLOAD, or generate functions within the script (possibly with input from an external source)

        foreach my $function (<DATA>) { no strict refs; chomp $function; *$function = sub { print "$function : @_\n"; }; } mink(12); go('somewhere'); monk(8); __DATA__ mink go
Re: Possible to catch undefined sub at "compile" time?
by syphilis (Archbishop) on Oct 10, 2007 at 12:27 UTC
    It would be nice if the typo ("mink") was caught ... at compile-time

    <flippancy>

    Assuming your script is actually going to call the go subroutine, your requirement would be satisfied by running the entire script inside a BEGIN{} block:
    BEGIN { use warnings; use strict; use 5.008; go(); sub monk {} sub go { mink(123); } }; print "After compilation, this message appears\n"; __END__ Outputs: Undefined subroutine &main::mink called at try.pl line 9. BEGIN failed--compilation aborted at try.pl line 11.
    </flippancy>

    Cheers,
    Rob
      Cheater!

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
        And the print "After compilation, this message appears\n"; that never gets executed .... that must be post-runtime code :-)

        You're right, of course - the <flippancy> tags could just as well (or better) be replaced with <cheat> tags.

        Cheers,
        Rob
Re: Possible to catch undefined sub at "compile" time? (two ways)
by tye (Sage) on Oct 10, 2007 at 14:26 UTC
    use warnings; use strict; sub monk {} sub go { mink 123; }

    Done.

    It also wouldn't be hard to write a module to scan the symbol table for references to undefined subroutines after the main script finished compiling and right before it starts running. I believe diotalevi(?) already wrote a module to report references to undefined subroutines.

    - tye        

      I didn't write B::Lint. It comes with your perl.

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

        I guess diotalevi's solution is what I was looking for:

        > perl -MO=Lint -c mink.pl Nonexistant subroutine 'mink' called at mink.pl line 4 mink.pl syntax OK

        It's a pity that it doesn't work on Windows to add '-MO=Lint' in the shebang line.

        Thanks everyone for your help and/or entertaining suggestions.

Re: Possible to catch undefined sub at "compile" time?
by SuicideJunkie (Vicar) on Oct 10, 2007 at 13:47 UTC
    Symbol-Approx-Sub-2.02
    You could also have perl "do what I mean" and run monk() despite your typo!

    Note: Don't use that in production code ;)