Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / source / completion / completion_labels.go
1 // Copyright 2019 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package completion
6
7 import (
8         "go/ast"
9         "go/token"
10         "math"
11 )
12
13 type labelType int
14
15 const (
16         labelNone labelType = iota
17         labelBreak
18         labelContinue
19         labelGoto
20 )
21
22 // wantLabelCompletion returns true if we want (only) label
23 // completions at the position.
24 func (c *completer) wantLabelCompletion() labelType {
25         if _, ok := c.path[0].(*ast.Ident); ok && len(c.path) > 1 {
26                 // We want a label if we are an *ast.Ident child of a statement
27                 // that accepts a label, e.g. "break Lo<>".
28                 return takesLabel(c.path[1])
29         }
30
31         return labelNone
32 }
33
34 // takesLabel returns the corresponding labelType if n is a statement
35 // that accepts a label, otherwise labelNone.
36 func takesLabel(n ast.Node) labelType {
37         if bs, ok := n.(*ast.BranchStmt); ok {
38                 switch bs.Tok {
39                 case token.BREAK:
40                         return labelBreak
41                 case token.CONTINUE:
42                         return labelContinue
43                 case token.GOTO:
44                         return labelGoto
45                 }
46         }
47         return labelNone
48 }
49
50 // labels adds completion items for labels defined in the enclosing
51 // function.
52 func (c *completer) labels(lt labelType) {
53         if c.enclosingFunc == nil {
54                 return
55         }
56
57         addLabel := func(score float64, l *ast.LabeledStmt) {
58                 labelObj := c.pkg.GetTypesInfo().ObjectOf(l.Label)
59                 if labelObj != nil {
60                         c.deepState.enqueue(candidate{obj: labelObj, score: score})
61                 }
62         }
63
64         switch lt {
65         case labelBreak, labelContinue:
66                 // "break" and "continue" only accept labels from enclosing statements.
67
68                 for i, p := range c.path {
69                         switch p := p.(type) {
70                         case *ast.FuncLit:
71                                 // Labels are function scoped, so don't continue out of functions.
72                                 return
73                         case *ast.LabeledStmt:
74                                 switch p.Stmt.(type) {
75                                 case *ast.ForStmt, *ast.RangeStmt:
76                                         // Loop labels can be used for "break" or "continue".
77                                         addLabel(highScore*math.Pow(.99, float64(i)), p)
78                                 case *ast.SwitchStmt, *ast.SelectStmt, *ast.TypeSwitchStmt:
79                                         // Switch and select labels can be used only for "break".
80                                         if lt == labelBreak {
81                                                 addLabel(highScore*math.Pow(.99, float64(i)), p)
82                                         }
83                                 }
84                         }
85                 }
86         case labelGoto:
87                 // Goto accepts any label in the same function not in a nested
88                 // block. It also doesn't take labels that would jump across
89                 // variable definitions, but ignore that case for now.
90                 ast.Inspect(c.enclosingFunc.body, func(n ast.Node) bool {
91                         if n == nil {
92                                 return false
93                         }
94
95                         switch n := n.(type) {
96                         // Only search into block-like nodes enclosing our "goto".
97                         // This prevents us from finding labels in nested blocks.
98                         case *ast.BlockStmt, *ast.CommClause, *ast.CaseClause:
99                                 for _, p := range c.path {
100                                         if n == p {
101                                                 return true
102                                         }
103                                 }
104                                 return false
105                         case *ast.LabeledStmt:
106                                 addLabel(highScore, n)
107                         }
108
109                         return true
110                 })
111         }
112 }