Roslynで作るC#コンパイラー拡張(1)
.NETコンパイラープラットフォーム「Roslyn」の概要とコンパイラー拡張
C# 6.0と同時にリリースされた.NETコンパイラープラットフォーム「Roslyn」。そのコンパイラー拡張の作り方を解説する連載の第1回。
Roslynとは
2014年の//BuildカンファレンスでAnders Hejlsberg(アンダース・ヘルスバーグ)氏が.NETコンパイラープラットフォーム、別名“Roslyn”(=元はコード名だったが現在では通称となっている)をその場でオープンソース化したことを覚えている人も多いだろう。その後、GitHubに公開する場所を変え、C# 6.0と同時にRoslynはリリースされた。
では、そのRoslynは何のために使われる、どのようなものなのだろうか。GitHubの説明を引用すれば、
豊富なコード解析APIを持ったオープンソースなC#とVB(Visual Basic)のコンパイラー
である。
この連載では「豊富なコード解析API」に焦点を当て、コンパイラープラットフォーム拡張を使ってコード解析ツールを自作する方法を説明する。連載1回目のこの記事では、ツールの作り方に入る前にRoslynの概要を説明し、自作できるツールの概要とそのための準備について紹介したい。
Roslyn登場の背景
最初に、公式の.NET Blog(英語)やMSDNマガジンの記事を参考に、Roslyn登場の背景を振り返ってみたい。
従来のC#/VBコンパイラーはブラックボックスであり、ソースコードを与えるとコンパイル結果のアセンブリが出力されるだけであった。Visual Studioなどが提供するIDE機能の中には、入力補完、リファクタリング、参照の検索といった「コンパイラーそのもの、あるいはそれに類する機能」が必要とされるものがある。IDEの側で、これらの機能を追加したりコンパイラーのアップデートに追随したりするケースを考えると、ブラックボックスなコンパイラーだけでは実現が難しく、正式なコンパイラーと同様の処理を推測し実装する必要があった。またこのような機能を持つツールはVisual Studioだけではなく、マイクロソフト以外のサードパーティの企業や、開発者個人が実装することもあり、機能をオープンにする要望が高まっていた。
そこでマイクロソフトは、一連のコンパイラー機能をコンパイラープラットフォームとして設計し直し、APIとして提供(≒サービスとして提供)することを決めた。その結果、Compiler platform as a Serviceとして、リリースするに至った。
Roslynの概要
Roslynは大きく分けて3つのAPIから構成されている。
RoslynのGitHubページのWiki(英語)より引用。
図の下から順に説明しよう。
- Compiler API: コンパイルの一連の過程(パイプライン)の各プロセスを、APIとしてアクセスできるようにしたもの。ユーザー定義の診断処理をプラグイン的に追加できるDiagnostics APIや、C#のコード片を実行するScripting APIも含まれる。
- Workspaces API: ソリューション、プロジェクト、ソースコードといった「コンパイル/コード解析などを行う単位」を扱うためのAPI。これらの単位は、Visual Studioで扱ってきたソリューションやプロジェクトなどと同じものであるが、APIとしてはVisual Studioに依存していない。
- Features API: リファクタリング、コード補完、ナビゲートといった「従来のツールが提供してきた機能単位」でのAPI。機能拡張という点では、RefactoringやCode FixesというRoslyn拡張機能プロジェクトとして作成する。プロジェクトやドキュメントを横断して処理を行うため、Workspaces APIを利用している。
このうちCompiler APIについて詳しく見てみよう。Roslynのコンパイラーは大きく4つのフェーズに分かれており、それぞれにAPIが用意されている。
図の左から順に説明しよう。
- Parser: Syntax Trees APIが対応。ソースコードを入力として、字句解析および構文木の解釈を行う。IDEでは、フォーマットや色付け、アウトライン表示などがこのAPIを利用している。
- Symbols、Metadata Import: Symbols API が対応。Parserが生成した構文木に加えて、参照している外部アセンブリからインポートしたメタデータから、シンボルを生成する。IDEでは、[Navigate to(移動)]や[オブジェクト ブラウザー]などが利用している。
- Binder: Binding and Flow Analysis APIが対応。生成されたシンボルに識別子をバインディングする。[名前の変更]や[メソッドの抽出]が利用している。
- IL Emitter: Emit APIが対応。ILとしてアセンブリを出力する。デバッグ時のEdit and Continue(エディットコンティニュー)機能が利用している。
この4つのフェーズを構成するAPIに加えて、関連するAPIとしてDiagnostics APIとScripting APIがCompiler APIには含まれている。
Diagnostics APIは、コンパイルエラーや警告をユーザーが追加できるように、APIとして提供するようにしたものだ。この連載で説明するRoslyn拡張がまさに利用するAPIである。
Scripting APIは、Roslyn本体のリリースからやや遅れて提供された。C#のコード片を評価し結果を返すAPIであり、REPL(Read-eval-print loop)やC#コードそのものを設定ファイルとして読み込む機能などに利用できる。
コンパイラープラットフォーム拡張
Diagnostic with Code FixとCode Refactoring
さて、この連載で扱うのは、独自の診断機能を提供し、コンパイラーから呼び出してもらって検知する拡張機能の開発である。Visual Studioの場合、起動している間中にバックグラウンドでコンパイラーが実行されているので、コードを変更時に何か問題があれば、即座に拡張機能も含めたコンパイラー警告がエディター上に表示される。この拡張機能は、Diagnostic with Code FixとCode Refactoringの大きく2つに分類できる。
両者の最大の違いは配布形式であり、Diagnostic with Code FixはNuGetライブラリもしくはVisual Studio拡張(Extensions)として配布でき、Code RefactoringについてはVisual Studio拡張のみである。
Diagnostic with Code Fixは、NuGetライブラリ経由でプロジェクトごとに参照を追加することで利用可能になるため、プロジェクトごとに利用するかどうかを決めることができる。すなわち、あるプロジェクトに固有のルール(特定のライブラリを利用するときのルールであったり、社内での開発ルールであったり)を配布するのに向いているだろう。
一方、Code Refactoringは、インストールしたVisual Studioを使うと全てのプロジェクトで利用可能になるため、汎用的なものが向いているだろう。また、同一のプロジェクトを開発している場合でも、人によってあるいは環境によってVisual Studioに入れているCode Refactoringが違う可能性も出てくる。
この連載では、両者について解説していくが、特にDiagnostic with Code Fixについて詳しく解説する予定である。
開発環境
コンパイラープラットフォーム拡張を開発するためには、「.NET Compiler Platform SDK」という名前で配布されているVisual Studio拡張を利用する。
Visual Studio 2015(※現在、Preview版の次期Visual Studio “15”でも利用可能だが、この連載ではVisual Studio 2015を利用する)および、Visual Studio SDKのインストールが事前に必要である。
インストールが完了して、プロジェクト作成時のプロジェクトテンプレートに[Analyzer with Code Fix (Nuget + VSIX)]と[Code Refactoring (VSIX)]が表示されていればよい(図3)。
サンプル
コンパイラー拡張は、コードを解析して修正対象を検知するのに加えて、適切な修正候補のコードを与える必要があるため、最初のうちはどのように実装すればよいか悩むことも多いだろう。その場合には、すでに実際に使われているコンパイラー拡張がGitHubなどに公開されているので、コードを手元においてデバッグ実行などで動作を確認し、コードを参考にするとよい。参考までに、下記に3つのリポジトリを挙げておこう。
- DotNetAnalyzers/StyleCopAnalyzers: An implementation of StyleCop rules using the .NET Compiler Platform - GitHub
- code-cracker/code-cracker: An analyzer library for C# and VB that uses Roslyn to produce refactorings, code analysis, and other niceties. - GitHub
- dotnet/roslyn-analyzers: .NET Compiler Platform ("Roslyn") Analyzers - GitHub
.NET Coreとの関係
さて、LinuxやMac(OS X)でも.NET Frameworkが実行できる.NET Coreが発表され、執筆時点でRC2がリリースされており、間もなく正式版がリリースされる見込みである。.NET CoreもコンパイラーはRoslynを利用している。しかし、コンパイラー拡張であるRoslyn SDKについては開発する環境に関して注意が必要だ。
コンパイラー拡張の仕組みそのものはVisual Studioに依存していないが、現在リリースされているRoslyn SDKは「開発環境」の項で説明した通りVisual Studio拡張として利用する形態であるため、Visual Studioが必須であり、Windows上でしか開発できない。また、Code Refactoringは配布形式がVisual Studio拡張であるため、これを利用するのもWindows上に限られる。
一方、Diagnostic with Code FixについてはNuGetライブラリという形式で配布すれば、LinuxやOS X上で開発する場合も利用可能である。ただし、Diagnostic with Code FixをVisual Studioで利用すると即座にエディター上に修正候補が表示されるが、LinuxやOS X上で利用可能なIDE/エディターで同じように表示されるかどうかはそのIDE/エディターが機能を提供しているかどうか次第である。例えばVisual Studio CodeはIDEとしての機能を提供しておらず、コマンドラインでコンパイルすると警告もしくはエラーとして表示されるにとどまっている。
.NET Coreに関しては情報に更新があれば、適宜連載中にお伝えする予定である。
■
連載第1回はコンパイラープラットフォーム「Roslyn」とその拡張の概要について説明した。次回は、まずDiagnostic with Code Fixプロジェクトを作成し、Roslyn SDKに触れてみる予定である。
1. 【現在、表示中】≫ .NETコンパイラープラットフォーム「Roslyn」の概要とコンパイラー拡張
C# 6.0と同時にリリースされた.NETコンパイラープラットフォーム「Roslyn」。そのコンパイラー拡張の作り方を解説する連載の第1回。
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のメッセージをローカライズする方法について説明する。連載最終回。