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 )