@@ -119,26 +119,26 @@ type StubInfo struct {
119119// function call. This is essentially what the refactor/satisfy does,
120120// more generally. Refactor to share logic, after auditing 'satisfy'
121121// 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 {
123123 for _ , n := range path {
124124 switch n := n .(type ) {
125125 case * ast.ValueSpec :
126- return fromValueSpec (fset , ti , n , pos )
126+ return fromValueSpec (fset , info , n , pos )
127127 case * ast.ReturnStmt :
128128 // An error here may not indicate a real error the user should know about, but it may.
129129 // Therefore, it would be best to log it out for debugging/reporting purposes instead of ignoring
130130 // it. However, event.Log takes a context which is not passed via the analysis package.
131131 // TODO(marwan-at-work): properly log this error.
132- si , _ := fromReturnStmt (fset , ti , pos , path , n )
132+ si , _ := fromReturnStmt (fset , info , pos , path , n )
133133 return si
134134 case * ast.AssignStmt :
135- return fromAssignStmt (fset , ti , n , pos )
135+ return fromAssignStmt (fset , info , n , pos )
136136 case * ast.CallExpr :
137137 // Note that some call expressions don't carry the interface type
138138 // because they don't point to a function or method declaration elsewhere.
139139 // For eaxmple, "var Interface = (*Concrete)(nil)". In that case, continue
140140 // 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 )
142142 if si != nil {
143143 return si
144144 }
@@ -150,23 +150,26 @@ func GetStubInfo(fset *token.FileSet, ti *types.Info, path []ast.Node, pos token
150150// fromCallExpr tries to find an *ast.CallExpr's function declaration and
151151// analyzes a function call's signature against the passed in parameter to deduce
152152// 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
158161 break
159162 }
160163 }
161- if paramIdx == - 1 {
164+ if arg == nil {
162165 return nil
163166 }
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 {
167170 return nil
168171 }
169- tv , ok := ti .Types [ce .Fun ]
172+ tv , ok := info .Types [call .Fun ]
170173 if ! ok {
171174 return nil
172175 }
@@ -175,13 +178,13 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
175178 return nil
176179 }
177180 var paramType types.Type
178- if sig .Variadic () && paramIdx >= sig .Params ().Len ()- 1 {
181+ if sig .Variadic () && argIdx >= sig .Params ().Len ()- 1 {
179182 v := sig .Params ().At (sig .Params ().Len () - 1 )
180183 if s , _ := v .Type ().(* types.Slice ); s != nil {
181184 paramType = s .Elem ()
182185 }
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 ()
185188 }
186189 if paramType == nil {
187190 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
192195 }
193196 return & StubInfo {
194197 Fset : fset ,
195- Concrete : concObj ,
198+ Concrete : concType ,
196199 Pointer : pointer ,
197200 Interface : iface ,
198201 }
@@ -203,21 +206,24 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
203206//
204207// For example, func() io.Writer { return myType{} }
205208// 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.
207211 returnIdx := - 1
208212 for i , r := range ret .Results {
209- if pos >= r .Pos () && pos <= r .End () {
213+ if r .Pos () <= pos && pos <= r .End () {
210214 returnIdx = i
215+ break
211216 }
212217 }
213218 if returnIdx == - 1 {
214219 return nil , fmt .Errorf ("pos %d not within return statement bounds: [%d-%d]" , pos , ret .Pos (), ret .End ())
215220 }
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 {
218224 return nil , nil
219225 }
220- funcType := enclosingFunction (path , ti )
226+ funcType := enclosingFunction (path , info )
221227 if funcType == nil {
222228 return nil , fmt .Errorf ("could not find the enclosing function of the return statement" )
223229 }
@@ -226,86 +232,91 @@ func fromReturnStmt(fset *token.FileSet, ti *types.Info, pos token.Pos, path []a
226232 len (ret .Results ),
227233 len (funcType .Results .List ))
228234 }
229- iface := ifaceType (funcType .Results .List [returnIdx ].Type , ti )
235+ iface := ifaceType (funcType .Results .List [returnIdx ].Type , info )
230236 if iface == nil {
231237 return nil , nil
232238 }
233239 return & StubInfo {
234240 Fset : fset ,
235- Concrete : concObj ,
241+ Concrete : concType ,
236242 Pointer : pointer ,
237243 Interface : iface ,
238244 }, nil
239245}
240246
241247// fromValueSpec returns *StubInfo from a variable declaration such as
242248// 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
248255 break
249256 }
250257 }
258+ if rhs == nil {
259+ return nil // e.g. pos was on the LHS (#64545)
260+ }
251261
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 {
263271 return nil
264272 }
265- ifaceObj := ifaceType (ifaceNode , ti )
273+ ifaceObj := ifaceType (ifaceNode , info )
266274 if ifaceObj == nil {
267275 return nil
268276 }
269277 return & StubInfo {
270278 Fset : fset ,
271- Concrete : concObj ,
279+ Concrete : concType ,
272280 Interface : ifaceObj ,
273281 Pointer : pointer ,
274282 }
275283}
276284
277- // fromAssignStmt returns *StubInfo from a variable re- assignment such as
285+ // fromAssignStmt returns *StubInfo from a variable assignment such as
278286// var x io.Writer
279287// 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.
282296 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
290308 break
291309 }
292310 }
293- if idx == - 1 {
311+ if lhs == nil || rhs == nil {
294312 return nil
295313 }
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 )
305316 if ifaceObj == nil {
306317 return nil
307318 }
308- concType , pointer := concreteType (rhs , ti )
319+ concType , pointer := concreteType (rhs , info )
309320 if concType == nil || concType .Obj ().Pkg () == nil {
310321 return nil
311322 }
@@ -382,11 +393,9 @@ func RelativeToFiles(concPkg *types.Package, concFile *ast.File, ifaceImports []
382393 }
383394}
384395
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 ]
390399 if ! ok {
391400 return nil
392401 }
@@ -398,8 +407,7 @@ func ifaceObjFromType(t types.Type) *types.TypeName {
398407 if ! ok {
399408 return nil
400409 }
401- _ , ok = named .Underlying ().(* types.Interface )
402- if ! ok {
410+ if ! types .IsInterface (named ) {
403411 return nil
404412 }
405413 // Interfaces defined in the "builtin" package return nil a Pkg().
@@ -418,8 +426,8 @@ func ifaceObjFromType(t types.Type) *types.TypeName {
418426// method will return a nil *types.Named. The second return parameter
419427// is a boolean that indicates whether the concreteType was defined as a
420428// 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 ]
423431 if ! ok {
424432 return nil , false
425433 }
0 commit comments