text/template: check for literals in chain of terms

The current parser ignores obvious errors such as:
{{0.1.E}}
{{true.any}}
{{"hello".wrong}}
{{nil.E}}

The common problem is that a chain is built from
a literal value. It then panics at execution time.

Furthermore, a double dot triggers the same behavior:
{{..E}}

Addresses a TODO left in Tree.operand to catch these
errors at parsing time.

Note that identifiers can include a '.', and pipelines
could return an object which a field can be derived
from (like a variable), so they are excluded from the check.

Fixes #10615

Change-Id: I903706d1c17861b5a8354632c291e73c9c0bc4e1
Reviewed-on: https://go-review.googlesource.com/9621
Reviewed-by: Rob Pike <r@golang.org>
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
index faac06f..a2ca98b 100644
--- a/src/text/template/parse/parse_test.go
+++ b/src/text/template/parse/parse_test.go
@@ -260,6 +260,14 @@
 	{"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""},                     // ! is just illegal here.
 	{"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""},                     // $x+2 should not parse as ($x) (+2).
 	{"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space.
+	// dot following a literal value
+	{"dot after integer", "{{1.E}}", hasError, ""},
+	{"dot after float", "{{0.1.E}}", hasError, ""},
+	{"dot after boolean", "{{true.E}}", hasError, ""},
+	{"dot after char", "{{'a'.any}}", hasError, ""},
+	{"dot after string", `{{"hello".guys}}`, hasError, ""},
+	{"dot after dot", "{{..E}}", hasError, ""},
+	{"dot after nil", "{{nil.E}}", hasError, ""},
 }
 
 var builtins = map[string]interface{}{
@@ -378,7 +386,7 @@
 		hasError, `unexpected ")"`},
 	{"space",
 		"{{`x`3}}",
-		hasError, `missing space?`},
+		hasError, `in operand`},
 	{"idchar",
 		"{{a#}}",
 		hasError, `'#'`},
@@ -410,6 +418,9 @@
 	{"undefvar",
 		"{{$a}}",
 		hasError, `undefined variable`},
+	{"wrongdot",
+		"{{true.any}}",
+		hasError, `unexpected . after term`},
 }
 
 func TestErrors(t *testing.T) {