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