Format and Code Analysis

This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its dotnet CLI) and NuGet 7.9, as published in the MSBuild, .NET and NuGet documentation on learn.microsoft.com, which is the reference these pages are written and verified against. Material about .NET 11 / NuGet 8.0 is still in preview and is always flagged as such — never presented as baseline.

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The C# Reference bibliography lists the reference material consulted while preparing these pages.

Two tools keep C# code consistent: dotnet format, which rewrites files to match .editorconfig, and the analyzers that run during every build and report CA (quality) and IDE (style) diagnostics. This page shows how they are wired into the build and CI with .NET SDK 10.0.402. The style rules themselves, and how to choose them, are in the C# Reference: Coding Conventions and Documentation — Analyzers and dotnet format.

dotnet format

dotnet format [<project|solution>] runs three passes, also available separately:

Sub-command What it fixes

whitespace

Indentation, spacing and new lines, from .editorconfig (no compilation needed).

style

Code-style (IDE) rules with a severity in .editorconfig, such as file-scoped namespaces.

analyzers

Fixable diagnostics of other analyzers (CA rules, third-party packages).

--verify-no-changes reports instead of fixing; --diagnostics IDE0161 CA1305 and --severity warn|error|info restrict what is considered; --include/--exclude restrict files.

Ledgerline's .editorconfig asks for file-scoped namespaces (csharp_style_namespace_declarations = file_scoped:warning). A new file that ignores that and the indentation rules:

namespace Ledgerline.Cli
{
    internal static class Formatting
    {
      public static string Amount(decimal value)   =>  value.ToString("0.00");
    }
}
dotnet build src/Ledgerline.Cli
dotnet format src/Ledgerline.Cli --verify-no-changes; echo "exit=$?"
    0 Warning(s)
~/ledgerline/src/Ledgerline.Cli/Formatting.cs(5,7): error WHITESPACE: Fix whitespace formatting. Insert '\s\s'. [~/ledgerline/src/Ledgerline.Cli/Ledgerline.Cli.csproj]
~/ledgerline/src/Ledgerline.Cli/Formatting.cs(5,50): error WHITESPACE: Fix whitespace formatting. Delete 2 characters. [~/ledgerline/src/Ledgerline.Cli/Ledgerline.Cli.csproj]
~/ledgerline/src/Ledgerline.Cli/Formatting.cs(5,55): error WHITESPACE: Fix whitespace formatting. Delete 1 characters. [~/ledgerline/src/Ledgerline.Cli/Ledgerline.Cli.csproj]
~/ledgerline/src/Ledgerline.Cli/Formatting.cs(1,11): error IDE0161: Convert to file-scoped namespace [~/ledgerline/src/Ledgerline.Cli/Ledgerline.Cli.csproj]
exit=2

The build passed without a warning: IDE rules are not enforced in builds by default (next section). dotnet format --verify-no-changes exits with 2 when files would change, which makes it a CI check. Fixing is the same command without the flag, or one pass at a time:

dotnet format whitespace src/Ledgerline.Cli
dotnet format style src/Ledgerline.Cli
namespace Ledgerline.Cli;

internal static class Formatting
{
    public static string Amount(decimal value) => value.ToString("0.00");
}

whitespace fixed the indentation and spacing; style converted the namespace. dotnet format needs a successful restore (it loads the projects), and style/analyzers compile the code, so run it after dotnet restore in CI.

Analysis during the build

The .NET analyzers ship in the SDK and are on for projects targeting .NET 5 and later. Their configuration, queried on Ledgerline.Cli:

dotnet msbuild src/Ledgerline.Cli -getProperty:EnableNETAnalyzers -getProperty:AnalysisLevel \
  -getProperty:AnalysisMode -getProperty:EnforceCodeStyleInBuild -getProperty:SdkAnalysisLevel \
  -getProperty:EffectiveAnalysisLevel -getProperty:NoWarn
{
  "Properties": {
    "EnableNETAnalyzers": "true",
    "AnalysisLevel": "latest",
    "AnalysisMode": "",
    "EnforceCodeStyleInBuild": "false",
    "SdkAnalysisLevel": "10.0.400",
    "EffectiveAnalysisLevel": "10.0",
    "NoWarn": "1701;1702"
  }
}
Property Meaning

