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 } }