Skip to content

Commit 1509a5a

Browse files
committed
Moved parameter list determinitation to the codefix
1 parent 9f833fb commit 1509a5a

2 files changed

Lines changed: 77 additions & 69 deletions

File tree

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

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,37 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
5353
return SpecializedTasks.CompletedTask;
5454
}
5555

56-
private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod, string argumentsProperty)
56+
private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
5757
{
5858
var parameterList = anonymousMethod.ParameterList;
5959
SyntaxNode lambdaExpression;
6060

6161
if (parameterList == null)
6262
{
63-
SeparatedSyntaxList<ParameterSyntax> newList = default(SeparatedSyntaxList<ParameterSyntax>);
63+
ImmutableArray<string> argumentList = default(ImmutableArray<string>);
6464

65-
if (!string.IsNullOrEmpty(argumentsProperty))
65+
switch (anonymousMethod.Parent.Kind())
6666
{
67-
var argumentNames = argumentsProperty.Split(',');
68-
List<ParameterSyntax> parameters = GenerateUniqueParameterNames(semanticModel, anonymousMethod, argumentNames);
69-
70-
newList = SyntaxFactory.SeparatedList(parameters, Enumerable.Repeat(ParameterListSeparator, parameters.Count - 1));
67+
case SyntaxKind.Argument:
68+
argumentList = GetMethodInvocationArgumentList(semanticModel, anonymousMethod);
69+
break;
70+
71+
case SyntaxKind.EqualsValueClause:
72+
argumentList = GetEqualsArgumentList(semanticModel, anonymousMethod);
73+
break;
74+
75+
case SyntaxKind.AddAssignmentExpression:
76+
case SyntaxKind.SubtractAssignmentExpression:
77+
argumentList = GetAssignmentArgumentList(semanticModel, anonymousMethod);
78+
break;
7179
}
7280

81+
List<ParameterSyntax> parameters = GenerateUniqueParameterNames(semanticModel, anonymousMethod, argumentList);
82+
83+
var newList = (parameters.Count > 0)
84+
? SyntaxFactory.SeparatedList(parameters, Enumerable.Repeat(ParameterListSeparator, parameters.Count - 1))
85+
: SyntaxFactory.SeparatedList<ParameterSyntax>();
86+
7387
parameterList = SyntaxFactory.ParameterList(newList)
7488
.WithLeadingTrivia(anonymousMethod.DelegateKeyword.LeadingTrivia)
7589
.WithTrailingTrivia(anonymousMethod.DelegateKeyword.TrailingTrivia);
@@ -119,7 +133,39 @@ private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, Anonymo
119133
.WithAdditionalAnnotations(Formatter.Annotation);
120134
}
121135

122-
private static List<ParameterSyntax> GenerateUniqueParameterNames(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod, string[] argumentNames)
136+
private static ImmutableArray<string> GetMethodInvocationArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
137+
{
138+
var argumentSyntax = (ArgumentSyntax)anonymousMethod.Parent;
139+
var argumentListSyntax = (ArgumentListSyntax)argumentSyntax.Parent;
140+
var originalInvocationExpression = (InvocationExpressionSyntax)argumentListSyntax.Parent;
141+
142+
var originalSymbolInfo = semanticModel.GetSymbolInfo(originalInvocationExpression);
143+
var argumentIndex = argumentListSyntax.Arguments.IndexOf(argumentSyntax);
144+
var parameterList = SA1130UseLambdaSyntax.GetDelegateParameterList((IMethodSymbol)originalSymbolInfo.Symbol, argumentIndex);
145+
return parameterList.Parameters.Select(p => p.Identifier.ToString()).ToImmutableArray();
146+
}
147+
148+
private static ImmutableArray<string> GetEqualsArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
149+
{
150+
var equalsValueClauseSyntax = (EqualsValueClauseSyntax)anonymousMethod.Parent;
151+
var variableDeclaration = (VariableDeclarationSyntax)equalsValueClauseSyntax.Parent.Parent;
152+
153+
var symbol = semanticModel.GetSymbolInfo(variableDeclaration.Type);
154+
var namedTypeSymbol = (INamedTypeSymbol)symbol.Symbol;
155+
return namedTypeSymbol.DelegateInvokeMethod.Parameters.Select(ps => ps.Name).ToImmutableArray();
156+
}
157+
158+
private static ImmutableArray<string> GetAssignmentArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
159+
{
160+
var assignmentExpressionSyntax = (AssignmentExpressionSyntax)anonymousMethod.Parent;
161+
162+
var symbol = semanticModel.GetSymbolInfo(assignmentExpressionSyntax.Left);
163+
var eventSymbol = (IEventSymbol)symbol.Symbol;
164+
var namedTypeSymbol = (INamedTypeSymbol)eventSymbol.Type;
165+
return namedTypeSymbol.DelegateInvokeMethod.Parameters.Select(ps => ps.Name).ToImmutableArray();
166+
}
167+
168+
private static List<ParameterSyntax> GenerateUniqueParameterNames(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod, ImmutableArray<string> argumentNames)
123169
{
124170
var parameters = new List<ParameterSyntax>();
125171

@@ -175,8 +221,7 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
175221

176222
var anonymousMethod = (AnonymousMethodExpressionSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
177223

178-
var argumentsProperty = diagnostic.Properties.ContainsKey(SA1130UseLambdaSyntax.DelegateArgumentNamesProperty) ? diagnostic.Properties[SA1130UseLambdaSyntax.DelegateArgumentNamesProperty] : string.Empty;
179-
var newSyntaxRoot = syntaxRoot.ReplaceNode(anonymousMethod, ReplaceWithLambda(semanticModel, anonymousMethod, argumentsProperty));
224+
var newSyntaxRoot = syntaxRoot.ReplaceNode(anonymousMethod, ReplaceWithLambda(semanticModel, anonymousMethod));
180225
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
181226

182227
return newDocument;
@@ -195,18 +240,14 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
195240
var semanticModel = await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
196241

197242
var nodes = new List<AnonymousMethodExpressionSyntax>();
198-
var nodeProperties = new Dictionary<AnonymousMethodExpressionSyntax, string>();
199243

200244
foreach (var diagnostic in diagnostics)
201245
{
202-
var argumentsProperty = diagnostic.Properties.ContainsKey(SA1130UseLambdaSyntax.DelegateArgumentNamesProperty) ? diagnostic.Properties[SA1130UseLambdaSyntax.DelegateArgumentNamesProperty] : string.Empty;
203246
var node = (AnonymousMethodExpressionSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
204-
205247
nodes.Add(node);
206-
nodeProperties.Add(node, argumentsProperty);
207248
}
208249

209-
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => ReplaceWithLambda(semanticModel, rewrittenNode, nodeProperties[rewrittenNode]));
250+
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => ReplaceWithLambda(semanticModel, rewrittenNode));
210251
}
211252
}
212253
}

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

