@@ -44,41 +44,73 @@ func initPrintFlags() {
44
44
name = name [:colon ]
45
45
}
46
46
47
- name = strings .ToLower (name )
48
- if name [len (name )- 1 ] == 'f' {
49
- isFormattedPrint [name ] = true
50
- } else {
51
- isPrint [name ] = true
52
- }
47
+ isPrint [strings .ToLower (name )] = true
53
48
}
54
49
}
55
50
56
- // isFormattedPrint records the formatted-print functions. Names are
57
- // lower-cased so the lookup is case insensitive.
58
- var isFormattedPrint = map [string ]bool {
59
- "errorf" : true ,
60
- "fatalf" : true ,
61
- "fprintf" : true ,
62
- "logf" : true ,
63
- "panicf" : true ,
64
- "printf" : true ,
65
- "sprintf" : true ,
66
- }
67
-
68
- // isPrint records the unformatted-print functions. Names are lower-cased
69
- // so the lookup is case insensitive.
51
+ // TODO(rsc): Incorporate user-defined printf wrappers again.
52
+ // The general plan is to allow vet of one package P to output
53
+ // additional information to supply to later vets of packages
54
+ // importing P. Then vet of P can record a list of printf wrappers
55
+ // and the later vet using P.Printf will find it in the list and check it.
56
+ // That's not ready for Go 1.10.
57
+ // When that does happen, uncomment the user-defined printf
58
+ // wrapper tests in testdata/print.go.
59
+
60
+ // isPrint records the print functions.
61
+ // If a key ends in 'f' then it is assumed to be a formatted print.
70
62
var isPrint = map [string ]bool {
71
- "error" : true ,
72
- "fatal" : true ,
73
- "fprint" : true ,
74
- "fprintln" : true ,
75
- "log" : true ,
76
- "panic" : true ,
77
- "panicln" : true ,
78
- "print" : true ,
79
- "println" : true ,
80
- "sprint" : true ,
81
- "sprintln" : true ,
63
+ "fmt.Errorf" : true ,
64
+ "fmt.Fprint" : true ,
65
+ "fmt.Fprintf" : true ,
66
+ "fmt.Fprintln" : true ,
67
+ "fmt.Print" : true ,
68
+ "fmt.Printf" : true ,
69
+ "fmt.Println" : true ,
70
+ "fmt.Sprint" : true ,
71
+ "fmt.Sprintf" : true ,
72
+ "fmt.Sprintln" : true ,
73
+ "log.Fatal" : true ,
74
+ "log.Fatalf" : true ,
75
+ "log.Fatalln" : true ,
76
+ "log.Logger.Fatal" : true ,
77
+ "log.Logger.Fatalf" : true ,
78
+ "log.Logger.Fatalln" : true ,
79
+ "log.Logger.Panic" : true ,
80
+ "log.Logger.Panicf" : true ,
81
+ "log.Logger.Panicln" : true ,
82
+ "log.Logger.Printf" : true ,
83
+ "log.Logger.Println" : true ,
84
+ "log.Panic" : true ,
85
+ "log.Panicf" : true ,
86
+ "log.Panicln" : true ,
87
+ "log.Print" : true ,
88
+ "log.Printf" : true ,
89
+ "log.Println" : true ,
90
+ "testing.B.Error" : true ,
91
+ "testing.B.Errorf" : true ,
92
+ "testing.B.Fatal" : true ,
93
+ "testing.B.Fatalf" : true ,
94
+ "testing.B.Log" : true ,
95
+ "testing.B.Logf" : true ,
96
+ "testing.B.Skip" : true ,
97
+ "testing.B.Skipf" : true ,
98
+ "testing.T.Error" : true ,
99
+ "testing.T.Errorf" : true ,
100
+ "testing.T.Fatal" : true ,
101
+ "testing.T.Fatalf" : true ,
102
+ "testing.T.Log" : true ,
103
+ "testing.T.Logf" : true ,
104
+ "testing.T.Skip" : true ,
105
+ "testing.T.Skipf" : true ,
106
+ "testing.TB.Error" : true ,
107
+ "testing.TB.Errorf" : true ,
108
+ "testing.TB.Fatal" : true ,
109
+ "testing.TB.Fatalf" : true ,
110
+ "testing.TB.Log" : true ,
111
+ "testing.TB.Logf" : true ,
112
+ "testing.TB.Skip" : true ,
113
+ "testing.TB.Skipf" : true ,
82
114
}
83
115
84
116
// formatString returns the format string argument and its index within
@@ -148,6 +180,11 @@ func stringConstantArg(f *File, call *ast.CallExpr, idx int) (string, bool) {
148
180
149
181
// checkCall triggers the print-specific checks if the call invokes a print function.
150
182
func checkFmtPrintfCall (f * File , node ast.Node ) {
183
+ if f .pkg .typesPkg == nil {
184
+ // This check now requires type information.
185
+ return
186
+ }
187
+
151
188
if d , ok := node .(* ast.FuncDecl ); ok && isStringer (f , d ) {
152
189
// Remember we saw this.
153
190
if f .stringers == nil {
@@ -165,24 +202,67 @@ func checkFmtPrintfCall(f *File, node ast.Node) {
165
202
if ! ok {
166
203
return
167
204
}
168
- var Name string
205
+
206
+ // Construct name like pkg.Printf or pkg.Type.Printf for lookup.
207
+ var name string
169
208
switch x := call .Fun .(type ) {
170
209
case * ast.Ident :
171
- Name = x .Name
210
+ if fn , ok := f .pkg .uses [x ].(* types.Func ); ok {
211
+ var pkg string
212
+ if fn .Pkg () == nil || fn .Pkg () == f .pkg .typesPkg {
213
+ pkg = vcfg .ImportPath
214
+ } else {
215
+ pkg = fn .Pkg ().Path ()
216
+ }
217
+ name = pkg + "." + x .Name
218
+ break
219
+ }
220
+
172
221
case * ast.SelectorExpr :
173
- Name = x .Sel .Name
174
- default :
222
+ // Check for "fmt.Printf".
223
+ if id , ok := x .X .(* ast.Ident ); ok {
224
+ if pkgName , ok := f .pkg .uses [id ].(* types.PkgName ); ok {
225
+ name = pkgName .Imported ().Path () + "." + x .Sel .Name
226
+ break
227
+ }
228
+ }
229
+
230
+ // Check for t.Logf where t is a *testing.T.
231
+ if sel := f .pkg .selectors [x ]; sel != nil {
232
+ recv := sel .Recv ()
233
+ if p , ok := recv .(* types.Pointer ); ok {
234
+ recv = p .Elem ()
235
+ }
236
+ if named , ok := recv .(* types.Named ); ok {
237
+ obj := named .Obj ()
238
+ var pkg string
239
+ if obj .Pkg () == nil || obj .Pkg () == f .pkg .typesPkg {
240
+ pkg = vcfg .ImportPath
241
+ } else {
242
+ pkg = obj .Pkg ().Path ()
243
+ }
244
+ name = pkg + "." + obj .Name () + "." + x .Sel .Name
245
+ break
246
+ }
247
+ }
248
+ }
249
+ if name == "" {
175
250
return
176
251
}
177
252
178
- name := strings .ToLower (Name )
179
- if _ , ok := isFormattedPrint [name ]; ok {
180
- f .checkPrintf (call , Name )
181
- return
253
+ shortName := name [strings .LastIndex (name , "." )+ 1 :]
254
+
255
+ _ , ok = isPrint [name ]
256
+ if ! ok {
257
+ // Next look up just "printf", for use with -printfuncs.
258
+ _ , ok = isPrint [strings .ToLower (shortName )]
182
259
}
183
- if _ , ok := isPrint [name ]; ok {
184
- f .checkPrint (call , Name )
185
- return
260
+ if ok {
261
+ if strings .HasSuffix (name , "f" ) {
262
+ f .checkPrintf (call , shortName )
263
+ } else {
264
+ f .checkPrint (call , shortName )
265
+ }
186
266
}
187
267
}
188
268
0 commit comments