@@ -15,9 +15,12 @@ import (
15
15
"strings"
16
16
17
17
"golang.org/x/tools/go/analysis"
18
- "golang.org/x/tools/go/ast/astutil"
18
+ "golang.org/x/tools/go/analysis/passes/inspect"
19
+ "golang.org/x/tools/go/ast/inspector"
19
20
"golang.org/x/tools/gopls/internal/fuzzy"
21
+ "golang.org/x/tools/gopls/internal/util/moreiters"
20
22
"golang.org/x/tools/internal/analysisinternal"
23
+ "golang.org/x/tools/internal/astutil/cursor"
21
24
"golang.org/x/tools/internal/typesinternal"
22
25
)
23
26
@@ -27,105 +30,41 @@ var doc string
27
30
var Analyzer = & analysis.Analyzer {
28
31
Name : "fillreturns" ,
29
32
Doc : analysisinternal .MustExtractDoc (doc , "fillreturns" ),
33
+ Requires : []* analysis.Analyzer {inspect .Analyzer },
30
34
Run : run ,
31
35
RunDespiteErrors : true ,
32
36
URL : "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/fillreturns" ,
33
37
}
34
38
35
39
func run (pass * analysis.Pass ) (interface {}, error ) {
40
+ inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
36
41
info := pass .TypesInfo
37
- if info == nil {
38
- return nil , fmt .Errorf ("nil TypeInfo" )
39
- }
40
42
41
43
outer:
42
44
for _ , typeErr := range pass .TypeErrors {
43
- // Filter out the errors that are not relevant to this analyzer.
44
- if ! FixesError (typeErr ) {
45
- continue
46
- }
47
- var file * ast.File
48
- for _ , f := range pass .Files {
49
- if f .FileStart <= typeErr .Pos && typeErr .Pos <= f .FileEnd {
50
- file = f
51
- break
52
- }
53
- }
54
- if file == nil {
55
- continue
56
- }
57
-
58
- // Get the end position of the error.
59
- // (This heuristic assumes that the buffer is formatted,
60
- // at least up to the end position of the error.)
61
- var buf bytes.Buffer
62
- if err := format .Node (& buf , pass .Fset , file ); err != nil {
63
- continue
45
+ if ! fixesError (typeErr ) {
46
+ continue // irrelevant type error
64
47
}
65
- typeErrEndPos := analysisinternal .TypeErrorEndPos (pass .Fset , buf .Bytes (), typeErr .Pos )
66
-
67
- // TODO(rfindley): much of the error handling code below returns, when it
68
- // should probably continue.
69
-
70
- // Get the path for the relevant range.
71
- path , _ := astutil .PathEnclosingInterval (file , typeErr .Pos , typeErrEndPos )
72
- if len (path ) == 0 {
73
- return nil , nil
74
- }
75
-
76
- // Find the enclosing return statement.
77
- var ret * ast.ReturnStmt
78
- var retIdx int
79
- for i , n := range path {
80
- if r , ok := n .(* ast.ReturnStmt ); ok {
81
- ret = r
82
- retIdx = i
83
- break
84
- }
48
+ _ , start , end , ok := typesinternal .ErrorCodeStartEnd (typeErr )
49
+ if ! ok {
50
+ continue // no position information
85
51
}
86
- if ret == nil {
87
- return nil , nil
52
+ curErr , ok := cursor .Root (inspect ).FindPos (start , end )
53
+ if ! ok {
54
+ continue // can't find node
88
55
}
89
56
90
- // Get the function type that encloses the ReturnStmt.
91
- var enclosingFunc * ast.FuncType
92
- for _ , n := range path [retIdx + 1 :] {
93
- switch node := n .(type ) {
94
- case * ast.FuncLit :
95
- enclosingFunc = node .Type
96
- case * ast.FuncDecl :
97
- enclosingFunc = node .Type
98
- }
99
- if enclosingFunc != nil {
100
- break
101
- }
102
- }
103
- if enclosingFunc == nil || enclosingFunc .Results == nil {
104
- continue
105
- }
106
-
107
- // Skip any generic enclosing functions, since type parameters don't
108
- // have 0 values.
109
- // TODO(rfindley): We should be able to handle this if the return
110
- // values are all concrete types.
111
- if tparams := enclosingFunc .TypeParams ; tparams != nil && tparams .NumFields () > 0 {
112
- return nil , nil
113
- }
114
-
115
- // Find the function declaration that encloses the ReturnStmt.
116
- var outer * ast.FuncDecl
117
- for _ , p := range path {
118
- if p , ok := p .(* ast.FuncDecl ); ok {
119
- outer = p
120
- break
57
+ // Find cursor for enclosing return statement (which may be curErr itself).
58
+ curRet := curErr
59
+ if _ , ok := curRet .Node ().(* ast.ReturnStmt ); ! ok {
60
+ curRet , ok = moreiters .First (curErr .Ancestors ((* ast .ReturnStmt )(nil )))
61
+ if ! ok {
62
+ continue // no enclosing return
121
63
}
122
64
}
123
- if outer == nil {
124
- return nil , nil
125
- }
65
+ ret := curRet .Node ().(* ast.ReturnStmt )
126
66
127
- // Skip any return statements that contain function calls with multiple
128
- // return values.
67
+ // Skip if any return argument is a tuple-valued function call.
129
68
for _ , expr := range ret .Results {
130
69
e , ok := expr .(* ast.CallExpr )
131
70
if ! ok {
@@ -136,24 +75,47 @@ outer:
136
75
}
137
76
}
138
77
78
+ // Get type of innermost enclosing function.
79
+ var funcType * ast.FuncType
80
+ curFunc , _ := enclosingFunc (curRet ) // can't fail
81
+ switch fn := curFunc .Node ().(type ) {
82
+ case * ast.FuncLit :
83
+ funcType = fn .Type
84
+ case * ast.FuncDecl :
85
+ funcType = fn .Type
86
+
87
+ // Skip generic functions since type parameters don't have zero values.
88
+ // TODO(rfindley): We should be able to handle this if the return
89
+ // values are all concrete types.
90
+ if funcType .TypeParams .NumFields () > 0 {
91
+ continue
92
+ }
93
+ }
94
+ if funcType .Results == nil {
95
+ continue
96
+ }
97
+
139
98
// Duplicate the return values to track which values have been matched.
140
99
remaining := make ([]ast.Expr , len (ret .Results ))
141
100
copy (remaining , ret .Results )
142
101
143
- fixed := make ([]ast.Expr , len (enclosingFunc .Results .List ))
102
+ fixed := make ([]ast.Expr , len (funcType .Results .List ))
144
103
145
104
// For each value in the return function declaration, find the leftmost element
146
105
// in the return statement that has the desired type. If no such element exists,
147
106
// fill in the missing value with the appropriate "zero" value.
148
107
// Beware that type information may be incomplete.
149
108
var retTyps []types.Type
150
- for _ , ret := range enclosingFunc .Results .List {
109
+ for _ , ret := range funcType .Results .List {
151
110
retTyp := info .TypeOf (ret .Type )
152
111
if retTyp == nil {
153
112
return nil , nil
154
113
}
155
114
retTyps = append (retTyps , retTyp )
156
115
}
116
+
117
+ curFile , _ := moreiters .First (curRet .Ancestors ((* ast .File )(nil )))
118
+ file := curFile .Node ().(* ast.File )
157
119
matches := analysisinternal .MatchingIdents (retTyps , file , ret .Pos (), info , pass .Pkg )
158
120
qual := typesinternal .FileQualifier (file , pass .Pkg )
159
121
for i , retTyp := range retTyps {
@@ -215,8 +177,8 @@ outer:
215
177
}
216
178
217
179
pass .Report (analysis.Diagnostic {
218
- Pos : typeErr . Pos ,
219
- End : typeErrEndPos ,
180
+ Pos : start ,
181
+ End : end ,
220
182
Message : typeErr .Msg ,
221
183
SuggestedFixes : []analysis.SuggestedFix {{
222
184
Message : "Fill in return values" ,
@@ -255,7 +217,7 @@ var wrongReturnNumRegexes = []*regexp.Regexp{
255
217
regexp .MustCompile (`not enough return values` ),
256
218
}
257
219
258
- func FixesError (err types.Error ) bool {
220
+ func fixesError (err types.Error ) bool {
259
221
msg := strings .TrimSpace (err .Msg )
260
222
for _ , rx := range wrongReturnNumRegexes {
261
223
if rx .MatchString (msg ) {
@@ -264,3 +226,12 @@ func FixesError(err types.Error) bool {
264
226
}
265
227
return false
266
228
}
229
+
230
+ // enclosingFunc returns the cursor for the innermost Func{Decl,Lit}
231
+ // that encloses c, if any.
232
+ func enclosingFunc (c cursor.Cursor ) (cursor.Cursor , bool ) {
233
+ for curAncestor := range c .Ancestors ((* ast .FuncDecl )(nil ), (* ast .FuncLit )(nil )) {
234
+ return curAncestor , true
235
+ }
236
+ return cursor.Cursor {}, false
237
+ }
0 commit comments