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

As far as I can tell, Perl cannot detect undefined subroutines at compile time. Is this right? I know Perl can detect undefined symbols of most other types at compile time via "use strict". So, one way around this problem is to use references to anonymous subroutines instead of named subroutines. E.g.,

sub MyFunction { ... }
...
MyFunction();

becomes

 
my $MyFunction = sub { ... };
...
$MyFunction->();

One consequence of this style is that subroutines must be defined before they are used. This may be considered a good thing or a bad thing.

Another consequence of this style is that it makes stack traces much less meaningful. This is almost certainly a bad thing. Consider the following program.

use Carp qw(cluck); my $f = sub { cluck 'cluck'; }; sub g { cluck 'cluck'; } $f->(); g();

The output of this program is

 
cluck at ./sub.pl line 9
  main::__ANON__() called at ./sub.pl line 12
cluck at ./sub.pl line 8
  main::g() called at ./sub.pl line 11

One way around this "__ANON__" problem is to use references to named subroutines rather than references to anonymous subroutines. E.g. the "__ANON__" problem goes away if the program above is modified with the following diff.

 
< my $f = sub { cluck 'cluck'; };
---
> my $f = \&g;

But, this re-exposes us to the problem of undefined subroutines going undetected at compile time. E.g. above, if the user had accidentally typed "\&G" instead of "\&g", this would go undetected until runtime.

In addition to being error-prone to write and maintain, this solution is cumbersome to read. E.g.

 
sub MyFunction { ... }
...
MyFunction();

Becomes

 
my $MyFunction = \&MyFunction;
sub MyFunction { ... }
...
$MyFunction->();

To discourage direct use of named subroutines, some naming convention should probably be used. Perhaps a prefix of "ppp", e.g.

 
my $MyFunction = \&pppMyFunction;
sub pppMyFunction { ... }
...
$MyFunction->();

In summary, I know of no satsifying solution to this problem. Anybody got any ideas?

Update: Thanks everyone for your replies. Sorry I did not search more carefully before posting; thanks toolic for those very relevant existing posts. It seems like avoiding parentheses and/or using B::Lint are the most promising directions for my purposes.

Update 2: I've added a post to my blog on this topic.

Replies are listed 'Best First'.
Re: Detecting undefined subroutines at compile time
by JavaFan (Canon) on May 02, 2011 at 18:16 UTC
    As far as I can tell, Perl cannot detect undefined subroutines at compile time.
    Sure it can. Why do you think Perl has forward declarations? It's just that if you write f(..), you're telling Perl, well, I'm going to call a function f, but you may not have seen it yet. But I promise, by the runtime, it'll be there.

    You want compile time checking of subroutine existence? Leave off the parens.

Re: Detecting undefined subroutines at compile time
by CountZero (Bishop) on May 02, 2011 at 18:01 UTC
    Actually this is far less a problem than it seems.

    Whereas a typo in a variable name can introduce subtle and difficult to find bugs, a typo in a subroutine name ALWAYS blows up in a spectacular way as soon as you try to access the non-existing subroutine.

    And yes, it will only happen at run-time, but if you are in the habit of deploying your scripts into production without any tests or even a few private runs on your dev-box, then you probably deserve the lesson your irate user will bring to you.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Detecting undefined subroutines at compile time
by moritz (Cardinal) on May 02, 2011 at 19:35 UTC
Re: Detecting undefined subroutines at compile time
by toolic (Bishop) on May 02, 2011 at 17:39 UTC
      compile(!) time :)

      ~:/tmp$ cat >tst use warnings; use strict; foo(); ~:/tmp$ perl -c tst tst syntax OK

      Cheers Rolf

      It seems like Perl can detect undefined subroutines at compile time..

      Nope, that's still runtime.

      use strict; use warnings; print "hello world\n"; foo();

      Output

      $perl func.pl hello world Undefined subroutine &main::foo called at func.pl line 6.

      I'm not sure how big of a problem this really is. This is why you make test scripts and actually verify your code. But I also wish I knew a solution for this issue as well.

        This is why you make test scripts and actually verify your code.
        I did make a test script and ran it to verify my code, as I clearly demonstrated. My problem was that my test was invalid due to my ignorance in the distinction between compile-time and runtime in this instance (also clearly demonstrated:). I'm here to learn, and I learn by making mistakes (sometimes in public).
      Grasshopper, you can determine undefined subroutines at compile time just as soon as you have plucked the Halting Problem from the palm of my hand:
      no strict; no warnings; no less tricky; foo(lish); his::bar(tab); silly->stuff; come on, please give up; package UNIVERSAL; sub AUTOLOAD { print "I am masquerading as $AUTOLOAD(@_)\n" }
      yields when run:
      I am masquerading as main::foo(lish) I am masquerading as his::bar(tab) I am masquerading as silly::stuff(silly) I am masquerading as on::come(on) I am masquerading as give::please(give up)
        Did he really ask for undefined methods? Those calls are per definition only resolved at runtime.

        Despite manipulating UNIVERSAL in your example, the functions foo() and bar() are still found by B::Xref

        $ perl -MO=Xref,-r tst.pl|grep subused tst.pl (main) 2 main & foo + subused tst.pl (main) 3 his & bar + subused ...

        I agree that every approach can be somehow tricked out, but IMHO the vanilla cases are well covered by B::Xref.

        Cheers Rolf

