Skip to content

Commit b50cdba

Browse files
authored
Features/cs0161 codefix (#17)
* wip. this branch fixes #3 * feature done
1 parent 34db99f commit b50cdba

File tree

5 files changed

+395
-0
lines changed

5 files changed

+395
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- CS0407
1616
- CS0123
1717
- CS1998
18+
- CS0161
1819

1920
## References
2021
- [Repository docs](https://github.com/taori/Amusoft.CodeAnalysis.Analyzers/tree/master/docs)
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
// Copyright 2020 Andreas Müller
2+
// This file is a part of Amusoft.CodeAnalysis.Analyzers and is licensed under Apache 2.0
3+
// See https://github.com/taori/Amusoft.CodeAnalysis.Analyzers/blob/master/LICENSE for details
4+
5+
using System;
6+
using System.Threading.Tasks;
7+
using Microsoft.CodeAnalysis.CSharp.Testing.MSTest;
8+
using Microsoft.CodeAnalysis.Testing;
9+
using Microsoft.VisualStudio.TestTools.UnitTesting;
10+
using static Microsoft.CodeAnalysis.Testing.DiagnosticResult;
11+
using Verifier = Microsoft.CodeAnalysis.CSharp.Testing.MSTest.CodeFixVerifier<Microsoft.CodeAnalysis.Testing.EmptyDiagnosticAnalyzer,
12+
Amusoft.CodeAnalysis.Analyzers.CS0161.FixByReplacingWithThrowExpression>;
13+
14+
namespace Amusoft.CodeAnalysis.Analyzers.Test.Tests.CS0161
15+
{
16+
[TestClass]
17+
public class FixByReplacingWithThrowExpressionTests
18+
{
19+
[TestMethod]
20+
public async Task EmptySourceNoAction()
21+
{
22+
await Verifier.VerifyCodeFixAsync(string.Empty, string.Empty);
23+
}
24+
25+
[TestMethod]
26+
public async Task RewriteElseBranch()
27+
{
28+
29+
var test = @"
30+
using System;
31+
using System.Collections.Generic;
32+
using System.Linq;
33+
using System.Text;
34+
using System.Threading.Tasks;
35+
using System.Diagnostics;
36+
37+
namespace ConsoleApplication1
38+
{
39+
public class Test
40+
{
41+
public static int Main() // CS0161
42+
{
43+
int i = 5;
44+
if (i < 10)
45+
{
46+
return i;
47+
}
48+
else
49+
{
50+
// Uncomment the following line to resolve.
51+
// return 1;
52+
}
53+
}
54+
}
55+
}";
56+
57+
58+
var fixtest = @"
59+
using System;
60+
using System.Collections.Generic;
61+
using System.Linq;
62+
using System.Text;
63+
using System.Threading.Tasks;
64+
using System.Diagnostics;
65+
66+
namespace ConsoleApplication1
67+
{
68+
public class Test
69+
{
70+
public static int Main() // CS0161
71+
{
72+
int i = 5;
73+
if (i < 10)
74+
{
75+
return i;
76+
}
77+
else
78+
{
79+
throw new NotImplementedException();
80+
// Uncomment the following line to resolve.
81+
// return 1;
82+
}
83+
}
84+
}
85+
}";
86+
var diagnostics = new DiagnosticResult[]
87+
{
88+
// Test0.cs(13,27): error CS0161: 'Test.Main()': not all code paths return a value
89+
DiagnosticResult.CompilerError("CS0161").WithSpan(13, 27, 13, 31).WithArguments("ConsoleApplication1.Test.Main()")
90+
};
91+
92+
await Verifier.VerifyCodeFixAsync(test, diagnostics, fixtest);
93+
}
94+
95+
[TestMethod]
96+
public async Task RewriteIfBranch()
97+
{
98+
var test = @"
99+
using System;
100+
using System.Collections.Generic;
101+
using System.Linq;
102+
using System.Text;
103+
using System.Threading.Tasks;
104+
using System.Diagnostics;
105+
106+
namespace ConsoleApplication1
107+
{
108+
public class Test
109+
{
110+
public static int Main() // CS0161
111+
{
112+
int i = 5;
113+
if (i < 10)
114+
{
115+
// return i;
116+
}
117+
else
118+
{
119+
// Uncomment the following line to resolve.
120+
return 1;
121+
}
122+
}
123+
}
124+
}";
125+
126+
127+
var fixtest = @"
128+
using System;
129+
using System.Collections.Generic;
130+
using System.Linq;
131+
using System.Text;
132+
using System.Threading.Tasks;
133+
using System.Diagnostics;
134+
135+
namespace ConsoleApplication1
136+
{
137+
public class Test
138+
{
139+
public static int Main() // CS0161
140+
{
141+
int i = 5;
142+
if (i < 10)
143+
{
144+
throw new NotImplementedException();
145+
// return i;
146+
}
147+
else
148+
{
149+
// Uncomment the following line to resolve.
150+
return 1;
151+
}
152+
}
153+
}
154+
}";
155+
var diagnostics = new DiagnosticResult[]
156+
{
157+
// Test0.cs(13,27): error CS0161: 'Test.Main()': not all code paths return a value
158+
DiagnosticResult.CompilerError("CS0161").WithSpan(13, 27, 13, 31).WithArguments("ConsoleApplication1.Test.Main()")
159+
};
160+
161+
await Verifier.VerifyCodeFixAsync(test, diagnostics, fixtest);
162+
}
163+
164+
[TestMethod]
165+
public async Task RewriteDeepBranch()
166+
{
167+
var test = @"
168+
using System;
169+
using System.Collections.Generic;
170+
using System.Linq;
171+
using System.Text;
172+
using System.Threading.Tasks;
173+
using System.Diagnostics;
174+
175+
namespace ConsoleApplication1
176+
{
177+
public class Test
178+
{
179+
public static int Main() // CS0161
180+
{
181+
int i = 5;
182+
if (i < 10)
183+
{
184+
if (i < 10)
185+
{
186+
// return i;
187+
}
188+
else
189+
{
190+
// Uncomment the following line to resolve.
191+
return 1;
192+
}
193+
}
194+
else
195+
{
196+
// Uncomment the following line to resolve.
197+
return 1;
198+
}
199+
}
200+
}
201+
}";
202+
203+
204+
var fixtest = @"
205+
using System;
206+
using System.Collections.Generic;
207+
using System.Linq;
208+
using System.Text;
209+
using System.Threading.Tasks;
210+
using System.Diagnostics;
211+
212+
namespace ConsoleApplication1
213+
{
214+
public class Test
215+
{
216+
public static int Main() // CS0161
217+
{
218+
int i = 5;
219+
if (i < 10)
220+
{
221+
if (i < 10)
222+
{
223+
throw new NotImplementedException();
224+
// return i;
225+
}
226+
else
227+
{
228+
// Uncomment the following line to resolve.
229+
return 1;
230+
}
231+
}
232+
else
233+
{
234+
// Uncomment the following line to resolve.
235+
return 1;
236+
}
237+
}
238+
}
239+
}";
240+
var diagnostics = new DiagnosticResult[]
241+
{
242+
// Test0.cs(13,27): error CS0161: 'Test.Main()': not all code paths return a value
243+
DiagnosticResult.CompilerError("CS0161").WithSpan(13, 27, 13, 31).WithArguments("ConsoleApplication1.Test.Main()")
244+
};
245+
246+
await Verifier.VerifyCodeFixAsync(test, diagnostics, fixtest);
247+
}
248+
249+
}
250+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright 2020 Andreas Müller
2+
// This file is a part of Amusoft.CodeAnalysis.Analyzers and is licensed under Apache 2.0
3+
// See https://github.com/taori/Amusoft.CodeAnalysis.Analyzers/blob/master/LICENSE for details
4+
5+
using System;
6+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
7+
using System.Composition;
8+
using System.Linq;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
using Amusoft.CodeAnalysis.Analyzers.Shared;
12+
using Microsoft.CodeAnalysis;
13+
using Microsoft.CodeAnalysis.CodeFixes;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
using Microsoft.CodeAnalysis.Editing;
17+
using Microsoft.CodeAnalysis.Formatting;
18+
using Microsoft.CodeAnalysis.Simplification;
19+
20+
namespace Amusoft.CodeAnalysis.Analyzers.CS0161
21+
{
22+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = "CS0161-FixByReplacingWithThrowExpression"), Shared]
23+
public class FixByReplacingWithThrowExpression : CodeFixProviderBase
24+
{
25+
/// <inheritdoc />
26+
protected override string DiagnosticId { get; } = "CS0161";
27+
28+
/// <inheritdoc />
29+
protected override string GetEquivalenceKey(SyntaxNode rootNode)
30+
{
31+
return "CS0161-FixByReplacingWithThrowExpression";
32+
}
33+
34+
/// <inheritdoc />
35+
protected override string GetTitle(SyntaxNode rootNode)
36+
{
37+
var member = GetAnnotationValue(rootNode, MemberAnnotation);
38+
var typeName = GetAnnotationValue(rootNode, TypeAnnotation);
39+
return string.Format(Resources.MessageFormat_CS0161_FixByReplacingWithThrowExpression, member, typeName);
40+
}
41+
42+
/// <inheritdoc />
43+
protected override async Task<SyntaxNode> FixedDiagnosticAsync(SyntaxNode rootNode, SyntaxNode diagnosticNode,
44+
CodeFixContext context,
45+
CancellationToken cancellationToken)
46+
{
47+
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
48+
if (diagnosticNode is MethodDeclarationSyntax methodDeclarationSyntax)
49+
{
50+
return await FixMethodAsync(context, semanticModel, methodDeclarationSyntax);
51+
}
52+
53+
return rootNode;
54+
}
55+
56+
private async Task<SyntaxNode> FixMethodAsync(CodeFixContext context, SemanticModel semanticModel,
57+
MethodDeclarationSyntax methodDeclarationSyntax)
58+
{
59+
var editor = await DocumentEditor.CreateAsync(context.Document);
60+
foreach (var conditionStatement in methodDeclarationSyntax.DescendantNodes().OfType<IfStatementSyntax>())
61+
{
62+
if (RequiresFix(semanticModel, conditionStatement.Statement))
63+
{
64+
editor.ReplaceNode(conditionStatement.Statement,
65+
GetFixedStatement(semanticModel, conditionStatement.Statement));
66+
}
67+
68+
if (RequiresFix(semanticModel, conditionStatement.Else.Statement))
69+
{
70+
var fixedStatement = GetFixedStatement(semanticModel, conditionStatement.Else.Statement);
71+
editor.ReplaceNode(conditionStatement.Else.Statement,
72+
fixedStatement);
73+
}
74+
}
75+
76+
return editor.GetChangedRoot();
77+
}
78+
79+
private SyntaxNode GetFixedStatement(SemanticModel semanticModel, StatementSyntax statementSyntax)
80+
{
81+
if (statementSyntax is BlockSyntax blockSyntax)
82+
{
83+
var fixedStatement = ThrowStatement(
84+
ObjectCreationExpression(
85+
IdentifierName(
86+
nameof(NotImplementedException)
87+
)
88+
).WithArgumentList(ArgumentList())
89+
);
90+
91+
var fixedBlock = blockSyntax.Statements.Insert(0, fixedStatement);
92+
93+
return blockSyntax.WithStatements(fixedBlock);
94+
}
95+
96+
// statementSyntax.InsertNodesBefore()
97+
return statementSyntax.InsertNodesBefore(
98+
statementSyntax,
99+
new[]
100+
{
101+
ThrowExpression(
102+
ObjectCreationExpression(
103+
IdentifierName(nameof(NotImplementedException))))
104+
}
105+
);
106+
}
107+
108+
private bool RequiresFix(SemanticModel semanticModel, StatementSyntax statementSyntax)
109+
{
110+
var controlFlow = semanticModel.AnalyzeControlFlow(statementSyntax);
111+
return controlFlow.ExitPoints.Length == 0;
112+
}
113+
}
114+
115+
//
116+
// public class Test
117+
// {
118+
// public static int Main() // CS0161
119+
// {
120+
// int i = 5;
121+
// if (i < 10)
122+
// {
123+
// return i;
124+
// }
125+
// else
126+
// {
127+
// // Uncomment the following line to resolve.
128+
// // return 1;
129+
// }
130+
// }
131+
// }
132+
}

0 commit comments

Comments
 (0)