EnableNETAnalyzers

Run the CA analyzers (default true for .NET 5+).

AnalysisLevel

Which release’s rule set and defaults: latest (default), preview, or a version (9.0) to stay on older defaults after an SDK upgrade. It can carry the mode as a suffix (latest-recommended).

AnalysisMode

How many rules are on: None, Default (empty), Minimum, Recommended, All. AnalysisMode<Category> (AnalysisModeSecurity, …) sets one category.

EnforceCodeStyleInBuild

Report IDE (style) rules with a severity during the build, not only in the editor.

TreatWarningsAsErrors, WarningsAsErrors, NoWarn

Escalate all warnings, escalate listed codes, suppress listed codes.

CodeAnalysisTreatWarningsAsErrors

Set to false to keep CA warnings as warnings while TreatWarningsAsErrors escalates everything else.

SdkAnalysisLevel

(.NET 9+) Make a newer SDK’s own tooling diagnostics behave as an older band’s — for example to delay new NuGet or SDK warnings — without pinning the SDK. It defaults to the running SDK’s band (10.0.400 here).

Enforcing style in the build

dotnet build src/Ledgerline.Cli --no-restore -p:EnforceCodeStyleInBuild=true
dotnet build src/Ledgerline.Cli --no-restore --no-incremental -p:EnforceCodeStyleInBuild=true

The first command reported nothing: the project had just been compiled, and changing this property on the command line did not make the compiler target out of date, so it was skipped. The rebuild reported the rule — as an error, because Ledgerline sets TreatWarningsAsErrors:

~/ledgerline/src/Ledgerline.Cli/Formatting.cs(1,11): error IDE0161: Convert to file-scoped namespace (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0161) [~/ledgerline/src/Ledgerline.Cli/Ledgerline.Cli.csproj]

Set EnforceCodeStyleInBuild in Directory.Build.props, where it is part of every build, rather than on one command line. Some IDE rules (IDE0005, unnecessary usings) also need GenerateDocumentationFile to be reported in builds.

Turning more rules on

AnalysisMode=All on the CLI and its library (warnings kept as warnings for the experiment) reported four rules the default mode leaves off:

dotnet build src/Ledgerline.Cli --no-restore --no-incremental -p:AnalysisMode=All -p:TreatWarningsAsErrors=false
      2 warning CA1062
      2 warning CA1305
      2 warning CA1861
      2 warning CA1869

(counts per rule, each reported twice by the console logger’s summary). Two of them, verbatim:

~/ledgerline/src/Ledgerline/Ledger.Json.cs(12,44): warning CA1869: Avoid creating a new 'JsonSerializerOptions' instance for every serialization operation. Cache and reuse instances instead.
~/ledgerline/src/Ledgerline/LedgerReader.cs(15,20): warning CA1062: In externally visible method 'IReadOnlyList<JournalLine> LedgerReader.Parse(string csv)', validate parameter 'csv' is non-null before using it. If appropriate, throw an 'ArgumentNullException' when the argument is 'null'.

Each is a real finding for a library (CA1305 for culture-dependent formatting, CA1861 for a constant array allocated per call). All is noisy for applications; Recommended is a common middle ground, with individual rules tuned in .editorconfig.

In CI

dotnet restore --locked-mode
dotnet format --verify-no-changes --no-restore
dotnet build -c Release --no-restore

With EnforceCodeStyleInBuild and TreatWarningsAsErrors set in Directory.Build.props, the build alone fails on style and quality rules; dotnet format --verify-no-changes adds the whitespace rules, which only the formatter checks.

Source: dotnet format.

Common mistakes

  • Expecting .editorconfig severities to fail the build — not without EnforceCodeStyleInBuild.

  • Checking a property change with an incremental build — rebuild (--no-incremental).

  • AnalysisLevel left at latest — every SDK upgrade can add warnings; pin it (10.0) for warning-free upgrades.

  • NoWarn in project files for rules that should be tuned — set severities in .editorconfig, where they are visible and per-folder.

  • Running dotnet format before restore — it cannot load the projects.