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
1 package printf
2
3 import (
4         "go/ast"
5         "go/types"
6
7         "golang.org/x/tools/go/analysis"
8         "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
9 )
10
11 var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
12
13 // matchArgType reports an error if printf verb t is not appropriate
14 // for operand arg.
15 //
16 // typ is used only for recursive calls; external callers must supply nil.
17 //
18 // (Recursion arises from the compound types {map,chan,slice} which
19 // may be printed with %d etc. if that is appropriate for their element
20 // types.)
21 func matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
22         return matchArgTypeInternal(pass, t, typ, arg, make(map[types.Type]bool))
23 }
24
25 // matchArgTypeInternal is the internal version of matchArgType. It carries a map
26 // remembering what types are in progress so we don't recur when faced with recursive
27 // types or mutually recursive types.
28 func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
29         // %v, %T accept any argument type.
30         if t == anyType {
31                 return true
32         }
33         if typ == nil {
34                 // external call
35                 typ = pass.TypesInfo.Types[arg].Type
36                 if typ == nil {
37                         return true // probably a type check problem
38                 }
39         }
40
41         // %w accepts only errors.
42         if t == argError {
43                 return types.ConvertibleTo(typ, errorType)
44         }
45
46         // If the type implements fmt.Formatter, we have nothing to check.
47         if isFormatter(typ) {
48                 return true
49         }
50         // If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
51         if t&argString != 0 && isConvertibleToString(pass, typ) {
52                 return true
53         }
54
55         typ = typ.Underlying()
56         if inProgress[typ] {
57                 // We're already looking at this type. The call that started it will take care of it.
58                 return true
59         }
60         inProgress[typ] = true
61
62         switch typ := typ.(type) {
63         case *types.Signature:
64                 return t == argPointer
65
66         case *types.Map:
67                 return t == argPointer ||
68                         // Recur: map[int]int matches %d.
69                         (matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress))
70
71         case *types.Chan:
72                 return t&argPointer != 0
73
74         case *types.Array:
75                 // Same as slice.
76                 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
77                         return true // %s matches []byte
78                 }
79                 // Recur: []int matches %d.
80                 return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
81
82         case *types.Slice:
83                 // Same as array.
84                 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
85                         return true // %s matches []byte
86                 }
87                 if t == argPointer {
88                         return true // %p prints a slice's 0th element
89                 }
90                 // Recur: []int matches %d. But watch out for
91                 //      type T []T
92                 // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
93                 return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
94
95         case *types.Pointer:
96                 // Ugly, but dealing with an edge case: a known pointer to an invalid type,
97                 // probably something from a failed import.
98                 if typ.Elem().String() == "invalid type" {
99                         if false {
100                                 pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, arg))
101                         }
102                         return true // special case
103                 }
104                 // If it's actually a pointer with %p, it prints as one.
105                 if t == argPointer {
106                         return true
107                 }
108
109                 under := typ.Elem().Underlying()
110                 switch under.(type) {
111                 case *types.Struct: // see below
112                 case *types.Array: // see below
113                 case *types.Slice: // see below
114                 case *types.Map: // see below
115                 default:
116                         // Check whether the rest can print pointers.
117                         return t&argPointer != 0
118                 }
119                 // If it's a top-level pointer to a struct, array, slice, or
120                 // map, that's equivalent in our analysis to whether we can
121                 // print the type being pointed to. Pointers in nested levels
122                 // are not supported to minimize fmt running into loops.
123                 if len(inProgress) > 1 {
124                         return false
125                 }
126                 return matchArgTypeInternal(pass, t, under, arg, inProgress)
127
128         case *types.Struct:
129                 return matchStructArgType(pass, t, typ, arg, inProgress)
130
131         case *types.Interface:
132                 // There's little we can do.
133                 // Whether any particular verb is valid depends on the argument.
134                 // The user may have reasonable prior knowledge of the contents of the interface.
135                 return true
136
137         case *types.Basic:
138                 switch typ.Kind() {
139                 case types.UntypedBool,
140                         types.Bool:
141                         return t&argBool != 0
142
143                 case types.UntypedInt,
144                         types.Int,
145                         types.Int8,
146                         types.Int16,
147                         types.Int32,
148                         types.Int64,
149                         types.Uint,
150                         types.Uint8,
151                         types.Uint16,
152                         types.Uint32,
153                         types.Uint64,
154                         types.Uintptr:
155                         return t&argInt != 0
156
157                 case types.UntypedFloat,
158                         types.Float32,
159                         types.Float64:
160                         return t&argFloat != 0
161
162                 case types.UntypedComplex,
163                         types.Complex64,
164                         types.Complex128:
165                         return t&argComplex != 0
166
167                 case types.UntypedString,
168                         types.String:
169                         return t&argString != 0
170
171                 case types.UnsafePointer:
172                         return t&(argPointer|argInt) != 0
173
174                 case types.UntypedRune:
175                         return t&(argInt|argRune) != 0
176
177                 case types.UntypedNil:
178                         return false
179
180                 case types.Invalid:
181                         if false {
182                                 pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
183                         }
184                         return true // Probably a type check problem.
185                 }
186                 panic("unreachable")
187         }
188
189         return false
190 }
191
192 func isConvertibleToString(pass *analysis.Pass, typ types.Type) bool {
193         if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
194                 // We explicitly don't want untyped nil, which is
195                 // convertible to both of the interfaces below, as it
196                 // would just panic anyway.
197                 return false
198         }
199         if types.ConvertibleTo(typ, errorType) {
200                 return true // via .Error()
201         }
202
203         // Does it implement fmt.Stringer?
204         if obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "String"); obj != nil {
205                 if fn, ok := obj.(*types.Func); ok {
206                         sig := fn.Type().(*types.Signature)
207                         if sig.Params().Len() == 0 &&
208                                 sig.Results().Len() == 1 &&
209                                 sig.Results().At(0).Type() == types.Typ[types.String] {
210                                 return true
211                         }
212                 }
213         }
214
215         return false
216 }
217
218 // hasBasicType reports whether x's type is a types.Basic with the given kind.
219 func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
220         t := pass.TypesInfo.Types[x].Type
221         if t != nil {
222                 t = t.Underlying()
223         }
224         b, ok := t.(*types.Basic)
225         return ok && b.Kind() == kind
226 }
227
228 // matchStructArgType reports whether all the elements of the struct match the expected
229 // type. For instance, with "%d" all the elements must be printable with the "%d" format.
230 func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
231         for i := 0; i < typ.NumFields(); i++ {
232                 typf := typ.Field(i)
233                 if !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
234                         return false
235                 }
236                 if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
237                         // Issue #17798: unexported Stringer or error cannot be properly formatted.
238                         return false
239                 }
240         }
241         return true
242 }