developing roslyn refactorings and code analyzersqnhdevdays.nl/developing roslyn refactorings...

Post on 20-Jun-2020

23 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

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

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

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

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

top related