Skip to content

Commit

Permalink
feature: fmt.Sprintf with one argument optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
catenacyber committed Oct 25, 2023
1 parent 8c0a424 commit 9cd4cff
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 13 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ perfsprint --fix ./...
To disable int/uint cast, you can use the flag `-int-conversion=false`

To disable `fmt.Errorf` optimization, you can use the flag `-errorf=false`
This optimization is not always equivalent.
The code
```
msg := "format string attack %s"
fmt.Errorf(msg)
```
will panic when its optimized version will not (so it should be safer).

To disable `fmt.Sprintf("toto")` optimization, you can use the flag `-sprintf1=false`
This optimization is not always equivalent.
The code
```
msg := "format string attack %s"
fmt.Sprintf(msg)
```
will panic when its optimized version will not (so it should be safer).

To enable `err.Error()` optimization, you can use the flag `-err-error=true`
This optimization only works when the error is not nil, otherwise the resulting code will panic.
Expand Down
16 changes: 15 additions & 1 deletion analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ type perfSprint struct {
intConv bool
errError bool
errorf bool
sprintf1 bool
}

func newPerfSprint() *perfSprint {
return &perfSprint{intConv: true, errError: false, errorf: true}
return &perfSprint{
intConv: true,
errError: false,
errorf: true,
sprintf1: true,
}
}

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

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

case calledObj == fmtSprintfObj && len(call.Args) == 1:
if n.sprintf1 {
fn = "fmt.Sprintf"
verb = "%s"
value = call.Args[0]
}

case calledObj == fmtSprintfObj && len(call.Args) == 2:
verbLit, ok := call.Args[0].(*ast.BasicLit)
if !ok {
Expand Down
3 changes: 0 additions & 3 deletions analyzer/testdata/src/noconv/p.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ func negative() {

fmt.Sprint("test", 42)
fmt.Sprint(42, 42)
fmt.Sprintf("test")
fmt.Sprintf("%v")
fmt.Sprintf("%d")
fmt.Sprintf("%d", 42, 42)
fmt.Sprintf("%#d", 42)
fmt.Sprintf("value %d", 42)
Expand Down
3 changes: 0 additions & 3 deletions analyzer/testdata/src/noconv/p.go.golden
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ func negative() {

fmt.Sprint("test", 42)
fmt.Sprint(42, 42)
fmt.Sprintf("test")
fmt.Sprintf("%v")
fmt.Sprintf("%d")
fmt.Sprintf("%d", 42, 42)
fmt.Sprintf("%#d", 42)
fmt.Sprintf("value %d", 42)
Expand Down
4 changes: 1 addition & 3 deletions analyzer/testdata/src/p/p.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func positive() {
var s string
fmt.Sprintf("%s", "hello") // want "fmt.Sprintf can be replaced with just using the string"
fmt.Sprintf("%v", "hello") // want "fmt.Sprintf can be replaced with just using the string"
fmt.Sprintf("hello") // want "fmt.Sprintf can be replaced with just using the string"
fmt.Sprint("hello") // want "fmt.Sprint can be replaced with just using the string"
fmt.Sprintf("%s", s) // want "fmt.Sprintf can be replaced with just using the string"
fmt.Sprintf("%v", s) // want "fmt.Sprintf can be replaced with just using the string"
Expand Down Expand Up @@ -197,9 +198,6 @@ func negative() {

fmt.Sprint("test", 42)
fmt.Sprint(42, 42)
fmt.Sprintf("test")
fmt.Sprintf("%v")
fmt.Sprintf("%d")
fmt.Sprintf("%d", 42, 42)
fmt.Sprintf("%#d", 42)
fmt.Sprintf("value %d", 42)
Expand Down
4 changes: 1 addition & 3 deletions analyzer/testdata/src/p/p.go.golden
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func positive() {
var s string
"hello" // want "fmt.Sprintf can be replaced with just using the string"
"hello" // want "fmt.Sprintf can be replaced with just using the string"
"hello" // want "fmt.Sprintf can be replaced with just using the string"
"hello" // want "fmt.Sprint can be replaced with just using the string"
s // want "fmt.Sprintf can be replaced with just using the string"
s // want "fmt.Sprintf can be replaced with just using the string"
Expand Down Expand Up @@ -197,9 +198,6 @@ func negative() {

fmt.Sprint("test", 42)
fmt.Sprint(42, 42)
fmt.Sprintf("test")
fmt.Sprintf("%v")
fmt.Sprintf("%d")
fmt.Sprintf("%d", 42, 42)
fmt.Sprintf("%#d", 42)
fmt.Sprintf("value %d", 42)
Expand Down

0 comments on commit 9cd4cff

Please sign in to comment.