Verbose warnings

S

Seebs

Is there any way to find out WHICH variable is uninitialized, short of
breaking a line up into bunches of single lines?

I inherited a HUGE script full of lines that look roughly like

$value = "$value1$value2$value3<a href="$value4"
fgcolor="$value5">$value6</a><font size="$value7">..."
which are trying to build HTML from *HORDES* of values.

I probably have, say, five hundred of these little buggers to catch.

I would be *deleriously* happy to discover a warning switch that would
cause perl to say "Use of uninitialized value (variable $value3)" or
something similar.

Perl is 5.6.1; I could probably use 5.8 without much trouble, but I don't
want to switch right now if I don't have to.

-s
 
S

Steve Grazzini

Seebs said:
Is there any way to find out WHICH variable is uninitialized, short of
breaking a line up into bunches of single lines?

I don't think so. The warning should say "Undefined value", and
not all values (scalars) are named variables:

% perl -we 'print undef'
Use of uninitialized value in print at -e line 1

Probably the best thing to do: validate those variables when you
set them...
 
P

Pedro

Steve said:
I don't think so. The warning should say "Undefined value", and
not all values (scalars) are named variables:

% perl -we 'print undef'
Use of uninitialized value in print at -e line 1

Using perl 5.8.1
$ perl -we 'print $xyz;'

Name "main::xyz" used only once: possible typo at -e line 1.
Use of uninitialized value in print at -e line 1.
 
T

Tad McClellan

Seebs said:
Is there any way to find out WHICH variable is uninitialized, short of
breaking a line up into bunches of single lines?


Yes, if they are package (dynamic) variables.

Are they package or lexical variables?
 
T

Tassilo v. Parseval

Also sprach Tad McClellan:
Yes, if they are package (dynamic) variables.

How so?

ethan@ethan:~$ perl -w
use vars qw/$c $d $e $f/;
$e = 1;
print "$c;$d;$e;$f\n";
__END__
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
;;1;

versus

ethan@ethan:~$ perl -w
my ($c,$d,$e,$f);
$e = 1;
print "$c;$d;$e;$f\n";
__END__
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
;;1;

At least up to (and including) 5.8.1, the behaviour seems to be
independent from using lexicals or dynamic variables.

Tassilo
 
S

Steve Grazzini

Pedro said:
Using perl 5.8.1
$ perl -we 'print $xyz;'

Name "main::xyz" used only once: possible typo at -e line 1.
Use of uninitialized value in print at -e line 1.

Those are two unrelated (and ancient -- nothing new in 5.8.1) warnings.
The OP wanted the "uninitialized" warning to include the variable name,
and that's what I don't think is supported.
 
M

Malcolm Dew-Jones

Seebs ([email protected]) wrote:
: Is there any way to find out WHICH variable is uninitialized, short of
: breaking a line up into bunches of single lines?

: I inherited a HUGE script full of lines that look roughly like

: $value = "$value1$value2$value3<a href="$value4"
: fgcolor="$value5">$value6</a><font size="$value7">..."
: which are trying to build HTML from *HORDES* of values.

: I probably have, say, five hundred of these little buggers to catch.

: I would be *deleriously* happy to discover a warning switch that would
: cause perl to say "Use of uninitialized value (variable $value3)" or
: something similar.

: Perl is 5.6.1; I could probably use 5.8 without much trouble, but I don't
: want to switch right now if I don't have to.

perl -MO=Deparse,-q

might reproduce your program in a format where it would be easier to
automate the adding of line breaks etc.

k
 
S

Seebs

Yes, if they are package (dynamic) variables.
Are they package or lexical variables?

Package variables. Which are, currently, set and reused across two different
pieces of code, one of which is incorporated with a 'require' statement.
(No, it's not a module; it's just a chunk of code.)

-s
 
E

Eric J. Roode

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

(e-mail address removed) (Seebs) wrote in @news.plethora.net:
Is there any way to find out WHICH variable is uninitialized, short of
breaking a line up into bunches of single lines?

