Skip to content

Commit 26e3c69

Browse files
authored
Merge pull request #3516 from bjornhellander/feature/sa1130-delegate-cast
Correct code fix for SA1130 when delegate expression is part of a cast expression
2 parents ec1112e + 6a1c3ef commit 26e3c69

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/ReadabilityRules/SA1130CodeFixProvider.cs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private static async Task<bool> CanFixAsync(CodeFixContext context, Diagnostic d
7272
private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
7373
{
7474
var parameterList = anonymousMethod.ParameterList;
75-
SyntaxNode lambdaExpression;
75+
ExpressionSyntax lambdaExpression;
7676
SyntaxToken arrowToken;
7777

7878
if (parameterList == null)
@@ -91,16 +91,17 @@ private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, Anonymo
9191

9292
case SyntaxKind.AddAssignmentExpression:
9393
case SyntaxKind.SubtractAssignmentExpression:
94-
var list = GetAssignmentArgumentList(semanticModel, anonymousMethod);
95-
96-
if (list == null)
9794
{
98-
return null;
95+
var list = GetAssignmentArgumentList(semanticModel, anonymousMethod);
96+
if (list == null)
97+
{
98+
return null;
99+
}
100+
101+
argumentList = list.Value;
102+
break;
99103
}
100104

101-
argumentList = list.Value;
102-
break;
103-
104105
case SyntaxKind.ArrowExpressionClause:
105106
case SyntaxKind.ReturnStatement:
106107
argumentList = GetMemberReturnTypeArgumentList(semanticModel, anonymousMethod);
@@ -110,6 +111,18 @@ private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, Anonymo
110111
}
111112

112113
break;
114+
115+
case SyntaxKind.CastExpression:
116+
{
117+
var list = GetCastTypeArgumentList(semanticModel, anonymousMethod);
118+
if (list == null)
119+
{
120+
return null;
121+
}
122+
123+
argumentList = list.Value;
124+
break;
125+
}
113126
}
114127

115128
List<ParameterSyntax> parameters = GenerateUniqueParameterNames(semanticModel, anonymousMethod, argumentList);
@@ -165,6 +178,13 @@ private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, Anonymo
165178
lambdaExpression = SyntaxFactory.ParenthesizedLambdaExpression(anonymousMethod.AsyncKeyword, parameterListSyntax, arrowToken, anonymousMethod.Body);
166179
}
167180

181+
if (anonymousMethod.Parent.IsKind(SyntaxKind.CastExpression))
182+
{
183+
// In this case, the lambda needs enclosing parenthesis to be syntactically correct
184+
lambdaExpression = SyntaxFactory.ParenthesizedExpression(lambdaExpression);
185+
}
186+
187+
// TODO: No tests require this annotation. Can it be removed?
168188
return lambdaExpression
169189
.WithAdditionalAnnotations(Formatter.Annotation);
170190
}
@@ -213,6 +233,21 @@ private static ImmutableArray<string> GetMemberReturnTypeArgumentList(SemanticMo
213233
return !(((IMethodSymbol)enclosingSymbol).ReturnType is INamedTypeSymbol returnType) ? ImmutableArray<string>.Empty : returnType.DelegateInvokeMethod.Parameters.Select(ps => ps.Name).ToImmutableArray();
214234
}
215235

236+
private static ImmutableArray<string>? GetCastTypeArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
237+
{
238+
var castExpression = (CastExpressionSyntax)anonymousMethod.Parent;
239+
240+
var symbol = semanticModel.GetSymbolInfo(castExpression.Type);
241+
var namedTypeSymbol = symbol.Symbol as INamedTypeSymbol;
242+
var parameters = namedTypeSymbol?.DelegateInvokeMethod?.Parameters;
243+
if (parameters == null)
244+
{
245+
return null;
246+
}
247+
248+
return parameters.Value.Select(ps => ps.Name).ToImmutableArray();
249+
}
250+
216251
private static List<ParameterSyntax> GenerateUniqueParameterNames(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod, ImmutableArray<string> argumentNames)
217252
{
218253
var parameters = new List<ParameterSyntax>();
@@ -306,7 +341,7 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
306341
return rewrittenNode;
307342
}
308343

309-
return newNode;
344+
return newNode.WithoutFormatting();
310345
});
311346
}
312347
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1130UnitTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -963,5 +963,52 @@ private void Test2(string description = null, Func<object, string> resolve = nul
963963

964964
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
965965
}
966+
967+
[Theory]
968+
[InlineData(
969+
"(Func<int>)[|delegate|] { return 1; }",
970+
"(Func<int>)(() => { return 1; })")]
971+
[InlineData(
972+
"(Func<int>)[|delegate|]() { return 1; }",
973+
"(Func<int>)(() => { return 1; })")]
974+
[InlineData(
975+
"(Func<int, int>)[|delegate|] { return 1; }",
976+
"(Func<int, int>)(arg => { return 1; })")]
977+
[InlineData(
978+
"(Func<int, int>)[|delegate|](int x) { return 1; }",
979+
"(Func<int, int>)(x => { return 1; })")]
980+
[InlineData(
981+
"(Func<int, int, int>)[|delegate|] { return 1; }",
982+
"(Func<int, int, int>)((arg1, arg2) => { return 1; })")]
983+
[InlineData(
984+
"(Func<int, int, int>)[|delegate|](int x, int y) { return 1; }",
985+
"(Func<int, int, int>)((x, y) => { return 1; })")]
986+
[WorkItem(3510, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3510")]
987+
public async Task TestDelegateUsedInCastAsync(string testExpression, string fixedExpression)
988+
{
989+
var testCode = $@"
990+
using System;
991+
992+
public class TypeName
993+
{{
994+
public void Test()
995+
{{
996+
var z = {testExpression};
997+
}}
998+
}}";
999+
1000+
var fixedCode = $@"
1001+
using System;
1002+
1003+
public class TypeName
1004+
{{
1005+
public void Test()
1006+
{{
1007+
var z = {fixedExpression};
1008+
}}
1009+
}}";
1010+
1011+
await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedCode, CancellationToken.None).ConfigureAwait(false);
1012+
}
9661013
}
9671014
}

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1130UseLambdaSyntax.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ private static bool HandleMethodInvocation(SemanticModel semanticModel, Anonymou
168168

169169
if (parameterList == null)
170170
{
171-
// This might happen if the call was using params witha type unknown to the analyzer, e.g. params Span<T>.
171+
// This might happen if the call was using params with a type unknown to the analyzer, e.g. params Span<T>.
172172
return false;
173173
}
174174

0 commit comments

Comments
 (0)