通过静态分析找到变量的类型?

the*_*ses 5 types static-analysis go

如何通过静态分析判断变量的类型?

假设我有以下代码:

func doSomething(x interface{}) {}

func main() {
  p := Person()
  doSomething(p)
}
Run Code Online (Sandbox Code Playgroud)

而我想分析一下doSomething(person),通过静态分析是否可以得到Person的类型?

如果有多个级别的分配怎么办?

p1 := Person()
p2 := p1
doSomething(p2)
Run Code Online (Sandbox Code Playgroud)

或者

parent := Parent()
p := Parent.Child() // type Person
doSomething(p)
Run Code Online (Sandbox Code Playgroud)

用例是我有一个在(非常大的)代码库中常用的通用函数,并且想引入该函数的新类型安全版本。为此,我希望自动确定函数的“类型”并相应地重构它:

// old
DB.InsertRow(person)

// new
Person.InsertRow(person)
Run Code Online (Sandbox Code Playgroud)

the*_*ses 1

使用Golang 静态标识符解析to use的建议golang.org/x/tools/go/types,我发现这对于该包来说非常简单golang.org/x/tools/go/analysis,它具有可用的类型信息以及解析的 ast

这是我的解决方案:

package rewriter

import (
    "go/ast"

    "golang.org/x/tools/go/analysis"

    "golang.org/x/tools/go/analysis/passes/inspect"

    "golang.org/x/tools/go/ast/inspector"
)

func run(pass *analysis.Pass) (interface{}, error) {
    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)

    nodeFilter := []ast.Node{
        (*ast.CallExpr)(nil),
    }

    inspect.Nodes(nodeFilter, func(node ast.Node, push bool) bool {
        callExpr, ok := node.(*ast.CallExpr)
        if !ok {
            return true
        }

        funcExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
        if !ok {
            return true
        }

        // check method name
        if funcExpr.Sel.Name != "doSomething" {
            return true
        }

        for _, arg := range callExpr.Args {
            // lookup type of the arg
            argType := pass.TypesInfo.Types[arg].Type
            if argType.String() == "*rewriter.Person" {
                // do whatever you want here
            }
        }
        return false
    })
    return nil, nil
}
Run Code Online (Sandbox Code Playgroud)

人们可以对此进行扩展以查看方法的接收者并根据需要添加重构逻辑(使用analysis.Diagnostic)。