チュートリアル: 最初のアナライザーとコード修正を作成する
.NET Compiler Platform SDK には、C# または Visual Basic コードをターゲットとするカスタム診断 (アナライザー)、コード修正、コード リファクタリング、診断サプレッサーを作成するために必要なツールが用意されています。
このチュートリアルでは、Roslyn API を使用して
必須コンポーネント
[ Visual Studio 2019](https://www.visualstudio.com/downloads) バージョン 16.8 以降
Visual Studio インストーラーで
インストール手順 - Visual Studio インストーラー
Visual Studio インストーラーで .NET Compiler Platform SDK を見つけるには、以下の 2 つの異なる方法があります。
Visual Studio インストーラーを使用したインストール - ワークロード ビュー
.NET Compiler Platform SDK は、Visual Studio 拡張機能の開発ワークロードの一部として自動的に選択されません。 省略可能なコンポーネントとして選択する必要があります。
- Visual Studio インストーラーを実行します。
- [変更] を選択します
- Visual Studio 拡張機能の開発ワークロードを確認します。
- 概要ツリーの [Visual Studio 拡張機能の開発] ノードを開きます。
- [.NET Compiler Platform SDK] のチェック ボックスをオンにします。 省略可能なコンポーネントの最後に表示されます。
また、必要に応じて、DGML エディターのビジュアライザーでグラフを表示します。
- 概要ツリーの [個別のコンポーネント] ノードを開きます。
- [DGML エディター] のチェック ボックスをオンにします。
Visual Studio インストーラーを使用したインストール - [個別のコンポーネント] タブ
- Visual Studio インストーラーを実行します。
- [変更] を選択します
- [個別のコンポーネント] タブを選択します。
- [.NET Compiler Platform SDK] のチェック ボックスをオンにします。 [コンパイラ、ビルド ツール、およびランタイム] セクションの上部に表示されます。
また、必要に応じて、DGML エディターのビジュアライザーでグラフを表示します。
- [DGML エディター] チェック ボックスをオンにします。 [コード ツール] セクションに表示されます。
アナライザーの作成と検証にはいくつかの手順があります。
- ソリューションを作成します。
- アナライザーの名前と説明を登録します。
- アナライザーの警告と推奨事項を報告します。
- 推奨事項を受け取るコード修正を実装します。
- 単体テストで分析を改善します。
ソリューションを作成する
- Visual Studio で、[ファイル] > [新規] > [プロジェクト...] の順に選択して、[新しいプロジェクト] ダイアログを表示します。
- [Visual C#] > [Extensibility] で、[Analyzer with code fix (.NET Standard)](コード修正付きアナライザー (.NET Standard)) を選択します。
- プロジェクトに「
** MakeConst** 」という名前を付けて、[OK] をクリックします。
Note
コンパイル エラー ("MSB4062: "CompareBuildTaskVersion" タスクを読み込めませんでした") が表示される場合があります。 解決するには、NuGet パッケージ マネージャーを使ってソリューション内の NuGet パッケージを更新するか、パッケージ マネージャー コンソール ウィンドウで Update-Package
を使います。
アナライザー テンプレートを調べる
コード修正テンプレート付きアナライザーを使用すると、5 つのプロジェクトが作成されます。
** MakeConst** : アナライザーが含まれています。** MakeConst.CodeFixes** : コード修正が含まれています。** MakeConst.Package** : アナライザーとコード修正の NuGet パッケージを生成するために使用されます。** MakeConst.Test** : 単体テスト プロジェクトです。** MakeConst.Vsix** : 新しいアナライザーが読み込まれた Visual Studio の 2 番目のインスタンスを起動する既定のスタートアップ プロジェクトです。F5 キーを押して、VSIX プロジェクトを開始します。
Note
アナライザーは、.NET Core 環境 (コマンド ライン ビルド) と .NET Framework 環境 (Visual Studio) で実行できるため、.NET Standard 2.0 をターゲットにする必要があります。
ヒント
アナライザーを実行するときは、Visual Studio の 2 つ目のコピーを開始します。 この 2 つ目のコピーは、別のレジストリ ハイブを使用して設定を保存します。 これにより、Visual Studio の 2 つのコピーのビジュアル設定を区別することができます。 Visual Studio の実験的な実行のために、別のテーマを選択することができます。 また、設定のローミングや、Visual Studio アカウントへのログインには、Visual Studio の実験的な実行が使用されません。 そのため、設定を分けておくことができます。
ハイブには、開発中のアナライザーだけでなく、以前に開いたアナライザーも含まれます。 Roslyn ハイブをリセットするには、%LocalAppData%\Microsoft\VisualStudio から手動で削除する必要があります。 Roslyn ハイブのフォルダー名は、
開始した 2 つ目の Visual Studio インスタンスで、新しい C# コンソール アプリケーション プロジェクトを作成します (任意のターゲット フレームワークが動作し、アナライザーはソース レベルで動作します)。波線の下線が表示されているトークンにマウス カーソルを移動すると、アナライザーに設定されている警告テキストが表示されます。
テンプレートを使用すると、次の図のように、型名が小文字の個々の型宣言に対して警告を報告するアナライザーが作成されます。
また、テンプレートには、小文字を含むすべての型名をすべて大文字に変更するコード修正も用意されています。 警告が表示された電球をクリックすると、推奨される変更が表示されます。 推奨される変更を受け入れると、型の名前と、ソリューション内のその型に対するすべての参照が更新されます。 最初のアナライザーの動作を確認したら、2 つ目の Visual Studio インスタンスを閉じ、アナライザー プロジェクトに戻ります。
アナライザーのすべての変更をテストするために、Visual Studio の 2 つ目のコピーを開始して、新しいコードを作成する必要はありません。 このテンプレートを使用すると、単体テスト プロジェクトも自動的に作成されます。 このプロジェクトには 2 つのテストが含まれています。
ヒント
アナライザーの単体テストは、アナライザーをトリガーするべきコード構造とトリガーするべきではないコード構造を知りたいときに最適なツールです。 Visual Studio のもう 1 つのコピーにアナライザーを読み込むことは、まだ検討していない可能性のあるコンストラクトを探索して見つけるために最適なツールです。
このチュートリアルでは、ローカル定数に変換できるローカル変数宣言をユーザーに報告するアナライザーを記述します。 次に例を示します。
int x = 0;
Console.WriteLine(x);
上記のコードでは、
const int x = 0;
Console.WriteLine(x);
変数を定数にすることができるかどうかを判断するために、構文解析、初期化子式の定数分析、および変数に書き込まれないというデータフロー分析が必要です。 .NET Compiler Platform には、この分析を簡単に実行できる API が用意されています。
アナライザーの登録を作成する
このテンプレートを使用すると、
- すべての診断アナライザーは、動作する言語を記述する
`[DiagnosticAnalyzer]` 属性を提供する必要があります。 - すべての診断アナライザーは、
クラスから (直接または間接的に) 派生する必要があります。
このテンプレートは、アナライザーの一部である基本機能も示しています。
- アクションを登録します。 アクションは、アナライザーをトリガーしてコード違反を調べるコードの変更を表します。 Visual Studio で、登録済みのアクションと一致するコード編集が検出されると、アナライザーの登録済みメソッドが呼び出されます。
- 診断を作成します。 アナライザーで違反を検出されると、違反をユーザーに通知するために Visual Studio で使用される診断オブジェクトが作成されます。
最初の手順は、登録定数が "Make Const" アナライザーを示すように、登録定数と
`AnalyzerDescription` を ":::no-loc text="Variables that are not modified should be made constants."::: " に変更します。`AnalyzerMessageFormat` を ":::no-loc text="Variable '{0}' can be made constant"::: " に変更します。`AnalyzerTitle` を ":::no-loc text="Variable can be made constant"::: " に変更します。
完了すると、次の図のようにリソース エディターが表示されます。
残りの変更はアナライザー ファイル内にあります。 Visual Studio で
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
これを次の行で置き換えます。
context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.LocalDeclarationStatement);
この変更後は、
private void AnalyzeNode(SyntaxNodeAnalysisContext context)
{
}
次のコードに示すように、
private const string Category = "Usage";
定数の可能性があるローカル宣言を見つける
次は
int x = 0;
Console.WriteLine(x);
最初の手順は、ローカル宣言を見つけることです。
var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
アナライザーにローカル宣言 (ローカル宣言のみ) の変更が登録されているため、このキャストは常に成功します。 他のノードの種類では
// make sure the declaration isn't already const:
if (localDeclaration.Modifiers.Any(SyntaxKind.ConstKeyword))
{
return;
}
最後に、変数を
// Perform data flow analysis on the local declaration.
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.
VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
この追加したコードで、変数は変更されなくなります。そのため、
context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation(), localDeclaration.Declaration.Variables.First().Identifier.ValueText));
int x = 0;
Console.WriteLine(x);
電球が表示され、アナライザーから診断が報告されます。 ただし、ご使用の Visual Studio のバージョンによっては、次のいずれかが表示されます。
- 電球。テンプレートで生成されたコード修正が引き続き使用されており、大文字にできることがわかります。
- エディターの上部に表示される、"'MakeConstCodeFixProvider' でエラーが生じ、無効になりました。" というバナー メッセージ。 これは、コード修正プロバイダーはまだ変更されていないため、
LocalDeclarationStatementSyntax
要素ではなくTypeDeclarationSyntax
要素が見つかることを想定しているためです。
次のセクションでは、コード修正の記述方法について説明します。
コード修正を記述する
アナライザーで、1 つまたは複数のコード修正が表示される可能性があります。 コード修正では、報告された問題を解決する編集が定義されます。 作成したアナライザーに、const キーワードを挿入するコード修正を用意することができます。
- int x = 0;
+ const int x = 0;
Console.WriteLine(x);
エディターの電球の UI からユーザーが選択すると、Visual Studio によってコードが変更されます。
テンプレートによって追加された
次に
すべてのコード修正プロバイダーは
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<LocalDeclarationStatementSyntax>().First();
次に、最後の行を変更してコード修正を登録します。 この修正で、既存の宣言に
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: CodeFixResources.CodeFixTitle,
createChangedDocument: c => MakeConstAsync(context.Document, declaration, c),
equivalenceKey: nameof(CodeFixResources.CodeFixTitle)),
diagnostic);
シンボル
private static async Task<Document> MakeConstAsync(Document document,
LocalDeclarationStatementSyntax localDeclaration,
CancellationToken cancellationToken)
{
}
新しい
宣言ステートメントの前に挿入される新しい MakeConstAsync
メソッドに次のコードを追加します。
// Remove the leading trivia from the local declaration.
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.
SyntaxToken constToken = SyntaxFactory.Token(leadingTrivia, SyntaxKind.ConstKeyword, SyntaxFactory.TriviaList(SyntaxFactory.ElasticMarker));
次に、以下のコードを使用して宣言に
// Insert the const token into the modifiers list, creating a new modifiers list.
SyntaxTokenList newModifiers = trimmedLocal.Modifiers.Insert(0, constToken);
// Produce the new local declaration.
LocalDeclarationStatementSyntax newLocal = trimmedLocal
.WithModifiers(newModifiers)
.WithDeclaration(localDeclaration.Declaration);
次に、C# の書式設定規則に合わせて新しい宣言の書式を設定します。 既存のコードに合わせて変更の書式を設定すると、エクスペリエンスが向上します。 既存のコードの直後に次のステートメントを追加します。
// Add an annotation to format the new local declaration.
LocalDeclarationStatementSyntax formattedLocal = newLocal.WithAdditionalAnnotations(Formatter.Annotation);
このコード用に新しい名前空間が必要です。 次の
using Microsoft.CodeAnalysis.Formatting;
最後の手順は編集です。 このプロセスには 3 つの手順があります。
- 既存のドキュメントのハンドルを取得する。
- 既存の宣言を新しい宣言で置き換えて、新しいドキュメントを作成する。
- 新しいドキュメントを返す。
// Replace the old local declaration with the new local declaration.
SyntaxNode oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode newRoot = oldRoot.ReplaceNode(localDeclaration, formattedLocal);
// Return document with transformed tree.
return document.WithSyntaxRoot(newRoot);
コード修正を試す準備が整いました。
ここでは、さまざまな作業を行いました。
単体テストをビルドする
アナライザーとコード修正は、const にすることができる単一の宣言という単純なケースで動作します。 この実装によって間違いが発生する可能性のある宣言ステートメントは数多くあります。 このようなケースには、テンプレートによって作成される単体テスト ライブラリを使用して対処します。 これは、Visual Studio の 2 つ目のコピーを繰り返し開くよりもはるかに高速です。
単体テスト プロジェクトで
このテンプレートは、単体テストのために、
ヒント
テスト ライブラリでは、次のような特殊なマークアップ構文がサポートされています。
`[|text|]` : 診断が`text` に報告されることを示します。 既定では、このフォームは、`DiagnosticAnalyzer.SupportedDiagnostics` によって提供される`DiagnosticDescriptor` が 1 つだけのアナライザーのテストにのみ使用できます。{|ExpectedDiagnosticId:text|}
: IdExpectedDiagnosticId
を使用した診断がtext
に報告されることを示します。
[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);
}
}
");
}
このテストを実行して、合格したことを確認します。 Visual Studio で、
有効な宣言のテストを作成する
一般的な規則として、アナライザーはできるだけ早く終了し、最低限の作業を行う必要があります。 ユーザーがコードを編集すると、Visual Studio では登録済みのアナライザーが呼び出されます。 応答性は重要な要件です。 診断を呼び出すべきではないコードのテストケースがいくつかあります。 このアナライザーは、このようなテストのうち、初期化後に変数が割り当てられるケースのテストを既に処理しています。 そのケースを表すために、次のテスト メソッドを追加します。
[TestMethod]
public async Task VariableIsAssigned_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int i = 0;
Console.WriteLine(i++);
}
}
");
}
このテストも合格します。 次に、まだ処理していない条件のテスト メソッドを追加します。
既に const なので、既に
`const` である宣言:[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); } } "); }
C# は複数の宣言を 1 つのステートメントとして許可するので、さらに複雑になる可能性があります。 次のテスト ケース文字列定数を考えてみましょう。
[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);
}
}
");
}
変数
テストをもう一度実行すると、これらの新しいテスト ケースが失敗することがわかります。
正しい宣言を無視するようにアナライザーを更新する
アナライザーの
- セマンティック分析では、単一の変数宣言を調査しました。 このコードは、同じステートメントで宣言されたすべての変数を調査する
`foreach` ループ内に配置する必要があります。 - 宣言された各変数には初期化子が必要です。
- 宣言された各変数の初期化子は、コンパイル時定数にする必要があります。
// Perform data flow analysis on the local declaration.
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.
VariableDeclaratorSyntax variable = localDeclaration.Declaration.Variables.Single();
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
次のコード スニペットのようにします。
// Ensure that all variables in the local declaration have initializers that
// are assigned with constant values.
foreach (VariableDeclaratorSyntax variable in localDeclaration.Declaration.Variables)
{
EqualsValueClauseSyntax initializer = variable.Initializer;
if (initializer == null)
{
return;
}
Optional<object> constantValue = context.SemanticModel.GetConstantValue(initializer.Value, context.CancellationToken);
if (!constantValue.HasValue)
{
return;
}
}
// Perform data flow analysis on the local declaration.
DataFlowAnalysis dataFlowAnalysis = context.SemanticModel.AnalyzeDataFlow(localDeclaration);
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.
ISymbol variableSymbol = context.SemanticModel.GetDeclaredSymbol(variable, context.CancellationToken);
if (dataFlowAnalysis.WrittenOutside.Contains(variableSymbol))
{
return;
}
}
最初の
最後の仕上げを加える
完了までもう少しです。 アナライザーが処理できる条件がまだいくつかあります。 Visual Studio では、ユーザーがコードを記述しているときにアナライザーが呼び出されます。 コンパイルされないコードに対してアナライザーが呼び出されることはよくあります。 診断アナライザーの int i = "abc"
のような正しくない宣言でもそのままローカル定数に変換されます。 この場合は、テスト メソッドを追加します。
[TestMethod]
public async Task DeclarationIsInvalid_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
int x = {|CS0029:""abc""|};
}
}
");
}
また、参照型が正しく処理されません。 参照型に使用できる唯一の定数値は、文字列リテラルを許可する System.String の場合を除き、null
です。 つまり、
[TestMethod]
public async Task DeclarationIsNotString_NoDiagnostic()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class Program
{
static void Main()
{
object s = ""abc"";
}
}
");
}
さらに徹底するには、文字列の定数宣言を作成できるように別のテストを追加します。 次のスニペットでは、診断を呼び出すコードと、修正が適用された後のコードの両方を定義しています。
[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"";
}
}
");
}
幸いにも、上記のすべてのバグは、ここで学んだテクニックを使って解決できます。
最初のバグを修正するには、まず
TypeSyntax variableTypeName = localDeclaration.Declaration.Type;
ITypeSymbol variableType = context.SemanticModel.GetTypeInfo(variableTypeName, context.CancellationToken).ConvertedType;
次に、
// Ensure that the initializer value can be converted to the type of the
// local declaration without a user-defined conversion.
Conversion conversion = context.SemanticModel.ClassifyConversion(initializer.Value, variableType);
if (!conversion.Exists || conversion.IsUserDefined)
{
return;
}
次の変更は、最後の変更に基づいています。 最初の foreach ループの中かっこの前に、定数が文字列または null の場合にローカル宣言の型を検査する次のコードを追加します。
// 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;
}
- 宣言が
`var` 宣言かどうかを検査し、その場合は次の処理を実行します。 - 推定型の新しい型を作成します。
- 型宣言がエイリアスでないことを確認します。 その場合は、
`const var` を宣言することができます。 `var` がこのプログラムの型名でないことを確認します (その場合、`const var` は有効です)。- 完全な型名を簡略化する
多数のコードが必要なようですが、 そうではありません。
// If the type of the declaration is 'var', create a new type name
// for the inferred type.
VariableDeclarationSyntax variableDeclaration = localDeclaration.Declaration;
TypeSyntax variableTypeName = variableDeclaration.Type;
if (variableTypeName.IsVar)
{
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).
IAliasSymbol aliasInfo = semanticModel.GetAliasInfo(variableTypeName, cancellationToken);
if (aliasInfo == null)
{
// Retrieve the type inferred for var.
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.
TypeSyntax typeName = SyntaxFactory.ParseTypeName(type.ToDisplayString())
.WithLeadingTrivia(variableTypeName.GetLeadingTrivia())
.WithTrailingTrivia(variableTypeName.GetTrailingTrivia());
// Add an annotation to simplify the type name.
TypeSyntax simplifiedTypeName = typeName.WithAdditionalAnnotations(Simplifier.Annotation);
// Replace the type in the variable declaration.
variableDeclaration = variableDeclaration.WithType(simplifiedTypeName);
}
}
}
// Produce the new local declaration.
LocalDeclarationStatementSyntax newLocal = trimmedLocal.WithModifiers(newModifiers)
.WithDeclaration(variableDeclaration);
using Microsoft.CodeAnalysis.Simplification;
テストを実行します。テストはすべて合格するはずです。 完成したアナライザーを実行してみてください。
- 2 つ目の Visual Studio インスタンスで、新しい C# コンソール アプリケーション プロジェクトを作成し、
`int x = "abc";` を Main メソッドに追加します。 最初のバグ修正のおかげで、このローカル変数宣言について警告は報告されません (ただし、想定どおりコンパイラ エラーが発生します)。 - 次に、Main メソッドに
`object s = "abc";` を追加します。 2 つ目のバグ修正のおかげで、警告は報告されません。 - 最後に、
`var` キーワードを使用する別のローカル変数を追加します。 警告が報告され、左側の下部に推奨が表示されます。 - 波線にエディターのカレットを移動し、
Ctrl + . キーを押します。 推奨されたコード修正が表示されます。 コード修正を選択して、 `var` キーワードが正しく処理されていることを確認します。
最後に、次のコードを追加します。
int i = 2;
int j = 32;
int k = i + j;
これらの変更の後は、最初の 2 つの変数にのみ赤い波線が表示されます。
おめでとうございます! ここでは最初の .NET Compiler Platform 拡張機能を作成しました。これは、その場でコード分析を実行し、問題を検出してそれを修正する簡単な修正案を提供する拡張機能です。 この過程で、.NET Compiler Platform SDK (Roslyn API) に含まれる多くのコード API を学びました。 サンプル GitHub リポジトリの
その他のリソース
[ 構文解析の概要](../get-started/syntax-analysis.md) [ セマンティック解析の概要](../get-started/semantic-analysis.md)
.NET