I inherited a HUGE script full of lines that look roughly like

$value = "$value1$value2$value3<a href="$value4"
fgcolor="$value5">$value6</a><font size="$value7">..."
which are trying to build HTML from *HORDES* of values.

I probably have, say, five hundred of these little buggers to catch.

500 statements? or 500 variables?

The solution that occurs to me is to tie each variable to a class that
gives more information when its (undefined) value is fetched. Something
like:

package MoreWarnings;
use Carp;

sub TIESCALAR
{
my ($class, $name) = shift;
my ($file, $line, $pkg) = caller;
bless { name => $name,
value=> undef,
file => $file,
line => $line,
pkg => $pkg }, $class;
}

sub STORE
{
my $self = shift;
$self->{value} = shift;
}

sub FETCH
{
my $self;
if (!defined $self->{value})
{
carp "Variable $self->{name} undefined";
# optionally add information about where it was tied
}
return $self->{value};
}


And then every place there was a

my $foo;

replace it with

tie my $foo, 'MoreWarnings', 'foo';

- --
Eric
$_ = reverse sort $ /. r , qw p ekca lre uJ reh
ts p , map $ _. $ " , qw e p h tona e and print

-----BEGIN PGP SIGNATURE-----
Version: PGPfreeware 7.0.3 for non-commercial use <http://www.pgp.com>

iQA/AwUBP6cELmPeouIeTNHoEQKiYgCcCs1wWqQhCfUt8lEObMaS9CAx0fUAoOnX
qbsRa9SKhZbGja0zf9cz1/ui
=mk7O
-----END PGP SIGNATURE-----
 
T

Tad McClellan

Tassilo v. Parseval said:
Also sprach Tad McClellan:


How so?


I think you were thinking that I was answering the question
that I trimmed, rather than the question that I quoted?

I trimmed the other one because its answer wasn't interesting. :)

ethan@ethan:~$ perl -w
use vars qw/$c $d $e $f/;
$e = 1;
print "$c;$d;$e;$f\n";
__END__
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
Use of uninitialized value in concatenation (.) or string at - line 3.
;;1;

At least up to (and including) 5.8.1, the behaviour seems to be
independent from using lexicals or dynamic variables.


Yes, but if they are dynamic variables, then we can use symrefs
and stay "short of breaking a line up into bunches of single lines".


[tadmc@temp]$ perl -w
use vars qw/$c $d $e $f/;
$e = 1;
print "\$$_ is undef\n" for grep !defined $$_, qw/ c d e f /;
__END__
$c is undef
$d is undef
$f is undef


:)
 
B

Bob Walton

Seebs said:
Is there any way to find out WHICH variable is uninitialized, short of
breaking a line up into bunches of single lines?

I inherited a HUGE script full of lines that look roughly like

$value = "$value1$value2$value3<a href="$value4"
fgcolor="$value5">$value6</a><font size="$value7">..."
which are trying to build HTML from *HORDES* of values.

I probably have, say, five hundred of these little buggers to catch.

I would be *deleriously* happy to discover a warning switch that would
cause perl to say "Use of uninitialized value (variable $value3)" or
something similar.

Perl is 5.6.1; I could probably use 5.8 without much trouble, but I don't
want to switch right now if I don't have to.

-s

You don't say if your variables are package variables or lexicals. If
they are all package variables, you could peruse the symbol table, maybe
something like:

for(sort keys %main::){
no strict 'refs'; #probably not needed if package variables :-(
print "$_ is uninitialized\n" unless defined $$_;
}

You will have to skip the extraneous stuff in the listing which isn't
your variables, such as all the builtin variables that default to undef,
filehandles, etc, but once those are ignored, it should give you a
listing of your uninitialized scalar variables (but it will include any
that were initialized to the undef value :)). It won't give you any
lexicals, though. Or any anonymous scalars.

If your variable names are as structured as your example shows, you
could test for names like that in the above loop, like:

next unless /^value\d+$/;

ahead of the print.

