Repository: zeromicro/go-zero
Pull request #3881 (2024-01-29 03:51:18)

(goctl): fix unresolved type if linked api imported


Patch:
diff --git a/tools/goctl/pkg/parser/api/parser/analyzer.go b/tools/goctl/pkg/parser/api/parser/analyzer.go
index 236630e4..a08974a9 100644
--- a/tools/goctl/pkg/parser/api/parser/analyzer.go
+++ b/tools/goctl/pkg/parser/api/parser/analyzer.go
@@ -403,6 +403,9 @@ func Parse(filename string, src interface{}) (*spec.ApiSpec, error) {
 	if err != nil {
 		return nil, err
 	}
+	if err := api.SelfCheck(); err != nil {
+		return nil, err
+	}
 
 	var result = new(spec.ApiSpec)
 	analyzer := Analyzer{
diff --git a/tools/goctl/pkg/parser/api/parser/api.go b/tools/goctl/pkg/parser/api/parser/api.go
index 6fc5584b..69d33b40 100644
--- a/tools/goctl/pkg/parser/api/parser/api.go
+++ b/tools/goctl/pkg/parser/api/parser/api.go
@@ -85,15 +85,12 @@ func convert2API(a *ast.AST, importSet map[string]lang.PlaceholderType, is *impo
 		}
 	}
 
-	if err := api.SelfCheck(); err != nil {
-		return nil, err
-	}
 	return api, nil
 }
 
 func (api *API) checkImportStmt() error {
 	f := newFilter()
-	b := f.addCheckItem("import value expression")
+	b := f.addCheckItem(api.Filename, "import value expression")
 	for _, v := range api.importStmt {
 		switch val := v.(type) {
 		case *ast.ImportLiteralStmt:
@@ -110,7 +107,7 @@ func (api *API) checkInfoStmt() error {
 		return nil
 	}
 	f := newFilter()
-	b := f.addCheckItem("info key expression")
+	b := f.addCheckItem(api.Filename, "info key expression")
 	for _, v := range api.info.Values {
 		b.check(v.Key)
 	}
@@ -119,9 +116,9 @@ func (api *API) checkInfoStmt() error {
 
 func (api *API) checkServiceStmt() error {
 	f := newFilter()
-	serviceNameChecker := f.addCheckItem("service name expression")
-	handlerChecker := f.addCheckItem("handler expression")
-	pathChecker := f.addCheckItem("path expression")
+	serviceNameChecker := f.addCheckItem(api.Filename, "service name expression")
+	handlerChecker := f.addCheckItem(api.Filename, "handler expression")
+	pathChecker := f.addCheckItem(api.Filename, "path expression")
 	var serviceName = map[string]string{}
 	for _, v := range api.ServiceStmts {
 		name := strings.TrimSuffix(v.Name.Format(""), "-api")
@@ -150,7 +147,7 @@ func (api *API) checkServiceStmt() error {
 
 func (api *API) checkTypeStmt() error {
 	f := newFilter()
-	b := f.addCheckItem("type expression")
+	b := f.addCheckItem(api.Filename, "type expression")
 	for _, v := range api.TypeStmt {
 		switch val := v.(type) {
 		case *ast.TypeLiteralStmt:
diff --git a/tools/goctl/pkg/parser/api/parser/filter.go b/tools/goctl/pkg/parser/api/parser/filter.go
index fd0ce0ae..b660f271 100644
--- a/tools/goctl/pkg/parser/api/parser/filter.go
+++ b/tools/goctl/pkg/parser/api/parser/filter.go
@@ -8,6 +8,7 @@ import (
 )
 
 type filterBuilder struct {
+	filename      string
 	m             map[string]placeholder.Type
 	checkExprName string
 	errorManager  *errorManager
@@ -15,10 +16,11 @@ type filterBuilder struct {
 
 func (b *filterBuilder) check(nodes ...*ast.TokenNode) {
 	for _, node := range nodes {
-		if _, ok := b.m[node.Token.Text]; ok {
+		fileNodeText := fmt.Sprintf("%s/%s", b.filename, node.Token.Text)
+		if _, ok := b.m[fileNodeText]; ok {
 			b.errorManager.add(ast.DuplicateStmtError(node.Pos(), "duplicate "+b.checkExprName))
 		} else {
-			b.m[node.Token.Text] = placeholder.PlaceHolder
+			b.m[fileNodeText] = placeholder.PlaceHolder
 		}
 	}
 }
@@ -46,8 +48,9 @@ func newFilter() *filter {
 	return &filter{}
 }
 
-func (f *filter) addCheckItem(checkExprName string) *filterBuilder {
+func (f *filter) addCheckItem(filename, checkExprName string) *filterBuilder {
 	b := &filterBuilder{
+		filename:      filename,
 		m:             make(map[string]placeholder.Type),
 		checkExprName: checkExprName,
 		errorManager:  newErrorManager(),


Test patch:
diff --git a/tools/goctl/pkg/parser/api/parser/analyzer_test.go b/tools/goctl/pkg/parser/api/parser/analyzer_test.go
index de4193b6..5e01d0f4 100644
--- a/tools/goctl/pkg/parser/api/parser/analyzer_test.go
+++ b/tools/goctl/pkg/parser/api/parser/analyzer_test.go
@@ -42,4 +42,8 @@ func Test_Parse(t *testing.T) {
 		_, err := Parse("./testdata/base.api", nil)
 		assertx.Error(t, err)
 	})
+	t.Run("link_import", func(t *testing.T) {
+		_, err := Parse("./testdata/link_import.api", nil)
+		assert.Nil(t, err)
+	})
 }
diff --git a/tools/goctl/pkg/parser/api/parser/testdata/base/request.api b/tools/goctl/pkg/parser/api/parser/testdata/base/request.api
new file mode 100644
index 00000000..fc1f3ac9
--- /dev/null
+++ b/tools/goctl/pkg/parser/api/parser/testdata/base/request.api
@@ -0,0 +1,6 @@
+syntax = "v1"
+
+type Baz {
+    Foo string `json:"foo"`
+    Baz bool `json:"bar"`
+}
diff --git a/tools/goctl/pkg/parser/api/parser/testdata/base/response.api b/tools/goctl/pkg/parser/api/parser/testdata/base/response.api
new file mode 100644
index 00000000..83091190
--- /dev/null
+++ b/tools/goctl/pkg/parser/api/parser/testdata/base/response.api
@@ -0,0 +1,10 @@
+syntax = "v1"
+
+import "request.api"
+type Bar {
+    Foo int `json:"foo"`
+    Bar bool `json:"bar"`
+    Baz
+    Qux map[string]string `json:"qux"`
+}
+
diff --git a/tools/goctl/pkg/parser/api/parser/testdata/link_import.api b/tools/goctl/pkg/parser/api/parser/testdata/link_import.api
new file mode 100644
index 00000000..fb08003f
--- /dev/null
+++ b/tools/goctl/pkg/parser/api/parser/testdata/link_import.api
@@ -0,0 +1,12 @@
+syntax = "v1"
+
+import "base/request.api"
+import "base/response.api"
+
+type Foo {}
+
+service demo {
+    @handler handlerName
+    get /users/id/:userId (Baz) returns (Bar)
+}
+