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