func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) { if len(t.Members) == 0 { // at least do something with in/out to avoid "declared and not used" errors sw.Do("_ = in\n_ = out\n", nil) } for _, m := range t.Members { t := m.Type if t.Kind == types.Alias { copied := *t.Underlying copied.Name = t.Name t = &copied } args := generator.Args{ "type": t, "name": m.Name, } switch t.Kind { case types.Builtin: sw.Do("out.$.name$ = in.$.name$\n", args) case types.Map, types.Slice, types.Pointer: sw.Do("if in.$.name$ != nil {\n", args) sw.Do("in, out := &in.$.name$, &out.$.name$\n", args) g.generateFor(t, sw) sw.Do("} else {\n", nil) sw.Do("out.$.name$ = nil\n", args) sw.Do("}\n", nil) case types.Struct: if hasDeepCopyMethod(t) { sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) } else if t.IsAssignable() { sw.Do("out.$.name$ = in.$.name$\n", args) } else if g.copyableAndInBounds(t) { sw.Do("if err := $.type|dcFnName$(&in.$.name$, &out.$.name$, c); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("}\n", nil) } else { sw.Do("if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("} else {\n", nil) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args) sw.Do("}\n", nil) } default: sw.Do("if in.$.name$ == nil {\n", args) sw.Do("out.$.name$ = nil\n", args) sw.Do("} else if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("} else {\n", nil) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args) sw.Do("}\n", nil) } } }
func (g *genDeepCopy) doStruct(t *types.Type, sw *generator.SnippetWriter) { if hasDeepCopyMethod(t) { sw.Do("*out = in.DeepCopy()\n", nil) return } // Simple copy covers a lot of cases. sw.Do("*out = *in\n", nil) // Now fix-up fields as needed. for _, m := range t.Members { t := m.Type hasMethod := hasDeepCopyMethod(t) if t.Kind == types.Alias { copied := *t.Underlying copied.Name = t.Name t = &copied } args := generator.Args{ "type": t, "kind": t.Kind, "name": m.Name, } switch t.Kind { case types.Builtin: if hasMethod { sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) } case types.Map, types.Slice, types.Pointer: if hasMethod { sw.Do("if in.$.name$ != nil {\n", args) sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) sw.Do("}\n", nil) } else { // Fixup non-nil reference-sematic types. sw.Do("if in.$.name$ != nil {\n", args) sw.Do("in, out := &in.$.name$, &out.$.name$\n", args) g.generateFor(t, sw) sw.Do("}\n", nil) } case types.Struct: if hasMethod { sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) } else if t.IsAssignable() { // Nothing else needed. } else if g.copyableAndInBounds(t) { // Not assignable but should have a deepcopy function. // TODO: do a topological sort of packages and ensure that this works, else inline it. sw.Do("if err := $.type|dcFnName$(&in.$.name$, &out.$.name$, c); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("}\n", nil) } else { // Fall back on the slow-path and hope it works. // TODO: don't depend on kubernetes code for this sw.Do("if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("} else {\n", nil) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args) sw.Do("}\n", nil) } default: // Interfaces, Arrays, and other Kinds we don't understand. sw.Do("// in.$.name$ is kind '$.kind$'\n", args) if hasMethod { sw.Do("if in.$.name$ != nil {\n", args) sw.Do("out.$.name$ = in.$.name$.DeepCopy()\n", args) sw.Do("}\n", args) } else { // TODO: don't depend on kubernetes code for this sw.Do("if in.$.name$ != nil {\n", args) sw.Do("if newVal, err := c.DeepCopy(&in.$.name$); err != nil {\n", args) sw.Do("return err\n", nil) sw.Do("} else {\n", nil) sw.Do("out.$.name$ = *newVal.(*$.type|raw$)\n", args) sw.Do("}\n", nil) sw.Do("}\n", nil) } } } }
func isDirectlyAssignable(inType, outType *types.Type) bool { // TODO: This should maybe check for actual assignability between the two // types, rather than superficial traits that happen to indicate it is // assignable in the ways we currently use this code. return inType.IsAssignable() && (inType.IsPrimitive() || isSamePackage(inType, outType)) }