Skip to content

Commit a7b603a

Browse files
authored
Merge pull request #274 from Jefftree/fix-deepcopy-generic
Fix deepcopy for generics
2 parents 44b8d15 + 0dc45e1 commit a7b603a

File tree

5 files changed

+110
-3
lines changed

5 files changed

+110
-3
lines changed

v2/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ require (
1010

1111
require (
1212
github.com/go-logr/logr v0.2.0 // indirect
13+
github.com/google/go-cmp v0.6.0 // indirect
1314
golang.org/x/mod v0.14.0 // indirect
1415
)

v2/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
22
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
3+
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4+
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
35
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
46
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
57
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=

v2/parser/parse.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,9 @@ func goVarNameToName(in string) types.Name {
572572
return goNameToName(nameParts[1])
573573
}
574574

575+
// goNameToName converts a go name string to a gengo types.Name.
576+
// It operates solely on the string on a best effort basis. The name may be updated
577+
// in walkType for generics.
575578
func goNameToName(in string) types.Name {
576579
// Detect anonymous type names. (These may have '.' characters because
577580
// embedded types may have packages, so we detect them specially.)
@@ -602,6 +605,10 @@ func goNameToName(in string) types.Name {
602605
// The final "." is the name of the type--previous ones must
603606
// have been in the package path.
604607
name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1]
608+
// Add back the generic component now that the package and type name have been separated.
609+
if genericIndex != len(in) {
610+
name.Name = name.Name + in[genericIndex:]
611+
}
605612
}
606613
return name
607614
}
@@ -745,7 +752,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type
745752
}
746753
out.Kind = types.Alias
747754
out.Underlying = p.walkType(u, nil, t.Underlying())
748-
case *gotypes.Struct:
755+
case *gotypes.Struct, *gotypes.Interface:
749756
name := goNameToName(t.String())
750757
tpMap := map[string]*types.Type{}
751758
if t.TypeParams().Len() != 0 {

v2/parser/parse_test.go

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"sort"
2525
"testing"
2626

27+
"github.com/google/go-cmp/cmp"
2728
"golang.org/x/tools/go/packages"
2829
"k8s.io/gengo/v2/types"
2930
)
@@ -893,6 +894,62 @@ func TestStructParse(t *testing.T) {
893894
}
894895
},
895896
},
897+
{
898+
description: "generic on field",
899+
testFile: "./testdata/generic-field",
900+
expected: func() *types.Type {
901+
fieldType := &types.Type{
902+
Name: types.Name{
903+
Package: "k8s.io/gengo/v2/parser/testdata/generic-field",
904+
Name: "Blah[T]",
905+
},
906+
Kind: types.Struct,
907+
CommentLines: []string{""},
908+
SecondClosestCommentLines: []string{""},
909+
Members: []types.Member{
910+
{
911+
Name: "V",
912+
Embedded: false,
913+
CommentLines: []string{"V is the first field."},
914+
Tags: `json:"v"`,
915+
Type: &types.Type{
916+
Kind: types.TypeParam,
917+
Name: types.Name{
918+
Name: "T",
919+
},
920+
},
921+
},
922+
},
923+
TypeParams: map[string]*types.Type{
924+
"T": {
925+
Name: types.Name{
926+
Name: "any",
927+
},
928+
Kind: types.Interface,
929+
},
930+
},
931+
}
932+
return &types.Type{
933+
Name: types.Name{
934+
Package: "k8s.io/gengo/v2/parser/testdata/generic-field",
935+
Name: "Foo",
936+
},
937+
Kind: types.Struct,
938+
CommentLines: []string{""},
939+
SecondClosestCommentLines: []string{""},
940+
Members: []types.Member{
941+
{
942+
Name: "B",
943+
Embedded: false,
944+
CommentLines: []string{""},
945+
Tags: `json:"b"`,
946+
Type: fieldType,
947+
},
948+
},
949+
TypeParams: map[string]*types.Type{},
950+
}
951+
},
952+
},
896953
{
897954
description: "generic multiple",
898955
testFile: "./testdata/generic-multi",
@@ -973,12 +1030,20 @@ func TestStructParse(t *testing.T) {
9731030
recursiveT := &types.Type{
9741031
Name: types.Name{
9751032
Package: "k8s.io/gengo/v2/parser/testdata/generic-recursive",
976-
Name: "DeepCopyable",
1033+
Name: "DeepCopyable[T]",
9771034
},
9781035
Kind: types.Interface,
9791036
CommentLines: []string{""},
9801037
SecondClosestCommentLines: []string{""},
9811038
Methods: map[string]*types.Type{},
1039+
TypeParams: map[string]*types.Type{
1040+
"T": {
1041+
Name: types.Name{
1042+
Name: "any",
1043+
},
1044+
Kind: types.Interface,
1045+
},
1046+
},
9821047
}
9831048
recursiveT.Methods["DeepCopy"] = &types.Type{
9841049
Name: types.Name{
@@ -1054,7 +1119,29 @@ func TestStructParse(t *testing.T) {
10541119
t.Fatalf("type %s not found", expected.Name.Name)
10551120
}
10561121
if e, a := expected, st; !reflect.DeepEqual(e, a) {
1057-
t.Errorf("wanted, got:\n%#v\n%#v", e, a)
1122+
t.Errorf("wanted, got:\n%#v\n%#v\n%s", e, a, cmp.Diff(e, a))
1123+
}
1124+
})
1125+
}
1126+
}
1127+
1128+
func TestGoNameToName(t *testing.T) {
1129+
testCases := []struct {
1130+
input string
1131+
expect types.Name
1132+
}{
1133+
{input: "foo", expect: types.Name{Name: "foo"}},
1134+
{input: "foo.bar", expect: types.Name{Package: "foo", Name: "bar"}},
1135+
{input: "foo.bar.baz", expect: types.Name{Package: "foo.bar", Name: "baz"}},
1136+
{input: "Foo[T]", expect: types.Name{Package: "", Name: "Foo[T]"}},
1137+
{input: "Foo[T any]", expect: types.Name{Package: "", Name: "Foo[T any]"}},
1138+
{input: "pkg.Foo[T]", expect: types.Name{Package: "pkg", Name: "Foo[T]"}},
1139+
{input: "pkg.Foo[T any]", expect: types.Name{Package: "pkg", Name: "Foo[T any]"}},
1140+
}
1141+
for _, tc := range testCases {
1142+
t.Run(tc.input, func(t *testing.T) {
1143+
if got, want := goNameToName(tc.input), tc.expect; !reflect.DeepEqual(got, want) {
1144+
t.Errorf("\nwant: %#v\ngot: %#v", want, got)
10581145
}
10591146
})
10601147
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package foo
2+
3+
type Blah[T any] struct {
4+
// V is the first field.
5+
V T `json:"v"`
6+
}
7+
8+
type Foo struct {
9+
B Blah[string] `json:"b"`
10+
}

0 commit comments

Comments
 (0)