From d111b6bc8d300a182d730c45f4453bfd640f4221 Mon Sep 17 00:00:00 2001 From: Martin Tomka Date: Wed, 19 Jul 2023 21:05:36 +0200 Subject: [PATCH] fixes --- .../UnconditionalSuppressMessageAttribute.cs | 94 +++++++++++++++++++ ...akerResilienceStrategyBuilderExtensions.cs | 10 +- ...backResilienceStrategyBuilderExtensions.cs | 6 +- ...gingResilienceStrategyBuilderExtensions.cs | 6 +- .../ResilienceStrategyBuilderBase.cs | 9 +- .../ResilienceStrategyBuilderExtensions.cs | 6 +- ...etryResilienceStrategyBuilderExtensions.cs | 6 +- .../Telemetry/ResilienceStrategyTelemetry.cs | 26 +++-- ...eoutResilienceStrategyBuilderExtensions.cs | 6 +- src/Polly.Core/Utils/ValidationHelper.cs | 6 +- ...iterResilienceStrategyBuilderExtensions.cs | 6 +- .../ResilienceStrategyBuilderTests.cs | 9 -- 12 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 src/LegacySupport/UnconditionalSuppressMessageAttribute.cs diff --git a/src/LegacySupport/UnconditionalSuppressMessageAttribute.cs b/src/LegacySupport/UnconditionalSuppressMessageAttribute.cs new file mode 100644 index 00000000000..50a66797c10 --- /dev/null +++ b/src/LegacySupport/UnconditionalSuppressMessageAttribute.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#pragma warning disable IDE0079 +#pragma warning disable SA1101 +#pragma warning disable SA1116 +#pragma warning disable SA1117 +#pragma warning disable SA1512 +#pragma warning disable SA1623 +#pragma warning disable SA1642 +#pragma warning disable S3903 + +namespace System.Diagnostics.CodeAnalysis; + +/// +/// /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a +/// single code artifact. +/// +/// +/// is different than +/// in that it doesn't have a +/// . So it is always preserved in the compiled assembly. +/// +[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] +[ExcludeFromCodeCoverage] +internal sealed class UnconditionalSuppressMessageAttribute : Attribute +{ + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } +} diff --git a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderExtensions.cs index 1236f532d1f..fdb3ee02d74 100644 --- a/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/CircuitBreaker/CircuitBreakerResilienceStrategyBuilderExtensions.cs @@ -5,8 +5,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// Circuit breaker strategy extensions for . /// @@ -102,6 +100,10 @@ public static ResilienceStrategyBuilder AddSimpleCircuitBreaker(this ResilienceS return builder.AddSimpleCircuitBreakerCore(options); } + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] private static TBuilder AddAdvancedCircuitBreakerCore(this TBuilder builder, AdvancedCircuitBreakerStrategyOptions options) where TBuilder : ResilienceStrategyBuilderBase { @@ -118,6 +120,10 @@ private static TBuilder AddAdvancedCircuitBreakerCore(this TB options); } + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] private static TBuilder AddSimpleCircuitBreakerCore(this TBuilder builder, SimpleCircuitBreakerStrategyOptions options) where TBuilder : ResilienceStrategyBuilderBase { diff --git a/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs index a997a31722a..b361f5b5014 100644 --- a/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Fallback/FallbackResilienceStrategyBuilderExtensions.cs @@ -4,8 +4,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// Provides extension methods for configuring fallback resilience strategies for . /// @@ -46,6 +44,10 @@ internal static ResilienceStrategyBuilder AddFallback(this ResilienceStrategyBui return builder; } + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] internal static void AddFallbackCore( this ResilienceStrategyBuilderBase builder, FallbackStrategyOptions options) diff --git a/src/Polly.Core/Hedging/HedgingResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Hedging/HedgingResilienceStrategyBuilderExtensions.cs index 50a98b0c805..7d2ee22b75e 100644 --- a/src/Polly.Core/Hedging/HedgingResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Hedging/HedgingResilienceStrategyBuilderExtensions.cs @@ -5,8 +5,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// Provides extension methods for configuring hedging resilience strategies for . /// @@ -47,6 +45,10 @@ internal static ResilienceStrategyBuilder AddHedging(this ResilienceStrategyBuil return builder; } + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] internal static void AddHedgingCore( this ResilienceStrategyBuilderBase builder, HedgingStrategyOptions options) diff --git a/src/Polly.Core/ResilienceStrategyBuilderBase.cs b/src/Polly.Core/ResilienceStrategyBuilderBase.cs index cf86d5671a0..92e4b273846 100644 --- a/src/Polly.Core/ResilienceStrategyBuilderBase.cs +++ b/src/Polly.Core/ResilienceStrategyBuilderBase.cs @@ -17,9 +17,6 @@ public abstract class ResilienceStrategyBuilderBase { private readonly List _entries = new(); private bool _used; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - private Action _validator = ValidationHelper.ValidateObject; -#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code private protected ResilienceStrategyBuilderBase() { @@ -121,11 +118,7 @@ private protected ResilienceStrategyBuilderBase(ResilienceStrategyBuilderBase ot /// /// Thrown when the attempting to assign to this property. [EditorBrowsable(EditorBrowsableState.Never)] - public Action Validator - { - get => _validator; - internal set => _validator = Guard.NotNull(value); - } + public Action Validator { get; private protected set; } = ValidationHelper.ValidateObject; internal abstract bool IsGenericBuilder { get; } diff --git a/src/Polly.Core/ResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/ResilienceStrategyBuilderExtensions.cs index 771f7c0d0a4..da92795899e 100644 --- a/src/Polly.Core/ResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/ResilienceStrategyBuilderExtensions.cs @@ -17,15 +17,17 @@ public static class ResilienceStrategyBuilderExtensions /// The same builder instance. /// Thrown when is null. /// Thrown when this builder was already used to create a strategy. The builder cannot be modified after it has been used. + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "The EmptyOptions have nothing to validate.")] public static TBuilder AddStrategy(this TBuilder builder, ResilienceStrategy strategy) where TBuilder : ResilienceStrategyBuilderBase { Guard.NotNull(builder); Guard.NotNull(strategy); -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code return builder.AddStrategy(_ => strategy, EmptyOptions.Instance); -#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code } /// diff --git a/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs index c1f8b754bbc..5c68d1b0569 100644 --- a/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Retry/RetryResilienceStrategyBuilderExtensions.cs @@ -4,8 +4,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// Retry extension methods for the . /// @@ -46,6 +44,10 @@ public static ResilienceStrategyBuilder AddRetry(this Resilien return builder; } + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] private static void AddRetryCore( this ResilienceStrategyBuilderBase builder, RetryStrategyOptions options) diff --git a/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs b/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs index 2c507ebf8a7..5ce68bad552 100644 --- a/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs +++ b/src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; + namespace Polly.Telemetry; /// @@ -31,6 +33,14 @@ internal ResilienceStrategyTelemetry(ResilienceTelemetrySource source, Diagnosti /// The resilience context associated with this event. /// The event arguments. /// Thrown when is . + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "The reflection is not used when consuming the event.")] + [UnconditionalSuppressMessage( + "AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "The reflection is not used when consuming the event.")] public void Report(ResilienceEvent resilienceEvent, ResilienceContext context, TArgs args) { Guard.NotNull(context); @@ -44,11 +54,7 @@ public void Report(ResilienceEvent resilienceEvent, ResilienceContext con var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, resilienceEvent, context, null, args!); -#pragma warning disable IL2026 // The consumer of this method is Polly.Extensions and it does not use reflection at all -#pragma warning disable IL3050 DiagnosticSource.Write(resilienceEvent.EventName, telemetryArgs); -#pragma warning restore IL3050 -#pragma warning restore IL2026 TelemetryEventArguments.Return(telemetryArgs); } @@ -60,6 +66,14 @@ public void Report(ResilienceEvent resilienceEvent, ResilienceContext con /// The type of the result. /// The reported resilience event. /// The event arguments. + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "The reflection is not used when consuming the event.")] + [UnconditionalSuppressMessage( + "AOT", + "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", + Justification = "The reflection is not used when consuming the event.")] public void Report(ResilienceEvent resilienceEvent, OutcomeArguments args) { args.Context.AddResilienceEvent(resilienceEvent); @@ -71,11 +85,7 @@ public void Report(ResilienceEvent resilienceEvent, OutcomeArgum var telemetryArgs = TelemetryEventArguments.Get(TelemetrySource, resilienceEvent, args.Context, args.Outcome.AsOutcome(), args.Arguments!); -#pragma warning disable IL2026 // The consumer of this method is Polly.Extensions and it does not use reflection at all -#pragma warning disable IL3050 DiagnosticSource.Write(resilienceEvent.EventName, telemetryArgs); -#pragma warning restore IL3050 -#pragma warning restore IL2026 TelemetryEventArguments.Return(telemetryArgs); } diff --git a/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs b/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs index 806753d50f5..1b928dcaf81 100644 --- a/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.Core/Timeout/TimeoutResilienceStrategyBuilderExtensions.cs @@ -4,8 +4,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// Extension methods for adding timeouts to a . /// @@ -41,6 +39,10 @@ public static TBuilder AddTimeout(this TBuilder builder, TimeSpan time /// Thrown when or is . /// Thrown when are invalid. [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TimeoutStrategyOptions))] + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members preserved.")] public static TBuilder AddTimeout(this TBuilder builder, TimeoutStrategyOptions options) where TBuilder : ResilienceStrategyBuilderBase { diff --git a/src/Polly.Core/Utils/ValidationHelper.cs b/src/Polly.Core/Utils/ValidationHelper.cs index 2ecd374cdde..f50e9644273 100644 --- a/src/Polly.Core/Utils/ValidationHelper.cs +++ b/src/Polly.Core/Utils/ValidationHelper.cs @@ -8,13 +8,16 @@ namespace Polly.Utils; internal static class ValidationHelper { [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TimeSpan))] + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "The member of options are preserved and no trimmed. See builder.AddStrategy() extension.")] public static void ValidateObject(ResilienceValidationContext context) { Guard.NotNull(context); var errors = new List(); -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code if (!Validator.TryValidateObject(context.Instance, new ValidationContext(context.Instance), errors, true)) { var stringBuilder = new StringBuilder(context.PrimaryMessage); @@ -28,6 +31,5 @@ public static void ValidateObject(ResilienceValidationContext context) throw new ValidationException(stringBuilder.ToString()); } -#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code } } diff --git a/src/Polly.RateLimiting/RateLimiterResilienceStrategyBuilderExtensions.cs b/src/Polly.RateLimiting/RateLimiterResilienceStrategyBuilderExtensions.cs index b36027581c2..31211a2eeea 100644 --- a/src/Polly.RateLimiting/RateLimiterResilienceStrategyBuilderExtensions.cs +++ b/src/Polly.RateLimiting/RateLimiterResilienceStrategyBuilderExtensions.cs @@ -5,8 +5,6 @@ namespace Polly; -#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - /// /// The rate limiter extensions for . /// @@ -96,6 +94,10 @@ public static TBuilder AddRateLimiter( /// Thrown when are invalid. /// Thrown when for are invalid. [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(RateLimiterStrategyOptions))] + [UnconditionalSuppressMessage( + "Trimming", + "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", + Justification = "All options members are preserved.")] public static TBuilder AddRateLimiter( this TBuilder builder, RateLimiterStrategyOptions options) diff --git a/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs b/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs index 72bb3cf9cef..78eb9a0175b 100644 --- a/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs +++ b/test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs @@ -143,15 +143,6 @@ The field RetryCount must be between -1 and 100. """); } - [Fact] - public void Validator_Null_Throws() - { - new ResilienceStrategyBuilder() - .Invoking(b => b.Validator = null!) - .Should() - .Throw(); - } - [Fact] public void AddStrategy_MultipleNonDelegating_Ok() {