Golang中的types包与doc包使用指南

Golang中的types包与doc包使用指南 go/typesgo/doc 这两个包功能非常强大……但它们似乎完全没有关联

在我的使用场景中,需要获取包的详细类型信息(这可以通过 go/importer 配合 go/types 轻松实现),但同时希望获得对应的文档说明(go/types 不包含文档信息,只有 go/doc 或 go/ast 才提供)。

有没有办法能同时获取类型信息和文档呢?

举个例子: 如何同时获取 “fmt.Printf” 的类型信息文档说明?


更多关于Golang中的types包与doc包使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

引用 j-forster:

但这看起来像是要解析所有包两次…

解析两次真的那么糟糕吗?除非你的代码运行在时间要求极高的场景中,否则性能问题不应成为你首要关注的重点。先让代码正常运行,再根据需要进行优化(如果确实需要的话)。

更多关于Golang中的types包与doc包使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


也许我没有理解您问题的重点,但包文档并不是包含在单独的文档文件中,而是通过 godoc 工具 从代码本身以及相应位置的注释(例如在 “package main” 上方、函数上方、类型或方法上方等)生成的。这是您要找的内容吗?

… 而是通过 godoc 工具生成的 …

那么让我们来看一个示例:

type InfoIWantToGet struct {
  Type types.Type // 来自 go/types
  Doc  string     // 来自 go/doc
}
func getInfo(symbol string) InfoIWantToGet {
  // 怎么实现? :D
}

// 用法:getInfo("fmt.Printf")

正如我所说:我需要获取 Go 符号的类型文档。 关于类型信息,我会使用 go/types 包和 go/importer。 而对于文档,我会使用 go/doc 和解析后的 AST。 但这看起来像是要解析所有包两次…

package main

import (
	"fmt"
	"go/types"
	"log"
	"os/exec"
)

type InfoIWantToGet struct {
	Type types.Type // from go/types
	Doc  string     // from go/doc
}

func main() {
	info := getInfo("Println")
	fmt.Println(info)

}

func getInfo(symbol string) InfoIWantToGet {
	// how? :D
	r := InfoIWantToGet{}
	out, err := exec.Command(`godoc`, "fmt", symbol).Output()
	if err != nil {
		log.Fatal(err)
	}
	r.Doc = string(out)
	return r
}

而且go/type是在包级别工作的,而不是函数级别,我实在不太理解你这里的意图…

在 Go 中,go/typesgo/doc 包确实设计用于不同目的,但可以通过结合使用来同时获取类型信息和文档。go/types 提供类型检查后的精确类型信息,而 go/doc 从 AST 解析文档注释。以下是一个示例,展示如何获取 fmt.Printf 的类型信息和文档。

首先,使用 go/types 获取类型信息,然后通过 go/doc 从包中提取文档。这需要加载包并解析其 AST。

package main

import (
    "fmt"
    "go/ast"
    "go/doc"
    "go/importer"
    "go/parser"
    "go/token"
    "go/types"
    "log"
    "strings"
)

func main() {
    // 设置文件集和包路径
    fset := token.NewFileSet()
    pkgPath := "fmt"

    // 解析包目录,获取 AST 文件
    pkgs, err := parser.ParseDir(fset, "/usr/local/go/src/fmt", nil, parser.ParseComments)
    if err != nil {
        log.Fatal("解析包失败:", err)
    }

    var files []*ast.File
    for _, pkg := range pkgs {
        for _, file := range pkg.Files {
            files = append(files, file)
        }
    }

    // 使用 go/types 进行类型检查
    conf := types.Config{Importer: importer.Default()}
    info := &types.Info{
        Defs: make(map[*ast.Ident]types.Object),
        Uses: make(map[*ast.Ident]types.Object),
    }
    pkg, err := conf.Check(pkgPath, fset, files, info)
    if err != nil {
        log.Fatal("类型检查失败:", err)
    }

    // 获取 fmt.Printf 的类型信息
    printfObj := pkg.Scope().Lookup("Printf")
    if printfObj == nil {
        log.Fatal("未找到 Printf")
    }
    printfFunc, ok := printfObj.(*types.Func)
    if !ok {
        log.Fatal("Printf 不是函数")
    }
    printfType := printfFunc.Type().(*types.Signature)
    fmt.Printf("Printf 类型: %s\n", printfType)

    // 使用 go/doc 提取文档
    docPkg := doc.New(pkg, pkgPath, 0)
    for _, f := range docPkg.Funcs {
        if f.Name == "Printf" {
            fmt.Printf("Printf 文档: %s\n", strings.TrimSpace(f.Doc))
            break
        }
    }
}

在这个示例中:

  • 我们解析 fmt 包的源代码文件(假设路径为 /usr/local/go/src/fmt,实际中可能需要根据环境调整)。
  • 使用 go/types 进行类型检查,获取 Printf 的类型签名。
  • 使用 go/doc 从 AST 中提取 Printf 的文档注释。

运行此代码将输出类似以下内容:

Printf 类型: func(format string, a ...interface{}) (n int, err error)
Printf 文档: Printf formats according to a format specifier and writes to standard output.
It returns the number of bytes written and any write error encountered.

注意:这种方法依赖于源代码的可用性,且路径可能需要调整。对于标准库,通常可以直接访问 Go 安装目录。如果处理自定义包,确保正确设置包路径。结合 go/astgo/types 可以更精细地控制,但此示例提供了一个基本框架。

回到顶部