Skip to content

Commit f64c0f4

Browse files
committed
internal/lsp/analysis/fillreturns: update fillreturns for new type errs
The error messages produced by go/types related to an incorrect number of return values have changed, and have been repositioned. Update the fillreturns analyzer to handle the Go 1.18 form of these errors. Ideally we'd use error codes here, but can't because they are not forwarded by go/packages. Added a TODO to revisit when error codes are exposed. Change-Id: I4c594fd2fd1cb7423f37ff9c6a57e9490dcbc2a4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/367714 Trust: Robert Findley <[email protected]> Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Suzy Mueller <[email protected]>
1 parent e212aff commit f64c0f4

File tree

7 files changed

+100
-80
lines changed

7 files changed

+100
-80
lines changed

gopls/doc/analyzers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ check for constraints that could be simplified to "any"
578578

579579
## **fillreturns**
580580

581-
suggested fixes for "wrong number of return values (want %d, got %d)"
581+
suggest fixes for errors due to an incorrect number of return values
582582

583583
This checker provides suggested fixes for type errors of the
584584
type "wrong number of return values (want %d, got %d)". For example:

gopls/internal/regtest/misc/fix_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ func Foo() {
6262
}
6363

6464
func TestFillReturns(t *testing.T) {
65-
t.Skip("temporarily skipped until CL 367196 is submitted")
6665
const files = `
6766
-- go.mod --
6867
module mod.com
@@ -79,7 +78,8 @@ func Foo() error {
7978
env.OpenFile("main.go")
8079
var d protocol.PublishDiagnosticsParams
8180
env.Await(OnceMet(
82-
env.DiagnosticAtRegexpWithMessage("main.go", `return`, "wrong number of return values"),
81+
// The error message here changed in 1.18; "return values" covers both forms.
82+
env.DiagnosticAtRegexpWithMessage("main.go", `return`, "return values"),
8383
ReadDiagnostics("main.go", &d),
8484
))
8585
codeActions := env.CodeAction("main.go", d.Diagnostics)

internal/lsp/analysis/fillreturns/fillreturns.go

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"go/format"
1515
"go/types"
1616
"regexp"
17-
"strconv"
1817
"strings"
1918

2019
"golang.org/x/tools/go/analysis"
@@ -23,7 +22,7 @@ import (
2322
"golang.org/x/tools/internal/typeparams"
2423
)
2524

26-
const Doc = `suggested fixes for "wrong number of return values (want %d, got %d)"
25+
const Doc = `suggest fixes for errors due to an incorrect number of return values
2726
2827
This checker provides suggested fixes for type errors of the
2928
type "wrong number of return values (want %d, got %d)". For example:
@@ -46,8 +45,6 @@ var Analyzer = &analysis.Analyzer{
4645
RunDespiteErrors: true,
4746
}
4847

49-
var wrongReturnNumRegex = regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`)
50-
5148
func run(pass *analysis.Pass) (interface{}, error) {
5249
info := pass.TypesInfo
5350
if info == nil {
@@ -58,7 +55,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
5855
outer:
5956
for _, typeErr := range errors {
6057
// Filter out the errors that are not relevant to this analyzer.
61-
if !FixesError(typeErr.Msg) {
58+
if !FixesError(typeErr) {
6259
continue
6360
}
6461
var file *ast.File
@@ -79,20 +76,32 @@ outer:
7976
}
8077
typeErrEndPos := analysisinternal.TypeErrorEndPos(pass.Fset, buf.Bytes(), typeErr.Pos)
8178

79+
// TODO(rfindley): much of the error handling code below returns, when it
80+
// should probably continue.
81+
8282
// Get the path for the relevant range.
8383
path, _ := astutil.PathEnclosingInterval(file, typeErr.Pos, typeErrEndPos)
8484
if len(path) == 0 {
8585
return nil, nil
8686
}
87-
// Check to make sure the node of interest is a ReturnStmt.
88-
ret, ok := path[0].(*ast.ReturnStmt)
89-
if !ok {
87+
88+
// Find the enclosing return statement.
89+
var ret *ast.ReturnStmt
90+
var retIdx int
91+
for i, n := range path {
92+
if r, ok := n.(*ast.ReturnStmt); ok {
93+
ret = r
94+
retIdx = i
95+
break
96+
}
97+
}
98+
if ret == nil {
9099
return nil, nil
91100
}
92101

93102
// Get the function type that encloses the ReturnStmt.
94103
var enclosingFunc *ast.FuncType
95-
for _, n := range path {
104+
for _, n := range path[retIdx+1:] {
96105
switch node := n.(type) {
97106
case *ast.FuncLit:
98107
enclosingFunc = node.Type
@@ -109,7 +118,7 @@ outer:
109118

110119
// Skip any generic enclosing functions, since type parameters don't
111120
// have 0 values.
112-
// TODO(rstambler): We should be able to handle this if the return
121+
// TODO(rfindley): We should be able to handle this if the return
113122
// values are all concrete types.
114123
if tparams := typeparams.ForFuncType(enclosingFunc); tparams != nil && tparams.NumFields() > 0 {
115124
return nil, nil
@@ -127,7 +136,8 @@ outer:
127136
return nil, nil
128137
}
129138

130-
// Skip any return statements that contain function calls with multiple return values.
139+
// Skip any return statements that contain function calls with multiple
140+
// return values.
131141
for _, expr := range ret.Results {
132142
e, ok := expr.(*ast.CallExpr)
133143
if !ok {
@@ -244,16 +254,23 @@ func matchingTypes(want, got types.Type) bool {
244254
return types.AssignableTo(want, got) || types.ConvertibleTo(want, got)
245255
}
246256

247-
func FixesError(msg string) bool {
248-
matches := wrongReturnNumRegex.FindStringSubmatch(strings.TrimSpace(msg))
249-
if len(matches) < 3 {
250-
return false
251-
}
252-
if _, err := strconv.Atoi(matches[1]); err != nil {
253-
return false
254-
}
255-
if _, err := strconv.Atoi(matches[2]); err != nil {
256-
return false
257+
// Error messages have changed across Go versions. These regexps capture recent
258+
// incarnations.
259+
//
260+
// TODO(rfindley): once error codes are exported and exposed via go/packages,
261+
// use error codes rather than string matching here.
262+
var wrongReturnNumRegexes = []*regexp.Regexp{
263+
regexp.MustCompile(`wrong number of return values \(want (\d+), got (\d+)\)`),
264+
regexp.MustCompile(`too many return values`),
265+
regexp.MustCompile(`not enough return values`),
266+
}
267+
268+
func FixesError(err types.Error) bool {
269+
msg := strings.TrimSpace(err.Msg)
270+
for _, rx := range wrongReturnNumRegexes {
271+
if rx.MatchString(msg) {
272+
return true
273+
}
257274
}
258-
return true
275+
return false
259276
}

internal/lsp/analysis/fillreturns/fillreturns_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
)
1414

1515
func Test(t *testing.T) {
16-
t.Skip("temporarily skipped until CL 367196 is submitted and this test is adjusted accordingly")
1716
testdata := analysistest.TestData()
1817
tests := []string{"a"}
1918
if typeparams.Enabled {

internal/lsp/analysis/fillreturns/testdata/src/a/a.go

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -25,80 +25,82 @@ func x() error {
2525
return errors.New("foo")
2626
}
2727

28+
// The error messages below changed in 1.18; "return values" covers both forms.
29+
2830
func b() (string, int, error) {
29-
return "", errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
31+
return "", errors.New("foo") // want "return values"
3032
}
3133

3234
func c() (string, int, error) {
33-
return 7, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
35+
return 7, errors.New("foo") // want "return values"
3436
}
3537

3638
func d() (string, int, error) {
37-
return "", 7 // want "wrong number of return values \\(want 3, got 2\\)"
39+
return "", 7 // want "return values"
3840
}
3941

4042
func e() (T, error, *bool) {
41-
return (z(http.ListenAndServe))("", nil) // want "wrong number of return values \\(want 3, got 1\\)"
43+
return (z(http.ListenAndServe))("", nil) // want "return values"
4244
}
4345

4446
func preserveLeft() (int, int, error) {
45-
return 1, errors.New("foo") // want "wrong number of return values \\(want 3, got 2\\)"
47+
return 1, errors.New("foo") // want "return values"
4648
}
4749

4850
func matchValues() (int, error, string) {
49-
return errors.New("foo"), 3 // want "wrong number of return values \\(want 3, got 2\\)"
51+
return errors.New("foo"), 3 // want "return values"
5052
}
5153

5254
func preventDataOverwrite() (int, string) {
53-
return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
55+
return errors.New("foo") // want "return values"
5456
}
5557

5658
func closure() (string, error) {
5759
_ = func() (int, error) {
58-
return // want "wrong number of return values \\(want 2, got 0\\)"
60+
return // want "return values"
5961
}
60-
return // want "wrong number of return values \\(want 2, got 0\\)"
62+
return // want "return values"
6163
}
6264

6365
func basic() (uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64, complex64, complex128, byte, rune, uint, int, uintptr, string, bool, error) {
64-
return // want "wrong number of return values \\(want 20, got 0\\)"
66+
return // want "return values"
6567
}
6668

6769
func complex() (*int, []int, [2]int, map[int]int) {
68-
return // want "wrong number of return values \\(want 4, got 0\\)"
70+
return // want "return values"
6971
}
7072

7173
func structsAndInterfaces() (T, url.URL, T1, I, I1, io.Reader, Client, ast2.Stmt) {
72-
return // want "wrong number of return values \\(want 8, got 0\\)"
74+
return // want "return values"
7375
}
7476

7577
func m() (int, error) {
7678
if 1 == 2 {
77-
return // want "wrong number of return values \\(want 2, got 0\\)"
79+
return // want "return values"
7880
} else if 1 == 3 {
79-
return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
81+
return errors.New("foo") // want "return values"
8082
} else {
81-
return 1 // want "wrong number of return values \\(want 2, got 1\\)"
83+
return 1 // want "return values"
8284
}
83-
return // want "wrong number of return values \\(want 2, got 0\\)"
85+
return // want "return values"
8486
}
8587

8688
func convertibleTypes() (ast2.Expr, int) {
87-
return &ast2.ArrayType{} // want "wrong number of return values \\(want 2, got 1\\)"
89+
return &ast2.ArrayType{} // want "return values"
8890
}
8991

9092
func assignableTypes() (map[string]int, int) {
9193
type X map[string]int
9294
var x X
93-
return x // want "wrong number of return values \\(want 2, got 1\\)"
95+
return x // want "return values"
9496
}
9597

9698
func interfaceAndError() (I, int) {
97-
return errors.New("foo") // want "wrong number of return values \\(want 2, got 1\\)"
99+
return errors.New("foo") // want "return values"
98100
}
99101

100102
func funcOneReturn() (string, error) {
101-
return strconv.Itoa(1) // want "wrong number of return values \\(want 2, got 1\\)"
103+
return strconv.Itoa(1) // want "return values"
102104
}
103105

104106
func funcMultipleReturn() (int, error, string) {
@@ -110,16 +112,16 @@ func localFuncMultipleReturn() (string, int, error, string) {
110112
}
111113

112114
func multipleUnused() (int, string, string, string) {
113-
return 3, 4, 5 // want "wrong number of return values \\(want 4, got 3\\)"
115+
return 3, 4, 5 // want "return values"
114116
}
115117

116118
func gotTooMany() int {
117119
if true {
118-
return 0, "" // want "wrong number of return values \\(want 1, got 2\\)"
120+
return 0, "" // want "return values"
119121
} else {
120-
return 1, 0, nil // want "wrong number of return values \\(want 1, got 3\\)"
122+
return 1, 0, nil // want "return values"
121123
}
122-
return 0, 5, false // want "wrong number of return values \\(want 1, got 3\\)"
124+
return 0, 5, false // want "return values"
123125
}
124126

125127
func fillVars() (int, string, ast.Node, bool, error) {
@@ -128,10 +130,10 @@ func fillVars() (int, string, ast.Node, bool, error) {
128130
var t bool
129131
if true {
130132
err := errors.New("fail")
131-
return // want "wrong number of return values \\(want 5, got 0\\)"
133+
return // want "return values"
132134
}
133135
n := ast.NewIdent("ident")
134136
int := 3
135137
var b bool
136-
return "" // want "wrong number of return values \\(want 5, got 1\\)"
138+
return "" // want "return values"
137139
}

0 commit comments

Comments
 (0)