Skip to content

Commit 68c59b6

Browse files
committed
SA1516 no longer counts attributes when determining if a declaration is multiline
1 parent 9a9ef63 commit 68c59b6

2 files changed

Lines changed: 84 additions & 4 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1516UnitTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,26 @@ public event System.EventHandler FooProperty
523523
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
524524
}
525525

526+
/// <summary>
527+
/// Verifies that private fields with attributes are handled properly.
528+
/// This is a regression test for #1595
529+
/// </summary>
530+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
531+
[Fact]
532+
public async Task VerifyThatPrivateFieldsAreHandledProperlyAsync()
533+
{
534+
string testCode = @"using System;
535+
536+
public class TestClass
537+
{
538+
[Obsolete]
539+
private int test1;
540+
private bool test2;
541+
}";
542+
543+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
544+
}
545+
526546
protected override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
527547
{
528548
yield return new SA1516ElementsMustBeSeparatedByBlankLine();

StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,9 @@ private static void HandleMemberList(SyntaxNodeAnalysisContext context, SyntaxLi
196196
// or the previous declaration is of different type
197197
// or the current declaration has documentation
198198
// or the current declaration is not a field declaration,
199-
if (IsMultiline(members[i - 1])
200-
|| !members[i - 1].IsKind(members[i].Kind())
201-
|| !members[i].IsKind(SyntaxKind.FieldDeclaration))
199+
if (!members[i - 1].IsKind(members[i].Kind())
200+
|| !members[i].IsKind(SyntaxKind.FieldDeclaration)
201+
|| IsMultiline(members[i - 1]))
202202
{
203203
ReportIfThereIsNoBlankLine(context, members[i - 1], members[i]);
204204
}
@@ -209,8 +209,21 @@ private static void HandleMemberList(SyntaxNodeAnalysisContext context, SyntaxLi
209209
private static bool IsMultiline(SyntaxNode node)
210210
{
211211
var lineSpan = node.GetLineSpan();
212+
var attributes = GetAttributes(node);
213+
int startLine;
212214

213-
return lineSpan.StartLinePosition.Line != lineSpan.EndLinePosition.Line;
215+
// Exclude attributes when determining if a node spans multiple lines
216+
if (attributes.Count > 0)
217+
{
218+
var lastAttributeSpan = node.SyntaxTree.GetLineSpan(attributes.Last().FullSpan);
219+
startLine = lastAttributeSpan.EndLinePosition.Line;
220+
}
221+
else
222+
{
223+
startLine = lineSpan.StartLinePosition.Line;
224+
}
225+
226+
return startLine != lineSpan.EndLinePosition.Line;
214227
}
215228

216229
private static void ReportIfThereIsNoBlankLine(SyntaxNodeAnalysisContext context, SyntaxNode firstNode, SyntaxNode secondNode)
@@ -270,5 +283,52 @@ private static bool HasEmptyLine(IEnumerable<SyntaxTrivia> allTrivia)
270283

271284
return false;
272285
}
286+
287+
// copied from Roslyn code, as its inaccessible there
288+
// (from Microsoft.CodeAnalysis.CSharp.Extensions.MemberDeclarationSyntaxExtensions)
289+
private static SyntaxList<AttributeListSyntax> GetAttributes(SyntaxNode node)
290+
{
291+
var memberDeclaration = node as MemberDeclarationSyntax;
292+
if (memberDeclaration != null)
293+
{
294+
switch (memberDeclaration.Kind())
295+
{
296+
case SyntaxKind.EnumDeclaration:
297+
return ((EnumDeclarationSyntax)memberDeclaration).AttributeLists;
298+
case SyntaxKind.EnumMemberDeclaration:
299+
return ((EnumMemberDeclarationSyntax)memberDeclaration).AttributeLists;
300+
case SyntaxKind.ClassDeclaration:
301+
case SyntaxKind.InterfaceDeclaration:
302+
case SyntaxKind.StructDeclaration:
303+
return ((TypeDeclarationSyntax)memberDeclaration).AttributeLists;
304+
case SyntaxKind.DelegateDeclaration:
305+
return ((DelegateDeclarationSyntax)memberDeclaration).AttributeLists;
306+
case SyntaxKind.FieldDeclaration:
307+
return ((FieldDeclarationSyntax)memberDeclaration).AttributeLists;
308+
case SyntaxKind.EventFieldDeclaration:
309+
return ((EventFieldDeclarationSyntax)memberDeclaration).AttributeLists;
310+
case SyntaxKind.ConstructorDeclaration:
311+
return ((ConstructorDeclarationSyntax)memberDeclaration).AttributeLists;
312+
case SyntaxKind.DestructorDeclaration:
313+
return ((DestructorDeclarationSyntax)memberDeclaration).AttributeLists;
314+
case SyntaxKind.PropertyDeclaration:
315+
return ((PropertyDeclarationSyntax)memberDeclaration).AttributeLists;
316+
case SyntaxKind.EventDeclaration:
317+
return ((EventDeclarationSyntax)memberDeclaration).AttributeLists;
318+
case SyntaxKind.IndexerDeclaration:
319+
return ((IndexerDeclarationSyntax)memberDeclaration).AttributeLists;
320+
case SyntaxKind.OperatorDeclaration:
321+
return ((OperatorDeclarationSyntax)memberDeclaration).AttributeLists;
322+
case SyntaxKind.ConversionOperatorDeclaration:
323+
return ((ConversionOperatorDeclarationSyntax)memberDeclaration).AttributeLists;
324+
case SyntaxKind.MethodDeclaration:
325+
return ((MethodDeclarationSyntax)memberDeclaration).AttributeLists;
326+
case SyntaxKind.IncompleteMember:
327+
return ((IncompleteMemberSyntax)memberDeclaration).AttributeLists;
328+
}
329+
}
330+
331+
return SyntaxFactory.List<AttributeListSyntax>();
332+
}
273333
}
274334
}

0 commit comments

Comments
 (0)