Skip to content

Commit 4b36f0f

Browse files
authored
Merge pull request #3178 from vweijsters/fix-3171
SA1119: Added support for typecast followed by switch expression
2 parents 13cdbb9 + f4e85a2 commit 4b36f0f

File tree

3 files changed

+133
-1
lines changed

3 files changed

+133
-1
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1119CodeFixProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ private static SyntaxNode GetReplacement(ParenthesizedExpressionSyntax oldNode)
7474
if (!leadingTrivia.Any())
7575
{
7676
var previousToken = oldNode.OpenParenToken.GetPreviousToken();
77-
if (!previousToken.IsKind(SyntaxKind.OpenParenToken) && (TriviaHelper.IndexOfTrailingWhitespace(previousToken.TrailingTrivia) == -1))
77+
if (!(previousToken.IsKind(SyntaxKind.OpenParenToken) || previousToken.IsKind(SyntaxKind.CloseParenToken))
78+
&& (TriviaHelper.IndexOfTrailingWhitespace(previousToken.TrailingTrivia) == -1))
7879
{
7980
leadingTrivia = SyntaxFactory.TriviaList(SyntaxFactory.Space);
8081
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp8/MaintainabilityRules/SA1119CSharp8UnitTests.cs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,118 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp8.MaintainabilityRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.Testing;
11+
12+
using StyleCop.Analyzers.Lightup;
613
using StyleCop.Analyzers.Test.CSharp7.MaintainabilityRules;
714

15+
using Xunit;
16+
17+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
18+
StyleCop.Analyzers.MaintainabilityRules.SA1119StatementMustNotUseUnnecessaryParenthesis,
19+
StyleCop.Analyzers.MaintainabilityRules.SA1119CodeFixProvider>;
20+
821
public class SA1119CSharp8UnitTests : SA1119CSharp7UnitTests
922
{
23+
/// <summary>
24+
/// Verifies that a type cast followed by a switch expression is handled correctly.
25+
/// </summary>
26+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
27+
[Fact]
28+
[WorkItem(3171, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3171")]
29+
public async Task TestTypeCastFollowedBySwitchExpressionIsHandledCorrectlyAsync()
30+
{
31+
const string testCode = @"
32+
public class Foo
33+
{
34+
public string TestMethod(int n, object a, object b)
35+
{
36+
return (string)(n switch { 1 => a, 2 => b });
37+
}
38+
}
39+
";
40+
41+
await VerifyCSharpDiagnosticAsync(LanguageVersion.CSharp8, testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
42+
}
43+
44+
/// <summary>
45+
/// Verifies that a type cast followed by a switch expression with unnecessary parenthesis is handled correctly.
46+
/// </summary>
47+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
48+
[Fact]
49+
[WorkItem(3171, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3171")]
50+
public async Task TestTypeCastFollowedBySwitchExpressionWithUnnecessaryParenthesisIsHandledCorrectlyAsync()
51+
{
52+
const string testCode = @"
53+
public class Foo
54+
{
55+
public string TestMethod(int n, object a, object b)
56+
{
57+
return (string)((n switch { 1 => a, 2 => b }));
58+
}
59+
}
60+
";
61+
62+
const string fixedCode = @"
63+
public class Foo
64+
{
65+
public string TestMethod(int n, object a, object b)
66+
{
67+
return (string)(n switch { 1 => a, 2 => b });
68+
}
69+
}
70+
";
71+
72+
DiagnosticResult[] expected =
73+
{
74+
Diagnostic(DiagnosticId).WithSpan(6, 24, 6, 55),
75+
Diagnostic(ParenthesesDiagnosticId).WithLocation(6, 24),
76+
Diagnostic(ParenthesesDiagnosticId).WithLocation(6, 54),
77+
};
78+
79+
await VerifyCSharpFixAsync(LanguageVersion.CSharp8, testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
80+
}
81+
82+
/// <summary>
83+
/// Verifies that a switch expression with unnecessary parenthesis is handled correcly.
84+
/// </summary>
85+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
86+
[Fact]
87+
[WorkItem(3171, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3171")]
88+
public async Task TestSwitchExpressionWithUnnecessaryParenthesisAsync()
89+
{
90+
const string testCode = @"
91+
public class Foo
92+
{
93+
public void TestMethod(int n, object a, object b)
94+
{
95+
var test = (n switch { 1 => a, 2 => b });
96+
}
97+
}
98+
";
99+
100+
const string fixedCode = @"
101+
public class Foo
102+
{
103+
public void TestMethod(int n, object a, object b)
104+
{
105+
var test = n switch { 1 => a, 2 => b };
106+
}
107+
}
108+
";
109+
110+
DiagnosticResult[] expected =
111+
{
112+
Diagnostic(DiagnosticId).WithSpan(6, 20, 6, 49),
113+
Diagnostic(ParenthesesDiagnosticId).WithLocation(6, 20),
114+
Diagnostic(ParenthesesDiagnosticId).WithLocation(6, 48),
115+
};
116+
117+
await VerifyCSharpFixAsync(LanguageVersion.CSharp8, testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
118+
}
10119
}
11120
}

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1119StatementMustNotUseUnnecessaryParenthesis.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ private static void HandleParenthesizedExpression(SyntaxNodeAnalysisContext cont
125125
return;
126126
}
127127

128+
if (IsSwitchExpressionPrecededByTypeCast(node))
129+
{
130+
return;
131+
}
132+
128133
ReportDiagnostic(context, node);
129134
}
130135
else
@@ -197,6 +202,23 @@ private static bool IsConditionalAccessInInterpolation(ExpressionSyntax node)
197202
return false;
198203
}
199204

205+
private static bool IsSwitchExpressionPrecededByTypeCast(ParenthesizedExpressionSyntax node)
206+
{
207+
if (!node.Expression.IsKind(SyntaxKindEx.SwitchExpression))
208+
{
209+
return false;
210+
}
211+
212+
var previousToken = node.OpenParenToken.GetPreviousToken();
213+
214+
while (previousToken.IsKind(SyntaxKind.OpenParenToken) && previousToken.Parent.IsKind(SyntaxKind.ParenthesizedExpression))
215+
{
216+
previousToken = previousToken.GetPreviousToken();
217+
}
218+
219+
return previousToken.IsKind(SyntaxKind.CloseParenToken) && previousToken.Parent.IsKind(SyntaxKind.CastExpression);
220+
}
221+
200222
private static void ReportDiagnostic(SyntaxNodeAnalysisContext context, ParenthesizedExpressionSyntax node)
201223
{
202224
context.ReportDiagnostic(Diagnostic.Create(Descriptor, node.GetLocation()));

0 commit comments

Comments
 (0)