k8s.io/gengo@v0.0.0-20240225000952-471f57d40f57/examples/defaulter-gen/generators/defaulter.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package generators
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"path/filepath"
    25  	"reflect"
    26  	"regexp"
    27  	"strconv"
    28  	"strings"
    29  
    30  	"k8s.io/gengo/args"
    31  	"k8s.io/gengo/generator"
    32  	"k8s.io/gengo/namer"
    33  	"k8s.io/gengo/types"
    34  
    35  	"k8s.io/klog/v2"
    36  )
    37  
    38  // CustomArgs is used tby the go2idl framework to pass args specific to this
    39  // generator.
    40  type CustomArgs struct {
    41  	ExtraPeerDirs []string // Always consider these as last-ditch possibilities for conversions.
    42  }
    43  
    44  var typeZeroValue = map[string]interface{}{
    45  	"uint":        0.,
    46  	"uint8":       0.,
    47  	"uint16":      0.,
    48  	"uint32":      0.,
    49  	"uint64":      0.,
    50  	"int":         0.,
    51  	"int8":        0.,
    52  	"int16":       0.,
    53  	"int32":       0.,
    54  	"int64":       0.,
    55  	"byte":        0.,
    56  	"float64":     0.,
    57  	"float32":     0.,
    58  	"bool":        false,
    59  	"time.Time":   "",
    60  	"string":      "",
    61  	"integer":     0.,
    62  	"number":      0.,
    63  	"boolean":     false,
    64  	"[]byte":      "", // base64 encoded characters
    65  	"interface{}": interface{}(nil),
    66  }
    67  
    68  // These are the comment tags that carry parameters for defaulter generation.
    69  const tagName = "k8s:defaulter-gen"
    70  const inputTagName = "k8s:defaulter-gen-input"
    71  const defaultTagName = "default"
    72  
    73  func extractDefaultTag(comments []string) []string {
    74  	return types.ExtractCommentTags("+", comments)[defaultTagName]
    75  }
    76  
    77  func extractTag(comments []string) []string {
    78  	return types.ExtractCommentTags("+", comments)[tagName]
    79  }
    80  
    81  func extractInputTag(comments []string) []string {
    82  	return types.ExtractCommentTags("+", comments)[inputTagName]
    83  }
    84  
    85  func checkTag(comments []string, require ...string) bool {
    86  	values := types.ExtractCommentTags("+", comments)[tagName]
    87  	if len(require) == 0 {
    88  		return len(values) == 1 && values[0] == ""
    89  	}
    90  	return reflect.DeepEqual(values, require)
    91  }
    92  
    93  func defaultFnNamer() *namer.NameStrategy {
    94  	return &namer.NameStrategy{
    95  		Prefix: "SetDefaults_",
    96  		Join: func(pre string, in []string, post string) string {
    97  			return pre + strings.Join(in, "_") + post
    98  		},
    99  	}
   100  }
   101  
   102  func objectDefaultFnNamer() *namer.NameStrategy {
   103  	return &namer.NameStrategy{
   104  		Prefix: "SetObjectDefaults_",
   105  		Join: func(pre string, in []string, post string) string {
   106  			return pre + strings.Join(in, "_") + post
   107  		},
   108  	}
   109  }
   110  
   111  // NameSystems returns the name system used by the generators in this package.
   112  func NameSystems() namer.NameSystems {
   113  	return namer.NameSystems{
   114  		"public":          namer.NewPublicNamer(1),
   115  		"raw":             namer.NewRawNamer("", nil),
   116  		"defaultfn":       defaultFnNamer(),
   117  		"objectdefaultfn": objectDefaultFnNamer(),
   118  	}
   119  }
   120  
   121  // DefaultNameSystem returns the default name system for ordering the types to be
   122  // processed by the generators in this package.
   123  func DefaultNameSystem() string {
   124  	return "public"
   125  }
   126  
   127  // defaults holds the declared defaulting functions for a given type (all defaulting functions
   128  // are expected to be func(1))
   129  type defaults struct {
   130  	// object is the defaulter function for a top level type (typically one with TypeMeta) that
   131  	// invokes all child defaulters. May be nil if the object defaulter has not yet been generated.
   132  	object *types.Type
   133  	// base is a defaulter function defined for a type SetDefaults_Pod which does not invoke all
   134  	// child defaults - the base defaulter alone is insufficient to default a type
   135  	base *types.Type
   136  	// additional is zero or more defaulter functions of the form SetDefaults_Pod_XXXX that can be
   137  	// included in the Object defaulter.
   138  	additional []*types.Type
   139  }
   140  
   141  // All of the types in conversions map are of type "DeclarationOf" with
   142  // the underlying type being "Func".
   143  type defaulterFuncMap map[*types.Type]defaults
   144  
   145  // Returns all manually-defined defaulting functions in the package.
   146  func getManualDefaultingFunctions(context *generator.Context, pkg *types.Package, manualMap defaulterFuncMap) {
   147  	buffer := &bytes.Buffer{}
   148  	sw := generator.NewSnippetWriter(buffer, context, "$", "$")
   149  
   150  	for _, f := range pkg.Functions {
   151  		if f.Underlying == nil || f.Underlying.Kind != types.Func {
   152  			klog.Errorf("Malformed function: %#v", f)
   153  			continue
   154  		}
   155  		if f.Underlying.Signature == nil {
   156  			klog.Errorf("Function without signature: %#v", f)
   157  			continue
   158  		}
   159  		signature := f.Underlying.Signature
   160  		// Check whether the function is defaulting function.
   161  		// Note that all of them have signature:
   162  		// object: func SetObjectDefaults_inType(*inType)
   163  		// base: func SetDefaults_inType(*inType)
   164  		// additional: func SetDefaults_inType_Qualifier(*inType)
   165  		if signature.Receiver != nil {
   166  			continue
   167  		}
   168  		if len(signature.Parameters) != 1 {
   169  			continue
   170  		}
   171  		if len(signature.Results) != 0 {
   172  			continue
   173  		}
   174  		inType := signature.Parameters[0]
   175  		if inType.Kind != types.Pointer {
   176  			continue
   177  		}
   178  		// Check if this is the primary defaulter.
   179  		args := defaultingArgsFromType(inType.Elem)
   180  		sw.Do("$.inType|defaultfn$", args)
   181  		switch {
   182  		case f.Name.Name == buffer.String():
   183  			key := inType.Elem
   184  			// We might scan the same package twice, and that's OK.
   185  			v, ok := manualMap[key]
   186  			if ok && v.base != nil && v.base.Name.Package != pkg.Path {
   187  				panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key))
   188  			}
   189  			v.base = f
   190  			manualMap[key] = v
   191  			klog.V(6).Infof("found base defaulter function for %s from %s", key.Name, f.Name)
   192  		// Is one of the additional defaulters - a top level defaulter on a type that is
   193  		// also invoked.
   194  		case strings.HasPrefix(f.Name.Name, buffer.String()+"_"):
   195  			key := inType.Elem
   196  			v, ok := manualMap[key]
   197  			if ok {
   198  				exists := false
   199  				for _, existing := range v.additional {
   200  					if existing.Name == f.Name {
   201  						exists = true
   202  						break
   203  					}
   204  				}
   205  				if exists {
   206  					continue
   207  				}
   208  			}
   209  			v.additional = append(v.additional, f)
   210  			manualMap[key] = v
   211  			klog.V(6).Infof("found additional defaulter function for %s from %s", key.Name, f.Name)
   212  		}
   213  		buffer.Reset()
   214  		sw.Do("$.inType|objectdefaultfn$", args)
   215  		if f.Name.Name == buffer.String() {
   216  			key := inType.Elem
   217  			// We might scan the same package twice, and that's OK.
   218  			v, ok := manualMap[key]
   219  			if ok && v.base != nil && v.base.Name.Package != pkg.Path {
   220  				panic(fmt.Sprintf("duplicate static defaulter defined: %#v", key))
   221  			}
   222  			v.object = f
   223  			manualMap[key] = v
   224  			klog.V(6).Infof("found object defaulter function for %s from %s", key.Name, f.Name)
   225  		}
   226  		buffer.Reset()
   227  	}
   228  }
   229  
   230  func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
   231  	boilerplate, err := arguments.LoadGoBoilerplate()
   232  	if err != nil {
   233  		klog.Fatalf("Failed loading boilerplate: %v", err)
   234  	}
   235  
   236  	packages := generator.Packages{}
   237  	header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
   238  
   239  	// Accumulate pre-existing default functions.
   240  	// TODO: This is too ad-hoc.  We need a better way.
   241  	existingDefaulters := defaulterFuncMap{}
   242  
   243  	buffer := &bytes.Buffer{}
   244  	sw := generator.NewSnippetWriter(buffer, context, "$", "$")
   245  
   246  	// We are generating defaults only for packages that are explicitly
   247  	// passed as InputDir.
   248  	for _, i := range context.Inputs {
   249  		klog.V(5).Infof("considering pkg %q", i)
   250  		pkg := context.Universe[i]
   251  		if pkg == nil {
   252  			// If the input had no Go files, for example.
   253  			continue
   254  		}
   255  		// typesPkg is where the types that needs defaulter are defined.
   256  		// Sometimes it is different from pkg. For example, kubernetes core/v1
   257  		// types are defined in vendor/k8s.io/api/core/v1, while pkg is at
   258  		// pkg/api/v1.
   259  		typesPkg := pkg
   260  
   261  		// Add defaulting functions.
   262  		getManualDefaultingFunctions(context, pkg, existingDefaulters)
   263  
   264  		var peerPkgs []string
   265  		if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
   266  			for _, pkg := range customArgs.ExtraPeerDirs {
   267  				if i := strings.Index(pkg, "/vendor/"); i != -1 {
   268  					pkg = pkg[i+len("/vendor/"):]
   269  				}
   270  				peerPkgs = append(peerPkgs, pkg)
   271  			}
   272  		}
   273  		// Make sure our peer-packages are added and fully parsed.
   274  		for _, pp := range peerPkgs {
   275  			context.AddDir(pp)
   276  			getManualDefaultingFunctions(context, context.Universe[pp], existingDefaulters)
   277  		}
   278  
   279  		typesWith := extractTag(pkg.Comments)
   280  		shouldCreateObjectDefaulterFn := func(t *types.Type) bool {
   281  			if defaults, ok := existingDefaulters[t]; ok && defaults.object != nil {
   282  				// A default generator is defined
   283  				baseTypeName := "<unknown>"
   284  				if defaults.base != nil {
   285  					baseTypeName = defaults.base.Name.String()
   286  				}
   287  				klog.V(5).Infof("  an object defaulter already exists as %s", baseTypeName)
   288  				return false
   289  			}
   290  			// opt-out
   291  			if checkTag(t.SecondClosestCommentLines, "false") {
   292  				return false
   293  			}
   294  			// opt-in
   295  			if checkTag(t.SecondClosestCommentLines, "true") {
   296  				return true
   297  			}
   298  			// For every k8s:defaulter-gen tag at the package level, interpret the value as a
   299  			// field name (like TypeMeta, ListMeta, ObjectMeta) and trigger defaulter generation
   300  			// for any type with any of the matching field names. Provides a more useful package
   301  			// level defaulting than global (because we only need defaulters on a subset of objects -
   302  			// usually those with TypeMeta).
   303  			if t.Kind == types.Struct && len(typesWith) > 0 {
   304  				for _, field := range t.Members {
   305  					for _, s := range typesWith {
   306  						if field.Name == s {
   307  							return true
   308  						}
   309  					}
   310  				}
   311  			}
   312  			return false
   313  		}
   314  
   315  		// if the types are not in the same package where the defaulter functions to be generated
   316  		inputTags := extractInputTag(pkg.Comments)
   317  		if len(inputTags) > 1 {
   318  			panic(fmt.Sprintf("there could only be one input tag, got %#v", inputTags))
   319  		}
   320  		if len(inputTags) == 1 {
   321  			var err error
   322  
   323  			inputPath := inputTags[0]
   324  			if strings.HasPrefix(inputPath, "./") || strings.HasPrefix(inputPath, "../") {
   325  				// this is a relative dir, which will not work under gomodules.
   326  				// join with the local package path, but warn
   327  				klog.Warningf("relative path %s=%s will not work under gomodule mode; use full package path (as used by 'import') instead", inputTagName, inputPath)
   328  				inputPath = filepath.Join(pkg.Path, inputTags[0])
   329  			}
   330  
   331  			typesPkg, err = context.AddDirectory(inputPath)
   332  			if err != nil {
   333  				klog.Fatalf("cannot import package %s", inputPath)
   334  			}
   335  			// update context.Order to the latest context.Universe
   336  			orderer := namer.Orderer{Namer: namer.NewPublicNamer(1)}
   337  			context.Order = orderer.OrderUniverse(context.Universe)
   338  		}
   339  
   340  		newDefaulters := defaulterFuncMap{}
   341  		for _, t := range typesPkg.Types {
   342  			if !shouldCreateObjectDefaulterFn(t) {
   343  				continue
   344  			}
   345  			if namer.IsPrivateGoName(t.Name.Name) {
   346  				// We won't be able to convert to a private type.
   347  				klog.V(5).Infof("  found a type %v, but it is a private name", t)
   348  				continue
   349  			}
   350  
   351  			// create a synthetic type we can use during generation
   352  			newDefaulters[t] = defaults{}
   353  		}
   354  
   355  		// only generate defaulters for objects that actually have defined defaulters
   356  		// prevents empty defaulters from being registered
   357  		for {
   358  			promoted := 0
   359  			for t, d := range newDefaulters {
   360  				if d.object != nil {
   361  					continue
   362  				}
   363  				if newCallTreeForType(existingDefaulters, newDefaulters).build(t, true) != nil {
   364  					args := defaultingArgsFromType(t)
   365  					sw.Do("$.inType|objectdefaultfn$", args)
   366  					newDefaulters[t] = defaults{
   367  						object: &types.Type{
   368  							Name: types.Name{
   369  								Package: pkg.Path,
   370  								Name:    buffer.String(),
   371  							},
   372  							Kind: types.Func,
   373  						},
   374  					}
   375  					buffer.Reset()
   376  					promoted++
   377  				}
   378  			}
   379  			if promoted != 0 {
   380  				continue
   381  			}
   382  
   383  			// prune any types that were not used
   384  			for t, d := range newDefaulters {
   385  				if d.object == nil {
   386  					klog.V(6).Infof("did not generate defaulter for %s because no child defaulters were registered", t.Name)
   387  					delete(newDefaulters, t)
   388  				}
   389  			}
   390  			break
   391  		}
   392  
   393  		if len(newDefaulters) == 0 {
   394  			klog.V(5).Infof("no defaulters in package %s", pkg.Name)
   395  		}
   396  
   397  		path := pkg.Path
   398  		// if the source path is within a /vendor/ directory (for example,
   399  		// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
   400  		// generation to output to the proper relative path (under vendor).
   401  		// Otherwise, the generator will create the file in the wrong location
   402  		// in the output directory.
   403  		// TODO: build a more fundamental concept in gengo for dealing with modifications
   404  		// to vendored packages.
   405  		if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) {
   406  			expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase)
   407  			if strings.Contains(expandedPath, "/vendor/") {
   408  				path = expandedPath
   409  			}
   410  		}
   411  
   412  		packages = append(packages,
   413  			&generator.DefaultPackage{
   414  				PackageName: filepath.Base(pkg.Path),
   415  				PackagePath: path,
   416  				HeaderText:  header,
   417  				GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
   418  					return []generator.Generator{
   419  						NewGenDefaulter(arguments.OutputFileBaseName, typesPkg.Path, pkg.Path, existingDefaulters, newDefaulters, peerPkgs),
   420  					}
   421  				},
   422  				FilterFunc: func(c *generator.Context, t *types.Type) bool {
   423  					return t.Name.Package == typesPkg.Path
   424  				},
   425  			})
   426  	}
   427  	return packages
   428  }
   429  
   430  // callTreeForType contains fields necessary to build a tree for types.
   431  type callTreeForType struct {
   432  	existingDefaulters     defaulterFuncMap
   433  	newDefaulters          defaulterFuncMap
   434  	currentlyBuildingTypes map[*types.Type]bool
   435  }
   436  
   437  func newCallTreeForType(existingDefaulters, newDefaulters defaulterFuncMap) *callTreeForType {
   438  	return &callTreeForType{
   439  		existingDefaulters:     existingDefaulters,
   440  		newDefaulters:          newDefaulters,
   441  		currentlyBuildingTypes: make(map[*types.Type]bool),
   442  	}
   443  }
   444  
   445  // resolveType follows pointers and aliases of `t` until reaching the first
   446  // non-pointer type in `t's` herarchy
   447  func resolveTypeAndDepth(t *types.Type) (*types.Type, int) {
   448  	var prev *types.Type
   449  	depth := 0
   450  	for prev != t {
   451  		prev = t
   452  		if t.Kind == types.Alias {
   453  			t = t.Underlying
   454  		} else if t.Kind == types.Pointer {
   455  			t = t.Elem
   456  			depth += 1
   457  		}
   458  	}
   459  	return t, depth
   460  }
   461  
   462  // getPointerElementPath follows pointers and aliases to returns all
   463  // pointer elements in the path from the given type, to its base value type.
   464  //
   465  // Example:
   466  //
   467  //	type MyString string
   468  //	type MyStringPointer *MyString
   469  //	type MyStringPointerPointer *MyStringPointer
   470  //	type MyStringAlias MyStringPointer
   471  //	type MyStringAliasPointer *MyStringAlias
   472  //	type MyStringAliasDoublePointer **MyStringAlias
   473  //
   474  //	t		  				   | defaultPointerElementPath(t)
   475  //	---------------------------|----------------------------------------
   476  //	MyString                   | []
   477  //	MyStringPointer            | [MyString]
   478  //	MyStringPointerPointer     | [MyStringPointer, MyString]
   479  //	MyStringAlias              | [MyStringPointer, MyString]
   480  //	MyStringAliasPointer       | [MyStringAlias, MyStringPointer, MyString]
   481  //	MyStringAliasDoublePointer | [*MyStringAlias, MyStringAlias, MyStringPointer, MyString]
   482  func getPointerElementPath(t *types.Type) []*types.Type {
   483  	var path []*types.Type
   484  	for t != nil {
   485  		switch t.Kind {
   486  		case types.Alias:
   487  			t = t.Underlying
   488  		case types.Pointer:
   489  			t = t.Elem
   490  			path = append(path, t)
   491  		default:
   492  			t = nil
   493  		}
   494  	}
   495  	return path
   496  }
   497  
   498  // getNestedDefault returns the first default value when resolving alias types
   499  func getNestedDefault(t *types.Type) string {
   500  	var prev *types.Type
   501  	for prev != t {
   502  		prev = t
   503  		defaultMap := extractDefaultTag(t.CommentLines)
   504  		if len(defaultMap) == 1 && defaultMap[0] != "" {
   505  			return defaultMap[0]
   506  		}
   507  		if t.Kind == types.Alias {
   508  			t = t.Underlying
   509  		} else if t.Kind == types.Pointer {
   510  			t = t.Elem
   511  		}
   512  	}
   513  	return ""
   514  }
   515  
   516  var refRE = regexp.MustCompile(`^ref\((?P<reference>[^"]+)\)$`)
   517  var refREIdentIndex = refRE.SubexpIndex("reference")
   518  
   519  // ParseSymbolReference looks for strings that match one of the following:
   520  //   - ref(Ident)
   521  //   - ref(pkgpath.Ident)
   522  //     If the input string matches either of these, it will return the (optional)
   523  //     pkgpath, the Ident, and true.  Otherwise it will return empty strings and
   524  //     false.
   525  func ParseSymbolReference(s, sourcePackage string) (types.Name, bool) {
   526  	matches := refRE.FindStringSubmatch(s)
   527  	if len(matches) < refREIdentIndex || matches[refREIdentIndex] == "" {
   528  		return types.Name{}, false
   529  	}
   530  
   531  	contents := matches[refREIdentIndex]
   532  	name := types.ParseFullyQualifiedName(contents)
   533  	if len(name.Package) == 0 {
   534  		name.Package = sourcePackage
   535  	}
   536  	return name, true
   537  }
   538  
   539  func populateDefaultValue(node *callNode, t *types.Type, tags string, commentLines []string, commentPackage string) *callNode {
   540  	defaultMap := extractDefaultTag(commentLines)
   541  	var defaultString string
   542  	if len(defaultMap) == 1 {
   543  		defaultString = defaultMap[0]
   544  	} else if len(defaultMap) > 1 {
   545  		klog.Fatalf("Found more than one default tag for %v", t.Kind)
   546  	}
   547  
   548  	baseT, depth := resolveTypeAndDepth(t)
   549  	if depth > 0 && defaultString == "" {
   550  		defaultString = getNestedDefault(t)
   551  	}
   552  
   553  	if len(defaultString) == 0 {
   554  		return node
   555  	}
   556  	var symbolReference types.Name
   557  	var defaultValue interface{}
   558  	if id, ok := ParseSymbolReference(defaultString, commentPackage); ok {
   559  		symbolReference = id
   560  		defaultString = ""
   561  	} else if err := json.Unmarshal([]byte(defaultString), &defaultValue); err != nil {
   562  		klog.Fatalf("Failed to unmarshal default: %v", err)
   563  	}
   564  
   565  	if defaultValue != nil {
   566  		zero := typeZeroValue[t.String()]
   567  		if reflect.DeepEqual(defaultValue, zero) {
   568  			// If the default value annotation matches the default value for the type,
   569  			// do not generate any defaulting function
   570  			return node
   571  		}
   572  	}
   573  
   574  	// callNodes are not automatically generated for primitive types. Generate one if the callNode does not exist
   575  	if node == nil {
   576  		node = &callNode{}
   577  		node.markerOnly = true
   578  	}
   579  
   580  	node.defaultIsPrimitive = baseT.IsPrimitive()
   581  	node.defaultType = baseT
   582  	node.defaultTopLevelType = t
   583  	node.defaultValue.InlineConstant = defaultString
   584  	node.defaultValue.SymbolReference = symbolReference
   585  	return node
   586  }
   587  
   588  // build creates a tree of paths to fields (based on how they would be accessed in Go - pointer, elem,
   589  // slice, or key) and the functions that should be invoked on each field. An in-order traversal of the resulting tree
   590  // can be used to generate a Go function that invokes each nested function on the appropriate type. The return
   591  // value may be nil if there are no functions to call on type or the type is a primitive (Defaulters can only be
   592  // invoked on structs today). When root is true this function will not use a newDefaulter. existingDefaulters should
   593  // contain all defaulting functions by type defined in code - newDefaulters should contain all object defaulters
   594  // that could be or will be generated. If newDefaulters has an entry for a type, but the 'object' field is nil,
   595  // this function skips adding that defaulter - this allows us to avoid generating object defaulter functions for
   596  // list types that call empty defaulters.
   597  func (c *callTreeForType) build(t *types.Type, root bool) *callNode {
   598  	parent := &callNode{}
   599  
   600  	if root {
   601  		// the root node is always a pointer
   602  		parent.elem = true
   603  	}
   604  
   605  	defaults, _ := c.existingDefaulters[t]
   606  	newDefaults, generated := c.newDefaulters[t]
   607  	switch {
   608  	case !root && generated && newDefaults.object != nil:
   609  		parent.call = append(parent.call, newDefaults.object)
   610  		// if we will be generating the defaulter, it by definition is a covering
   611  		// defaulter, so we halt recursion
   612  		klog.V(6).Infof("the defaulter %s will be generated as an object defaulter", t.Name)
   613  		return parent
   614  
   615  	case defaults.object != nil:
   616  		// object defaulters are always covering
   617  		parent.call = append(parent.call, defaults.object)
   618  		return parent
   619  
   620  	case defaults.base != nil:
   621  		parent.call = append(parent.call, defaults.base)
   622  		// if the base function indicates it "covers" (it already includes defaulters)
   623  		// we can halt recursion
   624  		if checkTag(defaults.base.CommentLines, "covers") {
   625  			klog.V(6).Infof("the defaulter %s indicates it covers all sub generators", t.Name)
   626  			return parent
   627  		}
   628  	}
   629  
   630  	// base has been added already, now add any additional defaulters defined for this object
   631  	parent.call = append(parent.call, defaults.additional...)
   632  
   633  	// if the type already exists, don't build the tree for it and don't generate anything.
   634  	// This is used to avoid recursion for nested recursive types.
   635  	if c.currentlyBuildingTypes[t] {
   636  		return nil
   637  	}
   638  	// if type doesn't exist, mark it as existing
   639  	c.currentlyBuildingTypes[t] = true
   640  
   641  	defer func() {
   642  		// The type will now acts as a parent, not a nested recursive type.
   643  		// We can now build the tree for it safely.
   644  		c.currentlyBuildingTypes[t] = false
   645  	}()
   646  
   647  	switch t.Kind {
   648  	case types.Pointer:
   649  		if child := c.build(t.Elem, false); child != nil {
   650  			child.elem = true
   651  			parent.children = append(parent.children, *child)
   652  		}
   653  	case types.Slice, types.Array:
   654  		if child := c.build(t.Elem, false); child != nil {
   655  			child.index = true
   656  			if t.Elem.Kind == types.Pointer {
   657  				child.elem = true
   658  			}
   659  			parent.children = append(parent.children, *child)
   660  		} else if member := populateDefaultValue(nil, t.Elem, "", t.Elem.CommentLines, t.Elem.Name.Package); member != nil {
   661  			member.index = true
   662  			parent.children = append(parent.children, *member)
   663  		}
   664  	case types.Map:
   665  		if child := c.build(t.Elem, false); child != nil {
   666  			child.key = true
   667  			parent.children = append(parent.children, *child)
   668  		} else if member := populateDefaultValue(nil, t.Elem, "", t.Elem.CommentLines, t.Elem.Name.Package); member != nil {
   669  			member.key = true
   670  			parent.children = append(parent.children, *member)
   671  		}
   672  
   673  	case types.Struct:
   674  		for _, field := range t.Members {
   675  			name := field.Name
   676  			if len(name) == 0 {
   677  				if field.Type.Kind == types.Pointer {
   678  					name = field.Type.Elem.Name.Name
   679  				} else {
   680  					name = field.Type.Name.Name
   681  				}
   682  			}
   683  			if child := c.build(field.Type, false); child != nil {
   684  				child.field = name
   685  				populateDefaultValue(child, field.Type, field.Tags, field.CommentLines, field.Type.Name.Package)
   686  				parent.children = append(parent.children, *child)
   687  			} else if member := populateDefaultValue(nil, field.Type, field.Tags, field.CommentLines, t.Name.Package); member != nil {
   688  				member.field = name
   689  				parent.children = append(parent.children, *member)
   690  			}
   691  		}
   692  	case types.Alias:
   693  		if child := c.build(t.Underlying, false); child != nil {
   694  			parent.children = append(parent.children, *child)
   695  		}
   696  	}
   697  	if len(parent.children) == 0 && len(parent.call) == 0 {
   698  		//klog.V(6).Infof("decided type %s needs no generation", t.Name)
   699  		return nil
   700  	}
   701  	return parent
   702  }
   703  
   704  const (
   705  	runtimePackagePath    = "k8s.io/apimachinery/pkg/runtime"
   706  	conversionPackagePath = "k8s.io/apimachinery/pkg/conversion"
   707  )
   708  
   709  // genDefaulter produces a file with a autogenerated conversions.
   710  type genDefaulter struct {
   711  	generator.DefaultGen
   712  	typesPackage       string
   713  	outputPackage      string
   714  	peerPackages       []string
   715  	newDefaulters      defaulterFuncMap
   716  	existingDefaulters defaulterFuncMap
   717  	imports            namer.ImportTracker
   718  	typesForInit       []*types.Type
   719  }
   720  
   721  func NewGenDefaulter(sanitizedName, typesPackage, outputPackage string, existingDefaulters, newDefaulters defaulterFuncMap, peerPkgs []string) generator.Generator {
   722  	return &genDefaulter{
   723  		DefaultGen: generator.DefaultGen{
   724  			OptionalName: sanitizedName,
   725  		},
   726  		typesPackage:       typesPackage,
   727  		outputPackage:      outputPackage,
   728  		peerPackages:       peerPkgs,
   729  		newDefaulters:      newDefaulters,
   730  		existingDefaulters: existingDefaulters,
   731  		imports:            generator.NewImportTrackerForPackage(outputPackage),
   732  		typesForInit:       make([]*types.Type, 0),
   733  	}
   734  }
   735  
   736  func (g *genDefaulter) Namers(c *generator.Context) namer.NameSystems {
   737  	// Have the raw namer for this file track what it imports.
   738  	return namer.NameSystems{
   739  		"raw": namer.NewRawNamer(g.outputPackage, g.imports),
   740  	}
   741  }
   742  
   743  func (g *genDefaulter) isOtherPackage(pkg string) bool {
   744  	if pkg == g.outputPackage {
   745  		return false
   746  	}
   747  	if strings.HasSuffix(pkg, `"`+g.outputPackage+`"`) {
   748  		return false
   749  	}
   750  	return true
   751  }
   752  
   753  func (g *genDefaulter) Filter(c *generator.Context, t *types.Type) bool {
   754  	defaults, ok := g.newDefaulters[t]
   755  	if !ok || defaults.object == nil {
   756  		return false
   757  	}
   758  	g.typesForInit = append(g.typesForInit, t)
   759  	return true
   760  }
   761  
   762  func (g *genDefaulter) Imports(c *generator.Context) (imports []string) {
   763  	var importLines []string
   764  	for _, singleImport := range g.imports.ImportLines() {
   765  		if g.isOtherPackage(singleImport) {
   766  			importLines = append(importLines, singleImport)
   767  		}
   768  	}
   769  	return importLines
   770  }
   771  
   772  func (g *genDefaulter) Init(c *generator.Context, w io.Writer) error {
   773  	sw := generator.NewSnippetWriter(w, c, "$", "$")
   774  
   775  	scheme := c.Universe.Type(types.Name{Package: runtimePackagePath, Name: "Scheme"})
   776  	schemePtr := &types.Type{
   777  		Kind: types.Pointer,
   778  		Elem: scheme,
   779  	}
   780  	sw.Do("// RegisterDefaults adds defaulters functions to the given scheme.\n", nil)
   781  	sw.Do("// Public to allow building arbitrary schemes.\n", nil)
   782  	sw.Do("// All generated defaulters are covering - they call all nested defaulters.\n", nil)
   783  	sw.Do("func RegisterDefaults(scheme $.|raw$) error {\n", schemePtr)
   784  	for _, t := range g.typesForInit {
   785  		args := defaultingArgsFromType(t)
   786  		sw.Do("scheme.AddTypeDefaultingFunc(&$.inType|raw${}, func(obj interface{}) { $.inType|objectdefaultfn$(obj.(*$.inType|raw$)) })\n", args)
   787  	}
   788  	sw.Do("return nil\n", nil)
   789  	sw.Do("}\n\n", nil)
   790  	return sw.Error()
   791  }
   792  
   793  func (g *genDefaulter) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
   794  	if _, ok := g.newDefaulters[t]; !ok {
   795  		return nil
   796  	}
   797  
   798  	klog.V(5).Infof("generating for type %v", t)
   799  
   800  	callTree := newCallTreeForType(g.existingDefaulters, g.newDefaulters).build(t, true)
   801  	if callTree == nil {
   802  		klog.V(5).Infof("  no defaulters defined")
   803  		return nil
   804  	}
   805  	i := 0
   806  	callTree.VisitInOrder(func(ancestors []*callNode, current *callNode) {
   807  		if ref := &current.defaultValue.SymbolReference; len(ref.Name) > 0 {
   808  			// Ensure package for symbol is imported in output generation
   809  			g.imports.AddSymbol(*ref)
   810  
   811  			// Rewrite the fully qualified name using the local package name
   812  			// from the imports
   813  			ref.Package = g.imports.LocalNameOf(ref.Package)
   814  		}
   815  
   816  		if len(current.call) == 0 {
   817  			return
   818  		}
   819  		path := callPath(append(ancestors, current))
   820  		klog.V(5).Infof("  %d: %s", i, path)
   821  		i++
   822  	})
   823  
   824  	sw := generator.NewSnippetWriter(w, c, "$", "$")
   825  	g.generateDefaulter(t, callTree, sw)
   826  	return sw.Error()
   827  }
   828  
   829  func defaultingArgsFromType(inType *types.Type) generator.Args {
   830  	return generator.Args{
   831  		"inType": inType,
   832  	}
   833  }
   834  
   835  func (g *genDefaulter) generateDefaulter(inType *types.Type, callTree *callNode, sw *generator.SnippetWriter) {
   836  	sw.Do("func $.inType|objectdefaultfn$(in *$.inType|raw$) {\n", defaultingArgsFromType(inType))
   837  	callTree.WriteMethod("in", 0, nil, sw)
   838  	sw.Do("}\n\n", nil)
   839  }
   840  
   841  // callNode represents an entry in a tree of Go type accessors - the path from the root to a leaf represents
   842  // how in Go code an access would be performed. For example, if a defaulting function exists on a container
   843  // lifecycle hook, to invoke that defaulter correctly would require this Go code:
   844  //
   845  //	for i := range pod.Spec.Containers {
   846  //	  o := &pod.Spec.Containers[i]
   847  //	  if o.LifecycleHook != nil {
   848  //	    SetDefaults_LifecycleHook(o.LifecycleHook)
   849  //	  }
   850  //	}
   851  //
   852  // That would be represented by a call tree like:
   853  //
   854  //	callNode
   855  //	  field: "Spec"
   856  //	  children:
   857  //	  - field: "Containers"
   858  //	    children:
   859  //	    - index: true
   860  //	      children:
   861  //	      - field: "LifecycleHook"
   862  //	        elem: true
   863  //	        call:
   864  //	        - SetDefaults_LifecycleHook
   865  //
   866  // which we can traverse to build that Go struct (you must call the field Spec, then Containers, then range over
   867  // that field, then check whether the LifecycleHook field is nil, before calling SetDefaults_LifecycleHook on
   868  // the pointer to that field).
   869  type callNode struct {
   870  	// field is the name of the Go member to access
   871  	field string
   872  	// key is true if this is a map and we must range over the key and values
   873  	key bool
   874  	// index is true if this is a slice and we must range over the slice values
   875  	index bool
   876  	// elem is true if the previous elements refer to a pointer (typically just field)
   877  	elem bool
   878  
   879  	// call is all of the functions that must be invoked on this particular node, in order
   880  	call []*types.Type
   881  	// children is the child call nodes that must also be traversed
   882  	children []callNode
   883  
   884  	// defaultValue is the defaultValue of a callNode struct
   885  	// Only primitive types and pointer types are eligible to have a default value
   886  	defaultValue defaultValue
   887  
   888  	// defaultIsPrimitive is used to determine how to assign the default value.
   889  	// Primitive types will be directly assigned while complex types will use JSON unmarshalling
   890  	defaultIsPrimitive bool
   891  
   892  	// markerOnly is true if the callNode exists solely to fill in a default value
   893  	markerOnly bool
   894  
   895  	// defaultType is the transitive underlying/element type of the node.
   896  	// The provided default value literal or reference is expected to be
   897  	// convertible to this type.
   898  	//
   899  	// e.g:
   900  	//	node type = *string 			-> 	defaultType = string
   901  	//	node type = StringPointerAlias 	-> 	defaultType = string
   902  	// Only populated if defaultIsPrimitive is true
   903  	defaultType *types.Type
   904  
   905  	// defaultTopLevelType is the final type the value should resolve to
   906  	// This is in constrast with default type, which resolves aliases and pointers.
   907  	defaultTopLevelType *types.Type
   908  }
   909  
   910  type defaultValue struct {
   911  	// The value was written directly in the marker comment and
   912  	// has been parsed as JSON
   913  	InlineConstant string
   914  	// The name of the symbol relative to the parsed package path
   915  	// i.e. k8s.io/pkg.apis.v1.Foo if from another package or simply `Foo`
   916  	// if within the same package.
   917  	SymbolReference types.Name
   918  }
   919  
   920  func (d defaultValue) IsEmpty() bool {
   921  	resolved := d.Resolved()
   922  	return resolved == ""
   923  }
   924  
   925  func (d defaultValue) Resolved() string {
   926  	if len(d.InlineConstant) > 0 {
   927  		return d.InlineConstant
   928  	}
   929  	return d.SymbolReference.String()
   930  }
   931  
   932  // CallNodeVisitorFunc is a function for visiting a call tree. ancestors is the list of all parents
   933  // of this node to the root of the tree - will be empty at the root.
   934  type CallNodeVisitorFunc func(ancestors []*callNode, node *callNode)
   935  
   936  func (n *callNode) VisitInOrder(fn CallNodeVisitorFunc) {
   937  	n.visitInOrder(nil, fn)
   938  }
   939  
   940  func (n *callNode) visitInOrder(ancestors []*callNode, fn CallNodeVisitorFunc) {
   941  	fn(ancestors, n)
   942  	ancestors = append(ancestors, n)
   943  	for i := range n.children {
   944  		n.children[i].visitInOrder(ancestors, fn)
   945  	}
   946  }
   947  
   948  var (
   949  	indexVariables = "ijklmnop"
   950  	localVariables = "abcdefgh"
   951  )
   952  
   953  // varsForDepth creates temporary variables guaranteed to be unique within lexical Go scopes
   954  // of this depth in a function. It uses canonical Go loop variables for the first 7 levels
   955  // and then resorts to uglier prefixes.
   956  func varsForDepth(depth int) (index, local string) {
   957  	if depth > len(indexVariables) {
   958  		index = fmt.Sprintf("i%d", depth)
   959  	} else {
   960  		index = indexVariables[depth : depth+1]
   961  	}
   962  	if depth > len(localVariables) {
   963  		local = fmt.Sprintf("local%d", depth)
   964  	} else {
   965  		local = localVariables[depth : depth+1]
   966  	}
   967  	return
   968  }
   969  
   970  // writeCalls generates a list of function calls based on the calls field for the provided variable
   971  // name and pointer.
   972  func (n *callNode) writeCalls(varName string, isVarPointer bool, sw *generator.SnippetWriter) {
   973  	accessor := varName
   974  	if !isVarPointer {
   975  		accessor = "&" + accessor
   976  	}
   977  	for _, fn := range n.call {
   978  		sw.Do("$.fn|raw$($.var$)\n", generator.Args{
   979  			"fn":  fn,
   980  			"var": accessor,
   981  		})
   982  	}
   983  }
   984  
   985  func getTypeZeroValue(t string) (interface{}, error) {
   986  	defaultZero, ok := typeZeroValue[t]
   987  	if !ok {
   988  		return nil, fmt.Errorf("Cannot find zero value for type %v in typeZeroValue", t)
   989  	}
   990  
   991  	// To generate the code for empty string, they must be quoted
   992  	if defaultZero == "" {
   993  		defaultZero = strconv.Quote(defaultZero.(string))
   994  	}
   995  	return defaultZero, nil
   996  }
   997  
   998  func (n *callNode) writeDefaulter(varName string, index string, isVarPointer bool, sw *generator.SnippetWriter) {
   999  	if n.defaultValue.IsEmpty() {
  1000  		return
  1001  	}
  1002  	args := generator.Args{
  1003  		"defaultValue": n.defaultValue.Resolved(),
  1004  		"varName":      varName,
  1005  		"index":        index,
  1006  		"varTopType":   n.defaultTopLevelType,
  1007  	}
  1008  
  1009  	variablePlaceholder := ""
  1010  
  1011  	if n.index {
  1012  		// Defaulting for array
  1013  		variablePlaceholder = "$.varName$[$.index$]"
  1014  	} else if n.key {
  1015  		// Defaulting for map
  1016  		variablePlaceholder = "$.varName$[$.index$]"
  1017  		mapDefaultVar := args["index"].(string) + "_default"
  1018  		args["mapDefaultVar"] = mapDefaultVar
  1019  	} else {
  1020  		// Defaulting for primitive type
  1021  		variablePlaceholder = "$.varName$"
  1022  	}
  1023  
  1024  	// defaultIsPrimitive is true if the type or underlying type (in an array/map) is primitive
  1025  	// or is a pointer to a primitive type
  1026  	// (Eg: int, map[string]*string, []int)
  1027  	if n.defaultIsPrimitive {
  1028  		// If the default value is a primitive when the assigned type is a pointer
  1029  		// keep using the address-of operator on the primitive value until the types match
  1030  		if pointerPath := getPointerElementPath(n.defaultTopLevelType); len(pointerPath) > 0 {
  1031  			// If the destination is a pointer, the last element in
  1032  			// defaultDepth is the element type of the bottommost pointer:
  1033  			// the base type of our default value.
  1034  			destElemType := pointerPath[len(pointerPath)-1]
  1035  			pointerArgs := args.WithArgs(generator.Args{
  1036  				"varDepth":     len(pointerPath),
  1037  				"baseElemType": destElemType,
  1038  			})
  1039  
  1040  			sw.Do(fmt.Sprintf("if %s == nil {\n", variablePlaceholder), pointerArgs)
  1041  			if len(n.defaultValue.InlineConstant) > 0 {
  1042  				// If default value is a literal then it can be assigned via var stmt
  1043  				sw.Do("var ptrVar$.varDepth$ $.baseElemType|raw$ = $.defaultValue$\n", pointerArgs)
  1044  			} else {
  1045  				// If default value is not a literal then it may need to be casted
  1046  				// to the base type of the destination pointer
  1047  				sw.Do("ptrVar$.varDepth$ := $.baseElemType|raw$($.defaultValue$)\n", pointerArgs)
  1048  			}
  1049  
  1050  			for i := len(pointerPath); i >= 1; i-- {
  1051  				dest := fmt.Sprintf("ptrVar%d", i-1)
  1052  				assignment := ":="
  1053  				if i == 1 {
  1054  					// Last assignment is into the storage destination
  1055  					dest = variablePlaceholder
  1056  					assignment = "="
  1057  				}
  1058  
  1059  				sourceType := "*" + destElemType.String()
  1060  				if i == len(pointerPath) {
  1061  					// Initial value is not a pointer
  1062  					sourceType = destElemType.String()
  1063  				}
  1064  				destElemType = pointerPath[i-1]
  1065  
  1066  				// Cannot include `dest` into args since its value may be
  1067  				// `variablePlaceholder` which is a template, not a value
  1068  				elementArgs := pointerArgs.WithArgs(generator.Args{
  1069  					"assignment":   assignment,
  1070  					"source":       fmt.Sprintf("ptrVar%d", i),
  1071  					"destElemType": destElemType,
  1072  				})
  1073  
  1074  				// Skip cast if type is exact match
  1075  				if destElemType.String() == sourceType {
  1076  					sw.Do(fmt.Sprintf("%v $.assignment$ &$.source$\n", dest), elementArgs)
  1077  				} else {
  1078  					sw.Do(fmt.Sprintf("%v $.assignment$ (*$.destElemType|raw$)(&$.source$)\n", dest), elementArgs)
  1079  				}
  1080  			}
  1081  		} else {
  1082  			// For primitive types, nil checks cannot be used and the zero value must be determined
  1083  			defaultZero, err := getTypeZeroValue(n.defaultType.String())
  1084  			if err != nil {
  1085  				klog.Error(err)
  1086  			}
  1087  			args["defaultZero"] = defaultZero
  1088  
  1089  			sw.Do(fmt.Sprintf("if %s == $.defaultZero$ {\n", variablePlaceholder), args)
  1090  
  1091  			if len(n.defaultValue.InlineConstant) > 0 {
  1092  				sw.Do(fmt.Sprintf("%s = $.defaultValue$", variablePlaceholder), args)
  1093  			} else {
  1094  				sw.Do(fmt.Sprintf("%s = $.varTopType|raw$($.defaultValue$)", variablePlaceholder), args)
  1095  			}
  1096  		}
  1097  	} else {
  1098  		sw.Do(fmt.Sprintf("if %s == nil {\n", variablePlaceholder), args)
  1099  		// Map values are not directly addressable and we need a temporary variable to do json unmarshalling
  1100  		// This applies to maps with non-primitive values (eg: map[string]SubStruct)
  1101  		if n.key {
  1102  			sw.Do("$.mapDefaultVar$ := $.varName$[$.index$]\n", args)
  1103  			sw.Do("if err := json.Unmarshal([]byte(`$.defaultValue$`), &$.mapDefaultVar$); err != nil {\n", args)
  1104  		} else {
  1105  			variablePointer := variablePlaceholder
  1106  			if !isVarPointer {
  1107  				variablePointer = "&" + variablePointer
  1108  			}
  1109  			sw.Do(fmt.Sprintf("if err := json.Unmarshal([]byte(`$.defaultValue$`), %s); err != nil {\n", variablePointer), args)
  1110  		}
  1111  		sw.Do("panic(err)\n", nil)
  1112  		sw.Do("}\n", nil)
  1113  		if n.key {
  1114  			sw.Do("$.varName$[$.index$] = $.mapDefaultVar$\n", args)
  1115  		}
  1116  	}
  1117  	sw.Do("}\n", nil)
  1118  }
  1119  
  1120  // WriteMethod performs an in-order traversal of the calltree, generating loops and if blocks as necessary
  1121  // to correctly turn the call tree into a method body that invokes all calls on all child nodes of the call tree.
  1122  // Depth is used to generate local variables at the proper depth.
  1123  func (n *callNode) WriteMethod(varName string, depth int, ancestors []*callNode, sw *generator.SnippetWriter) {
  1124  	// if len(n.call) > 0 {
  1125  	// 	sw.Do(fmt.Sprintf("// %s\n", callPath(append(ancestors, n)).String()), nil)
  1126  	// }
  1127  
  1128  	if len(n.field) > 0 {
  1129  		varName = varName + "." + n.field
  1130  	}
  1131  
  1132  	index, local := varsForDepth(depth)
  1133  	vars := generator.Args{
  1134  		"index": index,
  1135  		"local": local,
  1136  		"var":   varName,
  1137  	}
  1138  
  1139  	isPointer := n.elem && !n.index
  1140  	if isPointer && len(ancestors) > 0 {
  1141  		sw.Do("if $.var$ != nil {\n", vars)
  1142  	}
  1143  
  1144  	switch {
  1145  	case n.index:
  1146  		sw.Do("for $.index$ := range $.var$ {\n", vars)
  1147  		if !n.markerOnly {
  1148  			if n.elem {
  1149  				sw.Do("$.local$ := $.var$[$.index$]\n", vars)
  1150  			} else {
  1151  				sw.Do("$.local$ := &$.var$[$.index$]\n", vars)
  1152  			}
  1153  		}
  1154  
  1155  		n.writeDefaulter(varName, index, isPointer, sw)
  1156  		n.writeCalls(local, true, sw)
  1157  		for i := range n.children {
  1158  			n.children[i].WriteMethod(local, depth+1, append(ancestors, n), sw)
  1159  		}
  1160  		sw.Do("}\n", nil)
  1161  	case n.key:
  1162  		if !n.defaultValue.IsEmpty() {
  1163  			// Map keys are typed and cannot share the same index variable as arrays and other maps
  1164  			index = index + "_" + ancestors[len(ancestors)-1].field
  1165  			vars["index"] = index
  1166  			sw.Do("for $.index$ := range $.var$ {\n", vars)
  1167  			n.writeDefaulter(varName, index, isPointer, sw)
  1168  			sw.Do("}\n", nil)
  1169  		}
  1170  	default:
  1171  		n.writeDefaulter(varName, index, isPointer, sw)
  1172  		n.writeCalls(varName, isPointer, sw)
  1173  		for i := range n.children {
  1174  			n.children[i].WriteMethod(varName, depth, append(ancestors, n), sw)
  1175  		}
  1176  	}
  1177  
  1178  	if isPointer && len(ancestors) > 0 {
  1179  		sw.Do("}\n", nil)
  1180  	}
  1181  }
  1182  
  1183  type callPath []*callNode
  1184  
  1185  // String prints a representation of a callPath that roughly approximates what a Go accessor
  1186  // would look like. Used for debugging only.
  1187  func (path callPath) String() string {
  1188  	if len(path) == 0 {
  1189  		return "<none>"
  1190  	}
  1191  	var parts []string
  1192  	for _, p := range path {
  1193  		last := len(parts) - 1
  1194  		switch {
  1195  		case p.elem:
  1196  			if len(parts) > 0 {
  1197  				parts[last] = "*" + parts[last]
  1198  			} else {
  1199  				parts = append(parts, "*")
  1200  			}
  1201  		case p.index:
  1202  			if len(parts) > 0 {
  1203  				parts[last] = parts[last] + "[i]"
  1204  			} else {
  1205  				parts = append(parts, "[i]")
  1206  			}
  1207  		case p.key:
  1208  			if len(parts) > 0 {
  1209  				parts[last] = parts[last] + "[key]"
  1210  			} else {
  1211  				parts = append(parts, "[key]")
  1212  			}
  1213  		default:
  1214  			if len(p.field) > 0 {
  1215  				parts = append(parts, p.field)
  1216  			} else {
  1217  				parts = append(parts, "<root>")
  1218  			}
  1219  		}
  1220  	}
  1221  	var calls []string
  1222  	for _, fn := range path[len(path)-1].call {
  1223  		calls = append(calls, fn.Name.String())
  1224  	}
  1225  	if len(calls) == 0 {
  1226  		calls = append(calls, "<none>")
  1227  	}
  1228  
  1229  	return strings.Join(parts, ".") + " calls " + strings.Join(calls, ", ")
  1230  }