From 2ca02292c29a77fc50d678463c5ebee772f37334 Mon Sep 17 00:00:00 2001 From: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Date: Mon, 6 Feb 2023 04:35:23 -0800 Subject: [PATCH] NativeAOT Fully implements warning suppressions logic (#81266) Ports the warning suppression logic from linker. The only unsupported feature is reading the suppressions from attribute XML. This also doesn't port the "redundant suppression detection" logic from linker. Other changes: * Extracts some helpers for attribute handling into CustomAttributeExtensions (mostly around properties and events) * Implements equality and hashcode on property and event descriptors - so that they can be used as keys in a dictionary * Implements passing along the "--singlewarn" command line option in the test infra. Also ports over all of the warning suppression tests from linker: * Modified all of the "redundant suppressions" tests to be "trimmer-only" as that feature is only implemented there. * Removed the "suppressions from XML" tests Per discussion with @MichalStrehovsky I will rename `PropertyPseudoDesc` and `EventPseudoDesc` to `EcmaProperty` and `EcmaEvent`. But I'll do that in a separate PR as it would add a lot of noise to this one. --- .../tools/Common/Compiler/EventPseudoDesc.cs | 13 +- .../Common/Compiler/PropertyPseudoDesc.cs | 13 +- .../TypeSystem/Ecma/MetadataExtensions.cs | 2 +- .../Compiler/CustomAttributeExtensions.cs | 85 +++ .../Compiler/Dataflow/DiagnosticUtilities.cs | 28 - .../ILCompiler.Compiler/Compiler/Logger.cs | 87 +-- .../Compiler/Logging/SuppressMessageInfo.cs | 13 + ...onditionalSuppressMessageAttributeState.cs | 332 ++++++++++ .../ILCompiler.Compiler.csproj | 3 + .../Dependencies/TriggerWarnings_Lib.cs | 61 ++ .../TriggerWarnings_TrimmableLib.cs | 24 + .../AddSuppressionsBeforeAttributeRemoval.cs | 26 + .../AddSuppressionsBeforeAttributeRemoval.xml | 8 + ...dundantSuppressionsFeatureSubstitutions.cs | 71 ++ ...undantSuppressionsFeatureSubstitutions.xml | 12 + .../DetectRedundantSuppressionsFromXML.cs | 39 ++ .../DetectRedundantSuppressionsFromXML.xml | 19 + .../DetectRedundantSuppressionsInAssembly.cs | 30 + ...dantSuppressionsInCompilerGeneratedCode.cs | 85 +++ ...tRedundantSuppressionsInMembersAndTypes.cs | 331 ++++++++++ ...uppressionsInMembersAndTypesUsingTarget.cs | 92 +++ .../DetectRedundantSuppressionsInModule.cs | 30 + .../DetectRedundantSuppressionsSingleWarn.cs | 29 + ...dundantSuppressionsTrimmedMembersTarget.cs | 56 ++ ...uleSuppressionWithMemberScopeNullTarget.cs | 34 + ...uleSuppressionWithModuleScopeNullTarget.cs | 32 + ...oduleSuppressionWithNullScopeNullTarget.cs | 33 + .../SuppressWarningsInAssembly.cs | 28 + ...SuppressWarningsInCompilerGeneratedCode.cs | 612 ++++++++++++++++++ .../SuppressWarningsInCopyAssembly.cs | 34 + .../SuppressWarningsInMembersAndTypes.cs | 189 ++++++ ...essWarningsInMembersAndTypesUsingTarget.cs | 124 ++++ .../SuppressWarningsInModule.cs | 33 + ...SuppressWarningsUsingTargetViaXml.mono.xml | 35 + ...pressWarningsUsingTargetViaXml.netcore.xml | 35 + .../SuppressWarningsUsingTargetViaXmlMono.cs | 26 + ...uppressWarningsUsingTargetViaXmlNetCore.cs | 26 + ...ttedModuleSuppressionWithUnmatchedScope.cs | 48 ++ .../TestCases/TestDatabase.cs | 5 + .../Mono.Linker.Tests/TestCases/TestSuites.cs | 7 + .../TestCasesRunner/ILCompilerDriver.cs | 11 +- .../TestCasesRunner/ILCompilerOptions.cs | 1 + .../ILCompilerOptionsBuilder.cs | 3 + 43 files changed, 2680 insertions(+), 125 deletions(-) create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CustomAttributeExtensions.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/SuppressMessageInfo.cs create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/UnconditionalSuppressMessageAttributeState.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_Lib.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_TrimmableLib.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.xml create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.xml create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.xml create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInAssembly.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInCompilerGeneratedCode.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypes.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypesUsingTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInModule.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsSingleWarn.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsTrimmedMembersTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithMemberScopeNullTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithModuleScopeNullTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithNullScopeNullTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInAssembly.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCopyAssembly.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypes.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypesUsingTarget.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInModule.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.mono.xml create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.netcore.xml create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlMono.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlNetCore.cs create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/TargettedModuleSuppressionWithUnmatchedScope.cs diff --git a/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs b/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs index 33eb895ad2619..7adc1c6195978 100644 --- a/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs +++ b/src/coreclr/tools/Common/Compiler/EventPseudoDesc.cs @@ -87,11 +87,12 @@ public EventPseudoDesc(EcmaType type, EventDefinitionHandle handle) public override TypeSystemContext Context => _type.Context; - #region Do not use these - public override bool Equals(object obj) => throw new NotImplementedException(); - public override int GetHashCode() => throw new NotImplementedException(); - public static bool operator ==(EventPseudoDesc a, EventPseudoDesc b) => throw new NotImplementedException(); - public static bool operator !=(EventPseudoDesc a, EventPseudoDesc b) => throw new NotImplementedException(); - #endregion + public override bool Equals(object obj) => obj is not EventPseudoDesc @event ? false : this == @event; + + public override int GetHashCode() => _type.GetHashCode() ^ _handle.GetHashCode(); + + public static bool operator ==(EventPseudoDesc a, EventPseudoDesc b) => a._type == b._type && a._handle == b._handle; + + public static bool operator !=(EventPseudoDesc a, EventPseudoDesc b) => !(a == b); } } diff --git a/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs b/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs index ac374f331f5eb..c460ba67772c0 100644 --- a/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs +++ b/src/coreclr/tools/Common/Compiler/PropertyPseudoDesc.cs @@ -82,11 +82,12 @@ public PropertyPseudoDesc(EcmaType type, PropertyDefinitionHandle handle) public override TypeSystemContext Context => _type.Context; - #region Do not use these - public override bool Equals(object obj) => throw new NotImplementedException(); - public override int GetHashCode() => throw new NotImplementedException(); - public static bool operator ==(PropertyPseudoDesc a, PropertyPseudoDesc b) => throw new NotImplementedException(); - public static bool operator !=(PropertyPseudoDesc a, PropertyPseudoDesc b) => throw new NotImplementedException(); - #endregion + public override bool Equals(object obj) => obj is not PropertyPseudoDesc property ? false : this == property; + + public override int GetHashCode() => _type.GetHashCode() ^ _handle.GetHashCode(); + + public static bool operator ==(PropertyPseudoDesc a, PropertyPseudoDesc b) => a._type == b._type && a._handle == b._handle; + + public static bool operator !=(PropertyPseudoDesc a, PropertyPseudoDesc b) => !(a == b); } } diff --git a/src/coreclr/tools/Common/TypeSystem/Ecma/MetadataExtensions.cs b/src/coreclr/tools/Common/TypeSystem/Ecma/MetadataExtensions.cs index f319665850442..54e58b3b69699 100644 --- a/src/coreclr/tools/Common/TypeSystem/Ecma/MetadataExtensions.cs +++ b/src/coreclr/tools/Common/TypeSystem/Ecma/MetadataExtensions.cs @@ -122,7 +122,7 @@ public static CustomAttributeHandle GetCustomAttributeHandle(this MetadataReader return default(CustomAttributeHandle); } - private static bool IsEqualCustomAttributeName(CustomAttributeHandle attributeHandle, MetadataReader metadataReader, + public static bool IsEqualCustomAttributeName(CustomAttributeHandle attributeHandle, MetadataReader metadataReader, string attributeNamespace, string attributeName) { StringHandle namespaceHandle, nameHandle; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CustomAttributeExtensions.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CustomAttributeExtensions.cs new file mode 100644 index 0000000000000..73c83403d0a02 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CustomAttributeExtensions.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Reflection.Metadata; +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +namespace ILCompiler +{ + public static class CustomAttributeExtensions + { + public static CustomAttributeValue? GetDecodedCustomAttribute(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName) + { + var ecmaType = prop.OwningType as EcmaType; + var metadataReader = ecmaType.MetadataReader; + + var attributeHandle = metadataReader.GetCustomAttributeHandle(prop.GetCustomAttributes, + attributeNamespace, attributeName); + + if (attributeHandle.IsNil) + return null; + + return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); + } + + public static IEnumerable> GetDecodedCustomAttributes(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName) + { + var ecmaType = prop.OwningType as EcmaType; + var metadataReader = ecmaType.MetadataReader; + + var attributeHandles = prop.GetCustomAttributes; + foreach (var attributeHandle in attributeHandles) + { + if (MetadataExtensions.IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); + } + } + } + + public static CustomAttributeValue? GetDecodedCustomAttribute(this EventPseudoDesc @event, string attributeNamespace, string attributeName) + { + var ecmaType = @event.OwningType as EcmaType; + var metadataReader = ecmaType.MetadataReader; + + var attributeHandle = metadataReader.GetCustomAttributeHandle(@event.GetCustomAttributes, + attributeNamespace, attributeName); + + if (attributeHandle.IsNil) + return null; + + return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); + } + + public static IEnumerable> GetDecodedCustomAttributes(this EventPseudoDesc @event, string attributeNamespace, string attributeName) + { + var ecmaType = @event.OwningType as EcmaType; + var metadataReader = ecmaType.MetadataReader; + + var attributeHandles = @event.GetCustomAttributes; + foreach (var attributeHandle in attributeHandles) + { + if (MetadataExtensions.IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); + } + } + } + + public static IEnumerable> GetDecodedCustomAttributesForModule(this EcmaModule module, string attributeNamespace, string attributeName) + { + var metadataReader = module.MetadataReader; + + var attributeHandles = metadataReader.GetModuleDefinition().GetCustomAttributes(); + foreach (var attributeHandle in attributeHandles) + { + if (MetadataExtensions.IsEqualCustomAttributeName(attributeHandle, metadataReader, attributeNamespace, attributeName)) + { + yield return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(module)); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs index 213ace382d6c2..f5b408f388fd4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs @@ -69,34 +69,6 @@ internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string req return true; } - public static CustomAttributeValue? GetDecodedCustomAttribute(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName) - { - var ecmaType = prop.OwningType as EcmaType; - var metadataReader = ecmaType.MetadataReader; - - var attributeHandle = metadataReader.GetCustomAttributeHandle(prop.GetCustomAttributes, - attributeNamespace, attributeName); - - if (attributeHandle.IsNil) - return null; - - return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); - } - - public static CustomAttributeValue? GetDecodedCustomAttribute(this EventPseudoDesc @event, string attributeNamespace, string attributeName) - { - var ecmaType = @event.OwningType as EcmaType; - var metadataReader = ecmaType.MetadataReader; - - var attributeHandle = metadataReader.GetCustomAttributeHandle(@event.GetCustomAttributes, - attributeNamespace, attributeName); - - if (attributeHandle.IsNil) - return null; - - return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule)); - } - internal static string GetRequiresAttributeMessage(CustomAttributeValue attribute) { if (attribute.FixedArguments.Length != 0) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs index 4a3a9312e3747..5ddb1215b286d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logger.cs @@ -5,10 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.Reflection.Metadata; using Internal.TypeSystem; -using Internal.TypeSystem.Ecma; using ILCompiler.Dataflow; using ILCompiler.Logging; @@ -22,6 +20,7 @@ public class Logger { private readonly ILogWriter _logWriter; private readonly CompilerGeneratedState _compilerGeneratedState; + private readonly UnconditionalSuppressMessageAttributeState _unconditionalSuppressMessageAttributeState; private readonly HashSet _suppressedWarnings; private readonly HashSet _suppressedCategories; @@ -47,13 +46,14 @@ public Logger( IEnumerable suppressedCategories) { _logWriter = writer; - _compilerGeneratedState = ilProvider == null ? null : new CompilerGeneratedState(ilProvider, this); IsVerbose = isVerbose; _suppressedWarnings = new HashSet(suppressedWarnings); _isSingleWarn = singleWarn; _singleWarnEnabledAssemblies = new HashSet(singleWarnEnabledModules, StringComparer.OrdinalIgnoreCase); _singleWarnDisabledAssemblies = new HashSet(singleWarnDisabledModules, StringComparer.OrdinalIgnoreCase); _suppressedCategories = new HashSet(suppressedCategories, StringComparer.Ordinal); + _compilerGeneratedState = ilProvider == null ? null : new CompilerGeneratedState(ilProvider, this); + _unconditionalSuppressMessageAttributeState = new UnconditionalSuppressMessageAttributeState(_compilerGeneratedState, this); } public Logger(TextWriter writer, ILProvider ilProvider, bool isVerbose, IEnumerable suppressedWarnings, bool singleWarn, IEnumerable singleWarnEnabledModules, IEnumerable singleWarnDisabledModules, IEnumerable suppressedCategories) @@ -154,86 +154,7 @@ internal bool IsWarningSuppressed(int code, MessageOrigin origin) if (_suppressedWarnings.Contains(code)) return true; - // TODO: Suppressions with different scopes - - TypeSystemEntity member = origin.MemberDefinition; - if (IsSuppressed(code, member)) - return true; - - MethodDesc owningMethod; - if (_compilerGeneratedState != null) - { - while (_compilerGeneratedState?.TryGetOwningMethodForCompilerGeneratedMember(member, out owningMethod) == true) - { - Debug.Assert(owningMethod != member); - if (IsSuppressed(code, owningMethod)) - return true; - member = owningMethod; - } - } - - return false; - } - - private static bool IsSuppressed(int id, TypeSystemEntity warningOrigin) - { - TypeSystemEntity warningOriginMember = warningOrigin; - while (warningOriginMember != null) - { - if (IsSuppressedOnElement(id, warningOriginMember)) - return true; - - warningOriginMember = warningOriginMember.GetOwningType(); - } - - // TODO: Assembly-level suppressions - - return false; - } - - private static bool IsSuppressedOnElement(int id, TypeSystemEntity provider) - { - if (provider == null) - return false; - - // TODO: Assembly-level suppressions - - IEnumerable> suppressions = null; - - if (provider is TypeDesc type) - { - var ecmaType = type.GetTypeDefinition() as EcmaType; - suppressions = ecmaType?.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "UnconditionalSuppressMessageAttribute"); - } - - if (provider is MethodDesc method) - { - var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod; - suppressions = ecmaMethod?.GetDecodedCustomAttributes("System.Diagnostics.CodeAnalysis", "UnconditionalSuppressMessageAttribute"); - } - - if (suppressions != null) - { - foreach (CustomAttributeValue suppression in suppressions) - { - if (suppression.FixedArguments.Length != 2 - || suppression.FixedArguments[1].Value is not string warningId - || warningId.Length < 6 - || !warningId.StartsWith("IL") - || (warningId.Length > 6 && warningId[6] != ':') - || !int.TryParse(warningId.AsSpan(2, 4), out int suppressedCode)) - { - continue; - } - - if (id == suppressedCode) - { - return true; - } - } - } - - return false; + return _unconditionalSuppressMessageAttributeState.IsSuppressed(code, origin); } internal static bool IsWarningAsError(int _/*code*/) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/SuppressMessageInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/SuppressMessageInfo.cs new file mode 100644 index 0000000000000..946fbc958b229 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/SuppressMessageInfo.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace ILCompiler.Logging +{ + public struct SuppressMessageInfo + { + public int Id; + public string Scope; + public string Target; + public string MessageId; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/UnconditionalSuppressMessageAttributeState.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/UnconditionalSuppressMessageAttributeState.cs new file mode 100644 index 0000000000000..eefc89c9aebbd --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Logging/UnconditionalSuppressMessageAttributeState.cs @@ -0,0 +1,332 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; + +using Internal.TypeSystem; +using Internal.TypeSystem.Ecma; + +using ILCompiler.Dataflow; + +using ILLink.Shared; + +#nullable enable + +namespace ILCompiler.Logging +{ + public class UnconditionalSuppressMessageAttributeState + { + internal const string ScopeProperty = "Scope"; + internal const string TargetProperty = "Target"; + internal const string MessageIdProperty = "MessageId"; + + internal const string UnconditionalSuppressMessageAttributeNamespace = "System.Diagnostics.CodeAnalysis"; + internal const string UnconditionalSuppressMessageAttributeName = "UnconditionalSuppressMessageAttribute"; + + public class Suppression + { + public SuppressMessageInfo SuppressMessageInfo { get; } + public bool Used { get; set; } + public CustomAttributeValue OriginAttribute { get; } + public TypeSystemEntity Provider { get; } + + public Suppression(SuppressMessageInfo suppressMessageInfo, CustomAttributeValue originAttribute, TypeSystemEntity provider) + { + SuppressMessageInfo = suppressMessageInfo; + OriginAttribute = originAttribute; + Provider = provider; + } + } + + private sealed class AssemblyWarningsReportedHashtable : LockFreeReaderHashtable + { + protected override bool CompareKeyToValue(EcmaAssembly key, EcmaAssembly value) => key == value; + protected override bool CompareValueToValue(EcmaAssembly value1, EcmaAssembly value2) => value1 == value2; + protected override EcmaAssembly CreateValueFromKey(EcmaAssembly key) => throw new NotImplementedException(); + protected override int GetKeyHashCode(EcmaAssembly key) => key.GetHashCode(); + protected override int GetValueHashCode(EcmaAssembly value) => value.GetHashCode(); + } + + private readonly CompilerGeneratedState? _compilerGeneratedState; + private readonly Logger _logger; + private readonly AssemblyWarningsReportedHashtable _assemblyWarningsReportedHashtable; + + public UnconditionalSuppressMessageAttributeState(CompilerGeneratedState? compilerGeneratedState, Logger logger) + { + _compilerGeneratedState = compilerGeneratedState; + _logger = logger; + _assemblyWarningsReportedHashtable = new(); + } + + public bool IsSuppressed(int id, MessageOrigin warningOrigin) + { + // Check for suppressions on both the suppression context as well as the original member + // (if they're different). This is to correctly handle compiler generated code + // which needs to use suppressions from both the compiler generated scope + // as well as the original user defined method. + + TypeSystemEntity? provider = warningOrigin.MemberDefinition; + if (provider == null) + return false; + + if (IsSuppressed(id, provider)) + return true; + + if (_compilerGeneratedState != null) + { + while (_compilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember(provider, out MethodDesc? owningMethod)) + { + Debug.Assert(owningMethod != provider); + if (IsSuppressed(id, owningMethod)) + return true; + provider = owningMethod; + } + } + + return false; + } + + private bool IsSuppressed(int id, TypeSystemEntity? warningOrigin) + { + if (warningOrigin == null) + return false; + + ModuleDesc? module = GetModuleFromProvider(warningOrigin); + if (module is not EcmaAssembly ecmaAssembly) + return false; + + // Only report the warnings if they were not reported already for this assembly + List<(DiagnosticId, string?[])>? generatedWarnings = null; + if (_assemblyWarningsReportedHashtable.TryAdd(ecmaAssembly)) + generatedWarnings = new(); + + IEnumerable? moduleSuppressions = DecodeAssemblyAndModuleSuppressions(ecmaAssembly, generatedWarnings); + + if (generatedWarnings is not null) + { + foreach (var warning in generatedWarnings) + { + _logger.LogWarning(ecmaAssembly, warning.Item1, warning.Item2); + } + } + + TypeSystemEntity? warningOriginMember = warningOrigin; + while (warningOriginMember != null) + { + if (IsSuppressedOnElement(id, warningOriginMember, moduleSuppressions)) + return true; + + if (warningOriginMember is MethodDesc method) + { + if (method.GetPropertyForAccessor() is { } property) + { + Debug.Assert(property.OwningType == method.OwningType); + warningOriginMember = property; + continue; + } + else if (method.GetEventForAccessor() is { } @event) + { + Debug.Assert(@event.OwningType == method.OwningType); + warningOriginMember = @event; + continue; + } + } + + warningOriginMember = warningOriginMember.GetOwningType(); + } + + // Check if there's an assembly or module level suppression. + // Note that moduleSuppressions contains both assembly and module level suppressions all modified to target the module as the provider + if (IsSuppressedOnElement(id, module, moduleSuppressions)) + return true; + + return false; + } + + private static bool IsSuppressedOnElement(int id, TypeSystemEntity provider, IEnumerable? moduleSuppressions) + { + if (provider is not ModuleDesc) + { + foreach (var suppression in DecodeSuppressions(provider)) + { + if (suppression.SuppressMessageInfo.Id == id) + return true; + } + } + + if (moduleSuppressions is not null) + { + foreach (var suppression in moduleSuppressions) + { + if (suppression.Provider == provider && suppression.SuppressMessageInfo.Id == id) + return true; + } + } + + return false; + } + + private static bool TryDecodeSuppressMessageAttributeData(CustomAttributeValue attribute, out SuppressMessageInfo info) + { + info = default; + + // We need at least the Category and Id to decode the warning to suppress. + // The only UnconditionalSuppressMessageAttribute constructor requires those two parameters. + if (attribute.FixedArguments.Length < 2) + { + return false; + } + + // Ignore the category parameter because it does not identify the warning + // and category information can be obtained from warnings themselves. + // We only support warnings with code pattern IL####. + if (!(attribute.FixedArguments[1].Value is string warningId) || + warningId.Length < 6 || + !warningId.StartsWith("IL", StringComparison.Ordinal) || + !int.TryParse(warningId.AsSpan(2, 4), out info.Id)) + { + return false; + } + + if (warningId.Length > 6 && warningId[6] != ':') + return false; + + foreach (var p in attribute.NamedArguments) + { + switch (p.Name) + { + case ScopeProperty when p.Value is string scope: + info.Scope = scope; + break; + case TargetProperty when p.Value is string target: + info.Target = target; + break; + case MessageIdProperty when p.Value is string messageId: + info.MessageId = messageId; + break; + } + } + + return true; + } + + public static ModuleDesc? GetModuleFromProvider(TypeSystemEntity provider) + { + switch (provider) + { + case ModuleDesc module: + return module; + case MetadataType type: + return type.Module; + default: + return (provider.GetOwningType() as MetadataType)?.Module; + } + } + + private static IEnumerable DecodeSuppressions(TypeSystemEntity provider) + { + Debug.Assert(provider is not ModuleDesc); + + foreach (CustomAttributeValue ca in GetDecodedCustomAttributes(provider, UnconditionalSuppressMessageAttributeNamespace, UnconditionalSuppressMessageAttributeName)) + { + if (!TryDecodeSuppressMessageAttributeData(ca, out var info)) + continue; + + yield return new Suppression(info, originAttribute: ca, provider); + } + } + + private static List? DecodeAssemblyAndModuleSuppressions(EcmaAssembly ecmaAssembly, List<(DiagnosticId, string?[])>? warnings) + { + List? suppressions = null; + DecodeGlobalSuppressions( + ecmaAssembly, + ecmaAssembly.GetDecodedCustomAttributes(UnconditionalSuppressMessageAttributeNamespace, UnconditionalSuppressMessageAttributeName), + ref suppressions, + warnings); + + DecodeGlobalSuppressions( + ecmaAssembly, + ecmaAssembly.GetDecodedCustomAttributesForModule(UnconditionalSuppressMessageAttributeNamespace, UnconditionalSuppressMessageAttributeName), + ref suppressions, + warnings); + + return suppressions; + } + + private static void DecodeGlobalSuppressions( + EcmaAssembly module, + IEnumerable> attributes, + ref List? suppressions, + List<(DiagnosticId, string?[])>? warnings) + { + foreach (CustomAttributeValue instance in attributes) + { + if (!TryDecodeSuppressMessageAttributeData(instance, out SuppressMessageInfo info)) + continue; + + var scope = info.Scope?.ToLowerInvariant(); + if (info.Target == null && (scope == "module" || scope == null)) + { + suppressions ??= new(); + suppressions.Add(new Suppression(info, originAttribute: instance, module)); + continue; + } + + switch (scope) + { + case "module": + suppressions ??= new(); + suppressions.Add(new Suppression(info, originAttribute: instance, module)); + break; + + case "type": + case "member": + if (info.Target == null) + break; + + foreach (var result in DocumentationSignatureParser.GetMembersForDocumentationSignature(info.Target, module)) + { + suppressions ??= new(); + suppressions.Add(new Suppression(info, originAttribute: instance, result)); + } + + break; + default: + warnings?.Add((DiagnosticId.InvalidScopeInUnconditionalSuppressMessage, new string?[] { info.Scope ?? "", module.GetName().Name, info.Target ?? "" })); + break; + } + } + } + + private static IEnumerable> GetDecodedCustomAttributes(TypeSystemEntity entity, string attributeNamespace, string attributeName) + { + switch (entity) + { + case MethodDesc method: + if (method.GetTypicalMethodDefinition() is not EcmaMethod ecmaMethod) + return Enumerable.Empty>(); + return ecmaMethod.GetDecodedCustomAttributes(attributeNamespace, attributeName); + case MetadataType type: + if (type.GetTypeDefinition() is not EcmaType ecmaType) + return Enumerable.Empty>(); + return ecmaType.GetDecodedCustomAttributes(attributeNamespace, attributeName); + case FieldDesc field: + if (field.GetTypicalFieldDefinition() is not EcmaField ecmaField) + return Enumerable.Empty>(); + return ecmaField.GetDecodedCustomAttributes(attributeNamespace, attributeName); + case PropertyPseudoDesc property: + return property.GetDecodedCustomAttributes(attributeNamespace, attributeName); + case EventPseudoDesc @event: + return @event.GetDecodedCustomAttributes(attributeNamespace, attributeName); + default: + Debug.Fail("Trying to operate with unsupported TypeSystemEntity " + entity.GetType().ToString()); + return Enumerable.Empty>(); + } + } + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index db2e9308dc0a6..0cc8c90ca0144 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -351,6 +351,7 @@ + @@ -606,6 +607,8 @@ + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_Lib.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_Lib.cs new file mode 100644 index 0000000000000..844817a9de540 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_Lib.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Warnings.Dependencies; + +[assembly: TriggerWarnings_Lib.TriggerWarnings (typeof (TriggerWarnings_Lib.RUCType))] + +namespace Mono.Linker.Tests.Cases.Warnings.Dependencies +{ + public class TriggerWarnings_Lib + { + public static void Main () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + Warning1 (); + var getProperty = Warning2; + NestedType.Warning3 (); + var list = new List (); + NestedType.Warning4 (ref list); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (TriggerWarnings_Lib); + } + + public static void Warning1 () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public static int Warning2 { + get { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + return 0; + } + } + + public class NestedType + { + public static void Warning3 () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public static void Warning4 (ref List p) + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + + public class TriggerWarningsAttribute : Attribute + { + public TriggerWarningsAttribute ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) { } + } + + [RequiresUnreferencedCode ("--RUCType--")] + public class RUCType { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_TrimmableLib.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_TrimmableLib.cs new file mode 100644 index 0000000000000..73160856790a1 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/Dependencies/TriggerWarnings_TrimmableLib.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; + +[assembly: AssemblyMetadata ("IsTrimmable", "True")] + +namespace Mono.Linker.Tests.Cases.Warnings.Dependencies +{ + public class TriggerWarnings_TrimmableLib + { + public static void Main () + { + RequirePublicMethods (UnknownType ()); + RUCIntentional (); + } + + static Type UnknownType () => null; + + static void RequirePublicMethods ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) { } + + [RequiresUnreferencedCode ("RUC warning left in the trimmable assembly.")] + static void RUCIntentional () { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.cs new file mode 100644 index 0000000000000..dc0d349c3c9b8 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.cs @@ -0,0 +1,26 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SetupLinkAttributesFile ("AddSuppressionsBeforeAttributeRemoval.xml")] + + [ExpectedNoWarnings] + public class AddSuppressionsBeforeAttributeRemoval + { + [Kept] + public static Type TriggerUnrecognizedPattern () + { + return typeof (AddedPseudoAttributeAttribute); + } + + [UnconditionalSuppressMessage ("ILLinker", "IL2072")] + public static void Main () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.xml b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.xml new file mode 100644 index 0000000000000..e9817e45490e4 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/AddSuppressionsBeforeAttributeRemoval.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.cs new file mode 100644 index 0000000000000..39e8a4598fc6f --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.cs @@ -0,0 +1,71 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SetupLinkerSubstitutionFile ("DetectRedundantSuppressionsFeatureSubstitutions.xml")] + [SetupLinkerArgument ("--feature", "Feature", "false")] + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DetectRedundantSuppressionsFeatureSubstitutions + { + public static void Main () + { + ReportRedundantSuppressionWhenTrimmerIncompatibleCodeDisabled.Test (); + DoNotReportUsefulSuppressionWhenTrimmerIncompatibleCodeEnabled.Test (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (DetectRedundantSuppressionsFeatureSubstitutions); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + + public static bool IsFeatureEnabled { + get => throw new NotImplementedException (); + } + + class ReportRedundantSuppressionWhenTrimmerIncompatibleCodeDisabled + { + // The test simulates the following issue. + // https://github.com/dotnet/linker/issues/2921 + // The suppressed warning is issued in the 'if' branch. + // With feature switched to false, the linker sees only the 'else' branch. + // The 'else' branch contains trimmer-compatible code, the linker identifies the suppression as redundant. + + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static void Test () + { + if (IsFeatureEnabled) { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } else { + TrimmerCompatibleMethod (); + } + } + } + + class DoNotReportUsefulSuppressionWhenTrimmerIncompatibleCodeEnabled + { + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static void Test () + { + if (!IsFeatureEnabled) { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } else { + TrimmerCompatibleMethod (); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.xml b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.xml new file mode 100644 index 0000000000000..d541b2a0b26eb --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFeatureSubstitutions.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.cs new file mode 100644 index 0000000000000..274eb8119b407 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + [ExpectedWarning ("IL2121", "IL2026", ProducedBy = ProducedBy.Trimmer, FileName = "DetectRedundantSuppressionsFromXML.xml", SourceLine = 7)] + [ExpectedWarning ("IL2121", "IL2109", ProducedBy = ProducedBy.Trimmer, FileName = "DetectRedundantSuppressionsFromXML.xml", SourceLine = 12)] + [SetupLinkAttributesFile ("DetectRedundantSuppressionsFromXML.xml")] + public class DetectRedundantSuppressionsFromXML + { + public static void Main () + { + DetectRedundantSuppressions.Test (); + } + + public class DetectRedundantSuppressions + { + public static void Test () + { + DoNotTriggerWarning (); + } + + class SuppressedOnType : DoNotTriggerWarningType { } + + static void DoNotTriggerWarning () { } + + class DoNotTriggerWarningType { } + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.xml b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.xml new file mode 100644 index 0000000000000..cccfda2dcfc14 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsFromXML.xml @@ -0,0 +1,19 @@ + + + + + + + + ILLink + IL2026 + + + + ILLink + IL2109 + + + + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInAssembly.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInAssembly.cs new file mode 100644 index 0000000000000..81e626ac5b177 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInAssembly.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[assembly: ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] +[assembly: UnconditionalSuppressMessage ("Test", "IL2071:Redundant suppression, warning is not issued in this assembly")] + + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class DetectRedundantSuppressionsInAssembly + { + public static void Main () + { + TrimmerCompatibleMethod (); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInCompilerGeneratedCode.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInCompilerGeneratedCode.cs new file mode 100644 index 0000000000000..403231c672072 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInCompilerGeneratedCode.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DetectRedundantSuppressionsInCompilerGeneratedCode + { + public static void Main () + { + RedundantSuppressionOnLocalMethod.Test (); + RedundantSuppressionInIteratorBody.Test (); + RedundantSuppressionInAsyncBody.Test (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (DetectRedundantSuppressionsInCompilerGeneratedCode); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + + public class RedundantSuppressionOnLocalMethod + { + public static void Test () + { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + void LocalMethod () + { + TrimmerCompatibleMethod (); + } + + LocalMethod (); + } + } + + public class RedundantSuppressionInIteratorBody + { + public static void Test () + { + Enumerable (); + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + static IEnumerable Enumerable () + { + TrimmerCompatibleMethod (); + yield return 0; + } + } + + public class RedundantSuppressionInAsyncBody + { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static async void Test () + { + TrimmerCompatibleMethod (); + await MethodAsync (); + } + + [ExpectedWarning ("IL2121", "IL2070", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2070")] + static async Task MethodAsync () + { + return await Task.FromResult (0); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypes.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypes.cs new file mode 100644 index 0000000000000..336a1281477a3 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypes.cs @@ -0,0 +1,331 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DetectRedundantSuppressionsInMembersAndTypes + { + public static void Main () + { + RedundantSuppressionOnType.Test (); + RedundantSuppressionOnMethod.Test (); + RedundantSuppressionOnNestedType.Test (); + + RedundantSuppressionOnPropertyGet.Test (); + RedundantSuppressionOnProperty.Test (); + RedundantSuppressionOnPropertyWithOnlyGet.Test (); + RedundantSuppressionOnPropertyWithOnlySet.Test (); + RedundantSuppressionOnPropertyAccessedByReflection.Test (); + + RedundantSuppressionOnEventAdd.Test (); + RedundantSuppressionOnEvent.Test (); + RedundantSuppressionOnEventAccessedByReflection.Test (); + + MultipleRedundantSuppressions.Test (); + RedundantAndUsedSuppressions.Test (); + + DoNotReportNonLinkerSuppressions.Test (); + DoNotReportSuppressionsOnMethodsConvertedToThrow.Test (); + + SuppressRedundantSuppressionWarning.Test (); + DoNotReportUnnecessaryRedundantWarningSuppressions.Test (); + + RedundantSuppressionWithRUC.Test (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (DetectRedundantSuppressionsInMembersAndTypes); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public class RedundantSuppressionOnType + { + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class RedundantSuppressionOnMethod + { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class RedundantSuppressionOnNestedType + { + public static void Test () + { + NestedType.TrimmerCompatibleMethod (); + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public class NestedType + { + public static void TrimmerCompatibleMethod () + { + DetectRedundantSuppressionsInMembersAndTypes.TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnPropertyGet + { + public static void Test () + { + var property = TrimmerCompatibleProperty; + } + + public static string TrimmerCompatibleProperty { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + get { + return TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnProperty + { + public static void Test () + { + var property = TrimmerCompatibleProperty; + TrimmerCompatibleProperty = "test"; + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static string TrimmerCompatibleProperty { + get { + return TrimmerCompatibleMethod (); + } + set { + value = TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnPropertyWithOnlyGet + { + public static void Test () + { + var property = TrimmerCompatibleProperty; + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static string TrimmerCompatibleProperty { + get { + return TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnPropertyWithOnlySet + { + public static void Test () + { + TrimmerCompatibleProperty = "test"; + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static string TrimmerCompatibleProperty { + set { + value = TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnPropertyAccessedByReflection + { + public static void Test () + { + typeof (RedundantSuppressionOnPropertyAccessedByReflection).GetProperty ("TrimmerCompatibleProperty"); + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static string TrimmerCompatibleProperty { + get { + return TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnEventAdd + { + public static void Test () + { + TrimmerCompatibleEvent += EventSubscriber; + } + + static void EventSubscriber (object sender, EventArgs e) + { + + } + + public static event EventHandler TrimmerCompatibleEvent { + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + add { TrimmerCompatibleMethod (); } + remove { } + } + } + + public class RedundantSuppressionOnEvent + { + public static void Test () + { + TrimmerCompatibleEvent += EventSubscriber; + } + + static void EventSubscriber (object sender, EventArgs e) + { + + } + + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static event EventHandler TrimmerCompatibleEvent { + add { TrimmerCompatibleMethod (); } + remove { } + } + } + + public class RedundantSuppressionOnEventAccessedByReflection + { + public static void Test () + { + typeof (RedundantSuppressionOnEventAccessedByReflection).GetEvent ("TrimmerCompatibleEvent"); + } + + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static event EventHandler TrimmerCompatibleEvent { + add { TrimmerCompatibleMethod (); } + remove { } + } + } + + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public class MultipleRedundantSuppressions + { + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class RedundantAndUsedSuppressions + { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2071")] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static void Test () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + + public class DoNotReportNonLinkerSuppressions + { + [UnconditionalSuppressMessage ("Test", "IL3052")] + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class DoNotReportSuppressionsOnMethodsConvertedToThrow + { + // The tool is unable to determine whether a suppression is redundant when it is placed on a method with unreachable body. + // Currently suppressions on methods with unreachable bodies should never be reported as redundant. + // https://github.com/dotnet/linker/issues/2920 + public static void Test () + { + UsedToMarkMethod (null); + } + + static void UsedToMarkMethod (TypeWithMethodConvertedToThrow t) + { + t.MethodConvertedToThrow (); + } + + class TypeWithMethodConvertedToThrow + { + // The suppression is redundant, but it should not be reported. + [UnconditionalSuppressMessage ("Test", "IL2072")] + public void MethodConvertedToThrow () + { + TrimmerCompatibleMethod (); + } + } + } + + public class SuppressRedundantSuppressionWarning + { + [UnconditionalSuppressMessage ("Test", "IL2121")] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class DoNotReportUnnecessaryRedundantWarningSuppressions + { + [UnconditionalSuppressMessage ("Test", "IL2121")] + [UnconditionalSuppressMessage ("Test", "IL2072")] + public static void Test () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + + public class RedundantSuppressionWithRUC + { + [ExpectedWarning ("IL2026")] + public static void Test () + { + MethodMarkedRUC (); + } + + [ExpectedWarning ("IL2121", "IL2072", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2072")] + [RequiresUnreferencedCode ("Test")] + public static void MethodMarkedRUC () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypesUsingTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypesUsingTarget.cs new file mode 100644 index 0000000000000..545709c83effa --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInMembersAndTypesUsingTarget.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[module: UnconditionalSuppressMessage ("Test", "IL2071", Scope = "type", Target = "T:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.DetectRedundantSuppressionsInMembersAndTypesUsingTarget.RedundantSuppressionOnType")] +[module: UnconditionalSuppressMessage ("Test", "IL2071", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.DetectRedundantSuppressionsInMembersAndTypesUsingTarget.RedundantSuppressionOnMethod.Test")] +[module: UnconditionalSuppressMessage ("Test", "IL2071", Scope = "type", Target = "T:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.DetectRedundantSuppressionsInMembersAndTypesUsingTarget.RedundantSuppressionOnNestedType.NestedType")] +[module: UnconditionalSuppressMessage ("Test", "IL2071", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.DetectRedundantSuppressionsInMembersAndTypesUsingTarget.RedundantSuppressionOnProperty.get_TrimmerCompatibleProperty")] + +// The IL2121 warnings are reported on the suppressions targets. +// When the suppressions are declared on the assembly level, ideally they should also be reported on the assembly level. + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DetectRedundantSuppressionsInMembersAndTypesUsingTarget + { + public static void Main () + { + RedundantSuppressionOnType.Test (); + RedundantSuppressionOnMethod.Test (); + RedundantSuppressionOnNestedType.Test (); + RedundantSuppressionOnProperty.Test (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (DetectRedundantSuppressionsInMembersAndTypesUsingTarget); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + public class RedundantSuppressionOnType + { + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class RedundantSuppressionOnMethod + { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + public static void Test () + { + TrimmerCompatibleMethod (); + } + } + + public class RedundantSuppressionOnNestedType + { + public static void Test () + { + NestedType.TrimmerCompatibleMethod (); + } + + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + public class NestedType + { + public static void TrimmerCompatibleMethod () + { + TrimmerCompatibleMethod (); + } + } + } + + public class RedundantSuppressionOnProperty + { + public static void Test () + { + var property = TrimmerCompatibleProperty; + } + + public static string TrimmerCompatibleProperty { + [ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] + get { + return TrimmerCompatibleMethod (); + } + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInModule.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInModule.cs new file mode 100644 index 0000000000000..dba6e8c981a67 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsInModule.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[assembly: ExpectedWarning ("IL2121", "IL2071", ProducedBy = ProducedBy.Trimmer)] +[module: UnconditionalSuppressMessage ("Test", "IL2071:Redundant suppression, warning is not issued in this assembly")] + + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class DetectRedundantSuppressionsInModule + { + public static void Main () + { + TrimmerCompatibleMethod (); + } + + public static string TrimmerCompatibleMethod () + { + return "test"; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsSingleWarn.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsSingleWarn.cs new file mode 100644 index 0000000000000..87b16f1429490 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsSingleWarn.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [SetupLinkerArgument ("--singlewarn")] + [LogContains ("warning IL2104: Assembly 'test' produced trim warnings", ProducedBy = ProducedBy.Trimmer)] + [LogDoesNotContain ("IL2121")] + class DetectRedundantSuppressionsSingleWarn + { + public static void Main () + { + TrimmerCompatibleMethod (); + } + + [UnconditionalSuppressMessage ("test", "IL2072")] + public static string TrimmerCompatibleMethod () + { + return "test"; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsTrimmedMembersTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsTrimmedMembersTarget.cs new file mode 100644 index 0000000000000..f23e2312ba0d8 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/DetectRedundantSuppressionsTrimmedMembersTarget.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[assembly: UnconditionalSuppressMessage ("Test", "IL2071", + Scope = "type", + Target = "T:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.UnusedTypeWithRedundantSuppression")] +[assembly: UnconditionalSuppressMessage ("Test", "IL2071", + Scope = "member", + Target = "P:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.UnusedTypeWithMembers.UnusedPropertyWithSuppression")] +[assembly: UnconditionalSuppressMessage ("Test", "IL2071", + Scope = "member", + Target = "E:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.UnusedTypeWithMembers.UnusedEventWithSuppression")] +[assembly: UnconditionalSuppressMessage ("Test", "IL2071", + Scope = "member", + Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.UnusedTypeWithMembers.UnusedMethodWithSuppression")] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + class DetectRedundantSuppressionsTrimmedMembersTarget + { + [ExpectedWarning ("IL2072")] + static void Main () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (DetectRedundantSuppressionsTrimmedMembersTarget); + } + } + + class UnusedTypeWithRedundantSuppression + { + } + + class UnusedTypeWithMembers + { + int UnusedPropertyWithSuppression { get; set; } + + event EventHandler UnusedEventWithSuppression { + add { } + remove { } + } + + void UnusedMethodWithSuppression () { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithMemberScopeNullTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithMemberScopeNullTarget.cs new file mode 100644 index 0000000000000..75a4b4115721e --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithMemberScopeNullTarget.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +// A module level suppression with a 'type' or 'member' scope must also specify the target, pointing +// to the type or member where the suppression should be put. +[module: UnconditionalSuppressMessage ("Test", "IL2026", Scope = "member", Target = null)] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [LogContains ("IL2026")] + class ModuleSuppressionWithMemberScopeNullTarget + { + static void Main () + { + TriggerWarning (); + } + + [RequiresUnreferencedCode ("TriggerWarning")] + public static Type TriggerWarning () + { + return typeof (ModuleSuppressionWithMemberScopeNullTarget); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithModuleScopeNullTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithModuleScopeNullTarget.cs new file mode 100644 index 0000000000000..0e10fe49c5611 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithModuleScopeNullTarget.cs @@ -0,0 +1,32 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[module: UnconditionalSuppressMessage ("Test", "IL2026", Scope = "module", Target = null)] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [LogDoesNotContain ("IL2026")] + class ModuleSuppressionWithModuleScopeNullTarget + { + static void Main () + { + TriggerWarning (); + } + + [RequiresUnreferencedCode ("TriggerWarning")] + public static Type TriggerWarning () + { + return typeof (ModuleSuppressionWithModuleScopeNullTarget); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithNullScopeNullTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithNullScopeNullTarget.cs new file mode 100644 index 0000000000000..38ec8d9d512fe --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/ModuleSuppressionWithNullScopeNullTarget.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +// A module level suppression with a null scope will be put on the current module. +[module: UnconditionalSuppressMessage ("Test", "IL2026", Scope = null, Target = null)] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [LogDoesNotContain ("IL2026")] + class ModuleSuppressionWithNullScopeNullTarget + { + static void Main () + { + TriggerWarning (); + } + + [RequiresUnreferencedCode ("TriggerWarning")] + public static Type TriggerWarning () + { + return typeof (ModuleSuppressionWithNullScopeNullTarget); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInAssembly.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInAssembly.cs new file mode 100644 index 0000000000000..9aacd9c8efb25 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInAssembly.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[assembly: UnconditionalSuppressMessage ("Test", "IL2072:Suppress unrecognized reflection pattern warnings in this assembly")] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ +#if !NETCOREAPP + [Reference ("System.Core.dll")] +#endif + [SkipKeptItemsValidation] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + public class SuppressWarningsInAssembly + { + public static void Main () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (SuppressWarningsInAssembly); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs new file mode 100644 index 0000000000000..4c74df0ffe0ec --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs @@ -0,0 +1,612 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class SuppressWarningsInCompilerGeneratedCode + { + public static void Main () + { + SuppressInIteratorBody.Test (); + SuppressInAsyncBody.Test (); + SuppressInLocalFunction.Test (); + SuppressInLambda.Test (); + SuppressInComplex.Test (); + } + class SuppressInIteratorBody + { + [UnconditionalSuppressMessage ("Test", "IL2026")] + static IEnumerable TestCallRUCMethod () + { + RequiresUnreferencedCodeMethod (); + yield return 0; + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static IEnumerable TestReflectionAccessRUCMethod () + { + yield return 0; + typeof (SuppressWarningsInCompilerGeneratedCode) + .GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic) + .Invoke (null, new object[] { }); + yield return 0; + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static IEnumerable TestLdftnOnRUCMethod () + { + yield return 0; + var _ = new Action (RequiresUnreferencedCodeMethod); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static IEnumerable TestDynamicallyAccessedMethod () + { + typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); + yield return 0; + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] + static IEnumerable TestMethodParameterWithRequirements (Type unknownType = null) + { + unknownType.RequiresNonPublicMethods (); + yield return 0; + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static IEnumerable TestGenericMethodParameterRequirement () + { + MethodWithGenericWhichRequiresMethods (); + yield return 0; + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static IEnumerable TestGenericTypeParameterRequirement () + { + new TypeWithGenericWhichRequiresNonPublicFields (); + yield return 0; + } + + public static void Test () + { + TestCallRUCMethod (); + TestReflectionAccessRUCMethod (); + TestLdftnOnRUCMethod (); + TestDynamicallyAccessedMethod (); + TestMethodParameterWithRequirements (); + TestGenericMethodParameterRequirement (); + TestGenericTypeParameterRequirement (); + } + } + + class SuppressInAsyncBody + { + [UnconditionalSuppressMessage ("Test", "IL2026")] + static async void TestCallRUCMethod () + { + RequiresUnreferencedCodeMethod (); + await MethodAsync (); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static async void TestReflectionAccessRUCMethod () + { + await MethodAsync (); + typeof (SuppressWarningsInCompilerGeneratedCode) + .GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic) + .Invoke (null, new object[] { }); + await MethodAsync (); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static async void TestLdftnOnRUCMethod () + { + await MethodAsync (); + var _ = new Action (RequiresUnreferencedCodeMethod); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static async void TestDynamicallyAccessedMethod () + { + typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); + await MethodAsync (); + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] + static async void TestMethodParameterWithRequirements (Type unknownType = null) + { + unknownType.RequiresNonPublicMethods (); + await MethodAsync (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static async void TestGenericMethodParameterRequirement () + { + MethodWithGenericWhichRequiresMethods (); + await MethodAsync (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static async void TestGenericTypeParameterRequirement () + { + new TypeWithGenericWhichRequiresNonPublicFields (); + await MethodAsync (); + } + + public static void Test () + { + TestCallRUCMethod (); + TestReflectionAccessRUCMethod (); + TestLdftnOnRUCMethod (); + TestDynamicallyAccessedMethod (); + TestMethodParameterWithRequirements (); + TestGenericMethodParameterRequirement (); + TestGenericTypeParameterRequirement (); + } + } + + class SuppressInLocalFunction + { + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestCallRUCMethod () + { + LocalFunction (); + + void LocalFunction () => RequiresUnreferencedCodeMethod (); + } + + [UnconditionalSuppressMessage ("Test", "IL2121", Justification = "The IL2026 warning is issued only by the analyzer, for the linker this suppression is redundant.")] + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestCallRUCMethodUnused () + { + void LocalFunction () => RequiresUnreferencedCodeMethod (); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestReflectionAccessRUCMethod () + { + LocalFunction (); + + void LocalFunction () => typeof (SuppressWarningsInCompilerGeneratedCode) + .GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic) + .Invoke (null, new object[] { }); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestLdftnOnRUCMethod () + { + LocalFunction (); + + void LocalFunction () + { var _ = new Action (RequiresUnreferencedCodeMethod); } + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestDynamicallyAccessedMethod () + { + LocalFunction (); + + void LocalFunction () => typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] + static void TestMethodParameterWithRequirements (Type unknownType = null) + { + LocalFunction (); + + void LocalFunction () => unknownType.RequiresNonPublicMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static void TestGenericMethodParameterRequirement () + { + LocalFunction (); + + void LocalFunction () => MethodWithGenericWhichRequiresMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static void TestGenericTypeParameterRequirement () + { + LocalFunction (); + + void LocalFunction () => new TypeWithGenericWhichRequiresNonPublicFields (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static void TestGenericLocalFunction () + { + LocalFunction (); + + void LocalFunction<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + typeof (T).RequiresPublicMethods (); + } + } + + [UnconditionalSuppressMessage ("Test", "IL2087")] + static void TestGenericLocalFunctionInner () + { + LocalFunction (); + + void LocalFunction () + { + typeof (TUnknown).RequiresPublicMethods (); + typeof (TSecond).RequiresPublicMethods (); + } + } + + static void TestGenericLocalFunctionWithAnnotations<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () + { + LocalFunction (); + + void LocalFunction<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TInnerPublicMethods> () + { + typeof (TPublicMethods).RequiresPublicMethods (); + typeof (TInnerPublicMethods).RequiresPublicMethods (); + } + } + + static void TestGenericLocalFunctionWithAnnotationsAndClosure<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> (int p = 0) + { + LocalFunction (); + + void LocalFunction<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TInnerPublicMethods> () + { + p++; + typeof (TPublicMethods).RequiresPublicMethods (); + typeof (TInnerPublicMethods).RequiresPublicMethods (); + } + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestCallRUCMethodInLtftnLocalFunction () + { + var _ = new Action (LocalFunction); + + void LocalFunction () => RequiresUnreferencedCodeMethod (); + } + + class DynamicallyAccessedLocalFunction + { + [ExpectedWarning ("IL2118", "LocalFunction", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + public static void TestCallRUCMethodInDynamicallyAccessedLocalFunction () + { + typeof (DynamicallyAccessedLocalFunction).RequiresNonPublicMethods (); + + LocalFunction (); + + void LocalFunction () => RequiresUnreferencedCodeMethod (); + } + } + + static void TestSuppressionOnLocalFunction () + { + LocalFunction (); + + [UnconditionalSuppressMessage ("Test", "IL2026")] // This supresses the RequiresUnreferencedCodeMethod + void LocalFunction (Type unknownType = null) + { + RequiresUnreferencedCodeMethod (); + } + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] // This suppresses the unknownType.RequiresNonPublicMethods + static void TestSuppressionOnOuterAndLocalFunction () + { + LocalFunction (); + + [UnconditionalSuppressMessage ("Test", "IL2026")] // This supresses the RequiresUnreferencedCodeMethod + void LocalFunction (Type unknownType = null) + { + RequiresUnreferencedCodeMethod (); + unknownType.RequiresNonPublicMethods (); + } + } + + class TestSuppressionOnOuterWithSameName + { + public static void Test () + { + Outer (); + Outer (0); + } + + [UnconditionalSuppressMessage ("Test", "IL2121")] + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void Outer () + { + // Even though this method has the same name as Outer(int i), + // it should not suppress warnings originating from compiler-generated + // code for the lambda contained in Outer(int i). + } + + static void Outer (int i) + { + LocalFunction (); + + [ExpectedWarning ("IL2026", "--RequiresUnreferencedCodeMethod--")] + void LocalFunction () => RequiresUnreferencedCodeMethod (); + } + } + + public static void Test () + { + TestCallRUCMethod (); + TestCallRUCMethodUnused (); + TestReflectionAccessRUCMethod (); + TestLdftnOnRUCMethod (); + TestDynamicallyAccessedMethod (); + TestMethodParameterWithRequirements (); + TestGenericMethodParameterRequirement (); + TestGenericTypeParameterRequirement (); + TestGenericLocalFunction (); + TestGenericLocalFunctionInner (); + TestGenericLocalFunctionWithAnnotations (); + TestGenericLocalFunctionWithAnnotationsAndClosure (); + TestCallRUCMethodInLtftnLocalFunction (); + DynamicallyAccessedLocalFunction.TestCallRUCMethodInDynamicallyAccessedLocalFunction (); + TestSuppressionOnLocalFunction (); + TestSuppressionOnOuterAndLocalFunction (); + TestSuppressionOnOuterWithSameName.Test (); + } + } + + class SuppressInLambda + { + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestCallRUCMethod () + { + Action lambda = + () => RequiresUnreferencedCodeMethod (); + + lambda (); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestCallRUCMethodUnused () + { + Action _ = + () => RequiresUnreferencedCodeMethod (); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestReflectionAccessRUCMethod () + { + Action _ = + () => typeof (SuppressWarningsInCompilerGeneratedCode) + .GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic) + .Invoke (null, new object[] { }); + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestLdftnOnRUCMethod () + { + Action _ = + () => { var _ = new Action (RequiresUnreferencedCodeMethod); }; + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestDynamicallyAccessedMethod () + { + Action _ = + () => typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] + static void TestMethodParameterWithRequirements (Type unknownType = null) + { + Action _ = + () => unknownType.RequiresNonPublicMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static void TestGenericMethodParameterRequirement () + { + Action _ = + () => MethodWithGenericWhichRequiresMethods (); + } + + [UnconditionalSuppressMessage ("Test", "IL2091")] + static void TestGenericTypeParameterRequirement () + { + Action _ = + () => new TypeWithGenericWhichRequiresNonPublicFields (); + } + + class DynamicallyAccessedLambda + { + [ExpectedWarning ("IL2118", nameof (TestCallRUCMethodInDynamicallyAccessedLambda), ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + public static void TestCallRUCMethodInDynamicallyAccessedLambda () + { + typeof (DynamicallyAccessedLambda).RequiresAll (); + + Action lambda = () => RequiresUnreferencedCodeMethod (); + + lambda (); + } + } + + class DynamicallyAccessedLambdaUnused + { + [ExpectedWarning ("IL2118", nameof (TestCallRUCMethodInDynamicallyAccessedLambda), ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + public static void TestCallRUCMethodInDynamicallyAccessedLambda () + { + typeof (DynamicallyAccessedLambdaUnused).RequiresAll (); + + Action _ = () => RequiresUnreferencedCodeMethod (); + } + } + + static void TestSuppressionOnLambda () + { + var lambda = + // https://github.com/dotnet/roslyn/issues/59746 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Analyzer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + () => RequiresUnreferencedCodeMethod (); + + lambda (); + } + + [UnconditionalSuppressMessage ("Test", "IL2067")] + static void TestSuppressionOnOuterAndLambda () + { + var lambda = + // https://github.com/dotnet/roslyn/issues/59746 + [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Analyzer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + (Type unknownType) => { + RequiresUnreferencedCodeMethod (); + unknownType.RequiresNonPublicMethods (); + }; + + lambda (null); + } + + class TestSuppressionOnOuterWithSameName + { + public static void Test () + { + Outer (); + Outer (0); + } + + [UnconditionalSuppressMessage ("Test", "IL2121")] + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void Outer () + { + // Even though this method has the same name as Outer(int i), + // it should not suppress warnings originating from compiler-generated + // code for the lambda contained in Outer(int i). + } + + static void Outer (int i) + { + var lambda = + [ExpectedWarning ("IL2026", "--RequiresUnreferencedCodeMethod--")] + () => RequiresUnreferencedCodeMethod (); + + lambda (); + } + } + + public static void Test () + { + TestCallRUCMethod (); + TestCallRUCMethodUnused (); + TestReflectionAccessRUCMethod (); + TestLdftnOnRUCMethod (); + TestDynamicallyAccessedMethod (); + TestMethodParameterWithRequirements (); + TestGenericMethodParameterRequirement (); + TestGenericTypeParameterRequirement (); + DynamicallyAccessedLambda.TestCallRUCMethodInDynamicallyAccessedLambda (); + DynamicallyAccessedLambdaUnused.TestCallRUCMethodInDynamicallyAccessedLambda (); + TestSuppressionOnLambda (); + TestSuppressionOnOuterAndLambda (); + TestSuppressionOnOuterWithSameName.Test (); + + } + } + + class SuppressInComplex + { + [UnconditionalSuppressMessage ("Test", "IL2026")] + static void TestIteratorLocalFunction () + { + LocalFunction (); + + IEnumerable LocalFunction () + { + yield return 0; + RequiresUnreferencedCodeMethod (); + yield return 1; + } + } + + static async void TestIteratorLocalFunctionInAsync () + { + await MethodAsync (); + LocalFunction (); + await MethodAsync (); + + [UnconditionalSuppressMessage ("Test", "IL2026")] + IEnumerable LocalFunction () + { + yield return 0; + RequiresUnreferencedCodeMethod (); + yield return 1; + } + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static async void TestIteratorLocalFunctionInAsyncWithoutInner () + { + await MethodAsync (); + LocalFunction (); + await MethodAsync (); + + IEnumerable LocalFunction () + { + yield return 0; + RequiresUnreferencedCodeMethod (); + yield return 1; + } + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + static IEnumerable TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator () + { + MethodWithGenericWhichRequiresMethods (); + yield return 0; + } + + public static void Test () + { + TestIteratorLocalFunction (); + TestIteratorLocalFunctionInAsync (); + TestIteratorLocalFunctionInAsyncWithoutInner (); + TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator (); + } + } + + static async Task MethodAsync () + { + return await Task.FromResult (0); + } + + [RequiresUnreferencedCode ("--RequiresUnreferencedCodeMethod--")] + static void RequiresUnreferencedCodeMethod () + { + } + + class TypeWithRUCMethod + { + [RequiresUnreferencedCode ("--TypeWithRUCMethod.RequiresUnreferencedCodeMethod--")] + static void RequiresUnreferencedCodeMethod () + { + } + } + + static void MethodWithGenericWhichRequiresMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] T> () + { + } + + class TypeWithGenericWhichRequiresNonPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] T> { } + + class TestType { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCopyAssembly.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCopyAssembly.cs new file mode 100644 index 0000000000000..61cccba5de540 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCopyAssembly.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ +#if !NETCOREAPP + [Reference ("System.Core.dll")] +#endif + [SetupLinkerAction ("copy", "test")] + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class SuppressWarningsInCopyAssembly + { + public static void Main () + { + // Regression test for https://github.com/dotnet/runtime/issues/56252 + // where a compiler-generated method is marked before the source code with the suppression + foreach (var type in GetTypeNames ()) + Console.WriteLine (type); + } + + [UnconditionalSuppressMessage ("", "IL2026")] + public static IEnumerable GetTypeNames () + { + foreach (var type in Assembly.GetCallingAssembly ().GetTypes ()) + yield return type.Name; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypes.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypes.cs new file mode 100644 index 0000000000000..43ea7db14a48e --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypes.cs @@ -0,0 +1,189 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ +#if !NETCOREAPP + [Reference ("System.Core.dll")] +#endif + [SkipKeptItemsValidation] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + public class SuppressWarningsInMembersAndTypes + { + public static void Main () + { + var suppressWarningsInType = new SuppressWarningsInType (); + suppressWarningsInType.Warning1 (); + suppressWarningsInType.Warning2 (); + var nestedType = new SuppressWarningsInType.NestedType (); + nestedType.Warning3 (); + var property = suppressWarningsInType.Property; + suppressWarningsInType.Event += SuppressWarningsInType.EventSubscriber; + + var suppressWarningsInMembers = new SuppressWarningsInMembers (); + suppressWarningsInMembers.Method (); + suppressWarningsInMembers.SuppressionHasNullParameters (); + int propertyThatTriggersWarning = suppressWarningsInMembers.Property; + + NestedType.Warning (); + + SuppressOnTypeMarkedEntirely.Test (); + + SuppressOnProperty.Test (); + + SuppressOnEvent.Test (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (SuppressWarningsInMembersAndTypes); + } + + [UnconditionalSuppressMessage ("Test", "IL2072:Suppress UnrecognizedReflectionPattern warnings on a nested type")] + public class NestedType + { + public static void Warning () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + } + + [UnconditionalSuppressMessage ("Test", "IL2072:UnrecognizedReflectionPattern")] + public class SuppressWarningsInType + { + public void Warning1 () + { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public void Warning2 () + { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public class NestedType + { + public void Warning3 () + { + void Warning4 () + { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (); + Warning4 (); + } + } + + public int Property { + get { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + return 0; + } + } + + public static void EventSubscriber (object sender, EventArgs e) + { + + } + + public event EventHandler Event { + add { Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); } + remove { } + } + } + + public class SuppressWarningsInMembers + { + [UnconditionalSuppressMessage ("Test", "IL2072:UnrecognizedReflectionPattern")] + public void Method () + { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + [UnconditionalSuppressMessage ("Test", "IL2072:Suppression with scope value equal to null", + Scope = null, + Target = null, + MessageId = null)] + public void SuppressionHasNullParameters () + { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public int Property { + [UnconditionalSuppressMessage ("Test", "IL2072:UnrecognizedReflectionPattern")] + get { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + return 0; + } + } + } + + class SuppressOnTypeMarkedEntirely + { + [LogDoesNotContain (nameof (TypeWithSuppression) + " has more than one unconditional suppression")] + [UnconditionalSuppressMessage ("Test", "IL2026")] + class TypeWithSuppression + { + [ExpectedNoWarnings] + public TypeWithSuppression () + { + MethodWithRUC (); + + // Triggering the suppression check a second time + // still shouldn't warn about duplicate suppressions. + MethodWithRUC (); + } + + int _field; + } + + public static void Test () + { + typeof (TypeWithSuppression).RequiresAll (); + } + + [RequiresUnreferencedCode ("")] + static void MethodWithRUC () { } + } + + class SuppressOnProperty + { + public static void Test () + { + var test = Property; + } + + [UnconditionalSuppressMessage ("Test", "IL2072")] + static int Property { + get { + Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + return 0; + } + } + } + + class SuppressOnEvent + { + public static void Test () + { + Event += EventSubscriber; + } + + static void EventSubscriber (object sender, EventArgs e) + { + + } + + [UnconditionalSuppressMessage ("Test", "IL2072")] + static event EventHandler Event { + add { Expression.Call (SuppressWarningsInMembersAndTypes.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); } + remove { } + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypesUsingTarget.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypesUsingTarget.cs new file mode 100644 index 0000000000000..9d70394e9deb5 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInMembersAndTypesUsingTarget.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using System.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[module: UnconditionalSuppressMessage ("Test", "IL2072", Scope = "type", Target = "T:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInType")] +[module: UnconditionalSuppressMessage ("Test", "IL2072", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInMembers.Method")] +[module: UnconditionalSuppressMessage ("Test", "IL2072", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInMembers.get_Property")] +[module: UnconditionalSuppressMessage ("Test", "IL2072", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.SuppressWarningsInMembersAndTypesUsingTarget.NestedType.Warning")] +[module: UnconditionalSuppressMessage ("Test", "IL2072", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInMembers.MultipleWarnings")] +[module: UnconditionalSuppressMessage ("Test", "IL2026", Scope = "member", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInMembers.MultipleSuppressions")] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ +#if !NETCOREAPP + [Reference ("System.Core.dll")] +#endif + [SkipKeptItemsValidation] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + public class SuppressWarningsInMembersAndTypesUsingTarget + { + /// + /// This test case checks module level UnconditionalSuppressMessage, primarily using + /// System.Linq.Expressions.Expression.(Type type, string methodName, Type[]? typeArguments, params System.Linq.Expressions.Expression[]? arguments) + /// which has a RUC attribute but is treated as an intrinsic by the trimmer. The test case also has some member level suppressions and its + /// own RUC method (the IL2026 suppression in the code is due to this) + /// + public static void Main () + { + NestedType.Warning (); + var warningsInType = new WarningsInType (); + warningsInType.Warning1 (); + var warningInNestedType = new WarningsInType.NestedType (); + warningInNestedType.Warning3 (); + + var warningsInMembers = new WarningsInMembers (); + warningsInMembers.Method (); + int propertyThatTriggersWarning = warningsInMembers.Property; + + WarningsInMembers.MultipleWarnings (); + WarningsInMembers.MultipleSuppressions (); + } + + public static Type TriggerUnrecognizedPattern () + { + return typeof (SuppressWarningsInMembersAndTypesUsingTarget); + } + + public class NestedType + { + public static void Warning () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + } + } + + public class WarningsInType + { + public void Warning1 () + { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public void Warning2 () + { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public class NestedType + { + public void Warning3 () + { + void Warning4 () + { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (); + Warning4 (); + } + } + } + + [ExpectedNoWarnings] + public class WarningsInMembers + { + public void Method () + { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + public int Property { + get { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + return 0; + } + } + + [UnconditionalSuppressMessage ("Test", "IL2026")] + public static void MultipleWarnings () + { + Expression.Call (SuppressWarningsInMembersAndTypesUsingTarget.TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + RUCMethod (); + } + + // NativeAOT currently doesn't track suppressions such that it could detect duplicates + [LogContains ("Element 'Mono.Linker.Tests.Cases.Warnings.WarningSuppression.WarningsInMembers." + nameof (MultipleSuppressions) + "()'" + + " has more than one unconditional suppression.", ProducedBy = ProducedBy.Trimmer)] + [UnconditionalSuppressMessage ("Test", "IL2026")] + public static void MultipleSuppressions () + { + RUCMethod (); + } + + [RequiresUnreferencedCode ("--RUCMethod--")] + static void RUCMethod () + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInModule.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInModule.cs new file mode 100644 index 0000000000000..4d6e71febcf90 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInModule.cs @@ -0,0 +1,33 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +[module: UnconditionalSuppressMessage ("Test", "IL2072:Suppress unrecognized reflection pattern warnings in this module")] +[module: UnconditionalSuppressMessage ("Test", "IL2026:Test that specifying an invalid scope will result in a global suppression being ignored.", + Scope = "invalidScope", Target = "Non-existent target")] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ +#if !NETCOREAPP + [Reference ("System.Core.dll")] +#endif + [SkipKeptItemsValidation] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + [LogContains ("warning IL2108: .* Invalid scope 'invalidScope' used in 'UnconditionalSuppressMessageAttribute'", regexMatch: true)] + public class SuppressWarningsInModule + { + [ExpectedWarning ("IL2026", "TriggerUnrecognizedPattern()")] + public static void Main () + { + Expression.Call (TriggerUnrecognizedPattern (), "", Type.EmptyTypes); + } + + [RequiresUnreferencedCode ("Test that the global unconditional suppression will not work when using an invalid scope.")] + public static Type TriggerUnrecognizedPattern () + { + return typeof (SuppressWarningsInModule); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.mono.xml b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.mono.xml new file mode 100644 index 0000000000000..b7f60a39074e8 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.mono.xml @@ -0,0 +1,35 @@ + + + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Main + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.NestedType.Warning3 + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.NestedType.Warning4``1(System.Collections.Generic.List{``0}@) + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Warning1 + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.get_Warning2 + + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.netcore.xml b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.netcore.xml new file mode 100644 index 0000000000000..3761a1c857895 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXml.netcore.xml @@ -0,0 +1,35 @@ + + + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Main + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.NestedType.Warning3 + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.NestedType.Warning4``1(System.Collections.Generic.List{``0}@) + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Warning1 + + + ILLink + IL2072 + member + M:Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.get_Warning2 + + + diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlMono.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlMono.cs new file mode 100644 index 0000000000000..c191aa014f44c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlMono.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Warnings.Dependencies; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [TestCaseRequirements (TestRunCharacteristics.TargetingNetFramework, "This test is specific to .NET Framework")] + // For mono though, we have to specify the assembly (Mono.Linker.Tests.Cases.Expectations) because at the time of processing + // that assembly is not yet loaded into the closure in the linker, so it won't find the attribute type. + [SetupLinkAttributesFile ("SuppressWarningsUsingTargetViaXml.mono.xml")] + [SetupCompileBefore ("library.dll", new[] { typeof (TriggerWarnings_Lib) }, new[] { "System.Core.dll" })] + + [KeptAssembly ("library.dll")] + [SetupLinkerAction ("link", "library.dll")] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + public class SuppressWarningsUsingTargetViaXmlMono + { + public static void Main () + { + TriggerWarnings_Lib.Main (); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlNetCore.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlNetCore.cs new file mode 100644 index 0000000000000..bf6f717a0519f --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsUsingTargetViaXmlNetCore.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; +using Mono.Linker.Tests.Cases.Warnings.Dependencies; + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [TestCaseRequirements (TestRunCharacteristics.TargetingNetCore, "This test is specific to .NET Core")] + // For netcoreapp we don't have to specify the assembly for the attribute, since the attribute comes from corelib + // and will be found always. + [SetupLinkAttributesFile ("SuppressWarningsUsingTargetViaXml.netcore.xml")] + [SetupCompileBefore ("library.dll", new[] { typeof (TriggerWarnings_Lib) })] + + [KeptAssembly ("library.dll")] + [SetupLinkerAction ("link", "library.dll")] + [LogDoesNotContain ("TriggerUnrecognizedPattern()")] + public class SuppressWarningsUsingTargetViaXmlNetCore + { + public static void Main () + { + TriggerWarnings_Lib.Main (); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/TargettedModuleSuppressionWithUnmatchedScope.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/TargettedModuleSuppressionWithUnmatchedScope.cs new file mode 100644 index 0000000000000..881682a6e6bb0 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/TargettedModuleSuppressionWithUnmatchedScope.cs @@ -0,0 +1,48 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +// Module suppressions that have scope argument `type` or `member` will always cause the value of the target parameter +// to be parsed using the DocumentationSignatureParser, this parser rules where will the suppression be put depending upon the +// prefix used in the fully qualified member specified for the target. +[module: UnconditionalSuppressMessage ("Test", "IL2026", + Scope = "type", Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.TargettedModuleSuppressionWithUnmatchedScope.Main()")] +// The target of this suppression will be ignored since the suppression was put on a higher scope -- this suppression will be +// put on the module. +[module: UnconditionalSuppressMessage ("Test", "IL2072", + Scope = "module", Target = "T:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.TargettedModuleSuppressionWithUnmatchedScope")] +[module: UnconditionalSuppressMessage ("Test", "IL2026", + Target = "M:Mono.Linker.Tests.Cases.Warnings.WarningSuppression.TargettedModuleSuppressionWithUnmatchedScope.Main()")] + +namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression +{ + [SkipKeptItemsValidation] + [LogContains ("warning IL2108: .* Invalid scope '' used in 'UnconditionalSuppressMessageAttribute'", regexMatch: true)] + [LogDoesNotContain ("IL2026")] + [LogDoesNotContain ("IL2072")] + class TargettedModuleSuppressionWithUnmatchedScope + { + static void Main () + { + // IL2072 + Expression.Call (TriggerWarning (), "", Type.EmptyTypes); + // IL2026 + TriggerWarning (); + } + + [RequiresUnreferencedCode ("TriggerWarning")] + public static Type TriggerWarning () + { + return typeof (TargettedModuleSuppressionWithUnmatchedScope); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs index de77b4e4d8621..aef1f75e5188b 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs @@ -39,6 +39,11 @@ public static IEnumerable RequiresCapability () return TestNamesBySuiteName (); } + public static IEnumerable Warnings () + { + return TestNamesBySuiteName (); + } + public static TestCaseCollector CreateCollector () { GetDirectoryPaths (out string rootSourceDirectory, out string testCaseAssemblyPath); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs index e34ce26045a1f..2fe6e6346c6f3 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs @@ -45,6 +45,13 @@ public void RequiresCapability(string t) Run(t); } + [Theory] + [MemberData (nameof (TestDatabase.Warnings), MemberType = typeof (TestDatabase))] + public void Warnings (string t) + { + Run (t); + } + protected virtual void Run(string testName) { TestCase testCase = TestDatabase.GetTestCaseFromName(testName) ?? throw new InvalidOperationException($"Unknown test {testName}"); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs index dc1a00667d3b6..f5100d6acc132 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerDriver.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using ILCompiler; @@ -71,7 +72,15 @@ public ILScanResults Trim (ILCompilerOptions options, ILogWriter logWriter) compilationRoots.Add (new ILCompiler.DependencyAnalysis.TrimmingDescriptorNode (descriptor)); } - Logger logger = new Logger (logWriter, ilProvider, isVerbose: true); + Logger logger = new Logger ( + logWriter, + ilProvider, + isVerbose: true, + suppressedWarnings: Enumerable.Empty (), + options.SingleWarn, + singleWarnEnabledModules: Enumerable.Empty (), + singleWarnDisabledModules: Enumerable.Empty (), + suppressedCategories: Enumerable.Empty ()); ilProvider = new FeatureSwitchManager (ilProvider, logger, options.FeatureSwitches); diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptions.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptions.cs index 5b48963927e19..d7ae07095635a 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptions.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptions.cs @@ -15,5 +15,6 @@ public class ILCompilerOptions public Dictionary FeatureSwitches = new Dictionary (); public List Descriptors = new List (); public bool FrameworkCompilation; + public bool SingleWarn; } } diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptionsBuilder.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptionsBuilder.cs index afb660e9b7bc9..9637e03365977 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptionsBuilder.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ILCompilerOptionsBuilder.cs @@ -163,6 +163,9 @@ public virtual void AddAdditionalArgument (string flag, string[] values) if (flag == "--feature") { Options.FeatureSwitches.Add (values[0], bool.Parse (values[1])); } + else if (flag == "--singlewarn") { + Options.SingleWarn = true; + } } public virtual void ProcessTestInputAssembly (NPath inputAssemblyPath)