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