Skip to content

Commit b5c4faa

Browse files
authored
Merge pull request #2753 from vweijsters/fix-sa1137
Fixes alignment issues in SA1137 codefix
2 parents 2eb3ca3 + 95ea0b8 commit b5c4faa

2 files changed

Lines changed: 167 additions & 25 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/ReadabilityRules/SA1137UnitTests.cs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,7 +1538,7 @@ void MethodName()
15381538
}
15391539
15401540
break;
1541-
}
1541+
}
15421542
default:
15431543
label5a:
15441544
label5b:
@@ -1560,6 +1560,7 @@ void MethodName()
15601560
this.CSharpDiagnostic().WithLocation(34, 1),
15611561
this.CSharpDiagnostic().WithLocation(35, 1),
15621562
this.CSharpDiagnostic().WithLocation(36, 1),
1563+
this.CSharpDiagnostic().WithLocation(43, 1),
15631564
this.CSharpDiagnostic().WithLocation(44, 1),
15641565
this.CSharpDiagnostic().WithLocation(45, 1),
15651566
this.CSharpDiagnostic().WithLocation(46, 1),
@@ -1982,6 +1983,82 @@ void ZeroAlignmentMethod()
19821983
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
19831984
}
19841985

1986+
[Fact]
1987+
[WorkItem(2747, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2747")]
1988+
public async Task VerifyInitializerBracesAreCheckedAndFixedAsync()
1989+
{
1990+
var testCode = @"
1991+
using System.Collections.Generic;
1992+
1993+
public class TestClass
1994+
{
1995+
public void TestMethod()
1996+
{
1997+
List<int> testObject1 = new List<int>
1998+
{
1999+
1,
2000+
};
2001+
2002+
TestClass2 testObject2 = new TestClass2
2003+
{
2004+
Test = 1,
2005+
};
2006+
2007+
var testObject3 = new
2008+
{
2009+
TestValue = 1,
2010+
};
2011+
}
2012+
2013+
private class TestClass2
2014+
{
2015+
public int Test { get; set; }
2016+
}
2017+
}
2018+
";
2019+
2020+
var fixedCode = @"
2021+
using System.Collections.Generic;
2022+
2023+
public class TestClass
2024+
{
2025+
public void TestMethod()
2026+
{
2027+
List<int> testObject1 = new List<int>
2028+
{
2029+
1,
2030+
};
2031+
2032+
TestClass2 testObject2 = new TestClass2
2033+
{
2034+
Test = 1,
2035+
};
2036+
2037+
var testObject3 = new
2038+
{
2039+
TestValue = 1,
2040+
};
2041+
}
2042+
2043+
private class TestClass2
2044+
{
2045+
public int Test { get; set; }
2046+
}
2047+
}
2048+
";
2049+
2050+
DiagnosticResult[] expected =
2051+
{
2052+
this.CSharpDiagnostic().WithLocation(11, 1),
2053+
this.CSharpDiagnostic().WithLocation(16, 1),
2054+
this.CSharpDiagnostic().WithLocation(21, 1),
2055+
};
2056+
2057+
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
2058+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
2059+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
2060+
}
2061+
19852062
/// <inheritdoc/>
19862063
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
19872064
{

StyleCop.Analyzers/StyleCop.Analyzers/ReadabilityRules/SA1137ElementsShouldHaveTheSameIndentation.cs

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -217,13 +217,13 @@ private static void HandleSwitchStatement(SyntaxNodeAnalysisContext context)
217217
var labels = ImmutableList.CreateBuilder<SwitchLabelSyntax>();
218218
var statements = ImmutableList.CreateBuilder<StatementSyntax>();
219219
var labeledStatements = ImmutableList.CreateBuilder<StatementSyntax>();
220-
var blockStatements = ImmutableList.CreateBuilder<StatementSyntax>();
220+
var blockStatements = ImmutableList.CreateBuilder<BlockSyntax>();
221221
foreach (SwitchSectionSyntax switchSection in switchStatement.Sections)
222222
{
223223
labels.AddRange(switchSection.Labels);
224224
if (switchSection.Statements.Count == 1 && switchSection.Statements[0].IsKind(SyntaxKind.Block))
225225
{
226-
blockStatements.Add(switchSection.Statements[0]);
226+
blockStatements.Add((BlockSyntax)switchSection.Statements[0]);
227227
continue;
228228
}
229229

@@ -243,20 +243,22 @@ private static void HandleSwitchStatement(SyntaxNodeAnalysisContext context)
243243
CheckElements(context, labels.ToImmutable());
244244
CheckElements(context, statements.ToImmutable());
245245
CheckElements(context, labeledStatements.ToImmutable());
246-
CheckElements(context, blockStatements.ToImmutable());
246+
CheckBlocks(context, blockStatements.ToImmutable());
247247
}
248248

249249
private static void HandleInitializerExpression(SyntaxNodeAnalysisContext context)
250250
{
251251
var initializerExpression = (InitializerExpressionSyntax)context.Node;
252252

253+
CheckBraces(context, initializerExpression.OpenBraceToken, initializerExpression.CloseBraceToken);
253254
CheckElements(context, initializerExpression.Expressions);
254255
}
255256

256257
private static void HandleAnonymousObjectCreationExpression(SyntaxNodeAnalysisContext context)
257258
{
258259
var anonymousObjectCreationExpression = (AnonymousObjectCreationExpressionSyntax)context.Node;
259260

261+
CheckBraces(context, anonymousObjectCreationExpression.OpenBraceToken, anonymousObjectCreationExpression.CloseBraceToken);
260262
CheckElements(context, anonymousObjectCreationExpression.Initializers);
261263
}
262264

@@ -353,6 +355,50 @@ private static void CheckElements<T>(SyntaxNodeAnalysisContext context, Separate
353355
CheckElements(context, elements.ToImmutableList());
354356
}
355357

358+
// BlockSyntax is analyzed separately because it needs to check both braces.
359+
private static void CheckBlocks(SyntaxNodeAnalysisContext context, ImmutableList<BlockSyntax> elements)
360+
{
361+
if (elements.Count < 2)
362+
{
363+
return;
364+
}
365+
366+
elements = CleanupElementsList(elements);
367+
368+
if (elements.Count < 2)
369+
{
370+
return;
371+
}
372+
373+
bool first = true;
374+
string expectedIndentation = null;
375+
foreach (BlockSyntax element in elements)
376+
{
377+
SyntaxTrivia openBraceIndentationTrivia = element.OpenBraceToken.LeadingTrivia.LastOrDefault();
378+
string openBraceIndentation = openBraceIndentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? openBraceIndentationTrivia.ToString() : string.Empty;
379+
380+
SyntaxTrivia closeBraceIndentationTrivia = element.CloseBraceToken.LeadingTrivia.LastOrDefault();
381+
string closeBraceIndentation = closeBraceIndentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? closeBraceIndentationTrivia.ToString() : string.Empty;
382+
383+
if (first)
384+
{
385+
expectedIndentation = openBraceIndentation;
386+
first = false;
387+
continue;
388+
}
389+
390+
if (!string.Equals(expectedIndentation, openBraceIndentation, StringComparison.Ordinal))
391+
{
392+
ReportDiagnostic(context, element.OpenBraceToken, openBraceIndentationTrivia, openBraceIndentation, expectedIndentation);
393+
}
394+
395+
if (!string.Equals(expectedIndentation, closeBraceIndentation, StringComparison.Ordinal))
396+
{
397+
ReportDiagnostic(context, element.CloseBraceToken, closeBraceIndentationTrivia, closeBraceIndentation, expectedIndentation);
398+
}
399+
}
400+
}
401+
356402
private static void CheckElements<T>(SyntaxNodeAnalysisContext context, ImmutableList<T> elements)
357403
where T : SyntaxNode
358404
{
@@ -361,12 +407,7 @@ private static void CheckElements<T>(SyntaxNodeAnalysisContext context, Immutabl
361407
return;
362408
}
363409

364-
elements = elements.RemoveAll(
365-
element =>
366-
{
367-
SyntaxToken firstToken = GetFirstTokenForAnalysis(element);
368-
return firstToken.IsMissingOrDefault() || !firstToken.IsFirstInLine(allowNonWhitespaceTrivia: false);
369-
});
410+
elements = CleanupElementsList(elements);
370411

371412
if (elements.Count < 2)
372413
{
@@ -397,25 +438,22 @@ private static void CheckElements<T>(SyntaxNodeAnalysisContext context, Immutabl
397438
continue;
398439
}
399440

400-
if (string.Equals(expectedIndentation, indentation, StringComparison.Ordinal))
441+
if (!string.Equals(expectedIndentation, indentation, StringComparison.Ordinal))
401442
{
402-
// This handles the case where elements are indented properly
403-
continue;
443+
ReportDiagnostic(context, firstToken, indentationTrivia, indentation, expectedIndentation);
404444
}
445+
}
446+
}
405447

406-
Location location;
407-
if (indentation.Length == 0)
408-
{
409-
location = firstToken.GetLocation();
410-
}
411-
else
448+
private static ImmutableList<T> CleanupElementsList<T>(ImmutableList<T> elements)
449+
where T : SyntaxNode
450+
{
451+
return elements.RemoveAll(
452+
element =>
412453
{
413-
location = indentationTrivia.GetLocation();
414-
}
415-
416-
ImmutableDictionary<string, string> properties = ImmutableDictionary.Create<string, string>().SetItem(ExpectedIndentationKey, expectedIndentation);
417-
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location, properties));
418-
}
454+
SyntaxToken firstToken = GetFirstTokenForAnalysis(element);
455+
return firstToken.IsMissingOrDefault() || !firstToken.IsFirstInLine(allowNonWhitespaceTrivia: false);
456+
});
419457
}
420458

