Skip to content

Goで静的解析する際のテンプレート(標準版)

Goのソースコードを静的解析する際の、型チェックを実施するまでのテンプレートは次のようになる。

package main
import (
"encoding/json"
"flag"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
"os"
"golang.org/x/exp/maps"
)
func main() {
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, ".", nil, 0)
if err != nil {
log.Fatal(err)
}
c := types.Config{
Importer: importer.ForCompiler(fset, "source", nil),
}
for name, p := range pkgs {
// このnameはインポートパスではなくパッケージ名
// なのでmath/rand/v2だったらname=rand
pkg, err := c.Check(".", fset, maps.Values(p.Files), nil)
if err != nil {
log.Fatal(err)
}
}
}

インターネットのコード例は Importer には importer.Default を渡している場合が多いけれど、ここで importer.ForCompiler を使っているのはgo/typesのCheckでcannot find packageエラーになることがあるため。

Go 1.22でgo/ast.Packageなどいくつか非推奨になって、go/typesを使うように案内されているので、なんらかのシンボル情報を扱うときはgo/typesが必要となる。実際にgo/astだけで解析してみると、base64.StdEncodinghttp.Client もどちらも ast.SelectorExpr でしかないし、同様に call()(*http.Client)(nil)ast.CallExpr となっているように、意味の情報が全く与えられないので素朴な構文を扱う用途を外れると使いづらい。

他にも、go/* 以下のパッケージは全体的に扱いがとても面倒なので、今ならGoで静的解析する際のテンプレート(loader版)を使うほうが良いと思う。