Skip to content

x/tools/go/analysis/passes/modernize: stringscutprefix changes semantics for empty prefix #77563

@findleyr

Description

@findleyr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugReportIssues describing a possible bug in the Go implementation.NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.ToolsThis label describes issues relating to any tools in the x/tools repository.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions