Skip to content

Commit 9cd4cff

Browse files
committed
feature: fmt.Sprintf with one argument optimization
1 parent 8c0a424 commit 9cd4cff

File tree

6 files changed

+33
-13
lines changed

6 files changed

+33
-13
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ perfsprint --fix ./...
2020
To disable int/uint cast, you can use the flag `-int-conversion=false`
2121

2222
To disable `fmt.Errorf` optimization, you can use the flag `-errorf=false`
23+
This optimization is not always equivalent.
24+
The code
25+
```
26+
msg := "format string attack %s"
27+
fmt.Errorf(msg)
28+
```
29+
will panic when its optimized version will not (so it should be safer).
30+
31+
To disable `fmt.Sprintf("toto")` optimization, you can use the flag `-sprintf1=false`
32+
This optimization is not always equivalent.
33+
The code
34+
```
35+
msg := "format string attack %s"
36+
fmt.Sprintf(msg)
37+
```
38+
will panic when its optimized version will not (so it should be safer).
2339

2440
To enable `err.Error()` optimization, you can use the flag `-err-error=true`
2541
This optimization only works when the error is not nil, otherwise the resulting code will panic.

analyzer/analyzer.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ type perfSprint struct {
1919
intConv bool
2020
errError bool
2121
errorf bool
22+
sprintf1 bool
2223
}
2324

2425
func newPerfSprint() *perfSprint {
25-
return &perfSprint{intConv: true, errError: false, errorf: true}
26+
return &perfSprint{
27+
intConv: true,
28+
errError: false,
29+
errorf: true,
30+
sprintf1: true,
31+
}
2632
}
2733

2834
func New() *analysis.Analyzer {
@@ -36,6 +42,7 @@ func New() *analysis.Analyzer {
3642
r.Flags.BoolVar(&n.intConv, "int-conversion", true, "optimizes even if it requires an int or uint type cast")
3743
r.Flags.BoolVar(&n.errError, "err-error", false, "optimizes into err.Error() even if it is only equivalent for non-nil errors")
3844
r.Flags.BoolVar(&n.errorf, "errorf", true, "optimizes fmt.Errorf")
45+
r.Flags.BoolVar(&n.sprintf1, "sprintf1", true, "optimizes fmt.Sprintf with only one argument")
3946
return r
4047
}
4148

@@ -81,6 +88,13 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) {
8188
verb = "%v"
8289
value = call.Args[0]
8390

91+
case calledObj == fmtSprintfObj && len(call.Args) == 1:
92+
if n.sprintf1 {
93+
fn = "fmt.Sprintf"
94+
verb = "%s"
95+
value = call.Args[0]
96+
}
97+
8498
case calledObj == fmtSprintfObj && len(call.Args) == 2:
8599
verbLit, ok := call.Args[0].(*ast.BasicLit)
86100
if !ok {

analyzer/testdata/src/noconv/p.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,6 @@ func negative() {
104104

105105
fmt.Sprint("test", 42)
106106
fmt.Sprint(42, 42)
107-
fmt.Sprintf("test")
108-
fmt.Sprintf("%v")
109-
fmt.Sprintf("%d")
110107
fmt.Sprintf("%d", 42, 42)
111108
fmt.Sprintf("%#d", 42)
112109
fmt.Sprintf("value %d", 42)

analyzer/testdata/src/noconv/p.go.golden

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,6 @@ func negative() {
104104

105105
fmt.Sprint("test", 42)
106106
fmt.Sprint(42, 42)
107-
fmt.Sprintf("test")
108-
fmt.Sprintf("%v")
109-
fmt.Sprintf("%d")
110107
fmt.Sprintf("%d", 42, 42)
111108
fmt.Sprintf("%#d", 42)
112109
fmt.Sprintf("value %d", 42)

analyzer/testdata/src/p/p.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func positive() {
1616
var s string
1717
fmt.Sprintf("%s", "hello") // want "fmt.Sprintf can be replaced with just using the string"
1818
fmt.Sprintf("%v", "hello") // want "fmt.Sprintf can be replaced with just using the string"
19+
fmt.Sprintf("hello") // want "fmt.Sprintf can be replaced with just using the string"
1920
fmt.Sprint("hello") // want "fmt.Sprint can be replaced with just using the string"
2021
fmt.Sprintf("%s", s) // want "fmt.Sprintf can be replaced with just using the string"
2122
fmt.Sprintf("%v", s) // want "fmt.Sprintf can be replaced with just using the string"
@@ -197,9 +198,6 @@ func negative() {
197198

198199
fmt.Sprint("test", 42)
199200
fmt.Sprint(42, 42)
200-
fmt.Sprintf("test")
201-
fmt.Sprintf("%v")
202-
fmt.Sprintf("%d")
203201
fmt.Sprintf("%d", 42, 42)
204202
fmt.Sprintf("%#d", 42)
205203
fmt.Sprintf("value %d", 42)

analyzer/testdata/src/p/p.go.golden

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ func positive() {
1616
var s string
1717
"hello" // want "fmt.Sprintf can be replaced with just using the string"
1818
"hello" // want "fmt.Sprintf can be replaced with just using the string"
19+
"hello" // want "fmt.Sprintf can be replaced with just using the string"
1920
"hello" // want "fmt.Sprint can be replaced with just using the string"
2021
s // want "fmt.Sprintf can be replaced with just using the string"
2122
s // want "fmt.Sprintf can be replaced with just using the string"
@@ -197,9 +198,6 @@ func negative() {
197198

198199
fmt.Sprint("test", 42)
199200
fmt.Sprint(42, 42)
200-
fmt.Sprintf("test")
201-
fmt.Sprintf("%v")
202-
fmt.Sprintf("%d")
203201
fmt.Sprintf("%d", 42, 42)
204202
fmt.Sprintf("%#d", 42)
205203
fmt.Sprintf("value %d", 42)

0 commit comments

Comments
 (0)