.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.1.1-0.20210319172145-bda8f5cee399 / internal / lsp / source / completion / labels.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/source/completion/labels.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.1.1-0.20210319172145-bda8f5cee399/internal/lsp/source/completion/labels.go
new file mode 100644 (file)
index 0000000..e4fd961
--- /dev/null
@@ -0,0 +1,112 @@
+// 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
+               })
+       }
+}