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

43
Developing Roslyn Refactorings and Code Analyzers Fons Sonnemans @fonssonnemans

Upload: others

Post on 20-Jun-2020

7 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Developing Roslyn Refactorings and Code

Analyzers

Fons Sonnemans

@fonssonnemans

Page 2: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 3: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 4: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Install Visual Studio Extensibility Tools

4

Page 5: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

5

Page 6: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Download the .NET Compiler Platform SDK

6

Page 7: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Roslyn_SDK.vsix

7

Page 8: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Roslyn_SDK.vsix

8

Page 9: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Extra Extensibility Templates

9

Page 10: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Syntax Visualizer (Naked If)

10

Page 11: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Code Refactoring

Naked If

11

Page 12: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Code Refactoring: Naked If

12

Page 13: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 14: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 15: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Code Refactoring: Vsix

15

Page 16: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Code Refactoring: F5

16

Page 17: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Analyzer with Code Fix

18

Page 18: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

VS2013 Code Analysis (aka FxCop)

19

Page 19: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

VS2013 Code Analysis

20

Page 20: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

VS2013 Code Analysis

21

Page 21: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

VS2015 (Roslyn) Code Analyzers

22

Page 22: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

VS2015 (Roslyn) Code Analyzers

23

Page 23: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Analyzer with Code Fix

24

Page 24: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Analyzer with Code Fix

25

Page 25: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Analyzer with Code Fix - F5

26

Page 26: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

Analyzer with Code Fix - F5

28

Page 27: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 28: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 29: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

NakedIfAnalyzer - DiagnosticAnalyzer

31

Page 30: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 31: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 32: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

NakedIfAnalyzer – F5

34

Page 33: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

NakedIfAnalyzer – nupkg

35

Page 34: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 35: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

StyleCop

37

Page 38: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 41: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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

Page 43: Developing Roslyn Refactorings and Code Analyzersqnhdevdays.nl/Developing Roslyn Refactorings and... · // TODO: Replace the following code with your own analysis, generating a CodeAction

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