mr.dunstan has asked for the wisdom of the Perl Monks concerning the following question:

Sorry if this has been written on before; I couldn't find anything to this effect in the perlmonks search.

Is there a way I can check the validity of all the subroutines in my perl program, when some of these subroutines are located in external modules that I am "use"ing?

A coworker complained to me today that every once in awhile, during his extremely long-running perl program, that he still gets "undefined subroutines" every once in awhile, and was wondering if there was a way to detect these at compile time.

Any assistance would be much appreciated!

-mr.dunstan
  • Comment on Compile-time checking for undef subroutines?

Replies are listed 'Best First'.
Re: Compile-time checking for undef subroutines?
by broquaint (Abbot) on May 05, 2003 at 23:03 UTC
    Is there a way I can check the validity of all the subroutines in my perl program
    Unfortunately no. Because subroutines can be called and created dynamically there's no way of telling at compile time whether every subroutine has been defined (which is potentially any subroutine). You could possibly do some trickery with the B:: modules to get some way towards your goal (i.e if you assume all subroutines and subroutines calls are static). And if you're dealing with methods then it'd be difficult, if not impossible, to determine whether every method exists due, in no small part, to inheritance
    HTH

    _________
    broquaint

      Well, there's a bit more to that. Remember that in Perl you can call subroutines without using parenthesis. The compiler will only allow that if it knows about such a subroutine - the compiler must have seen either a definition or a declaration of the subroutine; that is, there must be an entry in the namespace stash. This means that when the compiler encounters something that looks like a subroutine (bareword that was declared defined as a sub, bareword followed by parenthesis or a bareword preceeded by &), it knows whether it's at that moment defined or declared.

      The reason you state, it can be created at runtime, is the reason the compiler doesn't balk, but postpones it to the runtime environment.

      So, the answer is, "yes, the compiler knows, and no, that information isn't available, but if it were, it wouldn't be half as useful as you might think - which is also the reason why it isn't available".

      Abigail

Re: Compile-time checking for undef subroutines?
by Util (Priest) on May 06, 2003 at 04:01 UTC

    In mr.dunstan's particular case, perhaps "half as useful" is just enough.

    If I were creating a tool to find undefined subs, and that tool was only to be used by myself and other Perl programmers within my (hypothetical) company, I could tune it to our site standards. We know which directories hold our in-house modules, and what AUTOLOADs those modules do. I would design for zero false negatives, and then tweak to reduce the false positives. I think the result would be useful, though imperfect.

    I have included a working example below. It uses B::Xref to get a listing of subroutines defined or used in the analysed program, including any modules used. Simply listing the subroutines used, but not defined, would clutter the list with runtime-defined subroutines from modules in the Perl library. I get around this by populating %known with the full pathnames of all the modules in @INC, except for those in the current directory, and skipping over the 'subused' lines from those files. You could add another hash to skip over known AUTOLOADed subs from your own modules.

    This is a proof-of-concept; much more could be done with a little more code. This version does not even tell you where the undefined subs occurred. It is lightweight enough to be cloned and hacked for multiple projects without creating maintenance issues.

    test.pl
    use MightyMod; use XML::Simple; my $y = BadSub(0); my $z = GoodSub(0); print "$y $z";
    MightyMod.pm
    package MightyMod; use strict; require Exporter; our @EXPORT = qw(GoodSub); our @ISA = qw(Exporter); sub GoodSub { return 42 + LostSub(@_); } 1;
    Find_Undefined_Subroutines.pl
    #!/usr/bin/perl -w use strict; use File::Find; my %known; find( sub{$known{$File::Find::name}=1 if /\.pm$/}, grep {$_ ne '.'} @INC ); my $pgm = shift or die; my @xref = `perl -MO=Xref,-r $pgm 2>/dev/null` or die; # Xref data looks like this: # test.pl (definitions) 5 main & GoodSub subdef # test.pl (main) 4 (lexical) $ y intro # test.pl (main) 4 main & BadSub subused my %h; foreach (@xref) { my ($file, $where, $line, $package, $sym, $name, $how) = split; next if $how eq 'subused' and $known{$file}; $h{$how}{$name}++; } foreach (sort keys %{$h{subused}}) { print "$_\n" unless $h{subdef}{$_}; }
    Output:
    $ ./Find_Undefined_Subroutines.pl test.pl BadSub LostSub

    By the way, B::Lint has an option, undefined-subs, which is supposed to do something similar to my code, but I could not make it find any errors. Perhaps it is because all my Perl interpreters are threaded; B::Lint's documentation says 'This module doesn't work correctly on thread-enabled perls'.

