猿问

如何以编程方式检索声明给定类型的包路径?

我正在寻找一种方法来检索本地安装的包,其中包含给定类型的声明和默认包名称。


IE:


// FindPackagesForType returns the list of possible packages for a given type

func FindPackagesForType(typeName string) []string {

    return []string {} // TODO: implement

}


func TestFindPackagesForType(t *testing.T) {

    assert.Contains(t, FindPackagesForType("io.Reader"), "io")

    assert.Contains(

        t,

        FindPackagesForType("types.Timestamp"),

        "github.com/gogo/protobuf/types",

    )

    assert.Contains(

        t,

        FindPackagesForType("types.ContainerCreateConfig"),

        "github.com/docker/docker/api/types",

    )

}

我可以尝试检索所有已安装的包,并在每个查找声明时通过 AST,但如果有一个解决方案可以更有效地做到这一点,同时还提供对 go 模块的支持,我想使用它。


这样做的原因是为了改进代码生成工具。这个想法是让用户提供类型的名称,并让工具识别最有可能的候选者,就像 goimports 添加缺失的导入一样。


慕盖茨4494581
浏览 103回答 2
2回答

天涯尽头无女友

您可以使用reflect.TypeOf(any).PkgPath()获取特定类型的包路径。但是,我们需要传递具有所需类型的对象(而不是您想要的字符串)。package mainimport (    "bytes"    "fmt"    "reflect"    "gopkg.in/mgo.v2/bson")func main() {    var a bytes.Buffer    fmt.Println(FindPackagesForType(a)) // output: bytes    var b bson.M    fmt.Println(FindPackagesForType(b)) // output: gopkg.in/mgo.v2/bson}func FindPackagesForType(any interface{}) string {    return reflect.TypeOf(any).PkgPath()}

慕的地10843

下面的程序列表uses和definitions给定的查询type和给定的 go 包。使用程序加载器包以编程方式加载 go 程序简单明了package mainimport (    "flag"    "fmt"    "strings"    "golang.org/x/tools/go/loader")func main() {    var query string    var uses bool    var defs bool    flag.StringVar(&query, "query", "", "the fully qualified type path")    flag.BoolVar(&uses, "uses", true, "capture uses")    flag.BoolVar(&defs, "definitions", true, "capture definitions")    flag.Parse()    if query == "" {        panic("query must not be empty")    }    var queryPkg string    queryType := query    if i := strings.LastIndex(query, "."); i > -1 {        queryPkg = query[:i]        queryType = query[i+1:]    }    var conf loader.Config    _, err := conf.FromArgs(flag.Args(), false)    if err != nil {        panic(err)    }    prog, err := conf.Load()    if err != nil {        panic(err)    }    for pkgType, pkgInfo := range prog.AllPackages {        if queryPkg != "" {            if !strings.HasPrefix(pkgType.Path(), queryPkg) {                continue            }        }        if defs {            for typeInfo, ident := range pkgInfo.Defs {                if !strings.HasPrefix(typeInfo.Name, queryType) {                    continue                }                f := prog.Fset.File(ident.Pos())                fpos := f.Position(ident.Pos())                fmt.Printf("def: %v %v.%v\n", fpos, pkgType.Path(), typeInfo.Name)            }        }        if uses {            for ident, oInfo := range pkgInfo.Uses {                if !strings.Contains(oInfo.Type().String(), queryType) {                    continue                }                f := prog.Fset.File(ident.Pos())                fpos := f.Position(ident.Pos())                fmt.Printf("use: %v %v\n", fpos, oInfo.Type().String())            }        }        // -    }}然后你像这样运行它$ go run main.go -query="io.Reader" iodef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:170:6 io.ReaderFromdef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:77:6 io.Readerdef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:211:6 io.ReaderAtuse: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/multi.go:20:13 []io.Readeruse: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/multi.go:21:16 *io.multiReader# a ton of output...[mh-cbon@Host-001 ploader] $ go run main.go -query="Config" io[mh-cbon@Host-001 ploader] $ go run main.go -query="io.Reader" -uses=false iodef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:170:6 io.ReaderFromdef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:211:6 io.ReaderAtdef: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:77:6 io.Reader您可能需要改进匹配器引擎以使其更适合。
随时随地看视频慕课网APP

相关分类

Go
我要回答