1

I'm writing a command line application where a user specifies 1) a directory containing Go files, 2) the name of a variable that should be a http.Handler, e.g.

go run cli.go /path/to/a/go/library MyCustomHandler

I'm trying to

  • parse the files
  • find the variable with the given name
  • verify that it's a http.Handler

I can do the first two no problem - I call parser.ParseDir, then get the package I want as an *ast.Package, then loop over it like this:

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
            }
            // ...
        }
    }
}

The problem is at this point the ValueSpec.Type is nil, and there doesn't appear to be any way to figure out whether this is a http.Handler or not.

The go/types package has more tools for checking types, but it also looks like you need to do a lot more setup work to get this, essentially parsing and type checking the whole program. Am I going to need to go down this path, or is there an easier way, just using the ast package, or using go build somehow?

1

1 Answer 1

2

did some trace and find the way, hoping help

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)
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.