Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ Please ADD ALL Changes to the UNRELEASED SECTION and not a specific release

## [Unreleased]
### Added
- FFS0049 rule to flag unpermitted SuppressMessage attributes
- Allow NX0001 (Nullable.Extended.Analyzer) suppressions anywhere in FFS0049
- Allow CC0091, CA1822, and FFS0012 suppressions on Benchmark methods and classes
- FunFair.CodeAnalysis.Benchmark.Tests: Benchmark project for SuppressMessageDiagnosticsAnalyzer (FFS0049)
### Fixed
- Use Ordinal (case-sensitive) comparison for checkId prefix matching in FFS0049
### Changed
- Dependencies - Updated FunFair.Test.Common to 6.2.23.2204
- Dependencies - Updated FunFair.Test.Source.Generator to 6.2.23.2204
- Dependencies - Updated Meziantou.Analyzer to 3.0.78
- Solution - Added .editorconfig and .globalconfig to solution; removed CodeAnalysis.ruleset
### Removed
### Deployment Changes

Expand Down Expand Up @@ -1212,4 +1218,4 @@ Releases that have at least been deployed to staging, BUT NOT necessarily releas

## [1.0.0] - 2019-12-18
### Added
- Banned DateTime.Now, DateTime.UtcNow, DateTime.Today, DateTimeOffset.Now and DateTimeOffset.UtcNow pointing to use DateTimeSource instead
- Banned DateTime.Now, DateTime.UtcNow, DateTime.Today, DateTimeOffset.Now and DateTimeOffset.UtcNow pointing to use DateTimeSource instead
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace FunFair.CodeAnalysis.Benchmark.Tests.Bench;

[SimpleJob]
[MinColumn]
[MaxColumn]
[MeanColumn]
[MedianColumn]
[MemoryDiagnoser(false)]
[SuppressMessage(category: "FunFair.CodeAnalysis", checkId: "FFS0012:Make Sealed", Justification = "Benchmarks")]
public class SuppressMessageAnalyzerBenchmark
{
private static readonly CancellationToken BenchmarkCancellationToken = new(canceled: false);

private static readonly string? AssemblyDirectory = Path.GetDirectoryName(typeof(object).Assembly.Location);

private static readonly ImmutableArray<MetadataReference> References =
[
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(AssemblyDirectory ?? string.Empty, path2: "System.Runtime.dll")),
MetadataReference.CreateFromFile(Path.Combine(AssemblyDirectory ?? string.Empty, path2: "System.dll")),
];

private CompilationWithAnalyzers? _allowedSuppression;
private CompilationWithAnalyzers? _disallowedSuppression;
private CompilationWithAnalyzers? _noSuppression;

[GlobalSetup]
public void Setup()
{
this._noSuppression = BuildCompilationWithAnalyzers(
source: """
public sealed class Example
{
public void Method() { }
}
"""
);

this._allowedSuppression = BuildCompilationWithAnalyzers(
source: """
using System.Diagnostics.CodeAnalysis;

public sealed class Example
{
[SuppressMessage("Nullable.Extended.Analyzer", "NX0001: Suppression of NullForgiving operator is not required", Justification = "Required here")]
public void Method() { }
}
"""
);

this._disallowedSuppression = BuildCompilationWithAnalyzers(
source: """
using System.Diagnostics.CodeAnalysis;

public sealed class Example
{
[SuppressMessage("Example", "EX0001: Some check", Justification = "Because")]
public void Method() { }
}
"""
);
}

[Benchmark]
public Task<ImmutableArray<Diagnostic>> NoSuppressionAsync()
{
return GetOrThrow(this._noSuppression).GetAnalyzerDiagnosticsAsync(BenchmarkCancellationToken);
}

[Benchmark]
public Task<ImmutableArray<Diagnostic>> AllowedSuppressionAsync()
{
return GetOrThrow(this._allowedSuppression).GetAnalyzerDiagnosticsAsync(BenchmarkCancellationToken);
}

[Benchmark]
public Task<ImmutableArray<Diagnostic>> DisallowedSuppressionAsync()
{
return GetOrThrow(this._disallowedSuppression).GetAnalyzerDiagnosticsAsync(BenchmarkCancellationToken);
}

