Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Perl provide a misleading line number in this warning message?

Tags:

perl

I have isolated a case where Perl provides a very misleading line number in a warning message. I tested the below in Strawberry Perl 5.16.3.

use strict;
use warnings;

my $choice = 0;

while ($choice == 0){

    #This is not numeric
    $choice = '5,6,7';

    if ($choice eq '-4'){
        print "This isn't going to happen\n";
    }
}

When you run this, you will get the warning message Argument "5,6,7" isn't numeric in numeric eq (==) at example.pl line 11. But line 11 corresponds to the line if ($choice eq '-4'){ which cannot possibly cause this warning message because it does not contain a numeric comparison.

It seems what's actually happening is that Perl advances to the next comparison, while ($choice == 0){, but the line counter used for the warning message does not advance.

What makes this particular case worse is that, since the "bad" comparison is the loop condition, it is actually far away from the provided line. In my (pre-simplification) script, it was hundreds of lines away from the provided line number.

Is this a bug or just an unfortunate limitation of the parser?

like image 767
Stephen Avatar asked Jun 01 '18 21:06

Stephen


2 Answers

It would be expensive to store the location of each operator instance. As a compromise, Perl only tracks the location of statements. It does so by adding a location-setting opcode at the start of every statement. The if statement is the last statement to be started before $choice == 0 is performed, so the warning is reported as coming from that line.

$ perl -MO=Concise,-exec a.pl
1  <0> enter
2  <;> nextstate(main 3 a.pl:4) v:*,&,{,x*,x&,x$,$
3  <$> const[IV 0] s
4  <0> padsv[$choice:3,12] sRM*/LVINTRO
5  <2> sassign vKS/2
6  <;> nextstate(main 4 a.pl:6) v:*,&,{,x*,x&,x$,$           <--- Location set to line 6
7  <{> enterloop(next->k last->p redo->8) v                  <--- Start of while loop
l  <0> padsv[$choice:3,12] s                                 <--- $choice == 0
m  <$> const[IV 0] s
n  <2> eq sK/2
o  <|> and(other->8) vK/1
8      <;> nextstate(main 6 a.pl:9) v:*,&,x*,x&,x$,$
9      <$> const[PV "5,6,7"] s
a      <0> padsv[$choice:3,12] sRM*
b      <2> sassign vKS/2
c      <;> nextstate(main 6 a.pl:11) v:*,&,x*,x&,x$,$        <--- Location set to line 11
d      <0> padsv[$choice:3,12] s                             <--- Start of if statement
e      <$> const[PV "-4"] s
f      <2> seq sK/2
g      <|> and(other->h) vK/1
h          <0> pushmark s
i          <$> const[PV "This isn't going to happen\n"] s
j          <@> print vK
k      <0> unstack v
           goto l                                            <--- Jump back to loop expr
p  <2> leaveloop vKP/2
q  <@> leave[1 ref] vKP/REFC
a.pl syntax OK

This is a known limitation. I don't know why they don't simply put a nextstate op in the loop expression.

like image 56
ikegami Avatar answered Nov 03 '22 19:11

ikegami


Apparently, starting with Perl 5.008, the interpreter began omitting an opcode that let the interpreter know the current line number.

Using the minimalist script:

while ($choice == 0) {
    $choice = '5,6,7';
}

We get this output from Perl 5.6 and B::Concise:

$ perl506 -MO=Concise badwarnline.pl
f  <@> leave[t1] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 badwarnline.pl:1) v ->3
e     <2> leaveloop vK/2 ->f
3        <{> enterloop(next->8 last->e redo->4) v ->a
-        <1> null vK/1 ->e
d           <|> and(other->4) vK/1 ->e
c              <2> eq sK/2 ->d
-                 <1> ex-rv2sv sK/1 ->b
a                    <$> gvsv(*choice) s ->b
b                 <$> const(IV 0) s ->c
-              <@> lineseq vKP ->-
4                 <;> nextstate(main 1 badwarnline.pl:2) v ->5
7                 <2> sassign vKS/2 ->8
5                    <$> const(PV "5,6,7") s ->6
-                    <1> ex-rv2sv sKRM*/1 ->7
6                       <$> gvsv(*choice) s ->7
8                 <0> unstack v ->9
9                 <;> nextstate(main 2 badwarnline.pl:1) v ->a

and this output from Perl v5.008:

$ perl508 -MO=Concise badwarnline.pl
e  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 badwarnline.pl:1) v ->3
d     <2> leaveloop vK/2 ->e
3        <{> enterloop(next->8 last->d redo->4) v ->9
-        <1> null vK/1 ->d
c           <|> and(other->4) vK/1 ->d
b              <2> eq sK/2 ->c
-                 <1> ex-rv2sv sK/1 ->a
9                    <#> gvsv[*choice] s ->a
a                 <$> const[IV 0] s ->b
-              <@> lineseq vKP ->-
4                 <;> nextstate(main 1 badwarnline.pl:2) v ->5
7                 <2> sassign vKS/2 ->8
5                    <$> const[PV "5,6,7"] s ->6
-                    <1> ex-rv2sv sKRM*/1 ->7
6                       <#> gvsv[*choice] s ->7
8                 <0> unstack v ->9

The main difference between these outputs is the last line produced by 5.006:

9                 <;> nextstate(main 2 badwarnline.pl:1) v ->a

which is omitted in the 5.008 output.

like image 25
mob Avatar answered Nov 03 '22 17:11

mob