mirror of
https://github.com/golang/go
synced 2024-11-18 13:14:47 -07:00
ee2abff5cf
I originally made this change to see if it would help with the timeouts. Based on the TryBot results, it doesn't -- but I still think it's more correct to have the contexts this way. It was my mistake to put the context on the completer in the first place, I think. Change-Id: Ib77c8f0ac0b0d0922b82db4120820fb96cb664f4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/227303 Run-TryBot: Rebecca Stambler <rstambler@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Heschi Kreinick <heschi@google.com>
114 lines
2.8 KiB
Go
114 lines
2.8 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 (
|
|
"context"
|
|
"go/ast"
|
|
"go/token"
|
|
"math"
|
|
)
|
|
|
|
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(ctx context.Context, lt labelType) {
|
|
if c.enclosingFunc == nil {
|
|
return
|
|
}
|
|
|
|
addLabel := func(score float64, l *ast.LabeledStmt) {
|
|
labelObj := c.pkg.GetTypesInfo().ObjectOf(l.Label)
|
|
if labelObj != nil {
|
|
c.found(ctx, candidate{obj: labelObj, score: score})
|
|
}
|
|
}
|
|
|
|
switch lt {
|
|
case labelBreak, labelContinue:
|
|
// "break" and "continue" only accept labels from enclosing statements.
|
|
|
|
for i, 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(highScore*math.Pow(.99, float64(i)), p)
|
|
case *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
|
|
// Switch and select labels can be used only for "break".
|
|
if lt == labelBreak {
|
|
addLabel(highScore*math.Pow(.99, float64(i)), 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(highScore, n)
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
}
|