dotnet-roslyn-analyzers

dotnet-roslyn-analyzers

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "dotnet-roslyn-analyzers" with this command: npx skills add novotnyllc/dotnet-artisan/novotnyllc-dotnet-artisan-dotnet-roslyn-analyzers

dotnet-roslyn-analyzers

Guidance for authoring custom Roslyn analyzers, code fix providers, code refactoring providers, and diagnostic suppressors. Covers project setup, DiagnosticDescriptor conventions, analysis context registration, code fix actions, code refactoring actions, multi-Roslyn-version targeting (3.8 through 4.14), testing with Microsoft.CodeAnalysis.Testing, NuGet packaging, and performance best practices.

For extended code examples (CodeRefactoringProvider implementation, multi-version project structure, test matrix configuration), see details.md in this skill directory.

Scope

  • DiagnosticAnalyzer authoring and analysis context registration

  • CodeFixProvider and CodeRefactoringProvider implementation

  • Multi-Roslyn-version targeting (3.8 through 4.14)

  • Testing with Microsoft.CodeAnalysis.Testing

  • NuGet packaging for analyzer assemblies

Out of scope

  • Consuming and configuring existing analyzers (CA rules, severity) -- see [skill:dotnet-add-analyzers]

  • Authoring source generators (IIncrementalGenerator) -- see [skill:dotnet-csharp-source-generators]

  • Naming conventions -- see [skill:dotnet-csharp-coding-standards]

Cross-references: [skill:dotnet-csharp-source-generators] for shared Roslyn packaging concepts and IIncrementalGenerator patterns, [skill:dotnet-add-analyzers] for consuming and configuring analyzers, [skill:dotnet-testing-strategy] for general test organization and framework selection, [skill:dotnet-csharp-coding-standards] for naming conventions applied to analyzer code.

Project Setup

Analyzer projects must target netstandard2.0 . The compiler loads analyzers into various host processes (Visual Studio on .NET Framework/Mono, MSBuild on .NET Core, dotnet build CLI) -- targeting net8.0+ breaks compatibility with hosts that do not run on that runtime.

<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> <IsRoslynComponent>true</IsRoslynComponent> <LangVersion>latest</LangVersion> </PropertyGroup>

<ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" PrivateAssets="all" /> </ItemGroup> </Project>

  • EnforceExtendedAnalyzerRules enables RS-series meta-diagnostics that catch common analyzer authoring mistakes.

  • IsRoslynComponent enables IDE tooling support for the project.

  • LangVersion>latest lets you write modern C# in the analyzer itself while still targeting netstandard2.0 .

  • All Roslyn SDK packages must use PrivateAssets="all" to avoid shipping them as transitive dependencies.

DiagnosticAnalyzer

Every analyzer inherits from DiagnosticAnalyzer and must be decorated with [DiagnosticAnalyzer(LanguageNames.CSharp)] .

[DiagnosticAnalyzer(LanguageNames.CSharp)] public sealed class NoPublicFieldsAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "MYLIB001";

private static readonly DiagnosticDescriptor Rule = new(
    id: DiagnosticId,
    title: "Public fields should be properties",
    messageFormat: "Field '{0}' is public; use a property instead",
    category: "Design",
    defaultSeverity: DiagnosticSeverity.Warning,
    isEnabledByDefault: true,
    helpLinkUri: $"https://example.com/docs/rules/{DiagnosticId}");

public override ImmutableArray&#x3C;DiagnosticDescriptor> SupportedDiagnostics { get; }
    = ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
    context.EnableConcurrentExecution();
    context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
    context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field);
}

private static void AnalyzeField(SymbolAnalysisContext context)
{
    var field = (IFieldSymbol)context.Symbol;
    if (field.DeclaredAccessibility == Accessibility.Public
        &#x26;&#x26; !field.IsConst &#x26;&#x26; !field.IsReadOnly)
    {
        context.ReportDiagnostic(Diagnostic.Create(Rule, field.Locations[0], field.Name));
    }
}

}

Analysis Context Registration

Method Granularity Use When

RegisterSyntaxNodeAction

Individual syntax nodes Pattern matching on specific syntax

RegisterSymbolAction

Declared symbols Checking symbol-level properties

RegisterOperationAction

IL-level operations Analyzing semantic operations

RegisterSyntaxTreeAction

Entire syntax tree File-level checks

RegisterCompilationStartAction

Compilation start Accumulate state across compilation

RegisterCompilationAction

Full compilation One-shot analysis after all files

DiagnosticDescriptor Conventions

ID Prefix Patterns

Pattern Example When

PROJ###

MYLIB001

Single-project analyzers

AREA####

PERF0001

Category-scoped analyzers

XX####

MA0042

Short-prefix convention

Avoid prefixes reserved by the .NET platform: CA , CS , RS , IDE , IL , SYSLIB .

