Skip to content

Fix crash in foreach deconstruction over null coalescing#84417

Open
adrientoub wants to merge 2 commits into
dotnet:mainfrom
adrientoub:fixCrashForeachDeconstruction
Open

Fix crash in foreach deconstruction over null coalescing#84417
adrientoub wants to merge 2 commits into
dotnet:mainfrom
adrientoub:fixCrashForeachDeconstruction

Conversation

@adrientoub

@adrientoub adrientoub commented Jul 6, 2026

Copy link
Copy Markdown

The UseCollectionInitializer analyzer crashed when analyzing a foreach deconstruction statement (foreach (var (x, y) in ...)) whose collection was a null-coalescing expression over an object creation (dict ?? new Dictionary<string, List<int>>()).

Fixed by adding the missing case and adding related tests to avoid regression on this specific issue.

Found this issue when enabling analyzer on my code base and this prevented all analyzers from running until this pattern was removed.

Microsoft Reviewers: Open in CodeFlow

Copilot AI review requested due to automatic review settings July 6, 2026 23:30
@adrientoub
adrientoub requested review from a team as code owners July 6, 2026 23:30
@dotnet-policy-service dotnet-policy-service Bot added Community The pull request was submitted by a contributor who is not a Microsoft employee. VSCode labels Jul 6, 2026
@adrientoub

Copy link
Copy Markdown
Author

@dotnet-policy-service agree company="Microsoft"

Copilot AI left a comment

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.

Pull request overview

This PR updates the C# compiler semantic model’s declared-symbol dispatch to handle foreach deconstruction statements (ForEachVariableStatementSyntax), and adds an analyzer regression test that exercises the foreach (var (x, y) in dict ?? new Dictionary<...>()) pattern to prevent a crash/regression.

Changes:

  • Add GetDeclaredSymbol(ForEachVariableStatementSyntax) and route SyntaxKind.ForEachVariableStatement through it in GetDeclaredSymbolCore.
  • Add a UseCollectionInitializer collection-expression regression test covering foreach deconstruction over null-coalescing with an object creation.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs Adds semantic-model support for ForEachVariableStatementSyntax in declared-symbol lookup and dispatch.
src/Analyzers/CSharp/Tests/UseCollectionInitializer/UseCollectionInitializerTests_CollectionExpression.cs Adds a regression test to ensure the analyzer/code fix handles (and doesn’t crash on) foreach deconstruction over ?? new Dictionary<...>().

Comment thread src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Comment thread src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs
Comment thread src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs Outdated
@RikkiGibson

Copy link
Copy Markdown
Member

Do you happen to have an exception stack for the original crash? Thanks!

@CyrusNajmabadi

Copy link
Copy Markdown
Contributor

This fix doesn't look right to me. Nodes that have multiple variables should not return the first one and disregard the rest. They should just return nothing. That's how fields work, for example.

/// Given a foreach variable statement, get the symbol for the (first) iteration variable.
/// </summary>
/// <param name="forEachVariableStatement">The foreach variable statement.</param>
public ILocalSymbol GetDeclaredSymbol(ForEachVariableStatementSyntax forEachVariableStatement)

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.

public ILocalSymbol GetDeclaredSymbol(ForEachVariableStatementSyntax forEachVariableStatement)

I do not think we should be adding an API like this, and with behavior like this. It sounds like the consumer should be using public abstract ISymbol GetDeclaredSymbol(SingleVariableDesignationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken)); API to get symbols for the declared locals.

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.

Agreed with Aleksey. Nothing should be passing a ForEachVariableStatementSyntax to GetDeclaredSymbol, and if it does then that component is the broken part.

@AlekseyTs

Copy link
Copy Markdown
Contributor

@adrientoub Please add a link to a GitHub issue you are trying to fix. Open one if it doesn't exist.

@AlekseyTs AlekseyTs left a comment

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.

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

Labels

Community The pull request was submitted by a contributor who is not a Microsoft employee. VSCode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants