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 }