From abec8df9e0cc725c5c2227b12f7c1db6f0f7d0b4 Mon Sep 17 00:00:00 2001 From: Lucas Bremgartner Date: Thu, 7 Mar 2024 12:50:06 +0100 Subject: [PATCH] Add support for generic types expressed by selector expressions --- generator/generator.go | 4 +- generator/generics.go | 38 ++++++++---- generator/generics_test.go | 116 ++++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 14 deletions(-) diff --git a/generator/generator.go b/generator/generator.go index afc0b6e..34e5e32 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -335,7 +335,7 @@ func findTarget(input processInput) (output processOutput, err error) { } output.imports = imports - output.genericTypes = buildGenericTypesFromSpec(ts) + output.genericTypes = buildGenericTypesFromSpec(ts, types, input.astPackage.Name) if it, ok := ts.Type.(*ast.InterfaceType); ok { output.methods, err = processInterface(it, targetProcessInput{ @@ -555,7 +555,7 @@ func processIdent(i *ast.Ident, input targetProcessInput) (methodsList, error) { return nil, errors.Wrap(errNotAnInterface, t.Name.Name) } - genericsTypes = buildGenericTypesFromSpec(t) + genericsTypes = buildGenericTypesFromSpec(t, input.types, input.typesPrefix) break } } diff --git a/generator/generics.go b/generator/generics.go index bc163f9..780956a 100644 --- a/generator/generics.go +++ b/generator/generics.go @@ -77,22 +77,40 @@ func (g genericTypes) buildVars() (string, string) { return buildGenericsWithBrackets(types), buildGenericsWithBrackets(params) } -func buildGenericTypesFromSpec(ts *ast.TypeSpec) (types genericTypes) { +func buildGenericTypesFromSpec(ts *ast.TypeSpec, allTypes []*ast.TypeSpec, typesPrefix string) (types genericTypes) { if ts.TypeParams != nil { for _, param := range ts.TypeParams.List { if param != nil { - if gpt, ok := param.Type.(*ast.Ident); ok { - var paramNames []string - for _, name := range param.Names { - if name != nil { - paramNames = append(paramNames, name.Name) + var typeIdentifier string + switch t := param.Type.(type) { + case *ast.Ident: + prefix := "" + if typesPrefix != "" { + for _, at := range allTypes { + if at.Name.Name == t.Name { + prefix = typesPrefix + "." + break + } } } - types = append(types, genericType{ - Type: gpt.Name, - Names: paramNames, - }) + + typeIdentifier = prefix + t.Name + case *ast.SelectorExpr: + typeIdentifier = t.X.(*ast.Ident).Name + "." + t.Sel.Name + default: + panic("unsupported generic type") + } + + var paramNames []string + for _, name := range param.Names { + if name != nil { + paramNames = append(paramNames, name.Name) + } } + types = append(types, genericType{ + Type: typeIdentifier, + Names: paramNames, + }) } } } diff --git a/generator/generics_test.go b/generator/generics_test.go index e10c803..d985179 100644 --- a/generator/generics_test.go +++ b/generator/generics_test.go @@ -79,7 +79,9 @@ func Test_genericTypes_buildVars(t *testing.T) { func Test_buildGenericTypesFromSpec(t *testing.T) { type args struct { - ts *ast.TypeSpec + ts *ast.TypeSpec + allTypes []*ast.TypeSpec + typesPrefix string } tests := []struct { name string @@ -116,10 +118,120 @@ func Test_buildGenericTypesFromSpec(t *testing.T) { }, }, }, + { + name: "build generic types foo.Bar from spec", + args: args{ + ts: &ast.TypeSpec{ + TypeParams: &ast.FieldList{ + List: []*ast.Field{ + { + Type: &ast.SelectorExpr{ + X: &ast.Ident{ + Name: "foo", + }, + Sel: &ast.Ident{ + Name: "Bar", + }, + }, + Names: []*ast.Ident{ + { + Name: "I", + }, + { + Name: "O", + }, + }, + }, + }, + }, + }, + }, + wantTypes: genericTypes{ + { + Type: "foo.Bar", + Names: []string{"I", "O"}, + }, + }, + }, + { + name: "build generic types Bar from spec without types prefix", + args: args{ + ts: &ast.TypeSpec{ + TypeParams: &ast.FieldList{ + List: []*ast.Field{ + { + Type: &ast.Ident{ + Name: "Bar", + }, + Names: []*ast.Ident{ + { + Name: "I", + }, + { + Name: "O", + }, + }, + }, + }, + }, + }, + allTypes: []*ast.TypeSpec{ + { + Name: &ast.Ident{ + Name: "Bar", + }, + }, + }, + }, + wantTypes: genericTypes{ + { + Type: "Bar", + Names: []string{"I", "O"}, + }, + }, + }, + { + name: "build generic types Bar from spec with types prefix", + args: args{ + ts: &ast.TypeSpec{ + TypeParams: &ast.FieldList{ + List: []*ast.Field{ + { + Type: &ast.Ident{ + Name: "Bar", + }, + Names: []*ast.Ident{ + { + Name: "I", + }, + { + Name: "O", + }, + }, + }, + }, + }, + }, + allTypes: []*ast.TypeSpec{ + { + Name: &ast.Ident{ + Name: "Bar", + }, + }, + }, + typesPrefix: "prefix", + }, + wantTypes: genericTypes{ + { + Type: "prefix.Bar", + Names: []string{"I", "O"}, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if gotTypes := buildGenericTypesFromSpec(tt.args.ts); !reflect.DeepEqual(gotTypes, tt.wantTypes) { + if gotTypes := buildGenericTypesFromSpec(tt.args.ts, tt.args.allTypes, tt.args.typesPrefix); !reflect.DeepEqual(gotTypes, tt.wantTypes) { t.Errorf("buildGenericTypesFromSpec() = %v, want %v", gotTypes, tt.wantTypes) } })