mirror of
https://github.com/golang/go
synced 2024-11-19 02:34:44 -07:00
8f1b74eef3
Fix label detection to know about *ast.RangeStmt and *ast.TypeSwitchStmt. Change-Id: I4061e165884f7064fe486249fe3664d572b7b628 Reviewed-on: https://go-review.googlesource.com/c/tools/+/202621 Reviewed-by: Rebecca Stambler <rstambler@golang.org> Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
112 lines
2.6 KiB
Go
112 lines
2.6 KiB
Go
// Copyright 2019 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package source
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
)
|
|
|
|
type labelType int
|
|
|
|
const (
|
|
labelNone labelType = iota
|
|
labelBreak
|
|
labelContinue
|
|
labelGoto
|
|
)
|
|
|
|
// wantLabelCompletion returns true if we want (only) label
|
|
// completions at the position.
|
|
func (c *completer) wantLabelCompletion() labelType {
|
|
if _, ok := c.path[0].(*ast.Ident); ok && len(c.path) > 1 {
|
|
// We want a label if we are an *ast.Ident child of a statement
|
|
// that accepts a label, e.g. "break Lo<>".
|
|
return takesLabel(c.path[1])
|
|
}
|
|
|
|
return labelNone
|
|
}
|
|
|
|
// takesLabel returns the corresponding labelType if n is a statement
|
|
// that accepts a label, otherwise labelNone.
|
|
func takesLabel(n ast.Node) labelType {
|
|
if bs, ok := n.(*ast.BranchStmt); ok {
|
|
switch bs.Tok {
|
|
case token.BREAK:
|
|
return labelBreak
|
|
case token.CONTINUE:
|
|
return labelContinue
|
|
case token.GOTO:
|
|
return labelGoto
|
|
}
|
|
}
|
|
return labelNone
|
|
}
|
|
|
|
// labels adds completion items for labels defined in the enclosing
|
|
// function.
|
|
func (c *completer) labels(lt labelType) {
|
|
if c.enclosingFunc == nil {
|
|
return
|
|
}
|
|
|
|
addLabel := func(l *ast.LabeledStmt) {
|
|
labelObj := c.pkg.GetTypesInfo().ObjectOf(l.Label)
|
|
if labelObj != nil {
|
|
c.found(labelObj, highScore, nil)
|
|
}
|
|
}
|
|
|
|
switch lt {
|
|
case labelBreak, labelContinue:
|
|
// "break" and "continue" only accept labels from enclosing statements.
|
|
|
|
for _, p := range c.path {
|
|
switch p := p.(type) {
|
|
case *ast.FuncLit:
|
|
// Labels are function scoped, so don't continue out of functions.
|
|
return
|
|
case *ast.LabeledStmt:
|
|
switch p.Stmt.(type) {
|
|
case *ast.ForStmt, *ast.RangeStmt:
|
|
// Loop labels can be used for "break" or "continue".
|
|
addLabel(p)
|
|
case *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
|
|
// Switch and select labels can be used only for "break".
|
|
if lt == labelBreak {
|
|
addLabel(p)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case labelGoto:
|
|
// Goto accepts any label in the same function not in a nested
|
|
// block. It also doesn't take labels that would jump across
|
|
// variable definitions, but ignore that case for now.
|
|
ast.Inspect(c.enclosingFunc.body, func(n ast.Node) bool {
|
|
if n == nil {
|
|
return false
|
|
}
|
|
|
|
switch n := n.(type) {
|
|
// Only search into block-like nodes enclosing our "goto".
|
|
// This prevents us from finding labels in nested blocks.
|
|
case *ast.BlockStmt, *ast.CommClause, *ast.CaseClause:
|
|
for _, p := range c.path {
|
|
if n == p {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
case *ast.LabeledStmt:
|
|
addLabel(n)
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
}
|