确定动态指定的Go变量是否具有特定类型

时间:2016-05-17 15:06:31

标签: go

我正在编写一个命令行应用程序,其中用户指定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

1 个答案:

答案 0 :(得分:1)

做了一些追踪并找到了方法,希望得到帮助

https://play.golang.org/p/f4XF8K_FbL

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)
    }
}