NineKit.Analyzers (0.1.0)
Installation
dotnet nuget add source --name forgejo-admin --username your_username --password your_token dotnet add package --source forgejo-admin --version 0.1.0 NineKit.AnalyzersAbout this package
NineKit custom Roslyn analyzers.
NineKit.Analyzers
Custom Roslyn analyzers for NineKit solutions to enforce code quality and prevent common anti-patterns.
Available Analyzers
NKA001: Prefer Task over ValueTask in application code
Purpose: Prevents unnecessary use of ValueTask in application code
Triggers:
- Method returns
ValueTask/ValueTask<T>outside of allowed namespaces (Infrastructure,Core,Abstractions,Providers)
Suppresses when:
- The method is
DisposeAsync()(required byIAsyncDisposable) - The file path indicates tests or analyzer sources
- The method or containing type is annotated with
[ValueTaskReturnJustification("...")] - A
[SuppressMessage]attribute is present
Documentation: See docs/dev-guides/task-vs-valuetask.md for policy details
NKA002: High-cardinality data used in metric tags
Purpose: Prevents cardinality explosion in OpenTelemetry/Prometheus metrics
Triggers:
- Using high-cardinality data (IDs, session IDs, user IDs, execution IDs) as metric tags
- Counter/Histogram/Record methods with suspicious tag names
Detects:
- Tag names:
execution_id,sessionId,userId,operationId,requestId,traceId,correlationId,transactionId - Parameter names containing:
id,session,user,execution,request,trace,correlation,transaction - Variables with ID-like names
Example:
// ❌ Triggers NKA002
ExecutionDurationHistogram.Record(duration,
new("execution_id", executionId)); // High cardinality!
// ✅ OK
ExecutionDurationHistogram.Record(duration,
new("status", status));
Documentation: See docs/dev-guides/metrics-cardinality-best-practices.md for best practices
Suppress:
#pragma warning disable NKA002
// Code here
#pragma warning restore NKA002
Logging template constants (built-in analyzer)
NKA0021 has been removed in favor of the built-in .NET analyzer CA2254 (Template should be a constant).
Use CA2254 as the single source of truth for preventing string interpolation and non-constant logging templates.
Configuration: Set centrally in .editorconfig (dotnet_diagnostic.CA2254.severity = warning).
NKA0031: Avoid Rocks mocks for ILogger and IOptions
Purpose: Prevents Rocks source generator bloat from mocking generic framework interfaces.
Triggers:
[assembly: Rock(typeof(ILogger<>), BuildType.Create)][assembly: Rock(typeof(IOptions<>), BuildType.Create)][assembly: Rock(typeof(IOptionsMonitor<>), BuildType.Create)]- Any other
Rock(typeof(ILogger<MyType>)),IOptions<T>, orIOptionsMonitor<T>usage
Recommended replacements:
FakeLogger<T>orTestLogging.CreateTestLogger<T>for loggersOptions.Create(value)orBaseUnitTest.CreateOptions(value)forIOptions<T>BaseUnitTest.CreateOptionsMonitor(value)orTestOptionsMonitor.Create(value)forIOptionsMonitor<T>
NKA103: Use RockContext for automatic Rocks verification
Purpose: Enforces Pattern A (RockContext) over Pattern B (manual .Verify()) for Rocks mocks.
Triggers:
.Verify()calls on Rocks expectation objects (e.g.,expectations.Verify(),mock.Expectations.Verify())
Rationale:
- Pattern A (Recommended):
using var context = new RockContext();- Automatic verification - Pattern B (Legacy):
expectations.Verify();- Manual verification (error-prone)
RockContext automatically verifies all mocks when disposed, eliminating manual .Verify() calls and preventing missed
verifications.
Fix: Replace manual .Verify() with RockContext:
// ❌ Legacy Pattern B - FLAGGED by NKA103
[Test]
public async Task MyTest()
{
var expectations = new IFooCreateExpectations();
// ... setup ...
var result = await service.DoWorkAsync();
expectations.Verify(); // Manual verification - error-prone!
}
// ✅ Recommended Pattern A
[Test]
public async Task MyTest()
{
using var context = new RockContext(); // Automatic verification!
var expectations = new IFooCreateExpectations();
// ... setup ...
var result = await service.DoWorkAsync();
// No .Verify() needed - context.Dispose() handles it
}
Configuration: Set to warning level in .editorconfig for test projects during migration, then upgrade to error
once migration is complete.
Documentation: See tests/CLAUDE.md - Rocks Mocking Library - Verification Pattern section
Integrating the analyzers
You can consume this analyzer via direct project reference or by packing it as a NuGet package.
Option A: ProjectReference (recommended for this repo)
Add the analyzer project to your solution:
# Add analyzer project to the solution
DOTNET_CLI_HOME=. dotnet sln SWEMK.slnx add src/Analyzers/NineKit.Analyzers/NineKit.Analyzers.csproj
Then, in each application project (SWEMK.Web, SWEMK.Maui, jobs/APIs as desired), add:
<ItemGroup>
<ProjectReference Include="../../Analyzers/NineKit.Analyzers/NineKit.Analyzers.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
Alternatively, centralize in Directory.Build.props under an ItemGroup scoped to app projects only.
Option B: Pack as NuGet
DOTNET_CLI_HOME=. dotnet pack src/Analyzers/NineKit.Analyzers/NineKit.Analyzers.csproj -c Release
Publish the .nupkg to your internal feed and then add a PackageReference where needed.
Configuration and Tuning
NKA001 Tuning
- Adjust
AllowedNamespaceMarkers/ExcludedPathMarkersinValueTaskReturnAnalyzer.cs - Suppress with
[ValueTaskReturnJustification("...")]or[SuppressMessage]
NKA002 Tuning
- Customize
HighCardinalityTagNamesarray inHighCardinalityMetricAnalyzer.cs - Adjust parameter name checks in
IsHighCardinalityParameterName() - Suppress with
[SuppressMessage("Observability", "NKA002")]
Building and Testing
# Build the analyzer
dotnet build src/Analyzers/NineKit.Analyzers/NineKit.Analyzers.csproj
# Run analyzer on solution
dotnet build SWEMK.slnx --verbosity minimal | grep NKA
Versioning
Keep analyzer dependencies aligned with the repo's compiler/tooling. See Directory.Build.props and global.json for
SDK versions.
Documentation
- NKA001:
docs/dev-guides/task-vs-valuetask.md - NKA002:
docs/dev-guides/metrics-cardinality-best-practices.md - Summary:
CARDINALITY_FIX_SUMMARY.md