Skip to content

Commit 9849873

Browse files
authored
Merge pull request #2411 from sharwell/fix-sa1009-operators
Fix behavior of SA1009 for closing parenthesis before binary operator
2 parents ca4acb4 + b8d237b commit 9849873

File tree

2 files changed

+95
-42
lines changed

2 files changed

+95
-42
lines changed

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

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

235+
/// <summary>
236+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#255 and
237+
/// DotNetAnalyzers/StyleCopAnalyzers#256.
238+
/// </summary>
239+
/// <param name="operatorToken">The operator to test.</param>
240+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
235241
[Theory]
236242
[InlineData("+")]
237243
[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)
244+
public async Task TestNotReportedWhenFollowedByUnaryPlusOrMinusAsync(string operatorToken)
270245
{
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);
246+
// This will be reported as SA1021 or SA1022
247+
var ignoredStatement = $"var i = (int) {operatorToken}2;";
248+
var correctStatement = $"var i = (int){operatorToken}2;";
274249

275-
DiagnosticResult expected = this.CSharpDiagnostic().WithArguments(string.Empty, "followed").WithLocation(7, 27);
276-
277-
await this.TestWhitespaceInStatementOrDeclAsync(invalidStatement, validStatement, expected).ConfigureAwait(false);
250+
await this.TestWhitespaceInStatementOrDeclAsync(ignoredStatement, string.Empty, EmptyDiagnosticResults).ConfigureAwait(false);
251+
await this.TestWhitespaceInStatementOrDeclAsync(correctStatement, string.Empty, EmptyDiagnosticResults).ConfigureAwait(false);
278252
}
279253

280254
[Fact]
@@ -835,6 +809,83 @@ void Method()
835809
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
836810
}
837811

812+
/// <summary>
813+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
814+
/// </summary>
815+
/// <param name="operatorToken">The operator to test.</param>
816+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
817+
[Theory]
818+
[InlineData("*")]
819+
[InlineData("/")]
820+
[InlineData("%")]
821+
[InlineData("+")]
822+
[InlineData("-")]
823+
[InlineData("<<")]
824+
[InlineData(">>")]
825+
[InlineData("<")]
826+
[InlineData(">")]
827+
[InlineData("<=")]
828+
[InlineData(">=")]
829+
[InlineData("==")]
830+
[InlineData("!=")]
831+
[InlineData("&")]
832+
[InlineData("^")]
833+
[InlineData("|")]
834+
public async Task TestFollowedByBinaryOperatorAsync(string operatorToken)
835+
{
836+
string testCode = $"var x = (3 + 2){operatorToken} 4;";
837+
string fixedCode = $"var x = (3 + 2) {operatorToken} 4;";
838+
839+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 27).WithArguments(string.Empty, "followed");
840+
841+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
842+
}
843+
844+
/// <summary>
845+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
846+
/// </summary>
847+
/// <param name="operatorToken">The operator to test.</param>
848+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
849+
[Theory]
850+
[InlineData("&&")]
851+
[InlineData("||")]
852+
public async Task TestFollowedByConditionalOperatorAsync(string operatorToken)
853+
{
854+
string testCode = $"var x = (true){operatorToken} false;";
855+
string fixedCode = $"var x = (true) {operatorToken} false;";
856+
857+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 26).WithArguments(string.Empty, "followed");
858+
859+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
860+
}
861+
862+
/// <summary>
863+
/// This is a regression test for DotNetAnalyzers/StyleCopAnalyzers#2409.
864+
/// </summary>
865+
/// <param name="operatorToken">The operator to test.</param>
866+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
867+
[Theory]
868+
[InlineData("=")]
869+
[InlineData("+=")]
870+
[InlineData("-=")]
871+
[InlineData("*=")]
872+
[InlineData("/=")]
873+
[InlineData("%=")]
874+
[InlineData("&=")]
875+
[InlineData("|=")]
876+
[InlineData("^=")]
877+
[InlineData("<<=")]
878+
[InlineData(">>=")]
879+
public async Task TestFollowedByAssignmentOperatorAsync(string operatorToken)
880+
{
881+
string testCode = $"var x = 0; (x){operatorToken} 4;";
882+
string fixedCode = $"var x = 0; (x) {operatorToken} 4;";
883+
884+
DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(7, 26).WithArguments(string.Empty, "followed");
885+
886+
await this.TestWhitespaceInStatementOrDeclAsync(testCode, fixedCode, expected).ConfigureAwait(false);
887+
}
888+
838889
[Fact]
839890
public async Task TestMissingTokenAsync()
840891
{

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)