Skip to content

Commit db0b461

Browse files
committed
SA1653: Fixed typos and added trivia test cases
1 parent 26e26b2 commit db0b461

5 files changed

Lines changed: 128 additions & 36 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1653CodeFixProvider.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private static SyntaxNode RewriteInitializer(InitializerExpressionSyntax initial
8484
var existingItems = new List<ExpressionSyntax>(initializer.Expressions);
8585
var last = existingItems.Last();
8686
existingItems.Remove(last);
87-
existingItems.Add(last.WithTrailingTrivia(last.GetTrailingTrivia().WithoutTrailingWhitespace()));
87+
existingItems.Add(last.WithoutTrailingTrivia());
8888

8989
var existingSeparators = initializer.Expressions.GetSeparators();
9090
var newSeparators = new List<SyntaxToken>(existingSeparators);
@@ -94,21 +94,16 @@ private static SyntaxNode RewriteInitializer(InitializerExpressionSyntax initial
9494
existingItems,
9595
newSeparators);
9696

97-
var newInitializer = SyntaxFactory.InitializerExpression(
98-
initializer.Kind(),
99-
initializer.OpenBraceToken,
100-
newInitializerExpressions,
101-
initializer.CloseBraceToken);
102-
103-
return newInitializer;
97+
var fixedInitializer = initializer.WithExpressions(newInitializerExpressions);
98+
return fixedInitializer;
10499
}
105100

106101
private static SyntaxNode RewriteAnonymousObjectInitializer(AnonymousObjectCreationExpressionSyntax initializer)
107102
{
108103
var existingItems = new List<AnonymousObjectMemberDeclaratorSyntax>(initializer.Initializers);
109104
var last = existingItems.Last();
110105
existingItems.Remove(last);
111-
existingItems.Add(last.WithTrailingTrivia(last.GetTrailingTrivia().WithoutTrailingWhitespace()));
106+
existingItems.Add(last.WithoutTrailingTrivia());
112107

113108
var existingSeparators = initializer.Initializers.GetSeparators();
114109
var newSeparators = new List<SyntaxToken>(existingSeparators);
@@ -118,12 +113,8 @@ private static SyntaxNode RewriteAnonymousObjectInitializer(AnonymousObjectCreat
118113
existingItems,
119114
newSeparators);
120115

121-
var newInitializer = SyntaxFactory.AnonymousObjectCreationExpression(
122-
initializer.NewKeyword,
123-
initializer.OpenBraceToken,
124-
newInitializerExpressions,
125-
initializer.CloseBraceToken);
126-
return newInitializer;
116+
var fixedInitializer = initializer.WithInitializers(newInitializerExpressions);
117+
return fixedInitializer;
127118
}
128119
}
129120
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/MaintainabilityRules/SA1653UnitTests.cs

Lines changed: 99 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void Foo()
7676
}
7777

7878
/// <summary>
79-
/// Verifies that an object initialiezr without a trailing comma produces a diagnostic.
79+
/// Verifies that an object initializer without a trailing comma produces a diagnostic.
8080
/// </summary>
8181
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
8282
[Fact]
@@ -143,7 +143,7 @@ void Foo()
143143
}
144144

145145
/// <summary>
146-
/// Verifies that an anonymous object initialiezr without a trailing comma produces a diagnostic.
146+
/// Verifies that an anonymous object initializer without a trailing comma produces a diagnostic.
147147
/// </summary>
148148
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
149149
[Fact]
@@ -188,7 +188,53 @@ void Foo()
188188
}
189189

