Skip to content

Commit 8fdf917

Browse files
committed
don't crash on incomplete syntax
1 parent ad78b56 commit 8fdf917

4 files changed

Lines changed: 71 additions & 14 deletions

File tree

src/Tocsoft.DateTimeAbstractions.Analyzer/DateTimeOffsetUsageCodeFixProvider.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,18 @@ public sealed override FixAllProvider GetFixAllProvider()
3434

3535
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
3636
{
37-
Diagnostic diagnostic = context.Diagnostics.First();
37+
Diagnostic diagnostic = context.Diagnostics.Where(x => x.Id == DateTimeOffsetUsageAnalyzer.DiagnosticId).FirstOrDefault();
3838

39-
// Register a code action that will invoke the fix.
40-
context.RegisterCodeFix(
39+
if (diagnostic != null)
40+
{
41+
// Register a code action that will invoke the fix.
42+
context.RegisterCodeFix(
4143
CodeAction.Create(
4244
title: Title,
4345
createChangedDocument: c => this.ReplaceWithCallToClock(context, c),
4446
equivalenceKey: Title),
4547
diagnostic);
48+
}
4649

4750
return Task.CompletedTask;
4851
}
@@ -64,12 +67,16 @@ private static async Task<SyntaxNode> ReplaceMemberCall(CodeFixContext context,
6467
{
6568
SemanticModel model = await context.Document.GetSemanticModelAsync(context.CancellationToken);
6669

67-
Diagnostic diagnostic = context.Diagnostics.First();
70+
Diagnostic diagnostic = context.Diagnostics.Where(x => x.Id == DateTimeUsageAnalyzer.DiagnosticId).FirstOrDefault();
6871
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
6972

70-
MemberAccessExpressionSyntax memberAccess = root.FindNode(diagnostic.Location.SourceSpan) as MemberAccessExpressionSyntax;
73+
SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan);
74+
MemberAccessExpressionSyntax memberAccess = node.DescendantNodesAndSelf(x => !(x is MemberAccessExpressionSyntax))
75+
.OfType<MemberAccessExpressionSyntax>()
76+
.First();
7177

7278
string propertyName = memberAccess.Name.ToString();
79+
SyntaxTriviaList trivia = memberAccess.GetTrailingTrivia();
7380

7481
MemberAccessExpressionSyntax expression = SyntaxFactory.MemberAccessExpression(
7582
SyntaxKind.SimpleMemberAccessExpression,
@@ -81,7 +88,7 @@ private static async Task<SyntaxNode> ReplaceMemberCall(CodeFixContext context,
8188
SyntaxFactory.IdentifierName("DateTimeAbstractions")),
8289
SyntaxFactory.IdentifierName("ClockOffset"))
8390
.WithAdditionalAnnotations(Simplifier.Annotation),
84-
SyntaxFactory.IdentifierName(propertyName));
91+
SyntaxFactory.IdentifierName(propertyName)).WithTrailingTrivia(trivia);
8592

8693
root = root.ReplaceNode(memberAccess, expression);
8794
return root;

src/Tocsoft.DateTimeAbstractions.Analyzer/DateTimeUsageCodeFixProvider.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,18 @@ public sealed override FixAllProvider GetFixAllProvider()
3434

