github.com/v2fly/tools@v0.100.0/internal/lsp/source/completion/labels.go (about) 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 }