C#idiomatic C#
analyzer-configurationidioms

Idioms

Analyzer configuration

Analyzers are only as useful as their configuration: what runs, at which severity, and where it is written down.

The compiler and the .NET analyzers ship thousands of rules and enable a few hundred of them. Which ones run, how loudly they speak, and where that is decided is project configuration, and it belongs in the repository next to the code.

Turn analysis on in the build, for every project

EnableNETAnalyzers is what puts the .NET analyzers into a build, and AnalysisLevel decides which set of rules that is. Set both in Directory.Build.props so every project is analyzed the same way instead of depending on which template created it.

Preferred

xml
<PropertyGroup>
  <EnableNETAnalyzers>true</EnableNETAnalyzers>
  <AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup>

Avoid

xml
<!-- analysis on in this project, left at the template default in the next -->

Corpus: Overview of .NET source code analysis · Configuration files for code analysis rules

Make warnings fail the build you gate on

A warning nobody reads is a comment. TreatWarningsAsErrors turns the analyzer output into a build failure, and EnforceCodeStyleInBuild extends the same treatment to the style rules, which are otherwise IDE-only.

Preferred

xml
<PropertyGroup>
  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

Avoid

xml
<!-- the CI build prints 400 warnings and exits 0 -->

Corpus: Overview of .NET source code analysis

Decide severity as a value, not as on or off

Every rule has a severity on a ladder that runs none, silent, suggestion, warning, error. Moving a rule down that ladder is a decision about the codebase; turning it off entirely throws the finding away.

Preferred

ini
dotnet_diagnostic.CA1031.severity = error
dotnet_diagnostic.CA1062.severity = suggestion

Avoid

ini
dotnet_diagnostic.CA1031.severity = none   # the real fix was to stop catching Exception

Corpus: Configuration options for code analysis

One rule, one category, or all of them

The same setting scopes to a single rule, to a whole category, or to everything. A category setting only moves the rules that category enables by default, which is what a "raise the security rules" decision wants.

Preferred

ini
dotnet_diagnostic.CA2007.severity = warning
dotnet_analyzer_diagnostic.category-Security.severity = warning

Avoid

ini
dotnet_analyzer_diagnostic.severity = error  # every finding is now a build failure

Corpus: Configuration options for code analysis

Write the configuration in .editorconfig

EditorConfig is where analyzer severity, style options and per-path exceptions all live. The nearest file to the source wins, and a section header such as [src/**/*.cs] scopes a whole folder, so a rule can be strict in new code and quiet in a legacy tree.

Preferred

ini
[*.cs]
dotnet_style_predefined_type_for_locals_parameters_members = true:warning

[src/legacy/**/*.cs]
dotnet_diagnostic.CA1031.severity = silent

Avoid

ini
# the same rules configured differently in five project files

Corpus: Configuration files for code analysis rules · Configuration options for code analysis

Use a global analyzer config for repository defaults

A .globalconfig file applies settings to every project that references it, which is how a repository states its defaults once instead of copying an .editorconfig into every project directory.

Corpus: Configuration files for code analysis rules

Style options carry their own severity

A style preference such as csharp_style_var_for_built_in_types is written as value:severity. Without the severity the option only informs the IDE; with it, the rule reports in a build that has EnforceCodeStyleInBuild.

Preferred

ini
csharp_style_var_when_type_is_apparent = true:warning

Avoid

ini
csharp_style_var_when_type_is_apparent = true  # read by the IDE, ignored by the build

Corpus: Code-style rule options

Quality rules have their own options

Beyond severity, some quality rules read a configuration option, and many of those options can be set per symbol rather than for the rule as a whole. Read the rule page before suppressing it: the option is often the intended dial.

Corpus: Code quality rule configuration options

Suppress at the narrowest scope that works

A preprocessor directive covers the lines around it, an attribute covers a member. Both should name the rule id and sit as close to the exception as the code allows, because a suppression with no rule id outlives its reason.

Preferred

csharp
#pragma warning disable CA1031 // the caller decides what a failed poll means
try { await PollAsync(ct); } catch (Exception ex) { _log.Warn(ex); }
#pragma warning restore CA1031

Avoid

csharp
#pragma warning disable   // everything, for the rest of the file

Corpus: How to suppress code analysis warnings

Turn a rule off in .editorconfig, not in NoWarn

dotnet_diagnostic.<id>.severity = none says which rule, in which scope, and it shows up in review. NoWarn hides the id inside a project file, where the next reader will not find it next to the code it concerns.

Avoid

xml
<NoWarn>$(NoWarn);CA1707</NoWarn>

Corpus: How to suppress code analysis warnings

Categories say what a rule is for

Every rule belongs to one category such as Design, Globalization, Security or Usage. The category is the shortest answer to "why is this rule complaining", and it is what catalog pages and category-wide severities are organised by.

Corpus: Rule categories

Adopt new rules deliberately

When a .NET version adds rules, the predefined configurations show what Microsoft turns on by default. Raising the analysis level and reviewing what appears is a smaller job than discovering it after a surprise upgrade.

Corpus: Predefined configuration files