diff --git a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md index 71b3c8d64b028..0672fc5e73971 100644 --- a/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md +++ b/docs/csharp/roslyn-sdk/tutorials/how-to-write-csharp-analyzer-code-fix.md @@ -1,25 +1,25 @@ --- title: "Tutorial: Write your first analyzer and code fix" description: This tutorial provides step-by-step instructions to build an analyzer and code fix using the .NET Compiler SDK (Roslyn APIs). -ms.date: 03/02/2021 +ms.date: 02/17/2026 ms.custom: mvc --- # Tutorial: Write your first analyzer and code fix -The .NET Compiler Platform SDK provides the tools you need to create custom diagnostics (analyzers), code fixes, code refactoring, and diagnostic suppressors that target C# or Visual Basic code. An **analyzer** contains code that recognizes violations of your rule. Your **code fix** contains the code that fixes the violation. The rules you implement can be anything from code structure to coding style to naming conventions and more. The .NET Compiler Platform provides the framework for running analysis as developers are writing code, and all the Visual Studio UI features for fixing code: showing squiggles in the editor, populating the Visual Studio Error List, creating the "light bulb" suggestions and showing the rich preview of the suggested fixes. +The .NET Compiler Platform SDK provides the tools you need to create custom diagnostics (analyzers), code fixes, code refactoring, and diagnostic suppressors that target C# or Visual Basic code. An **analyzer** contains code that recognizes violations of your rule. Your **code fix** contains the code that fixes the violation. The rules you implement can be anything from code structure to coding style to naming conventions and more. The .NET Compiler Platform provides the framework for running analysis as developers are writing code, and all the Visual Studio UI features for fixing code: showing squiggles in the editor, populating the Visual Studio Error List, creating the "light bulb" suggestions, and showing the rich preview of the suggested fixes. -In this tutorial, you'll explore the creation of an **analyzer** and an accompanying **code fix** using the Roslyn APIs. An analyzer is a way to perform source code analysis and report a problem to the user. Optionally, a code fix can be associated with the analyzer to represent a modification to the user's source code. This tutorial creates an analyzer that finds local variable declarations that could be declared using the `const` modifier but are not. The accompanying code fix modifies those declarations to add the `const` modifier. +In this tutorial, you explore the creation of an **analyzer** and an accompanying **code fix** using the Roslyn APIs. An analyzer is a way to perform source code analysis and report a problem to the user. Optionally, you can associate a code fix with the analyzer to represent a modification to the user's source code. This tutorial creates an analyzer that finds local variable declarations that could be declared using the `const` modifier but aren't. The accompanying code fix modifies those declarations to add the `const` modifier. ## Prerequisites -- [Visual Studio 2019](https://www.visualstudio.com/downloads) version 16.8 or later +- [Visual Studio](https://www.visualstudio.com/downloads) -You'll need to install the **.NET Compiler Platform SDK** via the Visual Studio Installer: +You need to install the **.NET Compiler Platform SDK** by using the Visual Studio Installer: [!INCLUDE[interactive-note](~/includes/roslyn-installation.md)] -There are several steps to creating and validating your analyzer: +Creating and validating your analyzer involves several steps: 1. Create the solution. 1. Register the analyzer name and description. @@ -29,12 +29,13 @@ There are several steps to creating and validating your analyzer: ## Create the solution -- In Visual Studio, choose **File > New > Project** to display the New Project dialog. -- Under **Visual C# > Extensibility**, choose **Analyzer with code fix (.NET Standard)**. -- Name your project "**MakeConst**" and click OK. +1. In Visual Studio, select **File** > **New** > **Project/Solution** to open the New Project dialog. +1. In the search box, type **Analyzer** to find the **Analyzer with Code Fix (.NET Standard)** template. +1. Select **Next**. +1. Name your project "**MakeConst**" and select **Create**. > [!NOTE] -> You may get a compilation error (_MSB4062: The "CompareBuildTaskVersion" task could not be loaded"_). To fix this, update the NuGet packages in the solution with NuGet Package Manager or use `Update-Package` in the Package Manager Console window. +> You might get a compilation error (_MSB4062: The "CompareBuildTaskVersion" task couldn't be loaded_). To fix this error, update the NuGet packages in the solution by using NuGet Package Manager or use `Update-Package` in the Package Manager Console window. ## Explore the analyzer template @@ -50,9 +51,9 @@ The analyzer with code fix template creates five projects: > Analyzers should target .NET Standard 2.0 because they can run in .NET Core environment (command line builds) and .NET Framework environment (Visual Studio). > [!TIP] -> When you run your analyzer, you start a second copy of Visual Studio. This second copy uses a different registry hive to store settings. That enables you to differentiate the visual settings in the two copies of Visual Studio. You can pick a different theme for the experimental run of Visual Studio. In addition, don't roam your settings or login to your Visual Studio account using the experimental run of Visual Studio. That keeps the settings different. +> When you run your analyzer, you start a second copy of Visual Studio. This second copy uses a different registry hive to store settings. That difference enables you to differentiate the visual settings in the two copies of Visual Studio. You can pick a different theme for the experimental run of Visual Studio. In addition, don't roam your settings or sign in to your Visual Studio account by using the experimental run of Visual Studio. That keeps the settings different. > -> The hive includes not only the analyzer under development, but also any previous analyzers opened. To reset Roslyn hive, you need to manually delete it from *%LocalAppData%\\Microsoft\\VisualStudio*. The folder name of Roslyn hive will end in `Roslyn`, for example, `16.0_9ae182f9Roslyn`. Note that you may need to clean the solution and rebuild it after deleting the hive. +> The hive includes not only the analyzer under development, but also any previous analyzers opened. To reset Roslyn hive, you need to manually delete it from *%LocalAppData%\\Microsoft\\VisualStudio*. The folder name of Roslyn hive ends in `Roslyn`, for example, `16.0_9ae182f9Roslyn`. You might need to clean the solution and rebuild it after deleting the hive. In the second Visual Studio instance that you just started, create a new C# Console Application project (any target framework will work -- analyzers work at the source level.) Hover over the token with a wavy underline, and the warning text provided by an analyzer appears. @@ -60,12 +61,12 @@ The template creates an analyzer that reports a warning on each type declaration ![Analyzer reporting warning](media/how-to-write-csharp-analyzer-code-fix/report-warning.png) -The template also provides a code fix that changes any type name containing lower case characters to all upper case. You can click on the light bulb displayed with the warning to see the suggested changes. Accepting the suggested changes updates the type name and all references to that type in the solution. Now that you've seen the initial analyzer in action, close the second Visual Studio instance and return to your analyzer project. +The template also provides a code fix that changes any type name containing lower case characters to all upper case. You can select the light bulb displayed with the warning to see the suggested changes. Accepting the suggested changes updates the type name and all references to that type in the solution. Now that you see the initial analyzer in action, close the second Visual Studio instance and return to your analyzer project. You don't have to start a second copy of Visual Studio and create new code to test every change in your analyzer. The template also creates a unit test project for you. That project contains two tests. `TestMethod1` shows the typical format of a test that analyzes code without triggering a diagnostic. `TestMethod2` shows the format of a test that triggers a diagnostic, and then applies a suggested code fix. As you build your analyzer and code fix, you'll write tests for different code structures to verify your work. Unit tests for analyzers are much quicker than testing them interactively with Visual Studio. > [!TIP] -> Analyzer unit tests are a great tool when you know what code constructs should and shouldn't trigger your analyzer. Loading your analyzer in another copy of Visual Studio is a great tool to explore and find constructs you may not have thought about yet. +> Analyzer unit tests are a great tool when you know what code constructs should and shouldn't trigger your analyzer. Loading your analyzer in another copy of Visual Studio is a great tool to explore and find constructs you might not have thought about yet. In this tutorial, you write an analyzer that reports to the user any local variable declarations that can be converted to local constants. For example, consider the following code: @@ -74,18 +75,18 @@ int x = 0; Console.WriteLine(x); ``` -In the code above, `x` is assigned a constant value and is never modified. It can be declared using the `const` modifier: +In the code preceding, `x` is assigned a constant value and is never modified. It can be declared by using the `const` modifier: ```csharp const int x = 0; Console.WriteLine(x); ``` -The analysis to determine whether a variable can be made constant is involved, requiring syntactic analysis, constant analysis of the initializer expression and dataflow analysis to ensure that the variable is never written to. The .NET Compiler Platform provides APIs that make it easier to perform this analysis. +The analysis to determine whether a variable can be made constant is involved, requiring syntactic analysis, constant analysis of the initializer expression, and dataflow analysis to ensure that the variable is never written to. The .NET Compiler Platform provides APIs that make it easier to perform this analysis. ## Create analyzer registrations -The template creates the initial `DiagnosticAnalyzer` class, in the *MakeConstAnalyzer.cs* file. This initial analyzer shows two important properties of every analyzer. +The template creates the initial `DiagnosticAnalyzer` class in the *MakeConstAnalyzer.cs* file. This initial analyzer shows two important properties of every analyzer. - Every diagnostic analyzer must provide a `[DiagnosticAnalyzer]` attribute that describes the language it operates on. - Every diagnostic analyzer must derive (directly or indirectly) from the class. @@ -95,19 +96,19 @@ The template also shows the basic features that are part of any analyzer: 1. Register actions. The actions represent code changes that should trigger your analyzer to examine code for violations. When Visual Studio detects code edits that match a registered action, it calls your analyzer's registered method. 1. Create diagnostics. When your analyzer detects a violation, it creates a diagnostic object that Visual Studio uses to notify the user of the violation. -You register actions in your override of method. In this tutorial, you'll visit **syntax nodes** looking for local declarations, and see which of those have constant values. If a declaration could be constant, your analyzer will create and report a diagnostic. +You register actions in your override of method. In this tutorial, you'll visit **syntax nodes** looking for local declarations, and see which of those have constant values. If a declaration could be constant, your analyzer creates and reports a diagnostic. -The first step is to update the registration constants and `Initialize` method so these constants indicate your "Make Const" analyzer. Most of the string constants are defined in the string resource file. You should follow that practice for easier localization. Open the *Resources.resx* file for the **MakeConst** analyzer project. This displays the resource editor. Update the string resources as follows: +The first step is to update the registration constants and `Initialize` method so these constants indicate your "Make Const" analyzer. Most of the string constants are defined in the string resource file. Follow that practice for easier localization. Open the *Resources.resx* file for the **MakeConst** analyzer project. This action displays the resource editor. Update the string resources as follows: -- Change `AnalyzerDescription` to ":::no-loc text="Variables that are not modified should be made constants.":::". +- Change `AnalyzerDescription` to ":::no-loc text="Variables that aren't modified should be made constants.":::". - Change `AnalyzerMessageFormat` to ":::no-loc text="Variable '{0}' can be made constant":::". - Change `AnalyzerTitle` to ":::no-loc text="Variable can be made constant":::". -When you have finished, the resource editor should appear as shown in the following figure: +When you finish, the resource editor should appear as shown in the following figure: ![Update string resources](media/how-to-write-csharp-analyzer-code-fix/update-string-resources.png) -The remaining changes are in the analyzer file. Open *MakeConstAnalyzer.cs* in Visual Studio. Change the registered action from one that acts on symbols to one that acts on syntax. In the `MakeConstAnalyzerAnalyzer.Initialize` method, find the line that registers the action on symbols: +Make the remaining changes in the analyzer file. Open *MakeConstAnalyzer.cs* in Visual Studio. Change the registered action from one that acts on symbols to one that acts on syntax. In the `MakeConstAnalyzerAnalyzer.Initialize` method, find the line that registers the action on symbols: ```csharp context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); @@ -117,7 +118,7 @@ Replace it with the following line: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="RegisterNodeAction"::: -After that change, you can delete the `AnalyzeSymbol` method. This analyzer examines , not statements. Notice that `AnalyzeNode` has red squiggles under it. The code you just added references an `AnalyzeNode` method that hasn't been declared. Declare that method using the following code: +After that change, you can delete the `AnalyzeSymbol` method. This analyzer examines , not statements. Notice that `AnalyzeNode` has red squiggles under it. The code you just added references an `AnalyzeNode` method that isn't declared. Declare that method by using the following code: ```csharp private void AnalyzeNode(SyntaxNodeAnalysisContext context) @@ -131,7 +132,7 @@ Change the `Category` to ":::no-loc text="Usage":::" in *MakeConstAnalyzer.cs* a ## Find local declarations that could be const -It's time to write the first version of the `AnalyzeNode` method. It should look for a single local declaration that could be `const` but is not, like the following code: +Now you're ready to write the first version of the `AnalyzeNode` method. It should look for a single local declaration that could be `const` but isn't, like the following code: ```csharp int x = 0; @@ -146,9 +147,9 @@ This cast always succeeds because your analyzer registered for changes to local :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="BailOutOnConst"::: -Finally, you need to check that the variable could be `const`. That means making sure it is never assigned after it is initialized. +Finally, you need to check that the variable could be `const`. That check makes sure the variable is never assigned after it's initialized. -You'll perform some semantic analysis using the . You use the `context` argument to determine whether the local variable declaration can be made `const`. A represents all of semantic information in a single source file. You can learn more in the article that covers [semantic models](../work-with-semantics.md). You'll use the to perform data flow analysis on the local declaration statement. Then, you use the results of this data flow analysis to ensure that the local variable isn't written with a new value anywhere else. Call the extension member to retrieve the for the variable and check that it isn't contained with the collection of the data flow analysis. Add the following code to the end of the `AnalyzeNode` method: +Perform some semantic analysis by using the . Use the `context` argument to determine whether the local variable declaration can be made `const`. A represents all of semantic information in a single source file. You can learn more in the article that covers [semantic models](../work-with-semantics.md). Use the to perform data flow analysis on the local declaration statement. Then, use the results of this data flow analysis to ensure that the local variable isn't written with a new value anywhere else. Call the extension member to retrieve the for the variable and check that it isn't contained within the collection of the data flow analysis. Add the following code to the end of the `AnalyzeNode` method: ```csharp // Perform data flow analysis on the local declaration. @@ -164,7 +165,7 @@ if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol)) } ``` -The code just added ensures that the variable isn't modified, and can therefore be made `const`. It's time to raise the diagnostic. Add the following code as the last line in `AnalyzeNode`: +The code you just added ensures that the variable isn't modified, and can therefore be made `const`. It's time to raise the diagnostic. Add the following code as the last line in `AnalyzeNode`: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="ReportDiagnostic"::: @@ -178,7 +179,7 @@ Console.WriteLine(x); The light bulb should appear, and your analyzer should report a diagnostic. However, depending on your version of Visual Studio, you'll either see: * The light bulb, which still uses the template generated code fix, will tell you it can be made upper case. -* A banner message at the top of the editor saying the 'MakeConstCodeFixProvider' encountered an error and has been disabled.'. This is because the code fix provider hasn't yet been changed and still expects to find `TypeDeclarationSyntax` elements instead of `LocalDeclarationStatementSyntax` elements. +* A banner message at the top of the editor saying the 'MakeConstCodeFixProvider' encountered an error and has been disabled. This error occurs because the code fix provider hasn't yet been changed and still expects to find `TypeDeclarationSyntax` elements instead of `LocalDeclarationStatementSyntax` elements. The next section explains how to write the code fix. @@ -204,7 +205,7 @@ All code fix providers derive from MakeConstAsync(Document document, } ``` -Your new `MakeConstAsync` method will transform the representing the user's source file into a new that now contains a `const` declaration. +Your new `MakeConstAsync` method transforms the representing the user's source file into a new that now contains a `const` declaration. You create a new `const` keyword token to insert at the front of the declaration statement. Be careful to first remove any leading trivia from the first token of the declaration statement and attach it to the `const` token. Add the following code to the `MakeConstAsync` method: @@ -255,15 +256,15 @@ Add the following code to the end of the `MakeConstAsync` method: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs" id="ReplaceDocument"::: -Your code fix is ready to try. Press F5 to run the analyzer project in a second instance of Visual Studio. In the second Visual Studio instance, create a new C# Console Application project and add a few local variable declarations initialized with constant values to the Main method. You'll see that they are reported as warnings as below. +Your code fix is ready to try. Press F5 to run the analyzer project in a second instance of Visual Studio. In the second Visual Studio instance, create a new C# Console Application project and add a few local variable declarations initialized with constant values to the Main method. You see that they're reported as warnings as shown in the following image. ![Can make const warnings](media/how-to-write-csharp-analyzer-code-fix/make-const-warning.png) -You've made a lot of progress. There are squiggles under the declarations that can be made `const`. But there is still work to do. This works fine if you add `const` to the declarations starting with `i`, then `j` and finally `k`. But, if you add the `const` modifier in a different order, starting with `k`, your analyzer creates errors: `k` can't be declared `const`, unless `i` and `j` are both already `const`. You've got to do more analysis to ensure you handle the different ways variables can be declared and initialized. +You've made a lot of progress. There are squiggles under the declarations that can be made `const`. But there is still work to do. This works fine if you add `const` to the declarations starting with `i`, then `j`, and finally `k`. But, if you add the `const` modifier in a different order, starting with `k`, your analyzer creates errors: `k` can't be declared `const`, unless `i` and `j` are both already `const`. You need to do more analysis to ensure you handle the different ways variables can be declared and initialized. ## Build unit tests -Your analyzer and code fix work on a simple case of a single declaration that can be made const. There are numerous possible declaration statements where this implementation makes mistakes. You'll address these cases by working with the unit test library written by the template. It's much faster than repeatedly opening a second copy of Visual Studio. +Your analyzer and code fix work on a simple case of a single declaration that you can make const. Numerous possible declaration statements exist where this implementation makes mistakes. You'll address these cases by working with the unit test library written by the template. It's much faster than repeatedly opening a second copy of Visual Studio. Open the *MakeConstUnitTests.cs* file in the unit test project. The template created two tests that follow the two common patterns for an analyzer and code fix unit test. `TestMethod1` shows the pattern for a test that ensures the analyzer doesn't report a diagnostic when it shouldn't. `TestMethod2` shows the pattern for reporting a diagnostic and running the code fix. @@ -283,7 +284,7 @@ Run this test to make sure it passes. In Visual Studio, open the **Test Explorer ## Create tests for valid declarations -As a general rule, analyzers should exit as quickly as possible, doing minimal work. Visual Studio calls registered analyzers as the user edits code. Responsiveness is a key requirement. There are several test cases for code that should not raise your diagnostic. Your analyzer already handles several of those tests. Add the following test methods to represent those cases: +As a general rule, analyzers should exit as quickly as possible, doing minimal work. Visual Studio calls registered analyzers as the user edits code. Responsiveness is a key requirement. Several test cases exist for code that shouldn't raise your diagnostic. Your analyzer already handles several of those tests. Add the following test methods to represent those cases: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="VariableAssigned"::: @@ -299,7 +300,7 @@ These tests pass because your analyzer already handles these conditions: Next, add test methods for conditions you haven't handled yet: -- Declarations where the initializer is not a constant, because they can't be compile-time constants: +- Declarations where the initializer isn't a constant, because they can't be compile-time constants: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="InitializerNotConstant"::: @@ -307,13 +308,13 @@ It can be even more complicated because C# allows multiple declarations as one s :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="MultipleInitializers"::: -The variable `i` can be made constant, but the variable `j` cannot. Therefore, this statement cannot be made a const declaration. +The variable `i` can be constant, but the variable `j` can't. Therefore, this statement can't be a const declaration. -Run your tests again, and you'll see these last two test cases fail. +Run your tests again, and you see these last two test cases fail. ## Update your analyzer to ignore correct declarations -You need some enhancements to your analyzer's `AnalyzeNode` method to filter out code that matches these conditions. They are all related conditions, so similar changes will fix all these conditions. Make the following changes to `AnalyzeNode`: +You need to enhance your analyzer's `AnalyzeNode` method to filter out code that matches these conditions. These conditions are all related, so similar changes fix all these conditions. Make the following changes to `AnalyzeNode`: - Your semantic analysis examined a single variable declaration. This code needs to be in a `foreach` loop that examines all the variables declared in the same statement. - Each declared variable needs to have an initializer. @@ -370,29 +371,29 @@ foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Varia } ``` -The first `foreach` loop examines each variable declaration using syntactic analysis. The first check guarantees that the variable has an initializer. The second check guarantees that the initializer is a constant. The second loop has the original semantic analysis. The semantic checks are in a separate loop because it has a greater impact on performance. Run your tests again, and you should see them all pass. +The first `foreach` loop examines each variable declaration by using syntactic analysis. The first check guarantees that the variable has an initializer. The second check guarantees that the initializer is a constant. The second loop has the original semantic analysis. The semantic checks are in a separate loop because they have a greater impact on performance. Run your tests again, and you should see them all pass. ## Add the final polish -You're almost done. There are a few more conditions for your analyzer to handle. Visual Studio calls analyzers while the user is writing code. It's often the case that your analyzer will be called for code that doesn't compile. The diagnostic analyzer's `AnalyzeNode` method does not check to see if the constant value is convertible to the variable type. So, the current implementation will happily convert an incorrect declaration such as `int i = "abc"` to a local constant. Add a test method for this case: +You're almost done. There are a few more conditions for your analyzer to handle. Visual Studio calls analyzers while the user is writing code. It's often the case that your analyzer is called for code that doesn't compile. The diagnostic analyzer's `AnalyzeNode` method doesn't check if the constant value is convertible to the variable type. So, the current implementation happily converts an incorrect declaration such as `int i = "abc"` to a local constant. Add a test method for this case: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="DeclarationIsInvalid"::: -In addition, reference types are not handled properly. The only constant value allowed for a reference type is `null`, except in the case of , which allows string literals. In other words, `const string s = "abc"` is legal, but `const object s = "abc"` is not. This code snippet verifies that condition: +In addition, reference types aren't handled properly. The only constant value allowed for a reference type is `null`, except in the case of , which allows string literals. In other words, `const string s = "abc"` is legal, but `const object s = "abc"` is not. This code snippet verifies that condition: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="DeclarationIsntString"::: -To be thorough, you need to add another test to make sure that you can create a constant declaration for a string. The following snippet defines both the code that raises the diagnostic, and the code after the fix has been applied: +To be thorough, you need to add another test to make sure that you can create a constant declaration for a string. The following snippet defines both the code that raises the diagnostic, and the code after the fix is applied: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="ConstantIsString"::: -Finally, if a variable is declared with the `var` keyword, the code fix does the wrong thing and generates a `const var` declaration, which is not supported by the C# language. To fix this bug, the code fix must replace the `var` keyword with the inferred type's name: +Finally, if a variable is declared with the `var` keyword, the code fix does the wrong thing and generates a `const var` declaration, which the C# language doesn't support. To fix this bug, the code fix must replace the `var` keyword with the inferred type's name: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs" id="VarDeclarations"::: -Fortunately, all of the above bugs can be addressed using the same techniques that you just learned. +Fortunately, you can address all of the above bugs by using the same techniques that you just learned. -To fix the first bug, first open *MakeConstAnalyzer.cs* and locate the foreach loop where each of the local declaration's initializers are checked to ensure that they're assigned with constant values. Immediately _before_ the first foreach loop, call `context.SemanticModel.GetTypeInfo()` to retrieve detailed information about the declared type of the local declaration: +To fix the first bug, open *MakeConstAnalyzer.cs* and locate the `foreach` loop where each of the local declaration's initializers are checked to ensure that they're assigned constant values. Immediately _before_ the first `foreach` loop, call `context.SemanticModel.GetTypeInfo()` to retrieve detailed information about the declared type of the local declaration: :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="VariableConvertedType"::: @@ -400,7 +401,7 @@ Then, inside your `foreach` loop, check each initializer to make sure it's conve :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="BailOutOnUserDefinedConversion"::: -The next change builds upon the last one. Before the closing curly brace of the first foreach loop, add the following code to check the type of the local declaration when the constant is a string or null. +The next change builds upon the last one. Before the closing curly brace of the first `foreach` loop, add the following code to check the type of the local declaration when the constant is a string or null. :::code language="csharp" source="snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs" id="HandleSpecialCases"::: @@ -424,9 +425,9 @@ using Microsoft.CodeAnalysis.Simplification; Run your tests, and they should all pass. Congratulate yourself by running your finished analyzer. Press Ctrl+F5 to run the analyzer project in a second instance of Visual Studio with the Roslyn Preview extension loaded. -- In the second Visual Studio instance, create a new C# Console Application project and add `int x = "abc";` to the Main method. Thanks to the first bug fix, no warning should be reported for this local variable declaration (though there's a compiler error as expected). -- Next, add `object s = "abc";` to the Main method. Because of the second bug fix, no warning should be reported. -- Finally, add another local variable that uses the `var` keyword. You'll see that a warning is reported and a suggestion appears beneath to the left. +- In the second Visual Studio instance, create a new C# Console Application project and add `int x = "abc";` to the Main method. Thanks to the first bug fix, no warning is reported for this local variable declaration (though there's a compiler error as expected). +- Next, add `object s = "abc";` to the Main method. Because of the second bug fix, no warning is reported. +- Finally, add another local variable that uses the `var` keyword. You see that a warning is reported and a suggestion appears beneath to the left. - Move the editor caret over the squiggly underline and press Ctrl+.. to display the suggested code fix. Upon selecting your code fix, note that the `var` keyword is now handled correctly. Finally, add the following code: @@ -439,7 +440,7 @@ int k = i + j; After these changes, you get red squiggles only on the first two variables. Add `const` to both `i` and `j`, and you get a new warning on `k` because it can now be `const`. -Congratulations! You've created your first .NET Compiler Platform extension that performs on-the-fly code analysis to detect an issue and provides a quick fix to correct it. Along the way, you've learned many of the code APIs that are part of the .NET Compiler Platform SDK (Roslyn APIs). You can check your work against the [completed sample](https://github.com/dotnet/samples/tree/main/csharp/roslyn-sdk/Tutorials/MakeConst) in our samples GitHub repository. +Congratulations! You created your first .NET Compiler Platform extension that performs on-the-fly code analysis to detect an issue and provides a quick fix to correct it. Along the way, you learned many of the code APIs that are part of the .NET Compiler Platform SDK (Roslyn APIs). You can check your work against the [completed sample](https://github.com/dotnet/samples/tree/main/csharp/roslyn-sdk/Tutorials/MakeConst) in the samples GitHub repository. ## Other resources diff --git a/includes/roslyn-installation.md b/includes/roslyn-installation.md index 03a3d6da0dafb..0c62319cdf916 100644 --- a/includes/roslyn-installation.md +++ b/includes/roslyn-installation.md @@ -10,7 +10,8 @@ The .NET Compiler Platform SDK is not automatically selected as part of the Visu 1. Select **Modify** 1. Check the **Visual Studio extension development** workload. 1. Open the **Visual Studio extension development** node in the summary tree. -1. Check the box for **.NET Compiler Platform SDK**. You'll find it last under the optional components. +1. Make sure the box for **.NET Compiler Platform SDK** is checked. +1. Select **Modify**. Optionally, you'll also want the **DGML editor** to display graphs in the visualizer: @@ -23,6 +24,7 @@ Optionally, you'll also want the **DGML editor** to display graphs in the visual 1. Select **Modify** 1. Select the **Individual components** tab 1. Check the box for **.NET Compiler Platform SDK**. You'll find it at the top under the **Compilers, build tools, and runtimes** section. +1. Select **Modify**. Optionally, you'll also want the **DGML editor** to display graphs in the visualizer: