@@ -23,7 +23,7 @@ func Implementation(ctx context.Context, snapshot Snapshot, f FileHandle, pp pro
23
23
ctx , done := event .Start (ctx , "source.Implementation" )
24
24
defer done ()
25
25
26
- impls , err := implementations (ctx , snapshot , f , pp )
26
+ impls , err := implementations (ctx , snapshot , f , pp , true )
27
27
if err != nil {
28
28
return nil , err
29
29
}
@@ -58,108 +58,168 @@ func Implementation(ctx context.Context, snapshot Snapshot, f FileHandle, pp pro
58
58
var ErrNotAType = errors .New ("not a type name or method" )
59
59
60
60
// implementations returns the concrete implementations of the specified
61
- // interface, or the interfaces implemented by the specified concrete type.
62
- // It populates only the definition-related fields of qualifiedObject.
63
- // (Arguably it should return a smaller data type.)
64
- func implementations (ctx context.Context , s Snapshot , f FileHandle , pp protocol.Position ) ([]qualifiedObject , error ) {
61
+ // interface, or the interfaces implemented by the specified concrete type,
62
+ // or the concrete implementations of a function type. It populates only
63
+ // the definition-related fields of qualifiedObject. (Arguably it should
64
+ // return a smaller data type.)
65
+ func implementations (ctx context.Context , s Snapshot , f FileHandle , pp protocol.Position , includeFuncs bool ) (impls []qualifiedObject , err error ) {
65
66
// Find all named types, even local types
66
67
// (which can have methods due to promotion).
67
- var (
68
- allNamed [] * types. Named
69
- pkgs = make ( map [ * types. Package ] Package )
70
- )
68
+ qos , err := qualifiedObjsAtProtocolPos ( ctx , s , f . URI (), pp )
69
+ if err != nil {
70
+ return nil , err
71
+ }
71
72
knownPkgs , err := s .KnownPackages (ctx )
72
73
if err != nil {
73
74
return nil , err
74
75
}
76
+ pkgs := make (map [* types.Package ]Package , len (knownPkgs ))
75
77
for _ , pkg := range knownPkgs {
76
78
pkgs [pkg .GetTypes ()] = pkg
77
- for _ , obj := range pkg .GetTypesInfo ().Defs {
78
- obj , ok := obj .(* types.TypeName )
79
- // We ignore aliases 'type M = N' to avoid duplicate reporting
80
- // of the Named type N.
81
- if ! ok || obj .IsAlias () {
82
- continue
83
- }
84
- if named , ok := obj .Type ().(* types.Named ); ok {
85
- allNamed = append (allNamed , named )
79
+ }
80
+
81
+ // Defer collection and caching of named types. It's only required for
82
+ // interface definitions, not function type defintions.
83
+ var allNamed []* types.Named
84
+ getAllNamed := func () []* types.Named {
85
+ if allNamed == nil {
86
+ for _ , pkg := range knownPkgs {
87
+ for _ , obj := range pkg .GetTypesInfo ().Defs {
88
+ obj , ok := obj .(* types.TypeName )
89
+ // We ignore aliases 'type M = N' to avoid duplicate reporting
90
+ // of the Named type N.
91
+ if ! ok || obj .IsAlias () {
92
+ continue
93
+ }
94
+ if named , ok := obj .Type ().(* types.Named ); ok {
95
+ allNamed = append (allNamed , named )
96
+ }
97
+ }
86
98
}
87
99
}
100
+ return allNamed
88
101
}
89
102
90
- qos , err := qualifiedObjsAtProtocolPos (ctx , s , f .URI (), pp )
91
- if err != nil {
92
- return nil , err
93
- }
94
- var (
95
- impls []qualifiedObject
96
- seen = make (map [token.Position ]bool )
97
- )
103
+ seen := make (map [token.Position ]bool )
98
104
for _ , qo := range qos {
99
- // Ascertain the query identifier (type or method).
100
- var (
101
- queryType types.Type
102
- queryMethod * types.Func
103
- )
105
+ var ok bool
104
106
switch obj := qo .obj .(type ) {
105
107
case * types.Func :
106
- queryMethod = obj
107
- if recv := obj . Type ().( * types. Signature ). Recv (); recv ! = nil {
108
- queryType = ensurePointer ( recv . Type ())
108
+ recv : = obj . Type ().( * types. Signature ). Recv ()
109
+ if recv = = nil {
110
+ break
109
111
}
112
+ impls = append (impls , findInterfaceImplementations (pkgs , getAllNamed , seen , s , ensurePointer (recv .Type ()), obj )... )
113
+ ok = true
110
114
case * types.TypeName :
111
- queryType = ensurePointer (obj .Type ())
115
+ sig , isFunc := obj .Type ().Underlying ().(* types.Signature )
116
+ if isFunc {
117
+ if ! includeFuncs {
118
+ break
119
+ }
120
+ impls = append (impls , findFunctionImplementations (pkgs , seen , s , sig )... )
121
+ ok = true
122
+ break
123
+ }
124
+ impls = append (impls , findInterfaceImplementations (pkgs , getAllNamed , seen , s , ensurePointer (obj .Type ()), nil )... )
125
+ ok = true
126
+ case * types.Var :
127
+ if ! includeFuncs {
128
+ break
129
+ }
130
+ sig , isFunc := obj .Type ().Underlying ().(* types.Signature )
131
+ if ! isFunc {
132
+ break
133
+ }
134
+ impls = append (impls , findFunctionImplementations (pkgs , seen , s , sig )... )
135
+ ok = true
112
136
}
113
137
114
- if queryType == nil {
138
+ if ! ok {
115
139
return nil , ErrNotAType
116
140
}
141
+ }
117
142
118
- if types .NewMethodSet (queryType ).Len () == 0 {
119
- return nil , nil
143
+ return impls , nil
144
+ }
145
+
146
+ func findInterfaceImplementations (pkgs map [* types.Package ]Package , getAllNamed func () []* types.Named , seen map [token.Position ]bool , s Snapshot , queryType types.Type , queryMethod * types.Func ) (impls []qualifiedObject ) {
147
+ if types .NewMethodSet (queryType ).Len () == 0 {
148
+ return nil
149
+ }
150
+
151
+ // Find all the named types that match our query.
152
+ for _ , named := range getAllNamed () {
153
+ var (
154
+ candObj types.Object = named .Obj ()
155
+ candType = ensurePointer (named )
156
+ )
157
+
158
+ if ! concreteImplementsIntf (candType , queryType ) {
159
+ continue
120
160
}
121
161
122
- // Find all the named types that match our query.
123
- for _ , named := range allNamed {
124
- var (
125
- candObj types.Object = named .Obj ()
126
- candType = ensurePointer (named )
127
- )
162
+ ms := types .NewMethodSet (candType )
163
+ if ms .Len () == 0 {
164
+ // Skip empty interfaces.
165
+ continue
166
+ }
128
167
129
- if ! concreteImplementsIntf (candType , queryType ) {
168
+ // If client queried a method, look up corresponding candType method.
169
+ if queryMethod != nil {
170
+ sel := ms .Lookup (queryMethod .Pkg (), queryMethod .Name ())
171
+ if sel == nil {
130
172
continue
131
173
}
174
+ candObj = sel .Obj ()
175
+ }
176
+
177
+ pos := s .FileSet ().Position (candObj .Pos ())
178
+ if candObj == queryMethod || seen [pos ] {
179
+ continue
180
+ }
132
181
133
- ms := types .NewMethodSet (candType )
134
- if ms .Len () == 0 {
135
- // Skip empty interfaces.
182
+ seen [pos ] = true
183
+
184
+ impls = append (impls , qualifiedObject {
185
+ obj : candObj ,
186
+ pkg : pkgs [candObj .Pkg ()], // may be nil (e.g. error)
187
+ })
188
+ }
189
+
190
+ return impls
191
+ }
192
+
193
+ func findFunctionImplementations (pkgs map [* types.Package ]Package , seen map [token.Position ]bool , s Snapshot , sig * types.Signature ) (impls []qualifiedObject ) {
194
+ for pkg := range pkgs {
195
+ for _ , name := range pkg .Scope ().Names () {
196
+ o := pkg .Scope ().Lookup (name )
197
+ if _ , isType := o .(* types.TypeName ); isType {
136
198
continue
137
199
}
138
-
139
- // If client queried a method, look up corresponding candType method.
140
- if queryMethod != nil {
141
- sel := ms .Lookup (queryMethod .Pkg (), queryMethod .Name ())
142
- if sel == nil {
143
- continue
144
- }
145
- candObj = sel .Obj ()
200
+ var csig * types.Signature
201
+ var isFunc bool
202
+ if csig , isFunc = o .Type ().Underlying ().(* types.Signature ); ! isFunc {
203
+ continue
146
204
}
147
205
148
- pos := s .FileSet ().Position (candObj .Pos ())
149
- if candObj == queryMethod || seen [pos ] {
206
+ if ! types .AssignableTo (sig , csig ) {
207
+ continue
208
+ }
209
+ pos := s .FileSet ().Position (o .Pos ())
210
+ if seen [pos ] {
150
211
continue
151
212
}
152
213
153
214
seen [pos ] = true
154
215
155
216
impls = append (impls , qualifiedObject {
156
- obj : candObj ,
157
- pkg : pkgs [candObj .Pkg ()], // may be nil (e.g. error)
217
+ obj : o ,
218
+ pkg : pkgs [o .Pkg ()], // may be nil (e.g. error)
158
219
})
159
220
}
160
221
}
161
-
162
- return impls , nil
222
+ return impls
163
223
}
164
224
165
225
// concreteImplementsIntf returns true if a is an interface type implemented by
0 commit comments