190190
/// <summary>
191-
/// Verifies that an array initialiezr without a trailing comma produces a diagnostic.
191+
/// Verifies that an anonymous object initializer without a trailing comma produces a diagnostic,
192+
/// and the code fix preserves trailing trivia.
193+
/// </summary>
194+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
195+
[Fact]
196+
public async Task VerifyAnonymousObjectInitializerWithTrailingTriviaAsync()
197+
{
198+
var testCode = @"
199+
class TestClass
200+
{
201+
void Foo()
202+
{
203+
var x = new
204+
{
205+
Min = 0,
206+
Max = 0 // trivia?
207+
};
208+
}
209+
}
210+
";
211+
212+
var fixedTestCode = @"
213+
class TestClass
214+
{
215+
void Foo()
216+
{
217+
var x = new
218+
{
219+
Min = 0,
220+
Max = 0, // trivia?
221+
};
222+
}
223+
}
224+
";
225+
226+
DiagnosticResult[] expected =
227+
{
228+
this.CSharpDiagnostic().WithLocation(9, 13),
229+
};
230+
231+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
232+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
233+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
234+
}
235+
236+
/// <summary>
237+
/// Verifies that an array initializer without a trailing comma produces a diagnostic.
192238
/// </summary>
193239
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
194240
[Fact]
@@ -235,7 +281,55 @@ void Foo()
235281
}
236282

237283
/// <summary>
238-
/// Verifies that a collection initialiezr without a trailing comma produces a diagnostic.
284+
/// Verifies that an array initializer without a trailing comma produces a diagnostic,
285+
/// and the code fix preserves existing trivia.
286+
/// </summary>
287+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
288+
[Fact]
289+
public async Task VerifyArrayInitializerWithTrailingTriviaAsync()
290+
{
291+
var testCode = @"
292+
class TestClass
293+
{
294+
void Foo()
295+
{
296+
var x = new[]
297+
{
298+
1,
299+
2,
300+
3 /* last item */
301+
};
302+
}
303+
}
304+
";
305+
306+
var fixedTestCode = @"
307+
class TestClass
308+
{
309+
void Foo()
310+
{
311+
var x = new[]
312+
{
313+
1,
314+
2,
315+
3, /* last item */
316+
};
317+
}
318+
}
319+
";
320+
321+
DiagnosticResult[] expected =
322+
{
323+
this.CSharpDiagnostic().WithLocation(10, 13),
324+
};
325+
326+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
327+
await this.VerifyCSharpDiagnosticAsync(fixedTestCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
328+
await this.VerifyCSharpFixAsync(testCode, fixedTestCode).ConfigureAwait(false);
329+
}
330+
331+
/// <summary>
332+
/// Verifies that a collection initializer without a trailing comma produces a diagnostic.
239333
/// </summary>
240334
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
241335
[Fact]
@@ -284,7 +378,7 @@ void Foo()
284378
}
285379

286380
/// <summary>
287-
/// Verifies that an array initialiezr without a trailing comma produces a diagnostic.
381+
/// Verifies that an array initializer without a trailing comma produces a diagnostic.
288382
/// </summary>
289383
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
290384
[Fact]

StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1653UseTrailingCommasForMultiLineInitializers.cs renamed to StyleCop.Analyzers/StyleCop.Analyzers/MaintainabilityRules/SA1653UseTrailingCommasInMultiLineInitializers.cs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ internal class SA1653UseTrailingCommasInMultiLineInitializers : DiagnosticAnalyz
3232

3333
private static readonly Action<CompilationStartAnalysisContext> CompilationStartAction = HandleCompilationStart;
3434
private static readonly Action<SyntaxNodeAnalysisContext> HandleObjectInitializerAction = HandleObjectInitializer;
35+
private static readonly Action<SyntaxNodeAnalysisContext> HandleAnonymousObjectInitializerAction = HandleAnonymousObjectInitializer;
36+
37+
private static readonly ImmutableArray<SyntaxKind> ObjectInitializerKinds =
38+
ImmutableArray.Create(SyntaxKind.ObjectInitializerExpression, SyntaxKind.ArrayInitializerExpression, SyntaxKind.CollectionInitializerExpression);
3539

3640
/// <inheritdoc/>
3741
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } =
@@ -45,33 +49,36 @@ public override void Initialize(AnalysisContext context)
4549

