Skip to content

Commit a77cb7e

Browse files
committed
Fix behavior of SA1009 for closing paren before binary operator
Fixes #2409
1 parent ca4acb4 commit a77cb7e

File tree

2 files changed

+83
-49
lines changed

2 files changed

+83
-49
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test/SpacingRules/SA1009UnitTests.cs

Lines changed: 77 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -232,51 +232,6 @@ public async Task TestLambdaExpressionWithNoSpaceAfterClosingParenthesisAsync()
232232
await this.TestWhitespaceInStatementOrDeclAsync(invalidStatement, validStatement, expected).ConfigureAwait(false);
233233
}
234234

235-
[Theory]
236-
[InlineData("+")]
237-
[InlineData("-")]
238-
[InlineData("*")]
239-
[InlineData("/")]
240-
public async Task TestSpaceAfterParenthisisInArithmeticOperationAsync(string operatorValue)
241-
{
242-
// e.g. var i = (1 + 1) + 2
243-
var validStatement = string.Format(@"var i = (1 + 1) {0} 2;", operatorValue);
244-
245-
await this.TestWhitespaceInStatementOrDeclAsync(validStatement, string.Empty, EmptyDiagnosticResults).ConfigureAwait(false);
246-
}
247-
248-
[Fact]
249-
public async Task TestNoSpaceAfterParenthisisInAddOperationAsync()
250-
{
251-
// Note - this looks wrong but according to comments in the implementation "this will be reported as SA1022"
252-
var invalidStatement = @"var i = (1 + 1)+ 2;";
253-
254-
await this.TestWhitespaceInStatementOrDeclAsync(invalidStatement, string.Empty, EmptyDiagnosticResults).ConfigureAwait(false);
255-
}
256-
257-
[Fact]
258-
public async Task TestNoSpaceAfterParenthisisInSubtractOperationAsync()
259-
{
260-
// Note - this looks wrong but according to comments in the implementation "this will be reported as SA1021"
261-
var invalidStatement = @"var i = (1 + 1)- 2;";
262-
263-
await this.TestWhitespaceInStatementOrDeclAsync(invalidStatement, string.Empty, EmptyDiagnosticResults).ConfigureAwait(false);
264-
}
265-
266-
[Theory]
267-
[InlineData("*")]
268-
[InlineData("/")]
269-
public async Task TestNoSpaceAfterParenthisisInArithmeticOperationAsync(string operatorValue)
270-
{
271-
// e.g. var i = (1 + 1)* 2;
272-
var invalidStatement = string.Format(@"var i = (1 + 1){0} 2;", operatorValue);
273-
var validStatement = string.Format(@"var i = (1 + 1) {0} 2;", operatorValue);
274-
275-
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments(string.Empty, "followed").WithLocation(7, 27);
276-
277-
await this.TestWhitespaceInStatementOrDeclAsync(invalidStatement, validStatement, expected).ConfigureAwait(false);
278-
}
279-
280235
[Fact]
281236
public async Task TestSpaceBeforeParenthisInIncrementingForLoopAsync()
282237
{
@@ -835,6 +790,83 @@ void Method()
835790
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
836791
}
837792

793+
/// <summary>
794+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
795+
/// </summary>
796+
/// <param name="operatorToken">The operator to test.</param>
797+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
798+
[Theory]
799+
[InlineData("*")]
800+
[InlineData("/")]
801+
[InlineData("%")]
802+
[InlineData("+")]
803+
[InlineData("-")]
804+
[InlineData("<<")]
805+
[InlineData(">>")]
806+
[InlineData("<")]
807+
[InlineData(">")]
808+
[InlineData("<=")]
809+
[InlineData(">=")]
810+
[InlineData("==")]
811+
[InlineData("!=")]
812+
[InlineData("&")]
813+
[InlineData("^")]
814+
[InlineData("|")]
815+
public async Task TestFollowedByBinaryOperatorAsync(string operatorToken)
816+
{
817+
string testCode = $"var x = (3 + 2){operatorToken} 4;";
818+
string fixedCode = $"var x = (3 + 2) {operatorToken} 4;";
819+
820+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 27).WithArguments(string.Empty, "followed");
821+
822+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
823+
}
824+
825+
/// <summary>
826+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
827+
/// </summary>
828+
/// <param name="operatorToken">The operator to test.</param>
829+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
830+
[Theory]
831+
[InlineData("&&")]
832+
[InlineData("||")]
833+
public async Task TestFollowedByConditionalOperatorAsync(string operatorToken)
834+
{
835+
string testCode = $"var x = (true){operatorToken} false;";
836+
string fixedCode = $"var x = (true) {operatorToken} false;";
837+
838+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 26).WithArguments(string.Empty, "followed");
839+
840+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
841+
}
842+
843+
/// <summary>
844+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
845+
/// </summary>
846+
/// <param name="operatorToken">The operator to test.</param>
847+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
848+
[Theory]
849+
[InlineData("=")]
850+
[InlineData("+=")]
851+
[InlineData("-=")]
852+
[InlineData("*=")]
853+
[InlineData("/=")]
854+
[InlineData("%=")]
855+
[InlineData("&=")]
856+
[InlineData("|=")]
857+
[InlineData("^=")]
858+
[InlineData("<<=")]
859+
[InlineData(">>=")]
860+
public async Task TestFollowedByAssignmentOperatorAsync(string operatorToken)
861+
{
862+
string testCode = $"var x = 0; (x){operatorToken} 4;";
863+
string fixedCode = $"var x = 0; (x) {operatorToken} 4;";
864+
865+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 26).WithArguments(string.Empty, "followed");
866+
867+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
868+
}
869+
838870
[Fact]
839871
public async Task TestMissingTokenAsync()
840872
{

StyleCop.Analyzers/StyleCop.Analyzers/SpacingRules/SA1009ClosingParenthesisMustBeSpacedCorrectly.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace StyleCop.Analyzers.SpacingRules
55
{
66
using System;
7-
using System.Collections.Generic;
87
using System.Collections.Immutable;
98
using Microsoft.CodeAnalysis;
109
using Microsoft.CodeAnalysis.CSharp;
@@ -94,10 +93,13 @@ private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, Syn
9493
case SyntaxKind.SemicolonToken:
9594
case SyntaxKind.CommaToken:
9695
case SyntaxKind.DoubleQuoteToken:
97-
case SyntaxKind.GreaterThanToken:
9896
precedesStickyCharacter = true;
9997
break;
10098

99+
case SyntaxKind.GreaterThanToken:
100+
precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.TypeArgumentList);
101+
break;
102+
101103
case SyntaxKind.QuestionToken:
102104
if (nextToken.Parent.IsKind(SyntaxKind.ConditionalAccessExpression))
103105
{
@@ -116,14 +118,14 @@ private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, Syn
116118
precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.UnaryPlusExpression);
117119

118120
// this will be reported as SA1022
119-
suppressFollowingSpaceError = true;
121+
suppressFollowingSpaceError = precedesStickyCharacter;
120122
break;
121123

122124
case SyntaxKind.MinusToken:
123125
precedesStickyCharacter = nextToken.Parent.IsKind(SyntaxKind.UnaryMinusExpression);
124126

125127
// this will be reported as SA1021
126-
suppressFollowingSpaceError = true;
128+
suppressFollowingSpaceError = precedesStickyCharacter;
127129
break;
128130

129131
case SyntaxKind.DotToken:

0 commit comments

Comments
 (0)