feat(analyzers): enable incremental latest-All enforcement

Centralize SDK and threading analyzers, baseline the .NET 10 All rule inventory at suggestion severity, and promote S3981 repo-wide. Fix the always-true worker count predicate and cover the idle/active branches.

Fixes #15

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
2026-07-16 21:52:27 +02:00
co-authored by Codex
parent 9938d9a6cd
commit 8f90cea5cc
14 changed files with 399 additions and 37 deletions
+9 -5
View File
@@ -106,13 +106,17 @@ ij_json_wrap_long_lines = false
dotnet_diagnostic.ca1848.severity = none
# --- Static-analysis pack adoption (ersatztv#15) ---
# Roslynator / SonarAnalyzer / Meziantou / AsyncFixer are referenced centrally
# (Directory.Build.targets). Default every analyzer diagnostic to `suggestion` so the new
# packs don't fail the TreatWarningsAsErrors build; high-value rules get promoted to
# warning/error one at a time (see ersatztv#15 / docs/contributing.md). Explicit per-rule
# severities (e.g. ca1848 above) still take precedence over this bulk default.
# Threading analyzers and Roslynator / SonarAnalyzer / Meziantou / AsyncFixer are enabled centrally.
# Default their diagnostics to `suggestion`; the SDK's exact per-rule suggestion baseline lives in
# eng/analyzers/sdk-all-suggestion.globalconfig because AnalysisLevel=latest-All otherwise injects
# exact warning severities that outrank this bulk setting. High-value rules are promoted one at a
# time. Explicit per-rule severities (e.g. ca1848 above) take precedence over both baselines.
dotnet_analyzer_diagnostic.severity = suggestion
# A collection count can never be negative. Treat comparisons that therefore collapse to a
# constant as errors; the first promotion caught a busy/idle branch that was permanently busy.
dotnet_diagnostic.S3981.severity = warning
# Blazor components: analyzers run on .razor/.cshtml @code too, and TWAE would otherwise
# turn their default-severity findings into build errors — keep them at suggestion as well.
[*.razor]
+13 -2
View File
@@ -3,6 +3,12 @@
<InformationalVersion>develop</InformationalVersion>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<AllowMissingPrunePackageData>true</AllowMissingPrunePackageData>
<!-- Analyzer posture (ersatztv#15): enable the complete SDK rule set and the
threading analyzer in every project. The checked-in globalconfig keeps the
SDK baseline at suggestion; individually promoted rules become CI-blocking. -->
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest-All</AnalysisLevel>
<EnableThreadingAnalyzers>true</EnableThreadingAnalyzers>
<!-- NuGet audit (on by default in .NET 10) reports vulnerable transitive
packages as NU1901-1904 warnings. Several projects set
TreatWarningsAsErrors=true, which would otherwise fail `dotnet restore`
@@ -10,8 +16,13 @@
advisories to warnings (still printed in build logs); NU1904 (critical)
stays an error so criticals still block. Track fixes separately.
WarningsAsErrors promotes NU1904 in EVERY project (even those without
TreatWarningsAsErrors), so "criticals block" actually holds repo-wide. -->
TreatWarningsAsErrors), so "criticals block" actually holds repo-wide.
S3981 is the first explicitly promoted analyzer rule (ersatztv#15). -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);NU1901;NU1902;NU1903</WarningsNotAsErrors>
<WarningsAsErrors>$(WarningsAsErrors);NU1904</WarningsAsErrors>
<WarningsAsErrors>$(WarningsAsErrors);NU1904;S3981</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<EditorConfigFiles Include="$(MSBuildThisFileDirectory)eng/analyzers/sdk-all-suggestion.globalconfig" />
</ItemGroup>
</Project>
-4
View File
@@ -1,8 +1,4 @@
<Project>
<PropertyGroup>
<EnableThreadingAnalyzers Condition="'$(EnableThreadingAnalyzers)' == ''">false</EnableThreadingAnalyzers>
</PropertyGroup>
<ItemGroup>
<PackageReference
Include="Microsoft.VisualStudio.Threading.Analyzers"
@@ -4,7 +4,6 @@
<TargetFramework>net10.0</TargetFramework>
<NoWarn>VSTHRD200,CA1873</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Configurations>Debug;Release;Debug No Sync</Configurations>
</PropertyGroup>
@@ -27,7 +27,7 @@ public class ReleaseMemoryHandler : IRequestHandler<ReleaseMemory>
return Task.CompletedTask;
}
bool hasActiveWorkers = _ffmpegSegmenterService.Workers.Count >= 0 || FFmpegProcess.ProcessCount > 0;
bool hasActiveWorkers = _ffmpegSegmenterService.Workers.Count > 0 || FFmpegProcess.ProcessCount > 0;
if (request.ForceAggressive || !hasActiveWorkers)
{
_logger.LogDebug("Starting aggressive garbage collection");
-1
View File
@@ -4,7 +4,6 @@
<TargetFramework>net10.0</TargetFramework>
<NoWarn>VSTHRD200,CA1873</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>disable</Nullable>
</PropertyGroup>
-1
View File
@@ -4,7 +4,6 @@
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<NoWarn>VSTHRD200,CA1873</NoWarn>
@@ -5,7 +5,6 @@
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<NoWarn>VSTHRD200,CA1873</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-1
View File
@@ -8,7 +8,6 @@
<Configurations>Debug;Release;Debug No Sync</Configurations>
<Platforms>AnyCPU</Platforms>
<UserSecretsId>729e6271-c307-43c8-8e36-1b36c39f6de2</UserSecretsId>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>VSTHRD200,CA1873</NoWarn>
</PropertyGroup>
@@ -0,0 +1,49 @@
using ErsatzTV.Application.Maintenance;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg;
using Microsoft.Extensions.Logging;
using NSubstitute;
using NUnit.Framework;
using Shouldly;
namespace ErsatzTV.Tests.Application.Maintenance;
[TestFixture]
[NonParallelizable]
public class ReleaseMemoryHandlerTests
{
[Test]
public async Task Should_Use_Aggressive_Collection_When_No_Workers_Are_Active()
{
FFmpegProcess.ProcessCount.ShouldBe(0);
IFFmpegSegmenterService segmenterService = Substitute.For<IFFmpegSegmenterService>();
segmenterService.Workers.Returns([]);
ILogger<ReleaseMemoryHandler> logger = Substitute.For<ILogger<ReleaseMemoryHandler>>();
var handler = new ReleaseMemoryHandler(segmenterService, logger);
await handler.Handle(new ReleaseMemory(false), CancellationToken.None);
ShouldHaveLogged(logger, "Starting aggressive garbage collection");
}
[Test]
public async Task Should_Use_Regular_Collection_When_A_Worker_Is_Active()
{
FFmpegProcess.ProcessCount.ShouldBe(0);
IFFmpegSegmenterService segmenterService = Substitute.For<IFFmpegSegmenterService>();
segmenterService.Workers.Returns([Substitute.For<IHlsSessionWorker>()]);
ILogger<ReleaseMemoryHandler> logger = Substitute.For<ILogger<ReleaseMemoryHandler>>();
var handler = new ReleaseMemoryHandler(segmenterService, logger);
await handler.Handle(new ReleaseMemory(false), CancellationToken.None);
ShouldHaveLogged(logger, "Starting garbage collection");
}
private static void ShouldHaveLogged(ILogger<ReleaseMemoryHandler> logger, string expectedMessage) =>
logger.ReceivedCalls()
.Any(call => call.GetArguments().ElementAtOrDefault(2)?.ToString() == expectedMessage)
.ShouldBeTrue();
}
-1
View File
@@ -11,7 +11,6 @@
<Configurations>Debug;Release;Debug No Sync</Configurations>
<Platforms>AnyCPU</Platforms>
<UserSecretsId>bf31217d-f4ec-4520-8cc3-138059044ede</UserSecretsId>
<AnalysisLevel>latest-Recommended</AnalysisLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
<NeutralLanguage>en-US</NeutralLanguage>
+29 -16
View File
@@ -299,27 +299,40 @@ running product from outside our C#/review stack.
## Static analysis & formatting
**Analyzer packs** — `Directory.Build.targets` references **Roslynator**, **SonarAnalyzer.CSharp**,
**Meziantou.Analyzer**, and **AsyncFixer** for every project (versions central via CPM; guarded on
`ManagePackageVersionsCentrally` so the gitignored `.mcp` tool isn't pulled in). They are introduced
**incrementally** (ersatztv#15): `.editorconfig` sets `dotnet_analyzer_diagnostic.severity = suggestion`
so the packs surface findings without failing the `TreatWarningsAsErrors` (TWAE) build. **Promotion is
the enforcement** — raising a rule to `warning` makes it a CI-blocking error via the existing TWAE
build, so no separate lint step is needed.
**Analyzers** — `Directory.Build.props` enables the SDK analyzers at `latest-All` and turns on
`Microsoft.VisualStudio.Threading.Analyzers` for every project. `Directory.Build.targets` also references
**Roslynator**, **SonarAnalyzer.CSharp**, **Meziantou.Analyzer**, and **AsyncFixer** repo-wide (versions
central via CPM; guarded on `ManagePackageVersionsCentrally` so the gitignored `.mcp` tool isn't pulled in).
They are introduced **incrementally** (ersatztv#15). `eng/analyzers/sdk-all-suggestion.globalconfig`
enumerates the .NET 10 SDK `All` inventory at `suggestion`; this exact-ID baseline is necessary because
the SDK's generated `latest-All` severities outrank `.editorconfig` bulk settings. `.editorconfig` keeps
the threading and curated-pack baselines at `suggestion`. Diagnostics remain visible to IDEs and
`dotnet format analyzers`, but do not create a wall of failures (a direct `latest-All` trial activated
455 existing errors in the TWAE projects).
**Promotion is the enforcement** — set an reviewed rule to `warning` in `.editorconfig` and append its ID
to the central `WarningsAsErrors` list in `Directory.Build.props`. The explicit list makes the rule block
in every project, including test projects that do not otherwise use TWAE. On a major SDK upgrade,
regenerate the checked-in SDK baseline from `analysislevel_<major>_all.globalconfig`, preserve SDK `none`
entries, and review newly introduced rules before accepting the snapshot.
Promoted rules are recorded here so the blocking subset stays intentional and reviewable:
- **Sonar `S3981` — `warning` + `WarningsAsErrors`** (ersatztv#15): rejects collection-count comparisons that are constant
regardless of collection size. Its first finding exposed `Workers.Count >= 0`, which permanently
classified scheduled memory releases as busy and skipped the intended aggressive idle collection.
- **StyleCop.Analyzers is intentionally excluded**: its latest stable (1.1.118) crashes (`AD0001`) on
C# `record` declarations, and its rules overlap the existing `.editorconfig`/Roslynator. Revisit via
the record-compatible `1.2.0-beta` only if specifically wanted.
- **Blazor `.razor` caveat**: editorconfig severity overrides don't reach analyzer diagnostics in Razor
`@code` (source-generator limitation — `dotnet format` can't fix them either), so the currently-firing
SonarAnalyzer rules are temporarily `NoWarn`-ed in `ErsatzTV.csproj` and burned down rule-by-rule in
**ersatztv#25**. The same rules run at `suggestion` on `.cs`.
- **The former Blazor `.razor` caveat is retired**: Blazor removal deleted the Razor sources and their
temporary Sonar `NoWarn` list. The `.razor`/`.cshtml` suggestion scopes remain in `.editorconfig` only
as a defensive default if server-rendered view code is ever reintroduced.
**Formatting** — the tree isn't yet `dotnet format`-clean (mixed UTF-8 BOM + whitespace inherited from
upstream: ~1,500 BOM files + ~480 whitespace). A one-time normalization lands as its **own dedicated
PR** (kept out of the analyzer work to stay reviewable); afterwards `dotnet format whitespace
--verify-no-changes` (+ `style`) joins the `test` job so drift can't return. `.gitattributes` already
pins line endings.
**Formatting** — the inherited tree still contains legacy UTF-8 BOM/whitespace debt, so the standing
policy is **format as you touch**, not a mass rewrite (ersatztv#311). The Husky pre-commit hook runs
`dotnet format --verify-no-changes` for staged C# files, and the blocking `format` CI job repeats that
check for C# files changed by the PR. Untouched legacy files remain outside the gate; `.gitattributes`
pins line endings. A one-time full-tree normalization remains a separate, unmade decision.
## Migration integrity (EF Core, both providers)
+6 -3
View File
@@ -111,9 +111,12 @@ talks exclusively to the REST API. Every former Blazor route now 302-redirects t
- **`TreatWarningsAsErrors=true`** in the app projects — a warning fails the build. `NoWarn` carries a
small, documented exemption list (e.g. `VSTHRD200`, `CA1873`); NuGet-audit `NU1901-1903` are demoted
to warnings in `Directory.Build.props` while `NU1904` (critical) blocks.
- **Static-analysis packs** (Roslynator, SonarAnalyzer, Meziantou, AsyncFixer) run at `suggestion` and
are promoted to `warning`/`error` rule-by-rule; promotion is the enforcement (the TWAE build). New
rules start at suggestion — never flip a wall of rules to error at once. (ersatztv#15)
- **Static analysis is centralized**: `Directory.Build.props` enables the SDK analyzers at
`latest-All` plus the threading analyzer for every project; `Directory.Build.targets` adds Roslynator,
SonarAnalyzer, Meziantou, and AsyncFixer. The checked-in SDK globalconfig and `.editorconfig` keep the
broad baseline at `suggestion`. Promote one reviewed rule at a time by setting it to `warning` and
adding its ID to the central `WarningsAsErrors` list; never flip a wall of rules to error at once.
Refresh the SDK globalconfig deliberately when moving to a new .NET SDK major. (ersatztv#15)
## 8. Testing
@@ -0,0 +1,292 @@
# .NET 10 SDK analyzer baseline for ersatztv#15.
#
# AnalysisLevel=latest-All enables the complete SDK rule inventory, whose generated
# per-rule severities otherwise override .editorconfig's bulk suggestion setting.
# This higher-priority snapshot keeps every current .NET 10 rule visible but non-blocking.
# Preserve SDK 'none' entries for rules introduced after .NET 10. On an SDK major upgrade,
# regenerate this list from analysislevel_<major>_all.globalconfig and review new rules.
# A per-rule severity in the repository .editorconfig overrides this baseline.
is_global = true
global_level = -50
dotnet_diagnostic.CA1000.severity = suggestion
dotnet_diagnostic.CA1001.severity = suggestion
dotnet_diagnostic.CA1002.severity = suggestion
dotnet_diagnostic.CA1003.severity = suggestion
dotnet_diagnostic.CA1008.severity = suggestion
dotnet_diagnostic.CA1010.severity = suggestion
dotnet_diagnostic.CA1012.severity = suggestion
dotnet_diagnostic.CA1016.severity = suggestion
dotnet_diagnostic.CA1018.severity = suggestion
dotnet_diagnostic.CA1019.severity = suggestion
dotnet_diagnostic.CA1024.severity = suggestion
dotnet_diagnostic.CA1027.severity = suggestion
dotnet_diagnostic.CA1028.severity = suggestion
dotnet_diagnostic.CA1030.severity = suggestion
dotnet_diagnostic.CA1031.severity = suggestion
dotnet_diagnostic.CA1032.severity = suggestion
dotnet_diagnostic.CA1033.severity = suggestion
dotnet_diagnostic.CA1034.severity = suggestion
dotnet_diagnostic.CA1036.severity = suggestion
dotnet_diagnostic.CA1040.severity = suggestion
dotnet_diagnostic.CA1041.severity = suggestion
dotnet_diagnostic.CA1043.severity = suggestion
dotnet_diagnostic.CA1044.severity = suggestion
dotnet_diagnostic.CA1046.severity = suggestion
dotnet_diagnostic.CA1047.severity = suggestion
dotnet_diagnostic.CA1050.severity = suggestion
dotnet_diagnostic.CA1051.severity = suggestion
dotnet_diagnostic.CA1052.severity = suggestion
dotnet_diagnostic.CA1054.severity = suggestion
dotnet_diagnostic.CA1055.severity = suggestion
dotnet_diagnostic.CA1056.severity = suggestion
dotnet_diagnostic.CA1058.severity = suggestion
dotnet_diagnostic.CA1061.severity = suggestion
dotnet_diagnostic.CA1062.severity = suggestion
dotnet_diagnostic.CA1063.severity = suggestion
dotnet_diagnostic.CA1064.severity = suggestion
dotnet_diagnostic.CA1065.severity = suggestion
dotnet_diagnostic.CA1066.severity = suggestion
dotnet_diagnostic.CA1067.severity = suggestion
dotnet_diagnostic.CA1068.severity = suggestion
dotnet_diagnostic.CA1069.severity = suggestion
dotnet_diagnostic.CA1070.severity = suggestion
dotnet_diagnostic.CA1200.severity = suggestion
dotnet_diagnostic.CA1303.severity = suggestion
dotnet_diagnostic.CA1304.severity = suggestion
dotnet_diagnostic.CA1305.severity = suggestion
dotnet_diagnostic.CA1307.severity = suggestion
dotnet_diagnostic.CA1308.severity = suggestion
dotnet_diagnostic.CA1309.severity = suggestion
dotnet_diagnostic.CA1310.severity = suggestion
dotnet_diagnostic.CA1311.severity = suggestion
dotnet_diagnostic.CA1401.severity = suggestion
dotnet_diagnostic.CA1419.severity = suggestion
dotnet_diagnostic.CA1421.severity = suggestion
dotnet_diagnostic.CA1507.severity = suggestion
dotnet_diagnostic.CA1508.severity = suggestion
dotnet_diagnostic.CA1510.severity = suggestion
dotnet_diagnostic.CA1511.severity = suggestion
dotnet_diagnostic.CA1512.severity = suggestion
dotnet_diagnostic.CA1513.severity = suggestion
dotnet_diagnostic.CA1514.severity = suggestion
dotnet_diagnostic.CA1515.severity = suggestion
dotnet_diagnostic.CA1516.severity = none
dotnet_diagnostic.CA1700.severity = suggestion
dotnet_diagnostic.CA1707.severity = suggestion
dotnet_diagnostic.CA1708.severity = suggestion
dotnet_diagnostic.CA1710.severity = suggestion
dotnet_diagnostic.CA1711.severity = suggestion
dotnet_diagnostic.CA1712.severity = suggestion
dotnet_diagnostic.CA1713.severity = suggestion
dotnet_diagnostic.CA1715.severity = suggestion
dotnet_diagnostic.CA1716.severity = suggestion
dotnet_diagnostic.CA1720.severity = suggestion
dotnet_diagnostic.CA1721.severity = suggestion
dotnet_diagnostic.CA1724.severity = suggestion
dotnet_diagnostic.CA1725.severity = suggestion
dotnet_diagnostic.CA1727.severity = suggestion
dotnet_diagnostic.CA1802.severity = suggestion
dotnet_diagnostic.CA1805.severity = suggestion
dotnet_diagnostic.CA1806.severity = suggestion
dotnet_diagnostic.CA1810.severity = suggestion
dotnet_diagnostic.CA1812.severity = suggestion
dotnet_diagnostic.CA1813.severity = suggestion
dotnet_diagnostic.CA1814.severity = suggestion
dotnet_diagnostic.CA1815.severity = suggestion
dotnet_diagnostic.CA1816.severity = suggestion
dotnet_diagnostic.CA1819.severity = suggestion
dotnet_diagnostic.CA1820.severity = suggestion
dotnet_diagnostic.CA1821.severity = suggestion
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_diagnostic.CA1823.severity = suggestion
dotnet_diagnostic.CA1824.severity = suggestion
dotnet_diagnostic.CA1825.severity = suggestion
dotnet_diagnostic.CA1826.severity = suggestion
dotnet_diagnostic.CA1827.severity = suggestion
dotnet_diagnostic.CA1828.severity = suggestion
dotnet_diagnostic.CA1829.severity = suggestion
dotnet_diagnostic.CA1830.severity = suggestion
dotnet_diagnostic.CA1832.severity = suggestion
dotnet_diagnostic.CA1833.severity = suggestion
dotnet_diagnostic.CA1834.severity = suggestion
dotnet_diagnostic.CA1835.severity = suggestion
dotnet_diagnostic.CA1836.severity = suggestion
dotnet_diagnostic.CA1837.severity = suggestion
dotnet_diagnostic.CA1838.severity = suggestion
dotnet_diagnostic.CA1839.severity = suggestion
dotnet_diagnostic.CA1840.severity = suggestion
dotnet_diagnostic.CA1841.severity = suggestion
dotnet_diagnostic.CA1842.severity = suggestion
dotnet_diagnostic.CA1843.severity = suggestion
dotnet_diagnostic.CA1844.severity = suggestion
dotnet_diagnostic.CA1845.severity = suggestion
dotnet_diagnostic.CA1846.severity = suggestion
dotnet_diagnostic.CA1847.severity = suggestion
dotnet_diagnostic.CA1848.severity = suggestion
dotnet_diagnostic.CA1849.severity = suggestion
dotnet_diagnostic.CA1850.severity = suggestion
dotnet_diagnostic.CA1851.severity = suggestion
dotnet_diagnostic.CA1852.severity = suggestion
dotnet_diagnostic.CA1853.severity = suggestion
dotnet_diagnostic.CA1854.severity = suggestion
dotnet_diagnostic.CA1855.severity = suggestion
dotnet_diagnostic.CA1856.severity = suggestion
dotnet_diagnostic.CA1858.severity = suggestion
dotnet_diagnostic.CA1859.severity = suggestion
dotnet_diagnostic.CA1860.severity = suggestion
dotnet_diagnostic.CA1861.severity = suggestion
dotnet_diagnostic.CA1862.severity = suggestion
dotnet_diagnostic.CA1863.severity = suggestion
dotnet_diagnostic.CA1864.severity = suggestion
dotnet_diagnostic.CA1865.severity = suggestion
dotnet_diagnostic.CA1866.severity = suggestion
dotnet_diagnostic.CA1867.severity = suggestion
dotnet_diagnostic.CA1868.severity = suggestion
dotnet_diagnostic.CA1869.severity = suggestion
dotnet_diagnostic.CA1870.severity = suggestion
dotnet_diagnostic.CA1871.severity = suggestion
dotnet_diagnostic.CA1872.severity = suggestion
dotnet_diagnostic.CA1873.severity = suggestion
dotnet_diagnostic.CA1874.severity = suggestion
dotnet_diagnostic.CA1875.severity = suggestion
dotnet_diagnostic.CA2000.severity = suggestion
dotnet_diagnostic.CA2002.severity = suggestion
dotnet_diagnostic.CA2007.severity = suggestion
dotnet_diagnostic.CA2008.severity = suggestion
dotnet_diagnostic.CA2009.severity = suggestion
dotnet_diagnostic.CA2011.severity = suggestion
dotnet_diagnostic.CA2012.severity = suggestion
dotnet_diagnostic.CA2016.severity = suggestion
dotnet_diagnostic.CA2019.severity = suggestion
dotnet_diagnostic.CA2020.severity = suggestion
dotnet_diagnostic.CA2025.severity = suggestion
dotnet_diagnostic.CA2100.severity = suggestion
dotnet_diagnostic.CA2101.severity = suggestion
dotnet_diagnostic.CA2119.severity = suggestion
dotnet_diagnostic.CA2153.severity = suggestion
dotnet_diagnostic.CA2201.severity = suggestion
dotnet_diagnostic.CA2207.severity = suggestion
dotnet_diagnostic.CA2208.severity = suggestion
dotnet_diagnostic.CA2211.severity = suggestion
dotnet_diagnostic.CA2213.severity = suggestion
dotnet_diagnostic.CA2214.severity = suggestion
dotnet_diagnostic.CA2215.severity = suggestion
dotnet_diagnostic.CA2216.severity = suggestion
dotnet_diagnostic.CA2217.severity = suggestion
dotnet_diagnostic.CA2218.severity = suggestion
dotnet_diagnostic.CA2219.severity = suggestion
dotnet_diagnostic.CA2224.severity = suggestion
dotnet_diagnostic.CA2225.severity = suggestion
dotnet_diagnostic.CA2226.severity = suggestion
dotnet_diagnostic.CA2227.severity = suggestion
dotnet_diagnostic.CA2231.severity = suggestion
dotnet_diagnostic.CA2234.severity = suggestion
dotnet_diagnostic.CA2235.severity = suggestion
dotnet_diagnostic.CA2237.severity = suggestion
dotnet_diagnostic.CA2241.severity = suggestion
dotnet_diagnostic.CA2242.severity = suggestion
dotnet_diagnostic.CA2243.severity = suggestion
dotnet_diagnostic.CA2244.severity = suggestion
dotnet_diagnostic.CA2245.severity = suggestion
dotnet_diagnostic.CA2246.severity = suggestion
dotnet_diagnostic.CA2248.severity = suggestion
dotnet_diagnostic.CA2249.severity = suggestion
dotnet_diagnostic.CA2250.severity = suggestion
dotnet_diagnostic.CA2251.severity = suggestion
dotnet_diagnostic.CA2252.severity = suggestion
dotnet_diagnostic.CA2253.severity = suggestion
dotnet_diagnostic.CA2254.severity = suggestion
dotnet_diagnostic.CA2262.severity = suggestion
dotnet_diagnostic.CA2263.severity = suggestion
dotnet_diagnostic.CA2300.severity = suggestion
dotnet_diagnostic.CA2301.severity = suggestion
dotnet_diagnostic.CA2302.severity = suggestion
dotnet_diagnostic.CA2305.severity = suggestion
dotnet_diagnostic.CA2310.severity = suggestion
dotnet_diagnostic.CA2311.severity = suggestion
dotnet_diagnostic.CA2312.severity = suggestion
dotnet_diagnostic.CA2315.severity = suggestion
dotnet_diagnostic.CA2321.severity = suggestion
dotnet_diagnostic.CA2322.severity = suggestion
dotnet_diagnostic.CA2326.severity = suggestion
dotnet_diagnostic.CA2327.severity = suggestion
dotnet_diagnostic.CA2328.severity = suggestion
dotnet_diagnostic.CA2329.severity = suggestion
dotnet_diagnostic.CA2330.severity = suggestion
dotnet_diagnostic.CA2350.severity = suggestion
dotnet_diagnostic.CA2351.severity = suggestion
dotnet_diagnostic.CA2352.severity = suggestion
dotnet_diagnostic.CA2353.severity = suggestion
dotnet_diagnostic.CA2354.severity = suggestion
dotnet_diagnostic.CA2355.severity = suggestion
dotnet_diagnostic.CA2356.severity = suggestion
dotnet_diagnostic.CA2361.severity = suggestion
dotnet_diagnostic.CA2362.severity = suggestion
dotnet_diagnostic.CA3001.severity = suggestion
dotnet_diagnostic.CA3002.severity = suggestion
dotnet_diagnostic.CA3003.severity = suggestion
dotnet_diagnostic.CA3004.severity = suggestion
dotnet_diagnostic.CA3005.severity = suggestion
dotnet_diagnostic.CA3006.severity = suggestion
dotnet_diagnostic.CA3007.severity = suggestion
dotnet_diagnostic.CA3008.severity = suggestion
dotnet_diagnostic.CA3009.severity = suggestion
dotnet_diagnostic.CA3010.severity = suggestion
dotnet_diagnostic.CA3011.severity = suggestion
dotnet_diagnostic.CA3012.severity = suggestion
dotnet_diagnostic.CA3061.severity = suggestion
dotnet_diagnostic.CA3075.severity = suggestion
dotnet_diagnostic.CA3076.severity = suggestion
dotnet_diagnostic.CA3077.severity = suggestion
dotnet_diagnostic.CA3147.severity = suggestion
dotnet_diagnostic.CA5350.severity = suggestion
dotnet_diagnostic.CA5351.severity = suggestion
dotnet_diagnostic.CA5358.severity = suggestion
dotnet_diagnostic.CA5359.severity = suggestion
dotnet_diagnostic.CA5360.severity = suggestion
dotnet_diagnostic.CA5361.severity = suggestion
dotnet_diagnostic.CA5362.severity = suggestion
dotnet_diagnostic.CA5363.severity = suggestion
dotnet_diagnostic.CA5364.severity = suggestion
dotnet_diagnostic.CA5365.severity = suggestion
dotnet_diagnostic.CA5366.severity = suggestion
dotnet_diagnostic.CA5367.severity = suggestion
dotnet_diagnostic.CA5368.severity = suggestion
dotnet_diagnostic.CA5369.severity = suggestion
dotnet_diagnostic.CA5370.severity = suggestion
dotnet_diagnostic.CA5371.severity = suggestion
dotnet_diagnostic.CA5372.severity = suggestion
dotnet_diagnostic.CA5373.severity = suggestion
dotnet_diagnostic.CA5374.severity = suggestion
dotnet_diagnostic.CA5375.severity = suggestion
dotnet_diagnostic.CA5376.severity = suggestion
dotnet_diagnostic.CA5377.severity = suggestion
dotnet_diagnostic.CA5378.severity = suggestion
dotnet_diagnostic.CA5379.severity = suggestion
dotnet_diagnostic.CA5380.severity = suggestion
dotnet_diagnostic.CA5381.severity = suggestion
dotnet_diagnostic.CA5382.severity = suggestion
dotnet_diagnostic.CA5383.severity = suggestion
dotnet_diagnostic.CA5384.severity = suggestion
dotnet_diagnostic.CA5385.severity = suggestion
dotnet_diagnostic.CA5386.severity = suggestion
dotnet_diagnostic.CA5387.severity = suggestion
dotnet_diagnostic.CA5388.severity = suggestion
dotnet_diagnostic.CA5389.severity = suggestion
dotnet_diagnostic.CA5390.severity = suggestion
dotnet_diagnostic.CA5391.severity = suggestion
dotnet_diagnostic.CA5392.severity = suggestion
dotnet_diagnostic.CA5393.severity = suggestion
dotnet_diagnostic.CA5394.severity = suggestion
dotnet_diagnostic.CA5395.severity = suggestion
dotnet_diagnostic.CA5396.severity = suggestion
dotnet_diagnostic.CA5397.severity = suggestion
dotnet_diagnostic.CA5398.severity = suggestion
dotnet_diagnostic.CA5399.severity = suggestion
dotnet_diagnostic.CA5400.severity = suggestion
dotnet_diagnostic.CA5401.severity = suggestion
dotnet_diagnostic.CA5402.severity = suggestion
dotnet_diagnostic.CA5403.severity = suggestion
dotnet_diagnostic.CA5404.severity = suggestion
dotnet_diagnostic.CA5405.severity = suggestion