gopls/internal/lsp: stop using ast.Ident.Obj
This change eliminates the last two uses of go/ast
identifier resolution in gopls (outside of analysis,
which is implicitly committed to it).
This will allow us to skip ast-based object resolution
during parsing, an optimization.
Change-Id: I15270e764737456c780a8b67681503fdc08c096d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/473315
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/gopls/internal/lsp/semantic.go b/gopls/internal/lsp/semantic.go
index a3a1bca..d26c10f 100644
--- a/gopls/internal/lsp/semantic.go
+++ b/gopls/internal/lsp/semantic.go
@@ -524,7 +524,7 @@
default:
// can't happen
if use == nil {
- msg := fmt.Sprintf("%#v/%#v %#v %#v", x, x.Obj, e.ti.Defs[x], e.ti.Uses[x])
+ msg := fmt.Sprintf("%#v %#v %#v", x, e.ti.Defs[x], e.ti.Uses[x])
e.unexpected(msg)
}
if use.Type() != nil {
diff --git a/gopls/internal/lsp/source/highlight.go b/gopls/internal/lsp/source/highlight.go
index e2f6c84..6438f03 100644
--- a/gopls/internal/lsp/source/highlight.go
+++ b/gopls/internal/lsp/source/highlight.go
@@ -81,22 +81,26 @@
highlightFuncControlFlow(path, result)
highlightIdentifier(node, file, info, result)
case *ast.ForStmt, *ast.RangeStmt:
- highlightLoopControlFlow(path, result)
+ highlightLoopControlFlow(path, info, result)
case *ast.SwitchStmt:
- highlightSwitchFlow(path, result)
+ highlightSwitchFlow(path, info, result)
case *ast.BranchStmt:
// BREAK can exit a loop, switch or select, while CONTINUE exit a loop so
// these need to be handled separately. They can also be embedded in any
// other loop/switch/select if they have a label. TODO: add support for
// GOTO and FALLTHROUGH as well.
- if node.Label != nil {
- highlightLabeledFlow(node, result)
- } else {
- switch node.Tok {
- case token.BREAK:
- highlightUnlabeledBreakFlow(path, result)
- case token.CONTINUE:
- highlightLoopControlFlow(path, result)
+ switch node.Tok {
+ case token.BREAK:
+ if node.Label != nil {
+ highlightLabeledFlow(path, info, node, result)
+ } else {
+ highlightUnlabeledBreakFlow(path, info, result)
+ }
+ case token.CONTINUE:
+ if node.Label != nil {
+ highlightLabeledFlow(path, info, node, result)
+ } else {
+ highlightLoopControlFlow(path, info, result)
}
}
default:
@@ -222,15 +226,16 @@
})
}
-func highlightUnlabeledBreakFlow(path []ast.Node, result map[posRange]struct{}) {
+// highlightUnlabeledBreakFlow highlights the innermost enclosing for/range/switch or swlect
+func highlightUnlabeledBreakFlow(path []ast.Node, info *types.Info, result map[posRange]struct{}) {
// Reverse walk the path until we find closest loop, select, or switch.
for _, n := range path {
switch n.(type) {
case *ast.ForStmt, *ast.RangeStmt:
- highlightLoopControlFlow(path, result)
+ highlightLoopControlFlow(path, info, result)
return // only highlight the innermost statement
case *ast.SwitchStmt:
- highlightSwitchFlow(path, result)
+ highlightSwitchFlow(path, info, result)
return
case *ast.SelectStmt:
// TODO: add highlight when breaking a select.
@@ -239,20 +244,23 @@
}
}
-func highlightLabeledFlow(node *ast.BranchStmt, result map[posRange]struct{}) {
- obj := node.Label.Obj
- if obj == nil || obj.Decl == nil {
+// highlightLabeledFlow highlights the enclosing labeled for, range,
+// or switch statement denoted by a labeled break or continue stmt.
+func highlightLabeledFlow(path []ast.Node, info *types.Info, stmt *ast.BranchStmt, result map[posRange]struct{}) {
+ use := info.Uses[stmt.Label]
+ if use == nil {
return
}
- label, ok := obj.Decl.(*ast.LabeledStmt)
- if !ok {
- return
- }
- switch label.Stmt.(type) {
- case *ast.ForStmt, *ast.RangeStmt:
- highlightLoopControlFlow([]ast.Node{label.Stmt, label}, result)
- case *ast.SwitchStmt:
- highlightSwitchFlow([]ast.Node{label.Stmt, label}, result)
+ for _, n := range path {
+ if label, ok := n.(*ast.LabeledStmt); ok && info.Defs[label.Label] == use {
+ switch label.Stmt.(type) {
+ case *ast.ForStmt, *ast.RangeStmt:
+ highlightLoopControlFlow([]ast.Node{label.Stmt, label}, info, result)
+ case *ast.SwitchStmt:
+ highlightSwitchFlow([]ast.Node{label.Stmt, label}, info, result)
+ }
+ return
+ }
}
}
@@ -265,7 +273,7 @@
return nil
}
-func highlightLoopControlFlow(path []ast.Node, result map[posRange]struct{}) {
+func highlightLoopControlFlow(path []ast.Node, info *types.Info, result map[posRange]struct{}) {
var loop ast.Node
var loopLabel *ast.Ident
stmtLabel := labelFor(path)
@@ -305,7 +313,7 @@
if !ok {
return true
}
- if b.Label == nil || labelDecl(b.Label) == loopLabel {
+ if b.Label == nil || info.Uses[b.Label] == info.Defs[loopLabel] {
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
}
return true
@@ -336,14 +344,14 @@
return true
}
// statement with labels that matches the loop
- if b.Label != nil && labelDecl(b.Label) == loopLabel {
+ if b.Label != nil && info.Uses[b.Label] == info.Defs[loopLabel] {
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
}
return true
})
}
-func highlightSwitchFlow(path []ast.Node, result map[posRange]struct{}) {
+func highlightSwitchFlow(path []ast.Node, info *types.Info, result map[posRange]struct{}) {
var switchNode ast.Node
var switchNodeLabel *ast.Ident
stmtLabel := labelFor(path)
@@ -385,7 +393,7 @@
return true
}
- if b.Label == nil || labelDecl(b.Label) == switchNodeLabel {
+ if b.Label == nil || info.Uses[b.Label] == info.Defs[switchNodeLabel] {
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
}
return true
@@ -403,7 +411,7 @@
return true
}
- if b.Label != nil && labelDecl(b.Label) == switchNodeLabel {
+ if b.Label != nil && info.Uses[b.Label] == info.Defs[switchNodeLabel] {
result[posRange{start: b.Pos(), end: b.End()}] = struct{}{}
}
@@ -411,23 +419,6 @@
})
}
-func labelDecl(n *ast.Ident) *ast.Ident {
- if n == nil {
- return nil
- }
- if n.Obj == nil {
- return nil
- }
- if n.Obj.Decl == nil {
- return nil
- }
- stmt, ok := n.Obj.Decl.(*ast.LabeledStmt)
- if !ok {
- return nil
- }
- return stmt.Label
-}
-
func highlightImportUses(path []ast.Node, info *types.Info, result map[posRange]struct{}) error {
basicLit, ok := path[0].(*ast.BasicLit)
if !ok {