3535
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
3636
{
37-
Diagnostic diagnostic = context.Diagnostics.First();
37+
Diagnostic diagnostic = context.Diagnostics.Where(x => x.Id == DateTimeUsageAnalyzer.DiagnosticId).FirstOrDefault();
3838

39-
// Register a code action that will invoke the fix.
40-
context.RegisterCodeFix(
39+
if (diagnostic != null)
40+
{
41+
// Register a code action that will invoke the fix.
42+
context.RegisterCodeFix(
4143
CodeAction.Create(
4244
title: Title,
4345
createChangedDocument: c => this.ReplaceWithCallToClock(context, c),
4446
equivalenceKey: Title),
4547
diagnostic);
48+
}
4649

4750
return Task.CompletedTask;
4851
}
@@ -64,12 +67,16 @@ private static async Task<SyntaxNode> ReplaceMemberCall(CodeFixContext context,
6467
{
6568
SemanticModel model = await context.Document.GetSemanticModelAsync(context.CancellationToken);
6669

67-
Diagnostic diagnostic = context.Diagnostics.First();
70+
Diagnostic diagnostic = context.Diagnostics.Where(x => x.Id == DateTimeUsageAnalyzer.DiagnosticId).FirstOrDefault();
6871
Microsoft.CodeAnalysis.Text.TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;
6972

70-
MemberAccessExpressionSyntax memberAccess = root.FindNode(diagnostic.Location.SourceSpan) as MemberAccessExpressionSyntax;
73+
SyntaxNode node = root.FindNode(diagnostic.Location.SourceSpan);
74+
MemberAccessExpressionSyntax memberAccess = node.DescendantNodesAndSelf(x => !(x is MemberAccessExpressionSyntax))
75+
.OfType<MemberAccessExpressionSyntax>()
76+
.First();
7177

7278
string propertyName = memberAccess.Name.ToString();
79+
SyntaxTriviaList trivia = memberAccess.GetTrailingTrivia();
7380

7481
MemberAccessExpressionSyntax expression = SyntaxFactory.MemberAccessExpression(
7582
SyntaxKind.SimpleMemberAccessExpression,
@@ -81,8 +88,8 @@ private static async Task<SyntaxNode> ReplaceMemberCall(CodeFixContext context,
8188
SyntaxFactory.IdentifierName("DateTimeAbstractions")),
8289
SyntaxFactory.IdentifierName("Clock"))
8390
.WithAdditionalAnnotations(Simplifier.Annotation),
84-
SyntaxFactory.IdentifierName(propertyName));
85-
91+
SyntaxFactory.IdentifierName(propertyName))
92+
.WithTrailingTrivia(trivia);
8693
root = root.ReplaceNode(memberAccess, expression);
8794
return root;
8895
}

src/Tocsoft.DateTimeAbstractions/Tocsoft.DateTimeAbstractions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<DebugType Condition="$(codecov) == ''">portable</DebugType>
99
<DebugSymbols>True</DebugSymbols>
1010
<VersionPrefix Condition="$(packageversion) != ''">$(packageversion)</VersionPrefix>
11-
<VersionPrefix Condition="$(packageversion) == ''">0.0.1</VersionPrefix>
11+
<VersionPrefix Condition="$(packageversion) == ''">0.0.2</VersionPrefix>
1212
</PropertyGroup>
1313

1414
<ItemGroup>

tests/Tocsoft.DateTimeAbstractions.Tests/DateTimeAnalyzer.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,49 @@ public TypeName(){
121121
this.VerifyCSharpFix(test, fixtest);
122122
}
123123

124+
[Fact]
125+
public void IncompleteCodeCanBeFixed()
126+
{
127+
string test = @"
128+
using System;
129+
namespace TestApplication
130+
{
131+
class TypeName
132+
{
133+
DateTime prop;
134+
public TypeName(){
135+
DateTime.Now
136+
}
137+
}
138+
}";
139+
this.AdditionalCodeFiles = new[]
140+
{
141+
@"
142+
using System;
143+
144+
namespace Tocsoft.DateTimeAbstractions
145+
{
146+
public static class Clock { public static DateTime Now { get; set; } }
147+
}"
148+
};
149+
150+
string fixtest = @"
151+
using System;
152+
using Tocsoft.DateTimeAbstractions;
153+
154+
namespace TestApplication
155+
{
156+
class TypeName
157+
{
158+
DateTime prop;
159+
public TypeName(){
160+
Clock.Now
161+
}
162+
}
163+
}";
164+
this.VerifyCSharpFix(test, fixtest);
165+
}
166+
124167
[Fact]
125168
public void DateTimeUtcNowMappsToClockUtcNow()
126169
{

0 commit comments

Comments
 (0)