@@ -16,23 +16,41 @@ import (
16
16
"golang.org/x/tools/go/analysis"
17
17
)
18
18
19
+ type optionInt struct {
20
+ enabled bool
21
+ intConv bool
22
+ }
23
+
24
+ type optionErr struct {
25
+ enabled bool
26
+ errError bool
27
+ errorf bool
28
+ }
29
+
30
+ type optionStr struct {
31
+ enabled bool
32
+ sprintf1 bool
33
+ strconcat bool
34
+ }
35
+
19
36
type perfSprint struct {
20
- intConv bool
21
- errError bool
22
- errorf bool
23
- sprintf1 bool
37
+ intFormat optionInt
38
+ errFormat optionErr
39
+ strFormat optionStr
40
+
41
+ boolFormat bool
42
+ hexFormat bool
24
43
fiximports bool
25
- strconcat bool
26
44
}
27
45
28
46
func newPerfSprint () * perfSprint {
29
47
return & perfSprint {
30
- intConv : true ,
31
- errError : false ,
32
- errorf : true ,
33
- sprintf1 : true ,
48
+ intFormat : optionInt {enabled : true , intConv : true },
49
+ errFormat : optionErr {enabled : true , errError : false , errorf : true },
50
+ strFormat : optionStr {enabled : true , sprintf1 : true , strconcat : true },
51
+ boolFormat : true ,
52
+ hexFormat : true ,
34
53
fiximports : true ,
35
- strconcat : true ,
36
54
}
37
55
}
38
56
@@ -44,12 +62,31 @@ func New() *analysis.Analyzer {
44
62
Run : n .run ,
45
63
Requires : []* analysis.Analyzer {inspect .Analyzer },
46
64
}
47
- r .Flags .BoolVar (& n .intConv , "int-conversion" , true , "optimizes even if it requires an int or uint type cast" )
48
- r .Flags .BoolVar (& n .errError , "err-error" , false , "optimizes into err.Error() even if it is only equivalent for non-nil errors" )
49
- r .Flags .BoolVar (& n .errorf , "errorf" , true , "optimizes fmt.Errorf" )
50
- r .Flags .BoolVar (& n .sprintf1 , "sprintf1" , true , "optimizes fmt.Sprintf with only one argument" )
65
+ r .Flags .BoolVar (& n .intFormat .enabled , "integer-format" , true , "enable/disable optimization of integer formatting" )
66
+ r .Flags .BoolVar (& n .intFormat .intConv , "int-conversion" , true , "optimizes even if it requires an int or uint type cast" )
67
+ r .Flags .BoolVar (& n .errFormat .enabled , "error-format" , true , "enable/disable optimization of error formatting" )
68
+ r .Flags .BoolVar (& n .errFormat .errError , "err-error" , false , "optimizes into err.Error() even if it is only equivalent for non-nil errors" )
69
+ r .Flags .BoolVar (& n .errFormat .errorf , "errorf" , true , "optimizes fmt.Errorf" )
70
+ r .Flags .BoolVar (& n .boolFormat , "bool-format" , true , "enable/disable optimization of bool formatting" )
71
+ r .Flags .BoolVar (& n .hexFormat , "hex-format" , true , "enable/disable optimization of hex formatting" )
72
+ r .Flags .BoolVar (& n .strFormat .enabled , "string-format" , true , "enable/disable optimization of string formatting" )
73
+ r .Flags .BoolVar (& n .strFormat .sprintf1 , "sprintf1" , true , "optimizes fmt.Sprintf with only one argument" )
74
+ r .Flags .BoolVar (& n .strFormat .strconcat , "strconcat" , true , "optimizes into strings concatenation" )
51
75
r .Flags .BoolVar (& n .fiximports , "fiximports" , true , "fix needed imports from other fixes" )
52
- r .Flags .BoolVar (& n .strconcat , "strconcat" , true , "optimizes into strings concatenation" )
76
+
77
+ if ! n .intFormat .enabled {
78
+ n .intFormat .intConv = false
79
+ }
80
+
81
+ if ! n .errFormat .enabled {
82
+ n .errFormat .errError = false
83
+ n .errFormat .errorf = false
84
+ }
85
+ if ! n .strFormat .enabled {
86
+ n .strFormat .sprintf1 = false
87
+ n .strFormat .strconcat = false
88
+ }
89
+
53
90
return r
54
91
}
55
92
@@ -102,7 +139,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
102
139
err error
103
140
)
104
141
switch {
105
- case calledObj == fmtErrorfObj && len (call .Args ) == 1 && n .errorf :
142
+ case calledObj == fmtErrorfObj && len (call .Args ) == 1 && n .errFormat . errorf :
106
143
fn = "fmt.Errorf"
107
144
verb = "%s"
108
145
value = call .Args [0 ]
@@ -112,7 +149,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
112
149
verb = "%v"
113
150
value = call .Args [0 ]
114
151
115
- case calledObj == fmtSprintfObj && len (call .Args ) == 1 && n .sprintf1 :
152
+ case calledObj == fmtSprintfObj && len (call .Args ) == 1 && n .strFormat . sprintf1 :
116
153
fn = "fmt.Sprintf"
117
154
verb = "%s"
118
155
value = call .Args [0 ]
@@ -141,7 +178,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
141
178
142
179
switch verb {
143
180
default :
144
- if fn == "fmt.Sprintf" && isConcatable (verb ) && n .strconcat {
181
+ if fn == "fmt.Sprintf" && isConcatable (verb ) && n .strFormat . strconcat {
145
182
break
146
183
}
147
184
return
@@ -160,7 +197,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
160
197
neededPackages [fname ] = make (map [string ]struct {})
161
198
}
162
199
removedFmtUsages [fname ]++
163
- if fn == "fmt.Errorf" {
200
+ if fn == "fmt.Errorf" && n . errFormat . enabled {
164
201
neededPackages [fname ]["errors" ] = struct {}{}
165
202
d = newAnalysisDiagnostic (
166
203
"" , // TODO: precise checker
@@ -177,7 +214,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
177
214
},
178
215
},
179
216
)
180
- } else {
217
+ } else if fn != "fmt.Errorf" && n . strFormat . enabled {
181
218
d = newAnalysisDiagnostic (
182
219
"" , // TODO: precise checker
183
220
call ,
@@ -194,7 +231,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
194
231
},
195
232
)
196
233
}
197
- case types .Implements (valueType , errIface ) && oneOf (verb , "%v" , "%s" ) && n .errError :
234
+ case types .Implements (valueType , errIface ) && oneOf (verb , "%v" , "%s" ) && n .errFormat . errError :
198
235
// known false positive if this error is nil
199
236
// fmt.Sprint(nil) does not panic like nil.Error() does
200
237
errMethodCall := formatNode (pass .Fset , value ) + ".Error()"
@@ -216,7 +253,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
216
253
},
217
254
)
218
255
219
- case isBasicType (valueType , types .Bool ) && oneOf (verb , "%v" , "%t" ):
256
+ case isBasicType (valueType , types .Bool ) && oneOf (verb , "%v" , "%t" ) && n . boolFormat :
220
257
fname := pass .Fset .File (call .Pos ()).Name ()
221
258
removedFmtUsages [fname ]++
222
259
if _ , ok := neededPackages [fname ]; ! ok {
@@ -239,7 +276,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
239
276
},
240
277
)
241
278
242
- case isArray && isBasicType (a .Elem (), types .Uint8 ) && oneOf (verb , "%x" ):
279
+ case isArray && isBasicType (a .Elem (), types .Uint8 ) && oneOf (verb , "%x" ) && n . hexFormat :
243
280
if _ , ok := value .(* ast.Ident ); ! ok {
244
281
// Doesn't support array literals.
245
282
return
@@ -273,7 +310,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
273
310
},
274
311
},
275
312
)
276
- case isSlice && isBasicType (s .Elem (), types .Uint8 ) && oneOf (verb , "%x" ):
313
+ case isSlice && isBasicType (s .Elem (), types .Uint8 ) && oneOf (verb , "%x" ) && n . hexFormat :
277
314
fname := pass .Fset .File (call .Pos ()).Name ()
278
315
removedFmtUsages [fname ]++
279
316
if _ , ok := neededPackages [fname ]; ! ok {
@@ -296,7 +333,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
296
333
},
297
334
)
298
335
299
- case isBasicType (valueType , types .Int8 , types .Int16 , types .Int32 ) && oneOf (verb , "%v" , "%d" ) && n .intConv :
336
+ case isBasicType (valueType , types .Int8 , types .Int16 , types .Int32 ) && oneOf (verb , "%v" , "%d" ) && n .intFormat . intConv :
300
337
fname := pass .Fset .File (call .Pos ()).Name ()
301
338
removedFmtUsages [fname ]++
302
339
if _ , ok := neededPackages [fname ]; ! ok {
@@ -325,7 +362,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
325
362
},
326
363
},
327
364
)
328
- case isBasicType (valueType , types .Int ) && oneOf (verb , "%v" , "%d" ):
365
+ case isBasicType (valueType , types .Int ) && oneOf (verb , "%v" , "%d" ) && n . intFormat . enabled :
329
366
fname := pass .Fset .File (call .Pos ()).Name ()
330
367
removedFmtUsages [fname ]++
331
368
if _ , ok := neededPackages [fname ]; ! ok {
@@ -347,7 +384,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
347
384
},
348
385
},
349
386
)
350
- case isBasicType (valueType , types .Int64 ) && oneOf (verb , "%v" , "%d" ):
387
+ case isBasicType (valueType , types .Int64 ) && oneOf (verb , "%v" , "%d" ) && n . intFormat . enabled :
351
388
fname := pass .Fset .File (call .Pos ()).Name ()
352
389
removedFmtUsages [fname ]++
353
390
if _ , ok := neededPackages [fname ]; ! ok {
@@ -377,7 +414,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
377
414
},
378
415
)
379
416
380
- case isBasicType (valueType , types .Uint8 , types .Uint16 , types .Uint32 , types .Uint ) && oneOf (verb , "%v" , "%d" , "%x" ) && n .intConv :
417
+ case isBasicType (valueType , types .Uint8 , types .Uint16 , types .Uint32 , types .Uint ) && oneOf (verb , "%v" , "%d" , "%x" ) && n .intFormat . intConv :
381
418
base := []byte ("), 10" )
382
419
if verb == "%x" {
383
420
base = []byte ("), 16" )
@@ -410,7 +447,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
410
447
},
411
448
},
412
449
)
413
- case isBasicType (valueType , types .Uint64 ) && oneOf (verb , "%v" , "%d" , "%x" ):
450
+ case isBasicType (valueType , types .Uint64 ) && oneOf (verb , "%v" , "%d" , "%x" ) && n . intFormat . enabled :
414
451
base := []byte (", 10" )
415
452
if verb == "%x" {
416
453
base = []byte (", 16" )
@@ -443,7 +480,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
443
480
},
444
481
},
445
482
)
446
- case isBasicType (valueType , types .String ) && fn == "fmt.Sprintf" && isConcatable (verb ):
483
+ case isBasicType (valueType , types .String ) && fn == "fmt.Sprintf" && isConcatable (verb ) && n . strFormat . enabled :
447
484
var fix string
448
485
if strings .HasSuffix (verb , "%s" ) {
449
486
fix = strconv .Quote (verb [:len (verb )- 2 ]) + "+" + formatNode (pass .Fset , value )
0 commit comments