Repository navigation
Copy MemberNotNull(When) and the trim/AOT attributes; fix resolver leak into the host runtime - #5
Open
christophwille wants to merge 1 commit into
Open
christophwille wants to merge 1 commit into
christophwille wants to merge 1 commit into
Conversation
Copy MemberNotNull, MemberNotNullWhen, RequiresUnreferencedCode, RequiresDynamicCode, RequiresAssemblyFiles and DynamicallyAccessedMembers from the annotated reference assemblies. Missing attribute types (and the DynamicallyAccessedMemberTypes enum) are created as internal types, as for the existing nullable attributes. Task.IsFaulted carries [MemberNotNullWhen(true, nameof(Exception))]; without it, annotated references made `task.IsFaulted ? task.Exception.Message : ...` warn CS8602. Also fix two existing bugs that the new attributes exposed: - AssemblyResolver loaded dependencies without itself as their resolver, so Cecil's default resolver found the build host's runtime (dotnet MSBuild only). For netcoreapp2.x references the weaver then used the host's System.Private.CoreLib attribute types. - The attribute constructor lookup also matched property setters such as set_Url(string). Tests: a Task.IsFaulted line in the integration test sources, and ProgramMainTests, which runs Program.Main end to end on netstandard2.0's netstandard.dll and checks the resolver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Summary
Addresses follow-up 6 of #1: the weaver only copied the C# 8 nullable attributes. It now also copies:
MemberNotNullWhenTask.IsFaulted)MemberNotNullRequiresUnreferencedCodeRequiresDynamicCodeDynamicallyAccessedMembers(+DynamicallyAccessedMemberTypesenum)RequiresAssemblyFilesCounts come from scanning
Microsoft.NETCore.App.Ref10.0.14ref/net10.0with System.Reflection.Metadata.When the target framework doesn't define one of these types, it is created as an internal type in the output assembly, the same way as the existing nullable attributes. The provided types are ported from tunnelvisionlabs#95 with the namespace changed.
While testing, the new attributes exposed two existing weaver bugs, both fixed here (see Bugs fixed).
The
Task.IsFaultedfalse positiveTask.IsFaultedcarries[MemberNotNullWhen(true, nameof(Exception))], and the API exists on netstandard and .NET Framework. The annotator madeTask.Exceptionnullable (AggregateException?) but dropped that guarantee. So against annotated references this warned:Verified red/green with
tests/SingleTFM(netstandard2.0): CS8602 with the previous package, clean with this one. The compiler honors the attribute from metadata atLangVersion 8, which the test projects use.Decisions and alternatives
1. Trim/AOT attributes: copied, although the SDK ignores them for these TFMs
I measured this with a probe project: the .NET SDK never runs the trim/AOT analyzers on netstandard2.0 or net48. With
IsTrimmable/IsAotCompatible, or even withEnableTrimAnalyzer=trueforced, it emits NETSDK1210/NETSDK1212, and the ILLink analyzer is not added. An override carrying[RequiresUnreferencedCode]whose base method lacks it reports no IL2046 on netstandard2.0, but does on net8.0. The annotator only produces .NET Framework, .NET Standard and .NETCoreApp < 3.0 references, so these attributes have no effect in normal SDK builds.2. Not copied:
DynamicDependency,UnconditionalSuppressMessageBoth have 0 uses in the .NET 10 reference assemblies, because they only appear in implementation code. Skipping them also avoids tunnelvisionlabs#95's
Type-typed constructor arguments, which would need importing across modules.3. Named arguments are still skipped
Annotateskips any attribute that has named arguments. tunnelvisionlabs#95 added code to copy them but kept that skip, so its copy code never ran. Only 3 of 812RequiresUnreferencedCodeuses have one (Url = ...), and those 3 stay uncopied.Url, is a small follow-up if those APIs matter.4. PolySharp: not used
As in #1, PolySharp would replace the shipped
NullableAttributes.cs/.vb, which changes what every consumer compiles. Nothing here needs it.5.
NullableAttributes.cs/.vbunchangedAdding
MemberNotNull(When)to the source file shipped to consumers would let netstandard/netfx consumers use these attributes in their own code._FrameworkIncludesNullableAttributes), so they would need their own condition.6. Known behavior that this extends
Every attribute of interest is created in every annotated assembly, whether used or not (
AddAttributeOfInterestresolves the type up front). This adds 6 more internal types per annotated assembly, the same as for the nullable attributes today.Bugs fixed
AssemblyResolverleaked into the build host's runtime (dotnet MSBuild only). Dependencies were loaded withReaderParametersthat had noAssemblyResolver. Cecil's default resolver, which on .NET Core searches the running runtime, then resolved their references. For netcoreapp2.x,Microsoft.CSharp.dllreferencesnetstandard. The facade's forward ofSystem.Objectwas resolved in the host's shared runtime, so the weaver used the host's publicSystem.Private.CoreLibattribute types instead of creating its own. The old attributes survived this by accident. With the new ones, the build crashed (MSB4018,Sequence contains more than one matching element) on netcoreapp2.0/2.1, MultiTFM and NoDisabledWarnings.msbuild.exe(the net472 task) was not affected. The resolver now passes itself to the assemblies it loads.dotnet, outputs for affected references now use their own internal attribute types instead of references into the host runtime's CoreLib. That is whatmsbuild.exealready did.IsMatchingConstructordidn't checkIsConstructor, so a real attribute type'sset_Url(string)matched the(string)constructor. It now only considers constructors. This was the immediate cause of the crash in 1.Tests
tests/{SingleTFM,MultiTFM,NoDisabledWarnings}/Class1.csandtests/WPF/UserControl1.xaml.csget theTask.IsFaultedline under#if !NET35 && !NETCOREAPP3_0 && !NETCOREAPP3_1. net35 has noTask; netcoreapp3.x isn't annotated and its own references lack the attribute. It is not added toRefVersion3.1.0, because the 3.1 references lack the attribute too.ProgramMainTests(new): runsProgram.Mainend to end on netstandard2.0'snetstandard.dll(from aNETStandard.Library2.0.3PackageDownload), annotated from the net10.0netstandard.dllthe test project compiles against. TheMicrosoft.NETCore.App.Refdownload stays removed there because of NU1505. It reads the output back and checks:Task.IsFaultedgetsMemberNotNullWhen(true, "Exception")Type.GetMethods(BindingFlags)getsDynamicallyAccessedMemberswith value 24 (PublicMethods | NonPublicMethods), which covers the enum argument end to endAssembly.GetTypes/Type.MakeGenericType/Assembly.GetFilesgetRequiresUnreferencedCode/RequiresDynamicCode/RequiresAssemblyFilesAssemblyResolver_LoadsDependenciesWithItself): resolvingObjectthrough theSystem.Runtimefacade must end in the search directory. It fails without the fix and passes with it.This is also the first end-to-end test of the weaver that doesn't need a full MSBuild run.
Verification
dotnet build ReferenceAssemblyAnnotator.slnx -c Release: clean (warnings as errors, StyleCop)dotnet test --project ICSharpCode.ReferenceAssemblyAnnotator.Tests -c Release --no-build: 25/25./build/test.ps1 -Configuration Release -Engine dotnet: 17/17./build/test.ps1 -Configuration Release -Engine msbuild: 17/17🤖 Generated with Claude Code