我正在为
exhaustive
分析器解决这个问题。在枚举发现阶段,需要查找常量值。
Playground: https://play.golang.org/p/nZLmgE4rJZH
考虑以下 ConstDecl。它由 3 个 ConstSpec 组成,每个 ConstSpec 都有 2 个名称。(此示例使用 iota,但下面的方法通常适用于任何 ConstDecl。)
package example
const (
A, B = iota, iota * 100
_, D
E, F
)
使用 go/ast
和 go/types
(或 x/tools/go/packages
),我们可以获得代表上述 ConstDecl 的 *ast.GenDecl 和该包的 *types.Info。
var decl *ast.GenDecl
var info *types.Info
以下是关于
decl
的真实情况。
decl.Tok == token.CONST
为了获得常量值,我们可以进行以下操作:
func printValuesConst(decl *ast.GenDecl, info *types.Info) {
for _, s := range decl.Specs {
v := s.(*ast.ValueSpec)
for _, name := range v.Names {
c := info.ObjectOf(name).(*types.Const)
fmt.Println(name, c.Val().ExactString())
}
}
}
这将会按照预期输出:
A 0
B 0
_ 1
D 100
E 2
F 200
小贴士: 使用var
代替const
请注意,上面的代码适用于const
块; 对于var
块,我们将不得不使用v.Values[i]
来获取值(假设索引i
存在值)。
Playground: https://play.golang.org/p/f4mYjXvsvHB
decl.Tok == token.VAR
func printValuesVar(decl *ast.GenDecl, info *types.Info) {
for _, s := range decl.Specs {
v := s.(*ast.ValueSpec)
for i, name := range v.Names {
if len(v.Values) <= i {
fmt.Println(name, "(no AST value)")
continue
}
tv := info.Types[v.Values[i]]
if tv.Value == nil {
fmt.Println(name, "(not constant value)")
continue
}
fmt.Println(name, tv.Value.ExactString())
}
}
}
go/types
对 AST 进行类型检查,该包具有TypeAndValue
结构体,您可以使用它来获取常量的值。 - mkopriva