Skip to content

Commit 83aca55

Browse files
madelinekalilgopherbot
authored andcommitted
go/analysis/passes/modernize: fmtappendf: remove whitespace
The text edits that remove "[]byte" from multi-line conversions like return []byte( fmt.Sprintf(...), ) do not delete the space after the first "(" and before the last ")", which can result in invalid code like: return fmt.Appendf(...) This CL corrects the range to delete to include spaces or other non-argument characters. Unfortunately, this may result in deleting comments. Also, add fmt.Sprint to the list of formatters that have different nil-semantics from their Append counterparts. (See CL 745540) Fixes golang/go#77557, golang/go#77821 Change-Id: Ia9e9325f7e77acabc52e8e084ce3c465b3a6b9d7 Reviewed-on: https://go-review.googlesource.com/c/tools/+/749521 Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Madeline Kalil <mkalil@google.com>
1 parent 6a2886b commit 83aca55

File tree

3 files changed

+31
-27
lines changed

3 files changed

+31
-27
lines changed

go/analysis/passes/modernize/fmtappendf.go

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ func fmtappendf(pass *analysis.Pass) (any, error) {
5454
if len(call.Args) == 0 {
5555
continue
5656
}
57-
// fmt.Sprintf and fmt.Appendf have different nil semantics
57+
// fmt.Sprint(f) and fmt.Append(f) have different nil semantics
5858
// when the format produces an empty string:
59-
// ([]byte(fmt.Sprintf("")) returns an empty but non-nil
59+
// []byte(fmt.Sprintf("")) returns an empty but non-nil
6060
// []byte{}, while fmt.Appendf(nil, "") returns nil) so we
6161
// should skip these cases.
62-
if fn.Name() == "Sprintf" {
62+
if fn.Name() == "Sprint" || fn.Name() == "Sprintf" {
6363
format := info.Types[call.Args[0]].Value
6464
if format != nil && mayFormatEmpty(constant.StringVal(format)) {
6565
continue
@@ -78,13 +78,18 @@ func fmtappendf(pass *analysis.Pass) (any, error) {
7878
old, new := fn.Name(), strings.Replace(fn.Name(), "Sprint", "Append", 1)
7979
edits := []analysis.TextEdit{
8080
{
81-
// delete "[]byte("
81+
// Delete "[]byte(", including any spaces before the first argument.
8282
Pos: conv.Pos(),
83-
End: conv.Lparen + 1,
83+
End: conv.Args[0].Pos(), // always exactly one argument in a valid byte slice conversion
8484
},
8585
{
86-
// remove ")"
87-
Pos: conv.Rparen,
86+
// Delete ")", including any non-args (space or
87+
// commas) that come before the right parenthesis.
88+
// Leaving an extra comma here produces invalid
89+
// code. (See golang/go#74709)
90+
// Unfortunately, this and the edit above may result
91+
// in deleting some comments.
92+
Pos: conv.Args[0].End(),
8893
End: conv.Rparen + 1,
8994
},
9095
{
@@ -97,20 +102,6 @@ func fmtappendf(pass *analysis.Pass) (any, error) {
97102
NewText: []byte("nil, "),
98103
},
99104
}
100-
if len(conv.Args) == 1 {
101-
arg := conv.Args[0]
102-
// Determine if we have T(fmt.SprintX(...)<non-args,
103-
// like a space or a comma>). If so, delete the non-args
104-
// that come before the right parenthesis. Leaving an
105-
// extra comma here produces invalid code. (See
106-
// golang/go#74709)
107-
if arg.End() < conv.Rparen {
108-
edits = append(edits, analysis.TextEdit{
109-
Pos: arg.End(),
110-
End: conv.Rparen,
111-
})
112-
}
113-
}
114105
if !analyzerutil.FileUsesGoVersion(pass, astutil.EnclosingFile(curCall), versions.Go1_19) {
115106
continue
116107
}

go/analysis/passes/modernize/testdata/src/fmtappendf/fmtappendf.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func emptystring() {
4747
// empty string edge case only applies to Sprintf
4848
_ = []byte(fmt.Sprintln("")) // want "Replace .*Sprintln.* with fmt.Appendln"
4949
// nope - these return []byte{}, while the fmt.Append version returns nil
50-
_ = []byte(fmt.Sprintf(""))
50+
_ = []byte(fmt.Sprint(""))
5151
_ = []byte(fmt.Sprintf("%s", ""))
5252
_ = []byte(fmt.Sprintf("%#s", ""))
5353
_ = []byte(fmt.Sprintf("%s%v", "", getString()))
@@ -58,6 +58,15 @@ func emptystring() {
5858
_ = []byte(fmt.Sprintf("%vother", "")) // want "Replace .*Sprint.* with fmt.Appendf"
5959
}
6060

61+
func multiline() []byte {
62+
_ = []byte( // want "Replace .*Sprintf.* with fmt.Appendf"
63+
fmt.Sprintf("str %d", 1))
64+
65+
return []byte( // want "Replace .*Sprintf.* with fmt.Appendf"
66+
fmt.Sprintf("str %d", 1),
67+
)
68+
}
69+
6170
func getString() string {
6271
return ""
6372
}

go/analysis/passes/modernize/testdata/src/fmtappendf/fmtappendf.go.golden

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,17 @@ func comma() {
3333
type S struct{ Bytes []byte }
3434
var _ = struct{ A S }{
3535
A: S{
36-
Bytes: // want "Replace .*Sprint.* with fmt.Appendf"
37-
fmt.Appendf(nil, "%d", 0),
36+
Bytes: fmt.Appendf(nil, "%d", 0),
3837
},
3938
}
40-
_ = // want "Replace .*Sprint.* with fmt.Appendf"
41-
fmt.Appendf(nil, "%d", 0)
39+
_ = fmt.Appendf(nil, "%d", 0)
4240
}
4341

4442
func emptystring() {
4543
// empty string edge case only applies to Sprintf
4644
_ = fmt.Appendln(nil, "") // want "Replace .*Sprintln.* with fmt.Appendln"
4745
// nope - these return []byte{}, while the fmt.Append version returns nil
48-
_ = []byte(fmt.Sprintf(""))
46+
_ = []byte(fmt.Sprint(""))
4947
_ = []byte(fmt.Sprintf("%s", ""))
5048
_ = []byte(fmt.Sprintf("%#s", ""))
5149
_ = []byte(fmt.Sprintf("%s%v", "", getString()))
@@ -56,6 +54,12 @@ func emptystring() {
5654
_ = fmt.Appendf(nil, "%vother", "") // want "Replace .*Sprint.* with fmt.Appendf"
5755
}
5856

57+
func multiline() []byte {
58+
_ = fmt.Appendf(nil, "str %d", 1)
59+
60+
return fmt.Appendf(nil, "str %d", 1)
61+
}
62+
5963
func getString() string {
6064
return ""
6165
}

0 commit comments

Comments
 (0)