diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 4257fd77336e2..5800d9808a455 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -776,18 +776,6 @@ updates:
interval: "weekly"
day: "wednesday"
open-pull-requests-limit: 5
- - package-ecosystem: "nuget"
- directory: "/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test" #MakeConst.Test.csproj
- schedule:
- interval: "weekly"
- day: "wednesday"
- open-pull-requests-limit: 5
- - package-ecosystem: "nuget"
- directory: "/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst" #MakeConst.csproj
- schedule:
- interval: "weekly"
- day: "wednesday"
- open-pull-requests-limit: 5
- package-ecosystem: "nuget"
directory: "/samples/snippets/visualbasic/VS_Snippets_Misc/tpldataflow_cancellationwinforms/vb/cancellationwinforms" #cancellationwinforms.vbproj
schedule:
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 53756b759c8ac..af7c563637b7b 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,19 +1,19 @@
---
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: 08/01/2018
+ms.date: 03/02/2021
ms.custom: mvc
---
# Tutorial: Write your first analyzer and code fix
-The .NET Compiler Platform SDK provides the tools you need to create custom warnings that target C# or Visual Basic code. Your **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, an analyzer can also provide a code fix that represents 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'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.
## Prerequisites
-- [Visual Studio 2019](https://www.visualstudio.com/downloads) version 16.7 or later
+- [Visual Studio 2019](https://www.visualstudio.com/downloads) version 16.8 or later
You'll need to install the **.NET Compiler Platform SDK** via the Visual Studio Installer:
@@ -27,34 +27,26 @@ There are several steps to creating and validating your analyzer:
1. Implement the code fix to accept recommendations.
1. Improve the analysis through unit tests.
-## Explore the analyzer template
-
-Your analyzer reports to the user any local variable declarations that can be converted to local constants. For example, consider the following code:
-
-```csharp
-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:
-
-```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 first step is to create a new C# **Analyzer with code fix** project.
+## 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.
-The analyzer with code fix template creates three projects: one contains the analyzer and code fix, the second is a unit test project, and the third is the VSIX project. The default startup project is the VSIX project. Press F5 to start the VSIX project. This starts a second instance of Visual Studio that has loaded your new analyzer.
+## Explore the analyzer template
+
+The analyzer with code fix template creates five projects:
+
+- **MakeConst**, which contains the analyzer.
+- **MakeConst.CodeFixes**, which contains the code fix.
+- **MakeConst.Package**, which is used to produce NuGet package for the analyzer and code fix.
+- **MakeConst.Test**, which is a unit test project.
+- **MakeConst.Vsix**, which is the default startup project that starts a second instance of Visual Studio that has loaded your new analyzer. Press F5 to start the VSIX project.
> [!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.
-In the second Visual Studio instance that you just started, create a new C# Console Application project (either .NET Core or .NET Framework project 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.
+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.
The template creates an analyzer that reports a warning on each type declaration where the type name contains lowercase letters, as shown in the following figure:
@@ -67,12 +59,28 @@ You don't have to start a second copy of Visual Studio and create new code to te
> [!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.
+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:
+
+```csharp
+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:
+
+```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.
+
## 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 from the class.
+- Every diagnostic analyzer must derive (directly or indirectly) from the class.
The template also shows the basic features that are part of any analyzer:
@@ -81,17 +89,17 @@ The template also shows the basic features that are part of any analyzer:
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.
-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. 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:
-- Change `AnalyzerTitle` to "Variable can be made constant".
-- Change `AnalyzerMessageFormat` to "Can be made constant".
-- Change `AnalyzerDescription` to "Make Constant".
+- Change `AnalyzerDescription` to ":::no-loc text="Variables that are not 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":::.
-Also, change the **Access Modifier** drop-down to `public`. That makes it easier to use these constants in unit tests. When you have finished, the resource editor should appear as follow figure shows:
+When you have finished, the resource editor should appear as follow figure shows:

-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:
+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:
```csharp
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
@@ -99,7 +107,7 @@ context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
Replace it with the following line:
-[!code-csharp[Register the node action](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstAnalyzer.cs#RegisterNodeAction "Register a node action")]
+[!code-csharp[Register the node action](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#RegisterNodeAction "Register a node action")]
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:
@@ -109,11 +117,9 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
}
```
-Change the `Category` to "Usage" in **MakeConstAnalyzer.cs** as shown in the following code:
+Change the `Category` to ":::no-loc text="Usage":::" in *MakeConstAnalyzer.cs* as shown in the following code:
-```csharp
-private const string Category = "Usage";
-```
+[!code-csharp[Category constant](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#Category "Change category to Usage")]
## Find local declarations that could be const
@@ -124,21 +130,13 @@ int x = 0;
Console.WriteLine(x);
```
-The first step is to find local declarations. Add the following code to `AnalyzeNode` in **MakeConstAnalyzer.cs**:
+The first step is to find local declarations. Add the following code to `AnalyzeNode` in *MakeConstAnalyzer.cs*:
-```csharp
-var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
-```
+[!code-csharp[localDeclaration variable](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#LocalDeclaration "Add localDeclaration variable")]
This cast always succeeds because your analyzer registered for changes to local declarations, and only local declarations. No other node type triggers a call to your `AnalyzeNode` method. Next, check the declaration for any `const` modifiers. If you find them, return immediately. The following code looks for any `const` modifiers on the local declaration:
-```csharp
-// make sure the declaration isn't already const:
-if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
-{
- return;
-}
-```
+[!code-csharp[bail-out on const keyword](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs#BailOutOnConst "bail-out on const keyword")]
Finally, you need to check that the variable could be `const`. That means making sure it is never assigned after it is initialized.
@@ -146,12 +144,12 @@ You'll perform some semantic analysis using the F5 to run your analyzer. You can load the console application you created earlier and then add the following test code:
@@ -177,33 +173,34 @@ The light bulb should appear, and your analyzer should report a diagnostic. Howe
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 inserts the const keyword:
-```csharp
-const int x = 0;
+```diff
+- int x = 0;
++ const int x = 0;
Console.WriteLine(x);
```
The user chooses it from the light bulb UI in the editor and Visual Studio changes the code.
-Open the **MakeConstCodeFixProvider.cs** file added by the template. This code fix is already wired up to the Diagnostic ID produced by your diagnostic analyzer, but it doesn't yet implement the right code transform. First you should remove some of the template code. Change the title string to "Make constant":
+Open *CodeFixResources.resx* file and change `CodeFixTitle` to ":::no-loc text="Make constant":::".
-[!code-csharp[Update the CodeFix title](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs#CodeFixTitle "Update the CodeFix title")]
+Open the *MakeConstCodeFixProvider.cs* file added by the template. This code fix is already wired up to the Diagnostic ID produced by your diagnostic analyzer, but it doesn't yet implement the right code transform.
Next, delete the `MakeUppercaseAsync` method. It no longer applies.
All code fix providers derive from . They all override to report available code fixes. In `RegisterCodeFixesAsync`, change the ancestor node type you're searching for to a to match the diagnostic:
-[!code-csharp[Find local declaration node](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs#FindDeclarationNode "Find the local declaration node that raised the diagnostic")]
+[!code-csharp[Find local declaration node](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#FindDeclarationNode "Find the local declaration node that raised the diagnostic")]
Next, change the last line to register a code fix. Your fix will create a new document that results from adding the `const` modifier to an existing declaration:
-[!code-csharp[Register the new code fix](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs#RegisterCodeFix "Register the new code fix")]
+[!code-csharp[Register the new code fix](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#RegisterCodeFix "Register the new code fix")]
You'll notice red squiggles in the code you just added on the symbol `MakeConstAsync`. Add a declaration for `MakeConstAsync` like the following code:
```csharp
-private async Task MakeConstAsync(Document document,
- LocalDeclarationStatementSyntax localDeclaration,
- CancellationToken cancellationToken)
+private static async Task MakeConstAsync(Document document,
+ LocalDeclarationStatementSyntax localDeclaration,
+ CancellationToken cancellationToken)
{
}
```
@@ -212,22 +209,22 @@ Your new `MakeConstAsync` method will transform the 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.
@@ -251,106 +248,51 @@ Your code fix is ready to try. Press F5 to run the analyzer project
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.
-## Build data driven tests
+## 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.
-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.
-
-The code for almost every test for your analyzer follows one of these two patterns. For the first step, you can rework these tests as data driven tests. Then, it will be easy to create new tests by adding new string constants to represent different test inputs.
+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.
-For efficiency, the first step is to refactor the two tests into data driven tests. Then, you only need to define a couple string constants for each new test. While you are refactoring, rename both methods to better names. Replace `TestMethod1` with this test that ensures no diagnostic is raised:
+The template uses [Microsoft.CodeAnalysis.Testing](https://github.com/dotnet/roslyn-sdk/blob/master/src/Microsoft.CodeAnalysis.Testing/README.md) packages for unit testing.
-```csharp
-[DataTestMethod]
-[DataRow("")]
-public void WhenTestCodeIsValidNoDiagnosticIsTriggered(string testCode)
-{
- VerifyCSharpDiagnostic(testCode);
-}
-```
-
-You can create a new data row for this test by defining any code fragment that should not cause your diagnostic to trigger a warning. This overload of `VerifyCSharpDiagnostic` passes when there are no diagnostics triggered for the source code fragment.
-
-Next, replace `TestMethod2` with this test that ensures a diagnostic is raised and a code fix applied for the source code fragment:
-
-```csharp
-[DataTestMethod]
-[DataRow(LocalIntCouldBeConstant, LocalIntCouldBeConstantFixed, 10, 13)]
-public void WhenDiagnosticIsRaisedFixUpdatesCode(
- string test,
- string fixTest,
- int line,
- int column)
-{
- var expected = new DiagnosticResult
- {
- Id = MakeConstAnalyzer.DiagnosticId,
- Message = new LocalizableResourceString(nameof(MakeConst.Resources.AnalyzerMessageFormat), MakeConst.Resources.ResourceManager, typeof(MakeConst.Resources)).ToString(),
- Severity = DiagnosticSeverity.Warning,
- Locations =
- new[] {
- new DiagnosticResultLocation("Test0.cs", line, column)
- }
- };
-
- VerifyCSharpDiagnostic(test, expected);
-
- VerifyCSharpFix(test, fixTest);
-}
-```
+> [!TIP]
+> The testing library supports a special markup syntax, including the following:
+>
+> - `[|text|]`: indicates that a diagnostic is reported for `text`. By default, this form may only be used for testing analyzers with exactly one `DiagnosticDescriptor` provided by `DiagnosticAnalyzer.SupportedDiagnostics`.
+> - `{|ExpectedDiagnosticId:text|}`: indicates that a diagnostic with `ExpectedDiagnosticId` is reported for `text`.
-The preceding code also made a couple changes to the code that builds the expected diagnostic result. It uses the public constants registered in the `MakeConst` analyzer. In addition, it uses two string constants for the input and fixed source. Add the following string constants to the `UnitTest` class:
+Add the following test method to the `MakeConstUnitTest` class:
-[!code-csharp[string constants for fix test](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#FirstFixTest "string constants for fix test")]
+[!code-csharp[test method for fix test](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#FirstFixTest "test method for fix test")]
-Run these two tests to make sure they pass. In Visual Studio, open the **Test Explorer** by selecting **Test** > **Windows** > **Test Explorer**. Then select the **Run All** link.
+Run these two tests to make sure they pass. In Visual Studio, open the **Test Explorer** by selecting **Test** > **Windows** > **Test Explorer**. Then select **Run All**.
## 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 one of those tests, the case where a variable is assigned after being initialized. Add the following string constant to your tests to represent that case:
+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 one of those tests, the case where a variable is assigned after being initialized. Add the following test method to represent that case:
-[!code-csharp[variable assigned](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VariableAssigned "a variable that is assigned after being initialized won't raise the diagnostic")]
+[!code-csharp[variable assigned](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VariableAssigned "a variable that is assigned after being initialized won't raise the diagnostic")]
-Then, add a data row for this test as shown in the snippet below:
-
-```csharp
-[DataTestMethod]
-[DataRow(""),
- DataRow(VariableAssigned)]
-public void WhenTestCodeIsValidNoDiagnosticIsTriggered(string testCode)
-```
-
-This test passes as well. Next, add constants for conditions you haven't handled yet:
+This test passes as well. Next, add test methods for conditions you haven't handled yet:
- Declarations that are already `const`, because they are already const:
- [!code-csharp[already const declaration](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#AlreadyConst "a declaration that is already const should not raise the diagnostic")]
+ [!code-csharp[already const declaration](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#AlreadyConst "a declaration that is already const should not raise the diagnostic")]
- Declarations that have no initializer, because there is no value to use:
- [!code-csharp[declarations that have no initializer](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#NoInitializer "a declaration that has no initializer should not raise the diagnostic")]
+ [!code-csharp[declarations that have no initializer](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#NoInitializer "a declaration that has no initializer should not raise the diagnostic")]
- Declarations where the initializer is not a constant, because they can't be compile-time constants:
- [!code-csharp[declarations where the initializer isn't const](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#InitializerNotConstant "a declaration where the initializer is not a compile-time constant should not raise the diagnostic")]
+ [!code-csharp[declarations where the initializer isn't const](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#InitializerNotConstant "a declaration where the initializer is not a compile-time constant should not raise the diagnostic")]
It can be even more complicated because C# allows multiple declarations as one statement. Consider the following test case string constant:
-[!code-csharp[multiple initializers](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#MultipleInitializers "A declaration can be made constant only if all variables in that statement can be made constant")]
-
-The variable `i` can be made constant, but the variable `j` cannot. Therefore, this statement cannot be made a const declaration. Add the `DataRow` declarations for all these tests:
+[!code-csharp[multiple initializers](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#MultipleInitializers "A declaration can be made constant only if all variables in that statement can be made constant")]
-```csharp
-[DataTestMethod]
-[DataRow(""),
- DataRow(VariableAssigned),
- DataRow(AlreadyConst),
- DataRow(NoInitializer),
- DataRow(InitializerNotConstant),
- DataRow(MultipleInitializers)]
-public void WhenTestCodeIsValidNoDiagnosticIsTriggered(string testCode)
-```
+The variable `i` can be made constant, but the variable `j` cannot. Therefore, this statement cannot be made a const declaration.
Run your tests again, and you'll see these new test cases fail.
@@ -366,12 +308,12 @@ In your `AnalyzeNode` method, replace the original semantic analysis:
```csharp
// Perform data flow analysis on the local declaration.
-var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
+DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
-var variable = localDeclaration.Declaration.Variables.Single();
-var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
+VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
+ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
@@ -383,15 +325,15 @@ with the following code snippet:
```csharp
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values.
-foreach (var variable in localDeclaration.Declaration.Variables)
+foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
- var initializer = variable.Initializer;
+ EqualsValueClauseSyntax initializer = variable.Initializer;
if (initializer == null)
{
return;
}
- var constantValue = context.SemanticModel.GetConstantValue(initializer.Value);
+ Optional constantValue = context.SemanticModel.GetConstantValue(initializer.Value, context.CancellationToken);
if (!constantValue.HasValue)
{
return;
@@ -399,13 +341,13 @@ foreach (var variable in localDeclaration.Declaration.Variables)
}
// Perform data flow analysis on the local declaration.
-var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
+DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
-foreach (var variable in localDeclaration.Declaration.Variables)
+foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
- var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
+ ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
@@ -417,69 +359,37 @@ The first `foreach` loop examines each variable declaration using syntactic anal
## 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 source string constant for that condition:
+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:
-[!code-csharp[Mismatched types don't raise diagnostics](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsInvalid "When the variable type and the constant type don't match, there's no diagnostic")]
+[!code-csharp[Mismatched types don't raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsInvalid "When the variable type and the constant type don't match, there's no diagnostic")]
In addition, reference types are not handled properly. The only constant value allowed for a reference type is `null`, except in this 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-csharp[Reference types don't raise diagnostics](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsntString "When the variable type is a reference type other than string, there's no diagnostic")]
+[!code-csharp[Reference types don't raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#DeclarationIsntString "When the variable type is a reference type other than string, there's no diagnostic")]
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:
-[!code-csharp[string reference types raise diagnostics](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#ConstantIsString "When the variable type is string, it can be constant")]
+[!code-csharp[string reference types raise diagnostics](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#ConstantIsString "When the variable type is string, it can be constant")]
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:
-[!code-csharp[var references need to use the inferred types](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VarDeclarations "Declarations made using var must have the type replaced with the inferred type")]
-
-These changes update the data row declarations for both tests. The following code shows these tests with all data row attributes:
-
-[!code-csharp[The finished tests](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#FinishedTests "The finished tests for the make const analyzer")]
+[!code-csharp[var references need to use the inferred types](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VarDeclarations "Declarations made using var must have the type replaced with the inferred type")]
Fortunately, all of the above bugs can be addressed using the same techniques that you just learned.
-To fix the first bug, first open **DiagnosticAnalyzer.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, first open *DiagnosticAnalyzer.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:
-```csharp
-var variableTypeName = localDeclaration.Declaration.Type;
-var variableType = context.SemanticModel.GetTypeInfo(variableTypeName).ConvertedType;
-```
+[!code-csharp[Retrieve type information](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#VariableConvertedType "Retrieve type information")]
Then, inside your `foreach` loop, check each initializer to make sure it's convertible to the variable type. Add the following check after ensuring that the initializer is a constant:
-```csharp
-// Ensure that the initializer value can be converted to the type of the
-// local declaration without a user-defined conversion.
-var conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
-if (!conversion.Exists || conversion.IsUserDefined)
-{
- return;
-}
-```
+[!code-csharp[Ensure non-user-defined conversion](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#BailOutOnUserDefinedConversion "Bail-out on user-defined conversion")]
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.
-```csharp
-// Special cases:
-// * If the constant value is a string, the type of the local declaration
-// must be System.String.
-// * If the constant value is null, the type of the local declaration must
-// be a reference type.
-if (constantValue.Value is string)
-{
- if (variableType.SpecialType != SpecialType.System_String)
- {
- return;
- }
-}
-else if (variableType.IsReferenceType && constantValue.Value != null)
-{
- return;
-}
-```
+[!code-csharp[Handle special cases](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs#HandleSpecialCases "Handle special cases")]
-You must write a bit more code in your code fix provider to replace the `var` keyword with the correct type name. Return to **CodeFixProvider.cs**. The code you'll add does the following steps:
+You must write a bit more code in your code fix provider to replace the `var` keyword with the correct type name. Return to *MakeConstCodeFixProvider.cs*. The code you'll add does the following steps:
- Check if the declaration is a `var` declaration, and if it is:
- Create a new type for the inferred type.
@@ -489,7 +399,7 @@ You must write a bit more code in your code fix provider to replace the `var` ke
That sounds like a lot of code. It's not. Replace the line that declares and initializes `newLocal` with the following code. It goes immediately after the initialization of `newModifiers`:
-[!code-csharp[Replace Var designations](~/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs#ReplaceVar "Replace a var designation with the explicit type")]
+[!code-csharp[Replace Var designations](snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs#ReplaceVar "Replace a var designation with the explicit type")]
You'll need to add one `using` directive to use the type:
diff --git a/docs/csharp/roslyn-sdk/tutorials/media/how-to-write-csharp-analyzer-code-fix/update-string-resources.png b/docs/csharp/roslyn-sdk/tutorials/media/how-to-write-csharp-analyzer-code-fix/update-string-resources.png
index 013251ef12390..0c2beba405416 100644
Binary files a/docs/csharp/roslyn-sdk/tutorials/media/how-to-write-csharp-analyzer-code-fix/update-string-resources.png and b/docs/csharp/roslyn-sdk/tutorials/media/how-to-write-csharp-analyzer-code-fix/update-string-resources.png differ
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst.sln b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst.sln
new file mode 100644
index 0000000000000..9126f0319c01e
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst.sln
@@ -0,0 +1,49 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30804.86
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst", "MakeConst\MakeConst\MakeConst.csproj", "{2CC0B384-3A66-4BC9-8506-CE6C0E2C62F0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst.CodeFixes", "MakeConst\MakeConst.CodeFixes\MakeConst.CodeFixes.csproj", "{A575A3F6-AAE2-4F28-A118-DA7A71216F05}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst.Package", "MakeConst\MakeConst.Package\MakeConst.Package.csproj", "{CCD63038-DF8D-4BA1-B151-F78E2A8999AF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst.Test", "MakeConst\MakeConst.Test\MakeConst.Test.csproj", "{5CA2BD3A-3B69-4FAB-9B80-4A9A518DF19B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst.Vsix", "MakeConst\MakeConst.Vsix\MakeConst.Vsix.csproj", "{C10E2C7A-4425-4A92-958C-637CFA1053F6}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2CC0B384-3A66-4BC9-8506-CE6C0E2C62F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2CC0B384-3A66-4BC9-8506-CE6C0E2C62F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2CC0B384-3A66-4BC9-8506-CE6C0E2C62F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2CC0B384-3A66-4BC9-8506-CE6C0E2C62F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A575A3F6-AAE2-4F28-A118-DA7A71216F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A575A3F6-AAE2-4F28-A118-DA7A71216F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A575A3F6-AAE2-4F28-A118-DA7A71216F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A575A3F6-AAE2-4F28-A118-DA7A71216F05}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CCD63038-DF8D-4BA1-B151-F78E2A8999AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CCD63038-DF8D-4BA1-B151-F78E2A8999AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CCD63038-DF8D-4BA1-B151-F78E2A8999AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CCD63038-DF8D-4BA1-B151-F78E2A8999AF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5CA2BD3A-3B69-4FAB-9B80-4A9A518DF19B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5CA2BD3A-3B69-4FAB-9B80-4A9A518DF19B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5CA2BD3A-3B69-4FAB-9B80-4A9A518DF19B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5CA2BD3A-3B69-4FAB-9B80-4A9A518DF19B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C10E2C7A-4425-4A92-958C-637CFA1053F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C10E2C7A-4425-4A92-958C-637CFA1053F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C10E2C7A-4425-4A92-958C-637CFA1053F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C10E2C7A-4425-4A92-958C-637CFA1053F6}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6113272F-D929-4E71-A537-A56340FDD4BF}
+ EndGlobalSection
+EndGlobal
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.Designer.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.Designer.cs
new file mode 100644
index 0000000000000..c9233481ad711
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace MakeConst {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class CodeFixResources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal CodeFixResources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MakeConst.CodeFixResources", typeof(CodeFixResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Make constant.
+ ///
+ internal static string CodeFixTitle {
+ get {
+ return ResourceManager.GetString("CodeFixTitle", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.resx b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.resx
new file mode 100644
index 0000000000000..ecaf72d3be1af
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/CodeFixResources.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Make constant
+ The title of the code fix.
+
+
\ No newline at end of file
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConst.CodeFixes.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConst.CodeFixes.csproj
new file mode 100644
index 0000000000000..143f53f23cd68
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConst.CodeFixes.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netstandard2.0
+ false
+ MakeConst
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs
similarity index 70%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs
index 288fdf9af7d53..de0f3b62dd8ff 100644
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstCodeFixProvider.cs
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.CodeFixes/MakeConstCodeFixProvider.cs
@@ -20,10 +20,6 @@ namespace MakeConst
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MakeConstCodeFixProvider)), Shared]
public class MakeConstCodeFixProvider : CodeFixProvider
{
- //
- private const string title = "Make constant";
- //
-
public sealed override ImmutableArray FixableDiagnosticIds
{
get { return ImmutableArray.Create(MakeConstAnalyzer.DiagnosticId); }
@@ -52,57 +48,59 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
- title: title,
+ title: CodeFixResources.CodeFixTitle,
createChangedDocument: c => MakeConstAsync(context.Document, declaration, c),
- equivalenceKey: title),
+ equivalenceKey: nameof(CodeFixResources.CodeFixTitle)),
diagnostic);
//
}
- private async Task MakeConstAsync(Document document, LocalDeclarationStatementSyntax localDeclaration, CancellationToken cancellationToken)
+ private static async Task MakeConstAsync(Document document,
+ LocalDeclarationStatementSyntax localDeclaration,
+ CancellationToken cancellationToken)
{
//
// Remove the leading trivia from the local declaration.
- var firstToken = localDeclaration.GetFirstToken();
- var leadingTrivia = firstToken.LeadingTrivia;
- var trimmedLocal = localDeclaration.ReplaceToken(
+ SyntaxToken firstToken = localDeclaration.GetFirstToken();
+ SyntaxTriviaList leadingTrivia = firstToken.LeadingTrivia;
+ LocalDeclarationStatementSyntax trimmedLocal = localDeclaration.ReplaceToken(
firstToken, firstToken.WithLeadingTrivia(SyntaxTriviaList.Empty));
// Create a const token with the leading trivia.
- var constToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.ConstKeyword, SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker));
+ SyntaxToken constToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.ConstKeyword, SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker));
//
// Insert the const token into the modifiers list, creating a new modifiers list.
- var newModifiers = trimmedLocal.Modifiers.Insert(0, constToken);
+ SyntaxTokenList newModifiers = trimmedLocal.Modifiers.Insert(0, constToken);
//
// If the type of the declaration is 'var', create a new type name
// for the inferred type.
- var variableDeclaration = localDeclaration.Declaration;
- var variableTypeName = variableDeclaration.Type;
+ VariableDeclarationSyntax variableDeclaration = localDeclaration.Declaration;
+ TypeSyntax variableTypeName = variableDeclaration.Type;
if (variableTypeName.IsVar)
{
- var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
+ SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
// Special case: Ensure that 'var' isn't actually an alias to another type
// (e.g. using var = System.String).
- var aliasInfo = semanticModel.GetAliasInfo(variableTypeName);
+ IAliasSymbol aliasInfo = semanticModel.GetAliasInfo(variableTypeName, cancellationToken);
if (aliasInfo == null)
{
// Retrieve the type inferred for var.
- var type = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;
+ ITypeSymbol type = semanticModel.GetTypeInfo(variableTypeName, cancellationToken).ConvertedType;
// Special case: Ensure that 'var' isn't actually a type named 'var'.
if (type.Name != "var")
{
// Create a new TypeSyntax for the inferred type. Be careful
// to keep any leading and trailing trivia from the var keyword.
- var typeName = SyntaxFactory.ParseTypeName(type.ToDisplayString())
+ TypeSyntax typeName = SyntaxFactory.ParseTypeName(type.ToDisplayString())
.WithLeadingTrivia(variableTypeName.GetLeadingTrivia())
.WithTrailingTrivia(variableTypeName.GetTrailingTrivia());
// Add an annotation to simplify the type name.
- var simplifiedTypeName = typeName.WithAdditionalAnnotations(Simplifier.Annotation);
+ TypeSyntax simplifiedTypeName = typeName.WithAdditionalAnnotations(Simplifier.Annotation);
// Replace the type in the variable declaration.
variableDeclaration = variableDeclaration.WithType(simplifiedTypeName);
@@ -110,19 +108,19 @@ private async Task MakeConstAsync(Document document, LocalDeclarationS
}
}
// Produce the new local declaration.
- var newLocal = trimmedLocal.WithModifiers(newModifiers)
+ LocalDeclarationStatementSyntax newLocal = trimmedLocal.WithModifiers(newModifiers)
.WithDeclaration(variableDeclaration);
//
//
// Add an annotation to format the new local declaration.
- var formattedLocal = newLocal.WithAdditionalAnnotations(Formatter.Annotation);
+ LocalDeclarationStatementSyntax formattedLocal = newLocal.WithAdditionalAnnotations(Formatter.Annotation);
//
//
// Replace the old local declaration with the new local declaration.
- var oldRoot = await document.GetSyntaxRootAsync(cancellationToken);
- var newRoot = oldRoot.ReplaceNode(localDeclaration, formattedLocal);
+ SyntaxNode oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ SyntaxNode newRoot = oldRoot.ReplaceNode(localDeclaration, formattedLocal);
// Return document with transformed tree.
return document.WithSyntaxRoot(newRoot);
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConst.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/MakeConst.Package.csproj
similarity index 58%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConst.csproj
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/MakeConst.Package.csproj
index f082edcf55774..0c233d2ef6a6a 100644
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConst.csproj
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/MakeConst.Package.csproj
@@ -1,15 +1,16 @@
-
+
netstandard2.0
false
- True
+ true
+ true
MakeConst
1.0.0.0
- wiwagn
+ Youssef
http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE
http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE
http://ICON_URL_HERE_OR_DELETE_THIS_LINE
@@ -20,21 +21,24 @@
Copyright
MakeConst, analyzers
true
-
-
-
-
-
+ $(TargetsForTfmSpecificContentInPackage);_AddAnalyzersToOutput
+
-
-
+
+
-
+
+
+
+
+
+
+
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/tools/install.ps1 b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/tools/install.ps1
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/tools/install.ps1
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/tools/install.ps1
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/tools/uninstall.ps1 b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/tools/uninstall.ps1
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/tools/uninstall.ps1
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Package/tools/uninstall.ps1
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConst.Test.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConst.Test.csproj
new file mode 100644
index 0000000000000..54b8a1638f7d9
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConst.Test.csproj
@@ -0,0 +1,28 @@
+
+
+
+ netcoreapp3.1
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs
new file mode 100644
index 0000000000000..7c88536ce9c81
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/MakeConstUnitTests.cs
@@ -0,0 +1,259 @@
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using VerifyCS = MakeConst.Test.CSharpCodeFixVerifier<
+ MakeConst.MakeConstAnalyzer,
+ MakeConst.MakeConstCodeFixProvider>;
+
+namespace MakeConst.Test
+{
+ [TestClass]
+ public class MakeConstUnitTest
+ {
+ //
+ [TestMethod]
+ public async Task VariableIsAssigned_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ int i = 0;
+ Console.WriteLine(i++);
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task VariableIsAlreadyConst_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ const int i = 0;
+ Console.WriteLine(i);
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task NoInitializer_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ int i;
+ i = 0;
+ Console.WriteLine(i);
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task InitializerIsNotConstant_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ int i = DateTime.Now.DayOfYear;
+ Console.WriteLine(i);
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task MultipleInitializers_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ int i = 0, j = DateTime.Now.DayOfYear;
+ Console.WriteLine(i);
+ Console.WriteLine(j);
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task DeclarationIsInvalid_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ int x = {|CS0029:""abc""|};
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task DeclarationIsNotString_NoDiagnostic()
+ {
+ await VerifyCS.VerifyAnalyzerAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ object s = ""abc"";
+ }
+}
+");
+ }
+ //
+
+
+ //
+ [TestMethod]
+ public async Task LocalIntCouldBeConstant_Diagnostic()
+ {
+ await VerifyCS.VerifyCodeFixAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ [|int i = 0;|]
+ Console.WriteLine(i);
+ }
+}
+", @"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ const int i = 0;
+ Console.WriteLine(i);
+ }
+}
+");
+ }
+ //
+
+
+ //
+ [TestMethod]
+ public async Task StringCouldBeConstant_Diagnostic()
+ {
+ await VerifyCS.VerifyCodeFixAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ [|string s = ""abc"";|]
+ }
+}
+", @"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ const string s = ""abc"";
+ }
+}
+");
+ }
+ //
+
+ //
+ [TestMethod]
+ public async Task VarIntDeclarationCouldBeConstant_Diagnostic()
+ {
+ await VerifyCS.VerifyCodeFixAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ [|var item = 4;|]
+ }
+}
+", @"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ const int item = 4;
+ }
+}
+");
+ }
+
+ [TestMethod]
+ public async Task VarStringDeclarationCouldBeConstant_Diagnostic()
+ {
+ await VerifyCS.VerifyCodeFixAsync(@"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ [|var item = ""abc"";|]
+ }
+}
+", @"
+using System;
+
+class Program
+{
+ static void Main()
+ {
+ const string item = ""abc"";
+ }
+}
+");
+ }
+ //
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1+Test.cs
new file mode 100644
index 0000000000000..90ebad09e5132
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1+Test.cs
@@ -0,0 +1,26 @@
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpAnalyzerVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ {
+ public class Test : CSharpAnalyzerTest
+ {
+ public Test()
+ {
+ SolutionTransforms.Add((solution, projectId) =>
+ {
+ var compilationOptions = solution.GetProject(projectId).CompilationOptions;
+ compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
+ solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
+
+ return solution;
+ });
+ }
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1.cs
new file mode 100644
index 0000000000000..408e6106131ff
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpAnalyzerVerifier`1.cs
@@ -0,0 +1,38 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpAnalyzerVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ {
+ ///
+ public static DiagnosticResult Diagnostic()
+ => CSharpAnalyzerVerifier.Diagnostic();
+
+ ///
+ public static DiagnosticResult Diagnostic(string diagnosticId)
+ => CSharpAnalyzerVerifier.Diagnostic(diagnosticId);
+
+ ///
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+ => CSharpAnalyzerVerifier.Diagnostic(descriptor);
+
+ ///
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs
new file mode 100644
index 0000000000000..04cd0c9c6043f
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2+Test.cs
@@ -0,0 +1,28 @@
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpCodeFixVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFix : CodeFixProvider, new()
+ {
+ public class Test : CSharpCodeFixTest
+ {
+ public Test()
+ {
+ SolutionTransforms.Add((solution, projectId) =>
+ {
+ var compilationOptions = solution.GetProject(projectId).CompilationOptions;
+ compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
+ solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
+
+ return solution;
+ });
+ }
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2.cs
new file mode 100644
index 0000000000000..a9a6c0efa7dec
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeFixVerifier`2.cs
@@ -0,0 +1,61 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpCodeFixVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFix : CodeFixProvider, new()
+ {
+ ///
+ public static DiagnosticResult Diagnostic()
+ => CSharpCodeFixVerifier.Diagnostic();
+
+ ///
+ public static DiagnosticResult Diagnostic(string diagnosticId)
+ => CSharpCodeFixVerifier.Diagnostic(diagnosticId);
+
+ ///
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+ => CSharpCodeFixVerifier.Diagnostic(descriptor);
+
+ ///
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, string fixedSource)
+ => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
+ => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs
new file mode 100644
index 0000000000000..6895353ee09c6
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1+Test.cs
@@ -0,0 +1,26 @@
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpCodeRefactoringVerifier
+ where TCodeRefactoring : CodeRefactoringProvider, new()
+ {
+ public class Test : CSharpCodeRefactoringTest
+ {
+ public Test()
+ {
+ SolutionTransforms.Add((solution, projectId) =>
+ {
+ var compilationOptions = solution.GetProject(projectId).CompilationOptions;
+ compilationOptions = compilationOptions.WithSpecificDiagnosticOptions(
+ compilationOptions.SpecificDiagnosticOptions.SetItems(CSharpVerifierHelper.NullableWarnings));
+ solution = solution.WithProjectCompilationOptions(projectId, compilationOptions);
+
+ return solution;
+ });
+ }
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1.cs
new file mode 100644
index 0000000000000..c585b65dfe473
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpCodeRefactoringVerifier`1.cs
@@ -0,0 +1,36 @@
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Testing;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class CSharpCodeRefactoringVerifier
+ where TCodeRefactoring : CodeRefactoringProvider, new()
+ {
+ ///
+ public static async Task VerifyRefactoringAsync(string source, string fixedSource)
+ {
+ await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
+ }
+
+ ///
+ public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource)
+ {
+ await VerifyRefactoringAsync(source, new[] { expected }, fixedSource);
+ }
+
+ ///
+ public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpVerifierHelper.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpVerifierHelper.cs
new file mode 100644
index 0000000000000..656a5f27c6391
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/CSharpVerifierHelper.cs
@@ -0,0 +1,33 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using System;
+using System.Collections.Immutable;
+
+namespace MakeConst.Test
+{
+ internal static class CSharpVerifierHelper
+ {
+ ///
+ /// By default, the compiler reports diagnostics for nullable reference types at
+ /// , and the analyzer test framework defaults to only validating
+ /// diagnostics at . This map contains all compiler diagnostic IDs
+ /// related to nullability mapped to , which is then used to enable all
+ /// of these warnings for default validation during analyzer and code fix tests.
+ ///
+ internal static ImmutableDictionary NullableWarnings { get; } = GetNullableWarningsFromCompiler();
+
+ private static ImmutableDictionary GetNullableWarningsFromCompiler()
+ {
+ string[] args = { "/warnaserror:nullable" };
+ var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory);
+ var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions;
+
+ // Workaround for https://github.com/dotnet/roslyn/issues/41610
+ nullableWarnings = nullableWarnings
+ .SetItem("CS8632", ReportDiagnostic.Error)
+ .SetItem("CS8669", ReportDiagnostic.Error);
+
+ return nullableWarnings;
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs
new file mode 100644
index 0000000000000..e1439131cedf2
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1+Test.cs
@@ -0,0 +1,17 @@
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.VisualBasic.Testing;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicAnalyzerVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ {
+ public class Test : VisualBasicAnalyzerTest
+ {
+ public Test()
+ {
+ }
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1.cs
new file mode 100644
index 0000000000000..4de0227738c7e
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicAnalyzerVerifier`1.cs
@@ -0,0 +1,38 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.VisualBasic.Testing;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicAnalyzerVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ {
+ ///
+ public static DiagnosticResult Diagnostic()
+ => VisualBasicAnalyzerVerifier.Diagnostic();
+
+ ///
+ public static DiagnosticResult Diagnostic(string diagnosticId)
+ => VisualBasicAnalyzerVerifier.Diagnostic(diagnosticId);
+
+ ///
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+ => VisualBasicAnalyzerVerifier.Diagnostic(descriptor);
+
+ ///
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs
new file mode 100644
index 0000000000000..1d3c45b94e3ab
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2+Test.cs
@@ -0,0 +1,16 @@
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.VisualBasic.Testing;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicCodeFixVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFix : CodeFixProvider, new()
+ {
+ public class Test : VisualBasicCodeFixTest
+ {
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2.cs
new file mode 100644
index 0000000000000..b395815495692
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeFixVerifier`2.cs
@@ -0,0 +1,61 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.VisualBasic.Testing;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicCodeFixVerifier
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFix : CodeFixProvider, new()
+ {
+ ///
+ public static DiagnosticResult Diagnostic()
+ => VisualBasicCodeFixVerifier.Diagnostic();
+
+ ///
+ public static DiagnosticResult Diagnostic(string diagnosticId)
+ => VisualBasicCodeFixVerifier.Diagnostic(diagnosticId);
+
+ ///
+ public static DiagnosticResult Diagnostic(DiagnosticDescriptor descriptor)
+ => VisualBasicCodeFixVerifier.Diagnostic(descriptor);
+
+ ///
+ public static async Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, string fixedSource)
+ => await VerifyCodeFixAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource)
+ => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource);
+
+ ///
+ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs
new file mode 100644
index 0000000000000..f412856557c0c
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1+Test.cs
@@ -0,0 +1,14 @@
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+using Microsoft.CodeAnalysis.VisualBasic.Testing;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicCodeRefactoringVerifier
+ where TCodeRefactoring : CodeRefactoringProvider, new()
+ {
+ public class Test : VisualBasicCodeRefactoringTest
+ {
+ }
+ }
+}
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs
new file mode 100644
index 0000000000000..6f7b723e0f5cd
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Test/Verifiers/VisualBasicCodeRefactoringVerifier`1.cs
@@ -0,0 +1,36 @@
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.Testing;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MakeConst.Test
+{
+ public static partial class VisualBasicCodeRefactoringVerifier
+ where TCodeRefactoring : CodeRefactoringProvider, new()
+ {
+ ///
+ public static async Task VerifyRefactoringAsync(string source, string fixedSource)
+ {
+ await VerifyRefactoringAsync(source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
+ }
+
+ ///
+ public static async Task VerifyRefactoringAsync(string source, DiagnosticResult expected, string fixedSource)
+ {
+ await VerifyRefactoringAsync(source, new[] { expected }, fixedSource);
+ }
+
+ ///
+ public static async Task VerifyRefactoringAsync(string source, DiagnosticResult[] expected, string fixedSource)
+ {
+ var test = new Test
+ {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange(expected);
+ await test.RunAsync(CancellationToken.None);
+ }
+ }
+}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Vsix/MakeConst.Vsix.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Vsix/MakeConst.Vsix.csproj
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Vsix/MakeConst.Vsix.csproj
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Vsix/MakeConst.Vsix.csproj
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Vsix/source.extension.vsixmanifest b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Vsix/source.extension.vsixmanifest
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Vsix/source.extension.vsixmanifest
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst.Vsix/source.extension.vsixmanifest
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConst.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConst.csproj
new file mode 100644
index 0000000000000..e3e16e1376989
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConst.csproj
@@ -0,0 +1,21 @@
+
+
+
+ netstandard2.0
+ false
+
+
+ *$(MSBuildProjectFile)*
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstAnalyzer.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs
similarity index 72%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstAnalyzer.cs
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs
index 41e2ef53a1ebb..4737b0f00e472 100644
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/MakeConstAnalyzer.cs
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/MakeConstAnalyzer.cs
@@ -20,7 +20,9 @@ public class MakeConstAnalyzer : DiagnosticAnalyzer
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 = "Usage";
+ //
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
@@ -29,50 +31,59 @@ public class MakeConstAnalyzer : DiagnosticAnalyzer
public override void Initialize(AnalysisContext context)
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
- //
- context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.None);
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
+ //
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
//
}
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
+ //
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
+ //
- // does not have const
+ //
+ // make sure the declaration isn't already const:
if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return;
}
+ //
- var variableTypeName = localDeclaration.Declaration.Type;
- var variableType = context.SemanticModel.GetTypeInfo(variableTypeName).ConvertedType;
+ //
+ TypeSyntax variableTypeName = localDeclaration.Declaration.Type;
+ ITypeSymbol variableType = context.SemanticModel.GetTypeInfo(variableTypeName, context.CancellationToken).ConvertedType;
+ //
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values.
- foreach (var variable in localDeclaration.Declaration.Variables)
+ foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
- var initializer = variable.Initializer;
+ EqualsValueClauseSyntax initializer = variable.Initializer;
if (initializer == null)
{
return;
}
- var constantValue = context.SemanticModel.GetConstantValue(initializer.Value);
+ Optional constantValue = context.SemanticModel.GetConstantValue(initializer.Value, context.CancellationToken);
if (!constantValue.HasValue)
{
return;
}
+ //
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
- var conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
+ Conversion conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return;
}
+ //
+ //
// Special cases:
// * If the constant value is a string, the type of the local declaration
// must be System.String.
@@ -89,23 +100,26 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
return;
}
+ //
}
// Perform data flow analysis on the local declaration.
- var dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
+ DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
- foreach (var variable in localDeclaration.Declaration.Variables)
+ foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
// Retrieve the local symbol for each variable in the local declaration
// and ensure that it is not written outside of the data flow analysis region.
- var variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable);
+ ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
}
+ //
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
+ //
}
}
}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/Resources.Designer.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/Resources.Designer.cs
similarity index 85%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/Resources.Designer.cs
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/Resources.Designer.cs
index 9bb2d18b778a4..dd7ac0eff4a33 100644
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/Resources.Designer.cs
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/Resources.Designer.cs
@@ -10,9 +10,8 @@
namespace MakeConst {
using System;
- using System.Reflection;
-
-
+
+
///
/// A strongly-typed resource class, for looking up localized strings, etc.
///
@@ -20,39 +19,39 @@ namespace MakeConst {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- public class Resources {
-
+ internal class Resources {
+
private static global::System.Resources.ResourceManager resourceMan;
-
+
private static global::System.Globalization.CultureInfo resourceCulture;
-
+
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
-
+
///
/// Returns the cached ResourceManager instance used by this class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Resources.ResourceManager ResourceManager {
+ internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MakeConst.Resources", typeof(Resources).GetTypeInfo().Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MakeConst.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
-
+
///
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- public static global::System.Globalization.CultureInfo Culture {
+ internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@@ -60,29 +59,29 @@ internal Resources() {
resourceCulture = value;
}
}
-
+
///
/// Looks up a localized string similar to Make Constant..
///
- public static string AnalyzerDescription {
+ internal static string AnalyzerDescription {
get {
return ResourceManager.GetString("AnalyzerDescription", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to can be made constant.
///
- public static string AnalyzerMessageFormat {
+ internal static string AnalyzerMessageFormat {
get {
return ResourceManager.GetString("AnalyzerMessageFormat", resourceCulture);
}
}
-
+
///
/// Looks up a localized string similar to Variable can be made constant..
///
- public static string AnalyzerTitle {
+ internal static string AnalyzerTitle {
get {
return ResourceManager.GetString("AnalyzerTitle", resourceCulture);
}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/Resources.resx b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/Resources.resx
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst/Resources.resx
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConst/MakeConst/Resources.resx
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/MakeConstTestProject.csproj b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/MakeConstTestProject.csproj
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/MakeConstTestProject.csproj
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/MakeConstTestProject.csproj
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/Program.cs b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/Program.cs
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/Program.cs
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/Program.cs
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/snippets.5000.json b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/snippets.5000.json
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/snippets.5000.json
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/MakeConstTestProject/MakeConstTestProject/snippets.5000.json
diff --git a/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/NuGet.config b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/NuGet.config
new file mode 100644
index 0000000000000..745e6c9f2386c
--- /dev/null
+++ b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/NuGet.config
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/snippets.5000.json b/docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/snippets.5000.json
similarity index 100%
rename from samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConstTestProject/MakeConstTestProject/snippets.5000.json
rename to docs/csharp/roslyn-sdk/tutorials/snippets/how-to-write-csharp-analyzer-code-fix/snippets.5000.json
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/CodeFixVerifier.Helper.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/CodeFixVerifier.Helper.cs
deleted file mode 100644
index 42f4b64fd369a..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/CodeFixVerifier.Helper.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CodeActions;
-using Microsoft.CodeAnalysis.Formatting;
-using Microsoft.CodeAnalysis.Simplification;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-
-namespace TestHelper
-{
- ///
- /// Diagnostic Producer class with extra methods dealing with applying codefixes
- /// All methods are static
- ///
- public abstract partial class CodeFixVerifier : DiagnosticVerifier
- {
- ///
- /// Apply the inputted CodeAction to the inputted document.
- /// Meant to be used to apply codefixes.
- ///
- /// The Document to apply the fix on
- /// A CodeAction that will be applied to the Document.
- /// A Document with the changes from the CodeAction
- private static Document ApplyFix(Document document, CodeAction codeAction)
- {
- var operations = codeAction.GetOperationsAsync(CancellationToken.None).Result;
- var solution = operations.OfType().Single().ChangedSolution;
- return solution.GetDocument(document.Id);
- }
-
- ///
- /// Compare two collections of Diagnostics,and return a list of any new diagnostics that appear only in the second collection.
- /// Note: Considers Diagnostics to be the same if they have the same Ids. In the case of multiple diagnostics with the same Id in a row,
- /// this method may not necessarily return the new one.
- ///
- /// The Diagnostics that existed in the code before the CodeFix was applied
- /// The Diagnostics that exist in the code after the CodeFix was applied
- /// A list of Diagnostics that only surfaced in the code after the CodeFix was applied
- private static IEnumerable GetNewDiagnostics(IEnumerable diagnostics, IEnumerable newDiagnostics)
- {
- var oldArray = diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
- var newArray = newDiagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
-
- int oldIndex = 0;
- int newIndex = 0;
-
- while (newIndex < newArray.Length)
- {
- if (oldIndex < oldArray.Length && oldArray[oldIndex].Id == newArray[newIndex].Id)
- {
- ++oldIndex;
- ++newIndex;
- }
- else
- {
- yield return newArray[newIndex++];
- }
- }
- }
-
- ///
- /// Get the existing compiler diagnostics on the inputted document.
- ///
- /// The Document to run the compiler diagnostic analyzers on
- /// The compiler diagnostics that were found in the code
- private static IEnumerable GetCompilerDiagnostics(Document document)
- {
- return document.GetSemanticModelAsync().Result.GetDiagnostics();
- }
-
- ///
- /// Given a document, turn it into a string based on the syntax root
- ///
- /// The Document to be converted to a string
- /// A string containing the syntax of the Document after formatting
- private static string GetStringFromDocument(Document document)
- {
- var simplifiedDoc = Simplifier.ReduceAsync(document, Simplifier.Annotation).Result;
- var root = simplifiedDoc.GetSyntaxRootAsync().Result;
- root = Formatter.Format(root, Formatter.Annotation, simplifiedDoc.Project.Solution.Workspace);
- return root.GetText().ToString();
- }
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticResult.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticResult.cs
deleted file mode 100644
index e9664d7562304..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticResult.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using Microsoft.CodeAnalysis;
-using System;
-
-namespace TestHelper
-{
- ///
- /// Location where the diagnostic appears, as determined by path, line number, and column number.
- ///
- public struct DiagnosticResultLocation
- {
- public DiagnosticResultLocation(string path, int line, int column)
- {
- if (line < -1)
- {
- throw new ArgumentOutOfRangeException(nameof(line), "line must be >= -1");
- }
-
- if (column < -1)
- {
- throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1");
- }
-
- this.Path = path;
- this.Line = line;
- this.Column = column;
- }
-
- public string Path { get; }
- public int Line { get; }
- public int Column { get; }
- }
-
- ///
- /// Struct that stores information about a Diagnostic appearing in a source
- ///
- public struct DiagnosticResult
- {
- private DiagnosticResultLocation[] locations;
-
- public DiagnosticResultLocation[] Locations
- {
- get
- {
- this.locations ??= new DiagnosticResultLocation[] { };
- return this.locations;
- }
-
- set
- {
- this.locations = value;
- }
- }
-
- public DiagnosticSeverity Severity { get; set; }
-
- public string Id { get; set; }
-
- public string Message { get; set; }
-
- public string Path
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Path : "";
- }
- }
-
- public int Line
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Line : -1;
- }
- }
-
- public int Column
- {
- get
- {
- return this.Locations.Length > 0 ? this.Locations[0].Column : -1;
- }
- }
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticVerifier.Helper.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticVerifier.Helper.cs
deleted file mode 100644
index fc0347daaad1e..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Helpers/DiagnosticVerifier.Helper.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.CodeAnalysis.Text;
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-
-namespace TestHelper
-{
- ///
- /// Class for turning strings into documents and getting the diagnostics on them
- /// All methods are static
- ///
- public abstract partial class DiagnosticVerifier
- {
- private static readonly MetadataReference CorlibReference = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
- private static readonly MetadataReference SystemCoreReference = MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location);
- private static readonly MetadataReference CSharpSymbolsReference = MetadataReference.CreateFromFile(typeof(CSharpCompilation).Assembly.Location);
- private static readonly MetadataReference CodeAnalysisReference = MetadataReference.CreateFromFile(typeof(Compilation).Assembly.Location);
-
- internal static string DefaultFilePathPrefix = "Test";
- internal static string CSharpDefaultFileExt = "cs";
- internal static string VisualBasicDefaultExt = "vb";
- internal static string TestProjectName = "TestProject";
-
- #region Get Diagnostics
-
- ///
- /// Given classes in the form of strings, their language, and an IDiagnosticAnalyzer to apply to it, return the diagnostics found in the string after converting it to a document.
- ///
- /// Classes in the form of strings
- /// The language the source classes are in
- /// The analyzer to be run on the sources
- /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
- private static Diagnostic[] GetSortedDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer)
- {
- return GetSortedDiagnosticsFromDocuments(analyzer, GetDocuments(sources, language));
- }
-
- ///
- /// Given an analyzer and a document to apply it to, run the analyzer and gather an array of diagnostics found in it.
- /// The returned diagnostics are then ordered by location in the source document.
- ///
- /// The analyzer to run on the documents
- /// The Documents that the analyzer will be run on
- /// An IEnumerable of Diagnostics that surfaced in the source code, sorted by Location
- protected static Diagnostic[] GetSortedDiagnosticsFromDocuments(DiagnosticAnalyzer analyzer, Document[] documents)
- {
- var projects = new HashSet();
- foreach (var document in documents)
- {
- projects.Add(document.Project);
- }
-
- var diagnostics = new List();
- foreach (var project in projects)
- {
- var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
- var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
- foreach (var diag in diags)
- {
- if (diag.Location == Location.None || diag.Location.IsInMetadata)
- {
- diagnostics.Add(diag);
- }
- else
- {
- for (int i = 0; i < documents.Length; i++)
- {
- var document = documents[i];
- var tree = document.GetSyntaxTreeAsync().Result;
- if (tree == diag.Location.SourceTree)
- {
- diagnostics.Add(diag);
- }
- }
- }
- }
- }
-
- var results = SortDiagnostics(diagnostics);
- diagnostics.Clear();
- return results;
- }
-
- ///
- /// Sort diagnostics by location in source document
- ///
- /// The list of Diagnostics to be sorted
- /// An IEnumerable containing the Diagnostics in order of Location
- private static Diagnostic[] SortDiagnostics(IEnumerable diagnostics)
- {
- return diagnostics.OrderBy(d => d.Location.SourceSpan.Start).ToArray();
- }
-
- #endregion
-
- #region Set up compilation and documents
- ///
- /// Given an array of strings as sources and a language, turn them into a project and return the documents and spans of it.
- ///
- /// Classes in the form of strings
- /// The language the source code is in
- /// A Tuple containing the Documents produced from the sources and their TextSpans if relevant
- private static Document[] GetDocuments(string[] sources, string language)
- {
- if (language != LanguageNames.CSharp && language != LanguageNames.VisualBasic)
- {
- throw new ArgumentException("Unsupported Language");
- }
-
- var project = CreateProject(sources, language);
- var documents = project.Documents.ToArray();
-
- if (sources.Length != documents.Length)
- {
- throw new InvalidOperationException("Amount of sources did not match amount of Documents created");
- }
-
- return documents;
- }
-
- ///
- /// Create a Document from a string through creating a project that contains it.
- ///
- /// Classes in the form of a string
- /// The language the source code is in
- /// A Document created from the source string
- protected static Document CreateDocument(string source, string language = LanguageNames.CSharp)
- {
- return CreateProject(new[] { source }, language).Documents.First();
- }
-
- ///
- /// Create a project using the inputted strings as sources.
- ///
- /// Classes in the form of strings
- /// The language the source code is in
- /// A Project created out of the Documents created from the source strings
- private static Project CreateProject(string[] sources, string language = LanguageNames.CSharp)
- {
- string fileNamePrefix = DefaultFilePathPrefix;
- string fileExt = language == LanguageNames.CSharp ? CSharpDefaultFileExt : VisualBasicDefaultExt;
-
- var projectId = ProjectId.CreateNewId(debugName: TestProjectName);
-
- var solution = new AdhocWorkspace()
- .CurrentSolution
- .AddProject(projectId, TestProjectName, TestProjectName, language)
- .AddMetadataReference(projectId, CorlibReference)
- .AddMetadataReference(projectId, SystemCoreReference)
- .AddMetadataReference(projectId, CSharpSymbolsReference)
- .AddMetadataReference(projectId, CodeAnalysisReference);
-
- int count = 0;
- foreach (var source in sources)
- {
- var newFileName = fileNamePrefix + count + "." + fileExt;
- var documentId = DocumentId.CreateNewId(projectId, debugName: newFileName);
- solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
- count++;
- }
- return solution.GetProject(projectId);
- }
- #endregion
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConst.Test.csproj b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConst.Test.csproj
deleted file mode 100644
index d56ec54585c43..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConst.Test.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- netcoreapp3.1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs
deleted file mode 100644
index b8205900dcba4..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/MakeConstUnitTests.cs
+++ /dev/null
@@ -1,308 +0,0 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CodeFixes;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using TestHelper;
-
-namespace MakeConst.Test
-{
- [TestClass]
- public class UnitTest : CodeFixVerifier
- {
-
- // This section contains code to analyze where no diagnostic should e reported
-
-//
-private const string VariableAssigned = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int i = 0;
- Console.WriteLine(i++);
- }
- }
-}";
-//
-
-//
-private const string AlreadyConst = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- const int i = 0;
- Console.WriteLine(i);
- }
- }
-}";
-//
-
-//
-private const string NoInitializer = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int i;
- i = 0;
- Console.WriteLine(i);
- }
- }
-}";
-//
-
-//
-private const string InitializerNotConstant = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int i = DateTime.Now.DayOfYear;
- Console.WriteLine(i);
- }
- }
-}";
-//
-
-//
-private const string MultipleInitializers = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int i = 0, j = DateTime.Now.DayOfYear;
- Console.WriteLine(i, j);
- }
- }
-}";
-//
-
-//
-private const string DeclarationIsInvalid = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int x = ""abc"";
- }
- }
-}";
-//
-
-//
-private const string ReferenceTypeIsntString = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- object s = ""abc"";
- }
- }
-}";
-//
-
-// This section contains code to analyze where the diagnostic should trigger,
-// followed by the code after the fix has been applied.
-
-//
-private const string LocalIntCouldBeConstant = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- int i = 0;
- Console.WriteLine(i);
- }
- }
-}";
-
-private const string LocalIntCouldBeConstantFixed = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- const int i = 0;
- Console.WriteLine(i);
- }
- }
-}";
-//
-
-//
-private const string ConstantIsString = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- string s = ""abc"";
- }
- }
-}";
-
-private const string ConstantIsStringFixed = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- const string s = ""abc"";
- }
- }
-}";
-//
-
-//
-private const string DeclarationUsesVar = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- var item = 4;
- }
- }
-}";
-
-private const string DeclarationUsesVarFixedHasType = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- const int item = 4;
- }
- }
-}";
-private const string StringDeclarationUsesVar = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- var item = ""abc"";
- }
- }
-}";
-private const string StringDeclarationUsesVarFixedHasType = @"
-using System;
-
-namespace MakeConstTest
-{
- class Program
- {
- static void Main(string[] args)
- {
- const string item = ""abc"";
- }
- }
-}";
-//
-
- //
- //No diagnostics expected to show up
- [DataTestMethod]
- [DataRow(""),
- DataRow(VariableAssigned),
- DataRow(AlreadyConst),
- DataRow(NoInitializer),
- DataRow(InitializerNotConstant),
- DataRow(MultipleInitializers),
- DataRow(DeclarationIsInvalid),
- DataRow(ReferenceTypeIsntString)]
- public void WhenTestCodeIsValidNoDiagnosticIsTriggered(string testCode)
- {
- VerifyCSharpDiagnostic(testCode);
- }
-
- [DataTestMethod]
- [DataRow(LocalIntCouldBeConstant, LocalIntCouldBeConstantFixed, 10, 13),
- DataRow(ConstantIsString, ConstantIsStringFixed, 10, 13),
- DataRow(DeclarationUsesVar, DeclarationUsesVarFixedHasType, 10, 13),
- DataRow(StringDeclarationUsesVar, StringDeclarationUsesVarFixedHasType, 10, 13)]
- public void WhenDiagosticIsRaisedFixUpdatesCode(
- string test,
- string fixTest,
- int line,
- int column)
- {
- var expected = new DiagnosticResult
- {
- Id = MakeConstAnalyzer.DiagnosticId,
- Message = new LocalizableResourceString(nameof(MakeConst.Resources.AnalyzerMessageFormat), MakeConst.Resources.ResourceManager, typeof(MakeConst.Resources)).ToString(),
- Severity = DiagnosticSeverity.Warning,
- Locations =
- new[] {
- new DiagnosticResultLocation("Test0.cs", line, column)
- }
- };
-
- VerifyCSharpDiagnostic(test, expected);
-
- VerifyCSharpFix(test, fixTest);
- }
- //
-
- protected override CodeFixProvider GetCSharpCodeFixProvider()
- {
- return new MakeConstCodeFixProvider();
- }
-
- protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
- {
- return new MakeConstAnalyzer();
- }
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/CodeFixVerifier.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/CodeFixVerifier.cs
deleted file mode 100644
index ebfcd33533d56..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/CodeFixVerifier.cs
+++ /dev/null
@@ -1,128 +0,0 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CodeActions;
-using Microsoft.CodeAnalysis.CodeFixes;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.CodeAnalysis.Formatting;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-
-namespace TestHelper
-{
- ///
- /// Superclass of all Unit tests made for diagnostics with codefixes.
- /// Contains methods used to verify correctness of codefixes
- ///
- public abstract partial class CodeFixVerifier : DiagnosticVerifier
- {
- ///
- /// Returns the codefix being tested (C#) - to be implemented in non-abstract class
- ///
- /// The CodeFixProvider to be used for CSharp code
- protected virtual CodeFixProvider GetCSharpCodeFixProvider()
- {
- return null;
- }
-
- ///
- /// Returns the codefix being tested (VB) - to be implemented in non-abstract class
- ///
- /// The CodeFixProvider to be used for VisualBasic code
- protected virtual CodeFixProvider GetBasicCodeFixProvider()
- {
- return null;
- }
-
- ///
- /// Called to test a C# codefix when applied on the inputted string as a source
- ///
- /// A class in the form of a string before the CodeFix was applied to it
- /// A class in the form of a string after the CodeFix was applied to it
- /// Index determining which codefix to apply if there are multiple
- /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
- protected void VerifyCSharpFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false)
- {
- VerifyFix(LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), GetCSharpCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics);
- }
-
- ///
- /// Called to test a VB codefix when applied on the inputted string as a source
- ///
- /// A class in the form of a string before the CodeFix was applied to it
- /// A class in the form of a string after the CodeFix was applied to it
- /// Index determining which codefix to apply if there are multiple
- /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
- protected void VerifyBasicFix(string oldSource, string newSource, int? codeFixIndex = null, bool allowNewCompilerDiagnostics = false)
- {
- VerifyFix(LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), GetBasicCodeFixProvider(), oldSource, newSource, codeFixIndex, allowNewCompilerDiagnostics);
- }
-
- ///
- /// General verifier for codefixes.
- /// Creates a Document from the source string, then gets diagnostics on it and applies the relevant codefixes.
- /// Then gets the string after the codefix is applied and compares it with the expected result.
- /// Note: If any codefix causes new diagnostics to show up, the test fails unless allowNewCompilerDiagnostics is set to true.
- ///
- /// The language the source code is in
- /// The analyzer to be applied to the source code
- /// The codefix to be applied to the code wherever the relevant Diagnostic is found
- /// A class in the form of a string before the CodeFix was applied to it
- /// A class in the form of a string after the CodeFix was applied to it
- /// Index determining which codefix to apply if there are multiple
- /// A bool controlling whether or not the test will fail if the CodeFix introduces other warnings after being applied
- private void VerifyFix(string language, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider, string oldSource, string newSource, int? codeFixIndex, bool allowNewCompilerDiagnostics)
- {
- var document = CreateDocument(oldSource, language);
- var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
- var compilerDiagnostics = GetCompilerDiagnostics(document);
- var attempts = analyzerDiagnostics.Length;
-
- for (int i = 0; i < attempts; ++i)
- {
- var actions = new List();
- var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
- codeFixProvider.RegisterCodeFixesAsync(context).Wait();
-
- if (!actions.Any())
- {
- break;
- }
-
- if (codeFixIndex != null)
- {
- document = ApplyFix(document, actions.ElementAt((int)codeFixIndex));
- break;
- }
-
- document = ApplyFix(document, actions.ElementAt(0));
- analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(analyzer, new[] { document });
-
- var newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));
-
- //check if applying the code fix introduced any new compiler diagnostics
- if (!allowNewCompilerDiagnostics && newCompilerDiagnostics.Any())
- {
- // Format and get the compiler diagnostics again so that the locations make sense in the output
- document = document.WithSyntaxRoot(Formatter.Format(document.GetSyntaxRootAsync().Result, Formatter.Annotation, document.Project.Solution.Workspace));
- newCompilerDiagnostics = GetNewDiagnostics(compilerDiagnostics, GetCompilerDiagnostics(document));
-
- Assert.IsTrue(false,
- string.Format("Fix introduced new compiler diagnostics:\r\n{0}\r\n\r\nNew document:\r\n{1}\r\n",
- string.Join("\r\n", newCompilerDiagnostics.Select(d => d.ToString())),
- document.GetSyntaxRootAsync().Result.ToFullString()));
- }
-
- //check if there are analyzer diagnostics left after the code fix
- if (!analyzerDiagnostics.Any())
- {
- break;
- }
- }
-
- //after applying all of the code fixes, compare the resulting string to the inputted one
- var actual = GetStringFromDocument(document);
- Assert.AreEqual(newSource, actual);
- }
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/DiagnosticVerifier.cs b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/DiagnosticVerifier.cs
deleted file mode 100644
index 22f8fce5db227..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.Test/Verifiers/DiagnosticVerifier.cs
+++ /dev/null
@@ -1,270 +0,0 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.Diagnostics;
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace TestHelper
-{
- ///
- /// Superclass of all Unit Tests for DiagnosticAnalyzers
- ///
- public abstract partial class DiagnosticVerifier
- {
- #region To be implemented by Test classes
- ///
- /// Get the CSharp analyzer being tested - to be implemented in non-abstract class
- ///
- protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
- {
- return null;
- }
-
- ///
- /// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
- ///
- protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
- {
- return null;
- }
- #endregion
-
- #region Verifier wrappers
-
- ///
- /// Called to test a C# DiagnosticAnalyzer when applied on the single inputted string as a source
- /// Note: input a DiagnosticResult for each Diagnostic expected
- ///
- /// A class in the form of a string to run the analyzer on
- /// DiagnosticResults that should appear after the analyzer is run on the source
- protected void VerifyCSharpDiagnostic(string source, params DiagnosticResult[] expected)
- {
- VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
- }
-
- ///
- /// Called to test a VB DiagnosticAnalyzer when applied on the single inputted string as a source
- /// Note: input a DiagnosticResult for each Diagnostic expected
- ///
- /// A class in the form of a string to run the analyzer on
- /// DiagnosticResults that should appear after the analyzer is run on the source
- protected void VerifyBasicDiagnostic(string source, params DiagnosticResult[] expected)
- {
- VerifyDiagnostics(new[] { source }, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
- }
-
- ///
- /// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source
- /// Note: input a DiagnosticResult for each Diagnostic expected
- ///
- /// An array of strings to create source documents from to run the analyzers on
- /// DiagnosticResults that should appear after the analyzer is run on the sources
- protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected)
- {
- VerifyDiagnostics(sources, LanguageNames.CSharp, GetCSharpDiagnosticAnalyzer(), expected);
- }
-
- ///
- /// Called to test a VB DiagnosticAnalyzer when applied on the inputted strings as a source
- /// Note: input a DiagnosticResult for each Diagnostic expected
- ///
- /// An array of strings to create source documents from to run the analyzers on
- /// DiagnosticResults that should appear after the analyzer is run on the sources
- protected void VerifyBasicDiagnostic(string[] sources, params DiagnosticResult[] expected)
- {
- VerifyDiagnostics(sources, LanguageNames.VisualBasic, GetBasicDiagnosticAnalyzer(), expected);
- }
-
- ///
- /// General method that gets a collection of actual diagnostics found in the source after the analyzer is run,
- /// then verifies each of them.
- ///
- /// An array of strings to create source documents from to run the analyzers on
- /// The language of the classes represented by the source strings
- /// The analyzer to be run on the source code
- /// DiagnosticResults that should appear after the analyzer is run on the sources
- private void VerifyDiagnostics(string[] sources, string language, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expected)
- {
- var diagnostics = GetSortedDiagnostics(sources, language, analyzer);
- VerifyDiagnosticResults(diagnostics, analyzer, expected);
- }
-
- #endregion
-
- #region Actual comparisons and verifications
- ///
- /// Checks each of the actual Diagnostics found and compares them with the corresponding DiagnosticResult in the array of expected results.
- /// Diagnostics are considered equal only if the DiagnosticResultLocation, Id, Severity, and Message of the DiagnosticResult match the actual diagnostic.
- ///
- /// The Diagnostics found by the compiler after running the analyzer on the source code
- /// The analyzer that was being run on the sources
- /// Diagnostic Results that should have appeared in the code
- private static void VerifyDiagnosticResults(IEnumerable actualResults, DiagnosticAnalyzer analyzer, params DiagnosticResult[] expectedResults)
- {
- int expectedCount = expectedResults.Count();
- int actualCount = actualResults.Count();
-
- if (expectedCount != actualCount)
- {
- string diagnosticsOutput = actualResults.Any() ? FormatDiagnostics(analyzer, actualResults.ToArray()) : " NONE.";
-
- Assert.IsTrue(false,
- string.Format("Mismatch between number of diagnostics returned, expected \"{0}\" actual \"{1}\"\r\n\r\nDiagnostics:\r\n{2}\r\n", expectedCount, actualCount, diagnosticsOutput));
- }
-
- for (int i = 0; i < expectedResults.Length; i++)
- {
- var actual = actualResults.ElementAt(i);
- var expected = expectedResults[i];
-
- if (expected.Line == -1 && expected.Column == -1)
- {
- if (actual.Location != Location.None)
- {
- Assert.IsTrue(false,
- string.Format("Expected:\nA project diagnostic with No location\nActual:\n{0}",
- FormatDiagnostics(analyzer, actual)));
- }
- }
- else
- {
- VerifyDiagnosticLocation(analyzer, actual, actual.Location, expected.Locations.First());
- var additionalLocations = actual.AdditionalLocations.ToArray();
-
- if (additionalLocations.Length != expected.Locations.Length - 1)
- {
- Assert.IsTrue(false,
- string.Format("Expected {0} additional locations but got {1} for Diagnostic:\r\n {2}\r\n",
- expected.Locations.Length - 1, additionalLocations.Length,
- FormatDiagnostics(analyzer, actual)));
- }
-
- for (int j = 0; j < additionalLocations.Length; ++j)
- {
- VerifyDiagnosticLocation(analyzer, actual, additionalLocations[j], expected.Locations[j + 1]);
- }
- }
-
- if (actual.Id != expected.Id)
- {
- Assert.IsTrue(false,
- string.Format("Expected diagnostic id to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Id, actual.Id, FormatDiagnostics(analyzer, actual)));
- }
-
- if (actual.Severity != expected.Severity)
- {
- Assert.IsTrue(false,
- string.Format("Expected diagnostic severity to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Severity, actual.Severity, FormatDiagnostics(analyzer, actual)));
- }
-
- if (actual.GetMessage() != expected.Message)
- {
- Assert.IsTrue(false,
- string.Format("Expected diagnostic message to be \"{0}\" was \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Message, actual.GetMessage(), FormatDiagnostics(analyzer, actual)));
- }
- }
- }
-
- ///
- /// Helper method to VerifyDiagnosticResult that checks the location of a diagnostic and compares it with the location in the expected DiagnosticResult.
- ///
- /// The analyzer that was being run on the sources
- /// The diagnostic that was found in the code
- /// The Location of the Diagnostic found in the code
- /// The DiagnosticResultLocation that should have been found
- private static void VerifyDiagnosticLocation(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, Location actual, DiagnosticResultLocation expected)
- {
- var actualSpan = actual.GetLineSpan();
-
- Assert.IsTrue(actualSpan.Path == expected.Path || (actualSpan.Path != null && actualSpan.Path.Contains("Test0.") && expected.Path.Contains("Test.")),
- string.Format("Expected diagnostic to be in file \"{0}\" was actually in file \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Path, actualSpan.Path, FormatDiagnostics(analyzer, diagnostic)));
-
- var actualLinePosition = actualSpan.StartLinePosition;
-
- // Only check line position if there is an actual line in the real diagnostic
- if (actualLinePosition.Line > 0)
- {
- if (actualLinePosition.Line + 1 != expected.Line)
- {
- Assert.IsTrue(false,
- string.Format("Expected diagnostic to be on line \"{0}\" was actually on line \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Line, actualLinePosition.Line + 1, FormatDiagnostics(analyzer, diagnostic)));
- }
- }
-
- // Only check column position if there is an actual column position in the real diagnostic
- if (actualLinePosition.Character > 0)
- {
- if (actualLinePosition.Character + 1 != expected.Column)
- {
- Assert.IsTrue(false,
- string.Format("Expected diagnostic to start at column \"{0}\" was actually at column \"{1}\"\r\n\r\nDiagnostic:\r\n {2}\r\n",
- expected.Column, actualLinePosition.Character + 1, FormatDiagnostics(analyzer, diagnostic)));
- }
- }
- }
- #endregion
-
- #region Formatting Diagnostics
- ///
- /// Helper method to format a Diagnostic into an easily readable string
- ///
- /// The analyzer that this verifier tests
- /// The Diagnostics to be formatted
- /// The Diagnostics formatted as a string
- private static string FormatDiagnostics(DiagnosticAnalyzer analyzer, params Diagnostic[] diagnostics)
- {
- var builder = new StringBuilder();
- for (int i = 0; i < diagnostics.Length; ++i)
- {
- builder.AppendLine("// " + diagnostics[i].ToString());
-
- var analyzerType = analyzer.GetType();
- var rules = analyzer.SupportedDiagnostics;
-
- foreach (var rule in rules)
- {
- if (rule != null && rule.Id == diagnostics[i].Id)
- {
- var location = diagnostics[i].Location;
- if (location == Location.None)
- {
- builder.AppendFormat("GetGlobalResult({0}.{1})", analyzerType.Name, rule.Id);
- }
- else
- {
- Assert.IsTrue(location.IsInSource,
- $"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n");
-
- string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt";
- var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
-
- builder.AppendFormat("{0}({1}, {2}, {3}.{4})",
- resultMethodName,
- linePosition.Line + 1,
- linePosition.Character + 1,
- analyzerType.Name,
- rule.Id);
- }
-
- if (i != diagnostics.Length - 1)
- {
- builder.Append(',');
- }
-
- builder.AppendLine();
- break;
- }
- }
- }
- return builder.ToString();
- }
- #endregion
- }
-}
diff --git a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.sln b/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.sln
deleted file mode 100644
index 9788e974e5342..0000000000000
--- a/samples/snippets/csharp/roslyn-sdk/Tutorials/MakeConst/MakeConst.sln
+++ /dev/null
@@ -1,37 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.2042
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeConst", "MakeConst\MakeConst.csproj", "{A0728C97-E1F1-48A2-BC54-2535D0F1454D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MakeConst.Test", "MakeConst.Test\MakeConst.Test.csproj", "{244B901F-16DB-42FA-BE3C-98DF48E560E9}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MakeConst.Vsix", "MakeConst.Vsix\MakeConst.Vsix.csproj", "{3CF664FF-1B84-42E0-B87B-5C6CF28C880B}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {A0728C97-E1F1-48A2-BC54-2535D0F1454D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0728C97-E1F1-48A2-BC54-2535D0F1454D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0728C97-E1F1-48A2-BC54-2535D0F1454D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0728C97-E1F1-48A2-BC54-2535D0F1454D}.Release|Any CPU.Build.0 = Release|Any CPU
- {244B901F-16DB-42FA-BE3C-98DF48E560E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {244B901F-16DB-42FA-BE3C-98DF48E560E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {244B901F-16DB-42FA-BE3C-98DF48E560E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {244B901F-16DB-42FA-BE3C-98DF48E560E9}.Release|Any CPU.Build.0 = Release|Any CPU
- {3CF664FF-1B84-42E0-B87B-5C6CF28C880B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {3CF664FF-1B84-42E0-B87B-5C6CF28C880B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {3CF664FF-1B84-42E0-B87B-5C6CF28C880B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {3CF664FF-1B84-42E0-B87B-5C6CF28C880B}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {B8FD1575-8A45-488D-8624-ABB0D59B89C7}
- EndGlobalSection
-EndGlobal