From d6673b34ee33c4fbd95a2a4a87141ac93688e2e6 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 26 Aug 2024 18:36:36 +0000 Subject: [PATCH 1/5] bugfix: Do not remove generic typeparams in goNameToName --- v2/parser/parse.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/v2/parser/parse.go b/v2/parser/parse.go index e7acf495..67afbde2 100644 --- a/v2/parser/parse.go +++ b/v2/parser/parse.go @@ -572,6 +572,9 @@ func goVarNameToName(in string) types.Name { return goNameToName(nameParts[1]) } +// goNameToName converts a go name string to a gengo types.Name. +// It operates solely on the string on a best effort basis. The name may be updated +// in walkType for generics. func goNameToName(in string) types.Name { // Detect anonymous type names. (These may have '.' characters because // embedded types may have packages, so we detect them specially.) @@ -602,6 +605,10 @@ func goNameToName(in string) types.Name { // The final "." is the name of the type--previous ones must // have been in the package path. name.Package, name.Name = strings.Join(nameParts[:n-1], "."), nameParts[n-1] + // Add back the generic component now that the package and type name have been separated. + if genericIndex != len(in) { + name.Name = name.Name + in[genericIndex:] + } } return name } From 5b52e8fae10520f84b81ee4b89a46ec56719fe16 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 26 Aug 2024 18:37:35 +0000 Subject: [PATCH 2/5] Add tests for generic in struct field --- v2/parser/parse_test.go | 56 ++++++++++++++++++++++++ v2/parser/testdata/generic-field/file.go | 10 +++++ 2 files changed, 66 insertions(+) create mode 100644 v2/parser/testdata/generic-field/file.go diff --git a/v2/parser/parse_test.go b/v2/parser/parse_test.go index 6bc99019..fc7f5fa0 100644 --- a/v2/parser/parse_test.go +++ b/v2/parser/parse_test.go @@ -893,6 +893,62 @@ func TestStructParse(t *testing.T) { } }, }, + { + description: "generic on field", + testFile: "./testdata/generic-field", + expected: func() *types.Type { + fieldType := &types.Type{ + Name: types.Name{ + Package: "k8s.io/gengo/v2/parser/testdata/generic-field", + Name: "Blah[T]", + }, + Kind: types.Struct, + CommentLines: []string{""}, + SecondClosestCommentLines: []string{""}, + Members: []types.Member{ + { + Name: "V", + Embedded: false, + CommentLines: []string{"V is the first field."}, + Tags: `json:"v"`, + Type: &types.Type{ + Kind: types.TypeParam, + Name: types.Name{ + Name: "T", + }, + }, + }, + }, + TypeParams: map[string]*types.Type{ + "T": { + Name: types.Name{ + Name: "any", + }, + Kind: types.Interface, + }, + }, + } + return &types.Type{ + Name: types.Name{ + Package: "k8s.io/gengo/v2/parser/testdata/generic-field", + Name: "Foo", + }, + Kind: types.Struct, + CommentLines: []string{""}, + SecondClosestCommentLines: []string{""}, + Members: []types.Member{ + { + Name: "B", + Embedded: false, + CommentLines: []string{""}, + Tags: `json:"b"`, + Type: fieldType, + }, + }, + TypeParams: map[string]*types.Type{}, + } + }, + }, { description: "generic multiple", testFile: "./testdata/generic-multi", diff --git a/v2/parser/testdata/generic-field/file.go b/v2/parser/testdata/generic-field/file.go new file mode 100644 index 00000000..747d059d --- /dev/null +++ b/v2/parser/testdata/generic-field/file.go @@ -0,0 +1,10 @@ +package foo + +type Blah[T any] struct { + // V is the first field. + V T `json:"v"` +} + +type Foo struct { + B Blah[string] `json:"b"` +} From 9299822f7000476057cdebea9750df7ec114b994 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 26 Aug 2024 18:37:57 +0000 Subject: [PATCH 3/5] Handle generics for named interface --- v2/parser/parse.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/parser/parse.go b/v2/parser/parse.go index 67afbde2..da9488b8 100644 --- a/v2/parser/parse.go +++ b/v2/parser/parse.go @@ -752,7 +752,7 @@ func (p *Parser) walkType(u types.Universe, useName *types.Name, in gotypes.Type } out.Kind = types.Alias out.Underlying = p.walkType(u, nil, t.Underlying()) - case *gotypes.Struct: + case *gotypes.Struct, *gotypes.Interface: name := goNameToName(t.String()) tpMap := map[string]*types.Type{} if t.TypeParams().Len() != 0 { From 0962201bc0df24a46b0122e2097f86923c562f8a Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 26 Aug 2024 18:39:06 +0000 Subject: [PATCH 4/5] Fix test for named interface --- v2/parser/parse_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/v2/parser/parse_test.go b/v2/parser/parse_test.go index fc7f5fa0..57589d93 100644 --- a/v2/parser/parse_test.go +++ b/v2/parser/parse_test.go @@ -1029,12 +1029,20 @@ func TestStructParse(t *testing.T) { recursiveT := &types.Type{ Name: types.Name{ Package: "k8s.io/gengo/v2/parser/testdata/generic-recursive", - Name: "DeepCopyable", + Name: "DeepCopyable[T]", }, Kind: types.Interface, CommentLines: []string{""}, SecondClosestCommentLines: []string{""}, Methods: map[string]*types.Type{}, + TypeParams: map[string]*types.Type{ + "T": { + Name: types.Name{ + Name: "any", + }, + Kind: types.Interface, + }, + }, } recursiveT.Methods["DeepCopy"] = &types.Type{ Name: types.Name{ From 0dc45e17c99d9202c5145a0ec27a94786b0939c8 Mon Sep 17 00:00:00 2001 From: Jefftree Date: Mon, 26 Aug 2024 18:40:08 +0000 Subject: [PATCH 5/5] Add tests for goNameToName and use cmp.Diff --- v2/go.mod | 1 + v2/go.sum | 2 ++ v2/parser/parse_test.go | 25 ++++++++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/v2/go.mod b/v2/go.mod index 254fc892..e17dbb4f 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -10,5 +10,6 @@ require ( require ( github.com/go-logr/logr v0.2.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect golang.org/x/mod v0.14.0 // indirect ) diff --git a/v2/go.sum b/v2/go.sum index cd75150f..a4f2dcae 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -1,5 +1,7 @@ github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= diff --git a/v2/parser/parse_test.go b/v2/parser/parse_test.go index 57589d93..cfef8348 100644 --- a/v2/parser/parse_test.go +++ b/v2/parser/parse_test.go @@ -24,6 +24,7 @@ import ( "sort" "testing" + "github.com/google/go-cmp/cmp" "golang.org/x/tools/go/packages" "k8s.io/gengo/v2/types" ) @@ -1118,7 +1119,29 @@ func TestStructParse(t *testing.T) { t.Fatalf("type %s not found", expected.Name.Name) } if e, a := expected, st; !reflect.DeepEqual(e, a) { - t.Errorf("wanted, got:\n%#v\n%#v", e, a) + t.Errorf("wanted, got:\n%#v\n%#v\n%s", e, a, cmp.Diff(e, a)) + } + }) + } +} + +func TestGoNameToName(t *testing.T) { + testCases := []struct { + input string + expect types.Name + }{ + {input: "foo", expect: types.Name{Name: "foo"}}, + {input: "foo.bar", expect: types.Name{Package: "foo", Name: "bar"}}, + {input: "foo.bar.baz", expect: types.Name{Package: "foo.bar", Name: "baz"}}, + {input: "Foo[T]", expect: types.Name{Package: "", Name: "Foo[T]"}}, + {input: "Foo[T any]", expect: types.Name{Package: "", Name: "Foo[T any]"}}, + {input: "pkg.Foo[T]", expect: types.Name{Package: "pkg", Name: "Foo[T]"}}, + {input: "pkg.Foo[T any]", expect: types.Name{Package: "pkg", Name: "Foo[T any]"}}, + } + for _, tc := range testCases { + t.Run(tc.input, func(t *testing.T) { + if got, want := goNameToName(tc.input), tc.expect; !reflect.DeepEqual(got, want) { + t.Errorf("\nwant: %#v\ngot: %#v", want, got) } }) }