Roslynで作るC#コンパイラー拡張(2)
.NETコンパイラープラットフォーム拡張の作り方
C# 6.0と同時にリリースされた.NETコンパイラープラットフォーム「Roslyn」。そのコンパイラー拡張の作り方を解説する連載の第2回。
連載第2回は、SDKが提供しているVisual Studioのプロジェクトテンプレートを見ながら、Analyzer with Code Fix(=Diagnostic with Code Fix)とCode Refactoringのプロジェクトの仕組みを紹介する。
Analyzer with Code Fixプロジェクトの作成
前回の開発環境の項で説明した通り、「.NET Compiler Platform SDK」を事前にVisual Studio 2015もしくは次バージョン(現在はプレビュー版)の“Visual Studio 15”にインストールしておく必要がある。今回はVisual Studio 2015 Update 3を利用している。
新しいプロジェクトの作成で図1のように、[Visual C#]-[Extensibility]-[Analyzer with Code Fix (NuGet + VSIX)]テンプレートを選ぶ。名前は「RoslynDemo」とした。
※テンプレートが表示されない場合は、適切な.NET Frameworkのバージョン(4.5.2以降)が選択されているかを確認してほしい。
上記の内容で作成すると、図2のように3つのプロジェクトが生成される。
各プロジェクトの内容は以下の通りだ。
- RoslynDemo (入力したプロジェクト名): CodeFixの本体となるプロジェクト
- RoslynDemo.Test (入力したプロジェクト名.Test): CodeFixの単体テストを行うためのプロジェクト
- RoslynDemo.Vsix (入力したプロジェクト名.Vsix): 開発中のテストのためにVSIX(=Visual Studio Integration Extension: VS拡張機能)で実行するためのプロジェクト
このうち、RoslynDemoプロジェクト内にある2つの.csファイル(CodeFixProvider
クラスとAnalyzer
クラス)がAnalyzer with Code Fixの中心となる処理を記述している。この2つのクラスは次章以降で詳細に説明するので、NuGetライブラリに関するファイルを先に説明しておこう。
RoslynDemoプロジェクトにあるDiagnosctics.nuspecファイルは、このAnalyzer with Code FixをNuGetライブラリとして作成するときに使用するファイルである。基本的には、サンプル値が入っている場所を適切な値に書き換えた後、プロジェクトをリリースビルドすると、.nupkgファイルも一緒に生成される。あとは、この.nupkgファイルをNuGet.orgなどのリポジトリに登録することで、NuGetライブラリとして公開できる。詳しくはNuspec Referenceを参考にしてほしい。
また、toolsフォルダー以下にある2つの.psファイルもNuGetライブラリに含めるスクリプトである。
NuGetライブラリの更新
RoslynDemoプロジェクトはいくつかのNuGetライブラリ(例えばMicrosoft.CodeAnalysis.Commonなど)を参照している。これらのライブラリも機能追加が行われているため、バージョンアップを行いたいことがあるだろう。しかし、バージョンアップするには注意が必要だ。
なぜなら、これらのライブラリはAnalyzer with Code Fixプロジェクトの新規作成時にNuGet経由でダウンロードされて参照に追加されるわけではなく、Visual Studio自体が最初から参照しているDLLに含まれているからである。つまりNuGetでバージョンアップした場合、Analyzerを開発・ビルドする際に参照するバージョンと、Analyzerをプロジェクトに追加して実行する際に参照するバージョンが食い違う可能性が出てくるということだ。
従ってまず、バージョンを上げる場合は(※次の参考画像を参照)、Visual Studioのどのバージョン以上をターゲットにするかを決めよう。決めたら、GitHubのRoslynのReleaseページを見て、対応するバージョンを探す。例えばリリースページを見ると[Visual-Studio-2015-Update-3-Micro-Update-1]という項目が見付かり、そのリンク先を見ると[version-1.3.2]とういタグが付いているのが分かる。この情報から、Visual Studio 2015 Update 3に最新のパッチ(KB3165756)を当てた状態では、そのversion-1.3.2に対応していると判断できる。そのため、開発しているAnalyzerが動作する環境として「Visual Studio 2015 Update 3(さらに正確にはパッチKB316756を当てた状態)以上」をターゲットとするのであれば、参考画像にあるライブラリのバージョンを「1.3.2」まで上げることができる。
NuGetパッケージマネージャーで[更新プログラム]を参照しているが、インストール済みのバージョンは「1.0.0」となっており、NuGet最新の安定版「1.3.2」までアップデート可能であることが分かる。
これに基づき、実際にMicrosoft.CodeAnalysis.Commonを1.3.2にアップデートすると、依存している他のライブラリも同じバージョンにそろう。なお、そのバージョンより下のVisual Studioでは作成したAnalyzerが正常に動作しなくなるため注意が必要だ。
なお、この連載で参照しているコードはGitHubでversion-1.3.2のタグのものを引用している。
VSIXによるデバッグ実行
VSIXプロジェクトは主に開発中のデバッグ実行のために用意されている(が、もちろんAnalyzerを配布するためのVSIXの作成に使うこともできる)。このプロジェクトのおかげで、デバッグ実行で起動されるVisual Studioは、開発で現在起動しているVisual Studioとは別のインスタンスとなり、隔離された設定ファイルで動くようになる。
ただし、1つのソリューション内で複数のAnalyzerを開発する場合、それぞれのデバッグ実行で起動するVisual Studioは同じインスタンスになり、つまりインストールされるAnalyzerが全てのAnalyzerプロジェクト間で共有されてしまう。これを避けたい場合は、Visual StudioがAnalyzerを読み込むフォルダーから該当のAnalyzerを削除するために、[ソリューションのクリーン]を行えばよい。
もしくは、その読み込むフォルダーのパスが実行ユーザー別の%LOCALAPPDATA%\Microsoft\VisualStudio\14.0Roslyn\Extensions\
となるので、その配下に存在する該当Analyzer用フォルダーを手動で直接削除してもよい。なお、このフォルダーパスにおける14.0Roslyn
のRoslynの部分は、デバッグ開始時のコマンドライン引数に指定する/rootsuffix
オプションの値によって変わるので注意してほしい。デフォルト値は図3にある通り、VSIXプロジェクトプロパティの[デバッグ]タブの[コマンドライン引数]に指定されている「Roslyn」という値である。例えばこれを別の値に変更してデバッグ実行すると、異なるフォルダーパスにVisual Studioインスタンス用の設定ファイルやVisual Studio拡張機能となるAnalyzerが生成されるのが確認できる。
DiagnosticAnalyzerクラス
最初に「<プロジェクト名>Analyzer」という名前で生成されるクラスを見てみよう。
本稿のサンプルではRoslynDemoAnalyzer
クラスがそれだ(リスト1)。まず、[DiagnosticAnalyzer(LanguageNames.CSharp)]
という属性が付いているが、これはこのクラスがAnalyzerを提供することを表している。
……省略……
namespace RoslynDemo
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RoslynDemoAnalyzer : DiagnosticAnalyzer
{
……省略……
}
}
|
このクラスはDiagnosticAnalyzer
という抽象クラス(Microsoft.CodeAnalysis.Diagnostics
名前空間)を継承している。このクラスの抽象メンバーを見てみよう。
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.Diagnostics
{
public abstract class DiagnosticAnalyzer
{
public abstract ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; }
public abstract void Initialize(AnalysisContext context);
……省略……
}
}
|
継承先のRoslynDemoAnalyzer
クラスでは、この2つのメンバーを実装することになる。順に見ていこう。
SupportedDiagnosticsプロパティ
このAnalyzerが対応しているDiagnostics機能(=診断機能)に関する説明を、外部から取得するためのプロパティである。説明はDiagnosticDescriptor
クラス(Microsoft.CodeAnalysis
名前空間)で指定するが、必要な情報はこのクラスのコンストラクターで与えることになる。コンストラクターに与えるパラメーターを説明しよう。
- id: このDiagnosticsを一意に識別するID文字列(以下、Diagnostics ID)。
CodeFixProvider
クラスからも参照する必要があるため、定数フィールドで定義することが多い - title: このDiagnosticsのタイトル
- messageFormat: このDiagnosticsの詳細メッセージを生成するためのフォーマット文字列
- category: このDiagnosticsのカテゴリ
- defaultSeverity: デフォルトのSeverity(重要度)。
DiagnosticSeverity
列挙体(Microsoft.CodeAnalysis名前空間)のメンバーで指定する。Error
を指定すればコンパイルエラー扱いにできる。Severityはプロジェクトごとに設定できる - isEnabledByDefault: デフォルトで有効にするかどうかのbool値
- helpLink: Diagnosticsに関する詳細な記述があるURL。ハイパーリンクとして表示される
- description: 省略可能。追加の説明
- customTags: 省略可能。可変長引数として指定するタグ
このうちtitle
/messageFormat
/description
の3つは、多言語化のためのLocalizableResourceString
クラス(Microsoft.CodeAnalysis
名前空間)を利用できる。単一言語で問題なければstring
型を指定することもできる。
このプロパティのシンプルな実装例をリスト3に示す。
……省略……
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RoslynDemoAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "RoslynDemo";
private static readonly DiagnosticDescriptor Rule
= new DiagnosticDescriptor(DiagnosticId,
"Type name contains '_'.",
"Type name '{0}' contains '_'",
"Naming",
DiagnosticSeverity.Error,
true,
helpLinkUri: "http://tech.tanaka733.net",
description: "Type name must not include '_'.");
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
……省略……
}
|
次節で説明するInitialize
メソッド(リスト4)を実装してから(※診断レポートのロジックは後述)、このDiagnosticDescriptorを指定したAnalyzerをデバッグ実行して、コンソールアプリケーションのプロジェクトなどを作成したうえで「Meta_data」という名前のクラスを記述してみると、図4に示すエラーが[エラー一覧]ウィンドウに表示される。[説明]列にフォーマットされたメッセージが、詳細のところにdescription
パラメーターで指定した値が表示されているのが分かる。
またVSIXではなく、NuGetライブラリとしてプロジェクトに追加した場合は、[ソリューション エクスプローラー]上のプロジェクト項目-[参照]-[アナライザー]にAnalyzerが表示される。各Analyzerの重要度(Severity)をプロジェクトごとに変更することもできる(図5)。
Initializeメソッド
次に説明するのがInitialize
メソッドである。
このメソッドは引数にAnalysisContext
型(Microsoft.CodeAnalysis.Diagnostics
名前空間)のインスタンスを取る。Initialize
メソッドの実装方法としては、この引数(context
)が持つRegisterXXXAction
メソッドを呼び出して、“Action”と呼ばれる「コンパイル時に行うDiagnostics処理」を登録することである。登録した処理でDiagnosticsを報告する際に、対応するDiagnosticDescriptor(=リスト3のRule
オブジェクト)を指定することになる。
1つのAnalyzerクラス内で複数のActionを登録することもでき、また複数のAnalyzerクラスに1つのActionを分けて登録することもできる。複数のAnalyzerが存在する場合、それぞれのAnalyzerは並列に実行され得るため、Analyzer間で状態の共有はできない。1つのAnalyzerの中で複数のActionを登録した場合、デフォルトではそのActionは直列に実行される。その順番に関しては、どのRegisterXXXAction
メソッドで登録したかに依存し、優先順位が同じActionの実行順は不定である。この実行順に関してはGitHubのドキュメント(英語)に詳しい記述があるので参考にしてほしい。
どのRegisterXXXAction
メソッドを利用するかは、どのようなActionを実装したいかによって決まるだろう。ActionはAction<T>
型で指定するが、T
にはDiagnosticsの判定に必要な情報が渡される。どのようなDiagnosticsを実装するかを決めた後、実装に必要なContextが決まり、どのRegisterXXXAction
を利用するかが決まる、という流れになる。今回のサンプルコードではクラス名という名前シンボルを使って判定したいため、Symbolに関する情報(=SymbolAnalysisContext
オブジェクト)が必要であることが分かるので、SymbolKind.NamedType
を指定してRegisterSymbolAction
メソッドを呼び出すことにする、という流れになる。
ここまでの説明を参考にして、Initialize
メソッドを登録するActionも含めて実装した例が以下のコードである。
……省略……
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RoslynDemoAnalyzer : DiagnosticAnalyzer
{
……省略……
public override void Initialize(AnalysisContext context)
{
context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
}
private static void AnalyzeSymbol(SymbolAnalysisContext context)
{
// SymbolKind.NamedTypeで登録したので対応するINamedTypeSymbolオブジェクトが渡される
var namedTypeSymbol = (INamedTypeSymbol)context.Symbol;
if (namedTypeSymbol.Name.Contains("_"))
{
var diagnostic = Diagnostic.Create(Rule, namedTypeSymbol.Locations[0], namedTypeSymbol.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
|
どのようなActionに対してどのRegisterXXXAction
を使うかについては、いくつか具体例を次回以降で紹介していく予定である。
CodeFixProviderクラス
Analyzerとして動作するためには、DiagnosticsAnalyzer
クラスだけでもよい。その場合、記述しているコードに対して指定したSeverityで表示される。警告を表示するだけでなく、修正候補を提示したい場合は、CodeFixProvider
抽象クラスを実装したクラス(本稿のサンプルではRoslynDemoCodeFixProvider
クラス)を記述することになる。
このクラスも[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoCodeFixProvider)), Shared]
という属性を付けることでCodeFixProvider
であることを表す(リスト5)。なお、Severityが「警告(Warning)」もしくは「エラー(Error)」でないと修正候補は表示されない。
……省略……
namespace RoslynDemo
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoCodeFixProvider)), Shared]
public class RoslynDemoCodeFixProvider : CodeFixProvider
{
……省略……
}
}
|
このクラスの継承元であるCodeFixProvider
は抽象クラスであり、そのメンバーは下記の通りである。
using System.Collections.Immutable;
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.CodeFixes
{
public abstract class CodeFixProvider
{
public abstract ImmutableArray<string> FixableDiagnosticIds { get; }
public abstract Task RegisterCodeFixesAsync(CodeFixContext context);
public virtual FixAllProvider GetFixAllProvider()
{
return null;
}
……省略……
}
}
|
これも順に見ていこう。
FixableDiagnosticIdsプロパティ
このCodeFixが修正する対象のDiagnostics IDを指定する。IDはDiagnosticsAnalyzer
クラスのDiagnosticDescriptor
に指定したIDと同じ値となるので、実際にはそのIDを参照することになるだろう。
……省略……
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoCodeFixProvider)), Shared]
public class RoslynDemoCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(RoslynDemoAnalyzer.DiagnosticId);
……省略……
}
|
GetFixAllProviderメソッド
GetFixAllProvider
メソッドはFixAllProvider
クラスのインスタンスを返すが、これはCode Fixを表示した箇所と同様の箇所がある場合に、それらもまとめて修正する機能である。継承元のクラスのGetFixAllProvider
メソッドはnullを返す実装になっているが、nullを返すと実行時にエラーが発生するため、適切なインスタンスを返す必要がある。多くの場合はあらかじめ用意されている、WellKnownFixAllProviders.BatchFixer
プロパティ(BatchFixAllProvider型。Microsoft.CodeAnalysis.CodeFixes名前空間)を返すことになるため、実装としてはリスト8のようになるだろう。
……省略……
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoCodeFixProvider)), Shared]
public class RoslynDemoCodeFixProvider : CodeFixProvider
{
……省略……
public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
……省略……
}
|
この実装を行った場合、Code Fix Actionの表示は図6のようになる。これはAnalyzerで対象となるクラス名の箇所でマウスオーバーして表示されるランプアイコンをクリックすると表示される。
複雑なFixAll機能を実装したい場合は、FixAllProvider
クラスを継承して作成することになる。この場合の詳細については、GitHubのドキュメント(英語)に詳細があるので参考にしてほしい。
RegisterCodeFixesAsyncメソッド
3つ目がRegisterCodeFixesAsync
メソッドだ。これはコンパイル結果に変更があるたびに呼ばれるメソッドであり、Code Fix Actionを登録する処理を実装する。まず実装を見てみよう。
……省略……
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoCodeFixProvider)), Shared]
public class RoslynDemoCodeFixProvider : CodeFixProvider
{
……省略……
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
// SyntaxRootの取得
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// 対応するDiagnosticとそのドキュメント上での位置を取得
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Code Fix Actionのために必要な情報(ここでは「型宣言のSyntax」)を取得。
// どのSyntaxが必要かはActionの実装に必要な情報によって変わる ……1
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<TypeDeclarationSyntax>().First();
// Actionの登録
context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedSolution: c => RemoveUnderscoreAsync(context.Document, declaration, c),
equivalenceKey: title),
diagnostic); //……2
}
private async Task<Solution> RemoveUnderscoreAsync(Document document, TypeDeclarationSyntax typeDecl, CancellationToken cancellationToken)
{
// 型宣言のSyntaxから型名を取得し、修正候補となる名前を生成
var identifierToken = typeDecl.Identifier;
var newName = identifierToken.Text.Replace("_", "");
// SemanticModelを取得し、修正対象となるSymbolを取得
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var typeSymbol = semanticModel.GetDeclaredSymbol(typeDecl, cancellationToken);
// Symbolを書き換えて修正後のSolutionを生成する。
// Renameの場合はRenamerクラスが書き換えるメソッドを提供している
var originalSolution = document.Project.Solution;
var optionSet = originalSolution.Workspace.Options;
var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, typeSymbol, newName, optionSet, cancellationToken).ConfigureAwait(false);
return newSolution;
}
}
|
Code Fix Actionは、RegisterCodeFixesAsync
メソッドの引数で渡されるCodeFixContext
(Microsoft.CodeAnalysis.CodeFixes
名前空間)のRegisterCodeFix
メソッドで登録する(2)。このとき、CodeAction
クラス(Microsoft.CodeAnalysis.CodeActions
名前空間)のCreate
メソッドにより作成されたActionとともに、そのActionに対応するDiagnosticsをメソッドに渡している。
実際のCodeFixで行う処理は、(CodeAction.Create
メソッドで引数として渡している)RemoveUnderscoreAsync
メソッドで実装している。このメソッドは、書き換えた後のSolution
インスタンス(Microsoft.CodeAnalysis
名前空間)を返せばよいが、その書き換えに必要な情報として、Document
(Microsoft.CodeAnalysis
名前空間)とSyntaxが多くの場合で必要になる(※本稿のサンプルでも、RemoveUnderscoreAsync
メソッドに引数として渡されたDocument
とSyntaxを、メソッド内で使用しているのが分かるだろう)。Actionの実装により「どのSyntaxが必要か」も変わるため、1の部分とRemoveUnderscoreAsync
メソッドの部分が実装ごとに変わることになるだろう。
Code Refactoringプロジェクト
さて、VSIXとして提供するCode Refactoringのテンプレートについても説明しよう。Visual Studioの新規ソリューションの作成で、[Code Refactoring (VSIX)]テンプレートを選ぶ(図7)。名前は「RoslynDemoRefactoring」とした。
こうして作成されたプロジェクトの内容が図8でなる。
各プロジェクトの内容は以下の通りだ。
- RoslynDemoRefactoring (入力したプロジェクト名): ロジックを記述するためのプロジェクト
- RoslynDemoRefactoring.Vsix (入力したプロジェクト名.Vsix): VSIXにパッケージングするためのプロジェクト
RoslynDemoRefactoringプロジェクトにはCodeRefactoringProvider.csというファイルが含まれており、そこにはRoslynDemoRefactoringCodeRefactoringProvider
クラスが実装されている。このクラスも(前述のDiagnosticAnalyzer
クラスの場合と同様に)[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoRefactoringCodeRefactoringProvider)), Shared]
という属性の指定により、CodeRefactoringProviderであることを表している(リスト10)。
……省略……
namespace RoslynDemoRefactoring
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(RoslynDemoRefactoringCodeRefactoringProvider)), Shared]
internal class RoslynDemoRefactoringCodeRefactoringProvider : CodeRefactoringProvider
{
……省略……
}
}
|
このクラスはCodeRefactoringProvider
という抽象クラス(Microsoft.CodeAnalysis.CodeRefactorings
名前空間)を継承している。このクラスの抽象メンバーを見てみよう。
using System.Threading.Tasks;
namespace Microsoft.CodeAnalysis.CodeRefactorings
{
public abstract class CodeRefactoringProvider
{
public abstract Task ComputeRefactoringsAsync(CodeRefactoringContext context);
}
}
|
継承先のRoslynDemoRefactoringCodeRefactoringProvider
クラスでは、このメンバーを実装することになる。その内容を見ていこう。
ComputeRefactoringsAsyncメソッド
ComputeRefactoringsAsync
メソッドで実装する内容は、CodeFixProviderのRegisterCodeFixesAsync
メソッドの内容とよく似ている。具体的には、ここではRefactoring Actionを登録する処理を実装する。そのサンプルとなる実装をリスト12に掲載する。
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// 選択箇所のSyntaxNodeを取得する
var node = root.FindNode(context.Span);
// このRefactoringは選択箇所が「型の名前」のときだけ利用できる
var typeDecl = node as TypeDeclarationSyntax;
if (typeDecl == null)
{
return;
}
// 任意の型の名前に対し、Myを頭に付けた名前に変更するActionを生成する
var action = CodeAction.Create("Add 'My' prefix", c => AddMyPrefixToTypeNameAsync(context.Document, typeDecl, c));
// Actionを登録する
context.RegisterRefactoring(action);
}
private async Task<Solution> AddMyPrefixToTypeNameAsync(Document document, TypeDeclarationSyntax typeDecl, CancellationToken cancellationToken)
{
// 変更後の「型の名前」を生成する
var identifierToken = typeDecl.Identifier;
var newName = "My" + identifierToken.Text;
// 変更対象となる型の名前に対応するSymbolを取得する
var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var typeSymbol = semanticModel.GetDeclaredSymbol(typeDecl, cancellationToken);
// Renamerを利用して、名前を変更した後のSolutionを生成する
var originalSolution = document.Project.Solution;
var optionSet = originalSolution.Workspace.Options;
var newSolution = await Renamer.RenameSymbolAsync(document.Project.Solution, typeSymbol, newName, optionSet, cancellationToken).ConfigureAwait(false);
return newSolution;
}
|
Refactoring Actionでは、Code Fix Actionの場合と違ってDiagnosticsが不要なだけで、処理の流れはほぼ同じだ。具体的には、ComputeRefactoringsAsync
メソッドの引数で渡されたCodeRefactoringContext
(Microsoft.CodeAnalysis.CodeRefactorings
名前空間)からDocument
とSyntaxの情報を取り出し、AddMyPrefixToTypeNameAsync
メソッドにRefactoring処理を記述したうえで、それらをCodeAction.Create
メソッドの引数として渡して生成したActionをRegisterRefactoring
メソッドで登録する。
■
次回は、Analyzer with Code Fixプロジェクトを利用し、いくつかAnalyzerとCode Fix Actionの具体例を取り上げて実装の仕方を説明していく予定である。
1. .NETコンパイラープラットフォーム「Roslyn」の概要とコンパイラー拡張
C# 6.0と同時にリリースされた.NETコンパイラープラットフォーム「Roslyn」。そのコンパイラー拡張の作り方を解説する連載の第1回。
2. 【現在、表示中】≫ .NETコンパイラープラットフォーム拡張の作り方
C# 6.0と同時にリリースされた.NETコンパイラープラットフォーム「Roslyn」。そのコンパイラー拡張の作り方を解説する連載の第2回。
3. Analyzerの作り方と、各メソッドの使い方
.NETコンパイラープラットフォーム「Roslyn」でコンパイラー拡張を作ってみよう。Analyzer with Code FixプロジェクトでAnalyzerを実装するために必要な各メソッドの使い方と、Analyzerの作り方を説明する。
4. Code Fix Actionの作り方
.NETコンパイラープラットフォーム「Roslyn」でコンパイラー拡張を作ってみよう。CodeFixProviderの実装方法を説明し、code-crackerのソースコードから引用する形で基本的なコード修正候補の作成例を示す。
5. 外部ファイルの読み込みとローカライズ
Roslynのコンパイラー拡張で外部ファイルを読み込んで活用する方法と、AnalyzerやCode Fix Actionのメッセージをローカライズする方法について説明する。連載最終回。