fix(goctl): recurse into inline struct in IsTagMember (#5671)

Co-authored-by: kevin <wanjunfeng@gmail.com>
This commit is contained in:
林桉
2026-08-01 01:19:17 +08:00
committed by GitHub
parent 36f2619756
commit 925f8a2bcc
6 changed files with 469 additions and 5 deletions

View File

@@ -349,6 +349,17 @@ func (a *Analyzer) fillTypes() error {
case spec.DefineStruct:
var members []spec.Member
for _, member := range v.Members {
if member.IsInline {
tp, err := a.resolveInlineType(member.Type, map[string]bool{v.RawName: true})
if err != nil {
return err
}
member.Type = tp
members = append(members, member)
continue
}
switch v := member.Type.(type) {
case spec.DefineStruct:
tp, err := a.findDefinedType(v.RawName)
@@ -371,6 +382,62 @@ func (a *Analyzer) fillTypes() error {
return nil
}
func (a *Analyzer) resolveInlineType(tp spec.Type, resolving map[string]bool) (spec.Type, error) {
switch v := tp.(type) {
case spec.DefineStruct:
if resolving[v.RawName] {
return v, nil
}
tp, err := a.findDefinedType(v.RawName)
if err != nil {
return nil, err
}
defined, ok := tp.(spec.DefineStruct)
if !ok {
return nil, fmt.Errorf("type %s is not a struct", v.RawName)
}
resolving[v.RawName] = true
defer delete(resolving, v.RawName)
for i := range defined.Members {
if !defined.Members[i].IsInline {
continue
}
resolved, err := a.resolveInlineType(defined.Members[i].Type, resolving)
if err != nil {
return nil, err
}
defined.Members[i].Type = resolved
}
return defined, nil
case spec.NestedStruct:
for i := range v.Members {
if !v.Members[i].IsInline {
continue
}
resolved, err := a.resolveInlineType(v.Members[i].Type, resolving)
if err != nil {
return nil, err
}
v.Members[i].Type = resolved
}
return v, nil
case spec.PointerType:
resolved, err := a.resolveInlineType(v.Type, resolving)
if err != nil {
return nil, err
}
v.Type = resolved
return v, nil
default:
return tp, nil
}
}
func (a *Analyzer) fillTypeExpr(expr *ast.TypeExpr) error {
head, _ := expr.CommentGroup()
switch val := expr.DataType.(type) {

View File

@@ -0,0 +1,69 @@
package parser
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
)
const inlineTagAPI = `
syntax = "v1"
type (
Auth {
Token string ` + "`header:\"Authorization\"`" + `
}
Middle {
Auth
}
PointerRequest {
*Auth
}
NestedRequest {
Middle
}
RecursiveRequest {
Token string ` + "`header:\"X-Token\"`" + `
*RecursiveRequest
}
)
service test-api {
@handler Pointer
get /pointer (PointerRequest)
@handler Nested
get /nested (NestedRequest)
@handler Recursive
get /recursive (RecursiveRequest)
}
`
func TestParseResolvesInlineTypesForTagLookup(t *testing.T) {
apiSpec, err := Parse("inline.api", inlineTagAPI)
require.NoError(t, err)
for _, name := range []string{"PointerRequest", "NestedRequest", "RecursiveRequest"} {
t.Run(name, func(t *testing.T) {
tp := findStructByName(t, apiSpec.Types, name)
require.NotEmpty(t, tp.GetTagMembers("header"))
require.Empty(t, tp.GetTagMembers("path"))
})
}
}
func findStructByName(t *testing.T, types []spec.Type, name string) spec.DefineStruct {
t.Helper()
for _, tp := range types {
if tp.Name() == name {
defined, ok := tp.(spec.DefineStruct)
require.True(t, ok)
return defined
}
}
t.Fatalf("type %s not found", name)
return spec.DefineStruct{}
}