-
Notifications
You must be signed in to change notification settings - Fork 0
/
check_signatures.go
138 lines (127 loc) · 3.95 KB
/
check_signatures.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
129
130
131
132
133
134
135
136
137
138
package main
import "bytes"
import "fmt"
import "defimpl/util"
import "go/ast"
import "go/parser"
import "go/token"
import "go/types"
import "text/template"
type fakeInterfaceDefinition struct {
Package string
}
// CheckSignatures is used in the NewVerbPhrase method of each verb to
// make sure that a method Field's FuncType in an interface matches
// that expected by the method definition that will be generated by
// the template returned by the VerbDefinition's MethodTemplate
// method.
//
// That template is applied to a special parameter that substitutes
// specific "variable names" into the template.
//
// Because the template substitution expressions might include other
// operations on the value of a template parameter, MatchVar should be
// used in the substitutions. Identity methods can be defined on
// MatchVar for any operations that the template might want to
// perform.
//
// CheckSignatures then executes the template and parses the result to
// serve as the pattern argument of AstMatch.
func CheckSignatures(ctx *context, vd VerbDefinition, pkg string, field *ast.Field, tmpl *template.Template) (
types.Type, error, map[string]interface{}) {
if len(field.Names) != 1 {
return nil, nil, nil
}
// This needs to match the definition of (*baseVerbPhrase).MethodName:
field_name := field.Names[0].Name
w := &bytes.Buffer{}
fmt.Fprintf(w, "package %s\n\n", pkg)
if err := tmpl.Execute(w, &CheckSignaturesVerbPhraseSurrogate{
MethodName: MatchVar(field_name),
InterfaceName: MatchVar("IGNORE"),
StructName: MatchVar("IGNORE"),
DelegateTo: MatchVar("IGNORE"),
SlotName: MatchVar("IGNORE"),
SlotType: MatchVar("_SLOT_TYPE"),
MethodParameters: MatchVar("__PARAMETERS"),
ParameterNames: MatchVar("IGNORE"),
MethodResults: MatchVar("__RESULTS"),
InterfaceDefinition: fakeInterfaceDefinition {
Package: "",
},
}); err != nil {
return nil, err, nil
}
fset := token.NewFileSet()
parsed, err := parser.ParseFile(fset, "defimpl/CheckSignatures:pattern", w.String(), parser.ParseComments)
if err != nil {
return nil, fmt.Errorf("%s for pattern:\n%s", err, w.String()), nil
}
var fd *ast.FuncDecl
// Find the method definition from the parsed template result
// that has the same name as field:
for _, decl := range parsed.Decls {
fd1, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
if fd1.Name == nil {
continue
}
if fd1.Name.Name == field_name {
fd = fd1
break
}
}
if fd == nil {
return nil, fmt.Errorf("verb MethodTemplate for %q should have exactly one FuncDecl",
vd.Tag()), nil
}
scratchpad := map[string]interface{}{}
// Match the method definition with the Field of the
// interface, extracting the data type:
matched, err := util.AstMatch(fd.Type, field.Type, scratchpad)
if err != nil {
return nil, fmt.Errorf("defimpl: %s: %s",
ctx.fset.Position(field.Comment.List[0].Slash).String(),
err), scratchpad
}
if !matched {
return nil, fmt.Errorf("Method signature inappropriate for verb %q",
vd.Tag()), scratchpad
}
if t, ok := scratchpad["_SLOT_TYPE"]; ok {
return ctx.info.Types[t.(ast.Expr)].Type, nil, scratchpad
} else {
return nil, nil, scratchpad
}
}
// CheckSignaturesVerbPhraseSurrogate should present the same
// "interface" to a Template as GlobalsTemplateParameter does. I
// don't think Go provides a way to assert this.
type CheckSignaturesVerbPhraseSurrogate struct {
MethodName MatchVar
InterfaceName MatchVar
StructName MatchVar
DelegateTo MatchVar
SlotName MatchVar
SlotType MatchVar
MethodParameters MatchVar
ParameterNames MatchVar
MethodResults MatchVar
InterfaceDefinition fakeInterfaceDefinition
}
// MatchVar is described in the comment for CheckSignatures.
type MatchVar string
func (v MatchVar) Elem() MatchVar {
return v
}
func (v MatchVar) Tag() MatchVar {
return v
}
func (_ CheckSignaturesVerbPhraseSurrogate) TypeString(v MatchVar) MatchVar {
return v
}
func (_ CheckSignaturesVerbPhraseSurrogate) Verb() MatchVar {
return MatchVar("SURROGATE")
}