当使用-analysis
标志调用时,godoc 会对它索引的 Go 包执行静态分析,并在源代码和包视图中显示结果。本文档简要介绍了这些功能。
类型分析功能
godoc -analysis=type
执行类似于编译器执行的静态检查:它检测格式错误的程序,将每个标识符解析为它表示的实体,计算每个表达式的类型和每个类型的函数集,并确定哪些类型可以分配给每个接口类型。类型分析速度相对较快,例如,对于标准库的 200 多个包,大约需要 10 秒。
编译器错误
如果任何源文件包含编译错误,则源代码视图将以红色突出显示错误位置。将鼠标悬停在上面会显示错误消息。
标识符解析
在源代码视图中,每个引用标识符都带有有关其引用的语言实体的信息:包、常量、变量、类型、函数或语句标签。将鼠标悬停在标识符上会显示实体的种类和类型(例如var x int
或func f func(int) string
)。
单击链接将带您到实体的定义。
类型信息:大小/对齐方式、方法集、接口
单击定义命名类型的标识符会导致一个面板出现,显示有关命名类型的信息,包括其大小和对齐方式(以字节为单位)、其方法集以及其实现关系:类型 T 的集合,这些类型可以分配给或来自此类型 U,其中 T 或 U 中至少有一个是接口。此示例显示了有关net/rpc.methodType
的信息。
方法集不仅包括类型的声明方法,还包括从结构体的匿名字段“提升”的任何方法,例如此示例中的sync.Mutex
。此外,接收器类型会显示为*T
或T
,具体取决于它是否需要接收器值的地址或副本。
方法集和实现关系也可以通过包视图获得。
指针分析功能
godoc -analysis=pointer
此外还执行精确的全程序指针分析。换句话说,它近似于每个引用可能引用的内存位置集——不仅是*T
类型的变量,还有[]T
、func
、map
、chan
和interface
。此信息揭示了每个动态调用(通过func
变量或接口方法)的可能目标,以及同一通道上发送和接收操作之间的关系。
与类型分析相比,指针分析需要更多的时间和内存,并且对于超过一百万行代码的代码库而言是不切实际的。
调用图导航
指针分析完成后,源代码视图会使用调用者和被调用者信息对代码进行注释:调用者信息与声明函数的func
关键字相关联,被调用者信息与函数调用的左括号'(
'相关联。
在此示例中,将鼠标悬停在rot13
函数(在strings/strings_test.go中定义)的声明上会显示它恰好在一个地方被调用。
单击链接将导航到唯一的调用者。(如果有多个调用者,则首先会显示一个选择列表。)
请注意,将鼠标悬停在此调用上会显示在此站点有 19 个可能的被调用者,其中我们的rot13
函数只是其中之一:这是通过func(rune) rune
类型变量进行的动态调用。单击调用会显示所有 19 个潜在被调用者的列表(显示为截断)。其中许多是匿名函数。
与基于类型的技术相比,指针分析提供了非常精确的调用图近似值。例如,下一个示例显示了testing
包中负责调用所有名为ExampleXYZ
的用户定义函数的动态调用。
回想一下,所有此类函数的类型都是func()
,即没有参数也没有结果。基于类型的近似值只能得出结论,即此调用可能会分派到与该类型匹配的任何函数——并且在大多数程序中这些函数非常多——但指针分析可以跟踪特定func
值在testing
包中的流向。作为其精度的指示,结果仅包含名称以Example
开头的函数。
包内调用图
包视图中以非常不同的方式呈现了相同的调用图信息。对于每个包,交互式树视图允许探索与该包相关的调用图;省略了来自其他包的所有函数。树的根是包的外部入口点:不仅是其导出的函数,还有从包外部(动态)调用的任何未导出或匿名函数。
此示例显示了path/filepath
包的入口点,并扩展了Glob
的调用图几个级别
请注意,Glob 和 Join 的节点出现了多次:树是循环图的部分展开;完全展开通常是无限的。
对于包视图中记录的每个函数,另一个交互式树视图允许探索从该函数开始的相同图。这是net/http.ListenAndServe
的内部图的一部分。
通道对等体(发送↔接收)
由于并发 Go 程序使用通道不仅传递值,还传递不同 goroutine 之间的控制权,因此在阅读 Go 代码时,自然会希望从通道发送导航到相应的接收,以便了解事件序列。
Godoc 为每个通道操作(创建、发送、范围、接收、关闭)添加了一个链接,该链接指向一个面板,其中显示有关可能与同一通道具有别名的其他操作的信息。
此示例来自net/http
的测试,显示了对chan bool
的发送操作。
单击<-
发送操作器会显示此通道在唯一位置(第 332 行)创建,并且有三个接收操作可能会读取此值。几乎不必指出某些通道元素类型被广泛使用(例如 struct{}、bool、int、interface{}),并且典型的 Go 程序可能包含数十个对chan bool
类型值的接收操作;然而,指针分析能够以比仅基于其类型更精细的精度区分通道上的操作。
另请注意,发送发生在与包含make
和接收操作的外部函数不同的(匿名)函数中。
这是另一个对不同chan bool
的发送示例,也在net/http
包中
分析仅找到一个可能从该通道接收的接收操作,在对此功能的测试中。
已知问题
所有分析结果都与一种配置(例如 amd64 linux)完全相关。基于不同平台或构建标记有条件编译的文件对分析不可见。
import "C"
的文件需要由 cgo 工具进行预处理。预处理后的文件偏移量与未预处理的文件不一致,因此标记未对齐。
文件不会定期重新分析。如果文件在运行的服务器下发生更改,则显示的标记将未对齐。
其他问题列在tools/godoc/analysis/README中。