Skip to content

cmd/fix: x/tools/go/analysis/passes/modernize: slicescontains hoists needle expression, changing side effect count #77564

@findleyr

Description

@findleyr

The slicescontains modernizer rewrites a for/range loop with an equality check into slices.ContainsFunc (or slices.Contains), but this changes how many times the needle expression is evaluated. In the original loop, the needle is evaluated once per iteration (until break). With slices.ContainsFunc, the needle is hoisted and evaluated once before iteration.

Before (go fix):

package main

import "fmt"

func main() {
	n := 0
	f := func() int { n++; return 3 }
	s := []int{1, 2, 3}
	found := false
	for _, elem := range s {
		if elem == f() {
			found = true
			break
		}
	}
	_ = found
	fmt.Println(n)
}

Output: 3 (f() called once per iteration: elem=1 vs 1, elem=2 vs 2, elem=3 vs 3 — match, break)

After (GOTOOLCHAIN=go1.26.0 go fix):

package main

import (
	"fmt"
	"slices"
)

func main() {
	n := 0
	f := func() int { n++; return 3 }
	s := []int{1, 2, 3}
	found := slices.Contains(s, f())
	_ = found
	fmt.Println(n)
}

Output: 1 (f() called once before iteration, needle=3 used for all comparisons)

The modernizer should not apply the transformation when the needle expression has side effects.

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