If your variables are all lexicals, you could temporarily comment the
"my" or "our" definitions and the use strict;, and do the same thing.

HTH.
 
S

Seebs

And then every place there was a

my $foo;

replace it with

tie my $foo, 'MoreWarnings', 'foo';

Oh, if there were declarations for the variables, I'd be set.

The problem is that they're set from a variety of sources, ranging from
evals of configuration files, to other CGI scripts, and so on...

Actually, it turns out that the biggest culprit was a couple of loops that
did
$output .= "$a[$x]";
in a loop to about 10 more than there were elements of a... "harmless", but
it cluttered up my log files something fierce.

I promise you that any perl code I ever try to deliver, to anyone, whether
commercial or off-the-cuff, will be built and tested with "perl -w".

I think I've done a good 20 hours of work on this program just trying to get
the undefined variable references cleaned up so I can read the rest of the
warnings and errors.

-s
 
S

Seebs

You don't say if your variables are package variables or lexicals. If
they are all package variables, you could peruse the symbol table, maybe
something like:

Yup.

But what I really *want* is an option to have perl emit any kind of
information at all about what the expression that yielded undef *looked*
like. I guess, my goal was, if possible, to find a way to do this that
could be used on arbitrary new code, preferably by sending perl a flag,
rather than modifying the code.

I recognize that it's not always a variable, but it seems like it would
be cheap to, when producing that warning, see whether it *is* a variable.

-s
 
S

Sam Holden

Yup.

But what I really *want* is an option to have perl emit any kind of
information at all about what the expression that yielded undef *looked*
like. I guess, my goal was, if possible, to find a way to do this that
could be used on arbitrary new code, preferably by sending perl a flag,
rather than modifying the code.

I recognize that it's not always a variable, but it seems like it would
be cheap to, when producing that warning, see whether it *is* a variable.

The debugger will at least allow you to break at the line indicated by
a warning and examine the state of the variables used in the expression.

I'm not sure if there is a standard way of getting it to act as
if it hit a breakpoint on a warning, but if you know the line number
you can break at that line and check the variables used before the
warning occurs.

If it's in a loop then you could do something like:

---test.pl---
my ($x,$y) = (1);
print "$x\n";
print "$y\n";
print "$x\n";
---test.pl---

; perl -w test.pl
1
Use of uninitialized value in concatenation (.) or string at test.pl line 3.

1
; perl -dw test.pl
; perl -dw test.pl

Loading DB routines from perl5db.pl version 1.22
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

main::(test.pl:1): my ($x,$y) = (1);
DB<1> $SIG{__WARN__} = sub {$::WARNING = 1;warn @_}

DB<2> b 4 $::WARNING
DB<3> c
1
Use of uninitialized value in concatenation (.) or string at test.pl line 3.

main::(test.pl:4): print "$x\n";
DB<4> v 3
1: my ($x,$y) = (1);
2: print "$x\n";
3: print "$y\n";
4==>b print "$x\n";
5
DB<5> x $y
0 undef

It's annoying to have to break after the line causing the warning, but I
don't know the debugger well enough to now if you can make it break
after a warning.
 
T

Tassilo v. Parseval

Also sprach Tad McClellan:
I think you were thinking that I was answering the question
that I trimmed, rather than the question that I quoted?

Guilty as charged, I still had the original posting as a whole in my
short-term memory.
Yes, but if they are dynamic variables, then we can use symrefs
and stay "short of breaking a line up into bunches of single lines".

Oh. The chance that you'd suggest symrefs is so marginal that I never
even considered it. :) Kind of explains why you didn't provide the code
in the first place.

Tassilo
 
T

Tad McClellan

Tassilo v. Parseval said:
The chance that you'd suggest symrefs is so marginal that I never
even considered it. :) Kind of explains why you didn't provide the code
in the first place.


I knew it would make me feel dirty, and wanted to avoid that if possible.
 
T

Tassilo v. Parseval

Also sprach Eric J. Roode:
(e-mail address removed) (Seebs) wrote in @news.plethora.net:


