Commit b8cb75fb authored by Rob Pike's avatar Rob Pike

text/template: add error check for parenthesized first argument in pipeline

An error check was missing: If the first argument of a pipeline is
parenthesized, and the pipeline has further arguments, then
syntactically the pipeline is a function invocation and there must
be a "call". Tricky rare corner case, but easily caught.

Add the error check and some tests to verify behavior.

Fixes #31810.

Change-Id: Ica80b7c11284e4ea9e8cc94a01dbbc9a67e42079
Reviewed-on: https://go-review.googlesource.com/c/go/+/206124Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent b60c7a5c
...@@ -461,7 +461,8 @@ func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final ref ...@@ -461,7 +461,8 @@ func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final ref
// Must be a function. // Must be a function.
return s.evalFunction(dot, n, cmd, cmd.Args, final) return s.evalFunction(dot, n, cmd, cmd.Args, final)
case *parse.PipeNode: case *parse.PipeNode:
// Parenthesized pipeline. The arguments are all inside the pipeline; final is ignored. // Parenthesized pipeline. The arguments are all inside the pipeline; final must be absent.
s.notAFunction(cmd.Args, final)
return s.evalPipeline(dot, n) return s.evalPipeline(dot, n)
case *parse.VariableNode: case *parse.VariableNode:
return s.evalVariableNode(dot, n, cmd.Args, final) return s.evalVariableNode(dot, n, cmd.Args, final)
......
...@@ -352,6 +352,12 @@ var execTests = []execTest{ ...@@ -352,6 +352,12 @@ var execTests = []execTest{
{"field on interface", "{{.foo}}", "<no value>", nil, true}, {"field on interface", "{{.foo}}", "<no value>", nil, true},
{"field on parenthesized interface", "{{(.).foo}}", "<no value>", nil, true}, {"field on parenthesized interface", "{{(.).foo}}", "<no value>", nil, true},
// Issue 31810: Parenthesized first element of pipeline with arguments.
// See also TestIssue31810.
{"unparenthesized non-function", "{{1 2}}", "", nil, false},
{"parenthesized non-function", "{{(1) 2}}", "", nil, false},
{"parenthesized non-function with no args", "{{(1)}}", "1", nil, true}, // This is fine.
// Method calls. // Method calls.
{".Method0", "-{{.Method0}}-", "-M0-", tVal, true}, {".Method0", "-{{.Method0}}-", "-M0-", tVal, true},
{".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true}, {".Method1(1234)", "-{{.Method1 1234}}-", "-1234-", tVal, true},
...@@ -1648,3 +1654,41 @@ func TestExecutePanicDuringCall(t *testing.T) { ...@@ -1648,3 +1654,41 @@ func TestExecutePanicDuringCall(t *testing.T) {
} }
} }
} }
// Issue 31810. Check that a parenthesized first argument behaves properly.
func TestIssue31810(t *testing.T) {
// A simple value with no arguments is fine.
var b bytes.Buffer
const text = "{{ (.) }}"
tmpl, err := New("").Parse(text)
if err != nil {
t.Error(err)
}
err = tmpl.Execute(&b, "result")
if err != nil {
t.Error(err)
}
if b.String() != "result" {
t.Errorf("%s got %q, expected %q", text, b.String(), "result")
}
// Even a plain function fails - need to use call.
f := func() string { return "result" }
b.Reset()
err = tmpl.Execute(&b, f)
if err == nil {
t.Error("expected error with no call, got none")
}
// Works if the function is explicitly called.
const textCall = "{{ (call .) }}"
tmpl, err = New("").Parse(textCall)
b.Reset()
err = tmpl.Execute(&b, f)
if err != nil {
t.Error(err)
}
if b.String() != "result" {
t.Errorf("%s got %q, expected %q", textCall, b.String(), "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