Рабочий пример, основанный на ответе Thundercat. guru
потребуется сделать что-то подобное, чтобы обеспечить поддержку поиска подходящих функций для передачи (например, реализации http.HandleFunc
).
package main
import (
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"go/types"
"log"
)
const hello = `package main
import "fmt"
const x = 1;
func main() {
fmt.Println("Hello, world")
}
// Printer defines a function that prints a string.
type Printer func(s string)
func consolePrinter(s string) {
fmt.Println(s)
}
`
func main() {
fset := token.NewFileSet()
// Parse the input string, []byte, or io.Reader,
// recording position information in fset.
// ParseFile returns an *ast.File, a syntax tree.
f, err := parser.ParseFile(fset, "hello.go", hello, 0)
if err != nil {
log.Fatal(err) // parse error
}
// A Config controls various options of the type checker.
// The defaults work fine except for one setting:
// we must specify how to deal with imports.
conf := types.Config{Importer: importer.Default()}
// Type-check the package containing only file f.
// Check returns a *types.Package.
pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
if err != nil {
log.Fatal(err) // type error
}
names, signatures := getFunctionTypes(pkg)
for i, name := range names {
fmt.Println("Functions which implement", name)
for _, implementor := range getFunctionsWhichImplement(signatures[i], pkg) {
fmt.Println(implementor)
}
}
}
func getFunctionTypes(pkg *types.Package) (names []string, signatures []*types.Signature) {
for _, name := range pkg.Scope().Names() {
o := pkg.Scope().Lookup(name)
if _, isType := o.(*types.TypeName); !isType {
continue
}
var sig *types.Signature
var isFunc bool
if sig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
continue
}
signatures = append(signatures, sig)
names = append(names, name)
}
return
}
func getFunctionsWhichImplement(sig *types.Signature, pkg *types.Package) (fns []types.Object) {
for _, name := range pkg.Scope().Names() {
o := pkg.Scope().Lookup(name)
if _, isType := o.(*types.TypeName); isType {
continue
}
var csig *types.Signature
var isFunc bool
if csig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
continue
}
if types.AssignableTo(sig, csig) {
fns = append(fns, o)
}
}
return
}
Вывод этого кода показан ниже:
Functions which implement Printer
func cmd/hello.consolePrinter(s string)