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
| [reply] |
|
|
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
| [reply] |
|
|
| [reply] |
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'.
| [reply] [d/l] [select] |
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 ]
| [reply] [d/l] [select] |
|
|
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
| [reply] |
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."
| [reply] [d/l] |
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. | [reply] |
|
|
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!
| [reply] |
|
|
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.
| [reply] |