private static CompilationWithAnalyzers GetOrThrow(CompilationWithAnalyzers? value)
{
return value ?? throw new InvalidOperationException("Benchmark not initialised. Call Setup() first.");
}

private static CompilationWithAnalyzers BuildCompilationWithAnalyzers(string source)
{
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName: "BenchmarkSource",
syntaxTrees: [CSharpSyntaxTree.ParseText(text: source, cancellationToken: BenchmarkCancellationToken)],
references: References,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
);

return compilation.WithAnalyzers([new SuppressMessageDiagnosticsAnalyzer()]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<DebuggerSupport>true</DebuggerSupport>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
<EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>true</EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<EnforceExtendedAnalyzerRules>false</EnforceExtendedAnalyzerRules>
<Features>strict;flow-analysis</Features>
<GenerateNeutralResourcesLanguageAttribute>true</GenerateNeutralResourcesLanguageAttribute>
<IlcGenerateStackTraceData>false</IlcGenerateStackTraceData>
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
<ImplicitUsings>disable</ImplicitUsings>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
<IsTrimmable>true</IsTrimmable>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
<LangVersion>latest</LangVersion>
<NoWarn />
<NuGetAudit>true</NuGetAudit>
<NuGetAuditLevel>high</NuGetAuditLevel>
<NuGetAuditMode>all</NuGetAuditMode>
<Nullable>enable</Nullable>
<OptimizationPreference>speed</OptimizationPreference>
<OutputType>Exe</OutputType>
<RunAOTCompilation>false</RunAOTCompilation>
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
<TieredCompilation>true</TieredCompilation>
<TieredPGO>true</TieredPGO>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
<WarningsAsErrors />
</PropertyGroup>
<Import Project="$(SolutionDir)UnitTests.props" Condition="Exists('$(SolutionDir)UnitTests.props')" />
<ItemGroup>
<ProjectReference Include="..\FunFair.CodeAnalysis\FunFair.CodeAnalysis.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FunFair.Test.Common" Version="6.2.23.2204" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.3.0" />
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AsyncFixer" Version="2.1.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="CSharpIsNullAnalyzer" Version="0.1.593" PrivateAssets="all" ExcludeAssets="runtime" />
<PackageReference Include="DisableDateTimeNow" Version="1.0.5883.39470" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="FunFair.Test.Source.Generator" Version="6.2.23.2204" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Meziantou.Analyzer" Version="3.0.78" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Nullable.Extended.Analyzer" Version="1.15.6581" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Philips.CodeAnalysis.DuplicateCodeAnalyzer" Version="2.0.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Philips.CodeAnalysis.MaintainabilityAnalyzers" Version="2.0.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="Roslynator.Analyzers" Version="4.15.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SecurityCodeScan.VS2019" Version="5.6.7" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SmartAnalyzers.CSharpExtensions.Annotations" Version="4.2.11" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.25.0.139117" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="SourceLink.Create.CommandLine" Version="2.8.3" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="ToStringWithoutOverrideAnalyzer" Version="0.6.0" PrivateAssets="All" ExcludeAssets="runtime" />
<PackageReference Include="xunit.analyzers" Version="1.27.0" PrivateAssets="All" ExcludeAssets="runtime" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Reports;
using FunFair.CodeAnalysis.Benchmark.Tests.Bench;
using FunFair.Test.Common;
using Xunit;

namespace FunFair.CodeAnalysis.Benchmark.Tests;

public sealed class SuppressMessageAnalyzerBenchmarkTests : LoggingTestBase
{
public SuppressMessageAnalyzerBenchmarkTests(ITestOutputHelper output)
: base(output) { }

[Fact]
public void Run_Benchmarks()
{
(Summary summary, AccumulationLogger logger) = Benchmark<SuppressMessageAnalyzerBenchmark>();

this.Output.WriteLine(logger.GetLog());

// Baseline measured on net9.0 Release: NoSuppression=560B, AllowedSuppression=560B, DisallowedSuppression=592B
summary.AssertAllocationsAtMost(benchmarkName: "NoSuppressionAsync", maximumBytes: 1024);
summary.AssertAllocationsAtMost(benchmarkName: "AllowedSuppressionAsync", maximumBytes: 1024);
summary.AssertAllocationsAtMost(benchmarkName: "DisallowedSuppressionAsync", maximumBytes: 1024);
}
}
Loading
Loading