diff --git a/MANIFEST b/MANIFEST index 930d640..28232b4 100644 --- a/MANIFEST +++ b/MANIFEST @@ -4154,6 +4154,7 @@ t/op/gv.t See if typeglobs work t/op/hashassign.t See if hash assignments work t/op/hash.t See if the complexity attackers are repelled t/op/hashwarn.t See if warnings for bad hash assignments work +t/op/heredoc.t See if heredoc edge and corner cases work t/op/inccode.t See if coderefs work in @INC t/op/inccode-tie.t See if tie to @INC works t/op/incfilter.t See if the source filters in coderef-in-@INC work diff --git a/pod/perlop.pod b/pod/perlop.pod index e677430..0895509 100644 --- a/pod/perlop.pod +++ b/pod/perlop.pod @@ -1750,6 +1750,9 @@ This is the only form of quoting in perl where there is no need to worry about escaping content, something that code generators can and do make good use of. +A bare terminator prefixed with a backslash also triggers this +quoting mode. + =item Backticks The content of the here doc is treated just as it would be if the @@ -1802,8 +1805,8 @@ you'll need to remove leading whitespace from each line manually: down from the door where it began. FINIS -If you use a here-doc within a delimited construct, such as in C, -the quoted material must come on the lines following the final delimiter. +When using a here-doc within a delimited construct, such as in C, +the quoted material starts on the line following the final delimiter. So instead of s/this/<. - Additionally, the quoting rules for the end of string identifier are not related to Perl's quoting rules -- C, C, and the like are not supported in place of C<''> and C<"">, and the only interpolation is for diff --git a/t/lib/warnings/toke b/t/lib/warnings/toke index e5ca400..2360042 100644 --- a/t/lib/warnings/toke +++ b/t/lib/warnings/toke @@ -141,14 +141,14 @@ Use of comma-less variable list is deprecated at - line 5. Use of comma-less variable list is deprecated at - line 5. ######## # toke.c -use warnings 'deprecated' ; +use warnings 'syntax' ; $a = <<; -no warnings 'deprecated' ; +no warnings 'syntax' ; $a = <<; EXPECT -Use of bare << to mean <<"" is deprecated at - line 3. +bare << meaning <<"" may be confusing at - line 3. ######## # toke.c use warnings 'syntax' ; diff --git a/t/op/heredoc.t b/t/op/heredoc.t new file mode 100644 index 0000000..607b19f --- /dev/null +++ b/t/op/heredoc.t @@ -0,0 +1,83 @@ +# tests for heredocs besides what is tested in base/lex.t + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + require './test.pl'; +} + +use strict; +plan(tests => 8); + + +# heredoc without newline (#65838) +{ + my $string = <<'HEREDOC'; +testing for 65838 +HEREDOC + + my $code = "<<'HEREDOC';\n${string}HEREDOC"; # HD w/o newline, in eval-string + my $hd = eval $code or warn "$@ ---"; + is($hd, $string, "no terminating newline in string-eval"); +} + + +# here-doc edge cases +{ + my $string = "testing for 65838"; + + fresh_perl_is( + "print <<'HEREDOC';\n${string}\nHEREDOC", + $string, + {}, + "heredoc at EOF without trailing newline" + ); + + fresh_perl_is( + "print <<;\n$string\n", + $string, + {}, + "blank-terminated heredoc at EOF" + ); +} + + +# here-doc parse failures +{ + fresh_perl_like( + "print <{progfile} = $tmpfile; - $runperl_args->{stderr} = 1; + $runperl_args->{progfile} ||= $tmpfile; + $runperl_args->{stderr} = 1 unless exists $runperl_args->{stderr}; open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!"; @@ -698,12 +698,6 @@ sub _fresh_perl { } my $pass = $resolve->($results); - unless ($pass) { - _diag "# PROG: \n$prog\n"; - _diag "# EXPECTED:\n", $resolve->(), "\n"; - _diag "# GOT:\n$results\n"; - _diag "# STATUS: $status\n"; - } # Use the first line of the program as a name if none was given unless( $name ) { @@ -712,6 +706,15 @@ sub _fresh_perl { } _ok($pass, _where(), "fresh_perl - $name"); + + unless ($pass) { + _diag "# PROG: \n$prog\n"; + _diag "# EXPECTED:\n", $resolve->(), "\n"; + _diag "# GOT:\n$results\n"; + _diag "# STATUS: $status\n"; + } + + return $pass; } # diff --git a/toke.c b/toke.c index a15dca6..5cccb61 100644 --- a/toke.c +++ b/toke.c @@ -11281,12 +11281,16 @@ S_scan_heredoc(pTHX_ register char *s) s++; } else { + if (*peek == '\\' || isALNUM_lazy_if(peek,UTF)) + s = peek; if (*s == '\\') + /* <<\FOO is equivalent to <<'FOO' */ s++, term = '\''; else term = '"'; - if (!isALNUM_lazy_if(s,UTF)) - deprecate_old("bare << to mean <<\"\""); + if (!isALNUM_lazy_if(s,UTF) && ckWARN(WARN_SYNTAX)) + Perl_warner(aTHX_ packWARN(WARN_SYNTAX), + "bare << meaning <<\"\" may be confusing"); for (; isALNUM_lazy_if(s,UTF); s++) { if (d < e) *d++ = *s; @@ -11448,8 +11452,30 @@ S_scan_heredoc(pTHX_ register char *s) #endif if (!outer || !(PL_oldoldbufptr = PL_oldbufptr = s = PL_linestart = filter_gets(PL_linestr, PL_rsfp, 0))) { - CopLINE_set(PL_curcop, (line_t)PL_multi_start); - missingterm(PL_tokenbuf); + I32 len_without; + /* in other words, we are out of input */ + /* maybe the string terminator didn't have a newline? */ + sv_catpvn(tmpstr,"\n",1); /* add a newline */ + len_without = SvCUR(tmpstr) - len; + s = SvPVX(tmpstr) + len_without; + if ( len_without > -1 /* there has been enough data */ + && *(s-1) == '\n' /* the terminator starts at line-begin */ + && *s == term /* the terminator starts correctly */ + && memEQ(s,PL_tokenbuf,len) /* and checks out okay */ + ) + { + /* chop the quoted heredoc at the right place */ + SvCUR_set(tmpstr,len_without); + /* pretend we got the string terminator from filter_gets */ + sv_setpvn(PL_linestr,s,len); + PL_oldoldbufptr = PL_oldbufptr = s = PL_linestart = SvPVX(PL_linestr);; + } + else + { + /* No, we have reached EOF without finding the terminator. */ + CopLINE_set(PL_curcop, (line_t)PL_multi_start); + missingterm(PL_tokenbuf); + } } #ifdef PERL_MAD stuffstart = s - SvPVX(PL_linestr);