500 statements? or 500 variables?

The solution that occurs to me is to tie each variable to a class that
gives more information when its (undefined) value is fetched. Something
like:

[...]

For many variables that will require quite a bit of changes to the code.
I was just experimenting with changing the value of PL_sv_undef and
indeed, this seems to be possible using XS. It's a little dangerous
and flaky really, but at least temporarily it can be done:

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"

SV old;
int defined;

MODULE = undef PACKAGE = undef

void
restore_undef()
CODE:
if (defined) {
memcpy(&PL_sv_undef, &old, sizeof(old));
defined = 0;
}

void
define_undef()
CODE:
/* save original undef */
memcpy(&old, &PL_sv_undef, sizeof(PL_sv_undef));

SvREADONLY_off(&PL_sv_undef);
SvUPGRADE(&PL_sv_undef, SVt_PV);
sv_setpvn(&PL_sv_undef, "<undef>", 7);

defined = 1;


ethan@ethan:/tmp/undef$ perl -w -Mblib -Mundef
undef::define_undef();
print "*", undef, "\n";
undef::restore_undef();
print "*", undef, "\n";
__END__
*<undef>
Use of uninitialized value in print at - line 4.
*

This doesn't quite solve the problem of getting the name of the variable
that holds an undefined value. But it makes the undefinedness visible
and only requires minimal additions to the code.

Tassilo
 
G

Greg Bacon

: Is there any way to find out WHICH variable is uninitialized, short of
: breaking a line up into bunches of single lines?
: [...]

Here's a start:

$ cat Devel/wundef.pm
package Devel::wundef;

BEGIN {
$SIG{__WARN__} = \&wundef;

open STDOUT, ">/dev/null" or die "$0: open /dev/null: $!\n";
}

use warnings;
use strict;

use Data::Dumper;

my %cache;
my %seen;

sub grabline {
my $path = shift;
my $num = shift;

die "$0: bad line ($num)" if $num <= 0;

return $cache{$path}->[$num-1] if $cache{$path};

open my $fh, "<", $path or die "$0: open $path: $!\n";
local $/ = "\n";
$cache{$path} = [ <$fh> ];

return $cache{$path}->[$num-1];
}

sub wundef {
my $warning = shift;
return unless $warning =~ /^Use of uninitialized value/;

my($pkg,$file,$line) = caller;
return unless $seen{$pkg}{$file}{$line}++;

print STDERR $warning;
local $_ = grabline $file, $line;
while (/\$((\w+::)*\w+)/g) {
my $var = $1;

if ($var =~ /::/) {
$var = $var;
}
else {
$var = $pkg . '::' . $var;
}

#warn "var = [$var]\n";
no strict 'vars';
print STDERR "$var undefined\n" unless defined $$var;
}
}

package DB;

use Data::Dumper;

sub DB {
# print "DB::DB called\n";
}

1;

$ cat try
#! /usr/local/bin/perl -w

$abc = 1;
$klm::nop = 2;

print $abc, $def, $efg::hij, $klm::nop;

$ perl -d:wundef try
Use of uninitialized value in print at try line 6.
main::def undefined
efg::hij undefined

Hope this helps,
Greg
 
R

Richard Voss

Greg said:
: Is there any way to find out WHICH variable is uninitialized, short of
: breaking a line up into bunches of single lines?
: [...]

Here's a start:

that will only work with package variables, not with lexicals.

I recommend the debugger.
 
L

Louis Erickson

: Greg Bacon wrote:
:> In article <[email protected]>,
:>
:> : Is there any way to find out WHICH variable is uninitialized, short of
:> : breaking a line up into bunches of single lines?
:> : [...]
:>
:> Here's a start:

: that will only work with package variables, not with lexicals.

: I recommend the debugger.

Couldn't they just put:

no warnings qw(uninitialized);

.... around the flaky section of code?

They can undo that with:

use warnings qw(uninitialized);

Or am I missing something really simple here?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,586
Members
45,096
Latest member
ThurmanCre

Latest Threads

Top