Skip to content

x/tools/gopls/internal/analysis/modernize: rangeint: transformation unsound when loop body has effects on iterated array #72917

Closed
@jba

Description

@jba

Go version

x/tools/gopls v0.18.1

Output of go env in your module/workspace:

irrelevant

What did you do?

This program prints 1, 2, 3 4:

func main() {
	s := []int{1, 2, 3}
	for i := 0; i < len(s); i++ {
		fmt.Println(s[i])
		if s[i] == 2 {
			s = append(s, 4)
		}
	}
}

What did you see happen?

Gopls wants to modernize the for loop to for i := range len(s).
Since the range expression is evaluated only once instead of each time through the loop, the semantics change, and the program prints 1, 2 3.

What did you expect to see?

Behavior unchanged.

Activity

added
ToolsThis label describes issues relating to any tools in the x/tools repository.
goplsIssues related to the Go language server, gopls.
on Mar 18, 2025
added this to the Unreleased milestone on Mar 18, 2025
adonovan

adonovan commented on Mar 18, 2025

@adonovan
Member

Thanks for reporting. (Curious: was this an encountered bug or an a priori deduction?)

Pretty much all our uses of equalSyntax in modernizers have some kind of exploitable edge case like this. Depending on how strict we want to be about aliasing and side effects, we may need to downgrade a number of modernizers if we are to achieve our goal of "first do no harm". Seeing how many subtle bugs we have had so far even among the easy cases, I am starting to wonder whether (continuing the medical analogy) modernizers are mere cosmetic surgery, a procedure of no medical benefit that is not without risk, and whether batch modernization is ever something we should recommend.

The benefits may be clearer in cases of interactive modernization, when the user is already critically studying the code (to stretch the metaphor, a form of informed consent), or in the LLM-inference feedback loop, where the user has indicated a penchant for backstreet brain surgery. ;-)

changed the title [-]x/tools/gopls/internal/analysis/modernize: a three-way for loop with changes in the loop body cannot be changed to a range[/-] [+]x/tools/gopls/internal/analysis/modernize: rangeint: transformation unsound when loop body has effects on iterated array[/+] on Mar 18, 2025
jba

jba commented on Mar 18, 2025

@jba
ContributorAuthor

Found it during actual use.

findleyr

findleyr commented on Mar 19, 2025

@findleyr
Member

Milestoning for next minor.

adonovan

adonovan commented on Mar 19, 2025

@adonovan
Member

This bug caused a bad transformation in the regexp package too:

func (m *machine) step(runq, nextq *queue, pos, nextPos int, c rune, nextCond *lazyFlag) {
	longest := m.re.longest
	for j := 0; j < len(runq.dense); j++ { // <--- don't use for/range: the length changes!
...
				runq.dense = runq.dense[:0]
	}

Fortunately it was caught by tests.

9 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

ToolsThis label describes issues relating to any tools in the x/tools repository.goplsIssues related to the Go language server, gopls.

Type

No type

Projects

No projects

Relationships

None yet

    Development

    No branches or pull requests

      Participants

      @adonovan@gopherbot@jba@findleyr

      Issue actions

        x/tools/gopls/internal/analysis/modernize: rangeint: transformation unsound when loop body has effects on iterated array · Issue #72917 · golang/go