From 330b376133e00ae418bcf01e641e127df01fbc28 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 29 Apr 2019 00:24:26 +0900 Subject: [PATCH] parse.y: fix here-doc identifier with newline * parse.y (heredoc_identifier): quoted here-document identifier must end within the same line. the only corner case that here-document identifier can contain a newline is that the closing qoute is placed at the beginning of the next line, and has been warned since 2.4. ```ruby <<"EOS " # warning: here document identifier ends with a newline EOS ``` --- NEWS | 6 ++++++ doc/syntax/literals.rdoc | 3 +++ parse.y | 14 ++------------ test/ruby/test_syntax.rb | 7 +++---- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index c92edb4b30..3b276e9216 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,12 @@ sufficient information, see the ChangeLog file or Redmine * Setting $, to non-nil value is warned now. Use of it in Array#join is warned too. +* Quoted here-document identifier must end within the same line. + + <<"EOS + " # This has been warned since 2.4 + EOS + === Core classes updates (outstanding ones only) Enumerable:: diff --git a/doc/syntax/literals.rdoc b/doc/syntax/literals.rdoc index ecf7d62a2a..00bc4f89d6 100644 --- a/doc/syntax/literals.rdoc +++ b/doc/syntax/literals.rdoc @@ -255,6 +255,9 @@ behaves like Kernel#`: cat #{__FILE__} HEREDOC +When surrounding with quotes, any characters but that quote and newline +can be used as the identifier. + To call a method on a heredoc place it after the opening identifier: expected_result = <<-EXPECTED.chomp diff --git a/parse.y b/parse.y index 7c5e8c3772..fddd02a9b3 100644 --- a/parse.y +++ b/parse.y @@ -6788,7 +6788,6 @@ heredoc_identifier(struct parser_params *p) int c = nextc(p), term, func = 0, term_len = 2; enum yytokentype token = tSTRING_BEG; long len; - int newline = 0; int indent = 0; if (c == '-') { @@ -6821,23 +6820,14 @@ heredoc_identifier(struct parser_params *p) tokadd(p, func); term = c; while ((c = nextc(p)) != -1 && c != term) { + if (c == '\n') goto unterminated; if (tokadd_mbchar(p, c) == -1) return 0; - if (!newline && c == '\n') newline = 1; - else if (newline) newline = 2; } if (c == -1) { + unterminated: yyerror(NULL, p, "unterminated here document identifier"); return -1; } - switch (newline) { - case 1: - rb_warn0("here document identifier ends with a newline"); - if (--p->tokidx > 0 && p->tokenbuf[p->tokidx] == '\r') --p->tokidx; - break; - case 2: - compile_error(p, "here document identifier across newlines, never match"); - return -1; - } break; default: diff --git a/test/ruby/test_syntax.rb b/test/ruby/test_syntax.rb index 257fe07b3c..66adeb1c09 100644 --- a/test/ruby/test_syntax.rb +++ b/test/ruby/test_syntax.rb @@ -852,10 +852,9 @@ eom assert_syntax_error("puts <<""EOS\n""ng\n""EOS\r""NO\n", /can't find string "EOS" anywhere before EOF/) end - def test_heredoc_newline - assert_warn(/ends with a newline/) do - eval("<<\"EOS\n\"\nEOS\n") - end + def test_unterminated_heredoc + assert_syntax_error("<<\"EOS\n\nEOS\n", /unterminated/) + assert_syntax_error("<<\"EOS\n\"\nEOS\n", /unterminated/) end def test__END___cr