Track nullable state for extension properties#84311
Draft
jcouv wants to merge 9 commits into
Draft
Conversation
92b9f11 to
9ae5a5c
Compare
9ae5a5c to
2fe7477
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates C# nullable flow analysis (NullableWalker) to track and consult null-state for extension instance properties, aligning their behavior with regular instance properties and addressing incorrect warnings/flow behavior in extension-property scenarios.
Changes:
- Update member-slot identity and default-state inheritance logic for extension block members in
NullableWalker. - Consult tracked member-slot state on reads of extension properties (mirroring normal member access).
- Add/adjust compiler nullability regression tests for extension properties, including state reset and attribute/postcondition/pattern interactions.
Show a summary per file
| File | Description |
|---|---|
| src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs | Tracks extension property members via receiver parameter type, avoids AsMemberOfType for extension members, and consults member-slot state on extension-property reads. |
| src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs | Adds/updates nullability tests validating extension-property state tracking and adjusts expectations in several pattern/postcondition scenarios. |
| src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests.cs | Adds nullability tests around AllowNull/DisallowNull on extension property setters in object initializers and with expressions (source + metadata). |
Copilot's findings
- Files reviewed: 3/3 changed files
- Comments generated: 2
| // (17,13): warning CS8602: Dereference of a possibly null reference. | ||
| // first.ToString(); // 1 | ||
| Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "first").WithLocation(17, 13)); | ||
| comp.VerifyEmitDiagnostics(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #81913
This was discussed with extensions working group (Aleksey and Mads)
Problem
C#'s nullable flow analysis (
NullableWalker) did not track null-state for extension instance properties (theextension(Receiver) { public T P { get; set; } }feature). As a result:if (o.P != null) o.P.ToString();still warned (the customer-reported false positive from Extensions: Confirm whether we should track nullable state for extension properties #81913).o.P = new object();, a prioro.P = null;still produced spuriousCS8602warnings.Fix
Three changes in
NullableWalker.cs:IsSlotMember— for an extension instance member, key against the extension parameter's (substituted) type viaTryGetInstanceExtensionParameterinstead of the markerContainingType, so the write path can create a stable member slot.VisitPropertyAccess(extension branch) — add read-side slot consultation (MakeMemberSlot+GetState) mirroring the regularVisitMemberAccesspath.InheritDefaultState— for extension block members, use the symbol directly instead ofAsMemberOfType(which asserts the member is not an extension block member and would otherwise crash).The read path keys on the reinferred
updatedPropertywhile the write path keys onnode.PropertySymbol; these resolve to the same slot becauseVariableIdentifier.Equalscompares withTypeCompareKind.AllIgnoreOptions(ignores nullability). Generic extensions work because the binder substitutes type arguments at the concrete access site.Extension indexers are intentionally unaffected — regular indexers aren't slot-tracked either (their identity includes argument values).
Microsoft Reviewers: Open in CodeFlow