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;