{"id":26702,"date":"2020-03-04T10:00:25","date_gmt":"2020-03-04T17:00:25","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=26702"},"modified":"2020-03-04T11:11:05","modified_gmt":"2020-03-04T18:11:05","slug":"how-to-write-a-roslyn-analyzer","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/how-to-write-a-roslyn-analyzer\/","title":{"rendered":"How to write a Roslyn Analyzer"},"content":{"rendered":"<p>Roslyn analyzers inspect your code for style, quality, maintainability, design and other issues. Because they are powered by the .NET Compiler Platform, they can produce warnings in your code as you type even before you\u2019ve finished the line. In other words, you don\u2019t have to build your code to find out that you made a mistake. Analyzers can also surface an automatic code fix through the Visual Studio light bulb prompt that allows you to clean up your code immediately. With live, project-based code analyzers in Visual Studio, API authors can ship domain-specific code analysis as part of their NuGet packages.<\/p>\n<p>You don\u2019t have to be a professional API author to write an analyzer. In this post, I&#8217;ll show you how to write your very first analyzer.<\/p>\n<p><strong>Getting started<\/strong><\/p>\n<p>In order to create a Roslyn Analyzer project, you need to install the .NET Compiler Platform SDK via the Visual Studio Installer. There are two different ways to find the .NET Compiler Platform SDK in the Visual Studio Installer:<\/p>\n<p>Install using the Visual Studio Installer \u2013 Workloads view:<\/p>\n<ol>\n<li>Run the Visual Studio Installer and select Modify.\n<\/br><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-installer.png\" alt=\"Visual Studio Installer\" width=\"1182\" height=\"422\" class=\"aligncenter size-full wp-image-26704\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-installer.png 1182w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-installer-300x107.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-installer-1024x366.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-installer-768x274.png 768w\" sizes=\"(max-width: 1182px) 100vw, 1182px\" \/> <\/li>\n<p><\/br><\/p>\n<li>Check the Visual Studio extension development workload.\n<\/br><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-extension-development.png\" alt=\"Visual Studio Extension Development Workload\" width=\"1182\" height=\"422\" class=\"aligncenter size-full wp-image-26704\" \/><\/li>\n<\/ol>\n<p>Install using the Visual Studio Installer &#8211; Individual components tab:<\/p>\n<ol>\n<li>Run the Visual Studio Installer and select Modify.<\/li>\n<p><\/br><\/p>\n<li>Select the Individual components tab.<\/li>\n<p><\/br><\/p>\n<li>Check the box for .NET Compiler Platform SDK.\n<\/br><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components.png\" alt=\"Visual Studio Individual Components\" width=\"1713\" height=\"490\" class=\"aligncenter size-full wp-image-26706\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components.png 1713w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components-300x86.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components-1024x293.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components-768x220.png 768w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/visual-studio-individual-components-1536x439.png 1536w\" sizes=\"(max-width: 1713px) 100vw, 1713px\" \/> <\/li>\n<\/ol>\n<p><\/br><\/p>\n<p><strong>Writing an analyzer<\/strong><\/p>\n<p>Let\u2019s begin by creating a syntax tree analyzer. This analyzer generates a syntax warning for any statement that is not enclosed in a block that has curly braces <code>{<\/code> and <code>}<\/code>. For example, the following code generates a warning for both the <code>if<\/code>-statement and the <code>System.Console.WriteLine<\/code> invocation statement, but the <code>while<\/code> statement is not flagged:<\/p>\n<p><\/br><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic.png\" alt=\"Brace Analyzer Diagnostic\" width=\"938\" height=\"427\" class=\"alignnone size-full wp-image-26707\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic.png 938w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-300x137.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-768x350.png 768w\" sizes=\"(max-width: 938px) 100vw, 938px\" \/><\/p>\n<ol>\n<li>Open Visual Studio.<\/li>\n<p><\/br><\/p>\n<li>On the Create a new project dialog search VSIX and select <strong>Analyzer with Code Fix (.NET Standard)<\/strong> in C# and click Next.\n<\/br><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/create-new-project-dialog.png\" alt=\"Create New Project Dialog\" width=\"1429\" height=\"954\" class=\"aligncenter size-full wp-image-26708\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/create-new-project-dialog.png 1429w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/create-new-project-dialog-300x200.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/create-new-project-dialog-1024x684.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/create-new-project-dialog-768x513.png 768w\" sizes=\"(max-width: 1429px) 100vw, 1429px\" \/><\/li>\n<p><\/br><\/p>\n<li>Name your project <strong>BraceAnalyzer<\/strong> and click OK. The solution should contain 3 projects: BraceAnalyzer, BraceAnalyzer.Test, BraceAnalyzer.Vsix.\n<\/br> <img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/analyzer-solution-layout.png\" alt=\"Analyzer Solution Layout\" width=\"624\" height=\"315\" class=\"alignnone size-full wp-image-26709\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/analyzer-solution-layout.png 624w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/analyzer-solution-layout-300x151.png 300w\" sizes=\"(max-width: 624px) 100vw, 624px\" \/>\n              <\/br><\/p>\n<ul>\n<li>BraceAnalyzer: This is the core analyzer project that contains the default analyzer implementation that reports a diagnostic for all type names that contain any lowercase letter.<\/li>\n<li>BraceAnalyzer.Test: This is a unit test project that lets you make sure your analyzer is producing the right diagnostics and fixes.<\/li>\n<li>BraceAnalyzer. Vsix: The VSIX project bundles the analyzer into an extension package (.vsix file). This is the startup project in the solution.<\/li>\n<\/ul>\n<\/li>\n<p><\/br><\/p>\n<li>In the Solution Explorer, open <strong>Resources.resx<\/strong> in the BraceAnalyzer project. This displays the resource editor. <\/li>\n<p><\/br><\/p>\n<li>Replace the existing resource string values for AnalyzerDescription, AnalyzerMessageFormat, and AnalyzerTitle with the following strings:\n              <\/br><\/p>\n<ul>\n<li>Change AnalyzerDescription to <code>Enclose statement with curly braces<\/code>.<\/li>\n<li>Change AnalyzerMessageFormat to <code>`{` brace expected<\/code>.<\/li>\n<li>Change AnalyzerTitle to <code>Enclose statement with curly braces<\/code>. <\/li>\n<\/ul>\n<p><\/br>\n<img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/resources-resx.png\" alt=\"Resources Resx\" width=\"1428\" height=\"330\" class=\"aligncenter size-full wp-image-26710\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/resources-resx.png 1428w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/resources-resx-300x69.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/resources-resx-1024x237.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/resources-resx-768x177.png 768w\" sizes=\"(max-width: 1428px) 100vw, 1428px\" \/>\n<\/li>\n<p><\/br><\/p>\n<li>Within the BraceAnalyzerAnalyzer.cs file, replace the Initialize method implementation with the following code:<\/li>\n<p><\/br><\/p>\n<pre class=\"prettyprint\">public override void Initialize(AnalysisContext context)\n{\n    context.RegisterSyntaxTreeAction(syntaxTreeContext =>\n    {\n        \/\/ Iterate through all statements in the tree\n        var root = syntaxTreeContext.Tree.GetRoot(syntaxTreeContext.CancellationToken);\n        foreach (var statement in root.DescendantNodes().OfType&lt;StatementSyntax&gt;())\n        {\n            \/\/ Skip analyzing block statements \n            if (statement is BlockSyntax)\n            {\n                continue;\n            }\n\n            \/\/ Report issues for all statements that are nested within a statement\n            \/\/ but not a block statement\n            if (statement.Parent is StatementSyntax && !(statement.Parent is BlockSyntax))\n            {\n                var diagnostic = Diagnostic.Create(Rule, statement.GetFirstToken().GetLocation());\n                syntaxTreeContext.ReportDiagnostic(diagnostic);\n            }\n        }\n    });\n}<\/pre>\n<p><\/br><\/p>\n<li>Check your progress by pressing F5 to run your analyzer. Make sure that the BraceAnalyzer.Vsix project is the startup project before pressing F5. Running the VSIX project loads an experimental instance of Visual Studio, which lets Visual Studio keep track of a separate set of Visual Studio extensions. <\/li>\n<p><\/br><\/p>\n<li>In the Visual Studio instance, create a new C# class library with the following code to verify that the analyzer diagnostic is neither reported for the method block nor the <code>while<\/code> statement, but is reported for the <code>if<\/code> statement and <code>System.Console.WriteLine<\/code> invocation statement:\n<\/br> <img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic.png\" alt=\"Brace Analyzer Diagnostic\" width=\"938\" height=\"427\" class=\"aligncenter size-full wp-image-26707\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic.png 938w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-300x137.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-768x350.png 768w\" sizes=\"(max-width: 938px) 100vw, 938px\" \/> <\/li>\n<p><\/br><\/p>\n<li>Now, add curly braces around the <code>System.Console.WriteLine<\/code> invocation statement and verify that the only single warning is now reported for the <code>if<\/code> statement:\n<\/br> <img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-for-if-statement.png\" alt=\"Brace Diagnostic For If Statement\" width=\"873\" height=\"410\" class=\"aligncenter size-full wp-image-26711\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-for-if-statement.png 873w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-for-if-statement-300x141.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2022\/03\/brace-diagnostic-for-if-statement-768x361.png 768w\" sizes=\"(max-width: 873px) 100vw, 873px\" \/> <\/li>\n<\/ol>\n<p><\/br><\/p>\n<p><strong>Writing a code fix<\/strong><\/p>\n<p>An analyzer can provide one or more code fixes. A code fix defines an edit that addresses the reported issue. For the analyzer that you created, you can provide a code fix that encloses a statement with a curly brace.<\/p>\n<ol>\n<li>Open the BraceAnalyzerCodeFixProvider.cs file. This code fix is already wired up to the Diagnostic ID produced by your diagnostic analyzer, but it doesn&#8217;t yet implement the right code transform. <\/li>\n<p><\/br><\/p>\n<li>Change the title string to \u201cAdd brace&#8221;: <\/li>\n<p><\/br><\/p>\n<pre class=\"prettyprint\">private const string title = \"Add brace\";<\/pre>\n<p><\/br><\/p>\n<li>Change the following line to register a code fix. Your fix will create a new document that results from adding braces.<\/li>\n<p><\/br><\/p>\n<pre class=\"prettyprint\">context.RegisterCodeFix(\n        CodeAction.Create(\n            title: title,\n            createChangedDocument: c => AddBracesAsync(context.Document, diagnostic, root),\n            equivalenceKey: title),\n        diagnostic);<\/pre>\n<p><\/br><\/p>\n<li>You&#8217;ll notice red squiggles in the code you just added on the <code>AddBracesAsync<\/code> symbol. Add a declaration for <code>AddBracesAsync<\/code> by replacing the <code>MakeUpperCaseAsync<\/code> method with the following code: <\/li>\n<p><\/br><\/p>\n<pre class=\"prettyprint\">Task&lt;Document&gt;<Document> AddBracesAsync(Document document, Diagnostic diagnostic, SyntaxNode root)\n        {\n            var statement = root.FindNode(diagnostic.Location.SourceSpan).FirstAncestorOrSelf&lt;StatementSyntax&gt;();\n            var newRoot = root.ReplaceNode(statement, SyntaxFactory.Block(statement));\n            return Task.FromResult(document.WithSyntaxRoot(newRoot));\n        }<\/pre>\n<p><\/br><\/p>\n<li>Press F5 to run the analyzer project in a second instance of Visual Studio. Place your cursor on the diagnostic and press (<strong>Ctrl+.<\/strong>) to trigger the <strong>Quick Actions and Refactorings<\/strong> menu. Notice your code fix to add a brace!\n<\/br> <img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/03\/brace-analyzer-code-fix2.png\" alt=\"Image brace analyzer code fix2\" width=\"1267\" height=\"792\" class=\"aligncenter size-full wp-image-26840\" srcset=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/03\/brace-analyzer-code-fix2.png 1267w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/03\/brace-analyzer-code-fix2-300x188.png 300w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/03\/brace-analyzer-code-fix2-1024x640.png 1024w, https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2020\/03\/brace-analyzer-code-fix2-768x480.png 768w\" sizes=\"(max-width: 1267px) 100vw, 1267px\" \/> <\/li>\n<\/ol>\n<p><\/br><\/p>\n<p><strong>Conclusion<\/strong><\/p>\n<p>Congratulations! You&#8217;ve created your first Roslyn analyzer that performs on-the-fly code analysis to detect an issue and provides a code fix to correct it. Now that you\u2019re familiar with the <a href=\"https:\/\/docs.microsoft.com\/dotnet\/csharp\/roslyn-sdk\/\">.NET Compiler Platform SDK<\/a> (Roslyn APIs), writing your next analyzer will be a breeze.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Roslyn analyzers inspect your code for style, quality, maintainability, design and other issues. Because they are powered by the .NET Compiler Platform, they can produce warnings in your code as you type even before you\u2019ve finished the line. In other words, you don\u2019t have to build your code to find out that you made a [&hellip;]<\/p>\n","protected":false},"author":5818,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[],"class_list":["post-26702","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet"],"acf":[],"blog_post_summary":"<p>Roslyn analyzers inspect your code for style, quality, maintainability, design and other issues. Because they are powered by the .NET Compiler Platform, they can produce warnings in your code as you type even before you\u2019ve finished the line. In other words, you don\u2019t have to build your code to find out that you made a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/26702","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/5818"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=26702"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/26702\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=26702"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=26702"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=26702"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}