@@ -119,26 +119,26 @@ type StubInfo struct {
119
119
// function call. This is essentially what the refactor/satisfy does,
120
120
// more generally. Refactor to share logic, after auditing 'satisfy'
121
121
// for safety on ill-typed code.
122
- func GetStubInfo (fset * token.FileSet , ti * types.Info , path []ast.Node , pos token.Pos ) * StubInfo {
122
+ func GetStubInfo (fset * token.FileSet , info * types.Info , path []ast.Node , pos token.Pos ) * StubInfo {
123
123
for _ , n := range path {
124
124
switch n := n .(type ) {
125
125
case * ast.ValueSpec :
126
- return fromValueSpec (fset , ti , n , pos )
126
+ return fromValueSpec (fset , info , n , pos )
127
127
case * ast.ReturnStmt :
128
128
// An error here may not indicate a real error the user should know about, but it may.
129
129
// Therefore, it would be best to log it out for debugging/reporting purposes instead of ignoring
130
130
// it. However, event.Log takes a context which is not passed via the analysis package.
131
131
// TODO(marwan-at-work): properly log this error.
132
- si , _ := fromReturnStmt (fset , ti , pos , path , n )
132
+ si , _ := fromReturnStmt (fset , info , pos , path , n )
133
133
return si
134
134
case * ast.AssignStmt :
135
- return fromAssignStmt (fset , ti , n , pos )
135
+ return fromAssignStmt (fset , info , n , pos )
136
136
case * ast.CallExpr :
137
137
// Note that some call expressions don't carry the interface type
138
138
// because they don't point to a function or method declaration elsewhere.
139
139
// For eaxmple, "var Interface = (*Concrete)(nil)". In that case, continue
140
140
// this loop to encounter other possibilities such as *ast.ValueSpec or others.
141
- si := fromCallExpr (fset , ti , pos , n )
141
+ si := fromCallExpr (fset , info , pos , n )
142
142
if si != nil {
143
143
return si
144
144
}
@@ -150,23 +150,26 @@ func GetStubInfo(fset *token.FileSet, ti *types.Info, path []ast.Node, pos token
150
150
// fromCallExpr tries to find an *ast.CallExpr's function declaration and
151
151
// analyzes a function call's signature against the passed in parameter to deduce
152
152
// the concrete and interface types.
153
- func fromCallExpr (fset * token.FileSet , ti * types.Info , pos token.Pos , ce * ast.CallExpr ) * StubInfo {
154
- paramIdx := - 1
155
- for i , p := range ce .Args {
156
- if pos >= p .Pos () && pos <= p .End () {
157
- paramIdx = i
153
+ func fromCallExpr (fset * token.FileSet , info * types.Info , pos token.Pos , call * ast.CallExpr ) * StubInfo {
154
+ // Find argument containing pos.
155
+ argIdx := - 1
156
+ var arg ast.Expr
157
+ for i , callArg := range call .Args {
158
+ if callArg .Pos () <= pos && pos <= callArg .End () {
159
+ argIdx = i
160
+ arg = callArg
158
161
break
159
162
}
160
163
}
161
- if paramIdx == - 1 {
164
+ if arg == nil {
162
165
return nil
163
166
}
164
- p := ce . Args [ paramIdx ]
165
- concObj , pointer := concreteType (p , ti )
166
- if concObj == nil || concObj .Obj ().Pkg () == nil {
167
+
168
+ concType , pointer := concreteType (arg , info )
169
+ if concType == nil || concType .Obj ().Pkg () == nil {
167
170
return nil
168
171
}
169
- tv , ok := ti .Types [ce .Fun ]
172
+ tv , ok := info .Types [call .Fun ]
170
173
if ! ok {
171
174
return nil
172
175
}
@@ -175,13 +178,13 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
175
178
return nil
176
179
}
177
180
var paramType types.Type
178
- if sig .Variadic () && paramIdx >= sig .Params ().Len ()- 1 {
181
+ if sig .Variadic () && argIdx >= sig .Params ().Len ()- 1 {
179
182
v := sig .Params ().At (sig .Params ().Len () - 1 )
180
183
if s , _ := v .Type ().(* types.Slice ); s != nil {
181
184
paramType = s .Elem ()
182
185
}
183
- } else if paramIdx < sig .Params ().Len () {
184
- paramType = sig .Params ().At (paramIdx ).Type ()
186
+ } else if argIdx < sig .Params ().Len () {
187
+ paramType = sig .Params ().At (argIdx ).Type ()
185
188
}
186
189
if paramType == nil {
187
190
return nil // A type error prevents us from determining the param type.
@@ -192,7 +195,7 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
192
195
}
193
196
return & StubInfo {
194
197
Fset : fset ,
195
- Concrete : concObj ,
198
+ Concrete : concType ,
196
199
Pointer : pointer ,
197
200
Interface : iface ,
198
201
}
@@ -203,21 +206,24 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
203
206
//
204
207
// For example, func() io.Writer { return myType{} }
205
208
// would return StubInfo with the interface being io.Writer and the concrete type being myType{}.
206
- func fromReturnStmt (fset * token.FileSet , ti * types.Info , pos token.Pos , path []ast.Node , ret * ast.ReturnStmt ) (* StubInfo , error ) {
209
+ func fromReturnStmt (fset * token.FileSet , info * types.Info , pos token.Pos , path []ast.Node , ret * ast.ReturnStmt ) (* StubInfo , error ) {
210
+ // Find return operand containing pos.
207
211
returnIdx := - 1
208
212
for i , r := range ret .Results {
209
- if pos >= r .Pos () && pos <= r .End () {
213
+ if r .Pos () <= pos && pos <= r .End () {
210
214
returnIdx = i
215
+ break
211
216
}
212
217
}
213
218
if returnIdx == - 1 {
214
219
return nil , fmt .Errorf ("pos %d not within return statement bounds: [%d-%d]" , pos , ret .Pos (), ret .End ())
215
220
}
216
- concObj , pointer := concreteType (ret .Results [returnIdx ], ti )
217
- if concObj == nil || concObj .Obj ().Pkg () == nil {
221
+
222
+ concType , pointer := concreteType (ret .Results [returnIdx ], info )
223
+ if concType == nil || concType .Obj ().Pkg () == nil {
218
224
return nil , nil
219
225
}
220
- funcType := enclosingFunction (path , ti )
226
+ funcType := enclosingFunction (path , info )
221
227
if funcType == nil {
222
228
return nil , fmt .Errorf ("could not find the enclosing function of the return statement" )
223
229
}
@@ -226,86 +232,91 @@ func fromReturnStmt(fset *token.FileSet, ti *types.Info, pos token.Pos, path []a
226
232
len (ret .Results ),
227
233
len (funcType .Results .List ))
228
234
}
229
- iface := ifaceType (funcType .Results .List [returnIdx ].Type , ti )
235
+ iface := ifaceType (funcType .Results .List [returnIdx ].Type , info )
230
236
if iface == nil {
231
237
return nil , nil
232
238
}
233
239
return & StubInfo {
234
240
Fset : fset ,
235
- Concrete : concObj ,
241
+ Concrete : concType ,
236
242
Pointer : pointer ,
237
243
Interface : iface ,
238
244
}, nil
239
245
}
240
246
241
247
// fromValueSpec returns *StubInfo from a variable declaration such as
242
248
// var x io.Writer = &T{}
243
- func fromValueSpec (fset * token.FileSet , ti * types.Info , vs * ast.ValueSpec , pos token.Pos ) * StubInfo {
244
- var idx int
245
- for i , vs := range vs .Values {
246
- if pos >= vs .Pos () && pos <= vs .End () {
247
- idx = i
249
+ func fromValueSpec (fset * token.FileSet , info * types.Info , spec * ast.ValueSpec , pos token.Pos ) * StubInfo {
250
+ // Find RHS element containing pos.
251
+ var rhs ast.Expr
252
+ for _ , r := range spec .Values {
253
+ if r .Pos () <= pos && pos <= r .End () {
254
+ rhs = r
248
255
break
249
256
}
250
257
}
258
+ if rhs == nil {
259
+ return nil // e.g. pos was on the LHS (#64545)
260
+ }
251
261
252
- valueNode := vs .Values [idx ]
253
- ifaceNode := vs .Type
254
- callExp , ok := valueNode .(* ast.CallExpr )
255
- // if the ValueSpec is `var _ = myInterface(...)`
256
- // as opposed to `var _ myInterface = ...`
257
- if ifaceNode == nil && ok && len (callExp .Args ) == 1 {
258
- ifaceNode = callExp .Fun
259
- valueNode = callExp .Args [0 ]
260
- }
261
- concObj , pointer := concreteType (valueNode , ti )
262
- if concObj == nil || concObj .Obj ().Pkg () == nil {
262
+ // Possible implicit/explicit conversion to interface type?
263
+ ifaceNode := spec .Type // var _ myInterface = ...
264
+ if call , ok := rhs .(* ast.CallExpr ); ok && ifaceNode == nil && len (call .Args ) == 1 {
265
+ // var _ = myInterface(v)
266
+ ifaceNode = call .Fun
267
+ rhs = call .Args [0 ]
268
+ }
269
+ concType , pointer := concreteType (rhs , info )
270
+ if concType == nil || concType .Obj ().Pkg () == nil {
263
271
return nil
264
272
}
265
- ifaceObj := ifaceType (ifaceNode , ti )
273
+ ifaceObj := ifaceType (ifaceNode , info )
266
274
if ifaceObj == nil {
267
275
return nil
268
276
}
269
277
return & StubInfo {
270
278
Fset : fset ,
271
- Concrete : concObj ,
279
+ Concrete : concType ,
272
280
Interface : ifaceObj ,
273
281
Pointer : pointer ,
274
282
}
275
283
}
276
284
277
- // fromAssignStmt returns *StubInfo from a variable re- assignment such as
285
+ // fromAssignStmt returns *StubInfo from a variable assignment such as
278
286
// var x io.Writer
279
287
// x = &T{}
280
- func fromAssignStmt (fset * token.FileSet , ti * types.Info , as * ast.AssignStmt , pos token.Pos ) * StubInfo {
281
- idx := - 1
288
+ func fromAssignStmt (fset * token.FileSet , info * types.Info , assign * ast.AssignStmt , pos token.Pos ) * StubInfo {
289
+ // The interface conversion error in an assignment is against the RHS:
290
+ //
291
+ // var x io.Writer
292
+ // x = &T{} // error: missing method
293
+ // ^^^^
294
+ //
295
+ // Find RHS element containing pos.
282
296
var lhs , rhs ast.Expr
283
- // Given a re-assignment interface conversion error,
284
- // the compiler error shows up on the right hand side of the expression.
285
- // For example, x = &T{} where x is io.Writer highlights the error
286
- // under "&T{}" and not "x".
287
- for i , hs := range as .Rhs {
288
- if pos >= hs .Pos () && pos <= hs .End () {
289
- idx = i
297
+ for i , r := range assign .Rhs {
298
+ if r .Pos () <= pos && pos <= r .End () {
299
+ if i >= len (assign .Lhs ) {
300
+ // This should never happen as we would get a
301
+ // "cannot assign N values to M variables"
302
+ // before we get an interface conversion error.
303
+ // But be defensive.
304
+ return nil
305
+ }
306
+ lhs = assign .Lhs [i ]
307
+ rhs = r
290
308
break
291
309
}
292
310
}
293
- if idx == - 1 {
311
+ if lhs == nil || rhs == nil {
294
312
return nil
295
313
}
296
- // Technically, this should never happen as
297
- // we would get a "cannot assign N values to M variables"
298
- // before we get an interface conversion error. Nonetheless,
299
- // guard against out of range index errors.
300
- if idx >= len (as .Lhs ) {
301
- return nil
302
- }
303
- lhs , rhs = as .Lhs [idx ], as .Rhs [idx ]
304
- ifaceObj := ifaceType (lhs , ti )
314
+
315
+ ifaceObj := ifaceType (lhs , info )
305
316
if ifaceObj == nil {
306
317
return nil
307
318
}
308
- concType , pointer := concreteType (rhs , ti )
319
+ concType , pointer := concreteType (rhs , info )
309
320
if concType == nil || concType .Obj ().Pkg () == nil {
310
321
return nil
311
322
}
@@ -382,11 +393,9 @@ func RelativeToFiles(concPkg *types.Package, concFile *ast.File, ifaceImports []
382
393
}
383
394
}
384
395
385
- // ifaceType will try to extract the types.Object that defines
386
- // the interface given the ast.Expr where the "missing method"
387
- // or "conversion" errors happen.
388
- func ifaceType (n ast.Expr , ti * types.Info ) * types.TypeName {
389
- tv , ok := ti .Types [n ]
396
+ // ifaceType returns the named interface type to which e refers, if any.
397
+ func ifaceType (e ast.Expr , info * types.Info ) * types.TypeName {
398
+ tv , ok := info .Types [e ]
390
399
if ! ok {
391
400
return nil
392
401
}
@@ -398,8 +407,7 @@ func ifaceObjFromType(t types.Type) *types.TypeName {
398
407
if ! ok {
399
408
return nil
400
409
}
401
- _ , ok = named .Underlying ().(* types.Interface )
402
- if ! ok {
410
+ if ! types .IsInterface (named ) {
403
411
return nil
404
412
}
405
413
// Interfaces defined in the "builtin" package return nil a Pkg().
@@ -418,8 +426,8 @@ func ifaceObjFromType(t types.Type) *types.TypeName {
418
426
// method will return a nil *types.Named. The second return parameter
419
427
// is a boolean that indicates whether the concreteType was defined as a
420
428
// pointer or value.
421
- func concreteType (n ast.Expr , ti * types.Info ) (* types.Named , bool ) {
422
- tv , ok := ti .Types [n ]
429
+ func concreteType (e ast.Expr , info * types.Info ) (* types.Named , bool ) {
430
+ tv , ok := info .Types [e ]
423
431
if ! ok {
424
432
return nil , false
425
433
}
0 commit comments