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

A looonnnggg time ago somebody noted this
well I just wasted an hour with the same, but I can add that it works OK on our Solaris 5.005, but not RH 5.6.0 or RH 5.6.1 - no idea on 5.8 (anyone?)
#!/usr/bin/perl -w use strict; my $string = "ABC"; my $value = undef; if ($string =~ qr(DEF)) { # warning reported here } elsif ($string =~ qr(GHI)) { } elsif ($value/1000 ne $value) { # warning actually here } else { }

Use of uninitialized value in division (/) at E:\src\perl\ line 5.

Use of uninitialized value in string ne at E:\src\perl\ line 5.

now, apart from long chains of if-elsif's being a pain and possibly a sign of poor design or implementation, we all know perfectly well that we all do it all the time - so is there a way to 'nudge' perl into reporting the correct line in the warning? Or would I be better off using some switch/case variant from now on?

Replies are listed 'Best First'.
Re: if-elsif weirdness
by hossman (Prior) on May 21, 2003 at 07:03 UTC

    Assuming I understand your question, you're looking for a generic "Best Practices" rule you can apply to using "if-elsif" to ensure that *if* you have a warning (or die)in the "EXPR" of (one of) the elsif, *then* the line number reported for that warning (or death) will be accurate.

    Is that correct?

    Assuming youre requirement is to keep using if-elsif, then i don't think there's anything you can do -- short of waiting for a new version of perl that does a better job of reporting the line number.

    One thing you can do, is replace all occurences of this...

    elsif (EXPR) BLOCK
    ...with this...
    else { if (EXPR) BLOCK }

    Using the test case from Node #42078 as an base for an example, use this instead...

    #!/usr/bin/perl -w $def = ''; undef $notdef; if($def eq 'foo') { # Line 4 } else { if ($def eq 'bar') { } else { if ($notdef eq "RHS") { # Line 7 } } }
    That should accurately cite the warning on Line 7.

    Personally, I don't think it's really that big a deal .. just make sure you keep in mind this problem for the future, and if you ever notice any warnings/deaths cited with a line number of some "if (EXPR)" code, then skim down and see if there's an "elsif" that might be the real culprit.

Re: if-elsif weirdness
by Zaxo (Archbishop) on May 21, 2003 at 05:12 UTC

    To update this to a newer version of perl, you should use warnings;. With that, you can turn off particular warnings with no warnings qw( uninitialized ); in this case. The old -w switch turns on all warnings.

    Your code has a serious deficiency in that division places the left side of the string comparison in numeric context, and is then stringified to '0' by the ne operator. The right side, being undef, stringifies to an empty string. The comparison cannot succeed unless $value equals '0'.

    On line numbering, you can modify perl's notion of __LINE__ and __FILE__ with the #line nnn ffff.ext directive. (Added) It follows C rules for preprocessor directives. Here's a command line example of its use:

    $ perl -e' > #line 42 bogus > die "As expected"' As expected at bogus line 42. $

    After Compline,

      well I want to know that a value I expect to be initialised is undefined, so turning off this specific warning wont happen.

      But much more importantly, and even of concern to you, Zaxo, is I want to know where it is happening - the sample code is specifically designed(!) to provoke the error that the wrong line number is reported - the fact that the expression's are bogus is unimportant. Imagine how much time you, Zaxo, would waste if you looked for a warning reported on line 100, when the real cause is line 400. Or that the warning is reported correctly on one platform, but is misreported on another - is it one warning?two different ones?

      I want information from the monks about the warning being reported on the incorrect line, not that the code to provoke the error is deficient in some unrelated way.


Re: if-elsif weirdness
by graff (Chancellor) on May 21, 2003 at 05:32 UTC
    I can confirm that perl 5.8 has exactly the problem you state in terms of citing the "wrong" line number -- on both solaris and (suse) linux. But I haven't found a way to make it do the "right" thing (not even with "# line N" comments, described in perlsyn) -- however the perlsyn man page does have a fair bit to say about using "SWITCH:" and "use Switch;"...

    update: Here's a version where I tried (and failed) to get correct line numbers reported for the error:

    #!/usr/bin/perl -w use strict; my $string = "ABC"; my $value = undef; if ($string =~ qr(DEF)) { # warning reported here print "foo"; } # line 8 elsif ($string =~ qr(GHI)) { print "bar"; } # line 11 elsif ($value/1000 ne $value) { # warning actually here print "baz"; } else { }
    The line with "$value/1000" is still reported by perl 5.8 as "line 5" (where the "if()" block begins). If I put "#line 11" above the start of the "if()" block, then perl picks it up, and presumably any error in any of the conditions would be reported as happening at line 11. That's not much help. (I haven't checked to see whether line-number reporting would work any better with a "switch"-style block -- I guess that might not be a sure thing...)
Re: if-elsif weirdness
by Abigail-II (Bishop) on May 21, 2003 at 12:51 UTC
    This is a well-known bug in Perl, that has been around for ages. Fixing it probably involves diving into the tokenizer, a part of perl only a few people dare to go into.


Re: if-elsif weirdness
by japhy (Canon) on May 21, 2003 at 15:36 UTC
    Internally speaking, the problem is because only "control ops" (Cop's) have a line number recorded with them. To Perl, an if-elsif-else sequence is one big logical operator, and that statement starts on a specific line. If there's a problem evaluating something in the operator itself, the line number the statement starts on is used. That's why
    if ($x) { print 1; } elsif ($x != 2) { print $y; }
    will warn that there's an uninitialized value at line 1 (even though it's at line 4), but it will properly tell you about the uninitialized value on line 5 (because each new statement in Perl is a new Cop).

    A similar problem is found in this case:

    print 1, 2, 3, undef, 4;
    Even though the code spans five lines, error messages will refer to the line the statement started on.

    It's not going to be easy to fix this bug, methinks.

    Update: but a good kludgy work-around is to use a do { ; ... } block!

    if ($x) { ... } elsif (do {; $x == 2}) { ... }
    That properly reports the warning for line 2. It's super-kludge, though.

    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;