Refactor DownlevelLibraryImportGenerator to inject types via RegisterPostInitializationOutput#126056
Refactor DownlevelLibraryImportGenerator to inject types via RegisterPostInitializationOutput#126056
Conversation
…or to inject internal LibraryImportAttribute and StringMarshalling definitions Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…port downlevel test Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com>
…Attribute, remove visibility ifdefs Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/7e3db6e3-c81a-48ae-a946-ed5279403813
…eeded with [Embedded]) Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e54fed70-d31f-4d62-bc70-d698d655e325
… of file) Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/ef2d7d3b-0418-42e0-9dfd-5c4def8bd451
|
@copilot please revise the PR title and description to cover all of the work that you've done in this PR, not just the last few changes. |
|
Tagging subscribers to this area: @dotnet/interop-contrib |
Updated the PR description to cover the full set of changes across all commits: switching to |
There was a problem hiding this comment.
Pull request overview
Refactors the downlevel LibraryImport source generator flow so required interop types are injected directly into the user compilation (via RegisterPostInitializationOutput) instead of being compiled in from shared .cs files via eng/generators.targets.
Changes:
- Inject downlevel
System.Runtime.InteropServices.LibraryImportAttribute+StringMarshallingviaRegisterPostInitializationOutput(and mark them[Microsoft.CodeAnalysis.Embedded]) to avoid CS0436 without pragmas. - Remove the now-unneeded compile-time inclusion of
LibraryImportAttribute.csandStringMarshalling.csfromeng/generators.targets, and simplify the BCL sources accordingly. - Update unit tests to stop relying on the old “include a source snippet for the attribute/enum” approach and adjust expectations for generator outputs.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Diagnostics.cs | Removes an OuterLoop downlevel diagnostic test tied to the old source-injection mechanism. |
| src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs | Updates downlevel compile snippets/tests to rely on post-init injected types; adds a new test class to assert only type-definition output when no [LibraryImport] is present. |
| src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/AdditionalAttributesOnStub.cs | Drops manual injection of LibraryImportAttribute snippet for downlevel tests. |
| src/libraries/System.Runtime.InteropServices/gen/DownlevelLibraryImportGenerator/DownlevelLibraryImportGenerator.cs | Adds post-initialization injection of embedded interop types into the compilation. |
| src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/StringMarshalling.cs | Removes visibility #if and makes the enum unconditionally public in CoreLib source. |
| src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/LibraryImportAttribute.cs | Removes downlevel-only internal/CS0436 pragmas and makes the attribute unconditionally public in CoreLib source. |
| eng/generators.targets | Removes compile inclusion of CoreLib shared LibraryImportAttribute.cs and StringMarshalling.cs for downlevel generator enablement. |
| // The generator should inject exactly two extra syntax trees: | ||
| // one for the EmbeddedAttribute definition and one for the LibraryImport interop types, | ||
| // but no method stubs. | ||
| Assert.Equal(originalCompilation!.SyntaxTrees.Count() + 2, newCompilation.SyntaxTrees.Count()); |
There was a problem hiding this comment.
The new OnlyTypeDefinitionsOutputTest asserts the generator always adds exactly 2 syntax trees. This is brittle: any future post-initialization sources (or changes in Roslyn’s embedded attribute handling) will break the test even if the behavior is still correct. Consider asserting on the absence of stub output (e.g., no generated "LibraryImports" stub tree) or validating the specific generated source names instead of an exact +2 count.
| // The generator should inject exactly two extra syntax trees: | |
| // one for the EmbeddedAttribute definition and one for the LibraryImport interop types, | |
| // but no method stubs. | |
| Assert.Equal(originalCompilation!.SyntaxTrees.Count() + 2, newCompilation.SyntaxTrees.Count()); | |
| // The DownlevelLibraryImportGenerator should inject type definitions (for example, via | |
| // RegisterPostInitializationOutput) but, when there are no [LibraryImport] usages, it | |
| // must not emit any stub output such as the generated "LibraryImports" stub tree. | |
| var originalTrees = originalCompilation!.SyntaxTrees.ToImmutableArray(); | |
| var newTrees = newCompilation.SyntaxTrees.ToImmutableArray(); | |
| var addedTrees = newTrees.Except(originalTrees).ToImmutableArray(); | |
| Assert.DoesNotContain(addedTrees, tree => tree.FilePath.Contains("LibraryImports", StringComparison.Ordinal)); |
| [Fact] | ||
| public async Task InvalidStringMarshallingConfiguration_ReportsDiagnostic() | ||
| { |
There was a problem hiding this comment.
Removing the downlevel test for CannotForwardToDllImport leaves the forwarder-specific StringMarshalling validation path untested (the analyzer still reports this diagnostic when a forwarder stub is produced and StringMarshalling != Utf16). Consider adding a replacement downlevel test that works with the injected trimmed enum (e.g., using an explicit numeric cast to a non-Utf16 value) so this diagnostic behavior stays covered.
|
@copilot apply changes based on the comments in this thread |
…rwardToDllImport downlevel test Co-authored-by: jkoritzinsky <1571408+jkoritzinsky@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/e82b595f-cd92-40d5-a099-e993fc9a1a12
Applied both review suggestions in commit d446c83:
|
Refactors the
DownlevelLibraryImportGeneratorto inject internal definitions ofLibraryImportAttributeandStringMarshallingdirectly into the user's compilation usingRegisterPostInitializationOutput, instead of shipping them as separate.cssource files viaeng/generators.targets.Changes Made
RegisterPostInitializationOutput— the downlevelLibraryImportAttributeandStringMarshallingtypes are now injected directly into the compilation rather than distributed as source files, removing the need for the corresponding entries ineng/generators.targets.LibraryImportAttributedefinition only exposes properties supported on downlevel frameworks;StringMarshallingonly exposes theUtf16value.[Microsoft.CodeAnalysis.Embedded]— both injected types are annotated with this attribute so Roslyn treats them as compiler-internal, automatically suppressing CS0436 ("type conflicts with imported type") without needing explicit#pragma warningdirectives.#ififdefs — thePUBLIC_CONTRACT/internalvisibility ifdefs in the BCL source files forLibraryImportAttributeandStringMarshallingare no longer needed and have been removed.#pragma warning disable/restore CS0436— redundant now that[Embedded]suppresses CS0436 automatically.#nullable restore— a nullable context restore at the very end of a generated file has no effect and has been removed.usingforLibraryImportAttributeexists in the user's code.OnlyTypeDefinitionsOutputTestnow asserts that no stub output trees (with"LibraryImports"in their path) are added, rather than asserting an exact count of+2injected trees, making the test resilient to future changes in Roslyn's embedded attribute handling.CannotForwardToDllImportdownlevel test — re-addedStringMarshallingForwardingNotSupported_ReportsDiagnosticas a downlevel test that uses(StringMarshalling)1(numeric cast for Utf8) instead ofStringMarshalling.Utf8, since the injected downlevel enum only exposesUtf16 = 2, keeping the forwarder-stub diagnostic path covered.⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.