// 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 mulitple 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 }