Skip to content

Commit

Permalink
fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
martintmk committed Jul 19, 2023
1 parent 4baa135 commit d111b6b
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 41 deletions.
94 changes: 94 additions & 0 deletions src/LegacySupport/UnconditionalSuppressMessageAttribute.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a
/// single code artifact.
/// </summary>
/// <remarks>
/// <see cref="UnconditionalSuppressMessageAttribute"/> is different than
/// <see cref="SuppressMessageAttribute"/> in that it doesn't have a
/// <see cref="ConditionalAttribute"/>. So it is always preserved in the compiled assembly.
/// </remarks>
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class UnconditionalSuppressMessageAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UnconditionalSuppressMessageAttribute"/>
/// class, specifying the category of the tool and the identifier for an analysis rule.
/// </summary>
/// <param name="category">The category for the attribute.</param>
/// <param name="checkId">The identifier of the analysis rule the attribute applies to.</param>
public UnconditionalSuppressMessageAttribute(string category, string checkId)
{
Category = category;
CheckId = checkId;
}

/// <summary>
/// Gets the category identifying the classification of the attribute.
/// </summary>
/// <remarks>
/// The <see cref="Category"/> property describes the tool or tool analysis category
/// for which a message suppression attribute applies.
/// </remarks>
public string Category { get; }

/// <summary>
/// Gets the identifier of the analysis tool rule to be suppressed.
/// </summary>
/// <remarks>
/// Concatenated together, the <see cref="Category"/> and <see cref="CheckId"/>
/// properties form a unique check identifier.
/// </remarks>
public string CheckId { get; }

/// <summary>
/// Gets or sets the scope of the code that is relevant for the attribute.
/// </summary>
/// <remarks>
/// The Scope property is an optional argument that specifies the metadata scope for which
/// the attribute is relevant.
/// </remarks>
public string? Scope { get; set; }

/// <summary>
/// Gets or sets a fully qualified path that represents the target of the attribute.
/// </summary>
/// <remarks>
/// The <see cref="Target"/> 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.
/// </remarks>
public string? Target { get; set; }

/// <summary>
/// Gets or sets an optional argument expanding on exclusion criteria.
/// </summary>
/// <remarks>
/// The <see cref="MessageId"/> property is an optional argument that specifies additional
/// exclusion where the literal metadata target is not sufficiently precise. For example,
/// the <see cref="UnconditionalSuppressMessageAttribute"/> 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.
/// </remarks>
public string? MessageId { get; set; }

