@@ -51,6 +51,7 @@ import (
51
51
"strconv"
52
52
"strings"
53
53
54
+ "golang.org/x/tools/go/types/objectpath"
54
55
"golang.org/x/tools/gopls/internal/lsp/safetoken"
55
56
"golang.org/x/tools/internal/typeparams"
56
57
)
@@ -72,13 +73,6 @@ func NewIndex(fset *token.FileSet, pkg *types.Package) *Index {
72
73
//
73
74
// Conversion to protocol (UTF-16) form is done by the caller after a
74
75
// search, not during index construction.
75
- // TODO(adonovan): opt: reconsider this choice, if FileHandles, not
76
- // ParsedGoFiles were to provide ColumnMapper-like functionality.
77
- // (Column mapping is currently associated with parsing,
78
- // but non-parsed and even non-Go files need it too.)
79
- // Since type checking requires reading (but not parsing) all
80
- // dependencies' Go files, we could do the conversion at type-checking
81
- // time at little extra cost in that case.
82
76
type Location struct {
83
77
Filename string
84
78
Start , End int // byte offsets
@@ -93,19 +87,30 @@ type Key struct {
93
87
// KeyOf returns the search key for the method sets of a given type.
94
88
// It returns false if the type has no methods.
95
89
func KeyOf (t types.Type ) (Key , bool ) {
96
- mset := methodSetInfo (func (types. Object ) ( _ gobPosition ) { return }, t , gobPosition {} )
90
+ mset := methodSetInfo (t , nil )
97
91
if mset .Mask == 0 {
98
92
return Key {}, false // no methods
99
93
}
100
94
return Key {mset }, true
101
95
}
102
96
97
+ // A Result reports a matching type or method in a method-set search.
98
+ type Result struct {
99
+ Location Location // location of the type or method
100
+
101
+ // methods only:
102
+ PkgPath string // path of declaring package (may differ due to embedding)
103
+ ObjectPath objectpath.Path // path of method within declaring package
104
+ }
105
+
103
106
// Search reports each type that implements (or is implemented by) the
104
107
// type that produced the search key. If methodID is nonempty, only
105
108
// that method of each type is reported.
106
- // The result is the location of each type or method.
107
- func (index * Index ) Search (key Key , methodID string ) []Location {
108
- var locs []Location
109
+ //
110
+ // The result does not include the error.Error method.
111
+ // TODO(adonovan): give this special case a more systematic treatment.
112
+ func (index * Index ) Search (key Key , methodID string ) []Result {
113
+ var results []Result
109
114
for _ , candidate := range index .pkg .MethodSets {
110
115
// Traditionally this feature doesn't report
111
116
// interface/interface elements of the relation.
@@ -125,19 +130,34 @@ func (index *Index) Search(key Key, methodID string) []Location {
125
130
}
126
131
127
132
if methodID == "" {
128
- locs = append (locs , index .location (candidate .Posn ))
133
+ results = append (results , Result { Location : index .location (candidate .Posn )} )
129
134
} else {
130
135
for _ , m := range candidate .Methods {
131
136
// Here we exploit knowledge of the shape of the fingerprint string.
132
137
if strings .HasPrefix (m .Fingerprint , methodID ) &&
133
138
m .Fingerprint [len (methodID )] == '(' {
134
- locs = append (locs , index .location (m .Posn ))
139
+
140
+ // Don't report error.Error among the results:
141
+ // it has no true source location, no package,
142
+ // and is excluded from the xrefs index.
143
+ if m .PkgPath == 0 || m .ObjectPath == 0 {
144
+ if methodID != "Error" {
145
+ panic ("missing info for" + methodID )
146
+ }
147
+ continue
148
+ }
149
+
150
+ results = append (results , Result {
151
+ Location : index .location (m .Posn ),
152
+ PkgPath : index .pkg .Strings [m .PkgPath ],
153
+ ObjectPath : objectpath .Path (index .pkg .Strings [m .ObjectPath ]),
154
+ })
135
155
break
136
156
}
137
157
}
138
158
}
139
159
}
140
- return locs
160
+ return results
141
161
}
142
162
143
163
// satisfies does a fast check for whether x satisfies y.
@@ -161,7 +181,7 @@ outer:
161
181
162
182
func (index * Index ) location (posn gobPosition ) Location {
163
183
return Location {
164
- Filename : index .pkg .Filenames [posn .File ],
184
+ Filename : index .pkg .Strings [posn .File ],
165
185
Start : posn .Offset ,
166
186
End : posn .Offset + posn .Len ,
167
187
}
@@ -170,54 +190,73 @@ func (index *Index) location(posn gobPosition) Location {
170
190
// An indexBuilder builds an index for a single package.
171
191
type indexBuilder struct {
172
192
gobPackage
173
- filenameIndex map [string ]int
193
+ stringIndex map [string ]int
174
194
}
175
195
176
196
// build adds to the index all package-level named types of the specified package.
177
197
func (b * indexBuilder ) build (fset * token.FileSet , pkg * types.Package ) * Index {
198
+ _ = b .string ("" ) // 0 => ""
199
+
200
+ objectPos := func (obj types.Object ) gobPosition {
201
+ posn := safetoken .StartPosition (fset , obj .Pos ())
202
+ return gobPosition {b .string (posn .Filename ), posn .Offset , len (obj .Name ())}
203
+ }
204
+
205
+ // setindexInfo sets the (Posn, PkgPath, ObjectPath) fields for each method declaration.
206
+ setIndexInfo := func (m * gobMethod , method * types.Func ) {
207
+ // error.Error has empty Position, PkgPath, and ObjectPath.
208
+ if method .Pkg () == nil {
209
+ return
210
+ }
211
+
212
+ m .Posn = objectPos (method )
213
+ m .PkgPath = b .string (method .Pkg ().Path ())
214
+
215
+ // Instantiations of generic methods don't have an
216
+ // object path, so we use the generic.
217
+ if p , err := objectpath .For (typeparams .OriginMethod (method )); err != nil {
218
+ panic (err ) // can't happen for a method of a package-level type
219
+ } else {
220
+ m .ObjectPath = b .string (string (p ))
221
+ }
222
+ }
223
+
178
224
// We ignore aliases, though in principle they could define a
179
225
// struct{...} or interface{...} type, or an instantiation of
180
226
// a generic, that has a novel method set.
181
227
scope := pkg .Scope ()
182
228
for _ , name := range scope .Names () {
183
229
if tname , ok := scope .Lookup (name ).(* types.TypeName ); ok && ! tname .IsAlias () {
184
- b .add (fset , tname )
230
+ if mset := methodSetInfo (tname .Type (), setIndexInfo ); mset .Mask != 0 {
231
+ mset .Posn = objectPos (tname )
232
+ // Only record types with non-trivial method sets.
233
+ b .MethodSets = append (b .MethodSets , mset )
234
+ }
185
235
}
186
236
}
187
237
188
238
return & Index {pkg : b .gobPackage }
189
239
}
190
240
191
- func (b * indexBuilder ) add (fset * token.FileSet , tname * types.TypeName ) {
192
- objectPos := func (obj types.Object ) gobPosition {
193
- posn := safetoken .StartPosition (fset , obj .Pos ())
194
- return gobPosition {b .fileIndex (posn .Filename ), posn .Offset , len (obj .Name ())}
195
- }
196
- if mset := methodSetInfo (objectPos , tname .Type (), objectPos (tname )); mset .Mask != 0 {
197
- // Only record types with non-trivial method sets.
198
- b .MethodSets = append (b .MethodSets , mset )
199
- }
200
- }
201
-
202
- // fileIndex returns a small integer that encodes the file name.
203
- func (b * indexBuilder ) fileIndex (filename string ) int {
204
- i , ok := b .filenameIndex [filename ]
241
+ // string returns a small integer that encodes the string.
242
+ func (b * indexBuilder ) string (s string ) int {
243
+ i , ok := b .stringIndex [s ]
205
244
if ! ok {
206
- i = len (b .Filenames )
207
- if b .filenameIndex == nil {
208
- b .filenameIndex = make (map [string ]int )
245
+ i = len (b .Strings )
246
+ if b .stringIndex == nil {
247
+ b .stringIndex = make (map [string ]int )
209
248
}
210
- b .filenameIndex [ filename ] = i
211
- b .Filenames = append (b .Filenames , filename )
249
+ b .stringIndex [ s ] = i
250
+ b .Strings = append (b .Strings , s )
212
251
}
213
252
return i
214
253
}
215
254
216
- // methodSetInfo returns the method-set fingerprint
217
- // of a type and records its position (typePosn)
218
- // and the position of each of its methods m ,
219
- // as provided by objectPos(m) .
220
- func methodSetInfo (objectPos func (types. Object ) gobPosition , t types.Type , typePosn gobPosition ) gobMethodSet {
255
+ // methodSetInfo returns the method-set fingerprint of a type.
256
+ // It calls the optional setIndexInfo function for each gobMethod.
257
+ // This is used during index construction, but not search (KeyOf) ,
258
+ // to store extra information .
259
+ func methodSetInfo (t types.Type , setIndexInfo func ( * gobMethod , * types. Func ) ) gobMethodSet {
221
260
// For non-interface types, use *T
222
261
// (if T is not already a pointer)
223
262
// since it may have more methods.
@@ -234,10 +273,18 @@ func methodSetInfo(objectPos func(types.Object) gobPosition, t types.Type, typeP
234
273
tricky = true
235
274
}
236
275
sum := crc32 .ChecksumIEEE ([]byte (fp ))
237
- methods [i ] = gobMethod {fp , sum , objectPos (m )}
276
+ methods [i ] = gobMethod {Fingerprint : fp , Sum : sum }
277
+ if setIndexInfo != nil {
278
+ setIndexInfo (& methods [i ], m ) // set Position, PkgPath, ObjectPath
279
+ }
238
280
mask |= 1 << uint64 (((sum >> 24 )^ (sum >> 16 )^ (sum >> 8 )^ sum )& 0x3f )
239
281
}
240
- return gobMethodSet {typePosn , types .IsInterface (t ), tricky , mask , methods }
282
+ return gobMethodSet {
283
+ IsInterface : types .IsInterface (t ),
284
+ Tricky : tricky ,
285
+ Mask : mask ,
286
+ Methods : methods ,
287
+ }
241
288
}
242
289
243
290
// EnsurePointer wraps T in a types.Pointer if T is a named, non-interface type.
@@ -398,7 +445,7 @@ func fingerprint(method *types.Func) (string, bool) {
398
445
399
446
// A gobPackage records the method set of each package-level type for a single package.
400
447
type gobPackage struct {
401
- Filenames []string // see gobPosition.File
448
+ Strings []string // index of strings used by gobPosition.File, gobMethod.{Pkg,Object}Path
402
449
MethodSets []gobMethodSet
403
450
}
404
451
@@ -413,13 +460,17 @@ type gobMethodSet struct {
413
460
414
461
// A gobMethod records the name, type, and position of a single method.
415
462
type gobMethod struct {
416
- Fingerprint string // string of form "methodID(params...)(results)"
417
- Sum uint32 // checksum of fingerprint
418
- Posn gobPosition // location of method declaration
463
+ Fingerprint string // string of form "methodID(params...)(results)"
464
+ Sum uint32 // checksum of fingerprint
465
+
466
+ // index records only (zero in KeyOf; also for index of error.Error).
467
+ Posn gobPosition // location of method declaration
468
+ PkgPath int // path of package containing method declaration
469
+ ObjectPath int // object path of method relative to PkgPath
419
470
}
420
471
421
472
// A gobPosition records the file, offset, and length of an identifier.
422
473
type gobPosition struct {
423
- File int // index into Index.filenames
474
+ File int // index into gopPackage.Strings
424
475
Offset , Len int // in bytes
425
476
}
0 commit comments