Commit 76ace947 authored by Didier Spezia's avatar Didier Spezia Committed by Rob Pike

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/9621Reviewed-by: default avatarRob Pike <r@golang.org>
parent 172f2765
...@@ -527,8 +527,10 @@ var execTests = []execTest{ ...@@ -527,8 +527,10 @@ var execTests = []execTest{
{"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true}, {"bug12XE", "{{printf `%T` 0XEE}}", "int", T{}, true},
// Chained nodes did not work as arguments. Issue 8473. // Chained nodes did not work as arguments. Issue 8473.
{"bug13", "{{print (.Copy).I}}", "17", tVal, true}, {"bug13", "{{print (.Copy).I}}", "17", tVal, true},
// Didn't protect against explicit nil in field chains. // Didn't protect against nil or literal values in field chains.
{"bug14", "{{nil.True}}", "", tVal, false}, {"bug14a", "{{(nil).True}}", "", tVal, false},
{"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false},
{"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false},
} }
func zeroArgs() string { func zeroArgs() string {
......
...@@ -554,7 +554,7 @@ func (t *Tree) command() *CommandNode { ...@@ -554,7 +554,7 @@ func (t *Tree) command() *CommandNode {
t.backup() t.backup()
case itemPipe: case itemPipe:
default: default:
t.errorf("unexpected %s in operand; missing space?", token) t.errorf("unexpected %s in operand", token)
} }
break break
} }
...@@ -582,12 +582,15 @@ func (t *Tree) operand() Node { ...@@ -582,12 +582,15 @@ func (t *Tree) operand() Node {
// Compatibility with original API: If the term is of type NodeField // Compatibility with original API: If the term is of type NodeField
// or NodeVariable, just put more fields on the original. // or NodeVariable, just put more fields on the original.
// Otherwise, keep the Chain node. // Otherwise, keep the Chain node.
// TODO: Switch to Chains always when we can. // Obvious parsing errors involving literal values are detected here.
// More complex error cases will have to be handled at execution time.
switch node.Type() { switch node.Type() {
case NodeField: case NodeField:
node = t.newField(chain.Position(), chain.String()) node = t.newField(chain.Position(), chain.String())
case NodeVariable: case NodeVariable:
node = t.newVariable(chain.Position(), chain.String()) node = t.newVariable(chain.Position(), chain.String())
case NodeBool, NodeString, NodeNumber, NodeNil, NodeDot:
t.errorf("unexpected . after term %q", node.String())
default: default:
node = chain node = chain
} }
......
...@@ -260,6 +260,14 @@ var parseTests = []parseTest{ ...@@ -260,6 +260,14 @@ var parseTests = []parseTest{
{"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here. {"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here.
{"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2). {"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. {"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{}{ var builtins = map[string]interface{}{
...@@ -378,7 +386,7 @@ var errorTests = []parseTest{ ...@@ -378,7 +386,7 @@ var errorTests = []parseTest{
hasError, `unexpected ")"`}, hasError, `unexpected ")"`},
{"space", {"space",
"{{`x`3}}", "{{`x`3}}",
hasError, `missing space?`}, hasError, `in operand`},
{"idchar", {"idchar",
"{{a#}}", "{{a#}}",
hasError, `'#'`}, hasError, `'#'`},
...@@ -410,6 +418,9 @@ var errorTests = []parseTest{ ...@@ -410,6 +418,9 @@ var errorTests = []parseTest{
{"undefvar", {"undefvar",
"{{$a}}", "{{$a}}",
hasError, `undefined variable`}, hasError, `undefined variable`},
{"wrongdot",
"{{true.any}}",
hasError, `unexpected . after term`},
} }
func TestErrors(t *testing.T) { func TestErrors(t *testing.T) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment