Skip to content

Commit b4fa9f4

Browse files
committed
Fix duplicate decoration in fix for SA1638
When running the fix for SA1638, the header decoration is duplicated
1 parent cc191fe commit b4fa9f4

2 files changed

Lines changed: 95 additions & 0 deletions

File tree

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace StyleCop.Analyzers.DocumentationRules
55
{
66
using System;
7+
using System.Collections.Generic;
78
using System.Collections.Immutable;
89
using System.Composition;
910
using System.Text;
@@ -212,6 +213,9 @@ private static SyntaxNode ReplaceHeader(Document document, SyntaxNode root, Styl
212213
var leadingSpaces = string.Empty;
213214
string possibleLeadingSpaces = string.Empty;
214215

216+
// remove header decoration lines, they will be re-generated
217+
trivia = RemoveHeaderDecorationLines(trivia, settings);
218+
215219
// Need to do this with index so we get the line endings correct.
216220
for (int i = 0; i < trivia.Count; i++)
217221
{
@@ -386,5 +390,35 @@ private static string GetCopyrightText(string prefixWithLeadingSpaces, string co
386390
copyrightText = copyrightText.Replace("\r\n", "\n");
387391
return string.Join(newLineText + prefixWithLeadingSpaces + " ", copyrightText.Split('\n')).Replace(prefixWithLeadingSpaces + " " + newLineText, prefixWithLeadingSpaces + newLineText);
388392
}
393+
394+
private static SyntaxTriviaList RemoveHeaderDecorationLines(SyntaxTriviaList trivia, StyleCopSettings settings)
395+
{
396+
if (!string.IsNullOrEmpty(settings.DocumentationRules.HeaderDecoration))
397+
{
398+
var decorationRemovalList = new List<int>();
399+
for (int i = 0; i < trivia.Count; i++)
400+
{
401+
var triviaLine = trivia[i];
402+
if (triviaLine.Kind() == SyntaxKind.SingleLineCommentTrivia && triviaLine.ToFullString().Contains(settings.DocumentationRules.HeaderDecoration))
403+
{
404+
decorationRemovalList.Add(i);
405+
406+
// also remove the line break
407+
if (i + 1 < trivia.Count && trivia[i + 1].Kind() == SyntaxKind.EndOfLineTrivia)
408+
{
409+
decorationRemovalList.Add(i + 1);
410+
}
411+
}
412+
}
413+
414+
// Remove decoration lines in reverse order.
415+
for (int i = decorationRemovalList.Count - 1; i >= 0; i--)
416+
{
417+
trivia = trivia.RemoveAt(decorationRemovalList[i]);
418+
}
419+
}
420+
421+
return trivia;
422+
}
389423
}
390424
}

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1638UnitTests.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
1414
/// </summary>
1515
public class SA1638UnitTests : FileHeaderTestBase
1616
{
17+
private const string DecoratedXmlMultiLineHeaderTestSettings = @"
18+
{
19+
""settings"": {
20+
""documentationRules"": {
21+
""companyName"": ""FooCorp"",
22+
""copyrightText"": "" Copyright (c) {companyName}. All rights reserved."",
23+
""headerDecoration"": ""-----------------------------------------------------------------------"",
24+
}
25+
}
26+
}
27+
";
28+
29+
private bool useDecoratedXmlMultiLineHeaderTestSettings;
30+
1731
/// <summary>
1832
/// Verifies that a file header with a mismatching file attribute in the copyright element will produce the expected diagnostic message.
1933
/// </summary>
@@ -44,9 +58,56 @@ namespace Bar
4458
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
4559
}
4660

61+
/// <summary>
62+
/// Verifies that a file header with a mismatching file attribute in the copyright element will produce the expected diagnostic message.
63+
/// </summary>
64+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
65+
[Fact]
66+
public async Task TestCopyrightElementWithMismatchingFileAttributeAndDecorationAsync()
67+
{
68+
var testCode = @"// -----------------------------------------------------------------------
69+
// <copyright file=""wrongfile.cs"" company=""FooCorp"">
70+
// Copyright (c) FooCorp. All rights reserved.
71+
// </copyright>
72+
// -----------------------------------------------------------------------
73+
74+
namespace Bar
75+
{
76+
}
77+
";
78+
var fixedCode = @"// -----------------------------------------------------------------------
79+
// <copyright file=""Test0.cs"" company=""FooCorp"">
80+
// Copyright (c) FooCorp. All rights reserved.
81+
// </copyright>
82+
// -----------------------------------------------------------------------
83+
84+
namespace Bar
85+
{
86+
}
87+
";
88+
89+
this.useDecoratedXmlMultiLineHeaderTestSettings = true;
90+
91+
var expectedDiagnostic = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1638Descriptor).WithLocation(2, 4);
92+
await this.VerifyCSharpDiagnosticAsync(testCode, expectedDiagnostic, CancellationToken.None).ConfigureAwait(false);
93+
await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
94+
await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
95+
}
96+
4797
protected override CodeFixProvider GetCSharpCodeFixProvider()
4898
{
4999
return new FileHeaderCodeFixProvider();
50100
}
101+
102+
/// <inheritdoc/>
103+
protected override string GetSettings()
104+
{
105+
if (this.useDecoratedXmlMultiLineHeaderTestSettings)
106+
{
107+
return DecoratedXmlMultiLineHeaderTestSettings;
108+
}
109+
110+
return base.GetSettings();
111+
}
51112
}
52113
}

0 commit comments

Comments
 (0)