-
Notifications
You must be signed in to change notification settings - Fork 0
/
interface.go
129 lines (113 loc) · 3.34 KB
/
interface.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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package main
import "defimpl/util"
import "fmt"
import "go/ast"
import "strings"
type InterfaceDefinition struct {
File *File
IsAbstract bool
InterfaceType *ast.InterfaceType
InterfaceName string
VerbPhrases []VerbPhrase
Inherited []*IDKey // Interfaces that are included by this one
AllInherited []*InterfaceDefinition // Transitive closure of all inherited interfaces.
}
func (idef *InterfaceDefinition) QualifiedName() string {
return util.ImplName(idef.Package(), idef.InterfaceName)
}
// StructName returns the default name for the implementing struct for
// the interface represented by this InterfaceDefinition.
func (idef *InterfaceDefinition) StructName() string {
return idef.InterfaceName + "Impl"
}
// DefinesStruct returns true if an implementing sruct should be
// defined for the interface represented by this InterfaceDefinition.
func (idef *InterfaceDefinition) DefinesStruct() bool {
return len(idef.VerbPhrases) > 0 && !idef.IsAbstract
}
func (idef *InterfaceDefinition) Fields() []*ast.Field {
return util.FieldListSlice(idef.InterfaceType.Methods)
}
func (idef *InterfaceDefinition) Package() string {
return idef.File.Package
}
const InterfaceIsAbstractMarker string = "(ABSTRACT)"
// isAbstractInterface returns true if the declaration -- which should
// define an interface -- has a comment with the "abstract" token.
func isAbstractInterface(x *ast.GenDecl) bool {
var hasAbstract = func(cmnt *ast.CommentGroup) bool {
if cmnt == nil || cmnt.List == nil {
return false
}
for _, c := range cmnt.List {
if strings.Contains(c.Text, InterfaceIsAbstractMarker) {
return true
}
}
return false
}
return hasAbstract(x.Doc)
}
// NewInterface returns a new InterfaceDefinition if decl represents
// an interface definition, otherwise it returns nil.
func NewInterface(ctx *context, file *File, decl ast.Decl) *InterfaceDefinition {
gd, ok := decl.(*ast.GenDecl)
if !ok {
return nil
}
spec, ok := gd.Specs[0].(*ast.TypeSpec)
if !ok {
return nil
}
it, ok := spec.Type.(*ast.InterfaceType)
if !ok {
return nil
}
if len(gd.Specs) > 1 {
// Apparently I have insufficient understanding of what interface type specs look like.
panic(fmt.Sprintf("type definition of an interface type has more than one Spec: %s\n",
ctx.fset.Position(gd.TokPos).String()))
}
id := &InterfaceDefinition{
File: file,
// It appears that the parser associates the comment group with
// the outer GenDecl rather than with the TypeSpec.
IsAbstract: isAbstractInterface(gd),
InterfaceType: it,
InterfaceName: spec.Name.Name,
Inherited: []*IDKey{},
}
for _, m := range id.Fields() {
GetVerbPhrase(ctx, id, m)
}
return id
}
// TypePackage returns the package name if the Expr (which should
// identify a type) specifies one.
func TypePackage(t ast.Expr) string {
if t == nil {
return ""
}
var tp func(ast.Expr, bool) string
tp = func(t ast.Expr, top bool) string {
switch e := t.(type) {
case *ast.Ident:
if top {
return ""
}
return e.Name
case *ast.SelectorExpr:
return tp(e.X, false)
case *ast.ArrayType:
return tp(e.Elt, true)
case *ast.StarExpr:
return tp(e.X, true)
case *ast.FuncType:
// Unnamed function, so no package.
return ""
default:
panic(fmt.Sprintf("TypePackage: unsupported expression type %T", t))
}
}
return tp(t, true)
}