4650
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
4751
{
48-
context.RegisterSyntaxNodeActionHonorExclusions(HandleObjectInitializerAction, SyntaxKind.ObjectInitializerExpression);
49-
context.RegisterSyntaxNodeActionHonorExclusions(HandleObjectInitializerAction, SyntaxKind.AnonymousObjectCreationExpression);
50-
context.RegisterSyntaxNodeActionHonorExclusions(HandleObjectInitializerAction, SyntaxKind.ArrayInitializerExpression);
51-
context.RegisterSyntaxNodeActionHonorExclusions(HandleObjectInitializerAction, SyntaxKind.CollectionInitializerExpression);
52+
context.RegisterSyntaxNodeActionHonorExclusions(HandleObjectInitializerAction, ObjectInitializerKinds);
53+
context.RegisterSyntaxNodeActionHonorExclusions(HandleAnonymousObjectInitializerAction, SyntaxKind.AnonymousObjectCreationExpression);
5254
}
5355

5456
private static void HandleObjectInitializer(SyntaxNodeAnalysisContext context)
5557
{
56-
var initializer = (ExpressionSyntax)context.Node;
57-
if (initializer == null
58-
|| !IsMultiline(initializer))
58+
var initializer = (InitializerExpressionSyntax)context.Node;
59+
if (initializer == null || !initializer.SpansMultipleLines())
5960
{
6061
return;
6162
}
6263

63-
var childNodesAndTokens = initializer.ChildNodesAndTokens().ToList();
64-
var lastToken = childNodesAndTokens[childNodesAndTokens.Count - 2];
65-
if (!lastToken.IsKind(SyntaxKind.CommaToken))
64+
if (initializer.Expressions.SeparatorCount < initializer.Expressions.Count)
6665
{
67-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, lastToken.GetLocation()));
66+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, initializer.Expressions.Last().GetLocation()));
6867
}
6968
}
7069

71-
private static bool IsMultiline(ExpressionSyntax initializer)
70+
private static void HandleAnonymousObjectInitializer(SyntaxNodeAnalysisContext context)
7271
{
73-
var lineSpan = initializer.GetLineSpan();
74-
return lineSpan.StartLinePosition.Line != lineSpan.EndLinePosition.Line;
72+
var initializer = (AnonymousObjectCreationExpressionSyntax)context.Node;
73+
if (initializer == null || !initializer.SpansMultipleLines())
74+
{
75+
return;
76+
}
77+
78+
if (initializer.Initializers.SeparatorCount < initializer.Initializers.Count)
79+
{
80+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, initializer.Initializers.Last().GetLocation()));
81+
}
7582
}
7683
}
7784
}

StyleCop.Analyzers/StyleCop.Analyzers/StyleCop.Analyzers.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@
183183
<Compile Include="MaintainabilityRules\SA1410RemoveDelegateParenthesisWhenPossible.cs" />
184184
<Compile Include="MaintainabilityRules\SA1411AttributeConstructorMustNotUseUnnecessaryParenthesis.cs" />
185185
<Compile Include="MaintainabilityRules\SA1412StoreFilesAsUtf8.cs" />
186-
<Compile Include="MaintainabilityRules\SA1653UseTrailingCommasForMultiLineInitializers.cs" />
186+
<Compile Include="MaintainabilityRules\SA1653UseTrailingCommasInMultiLineInitializers.cs" />
187187
<Compile Include="MaintainabilityRules\SystemDiagnosticsDebugDiagnosticBase.cs" />
188188
<Compile Include="NamingRules\NamingResources.Designer.cs">
189189
<AutoGen>True</AutoGen>

documentation/SA1653.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
## Cause
2121

22-
The last statement in a C# initializer is missing a trailing comma.
22+
The last statement in a multi-line C# initializer is missing a trailing comma.
2323

2424
## Rule description
2525

0 commit comments

Comments
 (0)