1
2
3
4
5 package printf
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/types"
11
12 "golang.org/x/tools/go/analysis"
13 "golang.org/x/tools/internal/typeparams"
14 )
15
16 var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
17
18
19
20
21
22
23 func matchArgType(pass *analysis.Pass, t printfArgType, arg ast.Expr) (reason string, ok bool) {
24
25 if t == anyType {
26 return "", true
27 }
28
29 typ := pass.TypesInfo.Types[arg].Type
30 if typ == nil {
31 return "", true
32 }
33
34 m := &argMatcher{t: t, seen: make(map[types.Type]bool)}
35 ok = m.match(typ, true)
36 return m.reason, ok
37 }
38
39
40
41
42
43
44
45
46
47
48 type argMatcher struct {
49 t printfArgType
50 seen map[types.Type]bool
51 reason string
52 }
53
54
55
56
57
58
59 func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
60
61 if m.t == argError {
62 return types.ConvertibleTo(typ, errorType)
63 }
64
65
66 if isFormatter(typ) {
67 return true
68 }
69
70
71 if m.t&argString != 0 && isConvertibleToString(typ) {
72 return true
73 }
74
75 if typ, _ := typ.(*typeparams.TypeParam); typ != nil {
76
77 if m.seen[typ] {
78 return true
79 }
80 m.seen[typ] = true
81 terms, err := typeparams.StructuralTerms(typ)
82 if err != nil {
83 return true
84 }
85
86 if len(terms) == 0 {
87
88
89
90
91
92
93
94 if m.t == anyType {
95 panic("unexpected printfArgType")
96 }
97 return false
98 }
99
100
101
102
103
104 reportReason := len(m.seen) == 1
105
106 for _, term := range terms {
107 if !m.match(term.Type(), topLevel) {
108 if reportReason {
109 if term.Tilde() {
110 m.reason = fmt.Sprintf("contains ~%s", term.Type())
111 } else {
112 m.reason = fmt.Sprintf("contains %s", term.Type())
113 }
114 }
115 return false
116 }
117 }
118 return true
119 }
120
121 typ = typ.Underlying()
122 if m.seen[typ] {
123
124
125
126
127
128
129
130
131 return true
132 }
133 m.seen[typ] = true
134
135 switch typ := typ.(type) {
136 case *types.Signature:
137 return m.t == argPointer
138
139 case *types.Map:
140 if m.t == argPointer {
141 return true
142 }
143
144 return m.match(typ.Key(), false) && m.match(typ.Elem(), false)
145
146 case *types.Chan:
147 return m.t&argPointer != 0
148
149 case *types.Array:
150
151 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
152 return true
153 }
154
155 return m.match(typ.Elem(), false)
156
157 case *types.Slice:
158
159 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
160 return true
161 }
162 if m.t == argPointer {
163 return true
164 }
165
166
167
168 return m.match(typ.Elem(), false)
169
170 case *types.Pointer:
171
172
173 if typ.Elem() == types.Typ[types.Invalid] {
174 return true
175 }
176
177 if m.t == argPointer {
178 return true
179 }
180
181 if typeparams.IsTypeParam(typ.Elem()) {
182 return true
183 }
184
185 under := typ.Elem().Underlying()
186 switch under.(type) {
187 case *types.Struct:
188 case *types.Array:
189 case *types.Slice:
190 case *types.Map:
191 default:
192
193 return m.t&argPointer != 0
194 }
195
196
197
198
199 if !topLevel {
200 return false
201 }
202 return m.match(under, false)
203
204 case *types.Struct:
205
206
207 for i := 0; i < typ.NumFields(); i++ {
208 typf := typ.Field(i)
209 if !m.match(typf.Type(), false) {
210 return false
211 }
212 if m.t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
213
214 return false
215 }
216 }
217 return true
218
219 case *types.Interface:
220
221
222
223 return true
224
225 case *types.Basic:
226 switch typ.Kind() {
227 case types.UntypedBool,
228 types.Bool:
229 return m.t&argBool != 0
230
231 case types.UntypedInt,
232 types.Int,
233 types.Int8,
234 types.Int16,
235 types.Int32,
236 types.Int64,
237 types.Uint,
238 types.Uint8,
239 types.Uint16,
240 types.Uint32,
241 types.Uint64,
242 types.Uintptr:
243 return m.t&argInt != 0
244
245 case types.UntypedFloat,
246 types.Float32,
247 types.Float64:
248 return m.t&argFloat != 0
249
250 case types.UntypedComplex,
251 types.Complex64,
252 types.Complex128:
253 return m.t&argComplex != 0
254
255 case types.UntypedString,
256 types.String:
257 return m.t&argString != 0
258
259 case types.UnsafePointer:
260 return m.t&(argPointer|argInt) != 0
261
262 case types.UntypedRune:
263 return m.t&(argInt|argRune) != 0
264
265 case types.UntypedNil:
266 return false
267
268 case types.Invalid:
269 return true
270 }
271 panic("unreachable")
272 }
273
274 return false
275 }
276
277 func isConvertibleToString(typ types.Type) bool {
278 if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
279
280
281
282 return false
283 }
284 if types.ConvertibleTo(typ, errorType) {
285 return true
286 }
287
288
289 if obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "String"); obj != nil {
290 if fn, ok := obj.(*types.Func); ok {
291 sig := fn.Type().(*types.Signature)
292 if sig.Params().Len() == 0 &&
293 sig.Results().Len() == 1 &&
294 sig.Results().At(0).Type() == types.Typ[types.String] {
295 return true
296 }
297 }
298 }
299
300 return false
301 }
302
View as plain text