src/text/template/exec.go GO 1,143 lines View on github.com → Search inside
1// Copyright 2011 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package template67import (8	"errors"9	"fmt"10	"internal/fmtsort"11	"io"12	"reflect"13	"runtime"14	"strings"15	"text/template/parse"16)1718// maxExecDepth specifies the maximum stack depth of templates within19// templates. This limit is only practically reached by accidentally20// recursive template invocations. This limit allows us to return21// an error instead of triggering a stack overflow.22var maxExecDepth = initMaxExecDepth()2324func initMaxExecDepth() int {25	if runtime.GOARCH == "wasm" {26		return 100027	}28	return 10000029}3031// state represents the state of an execution. It's not part of the32// template so that multiple executions of the same template33// can execute in parallel.34type state struct {35	tmpl  *Template36	wr    io.Writer37	node  parse.Node // current node, for errors38	vars  []variable // push-down stack of variable values.39	depth int        // the height of the stack of executing templates.40}4142// variable holds the dynamic value of a variable such as $, $x etc.43type variable struct {44	name  string45	value reflect.Value46}4748// push pushes a new variable on the stack.49func (s *state) push(name string, value reflect.Value) {50	s.vars = append(s.vars, variable{name, value})51}5253// mark returns the length of the variable stack.54func (s *state) mark() int {55	return len(s.vars)56}5758// pop pops the variable stack up to the mark.59func (s *state) pop(mark int) {60	s.vars = s.vars[0:mark]61}6263// setVar overwrites the last declared variable with the given name.64// Used by variable assignments.65func (s *state) setVar(name string, value reflect.Value) {66	for i := s.mark() - 1; i >= 0; i-- {67		if s.vars[i].name == name {68			s.vars[i].value = value69			return70		}71	}72	s.errorf("undefined variable: %s", name)73}7475// setTopVar overwrites the top-nth variable on the stack. Used by range iterations.76func (s *state) setTopVar(n int, value reflect.Value) {77	s.vars[len(s.vars)-n].value = value78}7980// varValue returns the value of the named variable.81func (s *state) varValue(name string) reflect.Value {82	for i := s.mark() - 1; i >= 0; i-- {83		if s.vars[i].name == name {84			return s.vars[i].value85		}86	}87	s.errorf("undefined variable: %s", name)88	return zero89}9091var zero reflect.Value9293type missingValType struct{}9495var missingVal = reflect.ValueOf(missingValType{})9697var missingValReflectType = reflect.TypeFor[missingValType]()9899func isMissing(v reflect.Value) bool {100	return v.IsValid() && v.Type() == missingValReflectType101}102103// at marks the state to be on node n, for error reporting.104func (s *state) at(node parse.Node) {105	s.node = node106}107108// doublePercent returns the string with %'s replaced by %%, if necessary,109// so it can be used safely inside a Printf format string.110func doublePercent(str string) string {111	return strings.ReplaceAll(str, "%", "%%")112}113114// TODO: It would be nice if ExecError was more broken down, but115// the way ErrorContext embeds the template name makes the116// processing too clumsy.117118// ExecError is the custom error type returned when Execute has an119// error evaluating its template. (If a write error occurs, the actual120// error is returned; it will not be of type ExecError.)121type ExecError struct {122	Name string // Name of template.123	Err  error  // Pre-formatted error.124}125126func (e ExecError) Error() string {127	return e.Err.Error()128}129130func (e ExecError) Unwrap() error {131	return e.Err132}133134// errorf records an ExecError and terminates processing.135func (s *state) errorf(format string, args ...any) {136	name := doublePercent(s.tmpl.Name())137	if s.node == nil {138		format = fmt.Sprintf("template: %s: %s", name, format)139	} else {140		location, context := s.tmpl.ErrorContext(s.node)141		format = fmt.Sprintf("template: %s: executing %q at <%s>: %s", location, name, doublePercent(context), format)142	}143	panic(ExecError{144		Name: s.tmpl.Name(),145		Err:  fmt.Errorf(format, args...),146	})147}148149// writeError is the wrapper type used internally when Execute has an150// error writing to its output. We strip the wrapper in errRecover.151// Note that this is not an implementation of error, so it cannot escape152// from the package as an error value.153type writeError struct {154	Err error // Original error.155}156157func (s *state) writeError(err error) {158	panic(writeError{159		Err: err,160	})161}162163// errRecover is the handler that turns panics into returns from the top164// level of Parse.165func errRecover(errp *error) {166	e := recover()167	if e != nil {168		switch err := e.(type) {169		case runtime.Error:170			panic(e)171		case writeError:172			*errp = err.Err // Strip the wrapper.173		case ExecError:174			*errp = err // Keep the wrapper.175		default:176			panic(e)177		}178	}179}180181// ExecuteTemplate applies the template associated with t that has the given name182// to the specified data object and writes the output to wr.183// If an error occurs executing the template or writing its output,184// execution stops, but partial results may already have been written to185// the output writer.186// A template may be executed safely in parallel, although if parallel187// executions share a Writer the output may be interleaved.188func (t *Template) ExecuteTemplate(wr io.Writer, name string, data any) error {189	tmpl := t.Lookup(name)190	if tmpl == nil {191		return fmt.Errorf("template: no template %q associated with template %q", name, t.name)192	}193	return tmpl.Execute(wr, data)194}195196// Execute applies a parsed template to the specified data object,197// and writes the output to wr.198// If an error occurs executing the template or writing its output,199// execution stops, but partial results may already have been written to200// the output writer.201// A template may be executed safely in parallel, although if parallel202// executions share a Writer the output may be interleaved.203//204// If data is a [reflect.Value], the template applies to the concrete205// value that the reflect.Value holds, as in [fmt.Print].206func (t *Template) Execute(wr io.Writer, data any) error {207	return t.execute(wr, data)208}209210func (t *Template) execute(wr io.Writer, data any) (err error) {211	defer errRecover(&err)212	value, ok := data.(reflect.Value)213	if !ok {214		value = reflect.ValueOf(data)215	}216	state := &state{217		tmpl: t,218		wr:   wr,219		vars: []variable{{"$", value}},220	}221	if t.Tree == nil || t.Root == nil {222		state.errorf("%q is an incomplete or empty template", t.Name())223	}224	state.walk(value, t.Root)225	return226}227228// DefinedTemplates returns a string listing the defined templates,229// prefixed by the string "; defined templates are: ". If there are none,230// it returns the empty string. For generating an error message here231// and in [html/template].232func (t *Template) DefinedTemplates() string {233	if t.common == nil {234		return ""235	}236	var b strings.Builder237	t.muTmpl.RLock()238	defer t.muTmpl.RUnlock()239	for name, tmpl := range t.tmpl {240		if tmpl.Tree == nil || tmpl.Root == nil {241			continue242		}243		if b.Len() == 0 {244			b.WriteString("; defined templates are: ")245		} else {246			b.WriteString(", ")247		}248		fmt.Fprintf(&b, "%q", name)249	}250	return b.String()251}252253// Sentinel errors for use with panic to signal early exits from range loops.254var (255	walkBreak    = errors.New("break")256	walkContinue = errors.New("continue")257)258259// Walk functions step through the major pieces of the template structure,260// generating output as they go.261func (s *state) walk(dot reflect.Value, node parse.Node) {262	s.at(node)263	switch node := node.(type) {264	case *parse.ActionNode:265		// Do not pop variables so they persist until next end.266		// Also, if the action declares variables, don't print the result.267		val := s.evalPipeline(dot, node.Pipe)268		if len(node.Pipe.Decl) == 0 {269			s.printValue(node, val)270		}271	case *parse.BreakNode:272		panic(walkBreak)273	case *parse.CommentNode:274	case *parse.ContinueNode:275		panic(walkContinue)276	case *parse.IfNode:277		s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)278	case *parse.ListNode:279		for _, node := range node.Nodes {280			s.walk(dot, node)281		}282	case *parse.RangeNode:283		s.walkRange(dot, node)284	case *parse.TemplateNode:285		s.walkTemplate(dot, node)286	case *parse.TextNode:287		if _, err := s.wr.Write(node.Text); err != nil {288			s.writeError(err)289		}290	case *parse.WithNode:291		s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList)292	default:293		s.errorf("unknown node: %s", node)294	}295}296297// walkIfOrWith walks an 'if' or 'with' node. The two control structures298// are identical in behavior except that 'with' sets dot.299func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse.PipeNode, list, elseList *parse.ListNode) {300	defer s.pop(s.mark())301	val := s.evalPipeline(dot, pipe)302	truth, ok := isTrue(indirectInterface(val))303	if !ok {304		s.errorf("if/with can't use %v", val)305	}306	if truth {307		if typ == parse.NodeWith {308			s.walk(val, list)309		} else {310			s.walk(dot, list)311		}312	} else if elseList != nil {313		s.walk(dot, elseList)314	}315}316317// IsTrue reports whether the value is true, in the sense of being nonzero,318// nonempty, or non-nil, and whether the value has a meaningful truth value.319// This is the definition of truth used in "if" actions and elsewhere in320// templates:321//322//   - A boolean value is true if it is true.323//   - A numeric value is true if it is nonzero.324//   - An array, map, slice, or string value is true if its length is325//     greater than zero.326//   - Any other value is true if it is non-nil; struct values are327//     never nil, and therefore always true.328func IsTrue(val any) (truth, ok bool) {329	return isTrue(reflect.ValueOf(val))330}331332func isTrue(val reflect.Value) (truth, ok bool) {333	if !val.IsValid() {334		// Something like var x interface{}, never set. It's a form of nil.335		return false, true336	}337	switch val.Kind() {338	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:339		truth = val.Len() > 0340	case reflect.Bool:341		truth = val.Bool()342	case reflect.Complex64, reflect.Complex128:343		truth = val.Complex() != 0344	case reflect.Chan, reflect.Func, reflect.Pointer, reflect.UnsafePointer, reflect.Interface:345		truth = !val.IsNil()346	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:347		truth = val.Int() != 0348	case reflect.Float32, reflect.Float64:349		truth = val.Float() != 0350	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:351		truth = val.Uint() != 0352	case reflect.Struct:353		truth = true // Struct values are always true.354	default:355		return356	}357	return truth, true358}359360func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {361	s.at(r)362	defer func() {363		if r := recover(); r != nil && r != walkBreak {364			panic(r)365		}366	}()367	defer s.pop(s.mark())368	val, _ := indirect(s.evalPipeline(dot, r.Pipe))369	// mark top of stack before any variables in the body are pushed.370	mark := s.mark()371	oneIteration := func(index, elem reflect.Value) {372		if len(r.Pipe.Decl) > 0 {373			if r.Pipe.IsAssign {374				// With two variables, index comes first.375				// With one, we use the element.376				if len(r.Pipe.Decl) > 1 {377					s.setVar(r.Pipe.Decl[0].Ident[0], index)378				} else {379					s.setVar(r.Pipe.Decl[0].Ident[0], elem)380				}381			} else {382				// Set top var (lexically the second if there383				// are two) to the element.384				s.setTopVar(1, elem)385			}386		}387		if len(r.Pipe.Decl) > 1 {388			if r.Pipe.IsAssign {389				s.setVar(r.Pipe.Decl[1].Ident[0], elem)390			} else {391				// Set next var (lexically the first if there392				// are two) to the index.393				s.setTopVar(2, index)394			}395		}396		defer s.pop(mark)397		defer func() {398			// Consume panic(walkContinue)399			if r := recover(); r != nil && r != walkContinue {400				panic(r)401			}402		}()403		s.walk(elem, r.List)404	}405	switch val.Kind() {406	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,407		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:408		if len(r.Pipe.Decl) > 1 {409			s.errorf("can't use %v to iterate over more than one variable", val)410			break411		}412		run := false413		for v := range val.Seq() {414			run = true415			// Pass element as second value, as we do for channels.416			oneIteration(reflect.Value{}, v)417		}418		if !run {419			break420		}421		return422	case reflect.Array, reflect.Slice:423		if val.Len() == 0 {424			break425		}426		for i := 0; i < val.Len(); i++ {427			oneIteration(reflect.ValueOf(i), val.Index(i))428		}429		return430	case reflect.Map:431		if val.Len() == 0 {432			break433		}434		om := fmtsort.Sort(val)435		for _, m := range om {436			oneIteration(m.Key, m.Value)437		}438		return439	case reflect.Chan:440		if val.IsNil() {441			break442		}443		if val.Type().ChanDir() == reflect.SendDir {444			s.errorf("range over send-only channel %v", val)445			break446		}447		i := 0448		for ; ; i++ {449			elem, ok := val.Recv()450			if !ok {451				break452			}453			oneIteration(reflect.ValueOf(i), elem)454		}455		if i == 0 {456			break457		}458		return459	case reflect.Invalid:460		break // An invalid value is likely a nil map, etc. and acts like an empty map.461	case reflect.Func:462		if val.Type().CanSeq() {463			if len(r.Pipe.Decl) > 1 {464				s.errorf("can't use %v iterate over more than one variable", val)465				break466			}467			run := false468			for v := range val.Seq() {469				run = true470				// Pass element as second value,471				// as we do for channels.472				oneIteration(reflect.Value{}, v)473			}474			if !run {475				break476			}477			return478		}479		if val.Type().CanSeq2() {480			run := false481			for i, v := range val.Seq2() {482				run = true483				if len(r.Pipe.Decl) > 1 {484					oneIteration(i, v)485				} else {486					// If there is only one range variable,487					// oneIteration will use the488					// second value.489					oneIteration(reflect.Value{}, i)490				}491			}492			if !run {493				break494			}495			return496		}497		fallthrough498	default:499		s.errorf("range can't iterate over %v", val)500	}501	if r.ElseList != nil {502		s.walk(dot, r.ElseList)503	}504}505506func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {507	s.at(t)508	tmpl := s.tmpl.Lookup(t.Name)509	if tmpl == nil {510		s.errorf("template %q not defined", t.Name)511	}512	if s.depth == maxExecDepth {513		s.errorf("exceeded maximum template depth (%v)", maxExecDepth)514	}515	// Variables declared by the pipeline persist.516	dot = s.evalPipeline(dot, t.Pipe)517	newState := *s518	newState.depth++519	newState.tmpl = tmpl520	// No dynamic scoping: template invocations inherit no variables.521	newState.vars = []variable{{"$", dot}}522	newState.walk(dot, tmpl.Root)523}524525// Eval functions evaluate pipelines, commands, and their elements and extract526// values from the data structure by examining fields, calling methods, and so on.527// The printing of those values happens only through walk functions.528529// evalPipeline returns the value acquired by evaluating a pipeline. If the530// pipeline has a variable declaration, the variable will be pushed on the531// stack. Callers should therefore pop the stack after they are finished532// executing commands depending on the pipeline value.533func (s *state) evalPipeline(dot reflect.Value, pipe *parse.PipeNode) (value reflect.Value) {534	if pipe == nil {535		return536	}537	s.at(pipe)538	value = missingVal539	for _, cmd := range pipe.Cmds {540		value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.541		// If the object has type interface{}, dig down one level to the thing inside.542		if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {543			value = value.Elem()544		}545	}546	for _, variable := range pipe.Decl {547		if pipe.IsAssign {548			s.setVar(variable.Ident[0], value)549		} else {550			s.push(variable.Ident[0], value)551		}552	}553	return value554}555556func (s *state) notAFunction(args []parse.Node, final reflect.Value) {557	if len(args) > 1 || !isMissing(final) {558		s.errorf("can't give argument to non-function %s", args[0])559	}560}561562func (s *state) evalCommand(dot reflect.Value, cmd *parse.CommandNode, final reflect.Value) reflect.Value {563	firstWord := cmd.Args[0]564	switch n := firstWord.(type) {565	case *parse.FieldNode:566		return s.evalFieldNode(dot, n, cmd.Args, final)567	case *parse.ChainNode:568		return s.evalChainNode(dot, n, cmd.Args, final)569	case *parse.IdentifierNode:570		// Must be a function.571		return s.evalFunction(dot, n, cmd, cmd.Args, final)572	case *parse.PipeNode:573		// Parenthesized pipeline. The arguments are all inside the pipeline; final must be absent.574		s.notAFunction(cmd.Args, final)575		return s.evalPipeline(dot, n)576	case *parse.VariableNode:577		return s.evalVariableNode(dot, n, cmd.Args, final)578	}579	s.at(firstWord)580	s.notAFunction(cmd.Args, final)581	switch word := firstWord.(type) {582	case *parse.BoolNode:583		return reflect.ValueOf(word.True)584	case *parse.DotNode:585		return dot586	case *parse.NilNode:587		s.errorf("nil is not a command")588	case *parse.NumberNode:589		return s.idealConstant(word)590	case *parse.StringNode:591		return reflect.ValueOf(word.Text)592	}593	s.errorf("can't evaluate command %q", firstWord)594	panic("not reached")595}596597// idealConstant is called to return the value of a number in a context where598// we don't know the type. In that case, the syntax of the number tells us599// its type, and we use Go rules to resolve. Note there is no such thing as600// a uint ideal constant in this situation - the value must be of int type.601func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {602	// These are ideal constants but we don't know the type603	// and we have no context.  (If it was a method argument,604	// we'd know what we need.) The syntax guides us to some extent.605	s.at(constant)606	switch {607	case constant.IsComplex:608		return reflect.ValueOf(constant.Complex128) // incontrovertible.609610	case constant.IsFloat &&611		!isHexInt(constant.Text) && !isRuneInt(constant.Text) &&612		strings.ContainsAny(constant.Text, ".eEpP"):613		return reflect.ValueOf(constant.Float64)614615	case constant.IsInt:616		n := int(constant.Int64)617		if int64(n) != constant.Int64 {618			s.errorf("%s overflows int", constant.Text)619		}620		return reflect.ValueOf(n)621622	case constant.IsUint:623		s.errorf("%s overflows int", constant.Text)624	}625	return zero626}627628func isRuneInt(s string) bool {629	return len(s) > 0 && s[0] == '\''630}631632func isHexInt(s string) bool {633	return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && !strings.ContainsAny(s, "pP")634}635636func (s *state) evalFieldNode(dot reflect.Value, field *parse.FieldNode, args []parse.Node, final reflect.Value) reflect.Value {637	s.at(field)638	return s.evalFieldChain(dot, dot, field, field.Ident, args, final)639}640641func (s *state) evalChainNode(dot reflect.Value, chain *parse.ChainNode, args []parse.Node, final reflect.Value) reflect.Value {642	s.at(chain)643	if len(chain.Field) == 0 {644		s.errorf("internal error: no fields in evalChainNode")645	}646	if chain.Node.Type() == parse.NodeNil {647		s.errorf("indirection through explicit nil in %s", chain)648	}649	// (pipe).Field1.Field2 has pipe as .Node, fields as .Field. Eval the pipeline, then the fields.650	pipe := s.evalArg(dot, nil, chain.Node)651	return s.evalFieldChain(dot, pipe, chain, chain.Field, args, final)652}653654func (s *state) evalVariableNode(dot reflect.Value, variable *parse.VariableNode, args []parse.Node, final reflect.Value) reflect.Value {655	// $x.Field has $x as the first ident, Field as the second. Eval the var, then the fields.656	s.at(variable)657	value := s.varValue(variable.Ident[0])658	if len(variable.Ident) == 1 {659		s.notAFunction(args, final)660		return value661	}662	return s.evalFieldChain(dot, value, variable, variable.Ident[1:], args, final)663}664665// evalFieldChain evaluates .X.Y.Z possibly followed by arguments.666// dot is the environment in which to evaluate arguments, while667// receiver is the value being walked along the chain.668func (s *state) evalFieldChain(dot, receiver reflect.Value, node parse.Node, ident []string, args []parse.Node, final reflect.Value) reflect.Value {669	n := len(ident)670	for i := 0; i < n-1; i++ {671		receiver = s.evalField(dot, ident[i], node, nil, missingVal, receiver)672	}673	// Now if it's a method, it gets the arguments.674	return s.evalField(dot, ident[n-1], node, args, final, receiver)675}676677func (s *state) evalFunction(dot reflect.Value, node *parse.IdentifierNode, cmd parse.Node, args []parse.Node, final reflect.Value) reflect.Value {678	s.at(node)679	name := node.Ident680	function, isBuiltin, ok := findFunction(name, s.tmpl)681	if !ok {682		s.errorf("%q is not a defined function", name)683	}684	return s.evalCall(dot, function, isBuiltin, cmd, name, args, final)685}686687// evalField evaluates an expression like (.Field) or (.Field arg1 arg2).688// The 'final' argument represents the return value from the preceding689// value of the pipeline, if any.690func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value {691	if !receiver.IsValid() {692		if s.tmpl.option.missingKey == mapError { // Treat invalid value as missing map key.693			s.errorf("nil data; no entry for key %q", fieldName)694		}695		return zero696	}697	typ := receiver.Type()698	receiver, isNil := indirect(receiver)699	if receiver.Kind() == reflect.Interface && isNil {700		// Calling a method on a nil interface can't work. The701		// MethodByName method call below would panic.702		s.errorf("nil pointer evaluating %s.%s", typ, fieldName)703		return zero704	}705706	// Unless it's an interface, need to get to a value of type *T to guarantee707	// we see all methods of T and *T.708	ptr := receiver709	if ptr.Kind() != reflect.Interface && ptr.Kind() != reflect.Pointer && ptr.CanAddr() {710		ptr = ptr.Addr()711	}712	if method := ptr.MethodByName(fieldName); method.IsValid() {713		return s.evalCall(dot, method, false, node, fieldName, args, final)714	}715	hasArgs := len(args) > 1 || !isMissing(final)716	// It's not a method; must be a field of a struct or an element of a map.717	switch receiver.Kind() {718	case reflect.Struct:719		tField, ok := receiver.Type().FieldByName(fieldName)720		if ok {721			field, err := receiver.FieldByIndexErr(tField.Index)722			if !tField.IsExported() {723				s.errorf("%s is an unexported field of struct type %s", fieldName, typ)724			}725			if err != nil {726				s.errorf("%v", err)727			}728			// If it's a function, we must call it.729			if hasArgs {730				s.errorf("%s has arguments but cannot be invoked as function", fieldName)731			}732			return field733		}734	case reflect.Map:735		// If it's a map, attempt to use the field name as a key.736		nameVal := reflect.ValueOf(fieldName)737		if nameVal.Type().AssignableTo(receiver.Type().Key()) {738			if hasArgs {739				s.errorf("%s is not a method but has arguments", fieldName)740			}741			result := receiver.MapIndex(nameVal)742			if !result.IsValid() {743				switch s.tmpl.option.missingKey {744				case mapInvalid:745					// Just use the invalid value.746				case mapZeroValue:747					result = reflect.Zero(receiver.Type().Elem())748				case mapError:749					s.errorf("map has no entry for key %q", fieldName)750				}751			}752			return result753		}754	case reflect.Pointer:755		etyp := receiver.Type().Elem()756		if etyp.Kind() == reflect.Struct {757			if _, ok := etyp.FieldByName(fieldName); !ok {758				// If there's no such field, say "can't evaluate"759				// instead of "nil pointer evaluating".760				break761			}762		}763		if isNil {764			s.errorf("nil pointer evaluating %s.%s", typ, fieldName)765		}766	}767	s.errorf("can't evaluate field %s in type %s", fieldName, typ)768	panic("not reached")769}770771var (772	errorType        = reflect.TypeFor[error]()773	fmtStringerType  = reflect.TypeFor[fmt.Stringer]()774	reflectValueType = reflect.TypeFor[reflect.Value]()775)776777// evalCall executes a function or method call. If it's a method, fun already has the receiver bound, so778// it looks just like a function call. The arg list, if non-nil, includes (in the manner of the shell), arg[0]779// as the function itself.780func (s *state) evalCall(dot, fun reflect.Value, isBuiltin bool, node parse.Node, name string, args []parse.Node, final reflect.Value) reflect.Value {781	if args != nil {782		args = args[1:] // Zeroth arg is function name/node; not passed to function.783	}784	typ := fun.Type()785	numIn := len(args)786	if !isMissing(final) {787		numIn++788	}789	numFixed := len(args)790	if typ.IsVariadic() {791		numFixed = typ.NumIn() - 1 // last arg is the variadic one.792		if numIn < numFixed {793			s.errorf("wrong number of args for %s: want at least %d got %d", name, typ.NumIn()-1, len(args))794		}795	} else if numIn != typ.NumIn() {796		s.errorf("wrong number of args for %s: want %d got %d", name, typ.NumIn(), numIn)797	}798	if err := goodFunc(name, typ); err != nil {799		s.errorf("%v", err)800	}801802	unwrap := func(v reflect.Value) reflect.Value {803		if v.Type() == reflectValueType {804			v = v.Interface().(reflect.Value)805		}806		return v807	}808809	// Special case for builtin and/or, which short-circuit.810	if isBuiltin && (name == "and" || name == "or") {811		argType := typ.In(0)812		var v reflect.Value813		for _, arg := range args {814			v = s.evalArg(dot, argType, arg).Interface().(reflect.Value)815			if truth(v) == (name == "or") {816				// This value was already unwrapped817				// by the .Interface().(reflect.Value).818				return v819			}820		}821		if !final.Equal(missingVal) {822			// The last argument to and/or is coming from823			// the pipeline. We didn't short circuit on an earlier824			// argument, so we are going to return this one.825			// We don't have to evaluate final, but we do826			// have to check its type. Then, since we are827			// going to return it, we have to unwrap it.828			v = unwrap(s.validateType(final, argType))829		}830		return v831	}832833	// Build the arg list.834	argv := make([]reflect.Value, numIn)835	// Args must be evaluated. Fixed args first.836	i := 0837	for ; i < numFixed && i < len(args); i++ {838		argv[i] = s.evalArg(dot, typ.In(i), args[i])839	}840	// Now the ... args.841	if typ.IsVariadic() {842		argType := typ.In(typ.NumIn() - 1).Elem() // Argument is a slice.843		for ; i < len(args); i++ {844			argv[i] = s.evalArg(dot, argType, args[i])845		}846	}847	// Add final value if necessary.848	if !isMissing(final) {849		t := typ.In(typ.NumIn() - 1)850		if typ.IsVariadic() {851			if numIn-1 < numFixed {852				// The added final argument corresponds to a fixed parameter of the function.853				// Validate against the type of the actual parameter.854				t = typ.In(numIn - 1)855			} else {856				// The added final argument corresponds to the variadic part.857				// Validate against the type of the elements of the variadic slice.858				t = t.Elem()859			}860		}861		argv[i] = s.validateType(final, t)862	}863864	// Special case for the "call" builtin.865	// Insert the name of the callee function as the first argument.866	if isBuiltin && name == "call" {867		var calleeName string868		if len(args) == 0 {869			// final must be present or we would have errored out above.870			calleeName = final.String()871		} else {872			calleeName = args[0].String()873		}874		argv = append([]reflect.Value{reflect.ValueOf(calleeName)}, argv...)875		fun = reflect.ValueOf(call)876	}877878	v, err := safeCall(fun, argv)879	// If we have an error that is not nil, stop execution and return that880	// error to the caller.881	if err != nil {882		s.at(node)883		s.errorf("error calling %s: %w", name, err)884	}885	return unwrap(v)886}887888// canBeNil reports whether an untyped nil can be assigned to the type. See reflect.Zero.889func canBeNil(typ reflect.Type) bool {890	switch typ.Kind() {891	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:892		return true893	case reflect.Struct:894		return typ == reflectValueType895	}896	return false897}898899// validateType guarantees that the value is valid and assignable to the type.900func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Value {901	if !value.IsValid() {902		if typ == nil {903			// An untyped nil interface{}. Accept as a proper nil value.904			return reflect.ValueOf(nil)905		}906		if canBeNil(typ) {907			// Like above, but use the zero value of the non-nil type.908			return reflect.Zero(typ)909		}910		s.errorf("invalid value; expected %s", typ)911	}912	if typ == reflectValueType && value.Type() != typ {913		return reflect.ValueOf(value)914	}915	if typ != nil && !value.Type().AssignableTo(typ) {916		if value.Kind() == reflect.Interface && !value.IsNil() {917			value = value.Elem()918			if value.Type().AssignableTo(typ) {919				return value920			}921			// fallthrough922		}923		// Does one dereference or indirection work? We could do more, as we924		// do with method receivers, but that gets messy and method receivers925		// are much more constrained, so it makes more sense there than here.926		// Besides, one is almost always all you need.927		switch {928		case value.Kind() == reflect.Pointer && value.Type().Elem().AssignableTo(typ):929			value = value.Elem()930			if !value.IsValid() {931				s.errorf("dereference of nil pointer of type %s", typ)932			}933		case reflect.PointerTo(value.Type()).AssignableTo(typ) && value.CanAddr():934			value = value.Addr()935		default:936			s.errorf("wrong type for value; expected %s; got %s", typ, value.Type())937		}938	}939	return value940}941942func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) reflect.Value {943	s.at(n)944	switch arg := n.(type) {945	case *parse.DotNode:946		return s.validateType(dot, typ)947	case *parse.NilNode:948		if canBeNil(typ) {949			return reflect.Zero(typ)950		}951		s.errorf("cannot assign nil to %s", typ)952	case *parse.FieldNode:953		return s.validateType(s.evalFieldNode(dot, arg, []parse.Node{n}, missingVal), typ)954	case *parse.VariableNode:955		return s.validateType(s.evalVariableNode(dot, arg, nil, missingVal), typ)956	case *parse.PipeNode:957		return s.validateType(s.evalPipeline(dot, arg), typ)958	case *parse.IdentifierNode:959		return s.validateType(s.evalFunction(dot, arg, arg, nil, missingVal), typ)960	case *parse.ChainNode:961		return s.validateType(s.evalChainNode(dot, arg, nil, missingVal), typ)962	}963	switch typ.Kind() {964	case reflect.Bool:965		return s.evalBool(typ, n)966	case reflect.Complex64, reflect.Complex128:967		return s.evalComplex(typ, n)968	case reflect.Float32, reflect.Float64:969		return s.evalFloat(typ, n)970	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:971		return s.evalInteger(typ, n)972	case reflect.Interface:973		if typ.NumMethod() == 0 {974			return s.evalEmptyInterface(dot, n)975		}976	case reflect.Struct:977		if typ == reflectValueType {978			return reflect.ValueOf(s.evalEmptyInterface(dot, n))979		}980	case reflect.String:981		return s.evalString(typ, n)982	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:983		return s.evalUnsignedInteger(typ, n)984	}985	s.errorf("can't handle %s for arg of type %s", n, typ)986	panic("not reached")987}988989func (s *state) evalBool(typ reflect.Type, n parse.Node) reflect.Value {990	s.at(n)991	if n, ok := n.(*parse.BoolNode); ok {992		value := reflect.New(typ).Elem()993		value.SetBool(n.True)994		return value995	}996	s.errorf("expected bool; found %s", n)997	panic("not reached")998}9991000func (s *state) evalString(typ reflect.Type, n parse.Node) reflect.Value {1001	s.at(n)1002	if n, ok := n.(*parse.StringNode); ok {1003		value := reflect.New(typ).Elem()1004		value.SetString(n.Text)1005		return value1006	}1007	s.errorf("expected string; found %s", n)1008	panic("not reached")1009}10101011func (s *state) evalInteger(typ reflect.Type, n parse.Node) reflect.Value {1012	s.at(n)1013	if n, ok := n.(*parse.NumberNode); ok && n.IsInt {1014		value := reflect.New(typ).Elem()1015		value.SetInt(n.Int64)1016		return value1017	}1018	s.errorf("expected integer; found %s", n)1019	panic("not reached")1020}10211022func (s *state) evalUnsignedInteger(typ reflect.Type, n parse.Node) reflect.Value {1023	s.at(n)1024	if n, ok := n.(*parse.NumberNode); ok && n.IsUint {1025		value := reflect.New(typ).Elem()1026		value.SetUint(n.Uint64)1027		return value1028	}1029	s.errorf("expected unsigned integer; found %s", n)1030	panic("not reached")1031}10321033func (s *state) evalFloat(typ reflect.Type, n parse.Node) reflect.Value {1034	s.at(n)1035	if n, ok := n.(*parse.NumberNode); ok && n.IsFloat {1036		value := reflect.New(typ).Elem()1037		value.SetFloat(n.Float64)1038		return value1039	}1040	s.errorf("expected float; found %s", n)1041	panic("not reached")1042}10431044func (s *state) evalComplex(typ reflect.Type, n parse.Node) reflect.Value {1045	if n, ok := n.(*parse.NumberNode); ok && n.IsComplex {1046		value := reflect.New(typ).Elem()1047		value.SetComplex(n.Complex128)1048		return value1049	}1050	s.errorf("expected complex; found %s", n)1051	panic("not reached")1052}10531054func (s *state) evalEmptyInterface(dot reflect.Value, n parse.Node) reflect.Value {1055	s.at(n)1056	switch n := n.(type) {1057	case *parse.BoolNode:1058		return reflect.ValueOf(n.True)1059	case *parse.DotNode:1060		return dot1061	case *parse.FieldNode:1062		return s.evalFieldNode(dot, n, nil, missingVal)1063	case *parse.IdentifierNode:1064		return s.evalFunction(dot, n, n, nil, missingVal)1065	case *parse.NilNode:1066		// NilNode is handled in evalArg, the only place that calls here.1067		s.errorf("evalEmptyInterface: nil (can't happen)")1068	case *parse.NumberNode:1069		return s.idealConstant(n)1070	case *parse.StringNode:1071		return reflect.ValueOf(n.Text)1072	case *parse.VariableNode:1073		return s.evalVariableNode(dot, n, nil, missingVal)1074	case *parse.PipeNode:1075		return s.evalPipeline(dot, n)1076	}1077	s.errorf("can't handle assignment of %s to empty interface argument", n)1078	panic("not reached")1079}10801081// indirect returns the item at the end of indirection, and a bool to indicate1082// if it's nil. If the returned bool is true, the returned value's kind will be1083// either a pointer or interface.1084func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {1085	for ; v.Kind() == reflect.Pointer || v.Kind() == reflect.Interface; v = v.Elem() {1086		if v.IsNil() {1087			return v, true1088		}1089	}1090	return v, false1091}10921093// indirectInterface returns the concrete value in an interface value,1094// or else the zero reflect.Value.1095// That is, if v represents the interface value x, the result is the same as reflect.ValueOf(x):1096// the fact that x was an interface value is forgotten.1097func indirectInterface(v reflect.Value) reflect.Value {1098	if v.Kind() != reflect.Interface {1099		return v1100	}1101	if v.IsNil() {1102		return reflect.Value{}1103	}1104	return v.Elem()1105}11061107// printValue writes the textual representation of the value to the output of1108// the template.1109func (s *state) printValue(n parse.Node, v reflect.Value) {1110	s.at(n)1111	iface, ok := printableValue(v)1112	if !ok {1113		s.errorf("can't print %s of type %s", n, v.Type())1114	}1115	_, err := fmt.Fprint(s.wr, iface)1116	if err != nil {1117		s.writeError(err)1118	}1119}11201121// printableValue returns the, possibly indirected, interface value inside v that1122// is best for a call to formatted printer.1123func printableValue(v reflect.Value) (any, bool) {1124	if v.Kind() == reflect.Pointer {1125		v, _ = indirect(v) // fmt.Fprint handles nil.1126	}1127	if !v.IsValid() {1128		return "<no value>", true1129	}11301131	if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) {1132		if v.CanAddr() && (reflect.PointerTo(v.Type()).Implements(errorType) || reflect.PointerTo(v.Type()).Implements(fmtStringerType)) {1133			v = v.Addr()1134		} else {1135			switch v.Kind() {1136			case reflect.Chan, reflect.Func:1137				return nil, false1138			}1139		}1140	}1141	return v.Interface(), true1142}

Code quality findings 15

Empty interface; prefer specific types or generics for type safety
empty-interface
// Something like var x interface{}, never set. It's a form of nil.
Empty interface; prefer specific types or generics for type safety
empty-interface
// If the object has type interface{}, dig down one level to the thing inside.
Empty interface; prefer specific types or generics for type safety
empty-interface
// An untyped nil interface{}. Accept as a proper nil value.
May hide panics instead of handling errors properly; use only with specific panic recovery logic
warning correctness recover-without-defer
e := recover()
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer t.muTmpl.RUnlock()
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Ensure errors are handled or logged
warning correctness unhandled-error
if err != nil {
Blank identifier discarding results; verify intentional ignoring of return values
warning correctness blank-identifier-discard
v, _ = indirect(v) // fmt.Fprint handles nil.
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for name, tmpl := range t.tmpl {
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch node := node.(type) {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, v := range val.Seq2() {
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch n := firstWord.(type) {
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch word := firstWord.(type) {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
argv = append([]reflect.Value{reflect.ValueOf(calleeName)}, argv...)
Type switch without default case; unhandled types will silently do nothing. Add a default case for safety
info correctness unchecked-type-switch
switch n := n.(type) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.