我正在编写一个命令行应用程序,其中用户指定1)包含Go文件的目录,2)应该是http.Handler的变量的名称。
go run cli.go /path/to/a/go/library MyCustomHandler我在试着
http.Handler我可以做前两个没有问题-我调用parser.ParseDir,然后得到我想要的包作为一个*ast.Package,然后循环它如下:
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
gd, ok := decl.(*ast.GenDecl)
if !ok || gd.Tok != token.VAR {
continue
}
if len(gd.Specs) != 1 {
continue
}
spec0 := gd.Specs[0]
vs, ok := spec0.(*ast.ValueSpec)
if !ok {
continue
}
if len(vs.Names) != 1 {
continue
}
ident := vs.Names[0]
if ident.Name != handlerName {
continue
}
// ...
}
}
}问题在于此时ValueSpec.Type为零,而且似乎没有任何方法来确定这是否是http.Handler。
go/types包有更多用于检查类型的工具,但它似乎还需要做更多的设置工作才能实现这一点,本质上是解析和检查整个程序。我是否需要沿着这条路走下去,还是有更简单的方法,只使用ast包,还是以某种方式使用go build?
发布于 2016-05-18 03:17:39
做了些追踪,找到了路,希望能帮上忙
package main
import (
"go/parser"
"go/token"
"os"
"go/ast"
"log"
"net/http"
//"reflect"
)
func MyCustomHandler(w http.ResponseWriter, r* http.Request){
}
func findHttpHandler(pkg *ast.Package, handlerName string) (*ast.FuncDecl, error) {
for _, file := range pkg.Files {
for _, decl := range file.Decls {
fd, ok := decl.(*ast.FuncDecl)
if !ok || fd == nil{
continue
}
if fd.Name.Name != handlerName{
continue
}
if len(fd.Type.Params.List) == 2 {
p1 := fd.Type.Params.List[0]
p2 := fd.Type.Params.List[1]
exp, ok := p1.Type.(*ast.SelectorExpr)
if !ok{
break;
}
ident, ok := exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "ResponseWriter"{
break;
}
exp2, ok := p2.Type.(*ast.StarExpr)
if !ok{
break;
}
exp = exp2.X.(*ast.SelectorExpr)
ident, ok = exp.X.(*ast.Ident)
if !ok{
break
}
if ident.Name!="http" || exp.Sel.Name != "Request"{
break;
}
return fd, nil
}
}
}
return nil, nil
}
func main() {
fs := token.NewFileSet()
pkgs, err := parser.ParseDir(fs, os.Args[1], nil, parser.Trace)
if err != nil{
log.Fatalln(err)
}
for _,pkg:=range pkgs{
d, _ := findHttpHandler(pkg, "MyCustomHandler");
log.Println(d)
}
}https://stackoverflow.com/questions/37279890
复制相似问题