text/template: allow newlines in raw quotes

This was disallowed for error-checking reasons but people ask for
it, it's easy, and it's clear what it all means.

Fixes #7323.

Change-Id: I26542f5ac6519e45b335ad789713a4d9e356279b
Reviewed-on: https://go-review.googlesource.com/9537
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/src/text/template/doc.go b/src/text/template/doc.go
index 223c595..0ce63f6 100644
--- a/src/text/template/doc.go
+++ b/src/text/template/doc.go
@@ -18,7 +18,7 @@
 The input text for a template is UTF-8-encoded text in any format.
 "Actions"--data evaluations or control structures--are delimited by
 "{{" and "}}"; all text outside actions is copied to the output unchanged.
-Actions may not span newlines, although comments can.
+Except for raw strings, actions may not span newlines, although comments can.
 
 Once parsed, a template may be executed safely in parallel.
 
@@ -106,7 +106,7 @@
 
 	- A boolean, string, character, integer, floating-point, imaginary
 	  or complex constant in Go syntax. These behave like Go's untyped
-	  constants, although raw strings may not span newlines.
+	  constants.
 	- The keyword nil, representing an untyped Go nil.
 	- The character '.' (period):
 		.
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index 8c4e165..27c74eb 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -1095,3 +1095,16 @@
 		t.Errorf("expected error; got none")
 	}
 }
+
+// Test that the error message for multiline unterminated string
+// refers to the line number of the opening quote.
+func TestUnterminatedStringError(t *testing.T) {
+	_, err := New("X").Parse("hello\n\n{{`unterminated\n\n\n\n}}\n some more\n\n")
+	if err == nil {
+		t.Fatal("expected error")
+	}
+	str := err.Error()
+	if !strings.Contains(str, "X:3: unexpected unterminated raw quoted strin") {
+		t.Fatalf("unexpected error: %s", str)
+	}
+}
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
index 1674aaf..b829b7e3 100644
--- a/src/text/template/parse/lex.go
+++ b/src/text/template/parse/lex.go
@@ -525,7 +525,7 @@
 Loop:
 	for {
 		switch l.next() {
-		case eof, '\n':
+		case eof:
 			return l.errorf("unterminated raw quoted string")
 		case '`':
 			break Loop
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
index d251ccf..a1cda19 100644
--- a/src/text/template/parse/lex_test.go
+++ b/src/text/template/parse/lex_test.go
@@ -58,18 +58,20 @@
 }
 
 var (
-	tEOF      = item{itemEOF, 0, ""}
-	tFor      = item{itemIdentifier, 0, "for"}
-	tLeft     = item{itemLeftDelim, 0, "{{"}
-	tLpar     = item{itemLeftParen, 0, "("}
-	tPipe     = item{itemPipe, 0, "|"}
-	tQuote    = item{itemString, 0, `"abc \n\t\" "`}
-	tRange    = item{itemRange, 0, "range"}
-	tRight    = item{itemRightDelim, 0, "}}"}
-	tRpar     = item{itemRightParen, 0, ")"}
-	tSpace    = item{itemSpace, 0, " "}
-	raw       = "`" + `abc\n\t\" ` + "`"
-	tRawQuote = item{itemRawString, 0, raw}
+	tEOF        = item{itemEOF, 0, ""}
+	tFor        = item{itemIdentifier, 0, "for"}
+	tLeft       = item{itemLeftDelim, 0, "{{"}
+	tLpar       = item{itemLeftParen, 0, "("}
+	tPipe       = item{itemPipe, 0, "|"}
+	tQuote      = item{itemString, 0, `"abc \n\t\" "`}
+	tRange      = item{itemRange, 0, "range"}
+	tRight      = item{itemRightDelim, 0, "}}"}
+	tRpar       = item{itemRightParen, 0, ")"}
+	tSpace      = item{itemSpace, 0, " "}
+	raw         = "`" + `abc\n\t\" ` + "`"
+	rawNL       = "`now is{{\n}}the time`" // Contains newline inside raw quote.
+	tRawQuote   = item{itemRawString, 0, raw}
+	tRawQuoteNL = item{itemRawString, 0, rawNL}
 )
 
 var lexTests = []lexTest{
@@ -104,6 +106,7 @@
 	{"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
 	{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
 	{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
+	{"raw quote with newline", "{{" + rawNL + "}}", []item{tLeft, tRawQuoteNL, tRight, tEOF}},
 	{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
 		tLeft,
 		{itemNumber, 0, "1"},
@@ -294,7 +297,7 @@
 		tLeft,
 		{itemError, 0, "unterminated quoted string"},
 	}},
-	{"unclosed raw quote", "{{`xx\n`}}", []item{
+	{"unclosed raw quote", "{{`xx}}", []item{
 		tLeft,
 		{itemError, 0, "unterminated raw quoted string"},
 	}},