From 7d4dc172bd6049b8a9b73afff342623488327f8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Hellander?= Date: Sat, 25 Feb 2023 08:11:12 +0100 Subject: [PATCH] Update SA1513 codefix to use the existing newline character sequence #3360 --- .../LayoutRules/SA1513CodeFixProvider.cs | 3 +- .../LayoutRules/SA1513UnitTests.cs | 24 +++++++++++++++ .../StyleCop.Analyzers/Helpers/TokenHelper.cs | 30 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1513CodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1513CodeFixProvider.cs index 1b38f0f3f..bea1d10e9 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1513CodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/LayoutRules/SA1513CodeFixProvider.cs @@ -65,7 +65,8 @@ private static async Task GetTransformedDocumentAsync(Document docum diagnostics.Select(diagnostic => root.FindToken(diagnostic.Location.SourceSpan.End)), (originalToken, rewrittenToken) => { - var newTrivia = rewrittenToken.LeadingTrivia.Insert(0, SyntaxFactory.CarriageReturnLineFeed); + var endOfLineTrivia = rewrittenToken.GetPrecedingEndOfLineTrivia(); + var newTrivia = rewrittenToken.LeadingTrivia.Insert(0, endOfLineTrivia); return rewrittenToken.WithLeadingTrivia(newTrivia); }); } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1513UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1513UnitTests.cs index 2bae9eade..7bb867cc3 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1513UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/LayoutRules/SA1513UnitTests.cs @@ -1013,5 +1013,29 @@ public void TestMethod(string extraSupport) await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3360, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3360")] + public async Task TestLineFeedEndOfLinesAsync() + { + var testCode = + "public class TestClass\n" + + "{\n" + + "}\n" + + "// Hello"; + + var fixedCode = + "public class TestClass\n" + + "{\n" + + "}\n" + + "\n" + + "// Hello"; + + DiagnosticResult[] expected = + { + Diagnostic().WithLocation(3, 2), + }; + await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs index 0f4e0bc76..9df1e5739 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Helpers/TokenHelper.cs @@ -175,5 +175,35 @@ internal static bool IsFollowedByWhitespace(this SyntaxToken token) triviaList = token.GetNextToken().LeadingTrivia; return triviaList.Count > 0 && triviaList.First().IsKind(SyntaxKind.WhitespaceTrivia); } + + /// + /// Returns the closest end of line trivia preceding the . + /// This currently only looks immediately before the specified token. + /// + /// The token to process. + /// The closest preceding end of line trivia, or if none is found. + internal static SyntaxTrivia GetPrecedingEndOfLineTrivia(this SyntaxToken token) + { + var leadingTrivia = token.LeadingTrivia; + for (var i = leadingTrivia.Count - 1; i >= 0; i--) + { + if (leadingTrivia[i].IsKind(SyntaxKind.EndOfLineTrivia)) + { + return leadingTrivia[i]; + } + } + + var prevToken = token.GetPreviousToken(); + var prevTrailingTrivia = prevToken.TrailingTrivia; + for (var i = prevTrailingTrivia.Count - 1; i >= 0; i--) + { + if (prevTrailingTrivia[i].IsKind(SyntaxKind.EndOfLineTrivia)) + { + return prevTrailingTrivia[i]; + } + } + + return SyntaxFactory.CarriageReturnLineFeed; + } } }