.NET Static Analysis
Built-in Analyzers
.NET Analyzers
.NET SDK includes analyzers for code quality and style. Enable in project file:
<PropertyGroup> <!-- Enable all .NET analyzers --> <EnableNETAnalyzers>true</EnableNETAnalyzers>
<!-- Analysis level (latest, 8.0, 7.0, etc.) --> <AnalysisLevel>latest</AnalysisLevel>
<!-- Analysis mode: Default, Minimum, Recommended, All --> <AnalysisMode>Recommended</AnalysisMode> </PropertyGroup>
Analysis Categories
Category Prefix Focus
Design CA1xxx API design guidelines
Globalization CA2xxx Internationalization
Performance CA18xx Performance optimizations
Security CA2xxx, CA3xxx Security vulnerabilities
Usage CA2xxx Correct API usage
Naming CA17xx Naming conventions
Reliability CA2xxx Error handling, resources
Running Analysis
With Build
Build with analyzers (default)
dotnet build
Ensure analyzers run
dotnet build /p:RunAnalyzersDuringBuild=true
Treat warnings as errors
dotnet build /p:TreatWarningsAsErrors=true
Run only analyzers (no compile)
dotnet build /p:RunAnalyzers=true /p:RunCodeAnalysis=true
As Separate Step
Format check (style only)
dotnet format --verify-no-changes
Format and fix
dotnet format
Analyze specific project
dotnet build src/MyApp/MyApp.csproj /p:TreatWarningsAsErrors=true
Configuring Rules
EditorConfig
.editorconfig
root = true
[*.cs]
Naming conventions
dotnet_naming_rule.private_fields_should_be_camel_case.severity = warning dotnet_naming_rule.private_fields_should_be_camel_case.symbols = private_fields dotnet_naming_rule.private_fields_should_be_camel_case.style = camel_case_style
dotnet_naming_symbols.private_fields.applicable_kinds = field dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.camel_case_style.capitalization = camel_case dotnet_naming_style.camel_case_style.required_prefix = _
Code style
csharp_style_var_for_built_in_types = true:suggestion csharp_style_expression_bodied_methods = when_on_single_line:suggestion csharp_prefer_braces = true:warning
Analyzer rules
dotnet_diagnostic.CA1062.severity = warning # Validate arguments dotnet_diagnostic.CA2007.severity = none # Don't require ConfigureAwait dotnet_diagnostic.IDE0044.severity = warning # Make field readonly
GlobalAnalyzerConfig
.globalconfig
is_global = true
Apply to all projects
dotnet_diagnostic.CA1062.severity = warning dotnet_diagnostic.CA2000.severity = error
Project-Level
<PropertyGroup> <!-- Suppress in entire project --> <NoWarn>$(NoWarn);CA1062;CA2007</NoWarn>
<!-- Treat specific as error --> <WarningsAsErrors>$(WarningsAsErrors);CA2000</WarningsAsErrors> </PropertyGroup>
Suppressing Warnings
Code-Level Suppression
// Suppress on member [SuppressMessage("Design", "CA1062:Validate arguments", Justification = "Validated by framework")] public void Process(Request request) { }
// Suppress on line #pragma warning disable CA1062 public void Process(Request request) { } #pragma warning restore CA1062
// Suppress all in file [assembly: SuppressMessage("Design", "CA1062")]
Global Suppressions
// GlobalSuppressions.cs using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Design", "CA1062", Scope = "namespaceanddescendants", Target = "~N:MyApp.Controllers")]
Common Analyzer Rules
CA1062 - Validate Arguments
// Warning: parameter 'request' is never validated public void Process(Request request) { request.Execute(); // CA1062 }
// Fixed public void Process(Request request) { ArgumentNullException.ThrowIfNull(request); request.Execute(); }
CA2007 - ConfigureAwait
// Warning in library code await SomeAsync(); // CA2007
// Fixed await SomeAsync().ConfigureAwait(false);
// Or suppress in application code (.editorconfig) dotnet_diagnostic.CA2007.severity = none
CA1822 - Mark Members Static
// Warning: can be static public int Calculate(int x) => x * 2; // CA1822
// Fixed public static int Calculate(int x) => x * 2;
IDE0044 - Make Field Readonly
// Warning private int _value; // IDE0044
// Fixed private readonly int _value;
CA2000 - Dispose Objects
// Warning: not disposed public void Process() { var stream = new FileStream("file.txt", FileMode.Open); } // CA2000
// Fixed public void Process() { using var stream = new FileStream("file.txt", FileMode.Open); }
dotnet format
Check Formatting
Check without changes
dotnet format --verify-no-changes
Check specific files
dotnet format --include "src/**/*.cs" --verify-no-changes
Exclude paths
dotnet format --exclude "/Migrations/"
Apply Fixes
Fix all issues
dotnet format
Fix style issues only
dotnet format style
Fix analyzers only
dotnet format analyzers
Fix specific severity
dotnet format --severity warn
Targeting
Whitespace only
dotnet format whitespace
Style and analyzers
dotnet format style dotnet format analyzers
Specific diagnostics
dotnet format --diagnostics CA1062 IDE0044
Third-Party Analyzers
StyleCop.Analyzers
dotnet add package StyleCop.Analyzers
.editorconfig
Configure StyleCop rules
dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this dotnet_diagnostic.SA1200.severity = none # Using directives placement dotnet_diagnostic.SA1633.severity = none # File header
Roslynator
dotnet add package Roslynator.Analyzers
SonarAnalyzer
dotnet add package SonarAnalyzer.CSharp
CI Integration
Quality Gate Script
#!/bin/bash
Run build with analysis
dotnet build --no-restore /p:TreatWarningsAsErrors=true
Check format
dotnet format --verify-no-changes
Exit with error if any issues
exit $?
Azure DevOps
- task: DotNetCoreCLI@2 displayName: 'Build with Analysis' inputs: command: build arguments: '/p:TreatWarningsAsErrors=true'
See analyzers.md for detailed analyzer configurations.