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