gpt4 book ai didi

parsing - Go 解析器未检测到结构类型的文档注释

转载 作者:IT老高 更新时间:2023-10-28 13:07:17 27 4
gpt4 key购买 nike

我正在尝试使用 Go 的 parser 读取结构类型的相关文档注释。和 ast包。在此示例中,代码只是将自身用作源代码。

package main

import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)

// FirstType docs
type FirstType struct {
// FirstMember docs
FirstMember string
}

// SecondType docs
type SecondType struct {
// SecondMember docs
SecondMember string
}

// Main docs
func main() {
fset := token.NewFileSet() // positions are relative to fset

d, err := parser.ParseDir(fset, "./", nil, parser.ParseComments)
if err != nil {
fmt.Println(err)
return
}

for _, f := range d {
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.FuncDecl:
fmt.Printf("%s:\tFuncDecl %s\t%s\n", fset.Position(n.Pos()), x.Name, x.Doc)
case *ast.TypeSpec:
fmt.Printf("%s:\tTypeSpec %s\t%s\n", fset.Position(n.Pos()), x.Name, x.Doc)
case *ast.Field:
fmt.Printf("%s:\tField %s\t%s\n", fset.Position(n.Pos()), x.Names, x.Doc)
}

return true
})
}
}

函数和字段的注释文档输出没有问题,但由于某种原因,找不到“FirstType docs”和“SecondType docs”。我错过了什么? Go 版本是 1.1.2。

(要运行上述内容,请将其保存到 main.go 文件中,然后 go run main.go)

最佳答案

好问题!

查看 go/doc 的源代码,我们可以看到它必须在 readType 函数中处理同样的情况。那里,它说:

324     func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {
...
334 // compute documentation
335 doc := spec.Doc
336 spec.Doc = nil // doc consumed - remove from AST
337 if doc == nil {
338 // no doc associated with the spec, use the declaration doc, if any
339 doc = decl.Doc
340 }
...

特别注意它需要如何处理 AST 没有附加到 TypeSpec 的文档的情况。为此,它依赖于 GenDecl。这为我们提供了关于如何直接使用 AST 来解析结构的文档注释的线索。调整问题代码中的 for 循环,为 *ast.GenDecl 添加一个案例:

for _, f := range d {
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.FuncDecl:
fmt.Printf("%s:\tFuncDecl %s\t%s\n", fset.Position(n.Pos()), x.Name, x.Doc.Text())
case *ast.TypeSpec:
fmt.Printf("%s:\tTypeSpec %s\t%s\n", fset.Position(n.Pos()), x.Name, x.Doc.Text())
case *ast.Field:
fmt.Printf("%s:\tField %s\t%s\n", fset.Position(n.Pos()), x.Names, x.Doc.Text())
case *ast.GenDecl:
fmt.Printf("%s:\tGenDecl %s\n", fset.Position(n.Pos()), x.Doc.Text())
}

return true
})
}

运行它给我们:

main.go:3:1:    GenDecl %!s(*ast.CommentGroup=<nil>)
main.go:11:1: GenDecl &{[%!s(*ast.Comment=&{69 // FirstType docs})]}
main.go:11:6: TypeSpec FirstType %!s(*ast.CommentGroup=<nil>)
main.go:13:2: Field [FirstMember] &{[%!s(*ast.Comment=&{112 // FirstMember docs})]}
main.go:17:1: GenDecl &{[%!s(*ast.Comment=&{155 // SecondType docs})]}
main.go:17:6: TypeSpec SecondType %!s(*ast.CommentGroup=<nil>)
main.go:19:2: Field [SecondMember] &{[%!s(*ast.Comment=&{200 // SecondMember docs})]}
main.go:23:1: FuncDecl main &{[%!s(*ast.Comment=&{245 // Main docs})]}
main.go:33:23: Field [n] %!s(*ast.CommentGroup=<nil>)
main.go:33:35: Field [] %!s(*ast.CommentGroup=<nil>)

还有,嘿!

我们已经打印出了久违的 FirstType 文档SecondType 文档!但这并不令人满意。为什么文档没有附加到 TypeSpecgo/doc/reader.go 文件花费了非常多的篇幅来规避这个问题,实际上生成了一个假的 GenDecl 并将其传递给 readType前面提到的函数,如果没有与struct声明相关的文档!

   503  fake := &ast.GenDecl{
504 Doc: d.Doc,
505 // don't use the existing TokPos because it
506 // will lead to the wrong selection range for
507 // the fake declaration if there are more
508 // than one type in the group (this affects
509 // src/cmd/godoc/godoc.go's posLink_urlFunc)
510 TokPos: s.Pos(),
511 Tok: token.TYPE,
512 Specs: []ast.Spec{s},
513 }

但是为什么会这样呢?

想象一下,我们稍微改变了问题中代码的类型定义(定义这样的结构并不常见,但 Go 仍然有效):

// This documents FirstType and SecondType together
type (
// FirstType docs
FirstType struct {
// FirstMember docs
FirstMember string
}

// SecondType docs
SecondType struct {
// SecondMember docs
SecondMember string
}
)

运行代码(包括 ast.GenDecl 的情况),我们得到:

main.go:3:1:    GenDecl %!s(*ast.CommentGroup=<nil>)
main.go:11:1: GenDecl &{[%!s(*ast.Comment=&{69 // This documents FirstType and SecondType together})]}
main.go:13:2: TypeSpec FirstType &{[%!s(*ast.Comment=&{129 // FirstType docs})]}
main.go:15:3: Field [FirstMember] &{[%!s(*ast.Comment=&{169 // FirstMember docs})]}
main.go:19:2: TypeSpec SecondType &{[%!s(*ast.Comment=&{215 // SecondType docs})]}
main.go:21:3: Field [SecondMember] &{[%!s(*ast.Comment=&{257 // SecondMember docs})]}
main.go:26:1: FuncDecl main &{[%!s(*ast.Comment=&{306 // Main docs})]}
main.go:36:23: Field [n] %!s(*ast.CommentGroup=<nil>)
main.go:36:35: Field [] %!s(*ast.CommentGroup=<nil>)

没错

现在结构类型定义有了自己的文档,GenDecl 也有了自己的文档。在第一种情况下,发布在问题中,文档附加到 GenDecl,因为 AST 看到类型定义的括号版本的“收缩”的各个结构类型定义,并且想要处理所有定义都相同,无论它们是否被分组。变量定义也会发生同样的事情,如下所示:

// some general docs
var (
// v docs
v int

// v2 docs
v2 string
)

因此,如果您希望使用纯 AST 解析评论,您需要注意它是如何工作的。但正如@mjibson 建议的那样,首选方法是使用go/doc。祝你好运!

关于parsing - Go 解析器未检测到结构类型的文档注释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19580688/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com