首页 > 解决方案 > 使用 x/tools/go/packages 查找嵌入另一种类型的类型

问题描述

假设我有以下代码,

package w

import "log"

type Z struct {
    log.Logger
}

type Y struct {} // skip this type

我想以编程方式查找类型Z,由它嵌入的事实标识log.Logger,如果不是,我想跳过声明,使用https://pkg.go.dev/golang.org/x/tools/go/packages

目前我已经能够编写以下代码,

package main

import (
    "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可以加载类型推断,搜索每个包的类型并检查接口兼容性;在我看来,我想要类似的东西。

鉴于它们简单地嵌入了特定类型,如何使用新的 packages 包来查找类型?

标签: goabstract-syntax-tree

解决方案


包装内有一个Var.Embeddedgo/types

您可以使用 usegolang.org/x/tools/go/loader来加载包并获取类型信息,然后使用Type.Underlyingand interface cast 来过滤掉结构类型,然后使用 iterate overStruct.Filed来获取Var字段,并检查Var.


推荐阅读