Skip to content

Commit 7806f54

Browse files
committed
Fix documentation rules assumption that files match default end-of-line settings
1 parent be637d7 commit 7806f54

File tree

12 files changed

+141
-106
lines changed

12 files changed

+141
-106
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace StyleCop.Analyzers.DocumentationRules
1818
using Microsoft.CodeAnalysis.CodeActions;
1919
using Microsoft.CodeAnalysis.CodeFixes;
2020
using Microsoft.CodeAnalysis.CSharp;
21-
using Microsoft.CodeAnalysis.Formatting;
21+
using Microsoft.CodeAnalysis.Text;
2222
using StyleCop.Analyzers.Helpers;
2323
using StyleCop.Analyzers.Helpers.ObjectPools;
2424
using StyleCop.Analyzers.Settings.ObjectModel;
@@ -79,13 +79,14 @@ private static async Task<Document> GetTransformedDocumentAsync(Document documen
7979
private static async Task<SyntaxNode> GetTransformedSyntaxRootAsync(Document document, CancellationToken cancellationToken)
8080
{
8181
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
82+
var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
8283
var settings = document.Project.AnalyzerOptions.GetStyleCopSettingsInCodeFix(root.SyntaxTree, cancellationToken);
8384

8485
var fileHeader = FileHeaderHelpers.ParseFileHeader(root);
8586
SyntaxNode newSyntaxRoot;
8687
if (fileHeader.IsMissing)
8788
{
88-
newSyntaxRoot = AddHeader(document, root, GetFileName(document), settings);
89+
newSyntaxRoot = AddHeader(document, sourceText, root, GetFileName(document), settings);
8990
}
9091
else
9192
{
@@ -98,18 +99,18 @@ private static async Task<SyntaxNode> GetTransformedSyntaxRootAsync(Document doc
9899
var xmlFileHeader = FileHeaderHelpers.ParseXmlFileHeader(root);
99100
if (isMultiLineComment && !xmlFileHeader.IsMalformed)
100101
{
101-
newSyntaxRoot = ReplaceWellFormedMultiLineCommentHeader(document, root, settings, commentIndex, xmlFileHeader);
102+
newSyntaxRoot = ReplaceWellFormedMultiLineCommentHeader(document, sourceText, root, settings, commentIndex, xmlFileHeader);
102103
}
103104
else
104105
{
105-
newSyntaxRoot = ReplaceHeader(document, root, settings, xmlFileHeader.IsMalformed);
106+
newSyntaxRoot = ReplaceHeader(document, sourceText, root, settings, xmlFileHeader.IsMalformed);
106107
}
107108
}
108109

109110
return newSyntaxRoot;
110111
}
111112

112-
private static SyntaxNode ReplaceWellFormedMultiLineCommentHeader(Document document, SyntaxNode root, StyleCopSettings settings, int commentIndex, XmlFileHeader header)
113+
private static SyntaxNode ReplaceWellFormedMultiLineCommentHeader(Document document, SourceText sourceText, SyntaxNode root, StyleCopSettings settings, int commentIndex, XmlFileHeader header)
113114
{
114115
SyntaxTriviaList trivia = root.GetLeadingTrivia();
115116
var commentTrivia = trivia[commentIndex];
@@ -135,7 +136,10 @@ private static SyntaxNode ReplaceWellFormedMultiLineCommentHeader(Document docum
135136
string interlinePadding = " *";
136137

137138
int minExpectedLength = (commentIndentation + interlinePadding).Length;
138-
string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
139+
var options = document.Project.Solution.Workspace.Options;
140+
var firstToken = root.GetFirstToken(includeZeroWidth: true);
141+
SyntaxTrivia newLineTrivia = FormattingHelper.GetEndOfLineForCodeFix(firstToken, sourceText, options);
142+
string newLineText = newLineTrivia.ToFullString();
139143

140144
// Examine second line to see if we should have stars or not if it's blank
141145
// set the interline padding to be blank also.
@@ -213,7 +217,7 @@ private static SyntaxNode ReplaceWellFormedMultiLineCommentHeader(Document docum
213217
return root.WithLeadingTrivia(trivia.Replace(commentTrivia, newTrivia));
214218
}
215219

216-
private static SyntaxNode ReplaceHeader(Document document, SyntaxNode root, StyleCopSettings settings, bool isMalformedHeader)
220+
private static SyntaxNode ReplaceHeader(Document document, SourceText sourceText, SyntaxNode root, StyleCopSettings settings, bool isMalformedHeader)
217221
{
218222
// If the header is well formed Xml then we parse out the copyright otherwise
219223
// Skip single line comments, whitespace, and end of line trivia until a blank line is encountered.
@@ -312,8 +316,10 @@ private static SyntaxNode ReplaceHeader(Document document, SyntaxNode root, Styl
312316
trivia = trivia.RemoveAt(removalList[i]);
313317
}
314318

315-
string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
316-
var newLineTrivia = SyntaxFactory.EndOfLine(newLineText);
319+
var options = document.Project.Solution.Workspace.Options;
320+
var firstToken = root.GetFirstToken(includeZeroWidth: true);
321+
SyntaxTrivia newLineTrivia = FormattingHelper.GetEndOfLineForCodeFix(firstToken, sourceText, options);
322+
string newLineText = newLineTrivia.ToFullString();
317323

318324
var newHeaderTrivia = CreateNewHeader(leadingSpaces + "//", GetFileName(document), settings, newLineText);
319325
if (!isMalformedHeader && copyrightTriviaIndex.HasValue)
@@ -356,10 +362,12 @@ private static bool FirstLineIsComment(SyntaxTriviaList trivia)
356362
return false;
357363
}
358364

359-
private static SyntaxNode AddHeader(Document document, SyntaxNode root, string name, StyleCopSettings settings)
365+
private static SyntaxNode AddHeader(Document document, SourceText sourceText, SyntaxNode root, string name, StyleCopSettings settings)
360366
{
361-
string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
362-
var newLineTrivia = SyntaxFactory.EndOfLine(newLineText);
367+
var options = document.Project.Solution.Workspace.Options;
368+
var firstToken = root.GetFirstToken(includeZeroWidth: true);
369+
SyntaxTrivia newLineTrivia = FormattingHelper.GetEndOfLineForCodeFix(firstToken, sourceText, options);
370+
string newLineText = newLineTrivia.ToFullString();
363371
var newTrivia = CreateNewHeader("//", name, settings, newLineText).Add(newLineTrivia).Add(newLineTrivia);
364372

365373
// Skip blank lines already at the beginning of the document, since we add our own

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1623UnitTests.cs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading.Tasks;
88
using Microsoft.CodeAnalysis.Testing;
99
using StyleCop.Analyzers.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
1011
using Xunit;
1112
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
1213
StyleCop.Analyzers.DocumentationRules.PropertySummaryDocumentationAnalyzer,
@@ -201,22 +202,24 @@ public int Property3
201202
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
202203
}
203204

204-
[Fact]
205+
[Theory]
206+
[InlineData("\n")]
207+
[InlineData("\r\n")]
205208
[WorkItem(1934, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1934")]
206-
public async Task SummaryInParagraphCanBeFixedAsync()
209+
public async Task SummaryInParagraphCanBeFixedAsync(string lineEnding)
207210
{
208211
var testCode = @"public class ClassName
209212
{
210213
/// <summary><para>Gets the first test value.</para></summary>
211-
public int Property1
214+
public int {|#0:Property1|}
212215
{
213216
set { }
214217
}
215218
216219
/// <summary>
217220
/// <para>Gets the second test value.</para>
218221
/// </summary>
219-
public int Property2
222+
public int {|#1:Property2|}
220223
{
221224
set { }
222225
}
@@ -226,11 +229,11 @@ public int Property2
226229
/// Gets the third test value.
227230
/// </para>
228231
/// </summary>
229-
public int Property3
232+
public int {|#2:Property3|}
230233
{
231234
set { }
232235
}
233-
}";
236+
}".ReplaceLineEndings(lineEnding);
234237
var fixedTestCode = @"public class ClassName
235238
{
236239
/// <summary><para>Sets the first test value.</para></summary>
@@ -256,13 +259,13 @@ public int Property3
256259
{
257260
set { }
258261
}
259-
}";
262+
}".ReplaceLineEndings(lineEnding);
260263

261264
DiagnosticResult[] expected =
262265
{
263-
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(4, 16).WithArguments("Sets"),
264-
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(12, 16).WithArguments("Sets"),
265-
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(22, 16).WithArguments("Sets"),
266+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(0).WithArguments("Sets"),
267+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(1).WithArguments("Sets"),
268+
Diagnostic(PropertySummaryDocumentationAnalyzer.SA1623Descriptor).WithLocation(2).WithArguments("Sets"),
266269
};
267270
await VerifyCSharpFixAsync(testCode, expected, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
268271
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1626UnitTests.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.DocumentationRules
75
{
86
using System.Threading;
97
using System.Threading.Tasks;
108
using Microsoft.CodeAnalysis.Testing;
9+
using StyleCop.Analyzers.Test.Helpers;
1110
using Xunit;
1211
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
1312
StyleCop.Analyzers.DocumentationRules.SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes,
@@ -45,27 +44,29 @@ public void Bar()
4544
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
4645
}
4746

48-
[Fact]
49-
public async Task TestMethodWithOneLineThreeSlashCommentAsync()
47+
[Theory]
48+
[InlineData("\n")]
49+
[InlineData("\r\n")]
50+
public async Task TestMethodWithOneLineThreeSlashCommentAsync(string lineEnding)
5051
{
5152
var testCode = @"public class TypeName
5253
{
5354
public void Bar()
5455
{
55-
/// This is a comment
56+
{|#0:///|} This is a comment
5657
}
5758
}
58-
";
59+
".ReplaceLineEndings(lineEnding);
5960
var fixedCode = @"public class TypeName
6061
{
6162
public void Bar()
6263
{
6364
// This is a comment
6465
}
6566
}
66-
";
67+
".ReplaceLineEndings(lineEnding);
6768

68-
DiagnosticResult expected = Diagnostic().WithLocation(5, 9);
69+
DiagnosticResult expected = Diagnostic().WithLocation(0);
6970
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
7071
}
7172

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
77
using System.Threading.Tasks;
88
using Microsoft.CodeAnalysis.Testing;
99
using StyleCop.Analyzers.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
1011
using Xunit;
1112

1213
/// <summary>
@@ -184,16 +185,19 @@ namespace Bar
184185
/// <summary>
185186
/// Verifies that a file without a header, but with leading trivia will produce the correct diagnostic message.
186187
/// </summary>
188+
/// <param name="lineEnding">The line ending to use in the test code.</param>
187189
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
188-
[Fact]
189-
public async Task TestMissingFileHeaderWithLeadingTriviaAsync()
190+
[Theory]
191+
[InlineData("\n")]
192+
[InlineData("\r\n")]
193+
public async Task TestMissingFileHeaderWithLeadingTriviaAsync(string lineEnding)
190194
{
191-
var testCode = @"#define MYDEFINE
195+
var testCode = @"{|#0:|}#define MYDEFINE
192196
193197
namespace Foo
194198
{
195199
}
196-
";
200+
".ReplaceLineEndings(lineEnding);
197201
var fixedCode = @"// <copyright file=""Test0.cs"" company=""FooCorp"">
198202
// Copyright (c) FooCorp. All rights reserved.
199203
// </copyright>
@@ -203,9 +207,9 @@ namespace Foo
203207
namespace Foo
204208
{
205209
}
206-
";
210+
".ReplaceLineEndings(lineEnding);
207211

208-
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1633DescriptorMissing).WithLocation(1, 1);
212+
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1633DescriptorMissing).WithLocation(0);
209213
await this.VerifyCSharpFixAsync(testCode, expectedDiagnostic, fixedCode, CancellationToken.None).ConfigureAwait(false);
210214
}
211215

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1634UnitTests.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.DocumentationRules
75
{
86
using System.Threading;
97
using System.Threading.Tasks;
108
using Microsoft.CodeAnalysis.Testing;
119
using StyleCop.Analyzers.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Helpers;
1211
using Xunit;
1312

1413
/// <summary>
1514
/// Unit tests for the SA1634 diagnostic.
1615
/// </summary>
1716
public class SA1634UnitTests : FileHeaderTestBase
1817
{
19-
private string multiLineSettings;
18+
private string? multiLineSettings;
2019

2120
/// <summary>
2221
/// Verifies that a file header without a copyright element will produce the expected diagnostic (none for the default case).
2322
/// </summary>
23+
/// <param name="lineEnding">The line ending to use in the test code.</param>
2424
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
25-
[Fact]
26-
public async Task TestFileHeaderWithMissingCopyrightTagAsync()
25+
[Theory]
26+
[InlineData("\n")]
27+
[InlineData("\r\n")]
28+
public async Task TestFileHeaderWithMissingCopyrightTagAsync(string lineEnding)
2729
{
28-
var testCode = @"// <author>
30+
var testCode = @"{|#0://|} <author>
2931
// John Doe
3032
// </author>
3133
// <summary>This is a test file.</summary>
3234
3335
namespace Bar
3436
{
3537
}
36-
";
38+
".ReplaceLineEndings(lineEnding);
3739
var fixedCode = @"// <copyright file=""Test0.cs"" company=""FooCorp"">
3840
// Copyright (c) FooCorp. All rights reserved.
3941
// </copyright>
@@ -45,9 +47,9 @@ namespace Bar
4547
namespace Bar
4648
{
4749
}
48-
";
50+
".ReplaceLineEndings(lineEnding);
4951

50-
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1634Descriptor).WithLocation(1, 1);
52+
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1634Descriptor).WithLocation(0);
5153
await this.VerifyCSharpFixAsync(testCode, expectedDiagnostic, fixedCode, CancellationToken.None).ConfigureAwait(false);
5254
}
5355

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1635UnitTests.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.DocumentationRules
75
{
86
using System.Threading;
97
using System.Threading.Tasks;
108
using StyleCop.Analyzers.DocumentationRules;
9+
using StyleCop.Analyzers.Test.Helpers;
1110
using Xunit;
1211

1312
/// <summary>
@@ -18,26 +17,29 @@ public class SA1635UnitTests : FileHeaderTestBase
1817
/// <summary>
1918
/// Verifies that a file header with a copyright element in short hand notation will produce the expected diagnostic message.
2019
/// </summary>
20+
/// <param name="lineEnding">The line ending to use in the test code.</param>
2121
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
22-
[Fact]
23-
public async Task TestFileHeaderWithShorthandCopyrightAsync()
22+
[Theory]
23+
[InlineData("\n")]
24+
[InlineData("\r\n")]
25+
public async Task TestFileHeaderWithShorthandCopyrightAsync(string lineEnding)
2426
{
25-
var testCode = @"// <copyright file=""Test0.cs"" company=""FooCorp""/>
27+
var testCode = @"// {|#0:<copyright|} file=""Test0.cs"" company=""FooCorp""/>
2628
2729
namespace Bar
2830
{
2931
}
30-
";
32+
".ReplaceLineEndings(lineEnding);
3133
var fixedCode = @"// <copyright file=""Test0.cs"" company=""FooCorp"">
3234
// Copyright (c) FooCorp. All rights reserved.
3335
// </copyright>
3436
3537
namespace Bar
3638
{
3739
}
38-
";
40+
".ReplaceLineEndings(lineEnding);
3941

40-
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(1, 4);
42+
var expectedDiagnostic = Diagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(0);
4143
await this.VerifyCSharpFixAsync(testCode, expectedDiagnostic, fixedCode, CancellationToken.None).ConfigureAwait(false);
4244
}
4345

0 commit comments

Comments
 (0)