Re: Compile-time checking for undef subroutines?
by halley (Prior) on May 05, 2003 at 23:59 UTC

    There is a check-at-runtime feature in the UNIVERSAL namespace called can. You can ask a blessed object if it can(domethod), for example, which has slight benefits over asking the object if it isa(MyClass).

    But this won't get you very far to checking your code for correctness, which generally needs to happen at compiletime. This can be difficult for the reasons the other folks have already explained: in Perl, compile time is anytime.

    --
    [ e d @ h a l l e y . c c ]

      This can be difficult for the reasons the other folks have already explained: in Perl, compile time is anytime.

      It's not difficult - otherwise we wouldn't have the "used only once" warning. It's not useful because of AUTOLOAD, and not having to declare subroutines. It's the usefulness which makes there's no warning, not whether it's difficult.

      Abigail

Re: Compile-time checking for undef subroutines?
by crouchingpenguin (Priest) on May 06, 2003 at 03:49 UTC

    Maybe not detect, but you can predeclare like you would a variable using the subs pragma.

    use subs qw( foo bar baz );


    cp
    ----
    "Never be afraid to try something new. Remember, amateurs built the ark. Professionals built the Titanic."

Re: Compile-time checking for undef subroutines?
by dragonchild (Archbishop) on May 06, 2003 at 14:13 UTC
    This kind of question indicates a serious lack of coding quality at the site. The best way to deal with this is to inspect your code. If your code is large, make it smaller. If it's messy, make it clean. If it's ... you get the idea. Who knows, maybe you'll find the solution to those other 47 bugs that have been annoying you, too.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      There's More Than One Way To Do It.The problem might be in the local development culture, which is not so easily changed.

      For example, at our office, coding standards enforcement was so lax it had to be automated with a Perl script that reads the code and e-mails the programmer if any of our rules are broken.

      This may be an environment where they can't enforce a particular style. The alternative might be to disallow use of some language features, in this case, dynamically created subs. If all the programmers can agree not to do that, a script could be written (probably using Parse::RecDescent and the Perl BNF) to accumulate declared subs and used subs, and list anything used but not declared.

      In this case, it might be easier to give the programmer a tool to solve the problem than to change development style.
      --
      Spring: Forces, Coiled Again!

        If all the programmers can agree not to do that, ...

        However, that seems to be the problem, doesn't it? I mean, the programmers are (seemingly) refusing to agree on much of anything!

        For example, at our office, coding standards enforcement was so lax it had to be automated with a Perl script that reads the code and e-mails the programmer if any of our rules are broken.

        Far be it from me to pontificate, but unless the development culture is updated, the root cause will persist and this exercise will have to be done again, and again, and again, and ... ad nauseum. Additionally, the developers will become lazy and depend on automated tools to do their development for them. Now, tools aren't a bad thing, but they are less than ... optimal ... when they substitute for developer care.

        Now, I wouldn't care at all if those developers would only ever work in shops I won't be working in. But, that isn't a guarantee, is it? I most definitely do NOT want to work with a developer who is so full of themselves that they will obstinately do their thing, even if it means that their coworker's efforts are stomped upon. I find developers like that to be worse than no developer at all.

        Furthermore, lax standards enforcement indicate that there's lax ... other enforcement, too. For example, if you hvae lax standards enforcement, you probably have lax requirements analysis, lax design walkthroughs, and lax testing efforts. Sounds like you have a lax product, too!

        ------
        We are the carpenters and bricklayers of the Information Age.

        Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.