Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / analysis / passes / printf / types.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/go/analysis/passes/printf/types.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201028153306-37f0764111ff/go/analysis/passes/printf/types.go
new file mode 100644 (file)
index 0000000..bd8a594
--- /dev/null
@@ -0,0 +1,242 @@
+package printf
+
+import (
+       "go/ast"
+       "go/types"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+)
+
+var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
+
+// matchArgType reports an error if printf verb t is not appropriate
+// for operand arg.
+//
+// typ is used only for recursive calls; external callers must supply nil.
+//
+// (Recursion arises from the compound types {map,chan,slice} which
+// may be printed with %d etc. if that is appropriate for their element
+// types.)
+func matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
+       return matchArgTypeInternal(pass, t, typ, arg, make(map[types.Type]bool))
+}
+
+// matchArgTypeInternal is the internal version of matchArgType. It carries a map
+// remembering what types are in progress so we don't recur when faced with recursive
+// types or mutually recursive types.
+func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
+       // %v, %T accept any argument type.
+       if t == anyType {
+               return true
+       }
+       if typ == nil {
+               // external call
+               typ = pass.TypesInfo.Types[arg].Type
+               if typ == nil {
+                       return true // probably a type check problem
+               }
+       }
+
+       // %w accepts only errors.
+       if t == argError {
+               return types.ConvertibleTo(typ, errorType)
+       }
+
+       // If the type implements fmt.Formatter, we have nothing to check.
+       if isFormatter(typ) {
+               return true
+       }
+       // If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
+       if t&argString != 0 && isConvertibleToString(pass, typ) {
+               return true
+       }
+
+       typ = typ.Underlying()
+       if inProgress[typ] {
+               // We're already looking at this type. The call that started it will take care of it.
+               return true
+       }
+       inProgress[typ] = true
+
+       switch typ := typ.(type) {
+       case *types.Signature:
+               return t == argPointer
+
+       case *types.Map:
+               return t == argPointer ||
+                       // Recur: map[int]int matches %d.
+                       (matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress))
+
+       case *types.Chan:
+               return t&argPointer != 0
+
+       case *types.Array:
+               // Same as slice.
+               if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+                       return true // %s matches []byte
+               }
+               // Recur: []int matches %d.
+               return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
+
+       case *types.Slice:
+               // Same as array.
+               if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
+                       return true // %s matches []byte
+               }
+               if t == argPointer {
+                       return true // %p prints a slice's 0th element
+               }
+               // Recur: []int matches %d. But watch out for
+               //      type T []T
+               // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
+               return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
+
+       case *types.Pointer:
+               // Ugly, but dealing with an edge case: a known pointer to an invalid type,
+               // probably something from a failed import.
+               if typ.Elem().String() == "invalid type" {
+                       if false {
+                               pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, arg))
+                       }
+                       return true // special case
+               }
+               // If it's actually a pointer with %p, it prints as one.
+               if t == argPointer {
+                       return true
+               }
+
+               under := typ.Elem().Underlying()
+               switch under.(type) {
+               case *types.Struct: // see below
+               case *types.Array: // see below
+               case *types.Slice: // see below
+               case *types.Map: // see below
+               default:
+                       // Check whether the rest can print pointers.
+                       return t&argPointer != 0
+               }
+               // If it's a top-level pointer to a struct, array, slice, or
+               // map, that's equivalent in our analysis to whether we can
+               // print the type being pointed to. Pointers in nested levels
+               // are not supported to minimize fmt running into loops.
+               if len(inProgress) > 1 {
+                       return false
+               }
+               return matchArgTypeInternal(pass, t, under, arg, inProgress)
+
+       case *types.Struct:
+               return matchStructArgType(pass, t, typ, arg, inProgress)
+
+       case *types.Interface:
+               // There's little we can do.
+               // Whether any particular verb is valid depends on the argument.
+               // The user may have reasonable prior knowledge of the contents of the interface.
+               return true
+
+       case *types.Basic:
+               switch typ.Kind() {
+               case types.UntypedBool,
+                       types.Bool:
+                       return t&argBool != 0
+
+               case types.UntypedInt,
+                       types.Int,
+                       types.Int8,
+                       types.Int16,
+                       types.Int32,
+                       types.Int64,
+                       types.Uint,
+                       types.Uint8,
+                       types.Uint16,
+                       types.Uint32,
+                       types.Uint64,
+                       types.Uintptr:
+                       return t&argInt != 0
+
+               case types.UntypedFloat,
+                       types.Float32,
+                       types.Float64:
+                       return t&argFloat != 0
+
+               case types.UntypedComplex,
+                       types.Complex64,
+                       types.Complex128:
+                       return t&argComplex != 0
+
+               case types.UntypedString,
+                       types.String:
+                       return t&argString != 0
+
+               case types.UnsafePointer:
+                       return t&(argPointer|argInt) != 0
+
+               case types.UntypedRune:
+                       return t&(argInt|argRune) != 0
+
+               case types.UntypedNil:
+                       return false
+
+               case types.Invalid:
+                       if false {
+                               pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
+                       }
+                       return true // Probably a type check problem.
+               }
+               panic("unreachable")
+       }
+
+       return false
+}
+
+func isConvertibleToString(pass *analysis.Pass, typ types.Type) bool {
+       if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
+               // We explicitly don't want untyped nil, which is
+               // convertible to both of the interfaces below, as it
+               // would just panic anyway.
+               return false
+       }
+       if types.ConvertibleTo(typ, errorType) {
+               return true // via .Error()
+       }
+
+       // Does it implement fmt.Stringer?
+       if obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "String"); obj != nil {
+               if fn, ok := obj.(*types.Func); ok {
+                       sig := fn.Type().(*types.Signature)
+                       if sig.Params().Len() == 0 &&
+                               sig.Results().Len() == 1 &&
+                               sig.Results().At(0).Type() == types.Typ[types.String] {
+                               return true
+                       }
+               }
+       }
+
+       return false
+}
+
+// hasBasicType reports whether x's type is a types.Basic with the given kind.
+func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
+       t := pass.TypesInfo.Types[x].Type
+       if t != nil {
+               t = t.Underlying()
+       }
+       b, ok := t.(*types.Basic)
+       return ok && b.Kind() == kind
+}
+
+// matchStructArgType reports whether all the elements of the struct match the expected
+// type. For instance, with "%d" all the elements must be printable with the "%d" format.
+func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
+       for i := 0; i < typ.NumFields(); i++ {
+               typf := typ.Field(i)
+               if !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
+                       return false
+               }
+               if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
+                       // Issue #17798: unexported Stringer or error cannot be properly formatted.
+                       return false
+               }
+       }
+       return true
+}