+++ /dev/null
-// 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 completion
-
-import (
- "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(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.deepState.enqueue(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
- })
- }
-}