DiagnosticCatalog 0.1.0
dotnet add package DiagnosticCatalog --version 0.1.0
NuGet\Install-Package DiagnosticCatalog -Version 0.1.0
<PackageReference Include="DiagnosticCatalog" Version="0.1.0" />
<PackageVersion Include="DiagnosticCatalog" Version="0.1.0" />
<PackageReference Include="DiagnosticCatalog" />
paket add DiagnosticCatalog --version 0.1.0
#r "nuget: DiagnosticCatalog, 0.1.0"
#:package DiagnosticCatalog@0.1.0
#addin nuget:?package=DiagnosticCatalog&version=0.1.0
#tool nuget:?package=DiagnosticCatalog&version=0.1.0
DiagnosticCatalog
Declare analyzer diagnostic rules as strongly referenced constants, so that
SuppressMessageAttribute takes compile-checked references instead of magic strings.
The problem
Both arguments of SuppressMessageAttribute are magic strings, and nothing
validates either one:
[SuppressMessage("Major Code Smell", "S1144", Justification = "...")]
They differ only in how they fail. Get the id wrong — a typo, or a rule the vendor
later renamed — and the suppression silently does nothing: the warning simply stays,
with nothing pointing at the cause. Get the category wrong and nothing happens at
all, ever: the .NET platform never reads that argument, so no compiler, analyzer, test
or tool can tell you. And you would not guess it — S1144's category is
"Major Code Smell", not "Code Smell" and not "Maintainability".
// Fails the build instead, if the rule is ever renamed or retired.
[SuppressMessage(SonarRule.S1144.Category, SonarRule.S1144.Id, Justification = "...")]
Sonar, the .NET CA rules and StyleCop are already packaged as DiagnosticCatalog.Sonar,
DiagnosticCatalog.NetAnalyzers and DiagnosticCatalog.StyleCop. This package is what
you need to declare a catalogue of your own.
Installation
<PackageReference Include="DiagnosticCatalog" Version="0.1.0" />
Do not add PrivateAssets="all" if your project publishes a catalogue for others
to consume: the package must flow to them so they can declare rules of their own, and
so that run-time reflection over your catalogue keeps working. The checks themselves
survive an unresolved attribute — the analyzers match on the fully qualified metadata
name DiagnosticCatalog.DiagnosticRuleAttribute, which is exactly the silent failure
mode that choice was made to design out — but do not rely on it.
Declaring a rule
A rule is a static, non-generic class marked [DiagnosticRule], exposing two mandatory
public constants:
using DiagnosticCatalog;
namespace JustDummies.Analyzers.Suppressions;
public static class Dummies
{
[DiagnosticRule]
public static class JD0007
{
public const string Id = nameof(JD0007);
public const string Category = "Usage";
}
}
Both members must be const. A property, a static readonly field or a record
cannot be used as an attribute argument, which is also why the contract is structural
rather than an interface or a base class.
Keep container names short — every use site pays for them twice. One constraint bounds
the shortening: never name the container after the first segment of its own
namespace. A consumer writing using JustDummies.Analyzers.Suppressions; resolves
JustDummies to the namespace, not to the imported container, and every reference fails
with CS0234. The consumer cannot work around it.
Using a rule
using System.Diagnostics.CodeAnalysis;
using JustDummies.Analyzers.Suppressions;
[SuppressMessage(
Dummies.JD0007.Category,
Dummies.JD0007.Id,
Justification = "This member is instantiated by the test infrastructure.")]
public sealed class DummyFactory
{
}
Optional metadata
A rule may carry the remaining DiagnosticDescriptor arguments. Every one of these is
a plain string, so this adds no dependency beyond this package:
[DiagnosticRule]
public static class JD0007
{
public const string Id = nameof(JD0007);
public const string Category = "Usage";
public const string Title = "Dummy factories should follow the expected convention";
public const string MessageFormat = "Type '{0}' does not follow the convention";
public const string Description = "Explains the condition detected by the analyzer.";
public const string HelpLinkUri = "https://justdummies.io/analyzers/JD0007";
}
If you own the analyzer, it can then build its descriptor from the very constants its suppressions reference — one source of truth for both:
using Microsoft.CodeAnalysis;
private static readonly DiagnosticDescriptor Descriptor = new(
JD0007.Id, JD0007.Title, JD0007.MessageFormat, JD0007.Category,
DiagnosticSeverity.Warning, isEnabledByDefault: true,
description: JD0007.Description, helpLinkUri: JD0007.HelpLinkUri);
DiagnosticSeverity is constant-capable, so a rule can also expose
public const DiagnosticSeverity Severity = DiagnosticSeverity.Warning; — but unlike the
string constants above, that type lives in Microsoft.CodeAnalysis.Common, so a rule
declaring it forces a Roslyn dependency on every consumer of the catalogue. Add it only
in a project that already references Microsoft.CodeAnalysis, such as your analyzer
itself. A standalone catalogue package should stay on plain strings.
Localised text (LocalizableString, resx-backed descriptors) falls outside the const
model; resource files remain the right tool for translated strings.
What this package is not
This package contains the attributes only — [DiagnosticRule] and
[assembly: CatalogSource]. It performs no checking.
The analyzers that validate rule declarations, verify that Category and Id come
from the same rule, and offer to replace string literals with catalogue references
ship separately:
<PackageReference Include="DiagnosticCatalog.Analyzers" Version="..." PrivateAssets="all" />
Applying [DiagnosticRule] introduces no runtime behaviour. The runtime resolves
attribute types lazily, so DiagnosticCatalog.dll is never loaded unless something
reflects over the rule types.
If you want no package dependency at all, the analyzers recognise the attribute by its
fully qualified metadata name. Declaring your own internal sealed class DiagnosticRuleAttribute in the DiagnosticCatalog namespace works just as well.
Recording where a catalogue came from
A catalogue that mirrors somebody else's analyzer is a snapshot. CatalogSource
records which upstream release it reflects and when, readable from metadata:
[assembly: CatalogSource(
source: "SonarAnalyzer.CSharp",
sourceVersion: "10.31.0.145097",
generatedOn: "2026-07-30")]
The date is a string because attribute arguments must be compile-time constants and
no date type can be one; the format is ISO 8601, yyyy-MM-dd. A first-party catalogue
maintained alongside its own analyzer does not need this — the two ship at one version.
Documentation
The full design, the diagnostic list and the verified platform behaviour it relies on are documented in the repository: specification (français).
License
Apache-2.0
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
net10.0
- No dependencies.
NuGet packages (3)
Showing the top 3 NuGet packages that depend on DiagnosticCatalog:
| Package | Downloads |
|---|---|
|
DiagnosticCatalog.StyleCop
The StyleCop.Analyzers rules as strongly referenced constants, so that SuppressMessageAttribute takes compile-checked references instead of magic strings. Ids, categories and help links are read from the analyzers' own DiagnosticDescriptor instances. Unofficial; not affiliated with the StyleCop.Analyzers project. |
|
|
DiagnosticCatalog.NetAnalyzers
The .NET code analysis (CA) rules as strongly referenced constants, so that SuppressMessageAttribute takes compile-checked references instead of magic strings. Ids, categories and help links are read from the analyzers' own DiagnosticDescriptor instances. |
|
|
DiagnosticCatalog.Sonar
The SonarAnalyzer.CSharp rules as strongly referenced constants, so that SuppressMessageAttribute takes compile-checked references instead of magic strings. Ids and categories are read from the analyzers' own DiagnosticDescriptor instances. Unofficial; not affiliated with or endorsed by SonarSource. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 2,388 | 7/30/2026 |