diff --git a/tools/goctl/api/gogen/gen_test.go b/tools/goctl/api/gogen/gen_test.go index d2cb684267177..349312e50c1de 100644 --- a/tools/goctl/api/gogen/gen_test.go +++ b/tools/goctl/api/gogen/gen_test.go @@ -2,7 +2,12 @@ package gogen import ( _ "embed" + "go/ast" goformat "go/format" + "go/importer" + goparser "go/parser" + "go/token" + "go/types" "os" "path/filepath" "strings" @@ -48,6 +53,10 @@ var ( noStructTagApi string //go:embed testdata/nest_type_api.api nestTypeApi string + //go:embed testdata/import_twice.api + importTwiceApi string + //go:embed testdata/another_import_api.api + anotherImportApi string ) func TestParser(t *testing.T) { @@ -232,6 +241,46 @@ func TestHasImportApi(t *testing.T) { validate(t, filename) } +func TestImportTwiceOnExperimental(t *testing.T) { + defaultExperimental := env.Get(env.GoctlExperimental) + env.Set(t, env.GoctlExperimental, "on") + defer env.Set(t, env.GoctlExperimental, defaultExperimental) + + filename := "greet.api" + err := os.WriteFile(filename, []byte(importTwiceApi), os.ModePerm) + assert.Nil(t, err) + defer os.Remove(filename) + + importApiName := "importApi.api" + err = os.WriteFile(importApiName, []byte(importApi), os.ModePerm) + assert.Nil(t, err) + defer os.Remove(importApiName) + + hasImportApiName := "hasImportApi.api" + err = os.WriteFile(hasImportApiName, []byte(hasImportApi), os.ModePerm) + assert.Nil(t, err) + defer os.Remove(hasImportApiName) + + anotherImportApiName := "anotherImportApi.api" + err = os.WriteFile(anotherImportApiName, []byte(anotherImportApi), os.ModePerm) + assert.Nil(t, err) + defer os.Remove(anotherImportApiName) + + api, err := parser.Parse(filename) + assert.Nil(t, err) + + var hasInline bool + for _, ty := range api.Types { + if ty.Name() == "ImportData" { + hasInline = true + break + } + } + assert.True(t, hasInline) + + validate(t, filename) +} + func TestNoStructApi(t *testing.T) { filename := "greet.api" err := os.WriteFile(filename, []byte(noStructTagApi), os.ModePerm) @@ -287,6 +336,9 @@ func validateWithCamel(t *testing.T, api, camel string) { code, err := os.ReadFile(path) assert.Nil(t, err) assert.Nil(t, validateCode(string(code))) + if strings.HasSuffix(path, "types.go") { + assert.Nil(t, checkRedeclaredType(string(code))) + } } return nil }) @@ -301,3 +353,20 @@ func validateCode(code string) error { _, err := goformat.Source([]byte(code)) return err } + +func checkRedeclaredType(code string) error { + fset := token.NewFileSet() + f, err := goparser.ParseFile(fset, "", code, goparser.ParseComments) + if err != nil { + return err + } + + conf := types.Config{ + Error: func(err error) {}, + Importer: importer.Default(), + } + + info := types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} + _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &info) + return err +} diff --git a/tools/goctl/api/gogen/testdata/another_import_api.api b/tools/goctl/api/gogen/testdata/another_import_api.api new file mode 100644 index 0000000000000..d82b67e409716 --- /dev/null +++ b/tools/goctl/api/gogen/testdata/another_import_api.api @@ -0,0 +1,17 @@ +import "importApi.api" + +type AnotherRequest { + Name string `path:"name,options=you|me"` +} + +type AnotherResponse { + Message string `json:"message"` // message +} + +@server( + group: greet +) +service A-api { + @handler AnotherImportHandler + get /greet/from/another/:name(AnotherRequest) returns (AnotherResponse) +} \ No newline at end of file diff --git a/tools/goctl/api/gogen/testdata/has_import_api.api b/tools/goctl/api/gogen/testdata/has_import_api.api index 6710280e6bc49..35dd5ed389263 100644 --- a/tools/goctl/api/gogen/testdata/has_import_api.api +++ b/tools/goctl/api/gogen/testdata/has_import_api.api @@ -1,16 +1,17 @@ import "importApi.api" -type Request struct { +type Request { Name string `path:"name,options=you|me"` } -type Response struct { +type Response { Message string `json:"message"` // message } +@server( + group: greet +) service A-api { - @server( - handler: GreetHandler - ) + @handler GreetHandler get /greet/from/:name(Request) returns (Response) } \ No newline at end of file diff --git a/tools/goctl/api/gogen/testdata/import_api.api b/tools/goctl/api/gogen/testdata/import_api.api index b663997d1f1ad..ac7921ca3f83f 100644 --- a/tools/goctl/api/gogen/testdata/import_api.api +++ b/tools/goctl/api/gogen/testdata/import_api.api @@ -1,3 +1,3 @@ -type ImportData struct { +type ImportData { Name string `path:"name,options=you|me"` } \ No newline at end of file diff --git a/tools/goctl/api/gogen/testdata/import_twice.api b/tools/goctl/api/gogen/testdata/import_twice.api new file mode 100644 index 0000000000000..00e6c0b5b79ab --- /dev/null +++ b/tools/goctl/api/gogen/testdata/import_twice.api @@ -0,0 +1,2 @@ +import "hasImportApi.api" +import "anotherImportApi.api" diff --git a/tools/goctl/pkg/parser/api/parser/api.go b/tools/goctl/pkg/parser/api/parser/api.go index 69d33b40c5f91..aee27727ad6cb 100644 --- a/tools/goctl/pkg/parser/api/parser/api.go +++ b/tools/goctl/pkg/parser/api/parser/api.go @@ -32,11 +32,8 @@ type API struct { func convert2API(a *ast.AST, importSet map[string]lang.PlaceholderType, is *importstack.ImportStack) (*API, error) { var api = new(API) api.importManager = is - api.importSet = make(map[string]lang.PlaceholderType) + api.importSet = importSet api.Filename = a.Filename - for k, v := range importSet { - api.importSet[k] = v - } one := a.Stmts[0] syntax, ok := one.(*ast.SyntaxStmt) if !ok {