Commit 1399b52d authored by Daniel Martí's avatar Daniel Martí

text/template/parse: error on bad range variables

The package used to accept invalid range pipelines, such as:

	{{range $k, .}}
	{{range $k, 123 := .}}

This is because the logic that allowed a range pipeline to declare
multiple variables was broken. When encountering a single comma inside a
range pipeline, it would happily continue parsing a second variable,
even if we didn't have a variable token at all.

Then, the loop would immediately break, and we'd parse the pipeline we'd
be ranging over. That is, we'd parse {{range $k, .}} as if it were
{{range $k = .}}.

To fix this, only allow the loop to continue if we know we're going to
parse another variable or a token that would end the pipeline. Also add
a few test cases for these error edge cases.

While at it, make use of T.Run, which was useful in debugging
Tree.pipeline via print statements.

Fixes #28437.

Change-Id: Idc9966bf643f0f3bc1b052620357e5b0aa2022ea
Reviewed-on: https://go-review.googlesource.com/c/145282
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Reviewed-by: 's avatarRob Pike <r@golang.org>
parent 81475ca2
...@@ -385,6 +385,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { ...@@ -385,6 +385,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
token := t.peekNonSpace() token := t.peekNonSpace()
pos := token.pos pos := token.pos
// Are there declarations or assignments? // Are there declarations or assignments?
// TODO(mvdan): simplify the loop break/continue logic
for { for {
if v := t.peekNonSpace(); v.typ == itemVariable { if v := t.peekNonSpace(); v.typ == itemVariable {
t.next() t.next()
...@@ -406,7 +407,12 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { ...@@ -406,7 +407,12 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) {
} }
if next.typ == itemChar && next.val == "," { if next.typ == itemChar && next.val == "," {
if context == "range" && len(vars) < 2 { if context == "range" && len(vars) < 2 {
continue switch t.peekNonSpace().typ {
case itemVariable, itemRightDelim, itemRightParen:
continue
default:
t.errorf("range can only initialize variables")
}
} }
t.errorf("too many declarations in %s", context) t.errorf("too many declarations in %s", context)
} }
......
...@@ -450,18 +450,37 @@ var errorTests = []parseTest{ ...@@ -450,18 +450,37 @@ var errorTests = []parseTest{
{"multilinerawstring", {"multilinerawstring",
"{{ $v := `\n` }} {{", "{{ $v := `\n` }} {{",
hasError, `multilinerawstring:2: unexpected unclosed action`}, hasError, `multilinerawstring:2: unexpected unclosed action`},
{"rangeundefvar",
"{{range $k}}{{end}}",
hasError, `undefined variable`},
{"rangeundefvars",
"{{range $k, $v}}{{end}}",
hasError, `undefined variable`},
{"rangemissingvalue1",
"{{range $k,}}{{end}}",
hasError, `missing value for range`},
{"rangemissingvalue2",
"{{range $k, $v := }}{{end}}",
hasError, `missing value for range`},
{"rangenotvariable1",
"{{range $k, .}}{{end}}",
hasError, `range can only initialize variables`},
{"rangenotvariable2",
"{{range $k, 123 := .}}{{end}}",
hasError, `range can only initialize variables`},
} }
func TestErrors(t *testing.T) { func TestErrors(t *testing.T) {
for _, test := range errorTests { for _, test := range errorTests {
_, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree)) t.Run(test.name, func(t *testing.T) {
if err == nil { _, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree))
t.Errorf("%q: expected error", test.name) if err == nil {
continue t.Fatalf("expected error %q, got nil", test.result)
} }
if !strings.Contains(err.Error(), test.result) { if !strings.Contains(err.Error(), test.result) {
t.Errorf("%q: error %q does not contain %q", test.name, err, test.result) t.Fatalf("error %q does not contain %q", err, test.result)
} }
})
} }
} }
......
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