Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 8, 2025

Fix nullable warning in object initializer member using extension Add method

Problem

When using implicit extension member access methods (the new extension feature) in object initializer collection Add calls, unexpected nullable warnings are reported. The warning incorrectly references the implicit receiver parameter instead of the actual user-provided parameter.

Root Cause

For implicit extension member access methods in collection initializers:

  • The receiver is stored in ImplicitReceiverOpt, not in the Arguments array
  • The nullable flow analysis wasn't creating a synthetic receiver argument for analysis
  • This caused parameter-to-argument mapping to be incorrect, leading to spurious warnings

Solution

Modified VisitCollectionElementInitializer in NullableWalker to:

  • Detect implicit extension member access methods (IsExtensionBlockMember())
  • Call ReInferMethodAndVisitArguments for new extension methods when not delaying completion (reusing existing logic)
  • For delayed completion scenarios, manually prepend receiver result after visiting arguments
  • Update addMethodAsMemberOfContainingType and setUpdatedSymbolAsContinuation to handle this case

The fix leverages existing ReInferMethodAndVisitArguments logic to avoid duplication and ensure consistent handling of new extension methods.

Testing

  • Added comprehensive test case covering both valid and nullable argument scenarios
  • All 4480 nullable reference type tests pass
  • All 2052 extension tests pass
Original prompt

This section details on the original issue you should resolve

<issue_title>Unexpected nullable warning in object initializer member using extension Add method</issue_title>
<issue_description>Version Used:

Steps to Reproduce: .NET Lab

#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;

class Program
{
    static void Main()
    {
        IEnumerable<string> items = [];
        _ = new C()
        {
            Items = { items } // unexpected warning CS8620: Argument of type 'IEnumerable<string>' cannot be used for parameter 'list' of type 'List<string>' in 'Ext.extension<string>(List<string>)' due to differences in the nullability of reference types.
        };
    }
}

class C
{
    public List<string> Items { get; set; } = [];
}

static class Ext
{
    extension<T>(List<T> list)
    {
        public void Add(IEnumerable<T> values) => list.AddRange(values);
    }
}

Expected Behavior: No warnings are reported

Actual Behavior: A nullable warning is reported for 'items'. Note that the parameter which is being referenced is also unexpected,

The bug is may be related to the adjustments which are made to extension calls in order to include the receiver argument. We would want to ensure that the scenario behaves similarly to an classic public static void Add<T>(this List<T> list, IEnumerable<T> values) => ...; extension method.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@RikkiGibson
Copy link
Member

@copilot

Regarding:

Classic extension methods set InvokedAsExtensionMethod = true on BoundCall, which flows to BoundCollectionElementInitializer
Implicit extension member access methods (new extensions) do NOT set this flag, causing the receiver to be analyzed incorrectly
This results in nullable flow analysis treating the first argument incorrectly, leading to spurious warnings

This assessment looks incorrect to me. The representation of old and new extension invocations is different, and deliberately so. We most likely shouldn't make any actual changes to the representation of the call in order to fix the nullable analysis. Instead, we need to adjust the code in NullableWalker which is visiting the call, in order to properly understand it. Look at existing places which call 'IsExtensionBlockMember' for examples.

Copilot AI and others added 2 commits November 8, 2025 00:46
Copilot AI changed the title [WIP] Fix unexpected nullable warning in object initializer Fix nullable warning for implicit extension member access in collection initializers Nov 8, 2025
Copilot AI requested a review from RikkiGibson November 8, 2025 00:56
Copilot finished work on behalf of RikkiGibson November 8, 2025 00:56
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/78828")]
public void Nullability_CollectionInitializer_WithImplicitExtensionMemberAccess()
Copy link
Member

Choose a reason for hiding this comment

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

@copilot it looks like the scenario covered by this test is covered just as well by Nullability_CollectionInitializer_WithImplicitExtensionMemberAccess_Nullable. Please delete this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Deleted in commit 93c622a.

…ree representation

Per @RikkiGibson's feedback, the fix now synthesizes the receiver as a temporary argument for nullable analysis within NullableWalker, rather than modifying the representation. This matches the pattern used in ReInferMethodAndVisitArguments for BoundCall nodes.

Co-authored-by: RikkiGibson <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 8, 2025

@copilot

Regarding:
...

You're correct. I've revised the fix in commit 87bc18e to synthesize the receiver as a temporary argument for nullable analysis within NullableWalker, following the same pattern as ReInferMethodAndVisitArguments. The fix now works with argument results without modifying the BoundTree representation.

Copilot AI requested a review from RikkiGibson November 8, 2025 01:11
Copilot finished work on behalf of RikkiGibson November 8, 2025 01:11
Deleted Nullability_CollectionInitializer_WithImplicitExtensionMemberAccess as its scenario is fully covered by Nullability_CollectionInitializer_WithImplicitExtensionMemberAccess_Nullable.

Co-authored-by: RikkiGibson <[email protected]>
Copilot finished work on behalf of RikkiGibson November 8, 2025 01:17
@RikkiGibson
Copy link
Member

@copilot It would be preferable to address the issue by making it so VisitObjectCreationExpressionBase calls ReInferMethodAndVisitArguments, rather than reimplementing logic of ReInferMethodAndVisitArguments.

Per @RikkiGibson's feedback, refactored VisitCollectionElementInitializer to call ReInferMethodAndVisitArguments for new extension methods when not delaying completion, eliminating duplication of its logic.

Co-authored-by: RikkiGibson <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 8, 2025

@copilot It would be preferable to address the issue by making it so VisitObjectCreationExpressionBase calls ReInferMethodAndVisitArguments, rather than reimplementing logic of ReInferMethodAndVisitAr...

Refactored in commit 9101ac6 to call ReInferMethodAndVisitArguments for new extension methods when not delaying completion, avoiding logic duplication.

Copilot finished work on behalf of RikkiGibson November 8, 2025 01:33
@AdmiralSnyder
Copy link
Contributor

@RikkiGibson would you say having the copilot agent doing the implementation here was a net benefit? given that you had to read and correct basically everything it did? (i'm just skimming this from the surface)
it feels to me like an analysis by copilot, with a recommendation on how to fix it, would have been more concise...

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.

Unexpected nullable warning in object initializer member using extension Add method

3 participants