@@ -15,9 +15,12 @@ import (
1515 "strings"
1616
1717 "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"
1920 "golang.org/x/tools/gopls/internal/fuzzy"
21+ "golang.org/x/tools/gopls/internal/util/moreiters"
2022 "golang.org/x/tools/internal/analysisinternal"
23+ "golang.org/x/tools/internal/astutil/cursor"
2124 "golang.org/x/tools/internal/typesinternal"
2225)
2326
@@ -27,105 +30,41 @@ var doc string
2730var Analyzer = & analysis.Analyzer {
2831 Name : "fillreturns" ,
2932 Doc : analysisinternal .MustExtractDoc (doc , "fillreturns" ),
33+ Requires : []* analysis.Analyzer {inspect .Analyzer },
3034 Run : run ,
3135 RunDespiteErrors : true ,
3236 URL : "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/fillreturns" ,
3337}
3438
3539func run (pass * analysis.Pass ) (interface {}, error ) {
40+ inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
3641 info := pass .TypesInfo
37- if info == nil {
38- return nil , fmt .Errorf ("nil TypeInfo" )
39- }
4042
4143outer:
4244 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
6447 }
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
8551 }
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
8855 }
8956
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
12163 }
12264 }
123- if outer == nil {
124- return nil , nil
125- }
65+ ret := curRet .Node ().(* ast.ReturnStmt )
12666
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.
12968 for _ , expr := range ret .Results {
13069 e , ok := expr .(* ast.CallExpr )
13170 if ! ok {
@@ -136,24 +75,47 @@ outer:
13675 }
13776 }
13877
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+
13998 // Duplicate the return values to track which values have been matched.
14099 remaining := make ([]ast.Expr , len (ret .Results ))
141100 copy (remaining , ret .Results )
142101
143- fixed := make ([]ast.Expr , len (enclosingFunc .Results .List ))
102+ fixed := make ([]ast.Expr , len (funcType .Results .List ))
144103
145104 // For each value in the return function declaration, find the leftmost element
146105 // in the return statement that has the desired type. If no such element exists,
147106 // fill in the missing value with the appropriate "zero" value.
148107 // Beware that type information may be incomplete.
149108 var retTyps []types.Type
150- for _ , ret := range enclosingFunc .Results .List {
109+ for _ , ret := range funcType .Results .List {
151110 retTyp := info .TypeOf (ret .Type )
152111 if retTyp == nil {
153112 return nil , nil
154113 }
155114 retTyps = append (retTyps , retTyp )
156115 }
116+
117+ curFile , _ := moreiters .First (curRet .Ancestors ((* ast .File )(nil )))
118+ file := curFile .Node ().(* ast.File )
157119 matches := analysisinternal .MatchingIdents (retTyps , file , ret .Pos (), info , pass .Pkg )
158120 qual := typesinternal .FileQualifier (file , pass .Pkg )
159121 for i , retTyp := range retTyps {
@@ -215,8 +177,8 @@ outer:
215177 }
216178
217179 pass .Report (analysis.Diagnostic {
218- Pos : typeErr . Pos ,
219- End : typeErrEndPos ,
180+ Pos : start ,
181+ End : end ,
220182 Message : typeErr .Msg ,
221183 SuggestedFixes : []analysis.SuggestedFix {{
222184 Message : "Fill in return values" ,
@@ -255,7 +217,7 @@ var wrongReturnNumRegexes = []*regexp.Regexp{
255217 regexp .MustCompile (`not enough return values` ),
256218}
257219
258- func FixesError (err types.Error ) bool {
220+ func fixesError (err types.Error ) bool {
259221 msg := strings .TrimSpace (err .Msg )
260222 for _ , rx := range wrongReturnNumRegexes {
261223 if rx .MatchString (msg ) {
@@ -264,3 +226,12 @@ func FixesError(err types.Error) bool {
264226 }
265227 return false
266228}
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