diff options
| author | Russ Cox <rsc@golang.org> | 2021-05-20 11:33:36 -0400 |
|---|---|---|
| committer | Russ Cox <rsc@golang.org> | 2021-09-21 14:02:52 +0000 |
| commit | 7d67f8d4354d864fcbe6b218b7f80bcdfde851c9 (patch) | |
| tree | c965e575b56e7a25b3c8053bee7b05954e3dcd85 /src/text/template/exec.go | |
| parent | 39e08c6cd75da72059a58f05eb500b48124d563e (diff) | |
| download | go-7d67f8d4354d864fcbe6b218b7f80bcdfde851c9.tar.xz | |
text/template: implement short-circuit and, or
Making the builtin and and or functions use short-circuit
evaluation was accepted as a proposal in April 2019,
but we never got around to implementing it. Do that.
Fixes #31103.
Change-Id: Ia43d4a9a6b0ab814f2dd3471ebaca3e7bb1505cf
Reviewed-on: https://go-review.googlesource.com/c/go/+/321490
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/text/template/exec.go')
| -rw-r--r-- | src/text/template/exec.go | 22 |
1 files changed, 18 insertions, 4 deletions
diff --git a/src/text/template/exec.go b/src/text/template/exec.go index 5ad3b4ec58..6e005b57d7 100644 --- a/src/text/template/exec.go +++ b/src/text/template/exec.go @@ -572,11 +572,11 @@ func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ide func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value { s.at(node) name := node.Ident - function, ok := findFunction(name, s.tmpl) + function, isBuiltin, ok := findFunction(name, s.tmpl) if !ok { s.errorf("%q is not a defined function", name) } - return s.evalCall(dot, function, cmd, name, args, final) + return s.evalCall(dot, function, isBuiltin, cmd, name, args, final) } // evalField evaluates an expression like (.Field) or (.Field arg1 arg2). @@ -605,7 +605,7 @@ func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, ptr = ptr.Addr() } if method := ptr.MethodByName(fieldName); method.IsValid() { - return s.evalCall(dot, method, node, fieldName, args, final) + return s.evalCall(dot, method, false, node, fieldName, args, final) } hasArgs := len(args) > 1 || final != missingVal // It's not a method; must be a field of a struct or an element of a map. @@ -669,7 +669,7 @@ var ( // evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so // it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0] // as the function itself. -func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { +func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value { if args != nil { args = args[1:] // Zeroth arg is function name/node; not passed to function. } @@ -691,6 +691,20 @@ func (s *state) evalCall(dot, fun reflect.Value, node parse.Node, name string, a // TODO: This could still be a confusing error; maybe goodFunc should provide info. s.errorf("can't call method/function %q with %d results", name, typ.NumOut()) } + + // Special case for builtin and/or, which short-circuit. + if isBuiltin && (name == "and" || name == "or") { + argType := typ.In(0) + var v reflect.Value + for _, arg := range args { + v = s.evalArg(dot, argType, arg).Interface().(reflect.Value) + if truth(v) == (name == "or") { + break + } + } + return v + } + // Build the arg list. argv := make([]reflect.Value, numIn) // Args must be evaluated. Fixed args first. |
