Skip to content

Commit

Permalink
[GH-87] Added convention to enforce a project file treats a given com…
Browse files Browse the repository at this point in the history
…piler warning as an error
  • Loading branch information
eddie.stanley committed Jan 6, 2024
1 parent be5b0b6 commit 62e51a9
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -336,5 +336,61 @@ public void MustSetPropertyValue_NoValues_Failure()
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("SdkClassLibrary1 should have property ThisPropertyShouldNeverEverExist with value x");
}

[Theory]
[TestCase("CS0162")]
[TestCase("CS4014")]
public void MustTreatWarningAsError_MultipleWarnings_Success(string warning)
{
var result = TheAssembly
.WithNameMatching("TestProjectTwo")
.MustConformTo(Convention.MustTreatWarningAsError(warning));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustTreatWarningAsError_SingleWarning_Success()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustTreatWarningAsError("CS0162"));

result.IsSatisfied.Should().BeTrue();
}

[Test]
public void MustTreatWarningAsError_MultipleWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("TestProjectTwo")
.MustConformTo(Convention.MustTreatWarningAsError("XX9999"));

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly TestProjectTwo should treat warning XX9999 as an error but does not");
}

[Test]
public void MustTreatWarningAsError_SingleWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("SdkClassLibrary1")
.MustConformTo(Convention.MustTreatWarningAsError("XX9999"));

result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly SdkClassLibrary1 should treat warning XX9999 as an error but does not");
}

[Test]
public void MustTreatWarningAsError_NoWarnings_Failure()
{
var result = TheAssembly
.WithNameMatching("TestSolution.TestProject")
.MustConformTo(Convention.MustTreatWarningAsError("CS0162"));

// TestSolution.TestProject does not set the WarningsAsErrors property, at time of writing
result.IsSatisfied.Should().BeFalse();
result.Failures.Single().Should().Be("Assembly TestSolution.TestProject should treat warning CS0162 as an error but does not");
}
}
}
10 changes: 10 additions & 0 deletions src/Core/Conventional/Convention.Assembly.cs
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,15 @@ public static MustSetPropertyValueAssemblyConventionSpecification MustSetPropert
{
return new MustSetPropertyValueAssemblyConventionSpecification(propertyName, value);
}

/// <summary>
/// Require this project to treat the specified compiler warning as an error
/// </summary>
/// <param name="warning">The warning code to treat as fatal (e.g. CS0162)</param>
/// <remarks>This convention is unaware of shared build prop files (Directory.Build.Props + Directory.Build.Targets) - see https://github.com/andrewabest/Conventional/issues/88</remarks>
public static MustTreatWarningAsErrorAssemblyConventionSpecification MustTreatWarningAsError(string warning)
{
return new MustTreatWarningAsErrorAssemblyConventionSpecification(warning);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Conventional.Conventions.Assemblies
{
public class MustTreatWarningAsErrorAssemblyConventionSpecification : AssemblyConventionSpecification
{
private readonly string _warning;

public MustTreatWarningAsErrorAssemblyConventionSpecification(string warning)
{
_warning = warning;
}

protected override ConventionResult IsSatisfiedByLegacyCsprojFormat(string assemblyName, XDocument projectDocument)
{
return IsSatisfiedBy(assemblyName, projectDocument);
}

protected override ConventionResult IsSatisfiedBy(string assemblyName, XDocument projectDocument)
{
// The Project element (and descendants) are namespaced in legacy csproj files, so our XPath ignores the
// namespace by considering the local element name only. Once we no-longer need to support legacy csproj
// files, the XPath can be simplified to /Project/ItemGroup/ProjectReference
var warningsAsErrors = projectDocument.XPathSelectElements("/*[local-name() = 'Project']/*[local-name() = 'PropertyGroup']/*[local-name() = 'WarningsAsErrors']")
.SingleOrDefault()
?.Value ?? "";

var warnings = warningsAsErrors.Split(new[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);
if (!warnings.Contains(_warning))
{
return ConventionResult.NotSatisfied(assemblyName, string.Format(FailureMessage, assemblyName, _warning));
}

return ConventionResult.Satisfied(assemblyName);
}

protected override string FailureMessage => "Assembly {0} should treat warning {1} as an error but does not";
}
}
1 change: 1 addition & 0 deletions src/Core/TestSolution/TestProjectTwo/TestProjectTwo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<AssemblyName>TestProjectTwo</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<WarningsAsErrors>CS0162,CS4014</WarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>

<PropertyGroup>
<WarningsAsErrors>CS0162</WarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0">
Expand Down

0 comments on commit 62e51a9

Please sign in to comment.