Severity Selection

Severity Use When

Error

Code will not work correctly at runtime

Warning

Code works but violates best practices

Info

Suggestion for improvement

Hidden

IDE-only refactoring suggestion

Default to Warning for most rules. Always provide a non-null helpLinkUri (RS1015 enforces this).

CodeFixProvider

Code fix providers offer automated corrections for diagnostics. Key patterns:

  • EquivalenceKey: Every CodeAction must have a unique equivalenceKey for FixAll support (RS1010, RS1011)

  • Document vs. Solution modification: Use createChangedDocument for single-file fixes, createChangedSolution for cross-file renames

  • Trivia preservation: Always transfer leading/trailing trivia from replaced nodes

  • FixAllProvider: Return WellKnownFixAllProviders.BatchFixer for batch-applicable fixes

See details.md for the complete CodeFixProvider implementation with property conversion.

DiagnosticSuppressor

Conditionally suppresses diagnostics from other analyzers when EditorConfig cannot express the suppression condition. Requires Roslyn 3.8+.

Approach Use When

EditorConfig severity override Suppression applies unconditionally

[SuppressMessage] attribute Suppression applies to a specific location

DiagnosticSuppressor

Suppression depends on code structure or patterns

Multi-Roslyn-Version Targeting

Version Boundaries

Roslyn Version Ships With Key APIs Added

3.8 VS 16.8 / .NET 5 SDK DiagnosticSuppressor

4.2 VS 17.2 / .NET 6 SDK Improved incremental analysis

4.4 VS 17.4 / .NET 7 SDK ForAttributeWithMetadataName

4.8 VS 17.8 / .NET 8 U1 CollectionExpression support

4.14 VS 17.14 / .NET 10 SDK Latest API surface

Use conditional compilation constants (ROSLYN_X_Y_OR_GREATER ) and version-specific NuGet paths (analyzers/dotnet/roslyn{version}/cs/ ). See details.md for the complete multi-version project structure.

Testing Analyzers

Use Microsoft.CodeAnalysis.Testing for ergonomic analyzer testing:

using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier< NoPublicFieldsAnalyzer, Microsoft.CodeAnalysis.Testing.DefaultVerifier>;

public class NoPublicFieldsAnalyzerTests { [Fact] public async Task PublicField_ReportsDiagnostic() { var test = """ public class MyClass { public int {|MYLIB001:Value|}; } """; await Verify.VerifyAnalyzerAsync(test); } }

Diagnostic Markup Syntax

Markup Meaning

`[ text

`{ DIAG_ID:text

NuGet Packaging

Analyzers ship as NuGet packages with assemblies in analyzers/dotnet/cs/ , not lib/ .

<PropertyGroup> <IncludeBuildOutput>false</IncludeBuildOutput> <DevelopmentDependency>true</DevelopmentDependency> <SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking> </PropertyGroup>

<ItemGroup> <None Include="$(OutputPath)$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" /> </ItemGroup>

Performance Best Practices

  • Resolve types once per compilation inside RegisterCompilationStartAction , not per-node/symbol callbacks

  • Cache SupportedDiagnostics as ImmutableArray field, not expression-bodied property

  • Enable concurrent execution -- always call context.EnableConcurrentExecution()

  • Filter early -- register for the most specific SyntaxKind possible

  • Avoid Compilation.GetSemanticModel() -- use the SemanticModel from the analysis context (RS1030)

Common Meta-Diagnostics (RS-Series)

ID Title What It Catches

RS1001 Missing DiagnosticAnalyzerAttribute

Analyzer class missing attribute

RS1008 Avoid storing per-compilation data Instance fields with compilation data

RS1010 Create code actions with unique EquivalenceKey

Missing equivalence key

RS1015 Provide non-null helpLinkUri

Empty help link

RS1016 Code fix providers should provide FixAll support Missing GetFixAllProvider()

RS1024 Symbols should be compared for equality Using == instead of SymbolEqualityComparer

RS1026 Enable concurrent execution Missing EnableConcurrentExecution()

RS1030 Do not invoke Compilation.GetSemanticModel()

Using wrong semantic model source

RS1041 Compiler extensions should target netstandard2.0

Wrong target framework

References

  • Tutorial: Write your first analyzer and code fix

  • Roslyn SDK overview

  • Microsoft.CodeAnalysis.Testing

  • Analyzer NuGet packaging conventions

  • dotnet/roslyn-analyzers (RS diagnostic source)

  • Meziantou.Analyzer (exemplar project)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

dotnet-ui

No summary provided by upstream source.

Repository SourceNeeds Review
General

dotnet-csharp

No summary provided by upstream source.

Repository SourceNeeds Review
General

dotnet-api

No summary provided by upstream source.

Repository SourceNeeds Review
General

dotnet-testing

No summary provided by upstream source.

Repository SourceNeeds Review