-
Notifications
You must be signed in to change notification settings - Fork 0
/
objects.go
255 lines (222 loc) · 7.38 KB
/
objects.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
package aliaser
import (
"bytes"
"go/types"
"strings"
"github.com/marcozac/go-aliaser/importer"
"github.com/marcozac/go-aliaser/util/sequence"
)
var _ Object = (*Const)(nil)
// Const is the type used to represent a constant in the loaded package.
// It contains the original constant and the resolver used to generate the
// aliases.
type Const struct {
*types.Const
objectResolver
}
// NewConst returns a new [Const] with the given constant. The importer is used
// to add the constant package to the list of imports.
func NewConst(c *types.Const, imp *importer.Importer) *Const {
return &Const{c, newObjectResolver(c, imp)}
}
var _ Object = (*Var)(nil)
// Var is the type used to represent a variable in the loaded package.
// It contains the original variable and the resolver used to generate the
// aliases.
type Var struct {
*types.Var
objectResolver
}
// NewVar returns a new [Var] with the given variable. The importer is used to
// add the variable package to the list of imports.
func NewVar(v *types.Var, imp *importer.Importer) *Var {
return &Var{v, newObjectResolver(v, imp)}
}
var _ Object = (*Func)(nil)
// Func is the type used to represent a function in the loaded package.
// It contains the original function and the resolver used to generate the
// aliases.
type Func struct {
*types.Func
objectResolver
tsig *Signature
}
// NewFunc returns a new [Func] with the given function. The importer is used to
// add the function package to the list of imports.
func NewFunc(fn *types.Func, imp *importer.Importer) *Func {
return &Func{fn, newObjectResolver(fn, imp), NewSignature(fn.Type().(*types.Signature), imp)}
}
// WriteSignature returns the signature of the function as a string. It is a wrapper
// around [types.WriteSignature] that uses a custom [types.Qualifier] to resolve
// the package aliases. The signature is wrapped by [Signature.Wrapper] to replace
// the parameter and result names on conflict with the package aliases.
func (fn *Func) WriteSignature() string {
buf := new(bytes.Buffer)
types.WriteSignature(buf, fn.tsig.Wrapper(), fn.qualifier)
return buf.String()
}
// CallArgs returns the arguments of the function as a string. The arguments are
// joined by a comma and the variadic argument is suffixed with "...".
// If the function has no arguments, it returns an empty string.
//
// Example:
//
// func(a int, b bool, c ...string) // "a, b, c..."
func (fn *Func) CallArgs() string {
params := fn.tsig.Wrapper().Params()
l := params.Len()
if l == 0 {
return ""
}
names := sequence.New(params.Len, func(i int) string { return params.At(i).Name() }).
SliceFuncIndex(func(s string, i int) string {
if i == l-1 && fn.tsig.Variadic() {
return s + "..."
}
return s
})
return strings.Join(names, ", ")
}
// Returns returns true if the function has results.
func (fn *Func) Returns() bool {
return fn.tsig.Results().Len() > 0
}
var _ Object = (*TypeName)(nil)
// TypeName is the type used to represent a type in the loaded package.
// It contains the original type and the resolver used to generate the
// aliases.
type TypeName struct {
*types.TypeName
objectResolver
}
// NewTypeName returns a new [TypeName] with the given type. The importer is used
// to add the type package to the list of imports.
func NewTypeName(tn *types.TypeName, imp *importer.Importer) *TypeName {
return &TypeName{tn, newObjectResolver(tn, imp)}
}
type objectResolver struct {
typeQualifier
orig types.Object
typeParams []*TypeParam
typeArgs *sequence.Sequence[types.Type]
}
func newObjectResolver(obj types.Object, imp *importer.Importer) objectResolver {
o := objectResolver{typeQualifier: typeQualifier{imp}, orig: obj}
o.importType(obj.Type())
o.setGenerics(obj.Type())
return o
}
// PackageAlias returns the alias of the package as declared in the import
// statement.
func (o *objectResolver) PackageAlias() string {
return o.imp.AliasOf(o.orig.Pkg())
}
// TypeString returns the object type as a string, resolving the package
// names using the aliases declared in the import statements.
func (o *objectResolver) TypeString() string {
return types.TypeString(o.orig.Type(), o.qualifier)
}
// Generic returns true if the object is generic, that is, if it has type
// parameters that are not resolved by type arguments.
//
// Example:
//
// type A[T1, T2 any] struct{ Foo T1; Bar T2 } // true
// type B[T1 any] A[T1, int] // false
// type C A[int, string] // false
func (o *objectResolver) Generic() bool {
tpl := len(o.TypeParams())
return tpl > 0 && tpl > len(o.TypeArgs())
}
// TypeParams returns the type parameters of the object as a slice.
func (o *objectResolver) TypeParams() []*TypeParam {
if o.typeParams == nil {
return nil
}
return o.typeParams
}
// TypeArgs returns the type arguments of the object as a slice.
func (o *objectResolver) TypeArgs() []types.Type {
if o.typeArgs == nil {
return nil
}
return o.typeArgs.Slice()
}
func (o *objectResolver) importType(typ types.Type) {
switch typ := typ.(type) {
case *types.Named:
if tn := typ.Obj(); tn != nil {
o.imp.AddImport(tn.Pkg())
}
case *types.Map:
o.importType(typ.Key())
o.importType(typ.Elem())
case interface{ Elem() types.Type }: // *types.Array, *types.Slice, *types.Chan, *types.Pointer
o.importType(typ.Elem())
case *types.Signature:
// do not call o.importType(typ.Params()) and o.importType(typ.Results())
// to avoid unnecessaty type switches
sequence.FromSequenceable(typ.Params()).
ForEach(o.varImporter)
sequence.FromSequenceable(typ.Results()).
ForEach(o.varImporter)
case *types.Struct:
sequence.New(typ.NumFields, typ.Field).
ForEach(o.varImporter)
case *types.Interface:
sequence.New(typ.NumMethods, typ.Method).
ForEach(o.funcImporter)
sequence.New(typ.NumEmbeddeds, typ.EmbeddedType).
ForEach(o.importType)
}
}
func (o *objectResolver) varImporter(pv *types.Var) {
o.importObject(pv)
}
func (o *objectResolver) funcImporter(fn *types.Func) {
o.importObject(fn)
}
func (o *objectResolver) importObject(pt PackageTyper) {
o.imp.AddImport(pt.Pkg())
o.importType(pt.Type())
}
func (o *objectResolver) setGenerics(typ types.Type) {
if typ, ok := typ.(interface{ TypeParams() *types.TypeParamList }); ok {
o.typeParams = make([]*TypeParam, typ.TypeParams().Len())
sequence.FromSequenceable(typ.TypeParams()).
ForEachIndex(func(tp *types.TypeParam, i int) {
o.importType(tp.Constraint())
o.typeParams[i] = NewTypeParam(tp, o.imp)
})
}
if typ, ok := typ.(interface{ TypeArgs() *types.TypeList }); ok {
o.typeArgs = sequence.FromSequenceable(typ.TypeArgs()).
ForEach(o.importType)
}
}
// Object extends the [types.Object] interface with methods to get the package
// alias and the resolved type as a string.
type Object interface {
types.Object
PackageAliaser
TypeStringer
}
// PackageTyper is a subset of the [types.Object] interface that provides
// methods to get the package and the type of the object.
type PackageTyper interface {
Pkg() *types.Package
Type() types.Type
}
// PackageAliaser is the interface implemented by types that can provide the
// alias of the package they belong to.
type PackageAliaser interface {
// PackageAlias returns the alias of the package as declared in the
// import statement.
PackageAlias() string
}
// TypeStringer is the interface implemented by types that can return their type
// as a string.
type TypeStringer interface {
// TypeString returns the type of the implementing type as a string.
TypeString() string
}