From 9f93878f44b1b20b8b5aede5d770bb5c0c401c5e Mon Sep 17 00:00:00 2001 From: Christoph Wille Date: Tue, 10 Nov 2026 09:29:14 +0100 Subject: [PATCH] Copy MemberNotNull(When) and the trim/AOT attributes 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 --- CLAUDE.md | 4 +- ...de.ReferenceAssemblyAnnotator.Tests.csproj | 17 +++ .../ProgramMainTests.cs | 104 ++++++++++++++++++ .../AssemblyResolver.cs | 8 +- .../Program.cs | 8 +- ...amicallyAccessedMemberTypesProvidedType.cs | 37 +++++++ ...llyAccessedMembersAttributeProvidedType.cs | 28 +++++ ...ypes+MemberNotNullAttributeProvidedType.cs | 35 ++++++ ...+MemberNotNullWhenAttributeProvidedType.cs | 37 +++++++ ...uiresAssemblyFilesAttributeProvidedType.cs | 30 +++++ ...equiresDynamicCodeAttributeProvidedType.cs | 28 +++++ ...esUnreferencedCodeAttributeProvidedType.cs | 28 +++++ .../WellKnownTypes.cs | 47 ++++++++ tests/MultiTFM/Class1.cs | 5 + tests/NoDisabledWarnings/Class1.cs | 5 + tests/SingleTFM/Class1.cs | 5 + tests/WPF/UserControl1.xaml.cs | 5 + 17 files changed, 425 insertions(+), 6 deletions(-) create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator.Tests/ProgramMainTests.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMemberTypesProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMembersAttributeProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullAttributeProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullWhenAttributeProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresAssemblyFilesAttributeProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresDynamicCodeAttributeProvidedType.cs create mode 100644 ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresUnreferencedCodeAttributeProvidedType.cs diff --git a/CLAUDE.md b/CLAUDE.md index 0a3fa69..0d82574 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -21,7 +21,7 @@ dotnet build ReferenceAssemblyAnnotator.slnx -c Release ## Tests -`ICSharpCode.ReferenceAssemblyAnnotator.Tests` (xUnit v3 on Microsoft Testing Platform, net10.0) unit-tests the comparers and `Program.Annotate`/`AnnotateType` on in-memory Cecil modules built with `CecilBuilder`; it sees the weaver's internals via `InternalsVisibleTo`. `global.json` selects the MTP runner for `dotnet test`. After building the solution: +`ICSharpCode.ReferenceAssemblyAnnotator.Tests` (xUnit v3 on Microsoft Testing Platform, net10.0) unit-tests the comparers and `Program.Annotate`/`AnnotateType` on in-memory Cecil modules built with `CecilBuilder`, and `ProgramMainTests` runs `Program.Main` end to end on netstandard2.0's `netstandard.dll` (from a `NETStandard.Library` download) against the net10.0 one the test project compiles against. The project sees the weaver's internals via `InternalsVisibleTo`. `global.json` selects the MTP runner for `dotnet test`. After building the solution: ``` dotnet test --project ICSharpCode.ReferenceAssemblyAnnotator.Tests -c Release --no-build @@ -42,7 +42,7 @@ dotnet msbuild -restore tests/SingleTFM -p:TestFramework=netstandard2.0 -warnase ``` - `tests/SingleTFM` is parameterized by `-p:TestFramework=` (net35 … net10.0); MultiTFM, RefVersion3.1.0, NoDisabledWarnings (uses `-p:TreatWarningsAsErrors=true`, since `-warnaserror` ignores `WarningsNotAsErrors`) and WPF cover the rest. -- The test projects leave out the `Microsoft.NETCore.App.Ref` `PackageDownload` for .NET Core 3.0+ (netcoreapp3.x, net10.0). Those aren't annotated, and the SDK adds its own download whenever the targeting pack isn't installed, so a second one fails with NU1505. The `GetHashCode` line in the test sources is `#if !NETCOREAPP3_0_OR_GREATER` because only annotated TFMs get `[DisallowNull]` removed (`Program.IsExcludedAnnotation`). +- The test projects leave out the `Microsoft.NETCore.App.Ref` `PackageDownload` for .NET Core 3.0+ (netcoreapp3.x, net10.0). Those aren't annotated, and the SDK adds its own download whenever the targeting pack isn't installed, so a second one fails with NU1505. The `GetHashCode` line in the test sources is `#if !NETCOREAPP3_0_OR_GREATER` because only annotated TFMs get `[DisallowNull]` removed (`Program.IsExcludedAnnotation`). The `Task.IsFaulted` line checks that `MemberNotNullWhen` is copied (CS8602 otherwise); it is left out of `RefVersion3.1.0` and excluded for net35 and netcoreapp3.x, whose references lack the attribute. - Keep the local source path in `tests/NuGet.Config` with forward slashes: with backslashes, source mapping doesn't match it on Linux/macOS (NU1100). CI: `.github/workflows/build.yml` runs the same build, the unit tests and the test script with both engines (Release only) and uploads the nupkg. On the runner, paths in the isolated cache exceed MAX_PATH under `msbuild.exe`. So the workflow passes `-PackagesPath` with a short path outside the checkout; that cache isn't wiped between cases, which is safe because a run only ever contains one annotator version. `test.ps1` also passes `core.longpaths=true` to `git clean`. diff --git a/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ICSharpCode.ReferenceAssemblyAnnotator.Tests.csproj b/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ICSharpCode.ReferenceAssemblyAnnotator.Tests.csproj index 1cff9b2..6f03d45 100644 --- a/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ICSharpCode.ReferenceAssemblyAnnotator.Tests.csproj +++ b/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ICSharpCode.ReferenceAssemblyAnnotator.Tests.csproj @@ -14,8 +14,25 @@ + + + + + + <_AnnotatedNetStandard Include="@(Reference)" Condition="'%(Filename)' == 'netstandard'" /> + + <_Parameter1>UnannotatedNetStandard + <_Parameter2>$([MSBuild]::EnsureTrailingSlash($(NuGetPackageRoot)))netstandard.library/2.0.3/build/netstandard2.0/ref/netstandard.dll + + + <_Parameter1>AnnotatedNetStandard + <_Parameter2>@(_AnnotatedNetStandard->'%(FullPath)') + + + + diff --git a/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ProgramMainTests.cs b/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ProgramMainTests.cs new file mode 100644 index 0000000..91b6bad --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator.Tests/ProgramMainTests.cs @@ -0,0 +1,104 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator.Tests +{ + using System; + using System.Collections.Immutable; + using System.IO; + using System.Linq; + using System.Reflection; + using Mono.Cecil; + using Xunit; + + /// + /// Runs end to end: annotates netstandard2.0's netstandard.dll and reads the output back. + /// + public class ProgramMainTests + { + private const string CodeAnalysis = "System.Diagnostics.CodeAnalysis"; + + private static readonly Lazy Annotated = new Lazy(AnnotateNetStandard); + + [Fact] + public void CopiesMemberNotNullWhen() + { + var isFaulted = GetType("System.Threading.Tasks", "Task").Properties.Single(property => property.Name == "IsFaulted"); + + var attribute = GetAttribute(isFaulted, "MemberNotNullWhenAttribute"); + + Assert.Equal(new object[] { true, "Exception" }, attribute.ConstructorArguments.Select(argument => argument.Value)); + } + + [Fact] + public void CopiesDynamicallyAccessedMembersWithEnumArgument() + { + var getMethods = GetType("System", "Type").Methods.Single(method => method.Name == "GetMethods" && method.Parameters.Count == 1); + + var attribute = GetAttribute(getMethods, "DynamicallyAccessedMembersAttribute"); + + // PublicMethods | NonPublicMethods + Assert.Equal(8 | 16, (int)attribute.ConstructorArguments.Single().Value); + } + + [Theory] + [InlineData("System.Reflection", "Assembly", "GetTypes", "RequiresUnreferencedCodeAttribute")] + [InlineData("System", "Type", "MakeGenericType", "RequiresDynamicCodeAttribute")] + [InlineData("System.Reflection", "Assembly", "GetFiles", "RequiresAssemblyFilesAttribute")] + public void CopiesRequiresAttributes(string @namespace, string typeName, string methodName, string attributeName) + { + var method = GetType(@namespace, typeName).Methods.First(method => method.Name == methodName); + + Assert.NotEmpty(GetAttribute(method, attributeName).ConstructorArguments); + } + + [Fact] + public void AssemblyResolver_LoadsDependenciesWithItself() + { + string netStandard = GetMetadata("UnannotatedNetStandard"); + using var resolver = new AssemblyResolver(ImmutableArray.Create(Path.GetDirectoryName(netStandard)!)); + + // The System.Runtime facade forwards Object to netstandard, which must not come from the test host's runtime. + var facade = resolver.Resolve(AssemblyNameReference.Parse("System.Runtime"))!; + + Assert.Equal(Path.GetFullPath(netStandard), facade.MainModule.TypeSystem.Object.Resolve().Module.FileName, ignoreCase: true); + } + + [Theory] + [InlineData("MemberNotNullAttribute")] + [InlineData("MemberNotNullWhenAttribute")] + [InlineData("RequiresUnreferencedCodeAttribute")] + [InlineData("RequiresDynamicCodeAttribute")] + [InlineData("RequiresAssemblyFilesAttribute")] + [InlineData("DynamicallyAccessedMembersAttribute")] + [InlineData("DynamicallyAccessedMemberTypes")] + public void SynthesizesInternalTypes(string typeName) + { + Assert.False(GetType(CodeAnalysis, typeName).IsPublic); + } + + private static TypeDefinition GetType(string @namespace, string name) + => Annotated.Value.GetType(@namespace, name) ?? throw new InvalidOperationException($"{@namespace}.{name} not found"); + + private static CustomAttribute GetAttribute(Mono.Cecil.ICustomAttributeProvider provider, string name) + => provider.CustomAttributes.Single(attribute => attribute.AttributeType.FullName == $"{CodeAnalysis}.{name}"); + + private static string GetMetadata(string key) + => typeof(ProgramMainTests).Assembly.GetCustomAttributes().Single(attribute => attribute.Key == key).Value!; + + private static ModuleDefinition AnnotateNetStandard() + { + string input = GetMetadata("UnannotatedNetStandard"); + string output = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".dll"); + try + { + Program.Main(null, input, ImmutableArray.Create(Path.GetDirectoryName(input)!), GetMetadata("AnnotatedNetStandard"), output); + return ModuleDefinition.ReadModule(new MemoryStream(File.ReadAllBytes(output))); + } + finally + { + File.Delete(output); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/AssemblyResolver.cs b/ICSharpCode.ReferenceAssemblyAnnotator/AssemblyResolver.cs index cab015a..621d1a9 100644 --- a/ICSharpCode.ReferenceAssemblyAnnotator/AssemblyResolver.cs +++ b/ICSharpCode.ReferenceAssemblyAnnotator/AssemblyResolver.cs @@ -11,19 +11,21 @@ namespace ICSharpCode.ReferenceAssemblyAnnotator internal sealed class AssemblyResolver : IAssemblyResolver { - private static readonly ReaderParameters DefaultReaderParameters = new ReaderParameters(ReadingMode.Deferred); - private readonly ImmutableArray _searchDirectories; private readonly Dictionary _assembliesByFileName = new Dictionary(); + // Dependencies must resolve through this resolver too; Cecil's default one would find the build host's runtime. + private readonly ReaderParameters _readerParameters; + public AssemblyResolver(ImmutableArray searchDirectories) { _searchDirectories = searchDirectories; + _readerParameters = new ReaderParameters(ReadingMode.Deferred) { AssemblyResolver = this }; } public AssemblyDefinition? Resolve(AssemblyNameReference name) { - return Resolve(name, DefaultReaderParameters); + return Resolve(name, _readerParameters); } public AssemblyDefinition? Resolve(AssemblyNameReference name, ReaderParameters parameters) diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/Program.cs b/ICSharpCode.ReferenceAssemblyAnnotator/Program.cs index 131dde8..9a650ba 100644 --- a/ICSharpCode.ReferenceAssemblyAnnotator/Program.cs +++ b/ICSharpCode.ReferenceAssemblyAnnotator/Program.cs @@ -57,6 +57,12 @@ internal static void Main(SuppressibleLoggingHelper? log, string referenceAssemb AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisNotNullAttribute); AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisNotNullIfNotNullAttribute); AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisNotNullWhenAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisMemberNotNullAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisMemberNotNullWhenAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisRequiresUnreferencedCodeAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisRequiresDynamicCodeAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisRequiresAssemblyFilesAttribute); + AddAttributeOfInterest(attributesOfInterest, wellKnownTypes.SystemDiagnosticsCodeAnalysisDynamicallyAccessedMembersAttribute); AnnotateAssembly(log, assemblyDefinition, annotatedAssemblyDefinition, attributesOfInterest); @@ -216,7 +222,7 @@ internal static void Annotate(ModuleDefinition module, ICustomAttributeProvider static bool IsMatchingConstructor(MethodDefinition constructor, CustomAttribute customAttribute) { - if (constructor.Parameters.Count != customAttribute.ConstructorArguments.Count) + if (!constructor.IsConstructor || constructor.Parameters.Count != customAttribute.ConstructorArguments.Count) return false; for (int i = 0; i < constructor.Parameters.Count; i++) diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMemberTypesProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMemberTypesProvidedType.cs new file mode 100644 index 0000000..5d97af1 --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMemberTypesProvidedType.cs @@ -0,0 +1,37 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using Mono.Cecil; + + internal partial class WellKnownTypes + { + // Only the underlying type matters: attribute blobs store the enum value as int32. + private sealed class DynamicallyAccessedMemberTypesProvidedType : ProvidedType + { + public DynamicallyAccessedMemberTypesProvidedType() + : base("System.Diagnostics.CodeAnalysis", "DynamicallyAccessedMemberTypes") + { + } + + protected override TypeReference DefineType(ModuleDefinition module, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + var type = new TypeDefinition( + @namespace: NamespaceName, + name: TypeName, + TypeAttributes.NotPublic | TypeAttributes.Sealed, + module.ImportReference(wellKnownTypes.SystemEnum)); + + type.Fields.Add(new FieldDefinition( + name: "value__", + attributes: FieldAttributes.Public | FieldAttributes.SpecialName | FieldAttributes.RTSpecialName, + fieldType: module.TypeSystem.Int32)); + + module.Types.Add(type); + + return type; + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMembersAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMembersAttributeProvidedType.cs new file mode 100644 index 0000000..d11b477 --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+DynamicallyAccessedMembersAttributeProvidedType.cs @@ -0,0 +1,28 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + + internal partial class WellKnownTypes + { + private sealed class DynamicallyAccessedMembersAttributeProvidedType : ProvidedAttributeType + { + public DynamicallyAccessedMembersAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "DynamicallyAccessedMembersAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, inherited: false)); + + var constructor = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor.Parameters.Add(new ParameterDefinition("memberTypes", ParameterAttributes.None, wellKnownTypes.SystemDiagnosticsCodeAnalysisDynamicallyAccessedMemberTypes.Value)); + attribute.Methods.Add(constructor); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullAttributeProvidedType.cs new file mode 100644 index 0000000..6f625b9 --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullAttributeProvidedType.cs @@ -0,0 +1,35 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + using Mono.Cecil.Rocks; + + internal partial class WellKnownTypes + { + private sealed class MemberNotNullAttributeProvidedType : ProvidedAttributeType + { + public MemberNotNullAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "MemberNotNullAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.NullableContext(1)); + attribute.CustomAttributes.Add(attributeFactory.Nullable(0)); + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, allowMultiple: true, inherited: false)); + + var constructor1 = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor1.Parameters.Add(new ParameterDefinition("member", ParameterAttributes.None, wellKnownTypes.TypeSystem.String)); + attribute.Methods.Add(constructor1); + + var constructor2 = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor2.Parameters.Add(new ParameterDefinition("members", ParameterAttributes.None, wellKnownTypes.TypeSystem.String.MakeArrayType())); + attribute.Methods.Add(constructor2); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullWhenAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullWhenAttributeProvidedType.cs new file mode 100644 index 0000000..0c4abf2 --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+MemberNotNullWhenAttributeProvidedType.cs @@ -0,0 +1,37 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + using Mono.Cecil.Rocks; + + internal partial class WellKnownTypes + { + private sealed class MemberNotNullWhenAttributeProvidedType : ProvidedAttributeType + { + public MemberNotNullWhenAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "MemberNotNullWhenAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.NullableContext(1)); + attribute.CustomAttributes.Add(attributeFactory.Nullable(0)); + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, allowMultiple: true, inherited: false)); + + var constructor1 = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor1.Parameters.Add(new ParameterDefinition("returnValue", ParameterAttributes.None, wellKnownTypes.TypeSystem.Boolean)); + constructor1.Parameters.Add(new ParameterDefinition("member", ParameterAttributes.None, wellKnownTypes.TypeSystem.String)); + attribute.Methods.Add(constructor1); + + var constructor2 = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor2.Parameters.Add(new ParameterDefinition("returnValue", ParameterAttributes.None, wellKnownTypes.TypeSystem.Boolean)); + constructor2.Parameters.Add(new ParameterDefinition("members", ParameterAttributes.None, wellKnownTypes.TypeSystem.String.MakeArrayType())); + attribute.Methods.Add(constructor2); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresAssemblyFilesAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresAssemblyFilesAttributeProvidedType.cs new file mode 100644 index 0000000..f5ef48e --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresAssemblyFilesAttributeProvidedType.cs @@ -0,0 +1,30 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + + internal partial class WellKnownTypes + { + private sealed class RequiresAssemblyFilesAttributeProvidedType : ProvidedAttributeType + { + public RequiresAssemblyFilesAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "RequiresAssemblyFilesAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event, inherited: false)); + + attribute.AddDefaultConstructor(wellKnownTypes.TypeSystem); + + var constructor = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor.Parameters.Add(new ParameterDefinition("message", ParameterAttributes.None, wellKnownTypes.TypeSystem.String)); + attribute.Methods.Add(constructor); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresDynamicCodeAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresDynamicCodeAttributeProvidedType.cs new file mode 100644 index 0000000..4420b3d --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresDynamicCodeAttributeProvidedType.cs @@ -0,0 +1,28 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + + internal partial class WellKnownTypes + { + private sealed class RequiresDynamicCodeAttributeProvidedType : ProvidedAttributeType + { + public RequiresDynamicCodeAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "RequiresDynamicCodeAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, inherited: false)); + + var constructor = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor.Parameters.Add(new ParameterDefinition("message", ParameterAttributes.None, wellKnownTypes.TypeSystem.String)); + attribute.Methods.Add(constructor); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresUnreferencedCodeAttributeProvidedType.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresUnreferencedCodeAttributeProvidedType.cs new file mode 100644 index 0000000..e09402e --- /dev/null +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes+RequiresUnreferencedCodeAttributeProvidedType.cs @@ -0,0 +1,28 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace ICSharpCode.ReferenceAssemblyAnnotator +{ + using System; + using Mono.Cecil; + + internal partial class WellKnownTypes + { + private sealed class RequiresUnreferencedCodeAttributeProvidedType : ProvidedAttributeType + { + public RequiresUnreferencedCodeAttributeProvidedType() + : base("System.Diagnostics.CodeAnalysis", "RequiresUnreferencedCodeAttribute") + { + } + + protected override void ImplementAttribute(ModuleDefinition module, TypeDefinition attribute, WellKnownTypes wellKnownTypes, CustomAttributeFactory attributeFactory) + { + attribute.CustomAttributes.Add(attributeFactory.AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, inherited: false)); + + var constructor = MethodFactory.Constructor(wellKnownTypes.TypeSystem); + constructor.Parameters.Add(new ParameterDefinition("message", ParameterAttributes.None, wellKnownTypes.TypeSystem.String)); + attribute.Methods.Add(constructor); + } + } + } +} diff --git a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes.cs b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes.cs index 5993109..3120692 100644 --- a/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes.cs +++ b/ICSharpCode.ReferenceAssemblyAnnotator/WellKnownTypes.cs @@ -10,6 +10,7 @@ namespace ICSharpCode.ReferenceAssemblyAnnotator internal partial class WellKnownTypes { + private readonly WellKnownType _systemEnum = new PredefinedType(typeof(Enum)); private readonly WellKnownType _systemAttribute = new PredefinedType(typeof(Attribute)); private readonly WellKnownType _systemAttributeTargets = new PredefinedType(typeof(AttributeTargets)); private readonly WellKnownType _systemAttributeUsageAttribute = new PredefinedType(typeof(AttributeUsageAttribute)); @@ -30,6 +31,14 @@ internal partial class WellKnownTypes private readonly WellKnownType _systemDiagnosticsCodeAnalysisNotNullAttribute = new NotNullAttributeProvidedType(); private readonly WellKnownType _systemDiagnosticsCodeAnalysisNotNullIfNotNullAttribute = new NotNullIfNotNullAttributeProvidedType(); private readonly WellKnownType _systemDiagnosticsCodeAnalysisNotNullWhenAttribute = new NotNullWhenAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisMemberNotNullAttribute = new MemberNotNullAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisMemberNotNullWhenAttribute = new MemberNotNullWhenAttributeProvidedType(); + + private readonly WellKnownType _systemDiagnosticsCodeAnalysisRequiresUnreferencedCodeAttribute = new RequiresUnreferencedCodeAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisRequiresDynamicCodeAttribute = new RequiresDynamicCodeAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisRequiresAssemblyFilesAttribute = new RequiresAssemblyFilesAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisDynamicallyAccessedMembersAttribute = new DynamicallyAccessedMembersAttributeProvidedType(); + private readonly WellKnownType _systemDiagnosticsCodeAnalysisDynamicallyAccessedMemberTypes = new DynamicallyAccessedMemberTypesProvidedType(); public WellKnownTypes(AssemblyDefinition assemblyDefinition) { @@ -79,12 +88,36 @@ public WellKnownTypes(AssemblyDefinition assemblyDefinition) SystemDiagnosticsCodeAnalysisNotNullWhenAttribute = new Lazy( () => _systemDiagnosticsCodeAnalysisNotNullWhenAttribute.GetOrCreateTypeReference(Module, this), LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisMemberNotNullAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisMemberNotNullAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisMemberNotNullWhenAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisMemberNotNullWhenAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + + SystemDiagnosticsCodeAnalysisRequiresUnreferencedCodeAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisRequiresUnreferencedCodeAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisRequiresDynamicCodeAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisRequiresDynamicCodeAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisRequiresAssemblyFilesAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisRequiresAssemblyFilesAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisDynamicallyAccessedMembersAttribute = new Lazy( + () => _systemDiagnosticsCodeAnalysisDynamicallyAccessedMembersAttribute.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); + SystemDiagnosticsCodeAnalysisDynamicallyAccessedMemberTypes = new Lazy( + () => _systemDiagnosticsCodeAnalysisDynamicallyAccessedMemberTypes.GetOrCreateTypeReference(Module, this), + LazyThreadSafetyMode.ExecutionAndPublication); } public ModuleDefinition Module { get; } public TypeSystem TypeSystem => Module.TypeSystem; + public TypeReference SystemEnum => _systemEnum.GetOrCreateTypeReference(Module, this); + public TypeReference SystemAttribute => _systemAttribute.GetOrCreateTypeReference(Module, this); public TypeReference SystemAttributeTargets => _systemAttributeTargets.GetOrCreateTypeReference(Module, this); @@ -121,6 +154,20 @@ public WellKnownTypes(AssemblyDefinition assemblyDefinition) public Lazy SystemDiagnosticsCodeAnalysisNotNullWhenAttribute { get; } + public Lazy SystemDiagnosticsCodeAnalysisMemberNotNullAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisMemberNotNullWhenAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisRequiresUnreferencedCodeAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisRequiresDynamicCodeAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisRequiresAssemblyFilesAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisDynamicallyAccessedMembersAttribute { get; } + + public Lazy SystemDiagnosticsCodeAnalysisDynamicallyAccessedMemberTypes { get; } + private static TypeDefinition ResolveRequiredWellKnownType(ModuleDefinition module, Type type) { return ResolveWellKnownType(module, type) diff --git a/tests/MultiTFM/Class1.cs b/tests/MultiTFM/Class1.cs index 09b33d8..d9002c5 100644 --- a/tests/MultiTFM/Class1.cs +++ b/tests/MultiTFM/Class1.cs @@ -19,5 +19,10 @@ public static void M(SqlConnection connection, [AllowNull] string? x) _ = EqualityComparer.Default.GetHashCode(x); #endif } + +#if !NET35 && !NETCOREAPP3_0 && !NETCOREAPP3_1 + // Task.IsFaulted carries [MemberNotNullWhen(true, nameof(Exception))]; without it this warns CS8602. + public static string F(System.Threading.Tasks.Task task) => task.IsFaulted ? task.Exception.Message : ""; +#endif } } diff --git a/tests/NoDisabledWarnings/Class1.cs b/tests/NoDisabledWarnings/Class1.cs index 09b33d8..d9002c5 100644 --- a/tests/NoDisabledWarnings/Class1.cs +++ b/tests/NoDisabledWarnings/Class1.cs @@ -19,5 +19,10 @@ public static void M(SqlConnection connection, [AllowNull] string? x) _ = EqualityComparer.Default.GetHashCode(x); #endif } + +#if !NET35 && !NETCOREAPP3_0 && !NETCOREAPP3_1 + // Task.IsFaulted carries [MemberNotNullWhen(true, nameof(Exception))]; without it this warns CS8602. + public static string F(System.Threading.Tasks.Task task) => task.IsFaulted ? task.Exception.Message : ""; +#endif } } diff --git a/tests/SingleTFM/Class1.cs b/tests/SingleTFM/Class1.cs index 09b33d8..d9002c5 100644 --- a/tests/SingleTFM/Class1.cs +++ b/tests/SingleTFM/Class1.cs @@ -19,5 +19,10 @@ public static void M(SqlConnection connection, [AllowNull] string? x) _ = EqualityComparer.Default.GetHashCode(x); #endif } + +#if !NET35 && !NETCOREAPP3_0 && !NETCOREAPP3_1 + // Task.IsFaulted carries [MemberNotNullWhen(true, nameof(Exception))]; without it this warns CS8602. + public static string F(System.Threading.Tasks.Task task) => task.IsFaulted ? task.Exception.Message : ""; +#endif } } diff --git a/tests/WPF/UserControl1.xaml.cs b/tests/WPF/UserControl1.xaml.cs index 4addbca..ea38b7d 100644 --- a/tests/WPF/UserControl1.xaml.cs +++ b/tests/WPF/UserControl1.xaml.cs @@ -25,5 +25,10 @@ public static void M(SqlConnection connection, [AllowNull] string? x) _ = EqualityComparer.Default.GetHashCode(x); #endif } + +#if !NET35 && !NETCOREAPP3_0 && !NETCOREAPP3_1 + // Task.IsFaulted carries [MemberNotNullWhen(true, nameof(Exception))]; without it this warns CS8602. + public static string F(System.Threading.Tasks.Task task) => task.IsFaulted ? task.Exception.Message : ""; +#endif } }