Lines changed: 21 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ internal class SA1130UseLambdaSyntax : DiagnosticAnalyzer
2323
/// </summary>
2424
public const string DiagnosticId = "SA1130";
2525

26-
/// <summary>
27-
/// Property identifier used to pass information to the codefix.
28-
/// </summary>
29-
internal const string DelegateArgumentNamesProperty = "DelegateArgumentNames";
30-
3126
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(ReadabilityResources.SA1130Title), ReadabilityResources.ResourceManager, typeof(ReadabilityResources));
3227
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(ReadabilityResources.SA1130MessageFormat), ReadabilityResources.ResourceManager, typeof(ReadabilityResources));
3328
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(ReadabilityResources.SA1130Description), ReadabilityResources.ResourceManager, typeof(ReadabilityResources));
@@ -52,6 +47,22 @@ public override void Initialize(AnalysisContext context)
5247
context.RegisterSyntaxNodeAction(AnonymousMethodExpressionAction, SyntaxKind.AnonymousMethodExpression);
5348
}
5449

50+
/// <summary>
51+
/// Gets the delegate parameter list from a method symbol and the argument index.
52+
/// </summary>
53+
/// <param name="methodSymbol">The symbol containing information about the method invocation.</param>
54+
/// <param name="argumentIndex">The index of the argument containing the delegate</param>
55+
/// <returns>A parameter list for the delegate parameters.</returns>
56+
internal static ParameterListSyntax GetDelegateParameterList(IMethodSymbol methodSymbol, int argumentIndex)
57+
{
58+
var delegateType = (INamedTypeSymbol)methodSymbol.Parameters[argumentIndex].Type;
59+
var delegateParameters = delegateType.DelegateInvokeMethod.Parameters;
60+
61+
var syntaxParameters = GetSyntaxParametersFromSymbolParameters(delegateParameters);
62+
63+
return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(syntaxParameters));
64+
}
65+
5566
private static void HandleAnonymousMethodExpression(SyntaxNodeAnalysisContext context)
5667
{
5768
var diagnosticProperties = ImmutableDictionary.CreateBuilder<string, string>();
@@ -66,12 +77,12 @@ private static void HandleAnonymousMethodExpression(SyntaxNodeAnalysisContext co
6677
break;
6778

6879
case SyntaxKind.EqualsValueClause:
69-
reportDiagnostic = HandleAssignment(context.SemanticModel, (EqualsValueClauseSyntax)anonymousMethod.Parent, diagnosticProperties);
80+
reportDiagnostic = true;
7081
break;
7182

7283
case SyntaxKind.AddAssignmentExpression:
7384
case SyntaxKind.SubtractAssignmentExpression:
74-
reportDiagnostic = HandleAssignmentExpression(context.SemanticModel, anonymousMethod, (AssignmentExpressionSyntax)anonymousMethod.Parent, diagnosticProperties);
85+
reportDiagnostic = true;
7586
break;
7687
}
7788

@@ -93,7 +104,9 @@ private static bool HandleMethodInvocation(SemanticModel semanticModel, Anonymou
93104
Location location = originalInvocationExpression.GetLocation();
94105

95106
var argumentIndex = argumentListSyntax.Arguments.IndexOf(argumentSyntax);
96-
var parameterList = GetDelegateParameterList(originalSymbolInfo, argumentIndex);
107+
108+
// Determine the parameter list from the method that is invoked, as delegates without parameters are allowed, but they cannot be replaced by a lambda without parameters.
109+
var parameterList = GetDelegateParameterList((IMethodSymbol)originalSymbolInfo.Symbol, argumentIndex);
97110

98111
// In some cases passing a delegate as an argument to a method is required to call the right overload
99112
// When there is an other overload that takes an expression.
@@ -110,57 +123,11 @@ private static bool HandleMethodInvocation(SemanticModel semanticModel, Anonymou
110123
{
111124
return false;
112125
}
113-
114-
var parameterNames = parameterList.Parameters.Select(p => p.Identifier.ToString());
115-
propertiesBuilder.Add(DelegateArgumentNamesProperty, string.Join(",", parameterNames));
116126
}
117127

118128
return true;
119129
}
120130

121-
private static bool HandleAssignment(SemanticModel semanticModel, EqualsValueClauseSyntax equalsValueClauseSyntax, ImmutableDictionary<string, string>.Builder propertiesBuilder)
122-
{
123-
var variableDeclaration = (VariableDeclarationSyntax)equalsValueClauseSyntax.Parent.Parent;
124-
var symbol = semanticModel.GetSymbolInfo(variableDeclaration.Type);
125-
126-
var namedTypeSymbol = symbol.Symbol as INamedTypeSymbol;
127-
if (namedTypeSymbol?.TypeKind == TypeKind.Delegate)
128-
{
129-
var delegateParameters = namedTypeSymbol.DelegateInvokeMethod.Parameters;
130-
propertiesBuilder.Add(DelegateArgumentNamesProperty, string.Join(",", delegateParameters.Select(ps => ps.Name)));
131-
return true;
132-
}
133-
134-
return false;
135-
}
136-
137-
private static bool HandleAssignmentExpression(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod, AssignmentExpressionSyntax assignmentExpressionSyntax, ImmutableDictionary<string, string>.Builder propertiesBuilder)
138-
{
139-
var symbol = semanticModel.GetSymbolInfo(assignmentExpressionSyntax.Left);
140-
141-
var eventSymbol = symbol.Symbol as IEventSymbol;
142-
if (eventSymbol?.Type.TypeKind == TypeKind.Delegate)
143-
{
144-
var delegateParameters = ((INamedTypeSymbol)eventSymbol.Type).DelegateInvokeMethod.Parameters;
145-
propertiesBuilder.Add(DelegateArgumentNamesProperty, string.Join(",", delegateParameters.Select(ps => ps.Name)));
146-
return true;
147-
}
148-
149-
return false;
150-
}
151-
152-
private static ParameterListSyntax GetDelegateParameterList(SymbolInfo originalSymbolInfo, int argumentIndex)
153-
{
154-
// Determine the parameter list from the method that is invoked, as delegates without parameters are allowed, but they cannot be replaced by a lambda without parameters.
155-
var methodSymbol = (IMethodSymbol)originalSymbolInfo.Symbol;
156-
var delegateType = (INamedTypeSymbol)methodSymbol.Parameters[argumentIndex].Type;
157-
var delegateParameters = delegateType.DelegateInvokeMethod.Parameters;
158-
159-
var syntaxParameters = GetSyntaxParametersFromSymbolParameters(delegateParameters);
160-
161-
return SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(syntaxParameters));
162-
}
163-
164131
private static ImmutableArray<ParameterSyntax> GetSyntaxParametersFromSymbolParameters(ImmutableArray<IParameterSymbol> symbolParameters)
165132
{
166133
var result = ImmutableArray.CreateBuilder<ParameterSyntax>(symbolParameters.Length);

0 commit comments

Comments
 (0)