5
5
package source
6
6
7
7
import (
8
- "context"
9
8
"errors"
10
- "fmt"
11
9
"go/ast"
12
10
"go/token"
13
11
"go/types"
14
-
15
- "golang.org/x/tools/gopls/internal/lsp/protocol"
16
- "golang.org/x/tools/gopls/internal/lsp/safetoken"
17
- "golang.org/x/tools/gopls/internal/span"
18
- "golang.org/x/tools/internal/event"
19
12
)
20
13
21
- var ErrNotAType = errors . New ( "not a type name or method" )
14
+ // TODO(adonovan): move these declarations elsewhere.
22
15
23
16
// concreteImplementsIntf returns true if a is an interface type implemented by
24
17
// concrete type b, or vice versa.
@@ -44,234 +37,14 @@ func concreteImplementsIntf(a, b types.Type) bool {
44
37
return types .AssignableTo (a , b )
45
38
}
46
39
47
- // A qualifiedObject is the result of resolving a reference from an
48
- // identifier to an object.
49
- type qualifiedObject struct {
50
- // definition
51
- obj types.Object // the referenced object
52
- pkg Package // the Package that defines the object (nil => universe)
53
-
54
- // reference (optional)
55
- node ast.Node // the reference (*ast.Ident or *ast.ImportSpec) to the object
56
- sourcePkg Package // the Package containing node
57
- }
58
-
59
40
var (
60
- errBuiltin = errors .New ("builtin object" )
41
+ // TODO(adonovan): why do various RPC handlers related to
42
+ // IncomingCalls return (nil, nil) on the protocol in response
43
+ // to this error? That seems like a violation of the protocol.
44
+ // Is it perhaps a workaround for VSCode behavior?
61
45
errNoObjectFound = errors .New ("no object found" )
62
46
)
63
47
64
- // qualifiedObjsAtProtocolPos returns info for all the types.Objects referenced
65
- // at the given position, for the following selection of packages:
66
- //
67
- // 1. all packages (including all test variants), in their workspace parse mode
68
- // 2. if not included above, at least one package containing uri in full parse mode
69
- //
70
- // Finding objects in (1) ensures that we locate references within all
71
- // workspace packages, including in x_test packages. Including (2) ensures that
72
- // we find local references in the current package, for non-workspace packages
73
- // that may be open.
74
- func qualifiedObjsAtProtocolPos (ctx context.Context , s Snapshot , uri span.URI , pp protocol.Position ) ([]qualifiedObject , error ) {
75
- fh , err := s .GetFile (ctx , uri )
76
- if err != nil {
77
- return nil , err
78
- }
79
- content , err := fh .Read ()
80
- if err != nil {
81
- return nil , err
82
- }
83
- m := protocol .NewMapper (uri , content )
84
- offset , err := m .PositionOffset (pp )
85
- if err != nil {
86
- return nil , err
87
- }
88
- return qualifiedObjsAtLocation (ctx , s , positionKey {uri , offset }, map [positionKey ]bool {})
89
- }
90
-
91
- // A positionKey identifies a byte offset within a file (URI).
92
- //
93
- // When a file has been parsed multiple times in the same FileSet,
94
- // there may be multiple token.Pos values denoting the same logical
95
- // position. In such situations, a positionKey may be used for
96
- // de-duplication.
97
- type positionKey struct {
98
- uri span.URI
99
- offset int
100
- }
101
-
102
- // qualifiedObjsAtLocation finds all objects referenced at offset in uri,
103
- // across all packages in the snapshot.
104
- func qualifiedObjsAtLocation (ctx context.Context , s Snapshot , key positionKey , seen map [positionKey ]bool ) ([]qualifiedObject , error ) {
105
- if seen [key ] {
106
- return nil , nil
107
- }
108
- seen [key ] = true
109
-
110
- // We search for referenced objects starting with all packages containing the
111
- // current location, and then repeating the search for every distinct object
112
- // location discovered.
113
- //
114
- // In the common case, there should be at most one additional location to
115
- // consider: the definition of the object referenced by the location. But we
116
- // try to be comprehensive in case we ever support variations on build
117
- // constraints.
118
- metas , err := s .MetadataForFile (ctx , key .uri )
119
- if err != nil {
120
- return nil , err
121
- }
122
- ids := make ([]PackageID , len (metas ))
123
- for i , m := range metas {
124
- ids [i ] = m .ID
125
- }
126
- pkgs , err := s .TypeCheck (ctx , TypecheckWorkspace , ids ... )
127
- if err != nil {
128
- return nil , err
129
- }
130
-
131
- // In order to allow basic references/rename/implementations to function when
132
- // non-workspace packages are open, ensure that we have at least one fully
133
- // parsed package for the current file. This allows us to find references
134
- // inside the open package. Use WidestPackage to capture references in test
135
- // files.
136
- hasFullPackage := false
137
- for _ , pkg := range pkgs {
138
- if pkg .ParseMode () == ParseFull {
139
- hasFullPackage = true
140
- break
141
- }
142
- }
143
- if ! hasFullPackage {
144
- pkg , _ , err := PackageForFile (ctx , s , key .uri , TypecheckFull , WidestPackage )
145
- if err != nil {
146
- return nil , err
147
- }
148
- pkgs = append (pkgs , pkg )
149
- }
150
-
151
- // report objects in the order we encounter them. This ensures that the first
152
- // result is at the cursor...
153
- var qualifiedObjs []qualifiedObject
154
- // ...but avoid duplicates.
155
- seenObjs := map [types.Object ]bool {}
156
-
157
- for _ , searchpkg := range pkgs {
158
- pgf , err := searchpkg .File (key .uri )
159
- if err != nil {
160
- return nil , err
161
- }
162
- pos := pgf .Tok .Pos (key .offset )
163
-
164
- // TODO(adonovan): replace this section with a call to objectsAt().
165
- path := pathEnclosingObjNode (pgf .File , pos )
166
- if path == nil {
167
- continue
168
- }
169
- var objs []types.Object
170
- switch leaf := path [0 ].(type ) {
171
- case * ast.Ident :
172
- // If leaf represents an implicit type switch object or the type
173
- // switch "assign" variable, expand to all of the type switch's
174
- // implicit objects.
175
- if implicits , _ := typeSwitchImplicits (searchpkg .GetTypesInfo (), path ); len (implicits ) > 0 {
176
- objs = append (objs , implicits ... )
177
- } else {
178
- obj := searchpkg .GetTypesInfo ().ObjectOf (leaf )
179
- if obj == nil {
180
- return nil , fmt .Errorf ("%w for %q" , errNoObjectFound , leaf .Name )
181
- }
182
- objs = append (objs , obj )
183
- }
184
- case * ast.ImportSpec :
185
- // Look up the implicit *types.PkgName.
186
- obj := searchpkg .GetTypesInfo ().Implicits [leaf ]
187
- if obj == nil {
188
- return nil , fmt .Errorf ("%w for import %s" , errNoObjectFound , UnquoteImportPath (leaf ))
189
- }
190
- objs = append (objs , obj )
191
- }
192
-
193
- // Get all of the transitive dependencies of the search package.
194
- pkgSet := map [* types.Package ]Package {
195
- searchpkg .GetTypes (): searchpkg ,
196
- }
197
- deps := recursiveDeps (s , searchpkg .Metadata ())[1 :]
198
- // Ignore the error from type checking, but check if the context was
199
- // canceled (which would have caused TypeCheck to exit early).
200
- depPkgs , _ := s .TypeCheck (ctx , TypecheckWorkspace , deps ... )
201
- if ctx .Err () != nil {
202
- return nil , ctx .Err ()
203
- }
204
- for _ , dep := range depPkgs {
205
- // Since we ignored the error from type checking, pkg may be nil.
206
- if dep != nil {
207
- pkgSet [dep .GetTypes ()] = dep
208
- }
209
- }
210
-
211
- for _ , obj := range objs {
212
- if obj .Parent () == types .Universe {
213
- return nil , fmt .Errorf ("%q: %w" , obj .Name (), errBuiltin )
214
- }
215
- pkg , ok := pkgSet [obj .Pkg ()]
216
- if ! ok {
217
- event .Error (ctx , fmt .Sprintf ("no package for obj %s: %v" , obj , obj .Pkg ()), err )
218
- continue
219
- }
220
- qualifiedObjs = append (qualifiedObjs , qualifiedObject {
221
- obj : obj ,
222
- pkg : pkg ,
223
- sourcePkg : searchpkg ,
224
- node : path [0 ],
225
- })
226
- seenObjs [obj ] = true
227
-
228
- // If the qualified object is in another file (or more likely, another
229
- // package), it's possible that there is another copy of it in a package
230
- // that we haven't searched, e.g. a test variant. See golang/go#47564.
231
- //
232
- // In order to be sure we've considered all packages, call
233
- // qualifiedObjsAtLocation recursively for all locations we encounter. We
234
- // could probably be more precise here, only continuing the search if obj
235
- // is in another package, but this should be good enough to find all
236
- // uses.
237
-
238
- if key , found := packagePositionKey (pkg , obj .Pos ()); found {
239
- otherObjs , err := qualifiedObjsAtLocation (ctx , s , key , seen )
240
- if err != nil {
241
- return nil , err
242
- }
243
- for _ , other := range otherObjs {
244
- if ! seenObjs [other .obj ] {
245
- qualifiedObjs = append (qualifiedObjs , other )
246
- seenObjs [other .obj ] = true
247
- }
248
- }
249
- } else {
250
- return nil , fmt .Errorf ("missing file for position of %q in %q" , obj .Name (), obj .Pkg ().Name ())
251
- }
252
- }
253
- }
254
- // Return an error if no objects were found since callers will assume that
255
- // the slice has at least 1 element.
256
- if len (qualifiedObjs ) == 0 {
257
- return nil , errNoObjectFound
258
- }
259
- return qualifiedObjs , nil
260
- }
261
-
262
- // packagePositionKey finds the positionKey for the given pos.
263
- //
264
- // The second result reports whether the position was found.
265
- func packagePositionKey (pkg Package , pos token.Pos ) (positionKey , bool ) {
266
- for _ , pgf := range pkg .CompiledGoFiles () {
267
- offset , err := safetoken .Offset (pgf .Tok , pos )
268
- if err == nil {
269
- return positionKey {pgf .URI , offset }, true
270
- }
271
- }
272
- return positionKey {}, false
273
- }
274
-
275
48
// pathEnclosingObjNode returns the AST path to the object-defining
276
49
// node associated with pos. "Object-defining" means either an
277
50
// *ast.Ident mapped directly to a types.Object or an ast.Node mapped
0 commit comments