为了账号安全,请及时绑定邮箱和手机立即绑定

查找使用 x/tools/go/packages 嵌入另一种类型的类型

查找使用 x/tools/go/packages 嵌入另一种类型的类型

Go
梵蒂冈之花 2022-08-01 10:38:27
假设我有下面的代码,package wimport "log"type Z struct {    log.Logger}type Y struct {} // skip this type我想以编程方式查找类型,由它嵌入的事实标识,如果它没有,我想跳过声明,使用 https://pkg.go.dev/golang.org/x/tools/go/packagesZlog.Logger目前我已经能够编写以下代码,package mainimport (    "fmt"    "go/ast"    "go/token"    "os"    "path/filepath"    "golang.org/x/tools/go/packages")func main() {    inputFile := os.Getenv("GOFILE")    cwd, err := os.Getwd()    if err != nil {        panic(err)    }    fmt.Println(inputFile)    fmt.Println(cwd)    cfg := &packages.Config{        // Mode:       packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.LoadTypes,        Mode:       packages.NeedSyntax | packages.LoadTypes,        BuildFlags: []string{"-tags=sqlg"},    }    pkgs, err := packages.Load(cfg, cwd)    if err != nil {        fmt.Fprintf(os.Stderr, "load: %v\n", err)        os.Exit(1)    }    if packages.PrintErrors(pkgs) > 0 {        os.Exit(1)    }    for _, pkg := range pkgs {        fmt.Println(pkg.ID, pkg.GoFiles)        for _, s := range pkg.Syntax {            file := pkg.Fset.File(s.Pos())            if filepath.Base(file.Name()) != inputFile {                continue            }            for _, d := range s.Decls {                switch x := d.(type) {                case *ast.GenDecl:                    if x.Tok == token.TYPE {                        fmt.Printf("%#v\n", x.Specs)                    }                case *ast.FuncDecl:                }            }        }    }}但我不太喜欢它,因为我正在挖掘语法树,寻找GenDecl,然后是Specs等以前,使用包加载器可以加载类型推断,搜索每个包的类型并检查接口兼容性;它对我来说很满意,我想要类似的东西。https://godoc.org/golang.org/x/tools/go/loader如何使用新的包包来查找类型,因为它们嵌入了简单的特定类型?
查看完整描述

2 回答

?
慕勒3428872

TA贡献1848条经验 获得超6个赞

有一个在go/types包。Var.Embedded

可以使用 use 加载包并获取类型信息,然后使用 和 接口强制转换为筛选出结构类型,然后使用迭代来获取字段,并选中 .golang.org/x/tools/go/loaderType.UnderlyingStruct.FiledVarVar


查看完整回答
反对 回复 2022-08-01
?
智慧大石

TA贡献1946条经验 获得超3个赞

多亏了叶子bebop的答案,我能够写一个更短的程序,原始帖子没有显示,但在v1中,我有8个级别的缩进来查找结构的嵌入属性。

这受到 https://pkg.go.dev/golang.org/x/tools@v0.0.0-20201226215659-b1c90890d22a/go/packages/gopackages 提供的演示的启发

package main


import (

    "fmt"

    "go/types"

    "os"

    "strings"


    "golang.org/x/tools/go/packages"

)


func main() {

    inputFile := os.Getenv("GOFILE")

    cwd, err := os.Getwd()

    if err != nil {

        panic(err)

    }

    fmt.Println(inputFile)

    fmt.Println(cwd)


    cfg := &packages.Config{

        // Mode:       packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.LoadTypes,

        Mode:       packages.NeedSyntax | packages.LoadTypes,

    }

    pkgs, err := packages.Load(cfg, cwd)

    if err != nil {

        fmt.Fprintf(os.Stderr, "load: %v\n", err)

        os.Exit(1)

    }

    if packages.PrintErrors(pkgs) > 0 {

        os.Exit(1)

    }


    for _, pkg := range pkgs {

        // qual := types.RelativeTo(pkg.Types)

        scope := pkg.Types.Scope()

        for _, name := range scope.Names() {

            obj := scope.Lookup(name)

            if obj != nil && obj.Type() != nil && obj.Type().Underlying() != nil {

                // obj is types.Named, 

                // obj.Type() is types.TypeName

                // obj.Type().Underlying() exposes the types.Struct which 

                // gives access to the desired types.Var.Embedded()

                st, ok := obj.Type().Underlying().(*types.Struct)

                if !ok {

                    continue

                }

                for i := 0; i < st.NumFields(); i++ {

                    f := st.Field(i)

                    if f.Embedded() && strings.HasSuffix(f.Type().String(), "log.Logger") {

                        fmt.Printf("%#v\n", f)

                    }

                }

            }

        }

    }

}

我相信它存在一个使用访问者模式的较短版本,但我还没有找到它。


在这个版本中,我仍然依靠字符串比较来类型检查属性,这是以后使用类型推断可以改进的东西。


查看完整回答
反对 回复 2022-08-01
  • 2 回答
  • 0 关注
  • 142 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信