Deparse -l: set correct line num at end of sub
authorDavid Mitchell <davem@iabyn.com>
Mon, 19 Mar 2018 20:35:26 +0000 (20:35 +0000)
committerDavid Mitchell <davem@iabyn.com>
Tue, 20 Mar 2018 07:59:26 +0000 (07:59 +0000)
A sub declaration like

    sub f { ...}

will be deparsed under -l as

    #line 1 "foo"
    sub f {
    #line 1 "foo"
        ....
    }

which means that the closing '}' of the sub is incorrectly seen as being
on the next line.

This matters when a glob is created based on a sub being compiled: the
glob's gp_line field is set to the line containing the '}'.

This was causing some tests in ext/B/t/xref.t to fail under
    ./TEST -deparse

This commit causes an extra #line directive to be emitted:

    #line 1 "foo"
    sub f {
    #line 1 "foo"
        ....
    #line 1 "foo"   <=== NEW
    }

Whether xref.t failed depended on another factor. The optimisation
which created the GV for a just-compiled sub as an RV to a CV rather than
a GV to CV, causes the later-vifified GV (upgraded from an RV) to instead
have a gp_line corresponding to the first cop in the sub's body.

Arguably this difference (gp_line being set to the line number of first
line of the sub rather than the last line) is a bug, but it's not obvious
how to fix it, and I don't address it here.

However, the optimisation originally only applied to GVs in the main
stash;  it was later extended to all stashes but then reverted again,
in the sequence of commits

    v5.27.4-66-g6881372
    v5.27.4-127-g6eed25e
    v5.27.5-321-gd964025
    v5.27.8-149-g1e2cfe1

which caused xref.t to pass, then fail again.

lib/B/Deparse.pm

index d3ab507..8fedf81 100644 (file)
@@ -1355,7 +1355,18 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
        else {
            $body = $self->deparse($root->first, 0);
        }
-        $body = "{\n\t$body\n\b}";
+
+        my $l = '';
+        if ($self->{'linenums'}) {
+            # a glob's gp_line is set from the line containing a
+            # sub's closing '}' if the CV is the first use of the GV.
+            # So make sure the linenum is set correctly for '}'
+            my $gv = $cv->GV;
+            my $line = $gv->LINE;
+            my $file = $gv->FILE;
+            $l = "\f#line $line \"$file\"\n";
+        }
+        $body = "{\n\t$body\n$l\b}";
     }
     else {
        my $sv = $cv->const_sv;