fix errror handling for ':attr(foo' with no ')'
authorDavid Mitchell <[email protected]>
Wed, 7 Sep 2016 08:30:26 +0000 (09:30 +0100)
committerDavid Mitchell <[email protected]>
Wed, 7 Sep 2016 10:42:30 +0000 (11:42 +0100)
When the parameter of an attribute has no matching closing ')', there
are several issues with the way the error is handled.

First, the code currently tries to set PL_bufptr back to the position
of the opening '(' for a better error message. However, since the error
will have been discovered only at EOF after all the remaining lines have
been read and discarded, the buffer no longer contains ':attr(...'.
So the error message includes a spurious \0 followed by possibly some
random chunk of characters from the last line read in.

Worse, if the input buffer gets realloced while perl searches for the ')',
then PL_bufptr is reset to point into the freed buffer. [perl #129086].

It does yyerror() rather than croak(), so further error messages appear,
even though we're at EOF and no further parsing can occur. Similar cases
such as no matching closing string delimiter all croak instead.

It resets cop_line to zero *before* printing the error, so the line number
of the error is erroneously reported as zero.

This commit fixes all these issues by handling the error more similarly
to the way unterminated strings are handled. In particular, it no longer
tries to print out the section of src where it thinks the error is.

For comparison, running perl on this code file:

    # this is line 1
    my $x :foo(bar;
    the
    quick
    brown
    fox jumps over the lazy dog

used to output:

    Unterminated attribute parameter in attribute list at /tmp/p1 line 0, near "\0\0x jumps "
    syntax error at /tmp/p1 line 0, at EOF
    Execution of /tmp/p1 aborted due to compilation errors.

but now outputs:

    Unterminated attribute parameter in attribute list at /tmp/p1 line 2.

Note how previously: the error message included two literal null chars
(represented by \0 above), followed by a random chunk of the last line;
it claimed to be on line 0; it output two further error messages.

For comparison, change the ':foo' to 'q' so that its an unterminated
string, and you get (and always got):

    Can't find string terminator ")" anywhere before EOF at /tmp/p1 line 2.

t/op/attrs.t
toke.c

index 318a9d6..5e3125f 100644 (file)
@@ -30,7 +30,7 @@ eval 'sub e2 ($) : plugh(0,0) xyzzy ;';
 like $@, qr/^Invalid CODE attributes: ["']?plugh\(0,0\)["']? /;
 
 eval 'sub e3 ($) : plugh(0,0 xyzzy ;';
-like $@, qr/Unterminated attribute parameter in attribute list at/;
+like $@, qr/^Unterminated attribute parameter in attribute list at \(eval \d+\) line 1\.$/;
 
 eval 'sub e4 ($) : plugh + XYZZY ;';
 like $@, qr/Invalid separator character '[+]' in attribute list at/;
@@ -64,9 +64,9 @@ like $@, qr/^Invalid SCALAR attribute: ["']?_5x5["']? at/;
 eval 'my $x : locked method;';
 like $@, qr/^Invalid SCALAR attributes: ["']?locked : method["']? at/;
 eval 'my $x : switch(10,foo();';
-like $@, qr/^Unterminated attribute parameter in attribute list at/;
+like $@, qr/^Unterminated attribute parameter in attribute list at \(eval \d+\) line 1\.$/;
 eval q/my $x : Ugly('(');/;
-like $@, qr/^Unterminated attribute parameter in attribute list at/;
+like $@, qr/^Unterminated attribute parameter in attribute list at \(eval \d+\) line 1\.$/;
 eval 'my $x : 5x5;';
 like $@, qr/error/;
 eval 'my $x : Y2::north;';
@@ -468,4 +468,16 @@ is runperl(
    "OK",
   'RT #129099 BEGIN()';
 
+
+#129086
+# When printing error message for an attribute arg without closing ')',
+# if the buffer got reallocated during the scan of the arg, the error
+# message would try to use the old buffer
+fresh_perl_like(
+   'my $abc: abcdefg(' . 'x' x 195 . "\n" . 'x' x 8200 ."\n",
+    qr/^Unterminated attribute parameter in attribute list at - line 1\.$/,
+    { stderr => 1 },
+    'RT #129086 attr(00000'
+),
+
 done_testing();
diff --git a/toke.c b/toke.c
index 3ade32b..bd3280c 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -5621,18 +5621,13 @@ Perl_yylex(pTHX)
                sv = newSVpvn_flags(s, len, UTF ? SVf_UTF8 : 0);
                if (*d == '(') {
                    d = scan_str(d,TRUE,TRUE,FALSE,NULL);
-                   COPLINE_SET_FROM_MULTI_END;
                    if (!d) {
-                       /* MUST advance bufptr here to avoid bogus
-                          "at end of line" context messages from yyerror().
-                        */
-                       PL_bufptr = s + len;
-                       yyerror("Unterminated attribute parameter in attribute list");
                        if (attrs)
                            op_free(attrs);
                        sv_free(sv);
-                       return REPORT(0);       /* EOF indicator */
+                        Perl_croak(aTHX_ "Unterminated attribute parameter in attribute list");
                    }
+                   COPLINE_SET_FROM_MULTI_END;
                }
                if (PL_lex_stuff) {
                    sv_catsv(sv, PL_lex_stuff);