Skip to content

Commit 63a7b87

Browse files
committed
go/analysis/passes/modernize: fix illegal byte argument error produced in stringscut modernizer
This CL fixes a bug in the stringscut modernizer. Previously, an instance of strings.IndexByte(s, 'a') would become strings.Cut(s, 'a') with 'a' being an illegal argument because strings.Cut expects a string, not a byte. If we examine an instance of strings.IndexByte and are about to suggest a modernization, we append an additional edit that transforms the byte literal or byte variable substr arg to a string literal. For example, the following calls to strings.IndexByte would yield the substr arg: strings.IndexByte(_, 'a') -> strings.Cut(_, "a") strings.IndexByte(_, 55) -> strings.Cut(_, "7") b := byte('a') strings.IndexByte(_, b) -> strings.Cut(_, string(b)) For bytes.IndexByte, the second argument must be a converted to a []byte: bytes.IndexByte(_, 'a') -> bytes.Cut(_, []byte{'a'}) Change-Id: I1a7670e95ebdffca5ce7a2a09a103f1a6fee6db3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/716600 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Alan Donovan <adonovan@google.com>
1 parent 1980517 commit 63a7b87

File tree

3 files changed

+72
-2
lines changed

3 files changed

+72
-2
lines changed

go/analysis/passes/modernize/stringscut.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"go/token"
1212
"go/types"
1313
"iter"
14+
"strconv"
1415

1516
"golang.org/x/tools/go/analysis"
1617
"golang.org/x/tools/go/analysis/passes/inspect"
@@ -227,6 +228,49 @@ func stringscut(pass *analysis.Pass) (any, error) {
227228
End: iIdent.End(),
228229
NewText: fmt.Appendf(nil, "%s, %s, %s", beforeVarName, afterVarName, okVarName),
229230
})
231+
// Calls to IndexByte have a byte as their second arg, which
232+
// must be converted to a string or []byte to be a valid arg for Cut.
233+
if obj.Name() == "IndexByte" {
234+
switch obj.Pkg().Name() {
235+
case "strings":
236+
searchByteVal := info.Types[substr].Value
237+
if searchByteVal == nil {
238+
// substr is a variable, e.g. substr := byte('b')
239+
// use string(substr)
240+
edits = append(edits, []analysis.TextEdit{
241+
{
242+
Pos: substr.Pos(),
243+
NewText: []byte("string("),
244+
},
245+
{
246+
Pos: substr.End(),
247+
NewText: []byte(")"),
248+
},
249+
}...)
250+
} else {
251+
// substr is a byte constant
252+
val, _ := constant.Int64Val(searchByteVal) // inv: must be a valid byte
253+
// strings.Cut requires a string, so convert byte literal to string literal; e.g. 'a' -> "a", 55 -> "7"
254+
edits = append(edits, analysis.TextEdit{
255+
Pos: substr.Pos(),
256+
End: substr.End(),
257+
NewText: strconv.AppendQuote(nil, string(byte(val))),
258+
})
259+
}
260+
case "bytes":
261+
// bytes.Cut requires a []byte, so wrap substr in a []byte{}
262+
edits = append(edits, []analysis.TextEdit{
263+
{
264+
Pos: substr.Pos(),
265+
NewText: []byte("[]byte{"),
266+
},
267+
{
268+
Pos: substr.End(),
269+
NewText: []byte("}"),
270+
},
271+
}...)
272+
}
273+
}
230274
pass.Report(analysis.Diagnostic{
231275
Pos: indexCall.Fun.Pos(),
232276
End: indexCall.Fun.End(),

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ func basic_strings_byte(s string) bool {
4343
return i >= 0
4444
}
4545

46+
func basic_strings_byte_int(s string) bool {
47+
i := strings.IndexByte(s, 55) // want "strings.IndexByte can be simplified using strings.Cut"
48+
print(s[:i])
49+
return i >= 0
50+
}
51+
52+
func basic_strings_byte_var(s string) bool {
53+
b := byte('b')
54+
i := strings.IndexByte(s, b) // want "strings.IndexByte can be simplified using strings.Cut"
55+
print(s[:i])
56+
return i >= 0
57+
}
58+
4659
func basic_bytes(b []byte) []byte {
4760
i := bytes.Index(b, []byte("str")) // want "bytes.Index can be simplified using bytes.Cut"
4861
if i >= 0 {

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,20 @@ func wrong_len_arg(s string, substr string) bool {
3838
}
3939

4040
func basic_strings_byte(s string) bool {
41-
before, _, ok := strings.Cut(s, '+') // want "strings.IndexByte can be simplified using strings.Cut"
41+
before, _, ok := strings.Cut(s, "+") // want "strings.IndexByte can be simplified using strings.Cut"
42+
print(before)
43+
return ok
44+
}
45+
46+
func basic_strings_byte_int(s string) bool {
47+
before, _, ok := strings.Cut(s, "7") // want "strings.IndexByte can be simplified using strings.Cut"
48+
print(before)
49+
return ok
50+
}
51+
52+
func basic_strings_byte_var(s string) bool {
53+
b := byte('b')
54+
before, _, ok := strings.Cut(s, string(b)) // want "strings.IndexByte can be simplified using strings.Cut"
4255
print(before)
4356
return ok
4457
}
@@ -53,7 +66,7 @@ func basic_bytes(b []byte) []byte {
5366
}
5467

5568
func basic_index_bytes(b []byte) string {
56-
before, after, ok := bytes.Cut(b, 's') // want "bytes.IndexByte can be simplified using bytes.Cut"
69+
before, after, ok := bytes.Cut(b, []byte{'s'}) // want "bytes.IndexByte can be simplified using bytes.Cut"
5770
if ok {
5871
return string(before)
5972
} else {

0 commit comments

Comments
 (0)