1/*
2Copyright 2015 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package generators
18
19import (
20	"fmt"
21	"io"
22	"path/filepath"
23	"sort"
24	"strings"
25
26	"k8s.io/gengo/args"
27	"k8s.io/gengo/examples/set-gen/sets"
28	"k8s.io/gengo/generator"
29	"k8s.io/gengo/namer"
30	"k8s.io/gengo/types"
31
32	"k8s.io/klog"
33)
34
35// CustomArgs is used tby the go2idl framework to pass args specific to this
36// generator.
37type CustomArgs struct {
38	BoundingDirs []string // Only deal with types rooted under these dirs.
39}
40
41// This is the comment tag that carries parameters for deep-copy generation.
42const (
43	tagEnabledName              = "k8s:deepcopy-gen"
44	interfacesTagName           = tagEnabledName + ":interfaces"
45	interfacesNonPointerTagName = tagEnabledName + ":nonpointer-interfaces" // attach the DeepCopy<Interface> methods to the
46)
47
48// Known values for the comment tag.
49const tagValuePackage = "package"
50
51// enabledTagValue holds parameters from a tagName tag.
52type enabledTagValue struct {
53	value    string
54	register bool
55}
56
57func extractEnabledTypeTag(t *types.Type) *enabledTagValue {
58	comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
59	return extractEnabledTag(comments)
60}
61
62func extractEnabledTag(comments []string) *enabledTagValue {
63	tagVals := types.ExtractCommentTags("+", comments)[tagEnabledName]
64	if tagVals == nil {
65		// No match for the tag.
66		return nil
67	}
68	// If there are multiple values, abort.
69	if len(tagVals) > 1 {
70		klog.Fatalf("Found %d %s tags: %q", len(tagVals), tagEnabledName, tagVals)
71	}
72
73	// If we got here we are returning something.
74	tag := &enabledTagValue{}
75
76	// Get the primary value.
77	parts := strings.Split(tagVals[0], ",")
78	if len(parts) >= 1 {
79		tag.value = parts[0]
80	}
81
82	// Parse extra arguments.
83	parts = parts[1:]
84	for i := range parts {
85		kv := strings.SplitN(parts[i], "=", 2)
86		k := kv[0]
87		v := ""
88		if len(kv) == 2 {
89			v = kv[1]
90		}
91		switch k {
92		case "register":
93			if v != "false" {
94				tag.register = true
95			}
96		default:
97			klog.Fatalf("Unsupported %s param: %q", tagEnabledName, parts[i])
98		}
99	}
100	return tag
101}
102
103// TODO: This is created only to reduce number of changes in a single PR.
104// Remove it and use PublicNamer instead.
105func deepCopyNamer() *namer.NameStrategy {
106	return &namer.NameStrategy{
107		Join: func(pre string, in []string, post string) string {
108			return strings.Join(in, "_")
109		},
110		PrependPackageNames: 1,
111	}
112}
113
114// NameSystems returns the name system used by the generators in this package.
115func NameSystems() namer.NameSystems {
116	return namer.NameSystems{
117		"public": deepCopyNamer(),
118		"raw":    namer.NewRawNamer("", nil),
119	}
120}
121
122// DefaultNameSystem returns the default name system for ordering the types to be
123// processed by the generators in this package.
124func DefaultNameSystem() string {
125	return "public"
126}
127
128func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
129	boilerplate, err := arguments.LoadGoBoilerplate()
130	if err != nil {
131		klog.Fatalf("Failed loading boilerplate: %v", err)
132	}
133
134	inputs := sets.NewString(context.Inputs...)
135	packages := generator.Packages{}
136	header := append([]byte(fmt.Sprintf("// +build !%s\n\n", arguments.GeneratedBuildTag)), boilerplate...)
137
138	boundingDirs := []string{}
139	if customArgs, ok := arguments.CustomArgs.(*CustomArgs); ok {
140		if customArgs.BoundingDirs == nil {
141			customArgs.BoundingDirs = context.Inputs
142		}
143		for i := range customArgs.BoundingDirs {
144			// Strip any trailing slashes - they are not exactly "correct" but
145			// this is friendlier.
146			boundingDirs = append(boundingDirs, strings.TrimRight(customArgs.BoundingDirs[i], "/"))
147		}
148	}
149
150	for i := range inputs {
151		klog.V(5).Infof("Considering pkg %q", i)
152		pkg := context.Universe[i]
153		if pkg == nil {
154			// If the input had no Go files, for example.
155			continue
156		}
157
158		ptag := extractEnabledTag(pkg.Comments)
159		ptagValue := ""
160		ptagRegister := false
161		if ptag != nil {
162			ptagValue = ptag.value
163			if ptagValue != tagValuePackage {
164				klog.Fatalf("Package %v: unsupported %s value: %q", i, tagEnabledName, ptagValue)
165			}
166			ptagRegister = ptag.register
167			klog.V(5).Infof("  tag.value: %q, tag.register: %t", ptagValue, ptagRegister)
168		} else {
169			klog.V(5).Infof("  no tag")
170		}
171
172		// If the pkg-scoped tag says to generate, we can skip scanning types.
173		pkgNeedsGeneration := (ptagValue == tagValuePackage)
174		if !pkgNeedsGeneration {
175			// If the pkg-scoped tag did not exist, scan all types for one that
176			// explicitly wants generation.
177			for _, t := range pkg.Types {
178				klog.V(5).Infof("  considering type %q", t.Name.String())
179				ttag := extractEnabledTypeTag(t)
180				if ttag != nil && ttag.value == "true" {
181					klog.V(5).Infof("    tag=true")
182					if !copyableType(t) {
183						klog.Fatalf("Type %v requests deepcopy generation but is not copyable", t)
184					}
185					pkgNeedsGeneration = true
186					break
187				}
188			}
189		}
190
191		if pkgNeedsGeneration {
192			klog.V(3).Infof("Package %q needs generation", i)
193			path := pkg.Path
194			// if the source path is within a /vendor/ directory (for example,
195			// k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/apis/meta/v1), allow
196			// generation to output to the proper relative path (under vendor).
197			// Otherwise, the generator will create the file in the wrong location
198			// in the output directory.
199			// TODO: build a more fundamental concept in gengo for dealing with modifications
200			// to vendored packages.
201			if strings.HasPrefix(pkg.SourcePath, arguments.OutputBase) {
202				expandedPath := strings.TrimPrefix(pkg.SourcePath, arguments.OutputBase)
203				if strings.Contains(expandedPath, "/vendor/") {
204					path = expandedPath
205				}
206			}
207			packages = append(packages,
208				&generator.DefaultPackage{
209					PackageName: strings.Split(filepath.Base(pkg.Path), ".")[0],
210					PackagePath: path,
211					HeaderText:  header,
212					GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
213						return []generator.Generator{
214							NewGenDeepCopy(arguments.OutputFileBaseName, pkg.Path, boundingDirs, (ptagValue == tagValuePackage), ptagRegister),
215						}
216					},
217					FilterFunc: func(c *generator.Context, t *types.Type) bool {
218						return t.Name.Package == pkg.Path
219					},
220				})
221		}
222	}
223	return packages
224}
225
226// genDeepCopy produces a file with autogenerated deep-copy functions.
227type genDeepCopy struct {
228	generator.DefaultGen
229	targetPackage string
230	boundingDirs  []string
231	allTypes      bool
232	registerTypes bool
233	imports       namer.ImportTracker
234	typesForInit  []*types.Type
235}
236
237func NewGenDeepCopy(sanitizedName, targetPackage string, boundingDirs []string, allTypes, registerTypes bool) generator.Generator {
238	return &genDeepCopy{
239		DefaultGen: generator.DefaultGen{
240			OptionalName: sanitizedName,
241		},
242		targetPackage: targetPackage,
243		boundingDirs:  boundingDirs,
244		allTypes:      allTypes,
245		registerTypes: registerTypes,
246		imports:       generator.NewImportTracker(),
247		typesForInit:  make([]*types.Type, 0),
248	}
249}
250
251func (g *genDeepCopy) Namers(c *generator.Context) namer.NameSystems {
252	// Have the raw namer for this file track what it imports.
253	return namer.NameSystems{
254		"raw": namer.NewRawNamer(g.targetPackage, g.imports),
255	}
256}
257
258func (g *genDeepCopy) Filter(c *generator.Context, t *types.Type) bool {
259	// Filter out types not being processed or not copyable within the package.
260	enabled := g.allTypes
261	if !enabled {
262		ttag := extractEnabledTypeTag(t)
263		if ttag != nil && ttag.value == "true" {
264			enabled = true
265		}
266	}
267	if !enabled {
268		return false
269	}
270	if !copyableType(t) {
271		klog.V(2).Infof("Type %v is not copyable", t)
272		return false
273	}
274	klog.V(4).Infof("Type %v is copyable", t)
275	g.typesForInit = append(g.typesForInit, t)
276	return true
277}
278
279func (g *genDeepCopy) copyableAndInBounds(t *types.Type) bool {
280	if !copyableType(t) {
281		return false
282	}
283	// Only packages within the restricted range can be processed.
284	if !isRootedUnder(t.Name.Package, g.boundingDirs) {
285		return false
286	}
287	return true
288}
289
290// deepCopyMethod returns the signature of a DeepCopy() method, nil or an error
291// if the type does not match. This allows more efficient deep copy
292// implementations to be defined by the type's author.  The correct signature
293// for a type T is:
294//    func (t T) DeepCopy() T
295// or:
296//    func (t *T) DeepCopy() *T
297func deepCopyMethod(t *types.Type) (*types.Signature, error) {
298	f, found := t.Methods["DeepCopy"]
299	if !found {
300		return nil, nil
301	}
302	if len(f.Signature.Parameters) != 0 {
303		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no parameters", t)
304	}
305	if len(f.Signature.Results) != 1 {
306		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one result", t)
307	}
308
309	ptrResult := f.Signature.Results[0].Kind == types.Pointer && f.Signature.Results[0].Elem.Name == t.Name
310	nonPtrResult := f.Signature.Results[0].Name == t.Name
311
312	if !ptrResult && !nonPtrResult {
313		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected to return %s or *%s", t, t.Name.Name, t.Name.Name)
314	}
315
316	ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
317	nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
318
319	if ptrRcvr && !ptrResult {
320		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a *%s result for a *%s receiver", t, t.Name.Name, t.Name.Name)
321	}
322	if nonPtrRcvr && !nonPtrResult {
323		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a %s result for a %s receiver", t, t.Name.Name, t.Name.Name)
324	}
325
326	return f.Signature, nil
327}
328
329// deepCopyMethodOrDie returns the signatrue of a DeepCopy method, nil or calls klog.Fatalf
330// if the type does not match.
331func deepCopyMethodOrDie(t *types.Type) *types.Signature {
332	ret, err := deepCopyMethod(t)
333	if err != nil {
334		klog.Fatal(err)
335	}
336	return ret
337}
338
339// deepCopyIntoMethod returns the signature of a DeepCopyInto() method, nil or an error
340// if the type is wrong. DeepCopyInto allows more efficient deep copy
341// implementations to be defined by the type's author.  The correct signature
342// for a type T is:
343//    func (t T) DeepCopyInto(t *T)
344// or:
345//    func (t *T) DeepCopyInto(t *T)
346func deepCopyIntoMethod(t *types.Type) (*types.Signature, error) {
347	f, found := t.Methods["DeepCopyInto"]
348	if !found {
349		return nil, nil
350	}
351	if len(f.Signature.Parameters) != 1 {
352		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected exactly one parameter", t)
353	}
354	if len(f.Signature.Results) != 0 {
355		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected no result type", t)
356	}
357
358	ptrParam := f.Signature.Parameters[0].Kind == types.Pointer && f.Signature.Parameters[0].Elem.Name == t.Name
359
360	if !ptrParam {
361		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected parameter of type *%s", t, t.Name.Name)
362	}
363
364	ptrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Kind == types.Pointer && f.Signature.Receiver.Elem.Name == t.Name
365	nonPtrRcvr := f.Signature.Receiver != nil && f.Signature.Receiver.Name == t.Name
366
367	if !ptrRcvr && !nonPtrRcvr {
368		// this should never happen
369		return nil, fmt.Errorf("type %v: invalid DeepCopy signature, expected a receiver of type %s or *%s", t, t.Name.Name, t.Name.Name)
370	}
371
372	return f.Signature, nil
373}
374
375// deepCopyIntoMethodOrDie returns the signature of a DeepCopyInto() method, nil or calls klog.Fatalf
376// if the type is wrong.
377func deepCopyIntoMethodOrDie(t *types.Type) *types.Signature {
378	ret, err := deepCopyIntoMethod(t)
379	if err != nil {
380		klog.Fatal(err)
381	}
382	return ret
383}
384
385func isRootedUnder(pkg string, roots []string) bool {
386	// Add trailing / to avoid false matches, e.g. foo/bar vs foo/barn.  This
387	// assumes that bounding dirs do not have trailing slashes.
388	pkg = pkg + "/"
389	for _, root := range roots {
390		if strings.HasPrefix(pkg, root+"/") {
391			return true
392		}
393	}
394	return false
395}
396
397func copyableType(t *types.Type) bool {
398	// If the type opts out of copy-generation, stop.
399	ttag := extractEnabledTypeTag(t)
400	if ttag != nil && ttag.value == "false" {
401		return false
402	}
403
404	// Filter out private types.
405	if namer.IsPrivateGoName(t.Name.Name) {
406		return false
407	}
408
409	if t.Kind == types.Alias {
410		// if the underlying built-in is not deepcopy-able, deepcopy is opt-in through definition of custom methods.
411		// Note that aliases of builtins, maps, slices can have deepcopy methods.
412		if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
413			return true
414		} else {
415			return t.Underlying.Kind != types.Builtin || copyableType(t.Underlying)
416		}
417	}
418
419	if t.Kind != types.Struct {
420		return false
421	}
422
423	return true
424}
425
426func underlyingType(t *types.Type) *types.Type {
427	for t.Kind == types.Alias {
428		t = t.Underlying
429	}
430	return t
431}
432
433func (g *genDeepCopy) isOtherPackage(pkg string) bool {
434	if pkg == g.targetPackage {
435		return false
436	}
437	if strings.HasSuffix(pkg, "\""+g.targetPackage+"\"") {
438		return false
439	}
440	return true
441}
442
443func (g *genDeepCopy) Imports(c *generator.Context) (imports []string) {
444	importLines := []string{}
445	for _, singleImport := range g.imports.ImportLines() {
446		if g.isOtherPackage(singleImport) {
447			importLines = append(importLines, singleImport)
448		}
449	}
450	return importLines
451}
452
453func argsFromType(ts ...*types.Type) generator.Args {
454	a := generator.Args{
455		"type": ts[0],
456	}
457	for i, t := range ts {
458		a[fmt.Sprintf("type%d", i+1)] = t
459	}
460	return a
461}
462
463func (g *genDeepCopy) Init(c *generator.Context, w io.Writer) error {
464	return nil
465}
466
467func (g *genDeepCopy) needsGeneration(t *types.Type) bool {
468	tag := extractEnabledTypeTag(t)
469	tv := ""
470	if tag != nil {
471		tv = tag.value
472		if tv != "true" && tv != "false" {
473			klog.Fatalf("Type %v: unsupported %s value: %q", t, tagEnabledName, tag.value)
474		}
475	}
476	if g.allTypes && tv == "false" {
477		// The whole package is being generated, but this type has opted out.
478		klog.V(5).Infof("Not generating for type %v because type opted out", t)
479		return false
480	}
481	if !g.allTypes && tv != "true" {
482		// The whole package is NOT being generated, and this type has NOT opted in.
483		klog.V(5).Infof("Not generating for type %v because type did not opt in", t)
484		return false
485	}
486	return true
487}
488
489func extractInterfacesTag(t *types.Type) []string {
490	var result []string
491	comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
492	values := types.ExtractCommentTags("+", comments)[interfacesTagName]
493	for _, v := range values {
494		if len(v) == 0 {
495			continue
496		}
497		intfs := strings.Split(v, ",")
498		for _, intf := range intfs {
499			if intf == "" {
500				continue
501			}
502			result = append(result, intf)
503		}
504	}
505	return result
506}
507
508func extractNonPointerInterfaces(t *types.Type) (bool, error) {
509	comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
510	values := types.ExtractCommentTags("+", comments)[interfacesNonPointerTagName]
511	if len(values) == 0 {
512		return false, nil
513	}
514	result := values[0] == "true"
515	for _, v := range values {
516		if v == "true" != result {
517			return false, fmt.Errorf("contradicting %v value %q found to previous value %v", interfacesNonPointerTagName, v, result)
518		}
519	}
520	return result, nil
521}
522
523func (g *genDeepCopy) deepCopyableInterfacesInner(c *generator.Context, t *types.Type) ([]*types.Type, error) {
524	if t.Kind != types.Struct {
525		return nil, nil
526	}
527
528	intfs := extractInterfacesTag(t)
529
530	var ts []*types.Type
531	for _, intf := range intfs {
532		t := types.ParseFullyQualifiedName(intf)
533		c.AddDir(t.Package)
534		intfT := c.Universe.Type(t)
535		if intfT == nil {
536			return nil, fmt.Errorf("unknown type %q in %s tag of type %s", intf, interfacesTagName, intfT)
537		}
538		if intfT.Kind != types.Interface {
539			return nil, fmt.Errorf("type %q in %s tag of type %s is not an interface, but: %q", intf, interfacesTagName, t, intfT.Kind)
540		}
541		g.imports.AddType(intfT)
542		ts = append(ts, intfT)
543	}
544
545	return ts, nil
546}
547
548// deepCopyableInterfaces returns the interface types to implement and whether they apply to a non-pointer receiver.
549func (g *genDeepCopy) deepCopyableInterfaces(c *generator.Context, t *types.Type) ([]*types.Type, bool, error) {
550	ts, err := g.deepCopyableInterfacesInner(c, t)
551	if err != nil {
552		return nil, false, err
553	}
554
555	set := map[string]*types.Type{}
556	for _, t := range ts {
557		set[t.String()] = t
558	}
559
560	result := []*types.Type{}
561	for _, t := range set {
562		result = append(result, t)
563	}
564
565	TypeSlice(result).Sort() // we need a stable sorting because it determines the order in generation
566
567	nonPointerReceiver, err := extractNonPointerInterfaces(t)
568	if err != nil {
569		return nil, false, err
570	}
571
572	return result, nonPointerReceiver, nil
573}
574
575type TypeSlice []*types.Type
576
577func (s TypeSlice) Len() int           { return len(s) }
578func (s TypeSlice) Less(i, j int) bool { return s[i].String() < s[j].String() }
579func (s TypeSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
580func (s TypeSlice) Sort()              { sort.Sort(s) }
581
582func (g *genDeepCopy) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
583	if !g.needsGeneration(t) {
584		return nil
585	}
586	klog.V(5).Infof("Generating deepcopy function for type %v", t)
587
588	sw := generator.NewSnippetWriter(w, c, "$", "$")
589	args := argsFromType(t)
590
591	if deepCopyIntoMethodOrDie(t) == nil {
592		sw.Do("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.\n", args)
593		if isReference(t) {
594			sw.Do("func (in $.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
595			sw.Do("{in:=&in\n", nil)
596		} else {
597			sw.Do("func (in *$.type|raw$) DeepCopyInto(out *$.type|raw$) {\n", args)
598		}
599		if deepCopyMethodOrDie(t) != nil {
600			if t.Methods["DeepCopy"].Signature.Receiver.Kind == types.Pointer {
601				sw.Do("clone := in.DeepCopy()\n", nil)
602				sw.Do("*out = *clone\n", nil)
603			} else {
604				sw.Do("*out = in.DeepCopy()\n", nil)
605			}
606			sw.Do("return\n", nil)
607		} else {
608			g.generateFor(t, sw)
609			sw.Do("return\n", nil)
610		}
611		if isReference(t) {
612			sw.Do("}\n", nil)
613		}
614		sw.Do("}\n\n", nil)
615	}
616
617	if deepCopyMethodOrDie(t) == nil {
618		sw.Do("// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new $.type|raw$.\n", args)
619		if isReference(t) {
620			sw.Do("func (in $.type|raw$) DeepCopy() $.type|raw$ {\n", args)
621		} else {
622			sw.Do("func (in *$.type|raw$) DeepCopy() *$.type|raw$ {\n", args)
623		}
624		sw.Do("if in == nil { return nil }\n", nil)
625		sw.Do("out := new($.type|raw$)\n", args)
626		sw.Do("in.DeepCopyInto(out)\n", nil)
627		if isReference(t) {
628			sw.Do("return *out\n", nil)
629		} else {
630			sw.Do("return out\n", nil)
631		}
632		sw.Do("}\n\n", nil)
633	}
634
635	intfs, nonPointerReceiver, err := g.deepCopyableInterfaces(c, t)
636	if err != nil {
637		return err
638	}
639	for _, intf := range intfs {
640		sw.Do(fmt.Sprintf("// DeepCopy%s is an autogenerated deepcopy function, copying the receiver, creating a new $.type2|raw$.\n", intf.Name.Name), argsFromType(t, intf))
641		if nonPointerReceiver {
642			sw.Do(fmt.Sprintf("func (in $.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
643			sw.Do("return *in.DeepCopy()", nil)
644			sw.Do("}\n\n", nil)
645		} else {
646			sw.Do(fmt.Sprintf("func (in *$.type|raw$) DeepCopy%s() $.type2|raw$ {\n", intf.Name.Name), argsFromType(t, intf))
647			sw.Do("if c := in.DeepCopy(); c != nil {\n", nil)
648			sw.Do("return c\n", nil)
649			sw.Do("}\n", nil)
650			sw.Do("return nil\n", nil)
651			sw.Do("}\n\n", nil)
652		}
653	}
654
655	return sw.Error()
656}
657
658// isReference return true for pointer, maps, slices and aliases of those.
659func isReference(t *types.Type) bool {
660	if t.Kind == types.Pointer || t.Kind == types.Map || t.Kind == types.Slice {
661		return true
662	}
663	return t.Kind == types.Alias && isReference(underlyingType(t))
664}
665
666// we use the system of shadowing 'in' and 'out' so that the same code is valid
667// at any nesting level. This makes the autogenerator easy to understand, and
668// the compiler shouldn't care.
669func (g *genDeepCopy) generateFor(t *types.Type, sw *generator.SnippetWriter) {
670	// derive inner types if t is an alias. We call the do* methods below with the alias type.
671	// basic rule: generate according to inner type, but construct objects with the alias type.
672	ut := underlyingType(t)
673
674	var f func(*types.Type, *generator.SnippetWriter)
675	switch ut.Kind {
676	case types.Builtin:
677		f = g.doBuiltin
678	case types.Map:
679		f = g.doMap
680	case types.Slice:
681		f = g.doSlice
682	case types.Struct:
683		f = g.doStruct
684	case types.Pointer:
685		f = g.doPointer
686	case types.Interface:
687		// interfaces are handled in-line in the other cases
688		klog.Fatalf("Hit an interface type %v. This should never happen.", t)
689	case types.Alias:
690		// can never happen because we branch on the underlying type which is never an alias
691		klog.Fatalf("Hit an alias type %v. This should never happen.", t)
692	default:
693		klog.Fatalf("Hit an unsupported type %v.", t)
694	}
695	f(t, sw)
696}
697
698// doBuiltin generates code for a builtin or an alias to a builtin. The generated code is
699// is the same for both cases, i.e. it's the code for the underlying type.
700func (g *genDeepCopy) doBuiltin(t *types.Type, sw *generator.SnippetWriter) {
701	if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
702		sw.Do("*out = in.DeepCopy()\n", nil)
703		return
704	}
705
706	sw.Do("*out = *in\n", nil)
707}
708
709// doMap generates code for a map or an alias to a map. The generated code is
710// is the same for both cases, i.e. it's the code for the underlying type.
711func (g *genDeepCopy) doMap(t *types.Type, sw *generator.SnippetWriter) {
712	ut := underlyingType(t)
713	uet := underlyingType(ut.Elem)
714
715	if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
716		sw.Do("*out = in.DeepCopy()\n", nil)
717		return
718	}
719
720	if !ut.Key.IsAssignable() {
721		klog.Fatalf("Hit an unsupported type %v.", uet)
722	}
723
724	sw.Do("*out = make($.|raw$, len(*in))\n", t)
725	sw.Do("for key, val := range *in {\n", nil)
726	dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
727	switch {
728	case dc != nil || dci != nil:
729		// Note: a DeepCopy exists because it is added if DeepCopyInto is manually defined
730		leftPointer := ut.Elem.Kind == types.Pointer
731		rightPointer := !isReference(ut.Elem)
732		if dc != nil {
733			rightPointer = dc.Results[0].Kind == types.Pointer
734		}
735		if leftPointer == rightPointer {
736			sw.Do("(*out)[key] = val.DeepCopy()\n", nil)
737		} else if leftPointer {
738			sw.Do("x := val.DeepCopy()\n", nil)
739			sw.Do("(*out)[key] = &x\n", nil)
740		} else {
741			sw.Do("(*out)[key] = *val.DeepCopy()\n", nil)
742		}
743	case ut.Elem.IsAnonymousStruct(): // not uet here because it needs type cast
744		sw.Do("(*out)[key] = val\n", nil)
745	case uet.IsAssignable():
746		sw.Do("(*out)[key] = val\n", nil)
747	case uet.Kind == types.Interface:
748		sw.Do("if val == nil {(*out)[key]=nil} else {\n", nil)
749		// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
750		// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
751		// parser does not give us the underlying interface name. So we cannot do any better.
752		sw.Do(fmt.Sprintf("(*out)[key] = val.DeepCopy%s()\n", uet.Name.Name), nil)
753		sw.Do("}\n", nil)
754	case uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer:
755		sw.Do("var outVal $.|raw$\n", uet)
756		sw.Do("if val == nil { (*out)[key] = nil } else {\n", nil)
757		sw.Do("in, out := &val, &outVal\n", uet)
758		g.generateFor(ut.Elem, sw)
759		sw.Do("}\n", nil)
760		sw.Do("(*out)[key] = outVal\n", nil)
761	case uet.Kind == types.Struct:
762		sw.Do("(*out)[key] = *val.DeepCopy()\n", uet)
763	default:
764		klog.Fatalf("Hit an unsupported type %v.", uet)
765	}
766	sw.Do("}\n", nil)
767}
768
769// doSlice generates code for a slice or an alias to a slice. The generated code is
770// is the same for both cases, i.e. it's the code for the underlying type.
771func (g *genDeepCopy) doSlice(t *types.Type, sw *generator.SnippetWriter) {
772	ut := underlyingType(t)
773	uet := underlyingType(ut.Elem)
774
775	if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
776		sw.Do("*out = in.DeepCopy()\n", nil)
777		return
778	}
779
780	sw.Do("*out = make($.|raw$, len(*in))\n", t)
781	if deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
782		sw.Do("for i := range *in {\n", nil)
783		// Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
784		sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
785		sw.Do("}\n", nil)
786	} else if uet.Kind == types.Builtin || uet.IsAssignable() {
787		sw.Do("copy(*out, *in)\n", nil)
788	} else {
789		sw.Do("for i := range *in {\n", nil)
790		if uet.Kind == types.Slice || uet.Kind == types.Map || uet.Kind == types.Pointer || deepCopyMethodOrDie(ut.Elem) != nil || deepCopyIntoMethodOrDie(ut.Elem) != nil {
791			sw.Do("if (*in)[i] != nil {\n", nil)
792			sw.Do("in, out := &(*in)[i], &(*out)[i]\n", nil)
793			g.generateFor(ut.Elem, sw)
794			sw.Do("}\n", nil)
795		} else if uet.Kind == types.Interface {
796			sw.Do("if (*in)[i] != nil {\n", nil)
797			// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
798			// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
799			// parser does not give us the underlying interface name. So we cannot do any better.
800			sw.Do(fmt.Sprintf("(*out)[i] = (*in)[i].DeepCopy%s()\n", uet.Name.Name), nil)
801			sw.Do("}\n", nil)
802		} else if uet.Kind == types.Struct {
803			sw.Do("(*in)[i].DeepCopyInto(&(*out)[i])\n", nil)
804		} else {
805			klog.Fatalf("Hit an unsupported type %v.", uet)
806		}
807		sw.Do("}\n", nil)
808	}
809}
810
811// doStruct generates code for a struct or an alias to a struct. The generated code is
812// is the same for both cases, i.e. it's the code for the underlying type.
813func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) {
814	ut := underlyingType(t)
815
816	if deepCopyMethodOrDie(t) != nil || deepCopyIntoMethodOrDie(t) != nil {
817		sw.Do("*out = in.DeepCopy()\n", nil)
818		return
819	}
820
821	// Simple copy covers a lot of cases.
822	sw.Do("*out = *in\n", nil)
823
824	// Now fix-up fields as needed.
825	for _, m := range ut.Members {
826		ft := m.Type
827		uft := underlyingType(ft)
828
829		args := generator.Args{
830			"type": ft,
831			"kind": ft.Kind,
832			"name": m.Name,
833		}
834		dc, dci := deepCopyMethodOrDie(ft), deepCopyIntoMethodOrDie(ft)
835		switch {
836		case dc != nil || dci != nil:
837			// Note: a DeepCopyInto exists because it is added if DeepCopy is manually defined
838			leftPointer := ft.Kind == types.Pointer
839			rightPointer := !isReference(ft)
840			if dc != nil {
841				rightPointer = dc.Results[0].Kind == types.Pointer
842			}
843			if leftPointer == rightPointer {
844				sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args)
845			} else if leftPointer {
846				sw.Do("x := in.$.name$.DeepCopy()\n", args)
847				sw.Do("out.$.name$ =  = &x\n", args)
848			} else {
849				sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
850			}
851		case uft.Kind == types.Builtin:
852			// the initial *out = *in was enough
853		case uft.Kind == types.Map, uft.Kind == types.Slice, uft.Kind == types.Pointer:
854			// Fixup non-nil reference-semantic types.
855			sw.Do("if in.$.name$ != nil {\n", args)
856			sw.Do("in, out := &in.$.name$, &out.$.name$\n", args)
857			g.generateFor(ft, sw)
858			sw.Do("}\n", nil)
859		case uft.Kind == types.Struct:
860			if ft.IsAssignable() {
861				sw.Do("out.$.name$ = in.$.name$\n", args)
862			} else {
863				sw.Do("in.$.name$.DeepCopyInto(&out.$.name$)\n", args)
864			}
865		case uft.Kind == types.Interface:
866			sw.Do("if in.$.name$ != nil {\n", args)
867			// Note: if t.Elem has been an alias "J" of an interface "I" in Go, we will see it
868			// as kind Interface of name "J" here, i.e. generate val.DeepCopyJ(). The golang
869			// parser does not give us the underlying interface name. So we cannot do any better.
870			sw.Do(fmt.Sprintf("out.$.name$ = in.$.name$.DeepCopy%s()\n", uft.Name.Name), args)
871			sw.Do("}\n", nil)
872		default:
873			klog.Fatalf("Hit an unsupported type %v.", uft)
874		}
875	}
876}
877
878// doPointer generates code for a pointer or an alias to a pointer. The generated code is
879// is the same for both cases, i.e. it's the code for the underlying type.
880func (g *genDeepCopy) doPointer(t *types.Type, sw *generator.SnippetWriter) {
881	ut := underlyingType(t)
882	uet := underlyingType(ut.Elem)
883
884	dc, dci := deepCopyMethodOrDie(ut.Elem), deepCopyIntoMethodOrDie(ut.Elem)
885	switch {
886	case dc != nil || dci != nil:
887		rightPointer := !isReference(ut.Elem)
888		if dc != nil {
889			rightPointer = dc.Results[0].Kind == types.Pointer
890		}
891		if rightPointer {
892			sw.Do("*out = (*in).DeepCopy()\n", nil)
893		} else {
894			sw.Do("x := (*in).DeepCopy()\n", nil)
895			sw.Do("*out = &x\n", nil)
896		}
897	case uet.IsAssignable():
898		sw.Do("*out = new($.Elem|raw$)\n", ut)
899		sw.Do("**out = **in", nil)
900	case uet.Kind == types.Map, uet.Kind == types.Slice, uet.Kind == types.Pointer:
901		sw.Do("*out = new($.Elem|raw$)\n", ut)
902		sw.Do("if **in != nil {\n", nil)
903		sw.Do("in, out := *in, *out\n", nil)
904		g.generateFor(uet, sw)
905		sw.Do("}\n", nil)
906	case uet.Kind == types.Struct:
907		sw.Do("*out = new($.Elem|raw$)\n", ut)
908		sw.Do("(*in).DeepCopyInto(*out)\n", nil)
909	default:
910		klog.Fatalf("Hit an unsupported type %v.", uet)
911	}
912}
913