Skip to content

Commit 7204462

Browse files
authored
Added suport for parsing comments inside of function bodies (#1824)
Added suport for parsing comments inside of function bodies --------- Co-authored-by: Jonas Ha <[email protected]>
1 parent e55c557 commit 7204462

File tree

5 files changed

+124
-26
lines changed

5 files changed

+124
-26
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ OPTIONS:
116116
--templateDelims value, --td value Provide custom delimiters for Go template generation. The format is leftDelim,rightDelim. For example: "[[,]]"
117117
--collectionFormat value, --cf value Set default collection format (default: "csv")
118118
--state value Initial state for the state machine (default: ""), @HostState in root file, @State in other files
119+
--parseFuncBody Parse API info within body of functions in go files, disabled by default (default: false)
119120
--help, -h show help (default: false)
120121
```
121122
@@ -931,7 +932,7 @@ func GetPosts(w http.ResponseWriter, r *http.Request) {
931932
_ = web.GenericNestedResponse[types.Post]{}
932933
}
933934
```
934-
See [this file](https://github.com/swaggo/swag/blob/master/testdata/generics_nested/api/api.go) for more details
935+
See [this file](https://github.com/swaggo/swag/blob/master/testdata/generics_nested/api/api.go) for more details
935936
and other examples.
936937
937938
### Change the default Go Template action delimiters

cmd/swag/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const (
4242
collectionFormatFlag = "collectionFormat"
4343
packagePrefixFlag = "packagePrefix"
4444
stateFlag = "state"
45+
parseFuncBodyFlag = "parseFuncBody"
4546
)
4647

4748
var initFlags = []cli.Flag{
@@ -180,6 +181,11 @@ var initFlags = []cli.Flag{
180181
Value: "",
181182
Usage: "Set host state for swagger.json",
182183
},
184+
&cli.BoolFlag{
185+
Name: parseFuncBodyFlag,
186+
// Value: false,
187+
Usage: "Parse API info within body of functions in go files, disabled by default (default: false)",
188+
},
183189
}
184190

185191
func initAction(ctx *cli.Context) error {
@@ -261,6 +267,7 @@ func initAction(ctx *cli.Context) error {
261267
CollectionFormat: collectionFormat,
262268
PackagePrefix: ctx.String(packagePrefixFlag),
263269
State: ctx.String(stateFlag),
270+
ParseFuncBody: ctx.Bool(parseFuncBodyFlag),
264271
})
265272
}
266273

gen/gen.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ type Config struct {
146146

147147
// State set host state
148148
State string
149+
150+
// ParseFuncBody whether swag should parse api info inside of funcs
151+
ParseFuncBody bool
149152
}
150153

151154
// Build builds swagger json file for given searchDir and mainAPIFile. Returns json.
@@ -213,6 +216,7 @@ func (g *Gen) Build(config *Config) error {
213216
p.ParseInternal = config.ParseInternal
214217
p.RequiredByDefault = config.RequiredByDefault
215218
p.HostState = config.State
219+
p.ParseFuncBody = config.ParseFuncBody
216220

217221
if err := p.ParseAPIMultiSearchDir(searchDirs, config.MainAPIFile, config.ParseDepth); err != nil {
218222
return err

parser.go

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ type Parser struct {
179179

180180
// HostState is the state of the host
181181
HostState string
182+
183+
// ParseFuncBody whether swag should parse api info inside of funcs
184+
ParseFuncBody bool
182185
}
183186

184187
// FieldParserFactory create FieldParser.
@@ -1016,37 +1019,57 @@ func matchExtension(extensionToMatch string, comments []*ast.Comment) (match boo
10161019

10171020
// ParseRouterAPIInfo parses router api info for given astFile.
10181021
func (parser *Parser) ParseRouterAPIInfo(fileInfo *AstFileInfo) error {
1019-
DeclsLoop:
1020-
for _, astDescription := range fileInfo.File.Decls {
1021-
if (fileInfo.ParseFlag & ParseOperations) == ParseNone {
1022-
continue
1022+
if (fileInfo.ParseFlag & ParseOperations) == ParseNone {
1023+
return nil
1024+
}
1025+
1026+
// parse File.Comments instead of File.Decls.Doc if ParseFuncBody flag set to "true"
1027+
if parser.ParseFuncBody {
1028+
for _, astComments := range fileInfo.File.Comments {
1029+
if astComments.List != nil {
1030+
if err := parser.parseRouterAPIInfoComment(astComments.List, fileInfo); err != nil {
1031+
return err
1032+
}
1033+
}
10231034
}
1035+
1036+
return nil
1037+
}
1038+
1039+
for _, astDescription := range fileInfo.File.Decls {
10241040
astDeclaration, ok := astDescription.(*ast.FuncDecl)
10251041
if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
1026-
if parser.matchTags(astDeclaration.Doc.List) &&
1027-
matchExtension(parser.parseExtension, astDeclaration.Doc.List) {
1028-
// for per 'function' comment, create a new 'Operation' object
1029-
operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
1030-
for _, comment := range astDeclaration.Doc.List {
1031-
err := operation.ParseComment(comment.Text, fileInfo.File)
1032-
if err != nil {
1033-
return fmt.Errorf("ParseComment error in file %s :%+v", fileInfo.Path, err)
1034-
}
1035-
if operation.State != "" && operation.State != parser.HostState {
1036-
continue DeclsLoop
1037-
}
1038-
}
1039-
err := processRouterOperation(parser, operation)
1040-
if err != nil {
1041-
return err
1042-
}
1042+
if err := parser.parseRouterAPIInfoComment(astDeclaration.Doc.List, fileInfo); err != nil {
1043+
return err
10431044
}
10441045
}
10451046
}
10461047

10471048
return nil
10481049
}
10491050

1051+
func (parser *Parser) parseRouterAPIInfoComment(comments []*ast.Comment, fileInfo *AstFileInfo) error {
1052+
if parser.matchTags(comments) && matchExtension(parser.parseExtension, comments) {
1053+
// for per 'function' comment, create a new 'Operation' object
1054+
operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
1055+
for _, comment := range comments {
1056+
err := operation.ParseComment(comment.Text, fileInfo.File)
1057+
if err != nil {
1058+
return fmt.Errorf("ParseComment error in file %s :%+v", fileInfo.Path, err)
1059+
}
1060+
if operation.State != "" && operation.State != parser.HostState {
1061+
return nil
1062+
}
1063+
}
1064+
err := processRouterOperation(parser, operation)
1065+
if err != nil {
1066+
return err
1067+
}
1068+
}
1069+
1070+
return nil
1071+
}
1072+
10501073
func refRouteMethodOp(item *spec.PathItem, method string) (op **spec.Operation) {
10511074
switch method {
10521075
case http.MethodGet:

parser_test.go

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3363,14 +3363,14 @@ func TestParseFunctionScopedStructDefinition(t *testing.T) {
33633363
src := `
33643364
package main
33653365
3366-
// @Param request body main.Fun.request true "query params"
3366+
// @Param request body main.Fun.request true "query params"
33673367
// @Success 200 {object} main.Fun.response
33683368
// @Router /test [post]
33693369
func Fun() {
33703370
type request struct {
33713371
Name string
33723372
}
3373-
3373+
33743374
type response struct {
33753375
Name string
33763376
Child string
@@ -3395,14 +3395,14 @@ func TestParseFunctionScopedStructRequestResponseJSON(t *testing.T) {
33953395
src := `
33963396
package main
33973397
3398-
// @Param request body main.Fun.request true "query params"
3398+
// @Param request body main.Fun.request true "query params"
33993399
// @Success 200 {object} main.Fun.response
34003400
// @Router /test [post]
34013401
func Fun() {
34023402
type request struct {
34033403
Name string
34043404
}
3405-
3405+
34063406
type response struct {
34073407
Name string
34083408
Child string
@@ -4123,3 +4123,66 @@ func TestParser_skipPackageByPrefix(t *testing.T) {
41234123
assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/cmd"))
41244124
assert.False(t, parser.skipPackageByPrefix("github.com/swaggo/swag/gen"))
41254125
}
4126+
4127+
func TestParser_ParseRouterApiInFuncBody(t *testing.T) {
4128+
t.Parallel()
4129+
4130+
src := `
4131+
package test
4132+
4133+
func Test(){
4134+
// @Router /api/{id} [get]
4135+
_ = func() {
4136+
}
4137+
}
4138+
`
4139+
p := New()
4140+
p.ParseFuncBody = true
4141+
err := p.packages.ParseFile("api", "api/api.go", src, ParseAll)
4142+
assert.NoError(t, err)
4143+
4144+
err = p.packages.RangeFiles(p.ParseRouterAPIInfo)
4145+
assert.NoError(t, err)
4146+
4147+
ps := p.swagger.Paths.Paths
4148+
4149+
val, ok := ps["/api/{id}"]
4150+
4151+
assert.True(t, ok)
4152+
assert.NotNil(t, val.Get)
4153+
}
4154+
4155+
func TestParser_ParseRouterApiInfoInAndOutFuncBody(t *testing.T) {
4156+
t.Parallel()
4157+
4158+
src := `
4159+
package test
4160+
4161+
// @Router /api/outside [get]
4162+
func otherRoute(){
4163+
}
4164+
4165+
func Test(){
4166+
// @Router /api/inside [get]
4167+
_ = func() {
4168+
}
4169+
}
4170+
`
4171+
p := New()
4172+
p.ParseFuncBody = true
4173+
err := p.packages.ParseFile("api", "api/api.go", src, ParseAll)
4174+
assert.NoError(t, err)
4175+
4176+
err = p.packages.RangeFiles(p.ParseRouterAPIInfo)
4177+
assert.NoError(t, err)
4178+
4179+
ps := p.swagger.Paths.Paths
4180+
4181+
val1, ok := ps["/api/outside"]
4182+
assert.True(t, ok)
4183+
assert.NotNil(t, val1.Get)
4184+
4185+
val2, ok := ps["/api/inside"]
4186+
assert.True(t, ok)
4187+
assert.NotNil(t, val2.Get)
4188+
}

0 commit comments

Comments
 (0)