/
typeutil.go
75 lines (59 loc) · 1.66 KB
/
typeutil.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package genutil
import (
"go/types"
"github.com/go-faster/errors"
"golang.org/x/tools/go/packages"
)
// Implementations finds iface implementations.
func Implementations(pkg *packages.Package, iface *types.Interface) []*types.Named {
var r []*types.Named
for _, def := range pkg.TypesInfo.Defs {
if def == nil || !def.Exported() {
continue
}
named, ok := def.Type().(*types.Named)
if !ok {
continue
}
if !types.Implements(types.NewPointer(named), iface) {
continue
}
r = append(r, named)
}
return r
}
// Interfaces is a simple utility struct to find interfaces and implementations.
type Interfaces struct {
pkg *packages.Package
implsCache map[string][]*types.Named
}
// NewInterfaces creates new Interfaces structure.
func NewInterfaces(pkg *packages.Package) *Interfaces {
return &Interfaces{pkg: pkg, implsCache: map[string][]*types.Named{}}
}
// Interface finds interface by name.
func (c *Interfaces) Interface(name string) (*types.Interface, error) {
obj := c.pkg.Types.Scope().Lookup(name)
if obj == nil {
return nil, errors.Errorf("%q not found", name)
}
v, ok := obj.Type().Underlying().(*types.Interface)
if !ok {
return nil, errors.Errorf("%q has unexpected kind type %T", name, obj.Type().Underlying())
}
return v, nil
}
// Implementations finds interface implementations by interface name.
func (c *Interfaces) Implementations(name string) ([]*types.Named, error) {
impls, ok := c.implsCache[name]
if ok {
return impls, nil
}
iface, err := c.Interface(name)
if err != nil {
return nil, errors.Wrapf(err, "find %q", name)
}
impls = Implementations(c.pkg, iface)
c.implsCache[name] = impls
return impls, nil
}