-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Move diagnostic generation from LibraryImportGenerator to LibraryImportDiagnosticsAnalyzer #123780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
… diagnostics Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…itional analyzer loading Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…till reports diagnostics) Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
This comment was marked as resolved.
This comment was marked as resolved.
jkoritzinsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot address this review.
...ime.InteropServices/gen/LibraryImportGenerator/Analyzers/LibraryImportDiagnosticsAnalyzer.cs
Show resolved
Hide resolved
...ime.InteropServices/gen/LibraryImportGenerator/Analyzers/LibraryImportDiagnosticsAnalyzer.cs
Outdated
Show resolved
Hide resolved
...raries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/DiagnosticOr.cs
Outdated
Show resolved
Hide resolved
...aries/System.Runtime.InteropServices/tests/Common/Verifiers/CSharpSourceGeneratorVerifier.cs
Show resolved
Hide resolved
…per review feedback Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
...ibraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs
Outdated
Show resolved
Hide resolved
...braries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs
Show resolved
Hide resolved
…null, remove helper method Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…analyzer only Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…y, remove Diagnostics from generator Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…ortDiagnosticsAnalyzer Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Addressed in commits f33ac74 and 9f85c66:
|
|
@copilot all of the test failures look like they're due to diagnostics reported by a marshalling generator resolver not being reported from the analyzers. |
…c verification Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…nalyzer verifier Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
Identified and partially fixed. The issue was that
Test status: 688/703 pass (14 failing). The 13 array marshalling tests are still failing because the analyzer returns 0 diagnostics for "Marshalling an array from unmanaged to managed requires..." cases. The |
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/interop-contrib |
jkoritzinsky
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot please address this feedback.
src/libraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/Comparers.cs
Outdated
Show resolved
Hide resolved
...ibraries/System.Runtime.InteropServices/gen/LibraryImportGenerator/LibraryImportGenerator.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request refactors diagnostic generation for LibraryImport by moving it from the LibraryImportGenerator source generator to a new LibraryImportDiagnosticsAnalyzer, following Roslyn team recommendations to separate diagnostic reporting from code generation.
Changes:
- Created
LibraryImportDiagnosticsAnalyzerthat runs diagnostic logic independently of the generator - Updated
LibraryImportGeneratorto filter invalid methods using the analyzer's validation logic instead of emitting diagnostics - Corrected
CompilationExtensions.GetEnvironmentFlagsto check forDisableRuntimeMarshallingAttributeon the assembly instead of the source module (aligning with the attribute'sAttributeTargets.Assemblyusage)
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
LibraryImportDiagnosticsAnalyzer.cs |
New analyzer that reports LibraryImport diagnostics independently from code generation |
LibraryImportGenerator.cs |
Removed diagnostic emission; now calls analyzer's validation method to filter methods |
Comparers.cs |
Removed Comparers static class wrapper (no longer needed without diagnostics in pipeline) |
CompilationExtensions.cs |
Fixed to check DisableRuntimeMarshallingAttribute on assembly (correct) vs module (incorrect) |
CSharpSourceGeneratorVerifier.cs |
Added TAnalyzer type parameter to support running both generator and analyzer in tests |
CSharpAnalyzerVerifier.cs |
Added constructor that disables compiler diagnostics and SYSLIB1092 by default |
Diagnostics.cs, CompileFails.cs, etc. |
Updated to use analyzer-only testing for diagnostic verification; removed compiler error expectations |
ByValueContentsMarshalling.cs |
Uses analyzer verifier with SYSLIB1092 re-enabled for specific tests |
| ComInterfaceGenerator test files | Updated type parameters to include analyzer types |
| bool foundLibraryImportMethod = false; | ||
| bool unsafeEnabled = context.Compilation.Options is CSharpCompilationOptions { AllowUnsafe: true }; | ||
|
|
||
| context.RegisterSymbolAction(symbolContext => | ||
| { | ||
| if (AnalyzeMethod(symbolContext, env, libraryImportAttrType, options)) | ||
| { | ||
| foundLibraryImportMethod = true; | ||
| } | ||
| }, SymbolKind.Method); | ||
|
|
||
| // Report RequiresAllowUnsafeBlocks once per compilation if there are LibraryImport methods and unsafe is not enabled | ||
| context.RegisterCompilationEndAction(endContext => | ||
| { | ||
| if (foundLibraryImportMethod && !unsafeEnabled) |
Copilot
AI
Feb 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The foundLibraryImportMethod variable is accessed and modified from concurrent RegisterSymbolAction callbacks without synchronization. This creates a race condition since EnableConcurrentExecution() is called on line 51. The variable should be an int field using Interlocked.CompareExchange or wrapped with proper synchronization to ensure thread-safe access across concurrent symbol analysis callbacks.
| bool foundLibraryImportMethod = false; | |
| bool unsafeEnabled = context.Compilation.Options is CSharpCompilationOptions { AllowUnsafe: true }; | |
| context.RegisterSymbolAction(symbolContext => | |
| { | |
| if (AnalyzeMethod(symbolContext, env, libraryImportAttrType, options)) | |
| { | |
| foundLibraryImportMethod = true; | |
| } | |
| }, SymbolKind.Method); | |
| // Report RequiresAllowUnsafeBlocks once per compilation if there are LibraryImport methods and unsafe is not enabled | |
| context.RegisterCompilationEndAction(endContext => | |
| { | |
| if (foundLibraryImportMethod && !unsafeEnabled) | |
| int foundLibraryImportMethod = 0; | |
| bool unsafeEnabled = context.Compilation.Options is CSharpCompilationOptions { AllowUnsafe: true }; | |
| context.RegisterSymbolAction(symbolContext => | |
| { | |
| if (AnalyzeMethod(symbolContext, env, libraryImportAttrType, options)) | |
| { | |
| Interlocked.CompareExchange(ref foundLibraryImportMethod, 1, 0); | |
| } | |
| }, SymbolKind.Method); | |
| // Report RequiresAllowUnsafeBlocks once per compilation if there are LibraryImport methods and unsafe is not enabled | |
| context.RegisterCompilationEndAction(endContext => | |
| { | |
| if (Interlocked.CompareExchange(ref foundLibraryImportMethod, 0, 0) == 1 && !unsafeEnabled) |
| public Test() : base() | ||
| { | ||
| // Ignore compiler diagnostics since we're only testing the analyzer. | ||
| // Without the generator, partial methods won't have implementations which causes CS8795. | ||
| CompilerDiagnostics = CompilerDiagnostics.None; | ||
| // Disable SYSLIB1092 recommendation diagnostic by default (same as source generator tests) | ||
| DisabledDiagnostics.Add(GeneratorDiagnostics.Ids.NotRecommendedGeneratedComInterfaceUsage); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to only disable a set of compiler diagnostics? I worry a typo in a new test case would raise a compiler diagnostic, but cause an expected analyzer diagnostic to not show up, and I could see myself spending a lot of time debugging the analyzer without realizing that it's just a typo error.
| { | ||
| if (AnalyzeMethod(symbolContext, env, libraryImportAttrType, options)) | ||
| { | ||
| foundLibraryImportMethod = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about how analyzers work in VS, is there any caching / incrementality that we might set this to true, then the LibraryImport is deleted, and we still report the message later?
| flags |= EnvironmentFlags.SkipLocalsInit; | ||
| } | ||
| if (compilation.SourceModule.GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute)) | ||
| if (compilation.Assembly.GetAttributes().Any(attr => attr.AttributeClass.ToDisplayString() == TypeNames.System_Runtime_CompilerServices_DisableRuntimeMarshallingAttribute)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we hit a failure that made this change necessary?
🤖 Copilot Code Review — PR #123780Holistic AssessmentMotivation: Moving diagnostic generation from the Approach: The implementation correctly reuses the existing diagnostic infrastructure ( Summary: Detailed Findings
|
Description
Moves diagnostic generation/emit from the
LibraryImportGeneratorsource generator to a newLibraryImportDiagnosticsAnalyzerin the same assembly, per Roslyn team recommendation.Changes Made
1. Created
LibraryImportDiagnosticsAnalyzerSymbolEqualityComparerto compare attribute types instead of string matchingLibraryImportGeneratorOptionsonce per compilation inRegisterCompilationStartActionGetDiagnosticIfInvalidMethodForGenerationisinternal staticso the generator can call it directly2. Updated
LibraryImportGeneratorLibraryImportDiagnosticsAnalyzer.GetDiagnosticIfInvalidMethodForGenerationdirectly to filter methodsDiagnosticOrin the pipelineDiagnosticsfield fromIncrementalStubGenerationContextComparers.GeneratedSyntaxto useSyntaxEquivalentComparer.InstancedirectlyComparersclass static field wrapper3. Updated
CompilationExtensions.GetEnvironmentFlagsDisableRuntimeMarshallingAttributeon the source assembly instead of the module4. Updated Test Infrastructure
TAnalyzertype parameter toCSharpSourceGeneratorVerifierDisabledDiagnostics.Add(GeneratorDiagnostics.Ids.NotRecommendedGeneratedComInterfaceUsage)toCSharpAnalyzerVerifierLibraryImportGeneratoruseLibraryImportDiagnosticsAnalyzerEmptyDiagnosticAnalyzer5. Updated Test Files
Diagnostics.cs: UsesCSharpAnalyzerVerifier(analyzer-only testing)CompileFails.cs: UsesCSharpAnalyzerVerifierfor diagnostic testsByValueContentsMarshalling.cs: Uses analyzer verifier with SYSLIB1092 re-enabled for specific testsTest Results
Key Architectural Decisions
GetDiagnosticIfInvalidMethodForGenerationmethod to share validation logic✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.