Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / lsp / source / completion / printf.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/completion/printf.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/internal/lsp/source/completion/printf.go
new file mode 100644 (file)
index 0000000..c90a365
--- /dev/null
@@ -0,0 +1,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 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
+}