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

Parsing Go解析器未检测到结构类型上的文档注释,parsing,go,abstract-syntax-tree,Parsing,Go,Abstract Syntax Tree,我正在尝试使用Go和包读取结构类型上的关联文档注释。在本例中,代码只是将自身用作源代码 package main import ( "fmt" "go/ast" "go/parser" "go/token" ) // FirstType docs type FirstType struct { // FirstMember docs FirstMember string } // SecondType docs type SecondType

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

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
        })
    }
}
func和字段的注释文档输出没有问题,但由于某些原因,找不到“FirstType文档”和“SecondType文档”。我错过了什么?Go版本是1.1.2


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

您需要使用
go/doc
包从ast提取文档:

package main

import (
    "fmt"
    "go/doc"
    "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 k, f := range d {
        fmt.Println("package", k)
        p := doc.New(f, "./", 0)

        for _, t := range p.Types {
            fmt.Println("  type", t.Name)
            fmt.Println("    docs:", t.Doc)
        }
    }
}
好问题! 查看
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>)
但为什么会这样? 假设我们稍微更改了问题中代码的类型定义(这样定义结构并不常见,但仍然有效):

运行代码(包括
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>)

因此,如果您希望使用纯AST解析注释,您需要知道这就是它的工作方式。但是@mjibson建议的首选方法是使用
go/doc
。祝你好运

尽管go/doc是一种解决方案,但这仍然不能回答为什么go/ast不提供关于类型的注释/doc(例如关于
TypeSpec
// This documents FirstType and SecondType together
type (
    // FirstType docs
    FirstType struct {
        // FirstMember docs
        FirstMember string
    }

    // SecondType docs
    SecondType struct {
        // SecondMember docs
        SecondMember string
    }
)
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>)
// some general docs
var (
    // v docs
    v int

    // v2 docs
    v2 string
)