Skip to content

Add code action to convert .forEach to for-in#2566

Closed
MoonMao42 wants to merge 1 commit intoswiftlang:mainfrom
MoonMao42:convert-foreach-to-forin
Closed

Add code action to convert .forEach to for-in#2566
MoonMao42 wants to merge 1 commit intoswiftlang:mainfrom
MoonMao42:convert-foreach-to-forin

Conversation

@MoonMao42
Copy link
Copy Markdown

@MoonMao42 MoonMao42 commented Mar 18, 2026

Closes #2509

Add a code action that converts .forEach closure calls to for-in loops.

Before:

let names = ["Alice", "Bob", "Charlie"]
names.forEach { name in
  print(name)
}

After:

let names = ["Alice", "Bob", "Charlie"]
for name in names {
  print(name)
}

Currently only handles closures with explicit named parameters ($0 shorthand is not supported).

}
let providersAndKinds: [(provider: CodeActionProvider, kind: CodeActionKind?)] = [
(retrieveSyntaxCodeActions, nil),
(retrieveConvertForEachToForInCodeActions, .refactorInline),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m just looking at pull requests for my issues and wouldn’t want to write something that would make you implement code before a maintainer has properly reviewed it, but to me it seems weird to have a provider just for this single code action.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current structure was mainly driven by the need to incorporate cursorInfo as suggested by @ahoppen, ensuring we only target Sequence.forEach and not custom implementations. Since this requires an asynchronous semantic check, it doesn't currently fit into the synchronous SyntaxCodeActionProvider.
Perhaps we can wait for @ahoppen or @rintaro to weigh in on the preferred architectural direction.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed this offline.

In general, if a CodeAction requires a heavy operation, it should go through the codeAction/resolve approach that @Tabonx is working on. (see #2549).

In this case, though, applicability depends on cursorInfo, so it's not great to assume .forEach is applicable up front, only for codeAction/resolve to fail later because it turns out not to refer to the stdlib symbol.

Because of that, we think it makes sense to introduce a mechanism to have a shared cursorInfo across all SyntaxCodeActionProviders in allSyntaxCodeActions. Maybe SyntaxCodeActionScope could expose a way to retrieve the cursorInfo for that scope.

One important consideration is minimizing cursorInfo requests. Ideally, this should be lazy, since no code actions may actually need cursorInfo for the scope. It would also be great to reuse the same cursorInfo in retrieveSyntaxCodeActions.

Yes, that would be a fairly significant change to the current code action infrastructure, so it's not something we should include in this PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed an issue for this #2584

@MoonMao42
Copy link
Copy Markdown
Author

Superseded by a new PR that includes the shared cursorInfo infrastructure from #2584.

@MoonMao42 MoonMao42 closed this Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Convert forEach to for-in loop code action

3 participants