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