diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/NoXmlFileHeaderUnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/NoXmlFileHeaderUnitTests.cs
index 04f0ae2ee..056a66615 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/NoXmlFileHeaderUnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/NoXmlFileHeaderUnitTests.cs
@@ -7,7 +7,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
using System.Threading;
using System.Threading.Tasks;
using Analyzers.DocumentationRules;
- using Microsoft.CodeAnalysis;
+ using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using TestHelper;
using Xunit;
@@ -15,7 +15,7 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
///
/// Unit tests for file header that do not follow the XML syntax.
///
- public class NoXmlFileHeaderUnitTests : DiagnosticVerifier
+ public class NoXmlFileHeaderUnitTests : CodeFixVerifier
{
private const string SettingsFileName = "stylecop.json";
private const string TestSettings = @"
@@ -45,10 +45,19 @@ public virtual async Task TestNoFileHeaderAsync()
var testCode = @"namespace Foo
{
}
+";
+ var fixedCode = @"// Copyright (c) FooCorp. All rights reserved.
+// Licensed under the ??? license. See LICENSE file in the project root for full license information.
+
+namespace Foo
+{
+}
";
var expectedDiagnostic = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1633DescriptorMissing).WithLocation(1, 1);
await this.VerifyCSharpDiagnosticAsync(testCode, expectedDiagnostic, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
///
@@ -87,13 +96,13 @@ namespace Bar
}
///
- /// Verifies that a valid file header built using multi line comments will not produce a diagnostic message.
+ /// Verifies that a valid file header built using multi-line comments will not produce a diagnostic message.
///
/// A representing the asynchronous unit test.
[Fact]
- public async Task TestValidFileHeaderWithMultiLineCommentsAsync()
+ public async Task TestValidFileHeaderWithMultiLineComments1Async()
{
- var testCodeFormat1 = @"/* Copyright (c) FooCorp. All rights reserved.
+ var testCode = @"/* Copyright (c) FooCorp. All rights reserved.
* Licensed under the ??? license. See LICENSE file in the project root for full license information.
*/
@@ -102,7 +111,17 @@ namespace Bar
}
";
- var testCodeFormat2 = @"/* Copyright (c) FooCorp. All rights reserved.
+ await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ }
+
+ ///
+ /// Verifies that a valid file header built using multi-line comments will not produce a diagnostic message.
+ ///
+ /// A representing the asynchronous unit test.
+ [Fact]
+ public async Task TestValidFileHeaderWithMultiLineComments2Async()
+ {
+ var testCode = @"/* Copyright (c) FooCorp. All rights reserved.
Licensed under the ??? license. See LICENSE file in the project root for full license information. */
namespace Bar
@@ -110,25 +129,27 @@ namespace Bar
}
";
- await this.VerifyCSharpDiagnosticAsync(testCodeFormat1, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(testCodeFormat2, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}
///
/// Verifies that a file header without text / only whitespace will produce the expected diagnostic message.
///
+ /// The comment text.
/// A representing the asynchronous unit test.
- [Fact]
- public async Task TestInvalidFileHeaderWithoutTextAsync()
+ [Theory]
+ [InlineData("//")]
+ [InlineData("// ")]
+ public async Task TestInvalidFileHeaderWithoutTextAsync(string comment)
{
- var testCodeFormat1 = @"//
+ var testCode = $@"{comment}
namespace Bar
-{
-}
+{{
+}}
";
-
- var testCodeFormat2 = "// " + @"
+ var fixedCode = @"// Copyright (c) FooCorp. All rights reserved.
+// Licensed under the ??? license. See LICENSE file in the project root for full license information.
namespace Bar
{
@@ -136,8 +157,9 @@ namespace Bar
";
var expected = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(1, 1);
- await this.VerifyCSharpDiagnosticAsync(testCodeFormat1, expected, CancellationToken.None).ConfigureAwait(false);
- await this.VerifyCSharpDiagnosticAsync(testCodeFormat2, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
///
@@ -150,12 +172,21 @@ public async Task TestInvalidFileHeaderWithWrongTextAsync()
var testCode = @"// Copyright (c) BarCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
+namespace Bar
+{
+}
+";
+ var fixedCode = @"// Copyright (c) FooCorp. All rights reserved.
+// Licensed under the ??? license. See LICENSE file in the project root for full license information.
+
namespace Bar
{
}
";
var expected = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1636Descriptor).WithLocation(1, 1);
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
///
@@ -169,5 +200,11 @@ protected sealed override IEnumerable GetCSharpDiagnosticAna
{
yield return new FileHeaderAnalyzers();
}
+
+ ///
+ protected override CodeFixProvider GetCSharpCodeFixProvider()
+ {
+ return new FileHeaderCodeFixProvider();
+ }
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1635UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1635UnitTests.cs
index 8d5a8800f..3b6b7683b 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1635UnitTests.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1635UnitTests.cs
@@ -23,6 +23,14 @@ public async Task TestFileHeaderWithShorthandCopyrightAsync()
{
var testCode = @"//
+namespace Bar
+{
+}
+";
+ var fixedCode = @"//
+// Copyright (c) FooCorp. All rights reserved.
+//
+
namespace Bar
{
}
@@ -30,6 +38,8 @@ namespace Bar
var expectedDiagnostic = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(1, 4);
await this.VerifyCSharpDiagnosticAsync(testCode, expectedDiagnostic, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
///
@@ -47,14 +57,25 @@ public async Task TestFileHeaderWithWhitespaceOnlyCopyrightAsync()
"namespace Bar\r\n" +
"{\r\n" +
"}\r\n";
+ string fixedCode =
+ "// \r\n" +
+ "// Copyright (c) FooCorp. All rights reserved.\r\n" +
+ "// \r\n" +
+ "\r\n" +
+ "namespace Bar\r\n" +
+ "{\r\n" +
+ "}\r\n";
var expectedDiagnostic = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(1, 4);
await this.VerifyCSharpDiagnosticAsync(testCode, expectedDiagnostic, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpDiagnosticAsync(fixedCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
+ await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false);
}
+ ///
protected override CodeFixProvider GetCSharpCodeFixProvider()
{
- throw new System.NotImplementedException();
+ return new FileHeaderCodeFixProvider();
}
}
}
diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/FileHeaderCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/FileHeaderCodeFixProvider.cs
index d26728dfd..b73357104 100644
--- a/StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/FileHeaderCodeFixProvider.cs
+++ b/StyleCop.Analyzers/StyleCop.Analyzers/DocumentationRules/FileHeaderCodeFixProvider.cs
@@ -12,6 +12,7 @@ namespace StyleCop.Analyzers.DocumentationRules
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
+ using Microsoft.CodeAnalysis.Formatting;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Settings.ObjectModel;
@@ -29,6 +30,7 @@ public class FileHeaderCodeFixProvider : CodeFixProvider
public override ImmutableArray FixableDiagnosticIds { get; }
= ImmutableArray.Create(
FileHeaderAnalyzers.SA1633DescriptorMissing.Id,
+ FileHeaderAnalyzers.SA1635Descriptor.Id,
FileHeaderAnalyzers.SA1636Descriptor.Id);
///
@@ -54,7 +56,7 @@ private static async Task GetTransformedDocumentAsync(Document documen
var settings = document.Project.AnalyzerOptions.GetStyleCopSettings();
var fileHeader = FileHeaderHelpers.ParseFileHeader(root);
- var newSyntaxRoot = fileHeader.IsMissing ? AddHeader(root, document.Name, settings) : ReplaceHeader(document, root, settings);
+ var newSyntaxRoot = fileHeader.IsMissing ? AddHeader(document, root, document.Name, settings) : ReplaceHeader(document, root, settings);
return document.WithSyntaxRoot(newSyntaxRoot);
}
@@ -104,36 +106,40 @@ private static SyntaxNode ReplaceHeader(Document document, SyntaxNode root, Styl
}
}
- return root.WithLeadingTrivia(CreateNewHeader(document.Name, settings).Add(SyntaxFactory.CarriageReturnLineFeed).Add(SyntaxFactory.CarriageReturnLineFeed).AddRange(trivia));
+ string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
+ return root.WithLeadingTrivia(CreateNewHeader(document.Name, settings, newLineText).Add(SyntaxFactory.CarriageReturnLineFeed).Add(SyntaxFactory.CarriageReturnLineFeed).AddRange(trivia));
}
- private static SyntaxNode AddHeader(SyntaxNode root, string name, StyleCopSettings settings)
+ private static SyntaxNode AddHeader(Document document, SyntaxNode root, string name, StyleCopSettings settings)
{
- var newTrivia = CreateNewHeader(name, settings).Add(SyntaxFactory.CarriageReturnLineFeed).Add(SyntaxFactory.CarriageReturnLineFeed);
+ string newLineText = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.NewLine, LanguageNames.CSharp);
+ var newTrivia = CreateNewHeader(name, settings, newLineText).Add(SyntaxFactory.CarriageReturnLineFeed).Add(SyntaxFactory.CarriageReturnLineFeed);
newTrivia = newTrivia.AddRange(root.GetLeadingTrivia());
return root.WithLeadingTrivia(newTrivia);
}
- private static SyntaxTriviaList CreateNewHeader(string filename, StyleCopSettings settings)
+ private static SyntaxTriviaList CreateNewHeader(string filename, StyleCopSettings settings, string newLineText)
{
- var copyrightText = "// " + GetCopyrightText(settings.DocumentationRules.CopyrightText);
+ var copyrightText = "// " + GetCopyrightText(settings.DocumentationRules.CopyrightText, newLineText);
var newHeader = settings.DocumentationRules.XmlHeader
- ? WrapInXmlComment(copyrightText, filename, settings)
+ ? WrapInXmlComment(copyrightText, filename, settings, newLineText)
: copyrightText;
return SyntaxFactory.ParseLeadingTrivia(newHeader);
}
- private static string WrapInXmlComment(string copyrightText, string filename, StyleCopSettings settings)
+ private static string WrapInXmlComment(string copyrightText, string filename, StyleCopSettings settings, string newLineText)
{
- return $@"//
-{copyrightText}
-// ";
+ return
+ $"// " + newLineText
+ + copyrightText + newLineText
+ + "// ";
}
- private static string GetCopyrightText(string copyrightText)
+ private static string GetCopyrightText(string copyrightText, string newLineText)
{
- return string.Join("\n// ", copyrightText.Split('\n'));
+ copyrightText = copyrightText.Replace("\r\n", "\n");
+ return string.Join(newLineText + "// ", copyrightText.Split('\n'));
}
}
}
\ No newline at end of file