33
44namespace StyleCop . Analyzers . ReadabilityRules
55{
6+ using System ;
67 using System . Collections . Generic ;
78 using System . Collections . Immutable ;
89 using System . Composition ;
@@ -24,6 +25,8 @@ namespace StyleCop.Analyzers.ReadabilityRules
2425 [ Shared ]
2526 internal class SA1130CodeFixProvider : CodeFixProvider
2627 {
28+ private static readonly SyntaxToken ParameterListSeparator = SyntaxFactory . Token ( SyntaxKind . CommaToken ) . WithTrailingTrivia ( SyntaxFactory . Space ) ;
29+
2730 /// <inheritdoc/>
2831 public override ImmutableArray < string > FixableDiagnosticIds { get ; } =
2932 ImmutableArray . Create ( SA1130UseLambdaSyntax . DiagnosticId ) ;
@@ -50,14 +53,38 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
5053 return SpecializedTasks . CompletedTask ;
5154 }
5255
53- private static SyntaxNode ReplaceWithLambda ( AnonymousMethodExpressionSyntax anonymousMethod )
56+ private static SyntaxNode ReplaceWithLambda ( SemanticModel semanticModel , AnonymousMethodExpressionSyntax anonymousMethod )
5457 {
5558 var parameterList = anonymousMethod . ParameterList ;
5659 SyntaxNode lambdaExpression ;
5760
5861 if ( parameterList == null )
5962 {
60- parameterList = SyntaxFactory . ParameterList ( )
63+ ImmutableArray < string > argumentList = default ( ImmutableArray < string > ) ;
64+
65+ switch ( anonymousMethod . Parent . Kind ( ) )
66+ {
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 ;
79+ }
80+
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+
87+ parameterList = SyntaxFactory . ParameterList ( newList )
6188 . WithLeadingTrivia ( anonymousMethod . DelegateKeyword . LeadingTrivia )
6289 . WithTrailingTrivia ( anonymousMethod . DelegateKeyword . TrailingTrivia ) ;
6390 }
@@ -106,15 +133,74 @@ private static SyntaxNode ReplaceWithLambda(AnonymousMethodExpressionSyntax anon
106133 . WithAdditionalAnnotations ( Formatter . Annotation ) ;
107134 }
108135
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 )
169+ {
170+ var parameters = new List < ParameterSyntax > ( ) ;
171+
172+ foreach ( var argumentName in argumentNames )
173+ {
174+ var baseName = argumentName ;
175+ var newName = baseName ;
176+ var index = 0 ;
177+
178+ while ( semanticModel . LookupSymbols ( anonymousMethod . SpanStart , name : newName ) . Length > 0 )
179+ {
180+ index ++ ;
181+ newName = baseName + index ;
182+ }
183+
184+ parameters . Add ( SyntaxFactory . Parameter ( SyntaxFactory . Identifier ( newName ) ) . WithType ( null ) ) ;
185+ }
186+
187+ return parameters ;
188+ }
189+
109190 private static ParameterListSyntax RemoveType ( ParameterListSyntax parameterList )
110191 {
111192 return parameterList . WithParameters ( SyntaxFactory . SeparatedList ( parameterList . Parameters . Select ( x => RemoveType ( x ) ) , parameterList . Parameters . GetSeparators ( ) ) ) ;
112193 }
113194
114195 private static ParameterSyntax RemoveType ( ParameterSyntax parameterSyntax )
115196 {
116- var syntax = parameterSyntax . WithType ( null )
117- . WithLeadingTrivia ( parameterSyntax . Type . GetLeadingTrivia ( ) . Concat ( parameterSyntax . Type . GetTrailingTrivia ( ) ) ) ;
197+ var syntax = parameterSyntax . WithType ( null ) ;
198+
199+ if ( parameterSyntax . Type != null )
200+ {
201+ syntax = syntax . WithLeadingTrivia ( parameterSyntax . Type . GetLeadingTrivia ( ) . Concat ( parameterSyntax . Type . GetTrailingTrivia ( ) ) ) ;
202+ }
203+
118204 return syntax . WithTrailingTrivia ( syntax . GetTrailingTrivia ( ) . WithoutTrailingWhitespace ( ) )
119205 . WithLeadingTrivia ( syntax . GetLeadingTrivia ( ) . WithoutWhitespace ( ) ) ;
120206 }
@@ -131,10 +217,11 @@ private static bool IsValid(ParameterSyntax parameterSyntax)
131217 private static async Task < Document > GetTransformedDocumentAsync ( Document document , Diagnostic diagnostic , CancellationToken cancellationToken )
132218 {
133219 var syntaxRoot = await document . GetSyntaxRootAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
220+ var semanticModel = await document . GetSemanticModelAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
134221
135222 var anonymousMethod = ( AnonymousMethodExpressionSyntax ) syntaxRoot . FindNode ( diagnostic . Location . SourceSpan , getInnermostNodeForTie : true ) ;
136223
137- var newSyntaxRoot = syntaxRoot . ReplaceNode ( anonymousMethod , ReplaceWithLambda ( anonymousMethod ) ) ;
224+ var newSyntaxRoot = syntaxRoot . ReplaceNode ( anonymousMethod , ReplaceWithLambda ( semanticModel , anonymousMethod ) ) ;
138225 var newDocument = document . WithSyntaxRoot ( newSyntaxRoot . WithoutFormatting ( ) ) ;
139226
140227 return newDocument ;
@@ -150,17 +237,17 @@ private class FixAll : DocumentBasedFixAllProvider
150237 protected override async Task < SyntaxNode > FixAllInDocumentAsync ( FixAllContext fixAllContext , Document document , ImmutableArray < Diagnostic > diagnostics )
151238 {
152239 var syntaxRoot = await document . GetSyntaxRootAsync ( fixAllContext . CancellationToken ) . ConfigureAwait ( false ) ;
240+ var semanticModel = await document . GetSemanticModelAsync ( fixAllContext . CancellationToken ) . ConfigureAwait ( false ) ;
153241
154242 var nodes = new List < AnonymousMethodExpressionSyntax > ( ) ;
155243
156244 foreach ( var diagnostic in diagnostics )
157245 {
158246 var node = ( AnonymousMethodExpressionSyntax ) syntaxRoot . FindNode ( diagnostic . Location . SourceSpan , getInnermostNodeForTie : true ) ;
159-
160247 nodes . Add ( node ) ;
161248 }
162249
163- return syntaxRoot . ReplaceNodes ( nodes , ( originalNode , rewrittenNode ) => ReplaceWithLambda ( rewrittenNode ) ) ;
250+ return syntaxRoot . ReplaceNodes ( nodes , ( originalNode , rewrittenNode ) => ReplaceWithLambda ( semanticModel , rewrittenNode ) ) ;
164251 }
165252 }
166253 }
0 commit comments