/// <summary>
/// Gets or sets the justification for suppressing the code analysis message.
/// </summary>
public string? Justification { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// Circuit breaker strategy extensions for <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -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<TBuilder, TResult>(this TBuilder builder, AdvancedCircuitBreakerStrategyOptions<TResult> options)
where TBuilder : ResilienceStrategyBuilderBase
{
Expand All @@ -118,6 +120,10 @@ private static TBuilder AddAdvancedCircuitBreakerCore<TBuilder, TResult>(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<TBuilder, TResult>(this TBuilder builder, SimpleCircuitBreakerStrategyOptions<TResult> options)
where TBuilder : ResilienceStrategyBuilderBase
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// Provides extension methods for configuring fallback resilience strategies for <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -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<TResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] TOptions>(
this ResilienceStrategyBuilderBase builder,
FallbackStrategyOptions<TResult> options)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// Provides extension methods for configuring hedging resilience strategies for <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -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<TResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(
this ResilienceStrategyBuilderBase builder,
HedgingStrategyOptions<TResult> options)
Expand Down
9 changes: 1 addition & 8 deletions src/Polly.Core/ResilienceStrategyBuilderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ public abstract class ResilienceStrategyBuilderBase
{
private readonly List<Entry> _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<ResilienceValidationContext> _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()
{
Expand Down Expand Up @@ -121,11 +118,7 @@ private protected ResilienceStrategyBuilderBase(ResilienceStrategyBuilderBase ot
/// </remarks>
/// <exception cref="ArgumentNullException">Thrown when the attempting to assign <see langword="null"/> to this property.</exception>
[EditorBrowsable(EditorBrowsableState.Never)]
public Action<ResilienceValidationContext> Validator
{
get => _validator;
internal set => _validator = Guard.NotNull(value);
}
public Action<ResilienceValidationContext> Validator { get; private protected set; } = ValidationHelper.ValidateObject;

internal abstract bool IsGenericBuilder { get; }

Expand Down
6 changes: 4 additions & 2 deletions src/Polly.Core/ResilienceStrategyBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ public static class ResilienceStrategyBuilderExtensions
/// <returns>The same builder instance.</returns>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="strategy"/> is null.</exception>
/// <exception cref="InvalidOperationException">Thrown when this builder was already used to create a strategy. The builder cannot be modified after it has been used.</exception>
[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<TBuilder>(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
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// Retry extension methods for the <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -46,6 +44,10 @@ public static ResilienceStrategyBuilder<TResult> AddRetry<TResult>(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<TResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TOptions>(
this ResilienceStrategyBuilderBase builder,
RetryStrategyOptions<TResult> options)
Expand Down
26 changes: 18 additions & 8 deletions src/Polly.Core/Telemetry/ResilienceStrategyTelemetry.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;

namespace Polly.Telemetry;

/// <summary>
Expand Down Expand Up @@ -31,6 +33,14 @@ internal ResilienceStrategyTelemetry(ResilienceTelemetrySource source, Diagnosti
/// <param name="context">The resilience context associated with this event.</param>
/// <param name="args">The event arguments.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="context"/> is <see langword="null"/>.</exception>
[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<TArgs>(ResilienceEvent resilienceEvent, ResilienceContext context, TArgs args)
{
Guard.NotNull(context);
Expand All @@ -44,11 +54,7 @@ public void Report<TArgs>(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);
}
Expand All @@ -60,6 +66,14 @@ public void Report<TArgs>(ResilienceEvent resilienceEvent, ResilienceContext con
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="resilienceEvent">The reported resilience event.</param>
/// <param name="args">The event arguments.</param>
[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<TArgs, TResult>(ResilienceEvent resilienceEvent, OutcomeArguments<TResult, TArgs> args)
{
args.Context.AddResilienceEvent(resilienceEvent);
Expand All @@ -71,11 +85,7 @@ public void Report<TArgs, TResult>(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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// Extension methods for adding timeouts to a <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -41,6 +39,10 @@ public static TBuilder AddTimeout<TBuilder>(this TBuilder builder, TimeSpan time
/// <exception cref="ArgumentNullException">Thrown when <paramref name="builder"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
/// <exception cref="ValidationException">Thrown when <paramref name="options"/> are invalid.</exception>
[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<TBuilder>(this TBuilder builder, TimeoutStrategyOptions options)
where TBuilder : ResilienceStrategyBuilderBase
{
Expand Down
6 changes: 4 additions & 2 deletions src/Polly.Core/Utils/ValidationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ValidationResult>();

#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);
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

/// <summary>
/// The rate limiter extensions for <see cref="ResilienceStrategyBuilder"/>.
/// </summary>
Expand Down Expand Up @@ -96,6 +94,10 @@ public static TBuilder AddRateLimiter<TBuilder>(
/// <exception cref="ValidationException">Thrown when <paramref name="options"/> are invalid.</exception>
/// <exception cref="ArgumentException">Thrown when <see cref="RateLimiterStrategyOptions.DefaultRateLimiterOptions"/> for <paramref name="options"/> are invalid.</exception>
[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<TBuilder>(
this TBuilder builder,
RateLimiterStrategyOptions options)
Expand Down
9 changes: 0 additions & 9 deletions test/Polly.Core.Tests/ResilienceStrategyBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ArgumentNullException>();
}

[Fact]
public void AddStrategy_MultipleNonDelegating_Ok()
{
Expand Down

0 comments on commit d111b6b

Please sign in to comment.