github.com/christarazi/controller-tools@v0.3.1-0.20210907042920-aa94049173f8/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  		// basic types themselves can be "shallow" copied, so all we need
   297  		// to do is check if our *actual* type (not the underlying one) has
   298  		// a custom method implemented.
   299  		if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod {
   300  			c.Line("*out = in.DeepCopy()")
   301  		}
   302  		c.Line("*out = *in")
   303  	case *types.Map:
   304  		c.genMapDeepCopy(actualName, last)
   305  	case *types.Slice:
   306  		c.genSliceDeepCopy(actualName, last)
   307  	case *types.Struct:
   308  		c.genStructDeepCopy(actualName, last)
   309  	case *types.Pointer:
   310  		c.genPointerDeepCopy(actualName, last)
   311  	case *types.Named:
   312  		// handled via the above loop, should never happen
   313  		c.pkg.AddError(fmt.Errorf("interface type %s encountered directly, invalid condition", last))
   314  	default:
   315  		c.pkg.AddError(fmt.Errorf("invalid type %s", last))
   316  	}
   317  }
   318  
   319  // genMapDeepCopy generates DeepCopy code for the given named type whose eventual
   320  // type is the given map type.
   321  func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.Map) {
   322  	// maps *must* have shallow-copiable types, since we just iterate
   323  	// through the keys, only trying to deepcopy the values.
   324  	if !fineToShallowCopy(mapType.Key()) {
   325  		c.pkg.AddError(fmt.Errorf("invalid map key type %s", mapType.Key()))
   326  		return
   327  	}
   328  
   329  	// make our actual type (not the underlying one)...
   330  	c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList))
   331  
   332  	// ...and copy each element appropriately
   333  	c.For("key, val := range *in", func() {
   334  		// check if we have manually written methods,
   335  		// in which case we'll just try and use those
   336  		hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, mapType.Elem())
   337  		hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, mapType.Elem())
   338  		switch {
   339  		case hasDeepCopyInto || hasDeepCopy:
   340  			// use the manually-written methods
   341  			_, fieldIsPtr := mapType.Elem().(*types.Pointer)                       // is "out" actually a pointer
   342  			inIsPtr := resultWillBePointer(mapType.Elem(), hasDeepCopy, copyOnPtr) // does copying "in" produce a pointer
   343  			if hasDeepCopy {
   344  				// If we're calling DeepCopy, check if it's receiver needs a pointer
   345  				inIsPtr = copyOnPtr
   346  			}
   347  			if inIsPtr == fieldIsPtr {
   348  				c.Line("(*out)[key] = val.DeepCopy()")
   349  			} else if fieldIsPtr {
   350  				c.Line("{") // use a block because we use `x` as a temporary
   351  				c.Line("x := val.DeepCopy()")
   352  				c.Line("(*out)[key] = &x")
   353  				c.Line("}")
   354  			} else {
   355  				c.Line("(*out)[key] = *val.DeepCopy()")
   356  			}
   357  		case fineToShallowCopy(mapType.Elem()):
   358  			// just shallow copy types for which it's safe to do so
   359  			c.Line("(*out)[key] = val")
   360  		default:
   361  			// otherwise, we've got some kind-specific actions,
   362  			// based on the element's eventual type.
   363  
   364  			underlyingElem := eventualUnderlyingType(mapType.Elem())
   365  
   366  			// if it passes by reference, let the main switch handle it
   367  			if passesByReference(underlyingElem) {
   368  				c.Linef("var outVal %[1]s", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList))
   369  				c.IfElse("val == nil", func() {
   370  					c.Line("(*out)[key] = nil")
   371  				}, func() {
   372  					c.Line("in, out := &val, &outVal")
   373  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: mapType.Elem()}, mapType.Elem())
   374  				})
   375  				c.Line("(*out)[key] = outVal")
   376  
   377  				return
   378  			}
   379  
   380  			// otherwise...
   381  			switch underlyingElem := underlyingElem.(type) {
   382  			case *types.Struct:
   383  				// structs will have deepcopy generated for them, so use that
   384  				c.Line("(*out)[key] = *val.DeepCopy()")
   385  			default:
   386  				c.pkg.AddError(fmt.Errorf("invalid map value type %s", underlyingElem))
   387  				return
   388  			}
   389  		}
   390  	})
   391  }
   392  
   393  // genSliceDeepCopy generates DeepCopy code for the given named type whose
   394  // underlying type is the given slice.
   395  func (c *copyMethodMaker) genSliceDeepCopy(actualName *namingInfo, sliceType *types.Slice) {
   396  	underlyingElem := eventualUnderlyingType(sliceType.Elem())
   397  
   398  	// make the actual type (not the underlying)
   399  	c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList))
   400  
   401  	// check if we need to do anything special, or just copy each element appropriately
   402  	switch {
   403  	case hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()):
   404  		// just use deepcopy if it's present (deepcopyinto will be filled in by our code)
   405  		c.For("i := range *in", func() {
   406  			c.Line("(*in)[i].DeepCopyInto(&(*out)[i])")
   407  		})
   408  	case fineToShallowCopy(underlyingElem):
   409  		// shallow copy if ok
   410  		c.Line("copy(*out, *in)")
   411  	default:
   412  		// copy each element appropriately
   413  		c.For("i := range *in", func() {
   414  			// fall back to normal code for reference types or those with custom logic
   415  			if passesByReference(underlyingElem) || hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()) {
   416  				c.If("(*in)[i] != nil", func() {
   417  					c.Line("in, out := &(*in)[i], &(*out)[i]")
   418  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: sliceType.Elem()}, sliceType.Elem())
   419  				})
   420  				return
   421  			}
   422  
   423  			switch underlyingElem.(type) {
   424  			case *types.Struct:
   425  				// structs will always have deepcopy
   426  				c.Linef("(*in)[i].DeepCopyInto(&(*out)[i])")
   427  			default:
   428  				c.pkg.AddError(fmt.Errorf("invalid slice element type %s", underlyingElem))
   429  			}
   430  		})
   431  	}
   432  }
   433  
   434  // genStructDeepCopy generates DeepCopy code for the given named type whose
   435  // underlying type is the given struct.
   436  func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Struct) {
   437  	c.Line("*out = *in")
   438  
   439  	for i := 0; i < structType.NumFields(); i++ {
   440  		field := structType.Field(i)
   441  
   442  		// if we have a manual deepcopy, use that
   443  		hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, field.Type())
   444  		hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, field.Type())
   445  		if hasDeepCopyInto || hasDeepCopy {
   446  			// NB(directxman12): yes, I know this is kind-of weird that we
   447  			// have all this special-casing here, but it's nice for testing
   448  			// purposes to be 1-to-1 with deepcopy-gen, which does all sorts of
   449  			// stuff like this (I'm pretty sure I found some codepaths that
   450  			// never execute there, because they're pretty clearly invalid
   451  			// syntax).
   452  
   453  			_, fieldIsPtr := field.Type().(*types.Pointer)
   454  			inIsPtr := resultWillBePointer(field.Type(), hasDeepCopy, copyOnPtr)
   455  			if fieldIsPtr {
   456  				// we'll need a if block to check for nilness
   457  				// we'll let genDeepCopyIntoBlock handle the details, we just needed the setup
   458  				c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() {
   459  					c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name())
   460  					c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type())
   461  				})
   462  			} else {
   463  				// special-case for compatibility with deepcopy-gen
   464  				if inIsPtr == fieldIsPtr {
   465  					c.Linef("out.%[1]s = in.%[1]s.DeepCopy()", field.Name())
   466  				} else {
   467  					c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name())
   468  				}
   469  			}
   470  			continue
   471  		}
   472  
   473  		// pass-by-reference fields get delegated to the main type
   474  		underlyingField := eventualUnderlyingType(field.Type())
   475  		if passesByReference(underlyingField) {
   476  			c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() {
   477  				c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name())
   478  				c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type())
   479  			})
   480  			continue
   481  		}
   482  
   483  		// otherwise...
   484  		switch underlyingField := underlyingField.(type) {
   485  		case *types.Basic:
   486  			// nothing to do, initial assignment copied this
   487  		case *types.Struct:
   488  			if fineToShallowCopy(field.Type()) {
   489  				c.Linef("out.%[1]s = in.%[1]s", field.Name())
   490  			} else {
   491  				c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name())
   492  			}
   493  		default:
   494  			c.pkg.AddError(fmt.Errorf("invalid field type %s", underlyingField))
   495  			return
   496  		}
   497  	}
   498  }
   499  
   500  // genPointerDeepCopy generates DeepCopy code for the given named type whose
   501  // underlying type is the given struct.
   502  func (c *copyMethodMaker) genPointerDeepCopy(_ *namingInfo, pointerType *types.Pointer) {
   503  	underlyingElem := eventualUnderlyingType(pointerType.Elem())
   504  
   505  	// if we have a manually written deepcopy, just use that
   506  	hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, pointerType.Elem())
   507  	hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, pointerType.Elem())
   508  	if hasDeepCopyInto || hasDeepCopy {
   509  		outNeedsPtr := resultWillBePointer(pointerType.Elem(), hasDeepCopy, copyOnPtr)
   510  		if hasDeepCopy {
   511  			outNeedsPtr = copyOnPtr
   512  		}
   513  		if outNeedsPtr {
   514  			c.Line("*out = (*in).DeepCopy()")
   515  		} else {
   516  			c.Line("x := (*in).DeepCopy()")
   517  			c.Line("*out = &x")
   518  		}
   519  		return
   520  	}
   521  
   522  	// shallow-copiable types are pretty easy
   523  	if fineToShallowCopy(underlyingElem) {
   524  		c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList))
   525  		c.Line("**out = **in")
   526  		return
   527  	}
   528  
   529  	// pass-by-reference types get delegated to the main switch
   530  	if passesByReference(underlyingElem) {
   531  		c.Linef("*out = new(%s)", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList))
   532  		c.If("**in != nil", func() {
   533  			c.Line("in, out := *in, *out")
   534  			c.genDeepCopyIntoBlock(&namingInfo{typeInfo: underlyingElem}, eventualUnderlyingType(underlyingElem))
   535  		})
   536  		return
   537  	}
   538  
   539  	// otherwise...
   540  	switch underlyingElem := underlyingElem.(type) {
   541  	case *types.Struct:
   542  		c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList))
   543  		c.Line("(*in).DeepCopyInto(*out)")
   544  	default:
   545  		c.pkg.AddError(fmt.Errorf("invalid pointer element type %s", underlyingElem))
   546  		return
   547  	}
   548  }
   549  
   550  // usePtrReceiver checks if we need a pointer receiver on methods for the given type
   551  // Pass-by-reference types don't get pointer receivers.
   552  func usePtrReceiver(typeInfo types.Type) bool {
   553  	switch typeInfo.(type) {
   554  	case *types.Pointer:
   555  		return false
   556  	case *types.Map:
   557  		return false
   558  	case *types.Slice:
   559  		return false
   560  	case *types.Named:
   561  		return usePtrReceiver(typeInfo.Underlying())
   562  	default:
   563  		return true
   564  	}
   565  }
   566  
   567  func resultWillBePointer(typeInfo types.Type, hasDeepCopy, deepCopyOnPtr bool) bool {
   568  	// if we have a manual deepcopy, we can just check what that returns
   569  	if hasDeepCopy {
   570  		return deepCopyOnPtr
   571  	}
   572  
   573  	// otherwise, we'll need to check its type
   574  	switch typeInfo := typeInfo.(type) {
   575  	case *types.Pointer:
   576  		// NB(directxman12): we don't have to worry about the elem having a deepcopy,
   577  		// since hasManualDeepCopy would've caught that.
   578  
   579  		// we'll be calling on the elem, so check that
   580  		return resultWillBePointer(typeInfo.Elem(), false, false)
   581  	case *types.Map:
   582  		return false
   583  	case *types.Slice:
   584  		return false
   585  	case *types.Named:
   586  		return resultWillBePointer(typeInfo.Underlying(), false, false)
   587  	default:
   588  		return true
   589  	}
   590  }
   591  
   592  // shouldBeCopied checks if we're supposed to make deepcopy methods the given type.
   593  //
   594  // This is the case if it's exported *and* either:
   595  // - has a partial manual DeepCopy implementation (in which case we fill in the rest)
   596  // - aliases to a non-basic type eventually
   597  // - is a struct
   598  func shouldBeCopied(pkg *loader.Package, info *markers.TypeInfo) bool {
   599  	if !ast.IsExported(info.Name) {
   600  		return false
   601  	}
   602  
   603  	typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name)
   604  	if typeInfo == types.Typ[types.Invalid] {
   605  		pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec))
   606  		return false
   607  	}
   608  
   609  	// according to gengo, everything named is an alias, except for an alias to a pointer,
   610  	// which is just a pointer, afaict.  Just roll with it.
   611  	if asPtr, isPtr := typeInfo.(*types.Named).Underlying().(*types.Pointer); isPtr {
   612  		typeInfo = asPtr
   613  	}
   614  
   615  	lastType := typeInfo
   616  	if _, isNamed := typeInfo.(*types.Named); isNamed {
   617  		// if it has a manual deepcopy or deepcopyinto, we're fine
   618  		if hasAnyDeepCopyMethod(pkg, typeInfo) {
   619  			return true
   620  		}
   621  
   622  		for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() {
   623  			// if it has a manual deepcopy or deepcopyinto, we're fine
   624  			if hasAnyDeepCopyMethod(pkg, underlyingType) {
   625  				return true
   626  			}
   627  
   628  			// aliases to other things besides basics need copy methods
   629  			// (basics can be straight-up shallow-copied)
   630  			if _, isBasic := underlyingType.(*types.Basic); !isBasic {
   631  				return true
   632  			}
   633  		}
   634  	}
   635  
   636  	// structs are the only thing that's not a basic that's copiable by default
   637  	_, isStruct := lastType.(*types.Struct)
   638  	return isStruct
   639  }
   640  
   641  // hasDeepCopyMethod checks if this type has a manual DeepCopy method and if
   642  // the method has a pointer receiver.
   643  func hasDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) (bool, bool) {
   644  	deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopy")
   645  	if len(ind) != 1 {
   646  		// ignore embedded methods
   647  		return false, false
   648  	}
   649  	if deepCopyMethod == nil {
   650  		return false, false
   651  	}
   652  
   653  	methodSig := deepCopyMethod.Type().(*types.Signature)
   654  	if methodSig.Params() != nil && methodSig.Params().Len() != 0 {
   655  		return false, false
   656  	}
   657  	if methodSig.Results() == nil || methodSig.Results().Len() != 1 {
   658  		return false, false
   659  	}
   660  
   661  	recvAsPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer)
   662  	if recvIsPtr {
   663  		// NB(directxman12): the pointer type returned here isn't comparable even though they
   664  		// have the same underlying type, for some reason (probably that
   665  		// LookupFieldOrMethod calls types.NewPointer for us), so check the
   666  		// underlying values.
   667  
   668  		resultPtr, resultIsPtr := methodSig.Results().At(0).Type().(*types.Pointer)
   669  		if !resultIsPtr {
   670  			// pointer vs non-pointer are different types
   671  			return false, false
   672  		}
   673  
   674  		if recvAsPtr.Elem() != resultPtr.Elem() {
   675  			return false, false
   676  		}
   677  	} else if methodSig.Results().At(0).Type() != methodSig.Recv().Type() {
   678  		return false, false
   679  	}
   680  
   681  	return true, recvIsPtr
   682  }
   683  
   684  // hasDeepCopyIntoMethod checks if this type has a manual DeepCopyInto method.
   685  func hasDeepCopyIntoMethod(pkg *loader.Package, typeInfo types.Type) bool {
   686  	deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopyInto")
   687  	if len(ind) != 1 {
   688  		// ignore embedded methods
   689  		return false
   690  	}
   691  	if deepCopyMethod == nil {
   692  		return false
   693  	}
   694  
   695  	methodSig := deepCopyMethod.Type().(*types.Signature)
   696  	if methodSig.Params() == nil || methodSig.Params().Len() != 1 {
   697  		return false
   698  	}
   699  	paramPtr, isPtr := methodSig.Params().At(0).Type().(*types.Pointer)
   700  	if !isPtr {
   701  		return false
   702  	}
   703  	if methodSig.Results() != nil && methodSig.Results().Len() != 0 {
   704  		return false
   705  	}
   706  
   707  	if recvPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer); recvIsPtr {
   708  		// NB(directxman12): the pointer type returned here isn't comparable even though they
   709  		// have the same underlying type, for some reason (probably that
   710  		// LookupFieldOrMethod calls types.NewPointer for us), so check the
   711  		// underlying values.
   712  		return paramPtr.Elem() == recvPtr.Elem()
   713  	}
   714  	return methodSig.Recv().Type() == paramPtr.Elem()
   715  }
   716  
   717  // hasAnyDeepCopyMethod checks if the given method has DeepCopy or DeepCopyInto
   718  // (either of which implies the other will exist eventually).
   719  func hasAnyDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) bool {
   720  	hasDeepCopy, _ := hasDeepCopyMethod(pkg, typeInfo)
   721  	return hasDeepCopy || hasDeepCopyIntoMethod(pkg, typeInfo)
   722  }
   723  
   724  // eventualUnderlyingType gets the "final" type in a sequence of named aliases.
   725  // It's effectively a shortcut for calling Underlying in a loop.
   726  func eventualUnderlyingType(typeInfo types.Type) types.Type {
   727  	last := typeInfo
   728  	for underlying := typeInfo.Underlying(); underlying != last; last, underlying = underlying, underlying.Underlying() {
   729  		// get the actual underlying type
   730  	}
   731  	return last
   732  }
   733  
   734  // fineToShallowCopy checks if a shallow-copying a type is equivalent to deepcopy-ing it.
   735  func fineToShallowCopy(typeInfo types.Type) bool {
   736  	switch typeInfo := typeInfo.(type) {
   737  	case *types.Basic:
   738  		// basic types (int, string, etc) are always fine to shallow-copy
   739  		return true
   740  	case *types.Named:
   741  		// aliases are fine to shallow-copy as long as they resolve to a shallow-copyable type
   742  		return fineToShallowCopy(typeInfo.Underlying())
   743  	case *types.Struct:
   744  		// structs are fine to shallow-copy if they have all shallow-copyable fields
   745  		for i := 0; i < typeInfo.NumFields(); i++ {
   746  			field := typeInfo.Field(i)
   747  			if !fineToShallowCopy(field.Type()) {
   748  				return false
   749  			}
   750  		}
   751  		return true
   752  	default:
   753  		return false
   754  	}
   755  }
   756  
   757  // passesByReference checks if the given type passesByReference
   758  // (except for interfaces, which are handled separately).
   759  func passesByReference(typeInfo types.Type) bool {
   760  	switch typeInfo.(type) {
   761  	case *types.Slice:
   762  		return true
   763  	case *types.Map:
   764  		return true
   765  	case *types.Pointer:
   766  		return true
   767  	default:
   768  		return false
   769  	}
   770  }
   771  
   772  var (
   773  	// ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a pointer receiver.
   774  	ptrDeepCopy = `
   775  // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s.
   776  func (in *%[1]s) DeepCopy() *%[1]s {
   777  	if in == nil { return nil }
   778  	out := new(%[1]s)
   779  	in.DeepCopyInto(out)
   780  	return out
   781  }
   782  `
   783  
   784  	// ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a non-pointer receiver.
   785  	bareDeepCopy = `
   786  // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s.
   787  func (in %[1]s) DeepCopy() %[1]s {
   788  	if in == nil { return nil }
   789  	out := new(%[1]s)
   790  	in.DeepCopyInto(out)
   791  	return *out
   792  }
   793  `
   794  
   795  	// ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a pointer receiver.
   796  	ptrDeepCopyObj = `
   797  // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
   798  func (in *%[1]s) DeepCopyObject() %[2]s.Object {
   799  	if c := in.DeepCopy(); c != nil {
   800  		return c
   801  	}
   802  	return nil
   803  }
   804  `
   805  	// ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a non-pointer receiver.
   806  	bareDeepCopyObj = `
   807  // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
   808  func (in %[1]s) DeepCopyObject() %[2]s.Object {
   809  	return in.DeepCopy()
   810  }
   811  `
   812  )