登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go go/doc.Examples 怎样区分空输出与未声明输出

来源:17golang原创

时间:2026-11-05 19:31:11 145浏览 收藏

go/doc.Examples 返回的 Example.Output 为空字符串,既可能表示没有输出声明,也可能表示显式声明了空输出。需要同时读取 EmptyOutput:它为 true 表示期望空输出;它为 false 且 Output 为空时,表示未声明输出。不要仅用 Output == "" 把两者合并。

官方地址:https://pkg.go.dev/go/doc

先确定工具只抽取声明

为文档系统整理示例时,可以把测试源码当作文本交给 parser,再让 doc.Examples 从 AST 中抽取记录。这个工具的职责是识别注释声明,不是运行示例。对来源代码保持这个边界,既能避免误把预期文本写成实测输出,也不需要执行源文件内的函数。

本文示例在普通 Go 程序中使用标准库,不启动子进程、不调用 go test,也不把 Play 非空视为执行成功。本次文章没有运行下面的代码。

保留注释后,再提取 Example

关键参数是 parser.ParseComments。输出声明存在于注释中,解析时丢掉注释,后续自然无法可靠地区分状态。下面把三个原创示例放在一个源码字符串里,分别表达空输出、无声明和非空输出。

package main

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

// classify 只解释源文件里的输出声明,不判定示例是否运行成功。
func classify(e *doc.Example) string {
    if e.EmptyOutput {
        return "显式空输出"
    }
    if e.Output != "" {
        return "声明非空输出"
    }
    return "未声明输出"
}

func main() {
    // 三种原创输入;字符串里的代码仅作为解析材料,不会被调用。
    src := `package sample_test
import "fmt"

// Example_blank 声明预期没有输出。
func Example_blank() {
    // Output:
}

// Example_missing 没有输出声明。
func Example_missing() {
    // 这里只是普通注释。
}

// Example_text 声明预期文本。
func Example_text() {
    fmt.Println("ready")
    // Output: ready
}
`
    fset := token.NewFileSet()
    file, err := parser.ParseFile(fset, "sample_test.go", src, parser.ParseComments)
    if err != nil {
        fmt.Println("解析失败:", err)
        return // 解析失败时不生成看似完整的声明清单。
    }
    for _, e := range doc.Examples(file) {
        fmt.Printf("%s: %s, Output=%q, EmptyOutput=%t\n",
            e.Name, classify(e), e.Output, e.EmptyOutput)
    }
}

Example_blank 的 Output 为空,EmptyOutput 为 true;Example_missing 的 Output 为空,EmptyOutput 为 false;Example_text 的 Output 包含 ready 对应的预期文本。这里给出的是代码语义推导,没有伪造终端运行记录。打印时使用 %q 是为了让换行等不可见字符可辨识。

源码注释解析与示例记录的静态组成图
静态结构图:源码、注释和 AST 是抽取工具的数据对象,Example 记录来自静态解析;图中不包含运行结果或终端截图。

按两个字段保存三种声明状态

EmptyOutputOutput应保存的含义
true空字符串显式期望空输出
false非空文本声明了预期文本
false空字符串未声明输出

对标准库抽取出的记录,上面三种组合足以解决本题。若业务代码手动构造 Example 并同时设置相互矛盾的字段,应另行定义输入契约,不能把手工对象的组合归咎于解析器。示例的 Unordered 字段另外表示输出顺序语义,不应被用来代替 EmptyOutput。

不要为省事对 Output 做 TrimSpace 后再覆盖原字段:原始文本有助于追踪声明来源。业务需要展示摘要时,可以额外保存展示字段,让抽取值和呈现值各有用途。

Example 字段与三种输出声明状态的关系图
静态说明图:Output 与 EmptyOutput 共同描述声明状态,Name 标识示例;这些字段不提供运行通过证明。

处理权限和审计记录时保留边界

实际接入仓库扫描时,先限定输入目录和源码大小,读取失败或语法解析失败单独记录。解析不执行函数,但仍会消耗内存和 CPU;面对不可信大文件,不应省略资源限制。本文的内嵌字符串只是为了把核心处理保持为一个可读例子。

清单可保留仓库相对文件名、Example.Name、Order、Output、EmptyOutput 和解析状态。doc.Examples 按 Name 排序;若报告希望展示源码顺序,应使用 Order 作依据,不能把返回列表顺序直接当作源文件顺序。

发布文档前,措辞应是“声明预期为空”或“未声明输出”,而非“执行后没有输出”。是否编译通过、运行时是否有副作用、输出是否匹配,需要独立的执行证据,静态抽取本身不能回答。

只有普通注释,函数体也没有打印,算空输出吗?

不算显式空输出声明。没有 Output 注释时,不能仅根据函数看起来安静就设置 EmptyOutput。

Play 非空是不是已经运行过?

不是。它是可用于表示完整示例程序的 AST 字段,不是运行回执。

直接调用 Examples 可以拿到 Suffix 吗?

该入口不填充 Suffix;官方文档说明 NewFromFiles 会为其从测试文件找到的示例填充此字段。不要据此误判输出声明缺失。

技术依据:Example 与 Examples 官方文档、示例抽取官方实现。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>