developing roslyn refactorings and code analyzersqnhdevdays.nl/developing roslyn refactorings...
TRANSCRIPT
Developing Roslyn Refactorings and Code
Analyzers
Fons Sonnemans
@fonssonnemans
Fons Sonnemans
• Software Development Consultant
• Programming Languages
• Clipper, Smalltalk, Visual Basic, C#
• Platforms
• Windows Forms, ASP.NET (Web Forms, MVC), XAML (WPF, Silverlight,
Windows Phone, Windows 8 & 10)
• Databases
• MS SQL Server, Oracle
• Role
• Trainer, Coach, Advisor, Architect, Designer, App Developer
• More info: www.reflectionit.nl
2
Roslyn: The .NET Compiler Platform
• A reimplementation of C# and VB compilers
• In C# and VB instead of in C++
• Exposes rich public APIs
• Open Source
• https://github.com/dotnet/roslyn
• APIs provide diagnostic analysis, symbol analysis
• Unify tools and diagnostics frameworks
• Extend VS capabilities
3
Install Visual Studio Extensibility Tools
4
5
Download the .NET Compiler Platform SDK
6
Roslyn_SDK.vsix
7
Roslyn_SDK.vsix
8
Extra Extensibility Templates
9
Syntax Visualizer (Naked If)
10
Code Refactoring
Naked If
11
Code Refactoring: Naked If
12
Code Refactoring: Naked If[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CodeRefactoring3CodeRefactoringProvider)), Shared]
internal class CodeRefactoring3CodeRefactoringProvider : CodeRefactoringProvider {
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) {
// TODO: Replace the following code with your own analysis, generating a CodeAction for each refactoring to offer
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// Find the node at the selection.
var node = root.FindNode(context.Span);
// Only offer a refactoring if the selected node is a NAKED If Statement
var ifStatement = node as IfStatementSyntax;
if (ifStatement == null || ifStatement.Statement is BlockSyntax) {
return;
}
// Create the 'Add Braces' Code Action
var action = CodeAction.Create("Add Braces", c => AddBracesAsync(context.Document, ifStatement, c));
// Register this code action.
context.RegisterRefactoring(action);
}
13
Code Refactoring: Naked Ifprivate async Task<Document> AddBracesAsync(Document document, IfStatementSyntax ifStatement, CancellationToken cancellationToken) {
// current statement
var oldStatement = ifStatement.Statement;
// add Braces
var newBlock = SyntaxFactory.Block(oldStatement);
// Replace old with new
var oldRoot = await document.GetSyntaxRootAsync(cancellationToken)
.ConfigureAwait(false);
var newRoot = oldRoot.ReplaceNode(oldStatement, newBlock);
return document.WithSyntaxRoot(newRoot);
}
14
Code Refactoring: Vsix
15
Code Refactoring: F5
16
Analyzer with Code Fix
18
VS2013 Code Analysis (aka FxCop)
19
VS2013 Code Analysis
20
VS2013 Code Analysis
21
VS2015 (Roslyn) Code Analyzers
22
VS2015 (Roslyn) Code Analyzers
23
Analyzer with Code Fix
24
Analyzer with Code Fix
25
Analyzer with Code Fix - F5
26
Analyzer with Code Fix - F5
28
NakedIfAnalyzer - DiagnosticAnalyzer[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class Analyzer4Analyzer : DiagnosticAnalyzer {
public const string DiagnosticId = "NakedIfAnalyzer";
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able,
// you can use regular strings for Title and MessageFormat.
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle),
Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat),
Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription),
Resources.ResourceManager, typeof(Resources));
private const string Category = "Statements";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category,
DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context) {
// TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.IfStatement);
}
29
NakedIfAnalyzer - DiagnosticAnalyzerprivate static void AnalyzeNode(SyntaxNodeAnalysisContext context) {
var node = context.Node;
var ifStatement = node as IfStatementSyntax;
// Only offer a refactoring if the selected node is a NAKED If Statement
//if (node != null && !(ifStatement.Statement is BlockSyntax)) {
if (ifStatement != null &&
(!(ifStatement.Statement is BlockSyntax) ||
(ifStatement.Else != null && !(ifStatement.Else.Statement is BlockSyntax)))) {
// Create the 'Add Braces Fix' Code Diagnostic
var diagnostic = Diagnostic.Create(Rule, ifStatement.IfKeyword.GetLocation(), ifStatement.Condition.ToString());
context.ReportDiagnostic(diagnostic);
}
}
30
NakedIfAnalyzer - DiagnosticAnalyzer
31
NakedIfAnalyzer - CodeFixProvider[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(Analyzer4CodeFixProvider)), Shared]
public class Analyzer4CodeFixProvider : CodeFixProvider {
...
private const string title = "Add missing braces";
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) {
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Find the type declaration identified by the diagnostic.
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<IfStatementSyntax>().First();
// Create the 'Add Braces' Code Action
var action = CodeAction.Create("Add Braces", c => AddBracesAsync(context.Document, declaration, c), title);
context.RegisterCodeFix(action, diagnostic);
}
32
NakedIfAnalyzer - CodeFixProviderprivate async Task<Document> AddBracesAsync(Document document, IfStatementSyntax ifStatement, CancellationToken cancellationToken) {
// Replace old with new
var root = await document.GetSyntaxRootAsync(cancellationToken)
.ConfigureAwait(false);
// Create a list of all non block statements
var l = new List<StatementSyntax>();
if (!(ifStatement.Statement is BlockSyntax)) {
l.Add(ifStatement.Statement);
}
if (ifStatement.Else != null && !(ifStatement.Else.Statement is BlockSyntax)) {
l.Add(ifStatement.Else.Statement);
}
// Replace all non block statements with the Blocked statements
var newRoot = root.ReplaceNodes(l, (x,y) => SyntaxFactory.Block(x));
return document.WithSyntaxRoot(newRoot);
}
33
NakedIfAnalyzer – F5
34
NakedIfAnalyzer – nupkg
35
Links
• C# - Adding a Code Fix to Your Roslyn Analyzer
• https://msdn.microsoft.com/en-us/magazine/dn904670.aspx
• A few important Roslyn API helpers
• http://www.vannevel.net/2015/06/07/a-few-important-roslyn-api-
helpers/
• RoslynQuoter
• http://roslynquoter.azurewebsites.net/
36
StyleCop
37
C# Essentials
38
VSDiagnostics
39
Windows 10.0 Build 10586
• PlatformSpecific.Analyzer - to spot when you're using
platform-specific UWP APIs
• http://blogs.msdn.com/b/lucian/archive/2015/12/03/platformspec
ific-analayzer-to-spot-when-you-re-using-platform-specific-uwp-
apis.aspx
40
Analyzers & Refactorings
41
Roslyn Analyzers
42
Code Anaylizers
• https://github.com/dotnet/roslyn-analyzers
• https://github.com/Vannevelj/VSDiagnostics
• https://github.com/DustinCampbell/CSharpEssentials
• https://github.com/SergeyTeplyakov/ExceptionAnalyzer
• https://github.com/Wintellect/Wintellect.Analyzers
• https://github.com/code-cracker/code-cracker
• https://github.com/olohmann/AsyncAwaitAnalyzer
• https://github.com/mjsabby/RoslynClrHeapAllocationAnalyzer
• https://github.com/DotNetAnalyzers/StyleCopAnalyzers
43
@fonssonnemans
fonssonnemans
reflectionit.nl/blog 44
Copyright
• Copyright © by Reflection IT BV. All rights reserved.
• Some parts quote Microsoft public materials.
• This presentation, its workshops, labs and related materials may
not be distributed or used in any form or manner without prior
written permission by the author.
45