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

确定动态指定的Go变量是否具有特定类型,go,Go,我正在编写一个命令行应用程序,其中用户指定1)一个包含Go文件的目录,2)一个变量的名称,该变量应该是http.Handler,例如 go run cli.go /path/to/a/go/library MyCustomHandler 我正在努力 解析文件 查找具有给定名称的变量 验证它是一个http.Handler 我可以毫无问题地完成前两个任务-我调用parser.ParseDir,然后将我想要的包作为*ast.package,然后像这样循环: func findHttpHandle

我正在编写一个命令行应用程序,其中用户指定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
为nil,并且似乎没有任何方法可以确定这是否是
http.Handler


go/types
软件包有更多的检查类型的工具,但是看起来你还需要做更多的设置工作才能做到这一点,基本上就是对整个程序进行解析和类型检查。我是否需要沿着这条路走下去,或者是否有更简单的方法,只是使用
ast
包,或者以某种方式使用
go build

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

嗨,凯文!:)这看起来很接近这个问题:我希望它有帮助。
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)
    }
}