Skip to content

Commit 9310eb8

Browse files
DanielMorsinggopherbot
authored andcommitted
gopls: convert some users of astutil to Cursors
This started as just a conversion of createUndeclared, but following the transitive closure up and down the call path, I ended up converting a significant portion of the code, focusing mainly on the areas where TODOs had been left to update the code to cursor. Change-Id: I8d30683f9963c7aa8895734cade37d59fd29fb3a Reviewed-on: https://go-review.googlesource.com/c/tools/+/702355 Auto-Submit: Alan Donovan <adonovan@google.com> Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
1 parent c312a17 commit 9310eb8

File tree

9 files changed

+272
-399
lines changed

9 files changed

+272
-399
lines changed

gopls/internal/analysis/fillstruct/fillstruct.go

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ import (
2424
"unicode"
2525

2626
"golang.org/x/tools/go/analysis"
27-
"golang.org/x/tools/go/ast/astutil"
2827
"golang.org/x/tools/gopls/internal/cache"
2928
"golang.org/x/tools/gopls/internal/cache/parsego"
3029
"golang.org/x/tools/gopls/internal/fuzzy"
3130
"golang.org/x/tools/gopls/internal/util/safetoken"
3231
"golang.org/x/tools/internal/analysisinternal"
32+
"golang.org/x/tools/internal/moreiters"
3333
"golang.org/x/tools/internal/typeparams"
3434
"golang.org/x/tools/internal/typesinternal"
3535
)
@@ -133,24 +133,18 @@ const FixCategory = "fillstruct" // recognized by gopls ApplyFix
133133
// diagnostics produced by the Analyzer above.
134134
func SuggestedFix(cpkg *cache.Package, pgf *parsego.File, start, end token.Pos) (*token.FileSet, *analysis.SuggestedFix, error) {
135135
var (
136+
file = pgf.File
136137
fset = cpkg.FileSet()
137138
pkg = cpkg.Types()
138139
info = cpkg.TypesInfo()
139140
pos = start // don't use end
140141
)
141-
// TODO(adonovan): simplify, using Cursor.
142-
file := pgf.Cursor.Node().(*ast.File)
143-
path, _ := astutil.PathEnclosingInterval(file, pos, pos)
144-
if len(path) == 0 {
142+
cur, ok := pgf.Cursor.FindByPos(pos, pos)
143+
if !ok {
145144
return nil, nil, fmt.Errorf("no enclosing ast.Node")
146145
}
147-
var expr *ast.CompositeLit
148-
for _, n := range path {
149-
if node, ok := n.(*ast.CompositeLit); ok {
150-
expr = node
151-
break
152-
}
153-
}
146+
curCompLit, _ := moreiters.First(cur.Enclosing((*ast.CompositeLit)(nil)))
147+
expr := curCompLit.Node().(*ast.CompositeLit)
154148

155149
typ := info.TypeOf(expr)
156150
if typ == nil {

gopls/internal/golang/codeaction.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,8 @@ func quickFix(ctx context.Context, req *codeActionsRequest) error {
355355
// See [createUndeclared] for command implementation.
356356
case strings.HasPrefix(msg, "undeclared name: "),
357357
strings.HasPrefix(msg, "undefined: "):
358-
path, _ := astutil.PathEnclosingInterval(req.pgf.File, start, end)
359-
title := undeclaredFixTitle(path, msg)
358+
cur, _ := req.pgf.Cursor.FindByPos(start, end)
359+
title := undeclaredFixTitle(cur, msg)
360360
if title != "" {
361361
req.addApplyFixAction(title, fixCreateUndeclared, req.loc)
362362
}

gopls/internal/golang/extract.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ Outer:
293293
//
294294
// `x` is a free variable defined in the IfStmt, we should not insert
295295
// the extracted expression outside the IfStmt scope, instead, return an error.
296+
//
297+
// TODO(dmo): make this function take a Cursor and simplify
296298
func stmtToInsertVarBefore(path []ast.Node, variables []*variable) (ast.Stmt, error) {
297299
enclosingIndex := -1 // index in path of enclosing stmt
298300
for i, p := range path {

gopls/internal/golang/invertifcondition.go

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import (
1111
"strings"
1212

1313
"golang.org/x/tools/go/analysis"
14-
"golang.org/x/tools/go/ast/astutil"
1514
"golang.org/x/tools/go/ast/inspector"
1615
"golang.org/x/tools/gopls/internal/cache"
1716
"golang.org/x/tools/gopls/internal/cache/parsego"
1817
"golang.org/x/tools/gopls/internal/util/safetoken"
18+
"golang.org/x/tools/internal/moreiters"
1919
)
2020

2121
// invertIfCondition is a singleFileFixFunc that inverts an if/else statement
@@ -246,28 +246,22 @@ func invertAndOr(fset *token.FileSet, expr *ast.BinaryExpr, src []byte) ([]byte,
246246
// canInvertIfCondition reports whether we can do invert-if-condition on the
247247
// code in the given range.
248248
func canInvertIfCondition(curFile inspector.Cursor, start, end token.Pos) (*ast.IfStmt, bool, error) {
249-
file := curFile.Node().(*ast.File)
250-
// TODO(adonovan): simplify, using Cursor.
251-
path, _ := astutil.PathEnclosingInterval(file, start, end)
252-
for _, node := range path {
253-
stmt, isIfStatement := node.(*ast.IfStmt)
254-
if !isIfStatement {
255-
continue
256-
}
257-
258-
if stmt.Else == nil {
259-
// Can't invert conditions without else clauses
260-
return nil, false, fmt.Errorf("else clause required")
261-
}
262-
263-
if _, hasElseIf := stmt.Else.(*ast.IfStmt); hasElseIf {
264-
// Can't invert conditions with else-if clauses, unclear what that
265-
// would look like
266-
return nil, false, fmt.Errorf("else-if not supported")
267-
}
249+
curIf, _ := curFile.FindByPos(start, end)
250+
curIf, ok := moreiters.First(curIf.Enclosing((*ast.IfStmt)(nil)))
251+
if !ok {
252+
return nil, false, fmt.Errorf("not an if statement")
253+
}
254+
stmt := curIf.Node().(*ast.IfStmt)
255+
if stmt.Else == nil {
256+
// Can't invert conditions without else clauses
257+
return nil, false, fmt.Errorf("else clause required")
258+
}
268259

269-
return stmt, true, nil
260+
if _, hasElseIf := stmt.Else.(*ast.IfStmt); hasElseIf {
261+
// Can't invert conditions with else-if clauses, unclear what that
262+
// would look like
263+
return nil, false, fmt.Errorf("else-if not supported")
270264
}
271265

272-
return nil, false, fmt.Errorf("not an if statement")
266+
return stmt, true, nil
273267
}

gopls/internal/golang/lines.go

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
"strings"
1818

1919
"golang.org/x/tools/go/analysis"
20-
"golang.org/x/tools/go/ast/astutil"
20+
"golang.org/x/tools/go/ast/edge"
2121
"golang.org/x/tools/go/ast/inspector"
2222
"golang.org/x/tools/gopls/internal/cache"
2323
"golang.org/x/tools/gopls/internal/cache/parsego"
@@ -171,39 +171,34 @@ func processLines(fset *token.FileSet, items []ast.Node, comments []*ast.Comment
171171

172172
// findSplitJoinTarget returns the first curly bracket/parens that encloses the current cursor.
173173
func findSplitJoinTarget(fset *token.FileSet, curFile inspector.Cursor, src []byte, start, end token.Pos) (itemType string, items []ast.Node, comments []*ast.CommentGroup, indent string, open, close token.Pos) {
174-
isCursorInside := func(nodePos, nodeEnd token.Pos) bool {
175-
return nodePos < start && end < nodeEnd
176-
}
177-
178-
file := curFile.Node().(*ast.File)
179-
// TODO(adonovan): simplify, using Cursor.
180174

181175
findTarget := func() (targetType string, target ast.Node, open, close token.Pos) {
182-
path, _ := astutil.PathEnclosingInterval(file, start, end)
183-
for _, node := range path {
184-
switch node := node.(type) {
185-
case *ast.FuncType:
186-
// params or results of func signature
187-
// Note:
188-
// - each ast.Field (e.g. "x, y, z int") is considered a single item.
189-
// - splitting Params and Results lists is not usually good style.
190-
if p := node.Params; isCursorInside(p.Opening, p.Closing) {
191-
return "parameters", p, p.Opening, p.Closing
192-
}
193-
if r := node.Results; r != nil && isCursorInside(r.Opening, r.Closing) {
194-
return "results", r, r.Opening, r.Closing
195-
}
196-
case *ast.CallExpr: // f(a, b, c)
197-
if isCursorInside(node.Lparen, node.Rparen) {
198-
return "arguments", node, node.Lparen, node.Rparen
199-
}
200-
case *ast.CompositeLit: // T{a, b, c}
201-
if isCursorInside(node.Lbrace, node.Rbrace) {
202-
return "elements", node, node.Lbrace, node.Rbrace
176+
cur, _ := curFile.FindByPos(start, end)
177+
for cur := range cur.Enclosing() {
178+
// TODO: do cur = enclosingUnparen(cur) first, once CL 701035 lands.
179+
ek, _ := cur.ParentEdge()
180+
switch ek {
181+
// params or results of func signature
182+
// Note:
183+
// - each ast.Field (e.g. "x, y, z int") is considered a single item.
184+
// - splitting Params and Results lists is not usually good style.
185+
case edge.FuncType_Params:
186+
p := cur.Node().(*ast.FieldList)
187+
return "parameters", p, p.Opening, p.Closing
188+
case edge.FuncType_Results:
189+
r := cur.Node().(*ast.FieldList)
190+
if !r.Opening.IsValid() {
191+
continue
203192
}
193+
return "results", r, r.Opening, r.Closing
194+
case edge.CallExpr_Args: // f(a, b, c)
195+
node := cur.Parent().Node().(*ast.CallExpr)
196+
return "arguments", node, node.Lparen, node.Rparen
197+
case edge.CompositeLit_Elts: // T{a, b, c}
198+
node := cur.Parent().Node().(*ast.CompositeLit)
199+
return "elements", node, node.Lbrace, node.Rbrace
204200
}
205201
}
206-
207202
return "", nil, 0, 0
208203
}
209204

@@ -240,6 +235,7 @@ func findSplitJoinTarget(fset *token.FileSet, curFile inspector.Cursor, src []by
240235
}
241236

242237
// preserve comments separately as it's not part of the targetNode AST.
238+
file := curFile.Node().(*ast.File)
243239
for _, cg := range file.Comments {
244240
if open <= cg.Pos() && cg.Pos() < close {
245241
comments = append(comments, cg)

gopls/internal/golang/stubmethods/stubcalledfunc.go

Lines changed: 54 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import (
1313
"strings"
1414
"unicode"
1515

16-
"golang.org/x/tools/go/ast/astutil"
16+
"golang.org/x/tools/go/ast/inspector"
1717
"golang.org/x/tools/gopls/internal/cache/parsego"
1818
"golang.org/x/tools/gopls/internal/util/typesutil"
19+
"golang.org/x/tools/internal/moreiters"
1920
"golang.org/x/tools/internal/typesinternal"
2021
)
2122

@@ -31,71 +32,73 @@ type CallStubInfo struct {
3132
After types.Object // decl after which to insert the new decl
3233
pointer bool
3334
info *types.Info
34-
path []ast.Node // path enclosing the CallExpr
35+
curCall inspector.Cursor // cursor to the CallExpr
3536
}
3637

3738
// GetCallStubInfo extracts necessary information to generate a method definition from
3839
// a CallExpr.
3940
func GetCallStubInfo(fset *token.FileSet, info *types.Info, pgf *parsego.File, start, end token.Pos) *CallStubInfo {
40-
// TODO(adonovan): simplify, using pgf.Cursor.
41-
path, _ := astutil.PathEnclosingInterval(pgf.File, start, end)
42-
for i, n := range path {
43-
switch n := n.(type) {
44-
case *ast.CallExpr:
45-
s, ok := n.Fun.(*ast.SelectorExpr)
46-
// TODO: support generating stub functions in the same way.
47-
if !ok {
48-
return nil
49-
}
41+
callCur, _ := pgf.Cursor.FindByPos(start, end)
42+
callCur, ok := moreiters.First(callCur.Enclosing((*ast.CallExpr)(nil)))
43+
if !ok {
44+
return nil
45+
}
46+
call := callCur.Node().(*ast.CallExpr)
47+
s, ok := call.Fun.(*ast.SelectorExpr)
48+
// TODO: support generating stub functions in the same way.
49+
if !ok {
50+
return nil
51+
}
5052

51-
// If recvExpr is a package name, compiler error would be
52-
// e.g., "undefined: http.bar", thus will not hit this code path.
53-
recvExpr := s.X
54-
recvType, pointer := concreteType(recvExpr, info)
53+
// If recvExpr is a package name, compiler error would be
54+
// e.g., "undefined: http.bar", thus will not hit this code path.
55+
recvExpr := s.X
56+
recvType, pointer := concreteType(recvExpr, info)
5557

56-
if recvType == nil || recvType.Obj().Pkg() == nil {
57-
return nil
58-
}
58+
if recvType == nil || recvType.Obj().Pkg() == nil {
59+
return nil
60+
}
5961

60-
// A method of a function-local type cannot be stubbed
61-
// since there's nowhere to put the methods.
62-
recv := recvType.Obj()
63-
if recv.Parent() != recv.Pkg().Scope() {
64-
return nil
65-
}
62+
// A method of a function-local type cannot be stubbed
63+
// since there's nowhere to put the methods.
64+
recv := recvType.Obj()
65+
if recv.Parent() != recv.Pkg().Scope() {
66+
return nil
67+
}
6668

67-
after := types.Object(recv)
68-
// If the enclosing function declaration is a method declaration,
69-
// and matches the receiver type of the diagnostic,
70-
// insert after the enclosing method.
71-
decl, ok := path[len(path)-2].(*ast.FuncDecl)
72-
if ok && decl.Recv != nil {
73-
if len(decl.Recv.List) != 1 {
74-
return nil
75-
}
76-
mrt := info.TypeOf(decl.Recv.List[0].Type)
77-
if mrt != nil && types.Identical(types.Unalias(typesinternal.Unpointer(mrt)), recv.Type()) {
78-
after = info.ObjectOf(decl.Name)
79-
}
80-
}
81-
return &CallStubInfo{
82-
Fset: fset,
83-
Receiver: recvType,
84-
MethodName: s.Sel.Name,
85-
After: after,
86-
pointer: pointer,
87-
path: path[i:],
88-
info: info,
89-
}
69+
after := types.Object(recv)
70+
// If the enclosing function declaration is a method declaration,
71+
// and matches the receiver type of the diagnostic,
72+
// insert after the enclosing method.
73+
var decl *ast.FuncDecl
74+
declCur, ok := moreiters.First(callCur.Enclosing((*ast.FuncDecl)(nil)))
75+
if ok {
76+
decl = declCur.Node().(*ast.FuncDecl)
77+
}
78+
if decl != nil && decl.Recv != nil {
79+
if len(decl.Recv.List) != 1 {
80+
return nil
81+
}
82+
mrt := info.TypeOf(decl.Recv.List[0].Type)
83+
if mrt != nil && types.Identical(types.Unalias(typesinternal.Unpointer(mrt)), recv.Type()) {
84+
after = info.ObjectOf(decl.Name)
9085
}
9186
}
92-
return nil
87+
return &CallStubInfo{
88+
Fset: fset,
89+
Receiver: recvType,
90+
MethodName: s.Sel.Name,
91+
After: after,
92+
pointer: pointer,
93+
curCall: callCur,
94+
info: info,
95+
}
9396
}
9497

9598
// Emit writes to out the missing method based on type info of si.Receiver and CallExpr.
9699
func (si *CallStubInfo) Emit(out *bytes.Buffer, qual types.Qualifier) error {
97100
params := si.collectParams()
98-
rets := typesutil.TypesFromContext(si.info, si.path, si.path[0].Pos())
101+
rets := typesutil.TypesFromContext(si.info, si.curCall)
99102
recv := si.Receiver.Obj()
100103
// Pointer receiver?
101104
var star string
@@ -180,7 +183,7 @@ func (si *CallStubInfo) collectParams() []param {
180183
params = append(params, p)
181184
}
182185

183-
args := si.path[0].(*ast.CallExpr).Args
186+
args := si.curCall.Node().(*ast.CallExpr).Args
184187
for _, arg := range args {
185188
t := si.info.TypeOf(arg)
186189
switch t := t.(type) {

0 commit comments

Comments
 (0)