github.com/alex123012/deckhouse-controller-tools@v0.0.0-20230510090815-d594daf1af8c/pkg/deepcopy/traverse.go (about)

     1  /*
     2  Copyright 2019 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 deepcopy
    18  
    19  import (
    20  	"fmt"
    21  	"go/ast"
    22  	"go/types"
    23  	"io"
    24  	"path"
    25  	"strings"
    26  	"unicode"
    27  	"unicode/utf8"
    28  
    29  	"sigs.k8s.io/controller-tools/pkg/loader"
    30  	"sigs.k8s.io/controller-tools/pkg/markers"
    31  )
    32  
    33  // NB(directxman12): This code is a bit of a byzantine mess.
    34  // I've tried to clean it up a bit from the original in deepcopy-gen,
    35  // but parts remain a bit convoluted.  Exercise caution when changing.
    36  // It's perhaps a tad over-commented now, but better safe than sorry.
    37  // It also seriously needs auditing for sanity -- there's parts where we
    38  // copy the original deepcopy-gen's output just to be safe, but some of that
    39  // could be simplified away if we're careful.
    40  
    41  // codeWriter assists in writing out Go code lines and blocks to a writer.
    42  type codeWriter struct {
    43  	out io.Writer
    44  }
    45  
    46  // Line writes a single line.
    47  func (c *codeWriter) Line(line string) {
    48  	fmt.Fprintln(c.out, line)
    49  }
    50  
    51  // Linef writes a single line with formatting (as per fmt.Sprintf).
    52  func (c *codeWriter) Linef(line string, args ...interface{}) {
    53  	fmt.Fprintf(c.out, line+"\n", args...)
    54  }
    55  
    56  // If writes an if statement with the given setup/condition clause, executing
    57  // the given function to write the contents of the block.
    58  func (c *codeWriter) If(setup string, block func()) {
    59  	c.Linef("if %s {", setup)
    60  	block()
    61  	c.Line("}")
    62  }
    63  
    64  // If writes if and else statements with the given setup/condition clause, executing
    65  // the given functions to write the contents of the blocks.
    66  func (c *codeWriter) IfElse(setup string, ifBlock func(), elseBlock func()) {
    67  	c.Linef("if %s {", setup)
    68  	ifBlock()
    69  	c.Line("} else {")
    70  	elseBlock()
    71  	c.Line("}")
    72  }
    73  
    74  // For writes an for statement with the given setup/condition clause, executing
    75  // the given function to write the contents of the block.
    76  func (c *codeWriter) For(setup string, block func()) {
    77  	c.Linef("for %s {", setup)
    78  	block()
    79  	c.Line("}")
    80  }
    81  
    82  // importsList keeps track of required imports, automatically assigning aliases
    83  // to import statement.
    84  type importsList struct {
    85  	byPath  map[string]string
    86  	byAlias map[string]string
    87  
    88  	pkg *loader.Package
    89  }
    90  
    91  // NeedImport marks that the given package is needed in the list of imports,
    92  // returning the ident (import alias) that should be used to reference the package.
    93  func (l *importsList) NeedImport(importPath string) string {
    94  	// we get an actual path from Package, which might include venddored
    95  	// packages if running on a package in vendor.
    96  	if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 {
    97  		importPath = importPath[ind+8: /* len("/vendor/") */]
    98  	}
    99  
   100  	// check to see if we've already assigned an alias, and just return that.
   101  	alias, exists := l.byPath[importPath]
   102  	if exists {
   103  		return alias
   104  	}
   105  
   106  	// otherwise, calculate an import alias by joining path parts till we get something unique
   107  	restPath, nextWord := path.Split(importPath)
   108  
   109  	for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] {
   110  		if restPath == "" {
   111  			// do something else to disambiguate if we're run out of parts and
   112  			// still have duplicates, somehow
   113  			alias += "x"
   114  		}
   115  
   116  		// can't have a first digit, per Go identifier rules, so just skip them
   117  		for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) {
   118  			nextWord = nextWord[runeLen:]
   119  		}
   120  
   121  		// make a valid identifier by replacing "bad" characters with underscores
   122  		nextWord = strings.Map(func(r rune) rune {
   123  			if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
   124  				return r
   125  			}
   126  			return '_'
   127  		}, nextWord)
   128  
   129  		alias = nextWord + alias
   130  		if len(restPath) > 0 {
   131  			restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */)
   132  		}
   133  	}
   134  
   135  	l.byPath[importPath] = alias
   136  	l.byAlias[alias] = importPath
   137  	return alias
   138  }
   139  
   140  // ImportSpecs returns a string form of each import spec
   141  // (i.e. `alias "path/to/import").  Aliases are only present
   142  // when they don't match the package name.
   143  func (l *importsList) ImportSpecs() []string {
   144  	res := make([]string, 0, len(l.byPath))
   145  	for importPath, alias := range l.byPath {
   146  		pkg := l.pkg.Imports()[importPath]
   147  		if pkg != nil && pkg.Name == alias {
   148  			// don't print if alias is the same as package name
   149  			// (we've already taken care of duplicates).
   150  			res = append(res, fmt.Sprintf("%q", importPath))
   151  		} else {
   152  			res = append(res, fmt.Sprintf("%s %q", alias, importPath))
   153  		}
   154  	}
   155  	return res
   156  }
   157  
   158  // namingInfo holds package and syntax for referencing a field, type,
   159  // etc.  It's used to allow lazily marking import usage.
   160  // You should generally retrieve the syntax using Syntax.
   161  type namingInfo struct {
   162  	// typeInfo is the type being named.
   163  	typeInfo     types.Type
   164  	nameOverride string
   165  }
   166  
   167  // Syntax calculates the code representation of the given type or name,
   168  // and marks that is used (potentially marking an import as used).
   169  func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) string {
   170  	if n.nameOverride != "" {
   171  		return n.nameOverride
   172  	}
   173  
   174  	// NB(directxman12): typeInfo.String gets us most of the way there,
   175  	// but fails (for us) on named imports, since it uses the full package path.
   176  	switch typeInfo := n.typeInfo.(type) {
   177  	case *types.Named:
   178  		// register that we need an import for this type,
   179  		// so we can get the appropriate alias to use.
   180  		typeName := typeInfo.Obj()
   181  		otherPkg := typeName.Pkg()
   182  		if otherPkg == basePkg.Types {
   183  			// local import
   184  			return typeName.Name()
   185  		}
   186  		alias := imports.NeedImport(loader.NonVendorPath(otherPkg.Path()))
   187  		return alias + "." + typeName.Name()
   188  	case *types.Basic:
   189  		return typeInfo.String()
   190  	case *types.Pointer:
   191  		return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)
   192  	case *types.Slice:
   193  		return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)
   194  	case *types.Map:
   195  		return fmt.Sprintf(
   196  			"map[%s]%s",
   197  			(&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports),
   198  			(&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports))
   199  	default:
   200  		basePkg.AddError(fmt.Errorf("name requested for invalid type: %s", typeInfo))
   201  		return typeInfo.String()
   202  	}
   203  }
   204  
   205  // copyMethodMakers makes DeepCopy (and related) methods for Go types,
   206  // writing them to its codeWriter.
   207  type copyMethodMaker struct {
   208  	pkg *loader.Package
   209  	*importsList
   210  	*codeWriter
   211  }
   212  
   213  // GenerateMethodsFor makes DeepCopy, DeepCopyInto, and DeepCopyObject methods
   214  // for the given type, when appropriate
   215  func (c *copyMethodMaker) GenerateMethodsFor(root *loader.Package, info *markers.TypeInfo) {
   216  	typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name)
   217  	if typeInfo == types.Typ[types.Invalid] {
   218  		root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec))
   219  	}
   220  
   221  	// figure out if we need to use a pointer receiver -- most types get a pointer receiver,
   222  	// except those that are aliases to types that are already pass-by-reference (pointers,
   223  	// interfaces. maps, slices).
   224  	ptrReceiver := usePtrReceiver(typeInfo)
   225  
   226  	hasManualDeepCopyInto := hasDeepCopyIntoMethod(root, typeInfo)
   227  	hasManualDeepCopy, deepCopyOnPtr := hasDeepCopyMethod(root, typeInfo)
   228  
   229  	// only generate each method if it hasn't been implemented.
   230  	if !hasManualDeepCopyInto {
   231  		c.Line("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.")
   232  		if ptrReceiver {
   233  			c.Linef("func (in *%s) DeepCopyInto(out *%s) {", info.Name, info.Name)
   234  		} else {
   235  			c.Linef("func (in %s) DeepCopyInto(out *%s) {", info.Name, info.Name)
   236  			c.Line("{in := &in") // add an extra block so that we can redefine `in` without type issues
   237  		}
   238  
   239  		// just wrap the existing deepcopy if present
   240  		if hasManualDeepCopy {
   241  			if deepCopyOnPtr {
   242  				c.Line("clone := in.DeepCopy()")
   243  				c.Line("*out = *clone")
   244  			} else {
   245  				c.Line("*out = in.DeepCopy()")
   246  			}
   247  		} else {
   248  			c.genDeepCopyIntoBlock(&namingInfo{nameOverride: info.Name}, typeInfo)
   249  		}
   250  
   251  		if !ptrReceiver {
   252  			c.Line("}") // close our extra "in redefinition" block
   253  		}
   254  		c.Line("}")
   255  	}
   256  
   257  	if !hasManualDeepCopy {
   258  		// these are both straightforward, so we just template them out.
   259  		if ptrReceiver {
   260  			c.Linef(ptrDeepCopy, info.Name)
   261  		} else {
   262  			c.Linef(bareDeepCopy, info.Name)
   263  		}
   264  
   265  		// maybe also generate DeepCopyObject, if asked.
   266  		if genObjectInterface(info) {
   267  			// we always need runtime.Object for DeepCopyObject
   268  			runtimeAlias := c.NeedImport("k8s.io/apimachinery/pkg/runtime")
   269  			if ptrReceiver {
   270  				c.Linef(ptrDeepCopyObj, info.Name, runtimeAlias)
   271  			} else {
   272  				c.Linef(bareDeepCopyObj, info.Name, runtimeAlias)
   273  			}
   274  		}
   275  	}
   276  }
   277  
   278  // genDeepCopyBody generates a DeepCopyInto block for the given type.  The
   279  // block is *not* wrapped in curly braces.
   280  func (c *copyMethodMaker) genDeepCopyIntoBlock(actualName *namingInfo, typeInfo types.Type) {
   281  	// we calculate *how* we should copy mostly based on the "eventual" type of
   282  	// a given type (i.e. the type that results from following all aliases)
   283  	last := eventualUnderlyingType(typeInfo)
   284  
   285  	// we might hit a type that has a manual deepcopy method written on non-root types
   286  	// (this case is handled for root types in GenerateMethodFor).
   287  	// In that case (when we're not dealing with a pointer, since those need special handling
   288  	// to match 1-to-1 with k8s deepcopy-gen), just use that.
   289  	if _, isPtr := last.(*types.Pointer); !isPtr && hasAnyDeepCopyMethod(c.pkg, typeInfo) {
   290  		c.Line("*out = in.DeepCopy()")
   291  		return
   292  	}
   293  
   294  	switch last := last.(type) {
   295  	case *types.Basic:
   296  		switch last.Kind() {
   297  		case types.Invalid, types.UnsafePointer:
   298  			c.pkg.AddError(fmt.Errorf("invalid type: %s", last))
   299  		default:
   300  			// basic types themselves can be "shallow" copied, so all we need
   301  			// to do is check if our *actual* type (not the underlying one) has
   302  			// a custom method implemented.
   303  			if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod {
   304  				c.Line("*out = in.DeepCopy()")
   305  			}
   306  			c.Line("*out = *in")
   307  		}
   308  	case *types.Map:
   309  		c.genMapDeepCopy(actualName, last)
   310  	case *types.Slice:
   311  		c.genSliceDeepCopy(actualName, last)
   312  	case *types.Struct:
   313  		c.genStructDeepCopy(actualName, last)
   314  	case *types.Pointer:
   315  		c.genPointerDeepCopy(actualName, last)
   316  	case *types.Named:
   317  		// handled via the above loop, should never happen
   318  		c.pkg.AddError(fmt.Errorf("interface type %s encountered directly, invalid condition", last))
   319  	default:
   320  		c.pkg.AddError(fmt.Errorf("invalid type: %s", last))
   321  	}
   322  }
   323  
   324  // genMapDeepCopy generates DeepCopy code for the given named type whose eventual
   325  // type is the given map type.
   326  func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.Map) {
   327  	// maps *must* have shallow-copiable types, since we just iterate
   328  	// through the keys, only trying to deepcopy the values.
   329  	if !fineToShallowCopy(mapType.Key()) {
   330  		c.pkg.AddError(fmt.Errorf("invalid map key type: %s", mapType.Key()))
   331  		return
   332  	}
   333  
   334  	// make our actual type (not the underlying one)...
   335  	c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList))
   336  
   337  	// ...and copy each element appropriately
   338  	c.For("key, val := range *in", func() {
   339  		// check if we have manually written methods,
   340  		// in which case we'll just try and use those
   341  		hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, mapType.Elem())
   342  		hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, mapType.Elem())
   343  		switch {
   344  		case hasDeepCopyInto || hasDeepCopy:
   345  			// use the manually-written methods
   346  			_, fieldIsPtr := mapType.Elem().(*types.Pointer)                       // is "out" actually a pointer
   347  			inIsPtr := resultWillBePointer(mapType.Elem(), hasDeepCopy, copyOnPtr) // does copying "in" produce a pointer
   348  			if hasDeepCopy {
   349  				// If we're calling DeepCopy, check if it's receiver needs a pointer
   350  				inIsPtr = copyOnPtr
   351  			}
   352  			if inIsPtr == fieldIsPtr {
   353  				c.Line("(*out)[key] = val.DeepCopy()")
   354  			} else if fieldIsPtr {
   355  				c.Line("{") // use a block because we use `x` as a temporary
   356  				c.Line("x := val.DeepCopy()")
   357  				c.Line("(*out)[key] = &x")
   358  				c.Line("}")
   359  			} else {
   360  				c.Line("(*out)[key] = *val.DeepCopy()")
   361  			}
   362  		case fineToShallowCopy(mapType.Elem()):
   363  			// just shallow copy types for which it's safe to do so
   364  			c.Line("(*out)[key] = val")
   365  		default:
   366  			// otherwise, we've got some kind-specific actions,
   367  			// based on the element's eventual type.
   368  
   369  			underlyingElem := eventualUnderlyingType(mapType.Elem())
   370  
   371  			// if it passes by reference, let the main switch handle it
   372  			if passesByReference(underlyingElem) {
   373  				c.Linef("var outVal %[1]s", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList))
   374  				c.IfElse("val == nil", func() {
   375  					c.Line("(*out)[key] = nil")
   376  				}, func() {
   377  					c.Line("in, out := &val, &outVal")
   378  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: mapType.Elem()}, mapType.Elem())
   379  				})
   380  				c.Line("(*out)[key] = outVal")
   381  
   382  				return
   383  			}
   384  
   385  			// otherwise...
   386  			switch underlyingElem := underlyingElem.(type) {
   387  			case *types.Struct:
   388  				// structs will have deepcopy generated for them, so use that
   389  				c.Line("(*out)[key] = *val.DeepCopy()")
   390  			default:
   391  				c.pkg.AddError(fmt.Errorf("invalid map value type: %s", underlyingElem))
   392  				return
   393  			}
   394  		}
   395  	})
   396  }
   397  
   398  // genSliceDeepCopy generates DeepCopy code for the given named type whose
   399  // underlying type is the given slice.
   400  func (c *copyMethodMaker) genSliceDeepCopy(actualName *namingInfo, sliceType *types.Slice) {
   401  	underlyingElem := eventualUnderlyingType(sliceType.Elem())
   402  
   403  	// make the actual type (not the underlying)
   404  	c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList))
   405  
   406  	// check if we need to do anything special, or just copy each element appropriately
   407  	switch {
   408  	case hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()):
   409  		// just use deepcopy if it's present (deepcopyinto will be filled in by our code)
   410  		c.For("i := range *in", func() {
   411  			c.Line("(*in)[i].DeepCopyInto(&(*out)[i])")
   412  		})
   413  	case fineToShallowCopy(underlyingElem):
   414  		// shallow copy if ok
   415  		c.Line("copy(*out, *in)")
   416  	default:
   417  		// copy each element appropriately
   418  		c.For("i := range *in", func() {
   419  			// fall back to normal code for reference types or those with custom logic
   420  			if passesByReference(underlyingElem) || hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()) {
   421  				c.If("(*in)[i] != nil", func() {
   422  					c.Line("in, out := &(*in)[i], &(*out)[i]")
   423  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: sliceType.Elem()}, sliceType.Elem())
   424  				})
   425  				return
   426  			}
   427  
   428  			switch underlyingElem.(type) {
   429  			case *types.Struct:
   430  				// structs will always have deepcopy
   431  				c.Linef("(*in)[i].DeepCopyInto(&(*out)[i])")
   432  			default:
   433  				c.pkg.AddError(fmt.Errorf("invalid slice element type: %s", underlyingElem))
   434  			}
   435  		})
   436  	}
   437  }
   438  
   439  // genStructDeepCopy generates DeepCopy code for the given named type whose
   440  // underlying type is the given struct.
   441  func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Struct) {
   442  	c.Line("*out = *in")
   443  
   444  	for i := 0; i < structType.NumFields(); i++ {
   445  		field := structType.Field(i)
   446  
   447  		// if we have a manual deepcopy, use that
   448  		hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, field.Type())
   449  		hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, field.Type())
   450  		if hasDeepCopyInto || hasDeepCopy {
   451  			// NB(directxman12): yes, I know this is kind-of weird that we
   452  			// have all this special-casing here, but it's nice for testing
   453  			// purposes to be 1-to-1 with deepcopy-gen, which does all sorts of
   454  			// stuff like this (I'm pretty sure I found some codepaths that
   455  			// never execute there, because they're pretty clearly invalid
   456  			// syntax).
   457  
   458  			_, fieldIsPtr := field.Type().(*types.Pointer)
   459  			inIsPtr := resultWillBePointer(field.Type(), hasDeepCopy, copyOnPtr)
   460  			if fieldIsPtr {
   461  				// we'll need a if block to check for nilness
   462  				// we'll let genDeepCopyIntoBlock handle the details, we just needed the setup
   463  				c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() {
   464  					c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name())
   465  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type())
   466  				})
   467  			} else {
   468  				// special-case for compatibility with deepcopy-gen
   469  				if inIsPtr == fieldIsPtr {
   470  					c.Linef("out.%[1]s = in.%[1]s.DeepCopy()", field.Name())
   471  				} else {
   472  					c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name())
   473  				}
   474  			}
   475  			continue
   476  		}
   477  
   478  		// pass-by-reference fields get delegated to the main type
   479  		underlyingField := eventualUnderlyingType(field.Type())
   480  		if passesByReference(underlyingField) {
   481  			c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() {
   482  				c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name())
   483  				c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type())
   484  			})
   485  			continue
   486  		}
   487  
   488  		// otherwise...
   489  		switch underlyingField := underlyingField.(type) {
   490  		case *types.Basic:
   491  			switch underlyingField.Kind() {
   492  			case types.Invalid, types.UnsafePointer:
   493  				c.pkg.AddError(loader.ErrFromNode(fmt.Errorf("invalid field type: %s", underlyingField), field))
   494  				return
   495  			default:
   496  				// nothing to do, initial assignment copied this
   497  			}
   498  		case *types.Struct:
   499  			if fineToShallowCopy(field.Type()) {
   500  				c.Linef("out.%[1]s = in.%[1]s", field.Name())
   501  			} else {
   502  				c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name())
   503  			}
   504  		default:
   505  			c.pkg.AddError(loader.ErrFromNode(fmt.Errorf("invalid field type: %s", underlyingField), field))
   506  			return
   507  		}
   508  	}
   509  }
   510  
   511  // genPointerDeepCopy generates DeepCopy code for the given named type whose
   512  // underlying type is the given struct.
   513  func (c *copyMethodMaker) genPointerDeepCopy(_ *namingInfo, pointerType *types.Pointer) {
   514  	underlyingElem := eventualUnderlyingType(pointerType.Elem())
   515  
   516  	// if we have a manually written deepcopy, just use that
   517  	hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, pointerType.Elem())
   518  	hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, pointerType.Elem())
   519  	if hasDeepCopyInto || hasDeepCopy {
   520  		outNeedsPtr := resultWillBePointer(pointerType.Elem(), hasDeepCopy, copyOnPtr)
   521  		if hasDeepCopy {
   522  			outNeedsPtr = copyOnPtr
   523  		}
   524  		if outNeedsPtr {
   525  			c.Line("*out = (*in).DeepCopy()")
   526  		} else {
   527  			c.Line("x := (*in).DeepCopy()")
   528  			c.Line("*out = &x")
   529  		}
   530  		return
   531  	}
   532  
   533  	// shallow-copiable types are pretty easy
   534  	if fineToShallowCopy(underlyingElem) {
   535  		c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList))
   536  		c.Line("**out = **in")
   537  		return
   538  	}
   539  
   540  	// pass-by-reference types get delegated to the main switch
   541  	if passesByReference(underlyingElem) {
   542  		c.Linef("*out = new(%s)", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList))
   543  		c.If("**in != nil", func() {
   544  			c.Line("in, out := *in, *out")
   545  			c.genDeepCopyIntoBlock(&namingInfo{typeInfo: underlyingElem}, eventualUnderlyingType(underlyingElem))
   546  		})
   547  		return
   548  	}
   549  
   550  	// otherwise...
   551  	switch underlyingElem := underlyingElem.(type) {
   552  	case *types.Struct:
   553  		c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList))
   554  		c.Line("(*in).DeepCopyInto(*out)")
   555  	default:
   556  		c.pkg.AddError(fmt.Errorf("invalid pointer element type: %s", underlyingElem))
   557  		return
   558  	}
   559  }
   560  
   561  // usePtrReceiver checks if we need a pointer receiver on methods for the given type
   562  // Pass-by-reference types don't get pointer receivers.
   563  func usePtrReceiver(typeInfo types.Type) bool {
   564  	switch typeInfo.(type) {
   565  	case *types.Pointer:
   566  		return false
   567  	case *types.Map:
   568  		return false
   569  	case *types.Slice:
   570  		return false
   571  	case *types.Named:
   572  		return usePtrReceiver(typeInfo.Underlying())
   573  	default:
   574  		return true
   575  	}
   576  }
   577  
   578  func resultWillBePointer(typeInfo types.Type, hasDeepCopy, deepCopyOnPtr bool) bool {
   579  	// if we have a manual deepcopy, we can just check what that returns
   580  	if hasDeepCopy {
   581  		return deepCopyOnPtr
   582  	}
   583  
   584  	// otherwise, we'll need to check its type
   585  	switch typeInfo := typeInfo.(type) {
   586  	case *types.Pointer:
   587  		// NB(directxman12): we don't have to worry about the elem having a deepcopy,
   588  		// since hasManualDeepCopy would've caught that.
   589  
   590  		// we'll be calling on the elem, so check that
   591  		return resultWillBePointer(typeInfo.Elem(), false, false)
   592  	case *types.Map:
   593  		return false
   594  	case *types.Slice:
   595  		return false
   596  	case *types.Named:
   597  		return resultWillBePointer(typeInfo.Underlying(), false, false)
   598  	default:
   599  		return true
   600  	}
   601  }
   602  
   603  // shouldBeCopied checks if we're supposed to make deepcopy methods the given type.
   604  //
   605  // This is the case if it's exported *and* either:
   606  // - has a partial manual DeepCopy implementation (in which case we fill in the rest)
   607  // - aliases to a non-basic type eventually
   608  // - is a struct
   609  func shouldBeCopied(pkg *loader.Package, info *markers.TypeInfo) bool {
   610  	if !ast.IsExported(info.Name) {
   611  		return false
   612  	}
   613  
   614  	typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name)
   615  	if typeInfo == types.Typ[types.Invalid] {
   616  		pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type: %s", info.Name), info.RawSpec))
   617  		return false
   618  	}
   619  
   620  	// according to gengo, everything named is an alias, except for an alias to a pointer,
   621  	// which is just a pointer, afaict.  Just roll with it.
   622  	if asPtr, isPtr := typeInfo.(*types.Named).Underlying().(*types.Pointer); isPtr {
   623  		typeInfo = asPtr
   624  	}
   625  
   626  	lastType := typeInfo
   627  	if _, isNamed := typeInfo.(*types.Named); isNamed {
   628  		// if it has a manual deepcopy or deepcopyinto, we're fine
   629  		if hasAnyDeepCopyMethod(pkg, typeInfo) {
   630  			return true
   631  		}
   632  
   633  		for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() {
   634  			// if it has a manual deepcopy or deepcopyinto, we're fine
   635  			if hasAnyDeepCopyMethod(pkg, underlyingType) {
   636  				return true
   637  			}
   638  
   639  			// aliases to other things besides basics need copy methods
   640  			// (basics can be straight-up shallow-copied)
   641  			if _, isBasic := underlyingType.(*types.Basic); !isBasic {
   642  				return true
   643  			}
   644  		}
   645  	}
   646  
   647  	// structs are the only thing that's not a basic that's copiable by default
   648  	_, isStruct := lastType.(*types.Struct)
   649  	return isStruct
   650  }
   651  
   652  // hasDeepCopyMethod checks if this type has a manual DeepCopy method and if
   653  // the method has a pointer receiver.
   654  func hasDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) (bool, bool) {
   655  	deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopy")
   656  	if len(ind) != 1 {
   657  		// ignore embedded methods
   658  		return false, false
   659  	}
   660  	if deepCopyMethod == nil {
   661  		return false, false
   662  	}
   663  
   664  	methodSig := deepCopyMethod.Type().(*types.Signature)
   665  	if methodSig.Params() != nil && methodSig.Params().Len() != 0 {
   666  		return false, false
   667  	}
   668  	if methodSig.Results() == nil || methodSig.Results().Len() != 1 {
   669  		return false, false
   670  	}
   671  
   672  	recvAsPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer)
   673  	if recvIsPtr {
   674  		// NB(directxman12): the pointer type returned here isn't comparable even though they
   675  		// have the same underlying type, for some reason (probably that
   676  		// LookupFieldOrMethod calls types.NewPointer for us), so check the
   677  		// underlying values.
   678  
   679  		resultPtr, resultIsPtr := methodSig.Results().At(0).Type().(*types.Pointer)
   680  		if !resultIsPtr {
   681  			// pointer vs non-pointer are different types
   682  			return false, false
   683  		}
   684  
   685  		if recvAsPtr.Elem() != resultPtr.Elem() {
   686  			return false, false
   687  		}
   688  	} else if methodSig.Results().At(0).Type() != methodSig.Recv().Type() {
   689  		return false, false
   690  	}
   691  
   692  	return true, recvIsPtr
   693  }
   694  
   695  // hasDeepCopyIntoMethod checks if this type has a manual DeepCopyInto method.
   696  func hasDeepCopyIntoMethod(pkg *loader.Package, typeInfo types.Type) bool {
   697  	deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopyInto")
   698  	if len(ind) != 1 {
   699  		// ignore embedded methods
   700  		return false
   701  	}
   702  	if deepCopyMethod == nil {
   703  		return false
   704  	}
   705  
   706  	methodSig := deepCopyMethod.Type().(*types.Signature)
   707  	if methodSig.Params() == nil || methodSig.Params().Len() != 1 {
   708  		return false
   709  	}
   710  	paramPtr, isPtr := methodSig.Params().At(0).Type().(*types.Pointer)
   711  	if !isPtr {
   712  		return false
   713  	}
   714  	if methodSig.Results() != nil && methodSig.Results().Len() != 0 {
   715  		return false
   716  	}
   717  
   718  	if recvPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer); recvIsPtr {
   719  		// NB(directxman12): the pointer type returned here isn't comparable even though they
   720  		// have the same underlying type, for some reason (probably that
   721  		// LookupFieldOrMethod calls types.NewPointer for us), so check the
   722  		// underlying values.
   723  		return paramPtr.Elem() == recvPtr.Elem()
   724  	}
   725  	return methodSig.Recv().Type() == paramPtr.Elem()
   726  }
   727  
   728  // hasAnyDeepCopyMethod checks if the given method has DeepCopy or DeepCopyInto
   729  // (either of which implies the other will exist eventually).
   730  func hasAnyDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) bool {
   731  	hasDeepCopy, _ := hasDeepCopyMethod(pkg, typeInfo)
   732  	return hasDeepCopy || hasDeepCopyIntoMethod(pkg, typeInfo)
   733  }
   734  
   735  // eventualUnderlyingType gets the "final" type in a sequence of named aliases.
   736  // It's effectively a shortcut for calling Underlying in a loop.
   737  func eventualUnderlyingType(typeInfo types.Type) types.Type {
   738  	for {
   739  		underlying := typeInfo.Underlying()
   740  		if underlying == typeInfo {
   741  			break
   742  		}
   743  		typeInfo = underlying
   744  	}
   745  	return typeInfo
   746  }
   747  
   748  // fineToShallowCopy checks if a shallow-copying a type is equivalent to deepcopy-ing it.
   749  func fineToShallowCopy(typeInfo types.Type) bool {
   750  	switch typeInfo := typeInfo.(type) {
   751  	case *types.Basic:
   752  		// basic types (int, string, etc) are always fine to shallow-copy,
   753  		// except for Invalid and UnsafePointer, which can't be copied at all.
   754  		switch typeInfo.Kind() {
   755  		case types.Invalid, types.UnsafePointer:
   756  			return false
   757  		default:
   758  			return true
   759  		}
   760  	case *types.Named:
   761  		// aliases are fine to shallow-copy as long as they resolve to a shallow-copyable type
   762  		return fineToShallowCopy(typeInfo.Underlying())
   763  	case *types.Struct:
   764  		// structs are fine to shallow-copy if they have all shallow-copyable fields
   765  		for i := 0; i < typeInfo.NumFields(); i++ {
   766  			field := typeInfo.Field(i)
   767  			if !fineToShallowCopy(field.Type()) {
   768  				return false
   769  			}
   770  		}
   771  		return true
   772  	default:
   773  		return false
   774  	}
   775  }
   776  
   777  // passesByReference checks if the given type passesByReference
   778  // (except for interfaces, which are handled separately).
   779  func passesByReference(typeInfo types.Type) bool {
   780  	switch typeInfo.(type) {
   781  	case *types.Slice:
   782  		return true
   783  	case *types.Map:
   784  		return true
   785  	case *types.Pointer:
   786  		return true
   787  	default:
   788  		return false
   789  	}
   790  }
   791  
   792  var (
   793  	// ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a pointer receiver.
   794  	ptrDeepCopy = `
   795  // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s.
   796  func (in *%[1]s) DeepCopy() *%[1]s {
   797  	if in == nil { return nil }
   798  	out := new(%[1]s)
   799  	in.DeepCopyInto(out)
   800  	return out
   801  }
   802  `
   803  
   804  	// ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a non-pointer receiver.
   805  	bareDeepCopy = `
   806  // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s.
   807  func (in %[1]s) DeepCopy() %[1]s {
   808  	if in == nil { return nil }
   809  	out := new(%[1]s)
   810  	in.DeepCopyInto(out)
   811  	return *out
   812  }
   813  `
   814  
   815  	// ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a pointer receiver.
   816  	ptrDeepCopyObj = `
   817  // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
   818  func (in *%[1]s) DeepCopyObject() %[2]s.Object {
   819  	if c := in.DeepCopy(); c != nil {
   820  		return c
   821  	}
   822  	return nil
   823  }
   824  `
   825  	// ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a non-pointer receiver.
   826  	bareDeepCopyObj = `
   827  // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
   828  func (in %[1]s) DeepCopyObject() %[2]s.Object {
   829  	return in.DeepCopy()
   830  }
   831  `
   832  )