diff --git a/parser.go b/parser.go index 6bf991e92..de2450933 100644 --- a/parser.go +++ b/parser.go @@ -975,19 +975,44 @@ func matchExtension(extensionToMatch string, comments []*ast.Comment) (match boo return true } +func getFuncDoc(decl any) (*ast.CommentGroup, bool) { + switch astDecl := decl.(type) { + case *ast.FuncDecl: // func name() {} + return astDecl.Doc, true + case *ast.GenDecl: // var name = namePointToFuncDirectlyOrIndirectly + if astDecl.Tok != token.VAR { + return nil, false + } + varSpec, ok := astDecl.Specs[0].(*ast.ValueSpec) + if !ok || len(varSpec.Values) != 1 { + return nil, false + } + _, ok = getFuncDoc(varSpec) + return astDecl.Doc, ok + case *ast.ValueSpec: + value, ok := astDecl.Values[0].(*ast.Ident) + if !ok || value == nil { + return nil, false + } + _, ok = getFuncDoc(value.Obj.Decl) + return astDecl.Doc, ok + } + return nil, false +} + // ParseRouterAPIInfo parses router api info for given astFile. func (parser *Parser) ParseRouterAPIInfo(fileInfo *AstFileInfo) error { - for _, astDescription := range fileInfo.File.Decls { + for _, decl := range fileInfo.File.Decls { if (fileInfo.ParseFlag & ParseOperations) == ParseNone { continue } - astDeclaration, ok := astDescription.(*ast.FuncDecl) - if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil { - if parser.matchTags(astDeclaration.Doc.List) && - matchExtension(parser.parseExtension, astDeclaration.Doc.List) { + funcDoc, ok := getFuncDoc(decl) + if ok && funcDoc != nil && funcDoc.List != nil { + if parser.matchTags(funcDoc.List) && + matchExtension(parser.parseExtension, funcDoc.List) { // for per 'function' comment, create a new 'Operation' object operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir)) - for _, comment := range astDeclaration.Doc.List { + for _, comment := range funcDoc.List { err := operation.ParseComment(comment.Text, fileInfo.File) if err != nil { return fmt.Errorf("ParseComment error in file %s :%+v", fileInfo.Path, err) diff --git a/parser_test.go b/parser_test.go index d818f705c..584a68a74 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2153,7 +2153,7 @@ func TestParseTypeOverrides(t *testing.T) { assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") - //windows will fail: \r\n \n + // windows will fail: \r\n \n assert.Equal(t, string(expected), string(b)) } @@ -2234,7 +2234,7 @@ func TestParseExternalModels(t *testing.T) { err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") - //ioutil.WriteFile("./testdata/external_models/main/expected.json",b,0777) + // ioutil.WriteFile("./testdata/external_models/main/expected.json",b,0777) expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) assert.NoError(t, err) assert.Equal(t, string(expected), string(b)) @@ -3636,6 +3636,40 @@ func Fun() { assert.Empty(t, childName) } +func TestParser_genVarDefinedFuncDoc(t *testing.T) { + t.Parallel() + + src := ` +package main + +func f() {} + +// @Summary generate var-defined functions' doc +// @Router /test [get] +var Func = f + +// @Summary generate indirectly pointing +// @Router /test2 [get] +var Func2 = Func +` + p := New() + err := p.packages.ParseFile("api", "api/api.go", src, ParseAll) + assert.NoError(t, err) + _, _ = p.packages.ParseTypes() + err = p.packages.RangeFiles(p.ParseRouterAPIInfo) + assert.NoError(t, err) + + val, ok := p.swagger.Paths.Paths["/test"] + assert.True(t, ok) + assert.NotNil(t, val.Get) + assert.Equal(t, val.Get.OperationProps.Summary, "generate var-defined functions' doc") + + val2, ok := p.swagger.Paths.Paths["/test2"] + assert.True(t, ok) + assert.NotNil(t, val2.Get) + assert.Equal(t, val2.Get.OperationProps.Summary, "generate indirectly pointing") +} + func TestDefineTypeOfExample(t *testing.T) { t.Run("String type", func(t *testing.T) {