-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
The stringscutprefix modernizer rewrites strings.TrimPrefix/strings.HasPrefix patterns into strings.CutPrefix, but the two have different semantics when the prefix is empty. strings.TrimPrefix(s, "") returns s unchanged (so rest != s is false), while strings.CutPrefix(s, "") returns (s, true), taking the "matched" branch.
Before (go fix):
package main
import (
"fmt"
"strings"
)
func main() {
s := "hello"
prefix := ""
if rest := strings.TrimPrefix(s, prefix); rest != s {
fmt.Println("trimmed:", rest)
} else {
fmt.Println("unchanged")
}
}Output: unchanged
After (GOTOOLCHAIN=go1.26.0 go fix):
package main
import (
"fmt"
"strings"
)
func main() {
s := "hello"
prefix := ""
if rest, ok := strings.CutPrefix(s, prefix); ok {
fmt.Println("trimmed:", rest)
} else {
fmt.Println("unchanged")
}
}Output: trimmed: hello
strings.CutPrefix("hello", "") returns ("hello", true) because the empty string is a valid prefix of any string. The original code compared rest != s, which is always false when the prefix is empty (since TrimPrefix returns the string unchanged). The modernizer should account for the fact that CutPrefix reports success for empty prefixes while the TrimPrefix+equality pattern does not.
CC @adonovan