From 88800252ea7f25bc79977522327050e0fc912215 Mon Sep 17 00:00:00 2001 From: zitzltho Date: Thu, 18 Feb 2016 11:34:15 +0100 Subject: [PATCH 1/5] add file header decoration comments if configured --- .../FileHeaderCodeFixProvider.cs | 12 +++++++++++- .../Settings/ObjectModel/DocumentationSettings.cs | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs index 9d1877774..146f6f109 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs @@ -365,10 +365,20 @@ private static string WrapInXmlComment(string prefixWithLeadingSpaces, string co string encodedCompanyName = new XAttribute("t", settings.DocumentationRules.CompanyName).ToString().Substring(2).Trim('"'); string encodedCopyrightText = new XText(copyrightText).ToString(); - return + string copyrightString = $"{prefixWithLeadingSpaces} " + newLineText + encodedCopyrightText + newLineText + prefixWithLeadingSpaces + " "; + + if (!string.IsNullOrEmpty(settings.DocumentationRules.HeaderDecoration)) + { + return + $"{prefixWithLeadingSpaces} {settings.DocumentationRules.HeaderDecoration}" + newLineText + + copyrightString + newLineText + + $"{prefixWithLeadingSpaces} {settings.DocumentationRules.HeaderDecoration}"; + } + + return copyrightString; } private static string GetCopyrightText(string prefixWithLeadingSpaces, string copyrightText, string newLineText) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/DocumentationSettings.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/DocumentationSettings.cs index 23cdfe4b6..2433e1dde 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/DocumentationSettings.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/ObjectModel/DocumentationSettings.cs @@ -32,6 +32,12 @@ internal class DocumentationSettings [JsonProperty("copyrightText", DefaultValueHandling = DefaultValueHandling.Ignore)] private string copyrightText; + /// + /// This is the backing field for the property. + /// + [JsonProperty("headerDecoration", DefaultValueHandling = DefaultValueHandling.Ignore)] + private string headerDecoration; + /// /// This is the cache for the property. /// @@ -94,6 +100,7 @@ protected internal DocumentationSettings() this.companyName = DefaultCompanyName; this.copyrightText = DefaultCopyrightText; this.variables = ImmutableDictionary.Empty.ToBuilder(); + this.headerDecoration = null; this.xmlHeader = true; this.documentExposedElements = true; @@ -126,6 +133,14 @@ public string CopyrightText } } + public string HeaderDecoration + { + get + { + return this.headerDecoration; + } + } + public ImmutableDictionary Variables { get From f471f873210449d0dc288763e6d789fffaffadb6 Mon Sep 17 00:00:00 2001 From: zitzltho Date: Wed, 9 Mar 2016 21:11:01 +0100 Subject: [PATCH 2/5] added test for file header decoration --- .../DocumentationRules/SA1633UnitTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs index 33a5b1629..c878fd5f5 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs @@ -24,10 +24,24 @@ public class SA1633UnitTests : FileHeaderTestBase } } } +"; + + private const string DecoratedXmlMultiLineHeaderTestSettings = @" +{ + ""settings"": { + ""documentationRules"": { + ""companyName"": ""FooCorp"", + ""copyrightText"": "" Copyright (c) {companyName}. All rights reserved."", + ""headerDecoration"": ""-----------------------------------------------------------------------"", + } + } +} "; private bool useNoXmlSettings; + private bool useDecoratedXmlMultiLineHeaderTestSettings; + /// /// Verifies that the analyzer will report for /// projects using XML headers (the default) when the file is completely missing a header. @@ -167,6 +181,36 @@ namespace Foo await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false); } + /// + /// Verifies that a file without a header, but with leading trivia will produce the correct diagnostic message. + /// + /// A representing the asynchronous unit test. + [Fact] + public async Task TestMissingFileHeaderWithDecorationAsync() + { + var testCode = @"namespace Foo +{ +} +"; + var fixedCode = @"// ----------------------------------------------------------------------- +// +// Copyright (c) FooCorp. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace Foo +{ +} +"; + + this.useDecoratedXmlMultiLineHeaderTestSettings = true; + + 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).ConfigureAwait(false); + } + /// /// Verifies that a file header without XML structure will produce the correct diagnostic message. /// @@ -308,6 +352,11 @@ protected override string GetSettings() return NoXmlMultiLineHeaderTestSettings; } + if (this.useDecoratedXmlMultiLineHeaderTestSettings) + { + return DecoratedXmlMultiLineHeaderTestSettings; + } + return base.GetSettings(); } From cc191fee249b7112a065732d3ddc7a9a29aca21b Mon Sep 17 00:00:00 2001 From: zitzltho Date: Mon, 14 Mar 2016 20:29:39 +0100 Subject: [PATCH 3/5] documentation for header decoration --- .../Settings/stylecop.schema.json | 5 ++++ documentation/Configuration.md | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json index d2aa0fb63..a74fa3867 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json @@ -194,6 +194,11 @@ "description": "Determines whether the file header should be wrapped in the StyleCop-standard XML structure.", "default": true }, + "headerDecoration": { + "type": "boolean", + "description": "The text used as decoration for the copyright header comment.", + "default": null + } "fileNamingConvention": { "type": "string", "description": "Specifies the preferred naming convention for files. The default value \"stylecop\" uses the naming convention defined by StyleCop Classic, while \"metadata\" uses a file naming convention that matches the metadata names of types.", diff --git a/documentation/Configuration.md b/documentation/Configuration.md index 6e1dad3d7..a5c8a85ee 100644 --- a/documentation/Configuration.md +++ b/documentation/Configuration.md @@ -324,6 +324,7 @@ The following properties are used to configure copyright headers in StyleCop Ana | `copyrightText` | `"Copyright (c) {companyName}. All rights reserved."` | Specifies the default copyright text which should appear in copyright headers | | `xmlHeader` | **true** | Specifies whether file headers should use standard StyleCop XML format, where the copyright notice is wrapped in a `` element | | `variables` | n/a | Specifies replacement variables which can be referenced in the `copyrightText` value | +| `headerDecoration` | n/a | This value can be set to add a decoration for the header comment so that the headers look similar to the ones generated by the StyleCop classic R# fix | #### Configuring Copyright Text @@ -373,6 +374,35 @@ When the `xmlHeader` property is explicitly set to **false**, StyleCop Analyzers // {copyrightText} ``` +#### Configuring Copyright Text Header Decoration + +The `headerDecoration` property is a string which can contain a string that's used for decorating the generated header so that the headers look similar to the ones generated by the StyleCop classic R# fix. + +The default value for the `headerDecoration` property is empty, so no decoration will be added. +> The header decoration is not checked, it's only used for fixing the header. + +```json +{ + "settings": { + "documentationRules": { + "companyName": "FooCorp", + "copyrightText": "Copyright (c) {companyName}. All rights reserved.", + "headerDecoration": "-----------------------------------------------------------------------" + } + } +} +``` + +With the above configuration, the fix for a file **TypeName.cs** would look like the following header. + +```csharp +// ----------------------------------------------------------------------- +// +// Copyright (c) FooCorp. All rights reserved. +// +// ----------------------------------------------------------------------- +``` + ### Documentation Requirements StyleCop Analyzers includes rules which require developers to document the majority of a code base by default. This requirement can easily overwhelm a team which did not use StyleCop for the entire development process. To help guide developers towards a properly documented code base, several properties are available in **stylecop.json** to progressively increase the documentation requirements. From b4fa9f4554a4a23172acac4c270714d3a3787c3e Mon Sep 17 00:00:00 2001 From: zitzltho Date: Thu, 7 Apr 2016 21:18:20 +0200 Subject: [PATCH 4/5] Fix duplicate decoration in fix for SA1638 When running the fix for SA1638, the header decoration is duplicated --- .../FileHeaderCodeFixProvider.cs | 34 +++++++++++ .../DocumentationRules/SA1638UnitTests.cs | 61 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs index 146f6f109..e6f13b6b4 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/DocumentationRules/FileHeaderCodeFixProvider.cs @@ -4,6 +4,7 @@ namespace StyleCop.Analyzers.DocumentationRules { using System; + using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Text; @@ -212,6 +213,9 @@ private static SyntaxNode ReplaceHeader(Document document, SyntaxNode root, Styl var leadingSpaces = string.Empty; string possibleLeadingSpaces = string.Empty; + // remove header decoration lines, they will be re-generated + trivia = RemoveHeaderDecorationLines(trivia, settings); + // Need to do this with index so we get the line endings correct. for (int i = 0; i < trivia.Count; i++) { @@ -386,5 +390,35 @@ private static string GetCopyrightText(string prefixWithLeadingSpaces, string co copyrightText = copyrightText.Replace("\r\n", "\n"); return string.Join(newLineText + prefixWithLeadingSpaces + " ", copyrightText.Split('\n')).Replace(prefixWithLeadingSpaces + " " + newLineText, prefixWithLeadingSpaces + newLineText); } + + private static SyntaxTriviaList RemoveHeaderDecorationLines(SyntaxTriviaList trivia, StyleCopSettings settings) + { + if (!string.IsNullOrEmpty(settings.DocumentationRules.HeaderDecoration)) + { + var decorationRemovalList = new List(); + for (int i = 0; i < trivia.Count; i++) + { + var triviaLine = trivia[i]; + if (triviaLine.Kind() == SyntaxKind.SingleLineCommentTrivia && triviaLine.ToFullString().Contains(settings.DocumentationRules.HeaderDecoration)) + { + decorationRemovalList.Add(i); + + // also remove the line break + if (i + 1 < trivia.Count && trivia[i + 1].Kind() == SyntaxKind.EndOfLineTrivia) + { + decorationRemovalList.Add(i + 1); + } + } + } + + // Remove decoration lines in reverse order. + for (int i = decorationRemovalList.Count - 1; i >= 0; i--) + { + trivia = trivia.RemoveAt(decorationRemovalList[i]); + } + } + + return trivia; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1638UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1638UnitTests.cs index 5b56ef35a..5a183baeb 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1638UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1638UnitTests.cs @@ -14,6 +14,20 @@ namespace StyleCop.Analyzers.Test.DocumentationRules /// public class SA1638UnitTests : FileHeaderTestBase { + private const string DecoratedXmlMultiLineHeaderTestSettings = @" +{ + ""settings"": { + ""documentationRules"": { + ""companyName"": ""FooCorp"", + ""copyrightText"": "" Copyright (c) {companyName}. All rights reserved."", + ""headerDecoration"": ""-----------------------------------------------------------------------"", + } + } +} +"; + + private bool useDecoratedXmlMultiLineHeaderTestSettings; + /// /// Verifies that a file header with a mismatching file attribute in the copyright element will produce the expected diagnostic message. /// @@ -44,9 +58,56 @@ namespace Bar await this.VerifyCSharpFixAsync(testCode, fixedCode, cancellationToken: CancellationToken.None).ConfigureAwait(false); } + /// + /// Verifies that a file header with a mismatching file attribute in the copyright element will produce the expected diagnostic message. + /// + /// A representing the asynchronous unit test. + [Fact] + public async Task TestCopyrightElementWithMismatchingFileAttributeAndDecorationAsync() + { + var testCode = @"// ----------------------------------------------------------------------- +// +// Copyright (c) FooCorp. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace Bar +{ +} +"; + var fixedCode = @"// ----------------------------------------------------------------------- +// +// Copyright (c) FooCorp. All rights reserved. +// +// ----------------------------------------------------------------------- + +namespace Bar +{ +} +"; + + this.useDecoratedXmlMultiLineHeaderTestSettings = true; + + var expectedDiagnostic = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1638Descriptor).WithLocation(2, 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() { return new FileHeaderCodeFixProvider(); } + + /// + protected override string GetSettings() + { + if (this.useDecoratedXmlMultiLineHeaderTestSettings) + { + return DecoratedXmlMultiLineHeaderTestSettings; + } + + return base.GetSettings(); + } } } From b63e425977df9ca71a030843952c98877f0e164d Mon Sep 17 00:00:00 2001 From: zitzltho Date: Fri, 8 Apr 2016 22:04:32 +0200 Subject: [PATCH 5/5] Fix stylecop.schema.json --- .../StyleCop.Analyzers/Settings/stylecop.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json index a74fa3867..1d2cb335c 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json @@ -195,7 +195,7 @@ "default": true }, "headerDecoration": { - "type": "boolean", + "type": "string", "description": "The text used as decoration for the copyright header comment.", "default": null }