marp | theme | _class | style | backgroundImage |
---|---|---|---|---|
true |
gaia |
lead |
section{
color: #002030;
}
|
url('background.jpg') |
Write C# code analyzers
slides at: https://github.com/tomuxmon/code-with-roslyn
- Before Roslyn (System.CodeDom)
- Roslyn development (starting 2010 - go prod 2015)
- Compiler SDK vs C# language
- Analyzers - inspect code and emit warnings in your IDE
- Fixers - offer code change suggestions to your IDE
- Code generation - generate code based on your code AST.
- Hints / Warnings
- IDE Integrations (VSCode specifics enableAnalyzersSupport: true)
- MSBuild Integrations (EnableNETAnalyzers, severity levels)
- dothen format -> apply auto fixes 😈
[*.cs]
dotnet_diagnostic.CA1822.severity = error
[*.MyGenerated.cs]
generated_code = true
- minimal analyzer
- minimal fixer
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
</ItemGroup>
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace min_analyzer;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class SomeAnalyzer : DiagnosticAnalyzer {
// ...snip...
}
private static readonly DiagnosticDescriptor someDescriptor = new(
"S01", "Method body token count",
"Method body contains '{0}' tokens. Reduce it to 40!",
"Security", DiagnosticSeverity.Warning, true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(someDescriptor);
public override void Initialize(AnalysisContext ctx) {
ctx.EnableConcurrentExecution();
ctx.ConfigureGeneratedCodeAnalysis(
GeneratedCodeAnalysisFlags.Analyze |
GeneratedCodeAnalysisFlags.ReportDiagnostics);
ctx.RegisterSyntaxNodeAction(
AnalyzeMethodSyntaxNode,
SyntaxKind.MethodDeclaration);
}
private void AnalyzeMethodSyntaxNode(SyntaxNodeAnalysisContext ctx) {
if (ctx.Node is MethodDeclarationSyntax myx) {
var node_count = myx.Body.DescendantNodes().Count();
if (node_count > 40) {
ctx.ReportDiagnostic(
Diagnostic.Create(
someDescriptor,
myx.Body.GetLocation(),
node_count));
}
}
}
using System.Collections.Immutable;
using System.Threading.Tasks;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
namespace min_fixer;
[ExportCodeFixProvider(LanguageNames.CSharp)]
public class SomeFixer : CodeFixProvider {
// ...snip...
}
// The name as it will appear in the light bulb menu
private const string FixTitle = "Reduce method size (start it from scratch)";
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create("S01");
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override async Task RegisterCodeFixesAsync(CodeFixContext ctx) {
var root = await ctx.Document.GetSyntaxRootAsync(ctx.CancellationToken);
var diagnostic = ctx.Diagnostics.First();
var methodSyntax = root.
FindToken(diagnostic.Location.SourceSpan.Start).
Parent.
AncestorsAndSelf().
OfType<MethodDeclarationSyntax>().
First();
ctx.RegisterCodeFix(
CodeAction.Create(
title: FixTitle,
createChangedDocument: c => FixAsync(ctx.Document, methodSyntax, c),
equivalenceKey: FixTitle),
diagnostic);
}
private static async Task<Document> FixAsync(
Document doc,
MethodDeclarationSyntax methodSyntax,
CancellationToken ct
) {
var oldRoot = await doc.GetSyntaxRootAsync(ct);
var newRoot = oldRoot.ReplaceNode(methodSyntax.Body, SyntaxFactory.Block());
return doc.WithSyntaxRoot(newRoot);
}
- Tutorial: Write your first analyzer and code fix
- Writing a Roslyn analyzer
- How to write a Roslyn Analyzer
- Roslyn analyzers and code-aware library for ImmutableArrays
- C# - Adding a Code Fix to Your Roslyn Analyzer
- Microsoft.CodeAnalysis.NetAnalyzers (included by default, named FxCop in previous life)
- Roslynator.Analyzers
- StyleCop.Analyzers
- Meziantou.Analyzer
- SonarAnalyzer.CSharp
- know something else? Shout it at me :)
Now Questions!