421459
private static SyntaxToken GetFirstTokenForAnalysis(SyntaxNode node)
@@ -433,5 +471,32 @@ private static SyntaxToken GetFirstTokenForAnalysis(SyntaxNode node)
433471

434472
return firstToken;
435473
}
474+
475+
private static void CheckBraces(SyntaxNodeAnalysisContext context, SyntaxToken openBraceToken, SyntaxToken closeBraceToken)
476+
{
477+
if (openBraceToken.GetLine() == closeBraceToken.GetLine())
478+
{
479+
// If the braces are on the same line, there is no point in checking indentation
480+
return;
481+
}
482+
483+
SyntaxTrivia openBraceIndentationTrivia = openBraceToken.LeadingTrivia.LastOrDefault();
484+
string openBraceIndentation = openBraceIndentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? openBraceIndentationTrivia.ToString() : string.Empty;
485+
486+
SyntaxTrivia closeBraceIndentationTrivia = closeBraceToken.LeadingTrivia.LastOrDefault();
487+
string closeBraceIndentation = closeBraceIndentationTrivia.IsKind(SyntaxKind.WhitespaceTrivia) ? closeBraceIndentationTrivia.ToString() : string.Empty;
488+
489+
if (!string.Equals(openBraceIndentation, closeBraceIndentation, StringComparison.Ordinal))
490+
{
491+
ReportDiagnostic(context, closeBraceToken, closeBraceIndentationTrivia, closeBraceIndentation, openBraceIndentation);
492+
}
493+
}
494+
495+
private static void ReportDiagnostic(SyntaxNodeAnalysisContext context, SyntaxToken token, SyntaxTrivia tokenLeadingTrivia, string indentation, string expectedIndentation)
496+
{
497+
Location location = (indentation.Length == 0) ? token.GetLocation() : tokenLeadingTrivia.GetLocation();
498+
ImmutableDictionary<string, string> properties = ImmutableDictionary.Create<string, string>().SetItem(ExpectedIndentationKey, expectedIndentation);
499+
context.ReportDiagnostic(Diagnostic.Create(Descriptor, location, properties));
500+
}
436501
}
437502
}

0 commit comments

Comments
 (0)