Skip to content

Commit 2160c5f

Browse files
committed
gopls/internal/lsp/analysis: fix stubmethods with variadic parameters
Fix the stubmethods code action when matching against calls of variadic functions, and add a regression test. Additionally, reinstate panic recovery for code actions that were migrated from convenience analyzers. I do not have confidence that this is the only such panic. Fixes golang/go#61693 Change-Id: Id2642a13d3f793de27c0d7ebfa665428d671c56f Reviewed-on: https://go-review.googlesource.com/c/tools/+/514755 Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Peter Weinberger <[email protected]>
1 parent 3d20bbe commit 2160c5f

File tree

3 files changed

+85
-8
lines changed

3 files changed

+85
-8
lines changed

gopls/internal/lsp/analysis/stubmethods/stubmethods.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,19 @@ func fromCallExpr(fset *token.FileSet, ti *types.Info, pos token.Pos, ce *ast.Ca
174174
if !ok {
175175
return nil
176176
}
177-
sigVar := sig.Params().At(paramIdx)
178-
iface := ifaceObjFromType(sigVar.Type())
177+
var paramType types.Type
178+
if sig.Variadic() && paramIdx >= sig.Params().Len()-1 {
179+
v := sig.Params().At(sig.Params().Len() - 1)
180+
if s, _ := v.Type().(*types.Slice); s != nil {
181+
paramType = s.Elem()
182+
}
183+
} else if paramIdx < sig.Params().Len() {
184+
paramType = sig.Params().At(paramIdx).Type()
185+
}
186+
if paramType == nil {
187+
return nil // A type error prevents us from determining the param type.
188+
}
189+
iface := ifaceObjFromType(paramType)
179190
if iface == nil {
180191
return nil
181192
}

gopls/internal/lsp/code_action.go

+37-6
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"strings"
1313

1414
"golang.org/x/tools/go/ast/inspector"
15+
"golang.org/x/tools/gopls/internal/bug"
1516
"golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct"
1617
"golang.org/x/tools/gopls/internal/lsp/analysis/infertypeargs"
1718
"golang.org/x/tools/gopls/internal/lsp/analysis/stubmethods"
@@ -198,26 +199,46 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara
198199
if err != nil {
199200
return nil, err
200201
}
201-
for _, pd := range diagnostics {
202+
for _, pd := range stubMethodsDiagnostics {
202203
start, end, err := pgf.RangePos(pd.Range)
203204
if err != nil {
204205
return nil, err
205206
}
206-
if d, ok := stubmethods.DiagnosticForError(pkg.FileSet(), pgf.File, start, end, pd.Message, pkg.GetTypesInfo()); ok {
207+
action, ok, err := func() (_ protocol.CodeAction, _ bool, rerr error) {
208+
// golang/go#61693: code actions were refactored to run outside of the
209+
// analysis framework, but as a result they lost their panic recovery.
210+
//
211+
// Stubmethods "should never fail"", but put back the panic recovery as a
212+
// defensive measure.
213+
defer func() {
214+
if r := recover(); r != nil {
215+
rerr = bug.Errorf("stubmethods panicked: %v", r)
216+
}
217+
}()
218+
d, ok := stubmethods.DiagnosticForError(pkg.FileSet(), pgf.File, start, end, pd.Message, pkg.GetTypesInfo())
219+
if !ok {
220+
return protocol.CodeAction{}, false, nil
221+
}
207222
cmd, err := command.NewApplyFixCommand(d.Message, command.ApplyFixArgs{
208223
URI: protocol.URIFromSpanURI(pgf.URI),
209224
Fix: source.StubMethods,
210225
Range: pd.Range,
211226
})
212227
if err != nil {
213-
return nil, err
228+
return protocol.CodeAction{}, false, err
214229
}
215-
actions = append(actions, protocol.CodeAction{
230+
return protocol.CodeAction{
216231
Title: d.Message,
217232
Kind: protocol.QuickFix,
218233
Command: &cmd,
219234
Diagnostics: []protocol.Diagnostic{pd},
220-
})
235+
}, true, nil
236+
}()
237+
if err != nil {
238+
return nil, err
239+
}
240+
if ok {
241+
actions = append(actions, action)
221242
}
222243
}
223244

@@ -387,7 +408,17 @@ func refactorExtract(ctx context.Context, snapshot source.Snapshot, pgf *source.
387408
return actions, nil
388409
}
389410

390-
func refactorRewrite(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh source.FileHandle, rng protocol.Range) ([]protocol.CodeAction, error) {
411+
func refactorRewrite(ctx context.Context, snapshot source.Snapshot, pkg source.Package, pgf *source.ParsedGoFile, fh source.FileHandle, rng protocol.Range) (_ []protocol.CodeAction, rerr error) {
412+
// golang/go#61693: code actions were refactored to run outside of the
413+
// analysis framework, but as a result they lost their panic recovery.
414+
//
415+
// These code actions should never fail, but put back the panic recovery as a
416+
// defensive measure.
417+
defer func() {
418+
if r := recover(); r != nil {
419+
rerr = bug.Errorf("refactor.rewrite code actions panicked: %v", r)
420+
}
421+
}()
391422
start, end, err := pgf.RangePos(rng)
392423
if err != nil {
393424
return nil, err
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
This test exercises stub methods functionality with variadic parameters.
2+
3+
In golang/go#61693 stubmethods was panicking in this case.
4+
5+
-- go.mod --
6+
module mod.com
7+
8+
go 1.18
9+
-- main.go --
10+
package main
11+
12+
type C int
13+
14+
func F(err ...error) {}
15+
16+
func _() {
17+
var x error
18+
F(x, C(0)) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub)
19+
}
20+
-- @stub/main.go --
21+
package main
22+
23+
type C int
24+
25+
// Error implements error.
26+
func (C) Error() string {
27+
panic("unimplemented")
28+
}
29+
30+
func F(err ...error) {}
31+
32+
func _() {
33+
var x error
34+
F(x, C(0)) //@suggestedfix(re"C.0.", re"missing method Error", "quickfix", stub)
35+
}

0 commit comments

Comments
 (0)