1

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

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 添加缺失的导入一样。

4

2 回答 2

1

您可以使用reflect.TypeOf(any).PkgPath()获取特定类型的包路径。但是,我们需要传递具有所需类型的对象(而不是您想要的字符串)。

package main

import (
    "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()
}
于 2019-12-18T13:55:50.893 回答
0

下面的程序列表usesdefinitions给定的查询type和给定的 go 包。

使用程序加载器包以编程方式加载 go 程序简单明了

package main

import (
    "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" io
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:170:6 io.ReaderFrom
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:77:6 io.Reader
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:211:6 io.ReaderAt
use: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/multi.go:20:13 []io.Reader
use: /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 io
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:170:6 io.ReaderFrom
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:211:6 io.ReaderAt
def: /home/mh-cbon/.gvm/gos/go1.12.7/src/io/io.go:77:6 io.Reader

您可能需要改进匹配器引擎以使其更适合。

于 2019-12-18T20:31:28.333 回答