Skip to content

xunit/xunit#3423: Suppress CS8618 for members set in InitializeAsync#203

Open
MatanTsach wants to merge 2 commits intoxunit:mainfrom
MatanTsach:fix/3423-suppress-cs8618-async-lifetime
Open

xunit/xunit#3423: Suppress CS8618 for members set in InitializeAsync#203
MatanTsach wants to merge 2 commits intoxunit:mainfrom
MatanTsach:fix/3423-suppress-cs8618-async-lifetime

Conversation

@MatanTsach
Copy link

Summary

Adds a DiagnosticSuppressor that suppresses CS8618 ("Non-nullable field/property must contain a non-null value when exiting constructor") when the member is directly assigned in an IAsyncLifetime.InitializeAsync implementation. Fixes xunit/xunit#3423.

Changes

  • Added NonNullableFieldInitializationSuppressor — walks the InitializeAsync method body looking for direct assignments to the flagged member
  • Added CS8618_Suppression descriptor to Descriptors.Suppressors.cs
  • Added VerifyCompilerWarningSuppressor test helpers to CSharpVerifier.Suppressors.cs (sets CompilerDiagnostics.Warnings so CS8618 is included in analysis)
  • Added 6 test cases covering: field/property suppression, non-IAsyncLifetime classes, uninitialized fields, this.-qualified access, and selective suppression with multiple fields

Design

The suppressor:

  1. Checks if the containing type implements IAsyncLifetime
  2. Finds the InitializeAsync implementation via FindImplementationForInterfaceMember
  3. Walks assignment expressions in the method body
  4. Suppresses only if the specific flagged member is directly assigned

Known Limitations

  • Only detects direct assignments in InitializeAsync body — assignments through helper methods called from InitializeAsync are not detected (conservative approach)
  • When a class has an explicit constructor, CS8618 may be reported on the constructor rather than the field/property (Roslyn behavior per AdditionalLocations for CS8618 in DiagnosticSuppressor dotnet/roslyn#58073). The suppressor handles the field/property location case, which covers the typical IAsyncLifetime pattern

Testing

  • 6 new tests, all passing
  • Full test suite: 3203 tests, 0 failures

Add a DiagnosticSuppressor that suppresses CS8618 (non-nullable field/property
not initialized) when the member is directly assigned in an IAsyncLifetime.InitializeAsync
implementation. Handles both fields and properties, including this-qualified access.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@MatanTsach
Copy link
Author

@dotnet-policy-service agree

When a class has an explicit constructor, CS8618 fires on the
constructor rather than the field/property. The suppressor now
resolves the target member by checking AdditionalLocations and
falling back to parsing the member name from the diagnostic message.

Also adds V3 (ValueTask-based IAsyncLifetime) test coverage for all
scenarios, bringing total tests from 6 to 16.

Inspired by edge cases identified by @ssaporito in #3423.
@MatanTsach
Copy link
Author

Hi, just checking in — is there anything else needed from my side on this PR? Happy to make adjustments if needed.

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.

Non-null value warnings for class members set in InitializeAsync are not suppressed

1 participant