英文:
Finding the type of a variable through static analysis?
问题
如何通过静态分析确定变量的类型?
假设我有以下代码:
func doSomething(x interface{}) {}
func main() {
p := Person()
doSomething(p)
}
我想分析doSomething(person)
,是否可以通过静态分析获取Person的类型?
如果有多层赋值会怎样?
p1 := Person()
p2 := p1
doSomething(p2)
或者
parent := Parent()
p := Parent.Child() // 类型为Person
doSomething(p)
使用情况是,我有一个通用函数在整个(非常大的)代码库中经常使用,并且希望引入一个新的类型安全版本的该函数。为了做到这一点,我希望自动确定函数的“类型”,并相应地进行重构:
// 旧的
DB.InsertRow(person)
// 新的
Person.InsertRow(person)
英文:
How can I determine the type of a variable through static analysis?
Suppose I have the following code:
func doSomething(x interface{}) {}
func main() {
p := Person()
doSomething(p)
}
And I want to analyze doSomething(person)
, is it possible to get the type of Person through static analysis?
What if there were multiple levels of assignment?
p1 := Person()
p2 := p1
doSomething(p2)
or
parent := Parent()
p := Parent.Child() // type Person
doSomething(p)
The use case is that I have a generic function that is commonly used throughout the (very large) codebase, and would like to introduce a new type safe version of this function. To do this, I hope to automatically determine the "type" of the function and refactor it accordingly:
// old
DB.InsertRow(person)
// new
Person.InsertRow(person)
答案1
得分: 1
通过静态分析找到表达式的类型是一项复杂的任务,有时甚至是不可能的,具体请参考https://stackoverflow.com/questions/27976493/golang-static-identifier-resolution。
使用案例是我有一个在整个(非常庞大)代码库中常用的通用函数,并且希望引入一个新的类型安全版本的该函数。为了做到这一点,我希望自动确定函数的“类型”并相应地进行重构:
// 旧的
DB.InsertRow(person)// 新的
Person.InsertRow(person)
仅出于重构的目的,我认为实现它并不值得麻烦。
你可以临时更改DB.InsertRow()
的签名,只接受特定类型,例如int
或你确定在任何地方都没有使用的自定义类型(例如type tempFoo struct{}
)。
这样做的目的是什么?这样一来,编译器将为你完成艰巨的工作。你将看到错误消息,显示代码库试图传递给DB.InsertRow()
的确切类型,所以我认为任务完成了。
例如,以下代码可以编译通过:
func doSomething(x interface{}) {}
func main() {
doSomething(image.Pt(1, 2))
doSomething("abc")
doSomething(image.Rect) // image.Rect是一个我们没有调用的函数,
// 所以我们在这里传递了一个函数类型的值
}
如果我们更改doSomething()
:
func doSomething(x int) {}
我们将从编译器得到我们正在寻找的类型:
./prog.go:10:14: 无法将类型为 image.Point 的 image.Pt(1, 2) 作为 int 类型的参数传递给 doSomething
./prog.go:11:14: 无法将无类型的 字符串常量 "abc" 作为 int 值的参数传递给 doSomething
./prog.go:12:14: 无法将类型为 func(x0 int, y0 int, x1 int, y1 int) image.Rectangle 的 image.Rect 作为 int 类型的参数传递给 doSomething
英文:
Finding the type of an expression through static analysis is non-trivial, and sometimes not possible, for details see https://stackoverflow.com/questions/27976493/golang-static-identifier-resolution.
> The use case is that I have a generic function that is commonly used throughout the (very large) codebase, and would like to introduce a new type safe version of this function. To do this, I hope to automatically determine the "type" of the function and refactor it accordingly:
>
> // old
> DB.InsertRow(person)
>
> // new
> Person.InsertRow(person)
Just for refactoring purposes, I don't think it is worth the hassle to implement it.
What you may do is change the signature of DB.InsertRow()
temporarily to accept only a specific type such as int
or your custom type you're sure is not used anywhere (e.g. type tempFoo struct{}
).
To what end? Doing so, the compiler will do the hard work for you. You will see error messages showing exactly the types your codebase is trying to pass to DB.InsertRow()
, so I'd say mission accomplished.
For example this code compiles:
func doSomething(x interface{}) {}
func main() {
doSomething(image.Pt(1, 2))
doSomething("abc")
doSomething(image.Rect) // image.Rect is a function which we don't call,
// so we're passing a value of a function type here
}
If we change doSomething()
:
func doSomething(x int) {}
We get the types we're seeking for from the compiler:
> ./prog.go:10:14: cannot use image.Pt(1, 2) (value of type image.Point) as type int in argument to doSomething
>
> ./prog.go:11:14: cannot use "abc" (untyped string constant) as int value in argument to doSomething
>
> ./prog.go:12:14: cannot use image.Rect (value of type func(x0 int, y0 int, x1 int, y1 int) image.Rectangle) as type int in argument to doSomething
答案2
得分: 1
使用来自https://stackoverflow.com/questions/27976493/golang-static-identifier-resolution的建议,使用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
}
// 检查方法名
if funcExpr.Sel.Name != "doSomething" {
return true
}
for _, arg := range callExpr.Args {
// 查找参数的类型
argType := pass.TypesInfo.Types[arg].Type
if argType.String() == "*rewriter.Person" {
// 在这里进行你想要的操作
}
}
return false
})
return nil, nil
}
可以根据需要扩展此代码,查看方法的接收器并添加重构逻辑(使用analysis.Diagnostic)。
英文:
Using the advice from https://stackoverflow.com/questions/27976493/golang-static-identifier-resolution to use golang.org/x/tools/go/types
, I found that this was pretty straight forward to do with the golang.org/x/tools/go/analysis
package, which has the types info available alongside the parsed ast.
This was my solution:
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
}
One can augment this to look at the receiver of the method and add refactoring logic as needed (using analysis.Diagnostic).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论