github.com/v2fly/tools@v0.100.0/internal/lsp/source/completion/printf.go (about)

     1  // Copyright 2020 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/constant"
    10  	"go/types"
    11  	"strconv"
    12  	"strings"
    13  	"unicode/utf8"
    14  )
    15  
    16  // printfArgKind returns the expected objKind when completing a
    17  // printf-like operand. call is the printf-like function call, and
    18  // argIdx is the index of call.Args being completed.
    19  func printfArgKind(info *types.Info, call *ast.CallExpr, argIdx int) objKind {
    20  	// Printf-like function name must end in "f".
    21  	fn := exprObj(info, call.Fun)
    22  	if fn == nil || !strings.HasSuffix(fn.Name(), "f") {
    23  		return kindAny
    24  	}
    25  
    26  	sig, _ := fn.Type().(*types.Signature)
    27  	if sig == nil {
    28  		return kindAny
    29  	}
    30  
    31  	// Must be variadic and take at least two params.
    32  	numParams := sig.Params().Len()
    33  	if !sig.Variadic() || numParams < 2 || argIdx < numParams-1 {
    34  		return kindAny
    35  	}
    36  
    37  	// Param preceding variadic args must be a (format) string.
    38  	if !types.Identical(sig.Params().At(numParams-2).Type(), types.Typ[types.String]) {
    39  		return kindAny
    40  	}
    41  
    42  	// Format string must be a constant.
    43  	strArg := info.Types[call.Args[numParams-2]].Value
    44  	if strArg == nil || strArg.Kind() != constant.String {
    45  		return kindAny
    46  	}
    47  
    48  	return formatOperandKind(constant.StringVal(strArg), argIdx-(numParams-1)+1)
    49  }
    50  
    51  // formatOperandKind returns the objKind corresponding to format's
    52  // operandIdx'th operand.
    53  func formatOperandKind(format string, operandIdx int) objKind {
    54  	var (
    55  		prevOperandIdx int
    56  		kind           = kindAny
    57  	)
    58  	for {
    59  		i := strings.Index(format, "%")
    60  		if i == -1 {
    61  			break
    62  		}
    63  
    64  		var operands []formatOperand
    65  		format, operands = parsePrintfVerb(format[i+1:], prevOperandIdx)
    66  
    67  		// Check if any this verb's operands correspond to our target
    68  		// operandIdx.
    69  		for _, v := range operands {
    70  			if v.idx == operandIdx {
    71  				if kind == kindAny {
    72  					kind = v.kind
    73  				} else if v.kind != kindAny {
    74  					// If multiple verbs refer to the same operand, take the
    75  					// intersection of their kinds.
    76  					kind &= v.kind
    77  				}
    78  			}
    79  
    80  			prevOperandIdx = v.idx
    81  		}
    82  	}
    83  	return kind
    84  }
    85  
    86  type formatOperand struct {
    87  	// idx is the one-based printf operand index.
    88  	idx int
    89  	// kind is a mask of expected kinds of objects for this operand.
    90  	kind objKind
    91  }
    92  
    93  // parsePrintfVerb parses the leading printf verb in f. The opening
    94  // "%" must already be trimmed from f. prevIdx is the previous
    95  // operand's index, or zero if this is the first verb. The format
    96  // string is returned with the leading verb removed. Multiple operands
    97  // can be returned in the case of dynamic widths such as "%*.*f".
    98  func parsePrintfVerb(f string, prevIdx int) (string, []formatOperand) {
    99  	var verbs []formatOperand
   100  
   101  	addVerb := func(k objKind) {
   102  		verbs = append(verbs, formatOperand{
   103  			idx:  prevIdx + 1,
   104  			kind: k,
   105  		})
   106  		prevIdx++
   107  	}
   108  
   109  	for len(f) > 0 {
   110  		// Trim first rune off of f so we are guaranteed to make progress.
   111  		r, l := utf8.DecodeRuneInString(f)
   112  		f = f[l:]
   113  
   114  		// We care about three things:
   115  		// 1. The verb, which maps directly to object kind.
   116  		// 2. Explicit operand indices like "%[2]s".
   117  		// 3. Dynamic widths using "*".
   118  		switch r {
   119  		case '%':
   120  			return f, nil
   121  		case '*':
   122  			addVerb(kindInt)
   123  			continue
   124  		case '[':
   125  			// Parse operand index as in "%[2]s".
   126  			i := strings.Index(f, "]")
   127  			if i == -1 {
   128  				return f, nil
   129  			}
   130  
   131  			idx, err := strconv.Atoi(f[:i])
   132  			f = f[i+1:]
   133  			if err != nil {
   134  				return f, nil
   135  			}
   136  
   137  			prevIdx = idx - 1
   138  			continue
   139  		case 'v', 'T':
   140  			addVerb(kindAny)
   141  		case 't':
   142  			addVerb(kindBool)
   143  		case 'c', 'd', 'o', 'O', 'U':
   144  			addVerb(kindInt)
   145  		case 'e', 'E', 'f', 'F', 'g', 'G':
   146  			addVerb(kindFloat | kindComplex)
   147  		case 'b':
   148  			addVerb(kindInt | kindFloat | kindComplex | kindBytes)
   149  		case 'q', 's':
   150  			addVerb(kindString | kindBytes | kindStringer | kindError)
   151  		case 'x', 'X':
   152  			// Omit kindStringer and kindError though technically allowed.
   153  			addVerb(kindString | kindBytes | kindInt | kindFloat | kindComplex)
   154  		case 'p':
   155  			addVerb(kindPtr | kindSlice)
   156  		case 'w':
   157  			addVerb(kindError)
   158  		case '+', '-', '#', ' ', '.', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
   159  			// Flag or numeric width/precicision value.
   160  			continue
   161  		default:
   162  			// Assume unrecognized rune is a custom fmt.Formatter verb.
   163  			addVerb(kindAny)
   164  		}
   165  
   166  		if len(verbs) > 0 {
   167  			break
   168  		}
   169  	}
   170  
   171  	return f, verbs
   172  }