Skip to content

Commit 61f03b7

Browse files
authored
Merge pull request #138 from bkoelman/av1553-caller-arg-expr
Suppress AV1553 on [CallerArgumentExpression] usage
2 parents a4c3fd3 + d4ca7db commit 61f03b7

File tree

3 files changed

+46
-5
lines changed

3 files changed

+46
-5
lines changed

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer.Test/Specs/Maintainability/DoNotUseOptionalParameterWithDefaultValueNullSpecs.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections;
2+
using System.Runtime.CompilerServices;
23
using CSharpGuidelinesAnalyzer.Rules.Maintainability;
34
using CSharpGuidelinesAnalyzer.Test.TestDataBuilders;
45
using Microsoft.CodeAnalysis.Diagnostics;
@@ -64,6 +65,24 @@ await VerifyGuidelineDiagnosticAsync(source,
6465
"Optional parameter 'p' of type 'string' has default value 'null'");
6566
}
6667

68+
[Fact]
69+
internal async Task When_using_optional_string_parameter_with_default_null_with_CallerArgumentExpression_it_must_be_skipped()
70+
{
71+
// Arrange
72+
ParsedSourceCode source = new TypeSourceCodeBuilder()
73+
.Using(typeof(CallerArgumentExpressionAttribute).Namespace)
74+
.InGlobalScope(@"
75+
class C
76+
{
77+
void M(object value, [CallerArgumentExpression(""value"")] string parameterName = null) => throw null;
78+
}
79+
")
80+
.Build();
81+
82+
// Act and assert
83+
await VerifyGuidelineDiagnosticAsync(source);
84+
}
85+
6786
[Fact]
6887
internal async Task When_using_optional_List_of_int_parameter_with_default_null_it_must_be_reported()
6988
{

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer/KnownTypes.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ public static INamedTypeSymbol SystemRuntimeCompilerServicesConfiguredValueTaskA
101101
return compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable");
102102
}
103103

104+
[CanBeNull]
105+
public static INamedTypeSymbol SystemRuntimeCompilerServicesCallerArgumentExpressionAttribute([NotNull] Compilation compilation)
106+
{
107+
Guard.NotNull(compilation, nameof(compilation));
108+
109+
return compilation.GetTypeByMetadataName("System.Runtime.CompilerServices.CallerArgumentExpressionAttribute");
110+
}
111+
104112
[CanBeNull]
105113
public static INamedTypeSymbol SystemRuntimeCompilerServicesConfiguredValueTaskAwaitableT([NotNull] Compilation compilation)
106114
{

src/CSharpGuidelinesAnalyzer/CSharpGuidelinesAnalyzer/Rules/Maintainability/DoNotUseOptionalParameterWithDefaultValueNullAnalyzer.cs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ public sealed class DoNotUseOptionalParameterWithDefaultValueNullAnalyzer : Diag
3131

3232
#pragma warning disable RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer.
3333
[NotNull]
34-
private static readonly Action<SyntaxNodeAnalysisContext, IList<INamedTypeSymbol>> AnalyzeParameterAction = (syntaxContext, taskTypes) =>
35-
syntaxContext.SkipEmptyName(symbolContext => AnalyzeParameter(symbolContext, taskTypes));
34+
private static readonly Action<SyntaxNodeAnalysisContext, IList<INamedTypeSymbol>, INamedTypeSymbol> AnalyzeParameterAction =
35+
(syntaxContext, taskTypes, callerArgumentExpressionAttributeType) => syntaxContext.SkipEmptyName(symbolContext =>
36+
AnalyzeParameter(symbolContext, taskTypes, callerArgumentExpressionAttributeType));
3637
#pragma warning restore RS1008 // Avoid storing per-compilation data into the fields of a diagnostic analyzer.
3738

3839
[ItemNotNull]
@@ -50,7 +51,11 @@ private static void RegisterCompilationStart([NotNull] CompilationStartAnalysisC
5051
{
5152
IList<INamedTypeSymbol> taskTypes = ResolveTaskTypes(startContext.Compilation).ToList();
5253

53-
startContext.RegisterSyntaxNodeAction(context => AnalyzeParameterAction(context, taskTypes), SyntaxKind.Parameter);
54+
INamedTypeSymbol callerArgumentExpressionAttributeType =
55+
KnownTypes.SystemRuntimeCompilerServicesCallerArgumentExpressionAttribute(startContext.Compilation);
56+
57+
startContext.RegisterSyntaxNodeAction(context => AnalyzeParameterAction(context, taskTypes, callerArgumentExpressionAttributeType),
58+
SyntaxKind.Parameter);
5459
}
5560

5661
[NotNull]
@@ -72,11 +77,13 @@ private static IEnumerable<INamedTypeSymbol> ResolveTaskTypes([NotNull] Compilat
7277
}
7378
}
7479

75-
private static void AnalyzeParameter(SymbolAnalysisContext context, [NotNull] [ItemNotNull] IList<INamedTypeSymbol> taskTypes)
80+
private static void AnalyzeParameter(SymbolAnalysisContext context, [NotNull] [ItemNotNull] IList<INamedTypeSymbol> taskTypes,
81+
[CanBeNull] INamedTypeSymbol callerArgumentExpressionAttributeType)
7682
{
7783
var parameter = (IParameterSymbol)context.Symbol;
7884

79-
if (parameter.IsOptional && parameter.HasExplicitDefaultValue && parameter.ExplicitDefaultValue == null)
85+
if (parameter.IsOptional && parameter.HasExplicitDefaultValue && parameter.ExplicitDefaultValue == null &&
86+
!HasCallerArgumentExpressionAttribute(parameter, callerArgumentExpressionAttributeType))
8087
{
8188
if (parameter.Type.IsOrImplementsIEnumerable() || IsTask(parameter.Type, taskTypes))
8289
{
@@ -90,6 +97,13 @@ private static void AnalyzeParameter(SymbolAnalysisContext context, [NotNull] [I
9097
}
9198
}
9299

100+
private static bool HasCallerArgumentExpressionAttribute([NotNull] IParameterSymbol parameter,
101+
[CanBeNull] INamedTypeSymbol callerArgumentExpressionAttributeType)
102+
{
103+
return callerArgumentExpressionAttributeType != null &&
104+
parameter.GetAttributes().Any(attr => Equals(attr.AttributeClass, callerArgumentExpressionAttributeType));
105+
}
106+
93107
private static bool IsTask([NotNull] ITypeSymbol type, [NotNull] [ItemNotNull] IList<INamedTypeSymbol> taskTypes)
94108
{
95109
ITypeSymbol unwrappedType = type.UnwrapNullableValueType();

0 commit comments

Comments
 (0)