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  }