internal/lsp/analysis/stubmethods: recognize *ast.CallExpr

This change makes it so that the stub methods analysis can recognize
errors happening to method and function call expressions that are being
passed a concrete type to an interface parameter. This way, a method stub CodeAction will appear at the call site.

Updates golang/go#37537

Change-Id: I886d53f06a85b9e5160d882aa742bb2b7fcea139
Reviewed-on: https://go-review.googlesource.com/c/tools/+/404655
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Suzy Mueller <suzmue@golang.org>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
This commit is contained in:
Marwan Sulaiman 2022-05-06 12:11:44 -07:00 committed by Robert Findley
parent facb0d30e9
commit 13bcb69cfa
5 changed files with 88 additions and 4 deletions

View File

@ -105,11 +105,59 @@ func GetStubInfo(ti *types.Info, path []ast.Node, pos token.Pos) *StubInfo {
return si
case *ast.AssignStmt:
return fromAssignStmt(ti, n, pos)
case *ast.CallExpr:
// Note that some call expressions don't carry the interface type
// because they don't point to a function or method declaration elsewhere.
// For eaxmple, "var Interface = (*Concrete)(nil)". In that case, continue
// this loop to encounter other possibilities such as *ast.ValueSpec or others.
si := fromCallExpr(ti, pos, n)
if si != nil {
return si
}
}
}
return nil
}
// fromCallExpr tries to find an *ast.CallExpr's function declaration and
// analyzes a function call's signature against the passed in parameter to deduce
// the concrete and interface types.
func fromCallExpr(ti *types.Info, pos token.Pos, ce *ast.CallExpr) *StubInfo {
paramIdx := -1
for i, p := range ce.Args {
if pos >= p.Pos() && pos <= p.End() {
paramIdx = i
break
}
}
if paramIdx == -1 {
return nil
}
p := ce.Args[paramIdx]
concObj, pointer := concreteType(p, ti)
if concObj == nil || concObj.Obj().Pkg() == nil {
return nil
}
tv, ok := ti.Types[ce.Fun]
if !ok {
return nil
}
sig, ok := tv.Type.(*types.Signature)
if !ok {
return nil
}
sigVar := sig.Params().At(paramIdx)
iface := ifaceObjFromType(sigVar.Type())
if iface == nil {
return nil
}
return &StubInfo{
Concrete: concObj,
Pointer: pointer,
Interface: iface,
}
}
// fromReturnStmt analyzes a "return" statement to extract
// a concrete type that is trying to be returned as an interface type.
//
@ -290,8 +338,11 @@ func ifaceType(n ast.Expr, ti *types.Info) types.Object {
if !ok {
return nil
}
typ := tv.Type
named, ok := typ.(*types.Named)
return ifaceObjFromType(tv.Type)
}
func ifaceObjFromType(t types.Type) types.Object {
named, ok := t.(*types.Named)
if !ok {
return nil
}

View File

@ -0,0 +1,13 @@
package stub
func main() {
check(&callExpr{}) //@suggestedfix("&", "refactor.rewrite")
}
func check(err error) {
if err != nil {
panic(err)
}
}
type callExpr struct{}

View File

@ -0,0 +1,20 @@
-- suggestedfix_stub_call_expr_4_8 --
package stub
func main() {
check(&callExpr{}) //@suggestedfix("&", "refactor.rewrite")
}
func check(err error) {
if err != nil {
panic(err)
}
}
type callExpr struct{}
// Error implements error
func (*callExpr) Error() string {
panic("unimplemented")
}

View File

@ -13,7 +13,7 @@ FoldingRangesCount = 2
FormatCount = 6
ImportCount = 8
SemanticTokenCount = 3
SuggestedFixCount = 62
SuggestedFixCount = 63
FunctionExtractionCount = 25
MethodExtractionCount = 6
DefinitionsCount = 95

View File

@ -13,7 +13,7 @@ FoldingRangesCount = 2
FormatCount = 6
ImportCount = 8
SemanticTokenCount = 3
SuggestedFixCount = 63
SuggestedFixCount = 64
FunctionExtractionCount = 25
MethodExtractionCount = 6
DefinitionsCount = 108