Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix issue #1346 about generics #1348

Merged
merged 2 commits into from Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 34 additions & 29 deletions generics.go
Expand Up @@ -59,7 +59,7 @@ func typeSpecFullName(typeSpecDef *TypeSpecDef) string {
return fullName
}

func (pkgDefs *PackagesDefinitions) parametrizeStruct(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
genericDefinitionsMutex.RLock()
tSpec, ok := genericsDefinitions[original][fullGenericForm]
genericDefinitionsMutex.RUnlock()
Expand Down Expand Up @@ -92,6 +92,7 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(file *ast.File, original *
if tdef != nil && !strings.Contains(genericParam, ".") {
genericParam = fullTypeName(file.Name.Name, genericParam)
}

genericParamTypeDefs[original.TypeSpec.TypeParams.List[i].Names[0].Name] = &genericTypeSpec{
ArrayDepth: arrayDepth,
TypeSpec: tdef,
Expand Down Expand Up @@ -142,33 +143,12 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(file *ast.File, original *
ident.Name = string(IgnoreNameOverridePrefix) + ident.Name

parametrizedTypeSpec.TypeSpec.Name = ident
origStructType := original.TypeSpec.Type.(*ast.StructType)

newStructTypeDef := &ast.StructType{
Struct: origStructType.Struct,
Incomplete: origStructType.Incomplete,
Fields: &ast.FieldList{
Opening: origStructType.Fields.Opening,
Closing: origStructType.Fields.Closing,
},
}

for _, field := range origStructType.Fields.List {
newField := &ast.Field{
Doc: field.Doc,
Names: field.Names,
Tag: field.Tag,
Comment: field.Comment,
}

newField.Type = resolveType(field.Type, field, genericParamTypeDefs)

newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
}
newType := resolveGenericType(original.TypeSpec.Type, genericParamTypeDefs)

genericDefinitionsMutex.Lock()
defer genericDefinitionsMutex.Unlock()
parametrizedTypeSpec.TypeSpec.Type = newStructTypeDef
parametrizedTypeSpec.TypeSpec.Type = newType
if genericsDefinitions[original] == nil {
genericsDefinitions[original] = map[string]*TypeSpecDef{}
}
Expand Down Expand Up @@ -217,25 +197,50 @@ func splitStructName(fullGenericForm string) (string, []string) {
return genericTypeName, genericParams
}

func resolveType(expr ast.Expr, field *ast.Field, genericParamTypeDefs map[string]*genericTypeSpec) ast.Expr {
func resolveGenericType(expr ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) ast.Expr {
switch astExpr := expr.(type) {
case *ast.Ident:
if genTypeSpec, ok := genericParamTypeDefs[astExpr.Name]; ok {
if genTypeSpec.ArrayDepth > 0 {
genTypeSpec.ArrayDepth--
return &ast.ArrayType{Elt: resolveType(expr, field, genericParamTypeDefs)}
return &ast.ArrayType{Elt: resolveGenericType(expr, genericParamTypeDefs)}
}
return genTypeSpec.Type()
}
case *ast.ArrayType:
return &ast.ArrayType{
Elt: resolveType(astExpr.Elt, field, genericParamTypeDefs),
Elt: resolveGenericType(astExpr.Elt, genericParamTypeDefs),
Len: astExpr.Len,
Lbrack: astExpr.Lbrack,
}
}
case *ast.StructType:
newStructTypeDef := &ast.StructType{
Struct: astExpr.Struct,
Incomplete: astExpr.Incomplete,
Fields: &ast.FieldList{
Opening: astExpr.Fields.Opening,
Closing: astExpr.Fields.Closing,
},
}

for _, field := range astExpr.Fields.List {
newField := &ast.Field{
Doc: field.Doc,
Names: field.Names,
Tag: field.Tag,
Comment: field.Comment,
}

return field.Type
newField.Type = resolveGenericType(field.Type, genericParamTypeDefs)
if newField.Type == nil {
newField.Type = field.Type
}

newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField)
}
return newStructTypeDef
}
return nil
}

func getExtendedGenericFieldType(file *ast.File, field ast.Expr) (string, error) {
Expand Down
2 changes: 1 addition & 1 deletion generics_other.go
Expand Up @@ -13,7 +13,7 @@ func typeSpecFullName(typeSpecDef *TypeSpecDef) string {
return typeSpecDef.FullName()
}

func (pkgDefs *PackagesDefinitions) parametrizeStruct(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
func (pkgDefs *PackagesDefinitions) parametrizeGenericType(file *ast.File, original *TypeSpecDef, fullGenericForm string, parseDependency bool) *TypeSpecDef {
return original
}

Expand Down
4 changes: 2 additions & 2 deletions generics_other_test.go
Expand Up @@ -32,10 +32,10 @@ func TestParametrizeStruct(t *testing.T) {
},
}

tr := pd.parametrizeStruct(&ast.File{}, tSpec, "", false)
tr := pd.parametrizeGenericType(&ast.File{}, tSpec, "", false)
assert.Equal(t, tr, tSpec)

tr = pd.parametrizeStruct(&ast.File{}, tSpec, "", true)
tr = pd.parametrizeGenericType(&ast.File{}, tSpec, "", true)
assert.Equal(t, tr, tSpec)
}

Expand Down
12 changes: 6 additions & 6 deletions generics_test.go
Expand Up @@ -108,7 +108,7 @@ func TestParametrizeStruct(t *testing.T) {
packages: make(map[string]*PackageDefinitions),
}
// valid
typeSpec := pd.parametrizeStruct(
typeSpec := pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand All @@ -119,7 +119,7 @@ func TestParametrizeStruct(t *testing.T) {
assert.Equal(t, "$test.Field-string-array_string", typeSpec.Name())

// definition contains one type params, but two type params are provided
typeSpec = pd.parametrizeStruct(
typeSpec = pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand All @@ -130,7 +130,7 @@ func TestParametrizeStruct(t *testing.T) {
assert.Nil(t, typeSpec)

// definition contains two type params, but only one is used
typeSpec = pd.parametrizeStruct(
typeSpec = pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand All @@ -141,7 +141,7 @@ func TestParametrizeStruct(t *testing.T) {
assert.Nil(t, typeSpec)

// name is not a valid type name
typeSpec = pd.parametrizeStruct(
typeSpec = pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand All @@ -151,7 +151,7 @@ func TestParametrizeStruct(t *testing.T) {
}}, "test.Field[string", false)
assert.Nil(t, typeSpec)

typeSpec = pd.parametrizeStruct(
typeSpec = pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand All @@ -161,7 +161,7 @@ func TestParametrizeStruct(t *testing.T) {
}}, "test.Field[string, [string]", false)
assert.Nil(t, typeSpec)

typeSpec = pd.parametrizeStruct(
typeSpec = pd.parametrizeGenericType(
&ast.File{Name: &ast.Ident{Name: "test2"}},
&TypeSpecDef{
TypeSpec: &ast.TypeSpec{
Expand Down
2 changes: 1 addition & 1 deletion packages.go
Expand Up @@ -432,7 +432,7 @@ func (pkgDefs *PackagesDefinitions) findGenericTypeSpec(typeName string, file *a
}

if strings.Contains(tName, genericName) {
if parametrized := pkgDefs.parametrizeStruct(file, tSpec, typeName, parseDependency); parametrized != nil {
if parametrized := pkgDefs.parametrizeGenericType(file, tSpec, typeName, parseDependency); parametrized != nil {
return parametrized
}
}
Expand Down
4 changes: 4 additions & 0 deletions testdata/generics_property/api/api.go
Expand Up @@ -11,13 +11,17 @@ type NestedResponse struct {
Post types.Field[[]types.Post]
}

type Audience[T any] []T

type CreateMovie struct {
Name string
MainActor types.Field[Person]
SupportingCast types.Field[[]Person]
Directors types.Field[*[]Person]
CameraPeople types.Field[[]*Person]
Producer types.Field[*Person]
Audience Audience[Person]
AudienceNames Audience[string]
}

type Person struct {
Expand Down
12 changes: 12 additions & 0 deletions testdata/generics_property/expected.json
Expand Up @@ -105,6 +105,18 @@
"api.CreateMovie": {
"type": "object",
"properties": {
"audience": {
"type": "array",
"items": {
"$ref": "#/definitions/api.Person"
}
},
"audienceNames": {
"type": "array",
"items": {
"type": "string"
}
},
"cameraPeople": {
"$ref": "#/definitions/types.Field-array_api_Person"
},
Expand Down