Skip to content

Commit b960955

Browse files
committed
Fix that the SA1130 code fix crashes on an invalid event symbol
1 parent 16aeec9 commit b960955

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

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

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,34 @@ public override FixAllProvider GetFixAllProvider()
3838
}
3939

4040
/// <inheritdoc/>
41-
public override Task RegisterCodeFixesAsync(CodeFixContext context)
41+
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
4242
{
4343
foreach (var diagnostic in context.Diagnostics)
4444
{
45+
if (!(await CanFixAsync(context, diagnostic).ConfigureAwait(false)))
46+
{
47+
continue;
48+
}
49+
4550
context.RegisterCodeFix(
4651
CodeAction.Create(
4752
ReadabilityResources.SA1130CodeFix,
4853
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, cancellationToken),
4954
nameof(SA1134CodeFixProvider)),
5055
diagnostic);
5156
}
57+
}
58+
59+
private static async Task<bool> CanFixAsync(CodeFixContext context, Diagnostic diagnostic)
60+
{
61+
var syntaxRoot = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
62+
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
63+
64+
var anonymousMethod = (AnonymousMethodExpressionSyntax)syntaxRoot.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
5265

53-
return SpecializedTasks.CompletedTask;
66+
var newNode = ReplaceWithLambda(semanticModel, anonymousMethod);
67+
68+
return newNode != null;
5469
}
5570

5671
private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
@@ -74,7 +89,14 @@ private static SyntaxNode ReplaceWithLambda(SemanticModel semanticModel, Anonymo
7489

7590
case SyntaxKind.AddAssignmentExpression:
7691
case SyntaxKind.SubtractAssignmentExpression:
77-
argumentList = GetAssignmentArgumentList(semanticModel, anonymousMethod);
92+
var list = GetAssignmentArgumentList(semanticModel, anonymousMethod);
93+
94+
if (list == null)
95+
{
96+
return null;
97+
}
98+
99+
argumentList = list.Value;
78100
break;
79101
}
80102

@@ -155,11 +177,17 @@ private static ImmutableArray<string> GetEqualsArgumentList(SemanticModel semant
155177
return namedTypeSymbol.DelegateInvokeMethod.Parameters.Select(ps => ps.Name).ToImmutableArray();
156178
}
157179

158-
private static ImmutableArray<string> GetAssignmentArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
180+
private static ImmutableArray<string>? GetAssignmentArgumentList(SemanticModel semanticModel, AnonymousMethodExpressionSyntax anonymousMethod)
159181
{
160182
var assignmentExpressionSyntax = (AssignmentExpressionSyntax)anonymousMethod.Parent;
161183

162184
var symbol = semanticModel.GetSymbolInfo(assignmentExpressionSyntax.Left);
185+
186+
if (symbol.Symbol == null)
187+
{
188+
return null;
189+
}
190+
163191
var eventSymbol = (IEventSymbol)symbol.Symbol;
164192
var namedTypeSymbol = (INamedTypeSymbol)eventSymbol.Type;
165193
return namedTypeSymbol.DelegateInvokeMethod.Parameters.Select(ps => ps.Name).ToImmutableArray();
@@ -221,7 +249,9 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
221249

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

224-
var newSyntaxRoot = syntaxRoot.ReplaceNode(anonymousMethod, ReplaceWithLambda(semanticModel, anonymousMethod));
252+
var newNode = ReplaceWithLambda(semanticModel, anonymousMethod);
253+
254+
var newSyntaxRoot = syntaxRoot.ReplaceNode(anonymousMethod, newNode);
225255
var newDocument = document.WithSyntaxRoot(newSyntaxRoot.WithoutFormatting());
226256

227257
return newDocument;
@@ -247,7 +277,17 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
247277
nodes.Add(node);
248278
}
249279

250-
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) => ReplaceWithLambda(semanticModel, rewrittenNode));
280+
return syntaxRoot.ReplaceNodes(nodes, (originalNode, rewrittenNode) =>
281+
{
282+
var newNode = ReplaceWithLambda(semanticModel, rewrittenNode);
283+
284+
if (newNode == null)
285+
{
286+
return rewrittenNode;
287+
}
288+
289+
return newNode;
290+
});
251291
}
252292
}
253293
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,5 +683,32 @@ public unsafe class TypeName
683683
var expected = DiagnosticResult.CompilerError("CS1660").WithLocation(7, 29);
684684
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
685685
}
686+
687+
[Fact]
688+
public async Task VerifyThatCodeFixDoesNotCrashOnMissingEventSymbolAsync()
689+
{
690+
var testCode = @"
691+
using System;
692+
693+
namespace StyleCopDemo
694+
{
695+
class Program
696+
{
697+
static void Main(string[] args)
698+
{
699+
TestEvent -= delegate { Console.WriteLine(""hello""); };
700+
}
701+
}
702+
}
703+
";
704+
705+
DiagnosticResult[] expected =
706+
{
707+
Diagnostic().WithLocation(10, 26),
708+
DiagnosticResult.CompilerError("CS0103").WithLocation(10, 13),
709+
};
710+
711+
await VerifyCSharpFixAsync(testCode, expected, testCode, CancellationToken.None).ConfigureAwait(false);
712+
}
686713
}
687714
}

0 commit comments

Comments
 (0)