github.com/christarazi/controller-tools@v0.3.1-0.20210907042920-aa94049173f8/pkg/deepcopy/traverse.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package deepcopy 18 19 import ( 20 "fmt" 21 "go/ast" 22 "go/types" 23 "io" 24 "path" 25 "strings" 26 "unicode" 27 "unicode/utf8" 28 29 "sigs.k8s.io/controller-tools/pkg/loader" 30 "sigs.k8s.io/controller-tools/pkg/markers" 31 ) 32 33 // NB(directxman12): This code is a bit of a byzantine mess. 34 // I've tried to clean it up a bit from the original in deepcopy-gen, 35 // but parts remain a bit convoluted. Exercise caution when changing. 36 // It's perhaps a tad over-commented now, but better safe than sorry. 37 // It also seriously needs auditing for sanity -- there's parts where we 38 // copy the original deepcopy-gen's output just to be safe, but some of that 39 // could be simplified away if we're careful. 40 41 // codeWriter assists in writing out Go code lines and blocks to a writer. 42 type codeWriter struct { 43 out io.Writer 44 } 45 46 // Line writes a single line. 47 func (c *codeWriter) Line(line string) { 48 fmt.Fprintln(c.out, line) 49 } 50 51 // Linef writes a single line with formatting (as per fmt.Sprintf). 52 func (c *codeWriter) Linef(line string, args ...interface{}) { 53 fmt.Fprintf(c.out, line+"\n", args...) 54 } 55 56 // If writes an if statement with the given setup/condition clause, executing 57 // the given function to write the contents of the block. 58 func (c *codeWriter) If(setup string, block func()) { 59 c.Linef("if %s {", setup) 60 block() 61 c.Line("}") 62 } 63 64 // If writes if and else statements with the given setup/condition clause, executing 65 // the given functions to write the contents of the blocks. 66 func (c *codeWriter) IfElse(setup string, ifBlock func(), elseBlock func()) { 67 c.Linef("if %s {", setup) 68 ifBlock() 69 c.Line("} else {") 70 elseBlock() 71 c.Line("}") 72 } 73 74 // For writes an for statement with the given setup/condition clause, executing 75 // the given function to write the contents of the block. 76 func (c *codeWriter) For(setup string, block func()) { 77 c.Linef("for %s {", setup) 78 block() 79 c.Line("}") 80 } 81 82 // importsList keeps track of required imports, automatically assigning aliases 83 // to import statement. 84 type importsList struct { 85 byPath map[string]string 86 byAlias map[string]string 87 88 pkg *loader.Package 89 } 90 91 // NeedImport marks that the given package is needed in the list of imports, 92 // returning the ident (import alias) that should be used to reference the package. 93 func (l *importsList) NeedImport(importPath string) string { 94 // we get an actual path from Package, which might include venddored 95 // packages if running on a package in vendor. 96 if ind := strings.LastIndex(importPath, "/vendor/"); ind != -1 { 97 importPath = importPath[ind+8: /* len("/vendor/") */] 98 } 99 100 // check to see if we've already assigned an alias, and just return that. 101 alias, exists := l.byPath[importPath] 102 if exists { 103 return alias 104 } 105 106 // otherwise, calculate an import alias by joining path parts till we get something unique 107 restPath, nextWord := path.Split(importPath) 108 109 for otherPath, exists := "", true; exists && otherPath != importPath; otherPath, exists = l.byAlias[alias] { 110 if restPath == "" { 111 // do something else to disambiguate if we're run out of parts and 112 // still have duplicates, somehow 113 alias += "x" 114 } 115 116 // can't have a first digit, per Go identifier rules, so just skip them 117 for firstRune, runeLen := utf8.DecodeRuneInString(nextWord); unicode.IsDigit(firstRune); firstRune, runeLen = utf8.DecodeRuneInString(nextWord) { 118 nextWord = nextWord[runeLen:] 119 } 120 121 // make a valid identifier by replacing "bad" characters with underscores 122 nextWord = strings.Map(func(r rune) rune { 123 if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' { 124 return r 125 } 126 return '_' 127 }, nextWord) 128 129 alias = nextWord + alias 130 if len(restPath) > 0 { 131 restPath, nextWord = path.Split(restPath[:len(restPath)-1] /* chop off final slash */) 132 } 133 } 134 135 l.byPath[importPath] = alias 136 l.byAlias[alias] = importPath 137 return alias 138 } 139 140 // ImportSpecs returns a string form of each import spec 141 // (i.e. `alias "path/to/import"). Aliases are only present 142 // when they don't match the package name. 143 func (l *importsList) ImportSpecs() []string { 144 res := make([]string, 0, len(l.byPath)) 145 for importPath, alias := range l.byPath { 146 pkg := l.pkg.Imports()[importPath] 147 if pkg != nil && pkg.Name == alias { 148 // don't print if alias is the same as package name 149 // (we've already taken care of duplicates). 150 res = append(res, fmt.Sprintf("%q", importPath)) 151 } else { 152 res = append(res, fmt.Sprintf("%s %q", alias, importPath)) 153 } 154 } 155 return res 156 } 157 158 // namingInfo holds package and syntax for referencing a field, type, 159 // etc. It's used to allow lazily marking import usage. 160 // You should generally retrieve the syntax using Syntax. 161 type namingInfo struct { 162 // typeInfo is the type being named. 163 typeInfo types.Type 164 nameOverride string 165 } 166 167 // Syntax calculates the code representation of the given type or name, 168 // and marks that is used (potentially marking an import as used). 169 func (n *namingInfo) Syntax(basePkg *loader.Package, imports *importsList) string { 170 if n.nameOverride != "" { 171 return n.nameOverride 172 } 173 174 // NB(directxman12): typeInfo.String gets us most of the way there, 175 // but fails (for us) on named imports, since it uses the full package path. 176 switch typeInfo := n.typeInfo.(type) { 177 case *types.Named: 178 // register that we need an import for this type, 179 // so we can get the appropriate alias to use. 180 typeName := typeInfo.Obj() 181 otherPkg := typeName.Pkg() 182 if otherPkg == basePkg.Types { 183 // local import 184 return typeName.Name() 185 } 186 alias := imports.NeedImport(loader.NonVendorPath(otherPkg.Path())) 187 return alias + "." + typeName.Name() 188 case *types.Basic: 189 return typeInfo.String() 190 case *types.Pointer: 191 return "*" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) 192 case *types.Slice: 193 return "[]" + (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports) 194 case *types.Map: 195 return fmt.Sprintf( 196 "map[%s]%s", 197 (&namingInfo{typeInfo: typeInfo.Key()}).Syntax(basePkg, imports), 198 (&namingInfo{typeInfo: typeInfo.Elem()}).Syntax(basePkg, imports)) 199 default: 200 basePkg.AddError(fmt.Errorf("name requested for invalid type %s", typeInfo)) 201 return typeInfo.String() 202 } 203 } 204 205 // copyMethodMakers makes DeepCopy (and related) methods for Go types, 206 // writing them to its codeWriter. 207 type copyMethodMaker struct { 208 pkg *loader.Package 209 *importsList 210 *codeWriter 211 } 212 213 // GenerateMethodsFor makes DeepCopy, DeepCopyInto, and DeepCopyObject methods 214 // for the given type, when appropriate 215 func (c *copyMethodMaker) GenerateMethodsFor(root *loader.Package, info *markers.TypeInfo) { 216 typeInfo := root.TypesInfo.TypeOf(info.RawSpec.Name) 217 if typeInfo == types.Typ[types.Invalid] { 218 root.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec)) 219 } 220 221 // figure out if we need to use a pointer receiver -- most types get a pointer receiver, 222 // except those that are aliases to types that are already pass-by-reference (pointers, 223 // interfaces. maps, slices). 224 ptrReceiver := usePtrReceiver(typeInfo) 225 226 hasManualDeepCopyInto := hasDeepCopyIntoMethod(root, typeInfo) 227 hasManualDeepCopy, deepCopyOnPtr := hasDeepCopyMethod(root, typeInfo) 228 229 // only generate each method if it hasn't been implemented. 230 if !hasManualDeepCopyInto { 231 c.Line("// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.") 232 if ptrReceiver { 233 c.Linef("func (in *%s) DeepCopyInto(out *%s) {", info.Name, info.Name) 234 } else { 235 c.Linef("func (in %s) DeepCopyInto(out *%s) {", info.Name, info.Name) 236 c.Line("{in := &in") // add an extra block so that we can redefine `in` without type issues 237 } 238 239 // just wrap the existing deepcopy if present 240 if hasManualDeepCopy { 241 if deepCopyOnPtr { 242 c.Line("clone := in.DeepCopy()") 243 c.Line("*out = *clone") 244 } else { 245 c.Line("*out = in.DeepCopy()") 246 } 247 } else { 248 c.genDeepCopyIntoBlock(&namingInfo{nameOverride: info.Name}, typeInfo) 249 } 250 251 if !ptrReceiver { 252 c.Line("}") // close our extra "in redefinition" block 253 } 254 c.Line("}") 255 } 256 257 if !hasManualDeepCopy { 258 // these are both straightforward, so we just template them out. 259 if ptrReceiver { 260 c.Linef(ptrDeepCopy, info.Name) 261 } else { 262 c.Linef(bareDeepCopy, info.Name) 263 } 264 265 // maybe also generate DeepCopyObject, if asked. 266 if genObjectInterface(info) { 267 // we always need runtime.Object for DeepCopyObject 268 runtimeAlias := c.NeedImport("k8s.io/apimachinery/pkg/runtime") 269 if ptrReceiver { 270 c.Linef(ptrDeepCopyObj, info.Name, runtimeAlias) 271 } else { 272 c.Linef(bareDeepCopyObj, info.Name, runtimeAlias) 273 } 274 } 275 } 276 } 277 278 // genDeepCopyBody generates a DeepCopyInto block for the given type. The 279 // block is *not* wrapped in curly braces. 280 func (c *copyMethodMaker) genDeepCopyIntoBlock(actualName *namingInfo, typeInfo types.Type) { 281 // we calculate *how* we should copy mostly based on the "eventual" type of 282 // a given type (i.e. the type that results from following all aliases) 283 last := eventualUnderlyingType(typeInfo) 284 285 // we might hit a type that has a manual deepcopy method written on non-root types 286 // (this case is handled for root types in GenerateMethodFor). 287 // In that case (when we're not dealing with a pointer, since those need special handling 288 // to match 1-to-1 with k8s deepcopy-gen), just use that. 289 if _, isPtr := last.(*types.Pointer); !isPtr && hasAnyDeepCopyMethod(c.pkg, typeInfo) { 290 c.Line("*out = in.DeepCopy()") 291 return 292 } 293 294 switch last := last.(type) { 295 case *types.Basic: 296 // basic types themselves can be "shallow" copied, so all we need 297 // to do is check if our *actual* type (not the underlying one) has 298 // a custom method implemented. 299 if hasMethod, _ := hasDeepCopyMethod(c.pkg, typeInfo); hasMethod { 300 c.Line("*out = in.DeepCopy()") 301 } 302 c.Line("*out = *in") 303 case *types.Map: 304 c.genMapDeepCopy(actualName, last) 305 case *types.Slice: 306 c.genSliceDeepCopy(actualName, last) 307 case *types.Struct: 308 c.genStructDeepCopy(actualName, last) 309 case *types.Pointer: 310 c.genPointerDeepCopy(actualName, last) 311 case *types.Named: 312 // handled via the above loop, should never happen 313 c.pkg.AddError(fmt.Errorf("interface type %s encountered directly, invalid condition", last)) 314 default: 315 c.pkg.AddError(fmt.Errorf("invalid type %s", last)) 316 } 317 } 318 319 // genMapDeepCopy generates DeepCopy code for the given named type whose eventual 320 // type is the given map type. 321 func (c *copyMethodMaker) genMapDeepCopy(actualName *namingInfo, mapType *types.Map) { 322 // maps *must* have shallow-copiable types, since we just iterate 323 // through the keys, only trying to deepcopy the values. 324 if !fineToShallowCopy(mapType.Key()) { 325 c.pkg.AddError(fmt.Errorf("invalid map key type %s", mapType.Key())) 326 return 327 } 328 329 // make our actual type (not the underlying one)... 330 c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList)) 331 332 // ...and copy each element appropriately 333 c.For("key, val := range *in", func() { 334 // check if we have manually written methods, 335 // in which case we'll just try and use those 336 hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, mapType.Elem()) 337 hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, mapType.Elem()) 338 switch { 339 case hasDeepCopyInto || hasDeepCopy: 340 // use the manually-written methods 341 _, fieldIsPtr := mapType.Elem().(*types.Pointer) // is "out" actually a pointer 342 inIsPtr := resultWillBePointer(mapType.Elem(), hasDeepCopy, copyOnPtr) // does copying "in" produce a pointer 343 if hasDeepCopy { 344 // If we're calling DeepCopy, check if it's receiver needs a pointer 345 inIsPtr = copyOnPtr 346 } 347 if inIsPtr == fieldIsPtr { 348 c.Line("(*out)[key] = val.DeepCopy()") 349 } else if fieldIsPtr { 350 c.Line("{") // use a block because we use `x` as a temporary 351 c.Line("x := val.DeepCopy()") 352 c.Line("(*out)[key] = &x") 353 c.Line("}") 354 } else { 355 c.Line("(*out)[key] = *val.DeepCopy()") 356 } 357 case fineToShallowCopy(mapType.Elem()): 358 // just shallow copy types for which it's safe to do so 359 c.Line("(*out)[key] = val") 360 default: 361 // otherwise, we've got some kind-specific actions, 362 // based on the element's eventual type. 363 364 underlyingElem := eventualUnderlyingType(mapType.Elem()) 365 366 // if it passes by reference, let the main switch handle it 367 if passesByReference(underlyingElem) { 368 c.Linef("var outVal %[1]s", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList)) 369 c.IfElse("val == nil", func() { 370 c.Line("(*out)[key] = nil") 371 }, func() { 372 c.Line("in, out := &val, &outVal") 373 c.genDeepCopyIntoBlock(&namingInfo{typeInfo: mapType.Elem()}, mapType.Elem()) 374 }) 375 c.Line("(*out)[key] = outVal") 376 377 return 378 } 379 380 // otherwise... 381 switch underlyingElem := underlyingElem.(type) { 382 case *types.Struct: 383 // structs will have deepcopy generated for them, so use that 384 c.Line("(*out)[key] = *val.DeepCopy()") 385 default: 386 c.pkg.AddError(fmt.Errorf("invalid map value type %s", underlyingElem)) 387 return 388 } 389 } 390 }) 391 } 392 393 // genSliceDeepCopy generates DeepCopy code for the given named type whose 394 // underlying type is the given slice. 395 func (c *copyMethodMaker) genSliceDeepCopy(actualName *namingInfo, sliceType *types.Slice) { 396 underlyingElem := eventualUnderlyingType(sliceType.Elem()) 397 398 // make the actual type (not the underlying) 399 c.Linef("*out = make(%[1]s, len(*in))", actualName.Syntax(c.pkg, c.importsList)) 400 401 // check if we need to do anything special, or just copy each element appropriately 402 switch { 403 case hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()): 404 // just use deepcopy if it's present (deepcopyinto will be filled in by our code) 405 c.For("i := range *in", func() { 406 c.Line("(*in)[i].DeepCopyInto(&(*out)[i])") 407 }) 408 case fineToShallowCopy(underlyingElem): 409 // shallow copy if ok 410 c.Line("copy(*out, *in)") 411 default: 412 // copy each element appropriately 413 c.For("i := range *in", func() { 414 // fall back to normal code for reference types or those with custom logic 415 if passesByReference(underlyingElem) || hasAnyDeepCopyMethod(c.pkg, sliceType.Elem()) { 416 c.If("(*in)[i] != nil", func() { 417 c.Line("in, out := &(*in)[i], &(*out)[i]") 418 c.genDeepCopyIntoBlock(&namingInfo{typeInfo: sliceType.Elem()}, sliceType.Elem()) 419 }) 420 return 421 } 422 423 switch underlyingElem.(type) { 424 case *types.Struct: 425 // structs will always have deepcopy 426 c.Linef("(*in)[i].DeepCopyInto(&(*out)[i])") 427 default: 428 c.pkg.AddError(fmt.Errorf("invalid slice element type %s", underlyingElem)) 429 } 430 }) 431 } 432 } 433 434 // genStructDeepCopy generates DeepCopy code for the given named type whose 435 // underlying type is the given struct. 436 func (c *copyMethodMaker) genStructDeepCopy(_ *namingInfo, structType *types.Struct) { 437 c.Line("*out = *in") 438 439 for i := 0; i < structType.NumFields(); i++ { 440 field := structType.Field(i) 441 442 // if we have a manual deepcopy, use that 443 hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, field.Type()) 444 hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, field.Type()) 445 if hasDeepCopyInto || hasDeepCopy { 446 // NB(directxman12): yes, I know this is kind-of weird that we 447 // have all this special-casing here, but it's nice for testing 448 // purposes to be 1-to-1 with deepcopy-gen, which does all sorts of 449 // stuff like this (I'm pretty sure I found some codepaths that 450 // never execute there, because they're pretty clearly invalid 451 // syntax). 452 453 _, fieldIsPtr := field.Type().(*types.Pointer) 454 inIsPtr := resultWillBePointer(field.Type(), hasDeepCopy, copyOnPtr) 455 if fieldIsPtr { 456 // we'll need a if block to check for nilness 457 // we'll let genDeepCopyIntoBlock handle the details, we just needed the setup 458 c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() { 459 c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name()) 460 c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type()) 461 }) 462 } else { 463 // special-case for compatibility with deepcopy-gen 464 if inIsPtr == fieldIsPtr { 465 c.Linef("out.%[1]s = in.%[1]s.DeepCopy()", field.Name()) 466 } else { 467 c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name()) 468 } 469 } 470 continue 471 } 472 473 // pass-by-reference fields get delegated to the main type 474 underlyingField := eventualUnderlyingType(field.Type()) 475 if passesByReference(underlyingField) { 476 c.If(fmt.Sprintf("in.%s != nil", field.Name()), func() { 477 c.Linef("in, out := &in.%[1]s, &out.%[1]s", field.Name()) 478 c.genDeepCopyIntoBlock(&namingInfo{typeInfo: field.Type()}, field.Type()) 479 }) 480 continue 481 } 482 483 // otherwise... 484 switch underlyingField := underlyingField.(type) { 485 case *types.Basic: 486 // nothing to do, initial assignment copied this 487 case *types.Struct: 488 if fineToShallowCopy(field.Type()) { 489 c.Linef("out.%[1]s = in.%[1]s", field.Name()) 490 } else { 491 c.Linef("in.%[1]s.DeepCopyInto(&out.%[1]s)", field.Name()) 492 } 493 default: 494 c.pkg.AddError(fmt.Errorf("invalid field type %s", underlyingField)) 495 return 496 } 497 } 498 } 499 500 // genPointerDeepCopy generates DeepCopy code for the given named type whose 501 // underlying type is the given struct. 502 func (c *copyMethodMaker) genPointerDeepCopy(_ *namingInfo, pointerType *types.Pointer) { 503 underlyingElem := eventualUnderlyingType(pointerType.Elem()) 504 505 // if we have a manually written deepcopy, just use that 506 hasDeepCopy, copyOnPtr := hasDeepCopyMethod(c.pkg, pointerType.Elem()) 507 hasDeepCopyInto := hasDeepCopyIntoMethod(c.pkg, pointerType.Elem()) 508 if hasDeepCopyInto || hasDeepCopy { 509 outNeedsPtr := resultWillBePointer(pointerType.Elem(), hasDeepCopy, copyOnPtr) 510 if hasDeepCopy { 511 outNeedsPtr = copyOnPtr 512 } 513 if outNeedsPtr { 514 c.Line("*out = (*in).DeepCopy()") 515 } else { 516 c.Line("x := (*in).DeepCopy()") 517 c.Line("*out = &x") 518 } 519 return 520 } 521 522 // shallow-copiable types are pretty easy 523 if fineToShallowCopy(underlyingElem) { 524 c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList)) 525 c.Line("**out = **in") 526 return 527 } 528 529 // pass-by-reference types get delegated to the main switch 530 if passesByReference(underlyingElem) { 531 c.Linef("*out = new(%s)", (&namingInfo{typeInfo: underlyingElem}).Syntax(c.pkg, c.importsList)) 532 c.If("**in != nil", func() { 533 c.Line("in, out := *in, *out") 534 c.genDeepCopyIntoBlock(&namingInfo{typeInfo: underlyingElem}, eventualUnderlyingType(underlyingElem)) 535 }) 536 return 537 } 538 539 // otherwise... 540 switch underlyingElem := underlyingElem.(type) { 541 case *types.Struct: 542 c.Linef("*out = new(%[1]s)", (&namingInfo{typeInfo: pointerType.Elem()}).Syntax(c.pkg, c.importsList)) 543 c.Line("(*in).DeepCopyInto(*out)") 544 default: 545 c.pkg.AddError(fmt.Errorf("invalid pointer element type %s", underlyingElem)) 546 return 547 } 548 } 549 550 // usePtrReceiver checks if we need a pointer receiver on methods for the given type 551 // Pass-by-reference types don't get pointer receivers. 552 func usePtrReceiver(typeInfo types.Type) bool { 553 switch typeInfo.(type) { 554 case *types.Pointer: 555 return false 556 case *types.Map: 557 return false 558 case *types.Slice: 559 return false 560 case *types.Named: 561 return usePtrReceiver(typeInfo.Underlying()) 562 default: 563 return true 564 } 565 } 566 567 func resultWillBePointer(typeInfo types.Type, hasDeepCopy, deepCopyOnPtr bool) bool { 568 // if we have a manual deepcopy, we can just check what that returns 569 if hasDeepCopy { 570 return deepCopyOnPtr 571 } 572 573 // otherwise, we'll need to check its type 574 switch typeInfo := typeInfo.(type) { 575 case *types.Pointer: 576 // NB(directxman12): we don't have to worry about the elem having a deepcopy, 577 // since hasManualDeepCopy would've caught that. 578 579 // we'll be calling on the elem, so check that 580 return resultWillBePointer(typeInfo.Elem(), false, false) 581 case *types.Map: 582 return false 583 case *types.Slice: 584 return false 585 case *types.Named: 586 return resultWillBePointer(typeInfo.Underlying(), false, false) 587 default: 588 return true 589 } 590 } 591 592 // shouldBeCopied checks if we're supposed to make deepcopy methods the given type. 593 // 594 // This is the case if it's exported *and* either: 595 // - has a partial manual DeepCopy implementation (in which case we fill in the rest) 596 // - aliases to a non-basic type eventually 597 // - is a struct 598 func shouldBeCopied(pkg *loader.Package, info *markers.TypeInfo) bool { 599 if !ast.IsExported(info.Name) { 600 return false 601 } 602 603 typeInfo := pkg.TypesInfo.TypeOf(info.RawSpec.Name) 604 if typeInfo == types.Typ[types.Invalid] { 605 pkg.AddError(loader.ErrFromNode(fmt.Errorf("unknown type %s", info.Name), info.RawSpec)) 606 return false 607 } 608 609 // according to gengo, everything named is an alias, except for an alias to a pointer, 610 // which is just a pointer, afaict. Just roll with it. 611 if asPtr, isPtr := typeInfo.(*types.Named).Underlying().(*types.Pointer); isPtr { 612 typeInfo = asPtr 613 } 614 615 lastType := typeInfo 616 if _, isNamed := typeInfo.(*types.Named); isNamed { 617 // if it has a manual deepcopy or deepcopyinto, we're fine 618 if hasAnyDeepCopyMethod(pkg, typeInfo) { 619 return true 620 } 621 622 for underlyingType := typeInfo.Underlying(); underlyingType != lastType; lastType, underlyingType = underlyingType, underlyingType.Underlying() { 623 // if it has a manual deepcopy or deepcopyinto, we're fine 624 if hasAnyDeepCopyMethod(pkg, underlyingType) { 625 return true 626 } 627 628 // aliases to other things besides basics need copy methods 629 // (basics can be straight-up shallow-copied) 630 if _, isBasic := underlyingType.(*types.Basic); !isBasic { 631 return true 632 } 633 } 634 } 635 636 // structs are the only thing that's not a basic that's copiable by default 637 _, isStruct := lastType.(*types.Struct) 638 return isStruct 639 } 640 641 // hasDeepCopyMethod checks if this type has a manual DeepCopy method and if 642 // the method has a pointer receiver. 643 func hasDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) (bool, bool) { 644 deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopy") 645 if len(ind) != 1 { 646 // ignore embedded methods 647 return false, false 648 } 649 if deepCopyMethod == nil { 650 return false, false 651 } 652 653 methodSig := deepCopyMethod.Type().(*types.Signature) 654 if methodSig.Params() != nil && methodSig.Params().Len() != 0 { 655 return false, false 656 } 657 if methodSig.Results() == nil || methodSig.Results().Len() != 1 { 658 return false, false 659 } 660 661 recvAsPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer) 662 if recvIsPtr { 663 // NB(directxman12): the pointer type returned here isn't comparable even though they 664 // have the same underlying type, for some reason (probably that 665 // LookupFieldOrMethod calls types.NewPointer for us), so check the 666 // underlying values. 667 668 resultPtr, resultIsPtr := methodSig.Results().At(0).Type().(*types.Pointer) 669 if !resultIsPtr { 670 // pointer vs non-pointer are different types 671 return false, false 672 } 673 674 if recvAsPtr.Elem() != resultPtr.Elem() { 675 return false, false 676 } 677 } else if methodSig.Results().At(0).Type() != methodSig.Recv().Type() { 678 return false, false 679 } 680 681 return true, recvIsPtr 682 } 683 684 // hasDeepCopyIntoMethod checks if this type has a manual DeepCopyInto method. 685 func hasDeepCopyIntoMethod(pkg *loader.Package, typeInfo types.Type) bool { 686 deepCopyMethod, ind, _ := types.LookupFieldOrMethod(typeInfo, true /* check pointers too */, pkg.Types, "DeepCopyInto") 687 if len(ind) != 1 { 688 // ignore embedded methods 689 return false 690 } 691 if deepCopyMethod == nil { 692 return false 693 } 694 695 methodSig := deepCopyMethod.Type().(*types.Signature) 696 if methodSig.Params() == nil || methodSig.Params().Len() != 1 { 697 return false 698 } 699 paramPtr, isPtr := methodSig.Params().At(0).Type().(*types.Pointer) 700 if !isPtr { 701 return false 702 } 703 if methodSig.Results() != nil && methodSig.Results().Len() != 0 { 704 return false 705 } 706 707 if recvPtr, recvIsPtr := methodSig.Recv().Type().(*types.Pointer); recvIsPtr { 708 // NB(directxman12): the pointer type returned here isn't comparable even though they 709 // have the same underlying type, for some reason (probably that 710 // LookupFieldOrMethod calls types.NewPointer for us), so check the 711 // underlying values. 712 return paramPtr.Elem() == recvPtr.Elem() 713 } 714 return methodSig.Recv().Type() == paramPtr.Elem() 715 } 716 717 // hasAnyDeepCopyMethod checks if the given method has DeepCopy or DeepCopyInto 718 // (either of which implies the other will exist eventually). 719 func hasAnyDeepCopyMethod(pkg *loader.Package, typeInfo types.Type) bool { 720 hasDeepCopy, _ := hasDeepCopyMethod(pkg, typeInfo) 721 return hasDeepCopy || hasDeepCopyIntoMethod(pkg, typeInfo) 722 } 723 724 // eventualUnderlyingType gets the "final" type in a sequence of named aliases. 725 // It's effectively a shortcut for calling Underlying in a loop. 726 func eventualUnderlyingType(typeInfo types.Type) types.Type { 727 last := typeInfo 728 for underlying := typeInfo.Underlying(); underlying != last; last, underlying = underlying, underlying.Underlying() { 729 // get the actual underlying type 730 } 731 return last 732 } 733 734 // fineToShallowCopy checks if a shallow-copying a type is equivalent to deepcopy-ing it. 735 func fineToShallowCopy(typeInfo types.Type) bool { 736 switch typeInfo := typeInfo.(type) { 737 case *types.Basic: 738 // basic types (int, string, etc) are always fine to shallow-copy 739 return true 740 case *types.Named: 741 // aliases are fine to shallow-copy as long as they resolve to a shallow-copyable type 742 return fineToShallowCopy(typeInfo.Underlying()) 743 case *types.Struct: 744 // structs are fine to shallow-copy if they have all shallow-copyable fields 745 for i := 0; i < typeInfo.NumFields(); i++ { 746 field := typeInfo.Field(i) 747 if !fineToShallowCopy(field.Type()) { 748 return false 749 } 750 } 751 return true 752 default: 753 return false 754 } 755 } 756 757 // passesByReference checks if the given type passesByReference 758 // (except for interfaces, which are handled separately). 759 func passesByReference(typeInfo types.Type) bool { 760 switch typeInfo.(type) { 761 case *types.Slice: 762 return true 763 case *types.Map: 764 return true 765 case *types.Pointer: 766 return true 767 default: 768 return false 769 } 770 } 771 772 var ( 773 // ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a pointer receiver. 774 ptrDeepCopy = ` 775 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s. 776 func (in *%[1]s) DeepCopy() *%[1]s { 777 if in == nil { return nil } 778 out := new(%[1]s) 779 in.DeepCopyInto(out) 780 return out 781 } 782 ` 783 784 // ptrDeepCopy is a DeepCopy for a type with an existing DeepCopyInto and a non-pointer receiver. 785 bareDeepCopy = ` 786 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new %[1]s. 787 func (in %[1]s) DeepCopy() %[1]s { 788 if in == nil { return nil } 789 out := new(%[1]s) 790 in.DeepCopyInto(out) 791 return *out 792 } 793 ` 794 795 // ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a pointer receiver. 796 ptrDeepCopyObj = ` 797 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 798 func (in *%[1]s) DeepCopyObject() %[2]s.Object { 799 if c := in.DeepCopy(); c != nil { 800 return c 801 } 802 return nil 803 } 804 ` 805 // ptrDeepCopy is a DeepCopyObject for a type with an existing DeepCopyInto and a non-pointer receiver. 806 bareDeepCopyObj = ` 807 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 808 func (in %[1]s) DeepCopyObject() %[2]s.Object { 809 return in.DeepCopy() 810 } 811 ` 812 )