Re: Detecting undefined subroutines at compile time
by LanX (Saint) on May 02, 2011 at 17:58 UTC
    you can use a perl-parser to find all calls and eval{} them.

    E.g. I have a patch of B::Deparse which extracts all such calls, but this opens new problems since compiling perl involves executing BEGIN{} blocks.

    IIRC there are also moduls in CORE to parse packages like etags and ctags do.¹

    Cheers Rolf

    UPDATES:

    1) I was thinking of B::Xref

    ~:/tmp$ cat tst.pl foo(); sub foo { print } foo(); ~:/tmp$ perl -MO=Xref,-d tst.pl File tst.pl Subroutine (main) Package main &foo &1, &4 tst.pl syntax OK ~:/tmp$

    so you can find all calls, now you just have to check the STASHes in a BEGIN Block.

    HTH!

      In absence of AUTOLOAD, you don't even need to eval the calls. You can just use defined &func to check whether a subroutine was defined. This will supposedly even see through forward declarations of

      sub foo;

      You still need to run the BEGIN blocks of all use statements at least.

        just found that B::Xref lists sub-calls and(!) sub-definitions:
        perl -MO=Xref tst.pl |grep foo tst.pl syntax OK &foo s1 &foo &1, &4 Subroutine foo

        so just needing to parse¹ and compare the output.

        But I agree that the dynamic nature of Perl makes such static parsing look strange.

        Cheers Rolf

        1) especially the raw output could be easily processed:

        $ perl -MO=Xref,-r tst.pl |grep foo tst.pl (definitions) 1 main & foo + subdef tst.pl syntax OK tst.pl (main) 1 main & foo + subused tst.pl (main) 4 main & foo + subused tst.pl foo 3 main $ _ + used $ cat tst.pl foo(); sub foo { print } foo();
Re: Detecting undefined subroutines at compile time
by John M. Dlugosz (Monsignor) on May 02, 2011 at 23:11 UTC
    See Sub::Name for one of your points. You can change the __ANON__ to something you choose.
      Or you could do this:
      local *__ANON__ = "maurice the happy giraffe"
      (or whatever name you want to report) inside the anonymous subroutine.
Re: Detecting undefined subroutines at compile time
by ikegami (Patriarch) on May 04, 2011 at 16:37 UTC

    Perl detects undeclared subs (which is better than detecting undefined subs) at compile-time if you omit parens on sub calls.

    >perl -c -e"use strict; sub f {} f;" -e syntax OK >perl -c -e"use strict; sub f; f;" -e syntax OK >perl -c -e"use strict; f;" Bareword "f" not allowed while "strict subs" in use at -e line 1. -e had compilation errors. >perl -c -e"use strict; sub g {} g 'abc';" -e syntax OK >perl -c -e"use strict; g 'abc';" String found where operator expected at -e line 1, near "g 'abc'" (Do you need to predeclare g?) syntax error at -e line 1, near "g 'abc'" -e had compilation errors.

    Omitting parens on sub calls causes a whole of weird stuff, so I recommend against doing this.

      Omitting parens on sub calls causes a whole of of weird stuff,

      The hard evidence in your post completely contradicts your premature summation.

      I see clear, consise error messages.

        Evidence that it does work at finding undeclared functions does not contradict the claim that omitting parens in function calls introduces problems.

        The main problem is that unary "+" or equivalent must be used in a number of places, and forgetting to do causes bugs. These are not always caught at compile-time, and they can be subtle.

        Another problem is misleading error messages.

        Three issues from omitted parens came up here and/or on stack-overflow in the last month, but I don't remember them.