Introduction
In this post, we will take a look into how a developer who is using a diagnostic analyzer could configure or suppress it based on their requirements, it could be changing default SeverityLevel
, disabling them, or telling the compiler to totally ignore (suppress) this diagnostic! We will take into different approaches to achieve this; we will also take a look into the options to enforce diagnostics!
Before getting started it is worth mentioning that there are two ways to tell the compiler what to do with a diagnostic, 1) Changing its default configuration properties and 2) Ignoring it, the latter is also known as suppressing the diagnostic.
Let's also review the line in which we are defining our DiagnosticDescriptor
containing its default behavior, in the following example, the diagnostic is by default enabled
and its SeverityLevel
is set to Error
:
private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description
);
Changing Default Configurations
Preprocessor Directives
The first way to disable a diagnostic is to use Pragmas Preprocessor Directives. #pragma warning
can enable or disable diagnostics, in our example project on GitHub, we could add #pragma warning
around the area we want to disable a diagnostic, it should be followed by the command (disable/enable) and the DiagnosticId
, in our case: SHG001
#pragma warning disable SHG001
throw new ArgumentException(@"Name cannot be null or whitespace", nameof(name));
#pragma warning restore SHG001
In IDEs there are suggested code actions for that:
.editorconfig and .globalconfig
The next approach is to disable or changing its severity level by adding a .editorconfig
file, when that file is created, you could follow the pattern dotnet_diagnostic.<diagnostic_id>.severity=<value>
in our example if I add a .editorconfig
file, the content would be similar to the following
[*.{cs,vb}]
dotnet_diagnostic.SHG001.severity = none
The section header in .editorconfig
files are necessary and [*.{cs,vb}]
indicates in which type of files it should look after such diagnostic.
We can achieve the same goal by adding .globalconfig
file, at the top level we need to add is_global = true
, however, it is not necessary if the file is named .globalconfig
. If the file is not named .globalconfig
you should also add the following line to the .csproj
file, or build.props
file:
<ItemGroup>
<GlobalAnalyzerConfigFiles Include="<path_to_global_analyzer_config>" />
</ItemGroup>
the content of '.globalconfig' file is almost similar to .editorconfig
file:
is_global = true
dotnet_diagnostic.SHG001.severity = none
PS: Bear in mind that the rules defined in either of these files applies to the files in the current directory and all subdirectories.
Compiler Options
Another way of telling the compiler how to treat a diagnostic is through compiler options, it could be in either of build.props
or .csproj
files, just add NoWarn
, which accepts a comma-separated list of DiagnosticId
s:
<NoWarn>SHG001/</NoWarn>
RuleSets
The next approach is to add a .ruleset
file in the project or any directory, and set the rules for the specified DiagnosticId
:
<?xml version="1.0" encoding="utf-8"?>
<RuleSet ToolsVersion="10.0"
Name="Template for custom ruleset">
<Rules AnalyzerId="Sample.Analyzer.MaybeSemanticAnalyzer" RuleNamespace="Sample.Analyzer">
<Rule Id="SHG001" Action="None"/>
</Rules>
</RuleSet>
To make it effective, you need to add a reference to it, either in the .csproj
or build.props
files; In .csproj
file it looks like:
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)CodeAnalysis.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
Preventing Configuration Changes
So far, all of the above mentioned approaches are overriding the configurations in the descriptor of the reported diagnostic, that means the diagnostic severity is either changed to something else, e.g. Warning
instead of an Error
or the diagnostic is disabled
instead of being enable
.
From a diagnostic author viewpoint, there might be scenarios that you need to prevent the consumer of your analyzer from either disabling it or changing its severity; interestingly enough, there is a way to do that.
Use one of the above-mentioned approaches to disable the diagnostic that we have created together from previous posts, go to the MaybeSemanticAnalyzer
file and change the DiagnosticDescriptor
to have customTags
with a value set to WellKnownDiagnosticTags.NotConfigurable
;
This indicates that the consumers of this analyzer could not change the configurations that are set in this rule.
// ....
private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description
, customTags: [WellKnownDiagnosticTags.NotConfigurable]
);
// ....
Build the project, and the error should be back again, even though you have set rules to disable it or changing its severity to something other than Error
.
....
1>------- Started building project: Sample.Console
....
1>Greeter.cs(10,13): Error SHG001 : Use Maybe.None instead of throw exception
1>------- Finished building project: Sample.Console. Succeeded: False. Errors: 1. Warnings: 0
Build completed in 00:00:02.398
Suppress Analyzers
SuppressMessageAttribute
As a consumer of a code analyzer, I could also tell the compiler to suppress the reported diagnostic via two attributes: SuppressMessageAttribute
and UnconditionalSuppressMessageAttribute
, these attribute could be placed on top of a method, class, or being expressed for the whole assembly, use it in either of these ways, and suppress the diagnostic SHG001
:
[SuppressMessage("Usage", "SHG001", Justification = "Not applicable")]
public static Maybe<string> Greet(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException(@"Name cannot be null or whitespace", nameof(name));
}
return $"Hello, {name}";
}
Now, the error should be gone again! You could add it the same way on the class, or use the following syntax for disabling it in an assembly, when you are doing that, it does not matter in which file it is placed:
[assembly: SuppressMessage("Usage", "SHG001", Justification = "Not applicable")]
Preventing Suppression
Ironically, there is also a way to prevent suppressing a diagnostic when defining its DiagnosticDescriptor
, you could pass another custom flag, WellKnownDiagnosticTags.Compiler
, this flag expresses that the diagnostic is reported by the compiler and that means it cannot be suppressed.
However, if WellKnownDiagnosticTags.NotConfigurable
does not exists for the DiagnosticDescriptor
it can still be disabled or have its severity changed!
Add the new flag and build the project, the error should be back, even though, we have both of the disable functionality and suppress attribute:
// ....
private static readonly DiagnosticDescriptor Rule = new(DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description
, customTags: [
WellKnownDiagnosticTags.NotConfigurable,
WellKnownDiagnosticTags.Compiler
]
);
// ....
Conclusion
We explored multiple ways to configure, suppress, and enforce C# diagnostics, whether you are consuming an analyzer or developing one yourself.
As a consumer, understanding how to suppress or adjust diagnostics is crucial for reducing noise and tailoring code analysis to your project’s needs. You could disable a diagnostic by using #pragma warning
, .editorconfig
, .globalconfig
, adding *.ruleset
files, or using compiler options; or you could suppress them by SuppressMessageAttribute
. There is this flexibility to override default configurations, unless the analyzer author has other plans.
From an analyzer author’s perspective, not all diagnostics should be left to consumer discretion. Critical ones that ensure security, maintainability, or business logic integrity might need to be enforced rather than suppressed. Using WellKnownDiagnosticTags.NotConfigurable
and WellKnownDiagnosticTags.Compiler
custom flags, ensures that your most important rules remain untouched, preventing accidental or intentional suppression.
At the end, thanks for reading, enjoy coding and Dametoon Garm [**]