diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1515UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1515UnitTests.cs
index 9d73167ac..778af1e74 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1515UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1515UnitTests.cs
@@ -243,5 +243,31 @@ public class TestConstants
await VerifyCSharpFixAsync(testCode, expectedDiagnostic, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
}
+
+ ///
+ /// Verifies that the analyzer will properly handle documentation followed by a comment,
+ /// even if there is another non-adjacent comment earlier.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ [WorkItem(3481, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3481")]
+ public async Task TestDocumentationFollowedByCommentWhenThereIsAlsoAnEarlierCommentAsync()
+ {
+ var testCode = @"
+public class Class1 // Comment 1
+{
+ public Class1()
+ {
+ }
+
+ ///
+ /// Gets value.
+ ///
+ // Comment 2
+ public double Value { get; }
+}";
+
+ await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1515SingleLineCommentMustBePrecededByBlankLine.cs b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1515SingleLineCommentMustBePrecededByBlankLine.cs
index 3c0d1ec8e..6e9faf64c 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1515SingleLineCommentMustBePrecededByBlankLine.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1515SingleLineCommentMustBePrecededByBlankLine.cs
@@ -108,21 +108,18 @@ public override void Initialize(AnalysisContext context)
private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
{
var syntaxRoot = context.Tree.GetRoot(context.CancellationToken);
- var previousCommentNotOnOwnLine = false;
foreach (var trivia in syntaxRoot.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)))
{
if (trivia.FullSpan.Start == 0)
{
// skip the trivia if it is at the start of the file
- previousCommentNotOnOwnLine = false;
continue;
}
if (trivia.ToString().StartsWith("////", StringComparison.Ordinal))
{
// ignore commented out code
- previousCommentNotOnOwnLine = false;
continue;
}
@@ -132,26 +129,21 @@ private static void HandleSyntaxTree(SyntaxTreeAnalysisContext context)
if (!IsOnOwnLine(triviaList, triviaIndex))
{
// ignore comments after other code elements.
- previousCommentNotOnOwnLine = true;
continue;
}
if (IsPrecededByBlankLine(triviaList, triviaIndex))
{
// allow properly formatted blank line comments.
- previousCommentNotOnOwnLine = false;
continue;
}
- if (!previousCommentNotOnOwnLine && IsPrecededBySingleLineCommentOrDocumentation(triviaList, triviaIndex))
+ if (IsPrecededBySingleLineCommentOnOwnLineOrDocumentation(triviaList, triviaIndex))
{
// allow consecutive single line comments.
- previousCommentNotOnOwnLine = false;
continue;
}
- previousCommentNotOnOwnLine = false;
-
if (IsAtStartOfScope(trivia))
{
// allow single line comment at scope start.
@@ -185,7 +177,7 @@ private static bool IsOnOwnLine(T triviaList, int triviaIndex)
return false;
}
- private static bool IsPrecededBySingleLineCommentOrDocumentation(T triviaList, int triviaIndex)
+ private static bool IsPrecededBySingleLineCommentOnOwnLineOrDocumentation(T triviaList, int triviaIndex)
where T : IReadOnlyList
{
var eolCount = 0;
@@ -206,6 +198,8 @@ private static bool IsPrecededBySingleLineCommentOrDocumentation(T triviaList
break;
case SyntaxKind.SingleLineCommentTrivia:
+ return IsOnOwnLine(triviaList, triviaIndex);
+
case SyntaxKind.SingleLineDocumentationCommentTrivia:
return true;