From 7f3b260a18f87f53bef06659d63f2018d2af6a28 Mon Sep 17 00:00:00 2001 From: Nils Andresen Date: Mon, 28 Sep 2020 00:41:37 +0200 Subject: [PATCH] (GH-5) added warning CCG0005 for not referencing StyleCop.Analyzers --- docs/input/guidelines/Analysers.md | 42 +++++++++++ docs/input/rules/ccg0005.md | 35 ++++++++++ e2e-tests/CCG0001/Default/Default.csproj | 5 +- e2e-tests/CCG0002/Default/Default.csproj | 7 ++ e2e-tests/CCG0003/Default/Default.csproj | 7 ++ .../CCG0003/ModifiedPath/ModifiedPath.csproj | 6 ++ e2e-tests/CCG0004/Default/Default.csproj | 4 ++ .../NoPrivateAssets/NoPrivateAssets.csproj | 4 ++ e2e-tests/CCG0005/Default/Default.csproj | 9 +++ e2e-tests/CCG0005/Default/spec.json | 6 ++ .../OmittingStyleCop/OmittingStyleCop.csproj | 12 ++++ e2e-tests/CCG0005/OmittingStyleCop/spec.json | 6 ++ .../build/CakeContrib.Guidelines.targets | 7 +- ...eference.targets => PrivateAssets.targets} | 9 ++- .../build/RequiredReferences.targets | 22 ++++++ src/Tasks.Tests/Fixtures/BaseBuildFixture.cs | 26 +++++++ .../CheckPrivateAssetsOnReferencesFixture.cs | 32 ++++----- .../Fixtures/RequiredReferencesFixture.cs | 57 +++++++++++++++ .../IntegrationTests/SimpleTest.cs | 10 --- src/Tasks.Tests/RequiredReferencesTests.cs | 63 +++++++++++++++++ src/Tasks/RequiredReferences.cs | 70 +++++++++++++++++++ 21 files changed, 400 insertions(+), 39 deletions(-) create mode 100644 docs/input/guidelines/Analysers.md create mode 100644 docs/input/rules/ccg0005.md create mode 100644 e2e-tests/CCG0005/Default/Default.csproj create mode 100644 e2e-tests/CCG0005/Default/spec.json create mode 100644 e2e-tests/CCG0005/OmittingStyleCop/OmittingStyleCop.csproj create mode 100644 e2e-tests/CCG0005/OmittingStyleCop/spec.json rename src/Guidelines/build/{CakeReference.targets => PrivateAssets.targets} (90%) create mode 100644 src/Guidelines/build/RequiredReferences.targets create mode 100644 src/Tasks.Tests/Fixtures/BaseBuildFixture.cs create mode 100644 src/Tasks.Tests/Fixtures/RequiredReferencesFixture.cs delete mode 100644 src/Tasks.Tests/IntegrationTests/SimpleTest.cs create mode 100644 src/Tasks.Tests/RequiredReferencesTests.cs create mode 100644 src/Tasks/RequiredReferences.cs diff --git a/docs/input/guidelines/Analysers.md b/docs/input/guidelines/Analysers.md new file mode 100644 index 0000000..8f9701a --- /dev/null +++ b/docs/input/guidelines/Analysers.md @@ -0,0 +1,42 @@ +--- +Order: 3 +Title: Recommended References +--- + + + +## Table of Contents + +- [Goals](#goals) +- [Related rules](#related-rules) +- [Usage](#usage) +- [Settings](#settings) + - [Opt-Out](#opt-out) + + + +## Goals + +To have consistency in code-style among the different tools/plugins the use of Analysers is recommended, especially the use of [StyleCop](https://github.com/DotNetAnalyzers/StyleCopAnalyzers). + +## Related rules + + * [CCG0005](../rules/ccg0005) + +## Usage + +Using this package automatically enables this guideline. + +## Settings + +### Opt-Out + +It it possible to opt-out of the check for StyleCop using the following setting: + +(*Keep in mind, though that it is not recommended to opt-out of this feature*) + +```xml + + + +``` diff --git a/docs/input/rules/ccg0005.md b/docs/input/rules/ccg0005.md new file mode 100644 index 0000000..7cfb2d2 --- /dev/null +++ b/docs/input/rules/ccg0005.md @@ -0,0 +1,35 @@ +--- +Order: 5 +Title: CCG0005 +Description: Usage of analysers is recommended +--- + + > No reference to `StyleCop.Analyzers` found. Usage of `StyleCop.Analyzers` is strongly recommended. + + + +## Table of Contents + +- [Cause](#cause) +- [Description](#description) +- [How to fix violations](#how-to-fix-violations) +- [Related guidelines](#related-guidelines) + + + +## Cause + +This warning is raised, when the recommended analyser [StyleCop](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) is not referenced. + +## Description + +Usage of [StyleCop](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) is recommended. + +## How to fix violations + +Add a reference to [StyleCop](https://github.com/DotNetAnalyzers/StyleCopAnalyzers). +(Or opt-out of this rule, by setting `CakeContribGuidelinesOmitRecommendedReference`) + +## Related guidelines + +* [Usage of Analysers](../guidelines/Analysers) \ No newline at end of file diff --git a/e2e-tests/CCG0001/Default/Default.csproj b/e2e-tests/CCG0001/Default/Default.csproj index 121c486..4bd4f44 100644 --- a/e2e-tests/CCG0001/Default/Default.csproj +++ b/e2e-tests/CCG0001/Default/Default.csproj @@ -5,7 +5,10 @@ - + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/e2e-tests/CCG0002/Default/Default.csproj b/e2e-tests/CCG0002/Default/Default.csproj index 1973cca..ac8c584 100644 --- a/e2e-tests/CCG0002/Default/Default.csproj +++ b/e2e-tests/CCG0002/Default/Default.csproj @@ -5,4 +5,11 @@ $(CakeContribGuidelinesIconDestinationLocation) + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/e2e-tests/CCG0003/Default/Default.csproj b/e2e-tests/CCG0003/Default/Default.csproj index ac4e6eb..938d76b 100644 --- a/e2e-tests/CCG0003/Default/Default.csproj +++ b/e2e-tests/CCG0003/Default/Default.csproj @@ -6,4 +6,11 @@ https://project/path/to/icon + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + diff --git a/e2e-tests/CCG0003/ModifiedPath/ModifiedPath.csproj b/e2e-tests/CCG0003/ModifiedPath/ModifiedPath.csproj index 289df4a..4506e6d 100644 --- a/e2e-tests/CCG0003/ModifiedPath/ModifiedPath.csproj +++ b/e2e-tests/CCG0003/ModifiedPath/ModifiedPath.csproj @@ -7,4 +7,10 @@ https://project/path/to/icon + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + diff --git a/e2e-tests/CCG0004/Default/Default.csproj b/e2e-tests/CCG0004/Default/Default.csproj index d80cb40..eaace37 100644 --- a/e2e-tests/CCG0004/Default/Default.csproj +++ b/e2e-tests/CCG0004/Default/Default.csproj @@ -8,6 +8,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/e2e-tests/CCG0004/NoPrivateAssets/NoPrivateAssets.csproj b/e2e-tests/CCG0004/NoPrivateAssets/NoPrivateAssets.csproj index b995910..801df75 100644 --- a/e2e-tests/CCG0004/NoPrivateAssets/NoPrivateAssets.csproj +++ b/e2e-tests/CCG0004/NoPrivateAssets/NoPrivateAssets.csproj @@ -8,6 +8,10 @@ + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/e2e-tests/CCG0005/Default/Default.csproj b/e2e-tests/CCG0005/Default/Default.csproj new file mode 100644 index 0000000..b1cabdc --- /dev/null +++ b/e2e-tests/CCG0005/Default/Default.csproj @@ -0,0 +1,9 @@ + + + + netstandard2.0 + $(CakeContribGuidelinesIconDestinationLocation) + https://project/path/to/icon + + + diff --git a/e2e-tests/CCG0005/Default/spec.json b/e2e-tests/CCG0005/Default/spec.json new file mode 100644 index 0000000..a12e46c --- /dev/null +++ b/e2e-tests/CCG0005/Default/spec.json @@ -0,0 +1,6 @@ +{ + "description": "not referencing StyleCop.Analyzers yields a warning", + "exitCode": 0, + "errors": [], + "warnings": [ "CCG0005" ] +} diff --git a/e2e-tests/CCG0005/OmittingStyleCop/OmittingStyleCop.csproj b/e2e-tests/CCG0005/OmittingStyleCop/OmittingStyleCop.csproj new file mode 100644 index 0000000..11f4441 --- /dev/null +++ b/e2e-tests/CCG0005/OmittingStyleCop/OmittingStyleCop.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + $(CakeContribGuidelinesIconDestinationLocation) + https://project/path/to/icon + + + + + + diff --git a/e2e-tests/CCG0005/OmittingStyleCop/spec.json b/e2e-tests/CCG0005/OmittingStyleCop/spec.json new file mode 100644 index 0000000..3ac9f4c --- /dev/null +++ b/e2e-tests/CCG0005/OmittingStyleCop/spec.json @@ -0,0 +1,6 @@ +{ + "description": "omitting StyleCop.Analyzers yields no warning", + "exitCode": 0, + "errors": [], + "warnings": [ ] +} diff --git a/src/Guidelines/build/CakeContrib.Guidelines.targets b/src/Guidelines/build/CakeContrib.Guidelines.targets index 8a44a10..f95c6f0 100644 --- a/src/Guidelines/build/CakeContrib.Guidelines.targets +++ b/src/Guidelines/build/CakeContrib.Guidelines.targets @@ -1,10 +1,11 @@ - - - \ No newline at end of file + + + diff --git a/src/Guidelines/build/CakeReference.targets b/src/Guidelines/build/PrivateAssets.targets similarity index 90% rename from src/Guidelines/build/CakeReference.targets rename to src/Guidelines/build/PrivateAssets.targets index 583a613..aea658b 100644 --- a/src/Guidelines/build/CakeReference.targets +++ b/src/Guidelines/build/PrivateAssets.targets @@ -1,12 +1,12 @@ - + @@ -22,5 +22,4 @@ References="@(PackageReference)" ProjectFile="$(MSBuildProjectFullPath)" /> - diff --git a/src/Guidelines/build/RequiredReferences.targets b/src/Guidelines/build/RequiredReferences.targets new file mode 100644 index 0000000..5423b86 --- /dev/null +++ b/src/Guidelines/build/RequiredReferences.targets @@ -0,0 +1,22 @@ + + + + + + + + + + + + + diff --git a/src/Tasks.Tests/Fixtures/BaseBuildFixture.cs b/src/Tasks.Tests/Fixtures/BaseBuildFixture.cs new file mode 100644 index 0000000..7c910b3 --- /dev/null +++ b/src/Tasks.Tests/Fixtures/BaseBuildFixture.cs @@ -0,0 +1,26 @@ +using Microsoft.Build.Utilities; + +namespace CakeContrib.Guidelines.Tasks.Tests.Fixtures +{ + public class BaseBuildFixture + where T : Task, new() + { + public MockBuildEngine BuildEngine { get; } + + protected T Task { get; } + + public BaseBuildFixture() + { + BuildEngine = new MockBuildEngine(); + Task = new T + { + BuildEngine = BuildEngine + }; + } + + public virtual bool Execute() + { + return Task.Execute(); + } + } +} diff --git a/src/Tasks.Tests/Fixtures/CheckPrivateAssetsOnReferencesFixture.cs b/src/Tasks.Tests/Fixtures/CheckPrivateAssetsOnReferencesFixture.cs index 10dbf32..91e7d60 100644 --- a/src/Tasks.Tests/Fixtures/CheckPrivateAssetsOnReferencesFixture.cs +++ b/src/Tasks.Tests/Fixtures/CheckPrivateAssetsOnReferencesFixture.cs @@ -6,35 +6,22 @@ namespace CakeContrib.Guidelines.Tasks.Tests.Fixtures { - public class CheckPrivateAssetsOnReferencesFixture + public class CheckPrivateAssetsOnReferencesFixture : BaseBuildFixture { - public MockBuildEngine BuildEngine { get; } - - private readonly CheckPrivateAssetsOnReferences task; - private readonly List references; private readonly List packagesToCheck; + private readonly List references; public CheckPrivateAssetsOnReferencesFixture() { - BuildEngine = new MockBuildEngine(); - task = new CheckPrivateAssetsOnReferences - { - BuildEngine = BuildEngine - }; - packagesToCheck = new List(); references = new List(); + packagesToCheck = new List(); } - public bool Execute() - { - task.References = references.ToArray(); - task.PackagesToCheck = packagesToCheck.ToArray(); - return task.Execute(); - } - - public void WithProjectFile(string fileName) + public override bool Execute() { - task.ProjectFile = fileName; + Task.References = references.ToArray(); + Task.PackagesToCheck = packagesToCheck.ToArray(); + return base.Execute(); } public void WithPackageToCheck(string packageName) @@ -44,6 +31,11 @@ public void WithPackageToCheck(string packageName) packagesToCheck.Add(packageToCheck.Object); } + public void WithProjectFile(string fileName) + { + Task.ProjectFile = fileName; + } + public void WithReferencedPackage(string packageName, string privateAssets = "") { var referencedPackage = new Mock(); diff --git a/src/Tasks.Tests/Fixtures/RequiredReferencesFixture.cs b/src/Tasks.Tests/Fixtures/RequiredReferencesFixture.cs new file mode 100644 index 0000000..104a63e --- /dev/null +++ b/src/Tasks.Tests/Fixtures/RequiredReferencesFixture.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; + +using Microsoft.Build.Framework; + +using Moq; + +namespace CakeContrib.Guidelines.Tasks.Tests.Fixtures +{ + public class RequiredReferencesFixture : BaseBuildFixture + { + private readonly List requiredReferences; + private readonly List omittedReferences; + private readonly List references; + + public RequiredReferencesFixture() + { + references = new List(); + requiredReferences = new List(); + omittedReferences = new List(); + } + + public override bool Execute() + { + Task.References = references.ToArray(); + Task.Required = requiredReferences.ToArray(); + Task.Omitted = omittedReferences.ToArray(); + return base.Execute(); + } + + public void WithRequiredReferences(string packageName) + { + var reference = new Mock(); + reference.Setup(x => x.ToString()).Returns(packageName); + requiredReferences.Add(reference.Object); + } + + public void WithOmittedReferences(string packageName) + { + var reference = new Mock(); + reference.Setup(x => x.ToString()).Returns(packageName); + omittedReferences.Add(reference.Object); + } + + public void WithProjectFile(string fileName) + { + Task.ProjectFile = fileName; + } + + public void WithReferencedPackage(string packageName, string privateAssets = "") + { + var referencedPackage = new Mock(); + referencedPackage.Setup(x => x.ToString()).Returns(packageName); + referencedPackage.Setup(x => x.GetMetadata("PrivateAssets")).Returns(privateAssets); + references.Add(referencedPackage.Object); + } + } +} diff --git a/src/Tasks.Tests/IntegrationTests/SimpleTest.cs b/src/Tasks.Tests/IntegrationTests/SimpleTest.cs deleted file mode 100644 index a685a8f..0000000 --- a/src/Tasks.Tests/IntegrationTests/SimpleTest.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace CakeContrib.Guidelines.Tasks.Tests.IntegrationTests -{ - public class SimpleTest - { - } -} diff --git a/src/Tasks.Tests/RequiredReferencesTests.cs b/src/Tasks.Tests/RequiredReferencesTests.cs new file mode 100644 index 0000000..54b98fd --- /dev/null +++ b/src/Tasks.Tests/RequiredReferencesTests.cs @@ -0,0 +1,63 @@ +using System.Linq; + +using CakeContrib.Guidelines.Tasks.Tests.Fixtures; + +using FluentAssertions; + +using Xunit; + +namespace CakeContrib.Guidelines.Tasks.Tests +{ + public class RequiredReferencesTests + { + [Fact] + public void Should_Not_Warn_If_RequiredPackage_Is_Referenced() + { + // given + const string required = "Some.Analyser"; + var fixture = new RequiredReferencesFixture(); + fixture.WithReferencedPackage(required); + fixture.WithRequiredReferences(required); + + // when + fixture.Execute(); + + // then + fixture.BuildEngine.WarningEvents.Should().HaveCount(0); + } + + [Fact] + public void Should_Warn_If_RequiredPackage_Is_Not_Referenced() + { + // given + var fixture = new RequiredReferencesFixture(); + fixture.WithReferencedPackage("Cool.Ref.Project"); + fixture.WithRequiredReferences("Some.Analyser"); + + // when + var actual = fixture.Execute(); + + // then + fixture.BuildEngine.WarningEvents.Should().HaveCount(1); + var theEvent = fixture.BuildEngine.WarningEvents.Single(); + theEvent.Code.Should().Be("CCG0005"); + } + + [Fact] + public void Should_Not_Warn_If_RequiredPackage_Is_Omitted() + { + // given + const string required = "Some.Analyser"; + var fixture = new RequiredReferencesFixture(); + fixture.WithReferencedPackage("Cool.Ref.Project"); + fixture.WithRequiredReferences(required); + fixture.WithOmittedReferences(required); + + // when + var actual = fixture.Execute(); + + // then + fixture.BuildEngine.WarningEvents.Should().HaveCount(0); + } + } +} diff --git a/src/Tasks/RequiredReferences.cs b/src/Tasks/RequiredReferences.cs new file mode 100644 index 0000000..58ee2d1 --- /dev/null +++ b/src/Tasks/RequiredReferences.cs @@ -0,0 +1,70 @@ +using System; +using System.Linq; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace CakeContrib.Guidelines.Tasks +{ + /// + /// The Task for the guideline . + /// + public class RequiredReferences : Task + { + /// + /// Gets or sets the References. + /// + [Required] + public ITaskItem[] References { get; set; } + + /// + /// Gets or sets the Packages to check. + /// + [Required] + public ITaskItem[] Required { get; set; } + + /// + /// Gets or sets the Packages to check. + /// + [Required] + public ITaskItem[] Omitted { get; set; } + + /// + /// Gets or sets the project file. + /// + public string ProjectFile { get; set; } + + /// + public override bool Execute() + { + foreach (var r in Required) + { + if (Omitted.Any(x => x.ToString().Equals(r.ToString(), StringComparison.OrdinalIgnoreCase))) + { + Log.LogMessage(MessageImportance.Low, $"Required reference '{r}' is set to omit."); + continue; + } + + Log.LogMessage(MessageImportance.Low, $"Checking required reference: {r}"); + if (References.Any(x => x.ToString().Equals(r.ToString(), StringComparison.OrdinalIgnoreCase))) + { + // found. + continue; + } + + Log.LogWarning( + null, + "CCG0005", + string.Empty, // TODO: Can we get HelpLink like in roslyn analysers? + ProjectFile ?? string.Empty, + 0, + 0, + 0, + 0, + $"No reference to '{r}' found. Usage of '{r}' is strongly recommended"); + } + + return true; + } + } +}