Skip to content

Commit 1b4e336

Browse files
committed
Fixed issues in SA1009 code fix
1 parent 4e73d4a commit 1b4e336

3 files changed

Lines changed: 112 additions & 8 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/SpacingRules/TokenSpacingCodeFixProvider.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ private static void UpdateReplaceMap(Dictionary<SyntaxToken, SyntaxToken> replac
113113
}
114114

115115
SyntaxTriviaList triviaList;
116+
SyntaxToken nextToken;
116117
switch (location)
117118
{
118119
case TokenSpacingProperties.LocationPreceding:
@@ -188,7 +189,18 @@ private static void UpdateReplaceMap(Dictionary<SyntaxToken, SyntaxToken> replac
188189
else
189190
{
190191
SyntaxTriviaList trailingTrivia = triviaList.AddRange(token.TrailingTrivia.WithoutLeadingWhitespace(endOfLineIsWhitespace: false));
191-
replaceMap[token] = token.WithLeadingTrivia().WithTrailingTrivia(trailingTrivia);
192+
193+
nextToken = token.GetNextToken();
194+
if (nextToken.IsKind(SyntaxKind.SemicolonToken))
195+
{
196+
// make the semicolon 'sticky'
197+
replaceMap[token] = token.WithLeadingTrivia().WithTrailingTrivia();
198+
replaceMap[nextToken] = nextToken.WithLeadingTrivia().WithTrailingTrivia(trailingTrivia.WithoutTrailingWhitespace());
199+
}
200+
else
201+
{
202+
replaceMap[token] = token.WithLeadingTrivia().WithTrailingTrivia(trailingTrivia);
203+
}
192204
}
193205

194206
break;
@@ -219,7 +231,7 @@ private static void UpdateReplaceMap(Dictionary<SyntaxToken, SyntaxToken> replac
219231
break;
220232

221233
case TokenSpacingProperties.LocationFollowing:
222-
var nextToken = token.GetNextToken();
234+
nextToken = token.GetNextToken();
223235
switch (action)
224236
{
225237
case TokenSpacingProperties.ActionInsert:

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

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,7 @@ public void TestMethod2()
532532
// This is a regression test for https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1206
533533
TestMethod3(
534534
true,
535-
(false || true)) // comment
536-
;
535+
(false || true)); // comment
537536
538537
// This is a regression test for https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1206
539538
if (
@@ -923,6 +922,81 @@ public void TestMethod()
923922
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
924923
}
925924

925+
[Fact]
926+
[WorkItem(2473, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2473")]
927+
public async Task TestCodefixBehaviorWithCommentAndSemiColonAsync()
928+
{
929+
var testCode = @"using System.Threading.Tasks;
930+
931+
public class TestClass
932+
{
933+
public async void TestMethod()
934+
{
935+
await Task.Delay(1000 // Comment
936+
);
937+
}
938+
}
939+
";
940+
941+
var fixedCode = @"using System.Threading.Tasks;
942+
943+
public class TestClass
944+
{
945+
public async void TestMethod()
946+
{
947+
await Task.Delay(1000); // Comment
948+
}
949+
}
950+
";
951+
952+
DiagnosticResult[] expected =
953+
{
954+
this.CSharpDiagnostic().WithLocation(8, 13).WithArguments(" not", "preceded"),
955+
};
956+
957+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
958+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
959+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
960+
}
961+
962+
[Fact]
963+
[WorkItem(2474, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2474")]
964+
public async Task TestCodefixBehaviorWithMemberAccessAsync()
965+
{
966+
var testCode = @"using System.Threading.Tasks;
967+
968+
public class TestClass
969+
{
970+
public async void TestMethod()
971+
{
972+
await Task.Delay(1000
973+
).ConfigureAwait(false);
974+
}
975+
}
976+
";
977+
978+
var fixedCode = @"using System.Threading.Tasks;
979+
980+
public class TestClass
981+
{
982+
public async void TestMethod()
983+
{
984+
await Task.Delay(1000)
985+
.ConfigureAwait(false);
986+
}
987+
}
988+
";
989+
990+
DiagnosticResult[] expected =
991+
{
992+
this.CSharpDiagnostic().WithLocation(8, 13).WithArguments(" not", "preceded"),
993+
};
994+
995+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
996+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
997+
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
998+
}
999+
9261000
/// <inheritdoc/>
9271001
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
9281002
{

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

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, Syn
8181
bool lastInLine = token.IsLastInLine();
8282
bool precedesStickyCharacter;
8383
bool allowEndOfLine = false;
84+
bool preserveLayout = false;
8485

8586
bool suppressFollowingSpaceError = false;
8687

@@ -141,8 +142,15 @@ private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, Syn
141142
break;
142143

143144
case SyntaxKind.DotToken:
145+
// allow a space for this case, but only if the ')' character is the last on the line
146+
allowEndOfLine = true;
147+
precedesStickyCharacter = true;
148+
149+
preserveLayout = nextToken.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression);
150+
break;
151+
144152
case SyntaxKind.MinusGreaterThanToken:
145-
// allow a space for these cases, but only if the ')' character is the last on the line
153+
// allow a space for this case, but only if the ')' character is the last on the line
146154
allowEndOfLine = true;
147155
precedesStickyCharacter = true;
148156
break;
@@ -198,9 +206,19 @@ private static void HandleCloseParenToken(SyntaxTreeAnalysisContext context, Syn
198206
if (precededBySpace)
199207
{
200208
// Closing parenthesis should{ not} be {preceded} by a space.
201-
var properties = token.IsFirstInLine()
202-
? TokenSpacingProperties.RemovePreceding
203-
: TokenSpacingProperties.RemoveImmediatePreceding;
209+
ImmutableDictionary<string, string> properties;
210+
211+
if (preserveLayout)
212+
{
213+
properties = TokenSpacingProperties.RemovePrecedingPreserveLayout;
214+
}
215+
else
216+
{
217+
properties = token.IsFirstInLine()
218+
? TokenSpacingProperties.RemovePreceding
219+
: TokenSpacingProperties.RemoveImmediatePreceding;
220+
}
221+
204222
context.ReportDiagnostic(Diagnostic.Create(Descriptor, token.GetLocation(), properties, " not", "preceded"));
205223
}
206224

0 commit comments

Comments
 (0)