为什么Perl在此警告消息中提供了误导性的行号?

时间:2018-06-01 21:43:46

标签: perl

我已经分离了一个案例,其中Perl在警告消息中提供了非常误导性的行号。我在Strawberry Perl 5.16.3中测试了以下内容。

Argument "5,6,7" isn't numeric in numeric eq (==) at example.pl line 11

运行此操作时,您将收到警告消息if ($choice eq '-4'){。但第11行对应的行while ($choice == 0){不能引起此警告消息,因为它不包含数字比较。

似乎实际正在发生的事情是Perl前进到下一个比较<div class="row"> <div class="col-sm-12"> <div class="table-responsive"> <table st-table="appointments" class="table table-striped table-appointments"> <thead> <tr> <th>Family Id</th> <th>Survey Name</th> <th>Appointment Date</th> <th>Start Time</th> <th>Progress</th> <th></th> </tr> </thead> <tbody> <tr ng-repeat="appointment in $ctrl.appointments"> <td class="appointments-familyid">{{appointment.familyIdName}}</td> <td class="appointments-survey">{{appointment.surveyName}}</td> <td class="appointments-date">{{appointment.date | date:'MMMM dd, yyyy'}}</td> <td class="appointments-starttime">{{appointment.startTime | date:'hh:mm a'}}</td> <td class="appointments-progress">{{appointment.progress}}</td> <td class="appointments-begin"> <button type="button" ng-click="$ctrl.startSurvey(appointment)" class="btn btn-sm btn-success"> Begin Survey <i class="glyphicon glyphicon-circle-arrow-right"> </i> </button> </td> </tr> </tbody> </table> </div> </div> </div> ,但用于警告消息的行计数器不会前进。

这个特殊情况更糟糕的是,因为&#34;坏&#34;比较是循环条件,它实际上远离提供的线。在我的(预简化)脚本中,它距离提供的行号数百行。

这是一个错误还是解析器的一个不幸的限制?

3 个答案:

答案 0 :(得分:17)

存储每个运算符实例的位置会很昂贵。作为妥协,Perl只跟踪语句的位置。它通过在每个语句的开头添加位置设置操作码来实现。 if语句是在执行$choice == 0之前要启动的最后一个语句,因此报告的警告来自该行。

$ 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

这是一个已知的限制。我不知道他们为什么不简单地在循环表达式中添加nextstate op。

答案 1 :(得分:12)

显然,从Perl 5.008开始,解释器开始省略一个操作码,让解释器知道当前的行号。

使用极简主义脚本:

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

我们从Perl 5.6和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

和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

这些输出之间的主要区别是5.006产生的最后一行:

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

在5.008输出中省略。

答案 2 :(得分:0)

如果有人愿意解决这个问题,这不是一个解决问题的途径。

  

存储每个运算符实例的位置会很昂贵。作为妥协,

我在运行时脱机时所做的最近工作减少了在树中添加其他位置的需要。

对于这个带有Devel::Trepan::Deparse的示例,您可以获得:

(trepanpl): disasm
Package Main
------------
    #4: my $choice = 0;
        COP (0x195a0b0) dbstate 
        BINOP (0x195a110) sassign 
=>          SVOP (0x195a158) const  IV (0x1953f68) 0 
            OP (0x195a198) padsv [1] 
    #6: while ($choice == 0){
        COP (0x1b5b070) dbstate 
        BINOP (0x1b5b0d0) leaveloop 
            LOOP (0x1b5b118) enterloop 
            UNOP (0x1b5b178) null 
                LOGOP (0x1b5b1b8) and 
                    BINOP (0x195a850) eq <<< This is where the error is
                        OP (0x195a000) padsv [1] 
                        SVOP (0x195a898) const  IV (0x1953fb0) 0 
                    LISTOP (0x195a6e8) lineseq 
    #9:     $choice = '5,6,7';
            COP (0x2459530) dbstate 
            BINOP (0x2459590) sassign 
                SVOP (0x24595d8) const  PV (0x2da5ae0) "5,6,7" 
                OP (0x21e8618) padsv [1] 
... 
(trepanpl): deparse 0x195a850
binary operator ==, eq    
while ($choice == 0)  {
       ------------

如果你输入其他补偿,你会得到其他选择。例如

(trepanpl): deparse 0x195a000
not my, padsv 
while ($choice == 0)  {
       -------
(trepanpl): deparse 0x21e8618
not my, padsv 
$choice = '5,6,7';
-------

请注意,您不仅可以获取该行的文本,还可以获取该行中存在问题的位置。如果代码是

while ($choice == 0 && $i_feel_pretty == 3) 
如果两个条件中的哪一个出现问题,那么即使它们都在同一条线上,它也会显示出来。

因此,当出现警告或错误时,需要获取OP地址,然后调用B::DeparseTree报告位置和上下文。

目前Perl提供$SIG{__WARN__}$SIG{__DIE__},但PL_op的值为 调用处理程序时不可用,即使处理程序是XS子程序。 PL_op被保存,然后设置为伪造的OP_ENTERSUB a 调用处理程序的目的。

然而,有希望解决这个问题可以比修复已经存在16年的这个错误更快地完成,并且我怀疑将很快得到解决。请参阅perl #133239

最后我会说现在B :: DeparseTree不会回到5.16并且一般来说代码需要的比我自己给予的更多的爱。