C#

Roslyn アナライザーへのコード修正の追加

Alex Turner

前回の「Roslyn を使用した API 向けライブ コード アナライザーの作成」(msdn.microsoft.com/magazine/dn879356、英語) の手順では、無効な正規表現 (regex) パターン文字列エラーをリアルタイムに表示するアナライザーを作成しました。それぞれの無効なパターンには、コンパイラ エラーと同じようにエディター上で赤い波線が表示されます。この波線はコードの入力中にリアルタイム表示されます。この機能は、新しい .NET コンパイラ プラットフォーム ("Roslyn") API によって実現しています。Roslyn は、Visual Studio 2015 Preview の C# および Visual Basic エクスペリエンスの原動力となるものです。

これをさらに洗練できるでしょうか。何が悪いかだけでなくその修正方法もわかる専門知識があれば、新しい Visual Studio の電球メニューを利用して関連するコード修正を提案できます。このコード修正によって、開発者はアナライザーを使用してコードのエラーを見つけるだけでなく、即座にエラーを解決できるようになります。

今回は、各正規表現の波線に対する修正を提供するコード修正プロバイダーを正規表現アナライザーに追加する方法を説明します。修正は電球メニューの項目として追加されます。ユーザーは修正をプレビューしてコードに自動適用できます。

前回のおさらい

今回の説明を始めるにあたり、前回の手順をおさらいしておいてください。前回は、アナライザーの前半 (それぞれの無効な正規表現パターン文字列の下に診断波線を生成する) 部分の作成方法を示しました。説明した作業は次のとおりです。

  • Visual Studio 2015 Preview、Visual Studio 2015 Preview SDK、および関連 Roslyn VSIX パッケージをインストールする
  • 新しい "Diagnostic with Code Fix" (診断とコード修正) プロジェクトを作成する
  • DiagnosticAnalyzer.cs にコードを追加して、無効な正規表現パターンの検出を実装する

簡単におさらいするには、図 1 に示す DiagnosticAnalyzer.cs の最終形のコードを参照してください。

図 1 DiagnosticAnalyzer.cs の完全なコード

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace RegexAnalyzer
{
  [DiagnosticAnalyzer(LanguageNames.CSharp)]
  public class RegexAnalyzerAnalyzer : DiagnosticAnalyzer
  {
    public const string DiagnosticId = "Regex";
    internal const string Title = "Regex error parsing string argument";
    internal const string MessageFormat = "Regex error {0}";
    internal const string Category = "Syntax";
    internal static DiagnosticDescriptor Rule =
      new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat,
      Category, DiagnosticSeverity.Error, isEnabledByDefault: true);
    public override ImmutableArray<DiagnosticDescriptor>
      SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
    public override void Initialize(AnalysisContext context)
    {
      context.RegisterSyntaxNodeAction(
        AnalyzeNode, SyntaxKind.InvocationExpression);
    }
    private void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
      var invocationExpr = (InvocationExpressionSyntax)context.Node;
      var memberAccessExpr =
        invocationExpr.Expression as MemberAccessExpressionSyntax;
      if (memberAccessExpr?.Name.ToString() != "Match") return;
      var memberSymbol = context.SemanticModel.
        GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
      if (!memberSymbol?.ToString().StartsWith(
        "System.Text.RegularExpressions.Regex.Match") ?? true) return;
      var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
      if ((argumentList?.Arguments.Count ?? 0) < 2) return;
      var regexLiteral =
        argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
      if (regexLiteral == null) return;
      var regexOpt = context.SemanticModel.GetConstantValue(regexLiteral);
      if (!regexOpt.HasValue) return;
      var regex = regexOpt.Value as string;
      if (regex == null) return;
      try
      {
        System.Text.RegularExpressions.Regex.Match("", regex);
      }
      catch (ArgumentException e)
      {
        var diagnostic =
          Diagnostic.Create(Rule, regexLiteral.GetLocation(), e.Message);
        context.ReportDiagnostic(diagnostic);
      }
    }
  }
}

不変構文ツリーの変換

前回、無効な正規表現パターンを検出する診断アナライザーを作成した際は、最初の手順として、問題のコードが示された構文ツリーのパターンを Syntax Visualizer を使って特定しました。その後、関連するノードの種類が見つかるたびに実行する分析メソッドを作成しました。このメソッドは、エラー波線の正当な理由になる構文ノードのパターンをチェックするものでした。

修正の作成でも、同じようなプロセスを踏みます。修正適用後のコード ファイルとして求める新しい状態に重点を置きながら、構文ツリーを処理します。多くのコード修正では、現在のツリーへの構文ノードの追加、ツリーからの削除、ツリーの構文ノードとの置換などを行い、新しい構文ツリーを生成します。構文ノードを直接操作することも、プロジェクト全体の変更 (名前の変更など) を行える API を使用することもできます。

.NET コンパイラ プラットフォームの構文ノード、ツリー、およびシンボルについて理解しておくべき非常に重要な特性は、これらが不変だということです。構文ノードや構文ツリーは作成したら変更できません。特定のツリーまたはノード オブジェクトは、常に同じ C# コードまたは Visual Basic コードを表します。

ソース コードを変換する API の不変性は、直感的には受け入れられないように思えます。構文ツリーもノードも変更できないとすれば、構文の子ノードを追加、削除、および置換するにはどうすればよいでしょう。ここで、最もよく使用すると思われるもう 1 つの不変な型である .NET の String 型について考えると参考になります。連結や、String.Replace を使用した部分文字列の置換など、文字列の変換操作は非常によく行われます。しかし、それらの操作の中に、元の文字列オブジェクトを実際に変更するものはありません。代わりに、各呼び出しでは、文字列の新しい状態を示す新しい文字列オブジェクトが返されます。この新しいオブジェクトは元の変数に割り当てて戻すことができますが、古い文字列を渡したメソッドは元の値を保持し続けます。

不変ツリーへのパラメーター ノードの追加: 構文ツリーの不変性がどのようなものかを調べるために、コード エディターで簡単な変換を手動で行い、構文ツリーへの影響を確認しましょう。

Visual Studio 2015 Preview (Syntax Visualizer 拡張機能がインストール済みのもの。インストール方法は前回の記事参照) で、新しい C# コード ファイルを作成します。C# コード ファイルの内容すべてを、次のコードに置き換えます。

class C
{
  void M()
  }
}

[表示]、[その他のウィンドウ]、[Roslyn Syntax Visualizer] の順にクリックして Syntax Visualizer を開き、ツリーを設定するためにコード ファイルの任意の場所をクリックします。[Roslyn Syntax Visualizer] ウィンドウで、ルート ノード [CompilationUnit] を右クリックし、[View Directed Syntax Graph] (有向構文グラフの表示) をクリックします。この構文ツリーを視覚化すると、図 2 のようなグラフになります (ここで示すグラフでは、空白やコメントを示す灰色と白の付加情報ノードを省略しています)。青の ParameterList 構文ノードには、かっこを表す 2 つの緑の子トークンがあります。また、ParameterList 構文ノードにはパラメーターがないので青の子構文ノードはありません。

変換前の構文ツリー
図 2 変換前の構文ツリー

ここでシミュレーションを行う変換は、int 型の新しいパラメーターを追加するものです。メソッド M のパラメーター リストのかっこ内に「int i」というコードを入力します。このコードを入力しながら、Syntax Visualizer の変化を観察します。

class C
{
  void M(int i)
  {
  }
}

入力終了前に、不完全なコードにコンパイル エラー (Syntax Visualizer に赤い背景のノードとして表示される) が含まれる場合でも、ツリーは依然、一貫していることに注目してください。コンパイラでは、新しいコードが有効な Parameter ノードを形成するという推測が行われます。構文ツリーのコンパイル エラーに対するこの回復力によって、不完全なコードに対して IDE の機能や診断が適切に機能します。

再度ルート ノード [CompilationUnit] を右クリックして新しいグラフを生成すると、図 3 のようになります (ここでも付加情報ノードを省略しています)。

変換後の構文ツリー
図 3 変換後の構文ツリー

今度は、ParameterList に 3 つの子トークンがあります。2 つのかっこのトークンは以前と同じですが、新しく Parameter 構文ノードが加わっています。エディターで「int i」と入力すると、Visual Studio では、新しいソース コードを表す新しい構文ツリーによって、ドキュメントの以前の構文ツリーが置き換えられます。

完全な置き換えは、1 つのオブジェクトである短い文字列に対して適切に機能します。では、構文ツリーに対してはどうでしょう。大きなコード ファイルには数千~数万もの構文ノードが含まれている場合があります。ファイル内に文字が入力されるたびにそれらのノードをすべて再作成するのは間違いなく望ましくありません。再生成すると、大量の孤立したオブジェクトが生成され、ガベージ コレクターによるクリーンアップでパフォーマンスに深刻な悪影響が生じます。

さいわい、構文ノードの不変性が、この回避策にもなります。ドキュメント内のノードの大半は、少しの変更では影響を受けないので、それらのノードを新しいツリーの子ノードとして再利用しても安全です。特定の構文ノードのデータを格納する内部ノードは、そのノードの下方の子ノードしか参照しません。これらの内部ノードには親ポインターがないため、コードのその部分が変更されない限り、同じ内部ノードを特定の構文ツリーの多くのイテレーション内で何度繰り返しても安全です。

このようにノードを再利用すると、各キー操作で再作成が必要となるツリーのノードは、変更された子孫が少なくとも 1 つあるノード (つまり、ルートまで先祖ノードをたどる限られたチェーン) のみです (図 4 参照)。他のノードはすべてそのまま再利用します。

変換の過程で置き換えられた先祖ノード
図 4 変換の過程で置き換えられた先祖ノード

ここで中心となる変更は、新しい Parameter ノードを作成し、子ノードとしてその新しい Parameter が挿入された新しい ParameterList に既存の ParameterList を置き換えることです。ParameterList を置き換えると、置き換えたノードを含むように先祖の子ノード リストを変更することになるため、一連の先祖ノードの置き換えも必要になります。コラムの後半では、SyntaxNode.ReplaceNode メソッドを使用して正規表現アナライザーに対してこのような置き換えを行います。このメソッドを使用すると、先祖ノードがすべて置き換えられます。

これで、コード修正を計画するためのおおまかなパターンがわかりました。そこで、診断のトリガーとなる前の状態のコードから作業を開始します。その後、修正で行う変更を、構文ツリーへの影響を観察しながら手動で加えます。最後に、置換ノードの作成とそのコードを含む新しい構文ツリーを返すために必要なコードを考えます。

前回作成した診断を含むプロジェクトを開いておきます。コード修正を実装するために、CodeFixProvider.cs について詳しく調べます。

GetFixableDiagnosticIds メソッド

修正と、修正で解決する診断は、診断 ID によって疎結合されています。各コード修正は、1 つ以上の診断 ID を対象にします。Visual Studio では、一致する ID が設定された診断を見つけると、提示するコード修正があるかどうかをコード修正プロバイダーに照会します。ID 文字列に基づいて疎結合されているため、アナライザーでは、別の開発者のアナライザーによって生成された診断に対して修正を提供することも、組み込みコンパイラのエラーや警告に対して修正を提供することもできます。

今回の場合、アナライザーから診断とコード修正の両方が提供されます。GetFixableDiagnosticIds メソッドでは Diagnostic 型で定義した診断 ID が返されることがわかっているので、ここで加える変更はありません。

ComputeFixesAsync メソッド

ComputeFixesAsync メソッドが、コード修正を主導します。このメソッドは、特定のコード範囲に一致する診断が 1 つ以上見つかると必ず呼び出されます。

テンプレートの ComputeFixesAsync メソッドの既定の実装により、コンテキストから最初の診断 (多くの場合、1 つだけ) が取り出され、診断の範囲が取得されます。次の行では、その範囲の構文ツリーを検索して、最も近い型宣言ノードを探します。既定のテンプレートのルールを使用している場合、そのノードが内容の修正を必要とする関連ノードです。

前回の例の場合、作成した診断アナライザーでは呼び出しを探して Regex.Match への呼び出しかどうかを確認していました。診断とコード修正の間でロジックの共有を容易にするには、ツリー検索の OfType フィルターで指定されている型を変更して、同じ InvocationExpressionSyntax ノードを探します。また、ローカル変数の名前を "invocationExpr" に変更します。

var invocationExpr = root.FindToken(
  diagnosticSpan.Start).Parent.AncestorsAndSelf()
  .OfType<InvocationExpressionSyntax>().First();

これで、診断アナライザーを開始するのと同じ呼び出しノードへの参照が設定されるようになります。次のステートメントでは、修正として提案するコード変更を検討するメソッドに、このノードを渡します。このメソッドの名前を MakeUppercaseAsync から FixRegexAsync に変更し、Fix regex の修正の説明を変更します。

context.RegisterFix(
  CodeAction.Create("Fix regex", c => FixRegexAsync(
  context.Document, invocationExpr, c)), diagnostic);

コンテキストの RegisterFix メソッドへの各呼び出しによって、新しいコード アクションと問題になっている診断波線が関連付けられます。また、電球メニュー内のメニュー項目が生成されます。コード変換を実行する FixRegexAsync メソッドをまだ実際に呼び出していないことに注意してください。代わりに、このメソッド呼び出しを、後から Visual Studio で呼び出すことのできるラムダ式にラップしています。このようにしているのは、変換結果を必要とするのは、ユーザーが実際に Fix regex 項目を選択した場合のみのためです。Visual Studio では、修正項目が強調表示されるか選択されている場合に、アクションを起動してプレビューを生成するか、修正を適用します。この状態になるまでは、ソリューション全体の名前変更などの負荷の高い操作に備えるため、Visual Studio では修正メソッドが実行されません。

コード修正プロバイダーは、特定の診断のすべてのインスタンスに対してコード修正を生成しなくてもかまいません。アナライザーによって波線を表示できる例の一部に対してしか、提案する修正を用意できないこともよくあります。一部に対する修正しか用意していない場合は、最初に、特定の状況を修正できるかどうかを判断するために必要な条件を ComputeFixesAsync でテストします。これらの条件が満たされない場合、RegisterFix を呼び出すことなく ComputeFixesAsync から戻る必要があります。

今回の例では、診断のすべてのインスタンスに対して修正を提供するため、確認する条件はありません。

FixRegexAsync メソッド

このメソッドがコード修正の中心です。現在の FixRegexAsync メソッドは、Document を受け取り、更新した Solution を生成します。診断アナライザーは特定のノードとシンボルを探しながら、コード修正を使用してソリューション全体のコードを変更できます。このテンプレート コードでは Renamer.RenameSymbolAsync を呼び出していることがわかります。これにより、シンボルの型宣言だけでなく、ソリューション内のそのシンボルに対するすべての参照も変更されます。

今回の場合、現在のドキュメントのパターン文字列にローカルの変更を加えるだけで、メソッドの戻り値の型を Task<Solution> から Task<Document> に変更できます。CodeAction.Create には Solution ではなく Document を受け取るもう 1 つのオーバーロードがあるため、このシグネチャは依然、ComputeFixesAsync のラムダ式と互換性があります。また、ComputeFixesAsync メソッドから渡す InvocationExpressionSyntax ノードに合うように typeDecl パラメーターを更新することも必要です。

private async Task<Document> FixRegexAsync(Document document,
  InvocationExpressionSyntax invocationExpr, CancellationToken cancellationToken)

「大文字にする」ロジックは必要ないため、メソッド本体の削除も行います。

置換するノードの検索: コード修正プロバイダーの前半は、InvocationExpression を詳しく調査して修正に通知するメソッド呼び出しに関連する部分を探すもので、診断アナライザーの前半部分と非常に似ています。実際、AnalyzeNode メソッドの try-catch ブロックまでの前半部分をコピーするだけです。ただし、invocationExpr を既にパラメーターとして受け取っているので、最初の行は省略します。これは診断を見つけることができた場合に処理されるコードなので、"if" チェックをすべて削除しても問題ありません。セマンティック モデルを直接提供するコンテキストがなくなったので、あとは Document 引数からセマンティック モデルを取得するように変更するだけです。

これらの変更を加えた後の FixRegexAsync メソッド本体は次のようになります。

var semanticModel = await document.GetSemanticModelAsync(cancellationToken);
var memberAccessExpr =
  invocationExpr.Expression as MemberAccessExpressionSyntax;
var memberSymbol =
  semanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
var regexLiteral =
  argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
var regexOpt = semanticModel.GetConstantValue(regexLiteral);
var regex = regexOpt.Value as string;

置換ノードの生成: 古い文字列リテラルを表す regexLiteral を再び用意できたので、新しい文字列リテラルを生成する必要があります。任意の正規表現パターンの修正に必要な文字列を正確に案出するのは大掛かりな作業で、ここで扱う範囲を大きく超えてしまいます。そこで今回は、文字列 "valid regex" (実際には有効な正規表現パターン文字列) を使用します。この案出作業に自分で取り組む場合は、非常に具体的で小さな正規表現問題を対象とするところから始めることをお勧めします。

置換する新しい構文ノードをツリーに生成する低レベルの方法としては、SyntaxFactory のメンバーを使用します。これらのメソッドを使用すると、選択したのとまったく同じ形状ですべての種類の構文ノードを作成できます。しかし、多くの場合、テキストから必要な式の解析だけを行い、ノードを作成する面倒な作業はすべてコンパイラに任せる方が簡単です。コード スニペットを解析するには、文字列リテラルのコードを指定して SyntaxFactory.ParseExpression を呼び出すだけです。

var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"");

この新しいリテラルは、ほとんどの場合、置換として適切に機能しますが、足りないものがあります。構文トークンには、空白やコメントを表す付加情報がある場合があることを思い出してください。古いコードの空白やコメントが削除されないように、古い定数式の付加情報をコピーする必要があります。作成する新しいノードに "Formatter" 注釈のタグを付けることもお勧めします。注釈によって、エンド ユーザーのコード スタイル設定に従って新しいノードを書式設定するよう、コード修正エンジンに通知されます。また、Microsoft.CodeAnalysis.Formatting 名前空間の using ディレクティブを追加する必要があります。これらを追加した後の ParseExpression 呼び出しは次のようになります。

var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"")
  .WithLeadingTrivia(regexLiteral.GetLeadingTrivia())
  .WithTrailingTrivia(regexLiteral.GetTrailingTrivia())
  .WithAdditionalAnnotations(Formatter.Annotation);

構文ツリーでの新しいノードの置換: 文字列リテラルの新しい構文ノードを作成したので、構文ツリー内の古いノードと置換して、修正した正規表現パターン文字列を設定した新しいツリーを生成します。

最初に、現在のドキュメントの構文ツリーからルート ノードを取得します。

var root = await document.GetSyntaxRootAsync();

次に、この構文ルートで ReplaceNode メソッドを呼び出して、古い構文ノードを新しい構文ノードに置き換えます。

var newRoot = root.ReplaceNode(regexLiteral, newLiteral);

ここでは新しいルート ノードを生成しています。構文ノードを置換する場合は、ルートまでのすべての親を置き換えることも必要です。前述のように、.NET コンパイラ プラットフォームの構文ノードはすべて不変です。この置換操作は、実際には指示されたように対象ノードとその祖先を置換した新しいルート構文ノードを返しているだけです。

修正した文字列リテラルを含む新しい構文ルートができたので、ツリーをもう 1 段階上がって、更新されたルートを含む新しい Document オブジェクトを生成します。ルートを置換するには、Document の WithSyntaxRoot メソッドを使用します。

var newDocument = document.WithSyntaxRoot(newRoot);

これは、解析した式に対して WithLeadingTrivia などのメソッドを呼び出すときに見たのと同じ With API パターンです。この With パターンは、Roslyn 不変オブジェクト モデルで既存のオブジェクトを変換するときによく見られます。考え方としては、新しい文字列オブジェクトを返す .NET String.Replace メソッドに似ています。

変換するドキュメントを用意したら、FixRegexAsync から次のように返します。

return newDocument;

CodeFixProvider.cs のコードは、図 5 のようになります。

図 5 CodeFixProvider.cs の完全なコード

using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
namespace RegexAnalyzer
{
  [ExportCodeFixProvider("RegexAnalyzerCodeFixProvider",
    LanguageNames.CSharp), Shared]
  public class RegexAnalyzerCodeFixProvider : CodeFixProvider
  {
    public sealed override ImmutableArray<string> GetFixableDiagnosticIds()
    {
      return ImmutableArray.Create(RegexAnalyzer.DiagnosticId);
    }
    public sealed override FixAllProvider GetFixAllProvider()
    {
      return WellKnownFixAllProviders.BatchFixer;
    }
    public sealed override async Task ComputeFixesAsync(CodeFixContext context)
    {
      var root =
        await context.Document.GetSyntaxRootAsync(context.CancellationToken)
        .ConfigureAwait(false);
      var diagnostic = context.Diagnostics.First();
      var diagnosticSpan = diagnostic.Location.SourceSpan;
      // Find the invocation expression identified by the diagnostic.
      var invocationExpr =   
        root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf()
        .OfType<InvocationExpressionSyntax>().First();
      // Register a code action that will invoke the fix.
      context.RegisterFix(
        CodeAction.Create("Fix regex", c =>
        FixRegexAsync(context.Document, invocationExpr, c)), diagnostic);
    }
    private async Task<Document> FixRegexAsync(Document document,
      InvocationExpressionSyntax invocationExpr,
      CancellationToken cancellationToken)
    {
      var semanticModel =
        await document.GetSemanticModelAsync(cancellationToken);
      var memberAccessExpr =
        invocationExpr.Expression as MemberAccessExpressionSyntax;
      var memberSymbol =
        semanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol;
      var argumentList = invocationExpr.ArgumentList as ArgumentListSyntax;
      var regexLiteral =
        argumentList.Arguments[1].Expression as LiteralExpressionSyntax;
      var regexOpt = semanticModel.GetConstantValue(regexLiteral);
      var regex = regexOpt.Value as string;
      var newLiteral = SyntaxFactory.ParseExpression("\"valid regex\"")
        .WithLeadingTrivia(regexLiteral.GetLeadingTrivia())
        .WithTrailingTrivia(regexLiteral.GetTrailingTrivia())
        .WithAdditionalAnnotations(Formatter.Annotation);
      var root = await document.GetSyntaxRootAsync();
      var newRoot = root.ReplaceNode(regexLiteral, newLiteral);
      var newDocument = document.WithSyntaxRoot(newRoot);
      return newDocument;
    }
  }
}

試してみる: これで作業は完了です。これで、診断が表示されユーザーが電球メニューから修正を選択したときに変換を実行するコード修正を定義できました。コード修正を試すには、Visual Studio のメイン インスタンスでもう一度 F5 キーを押し、コンソール アプリケーションを開きます。今度は、波線にカーソルを重ねると、左側に電球が表示されます。電球をクリックすると、定義した Fix regex コード アクションを含むメニューが表示されます (図 6 参照)。このメニューには、古い Document と新しく作成した Document の間のインライン差分付きのプレビューが表示され、修正を適用した場合のコードの状態が示されます。

コード修正を試す
図 6 コード修正を試す

メニュー項目を選択すると、Visual Studio では新しい Document を取得し、そのソース ファイルのエディター バッファーの現在状態として採用します。これで修正が適用されました。

おつかれさまでした

これで完了です。合計約 70 行の新しいコードで、ユーザーのコード内の問題をユーザーの入力中にリアルタイムに特定してエラーとして赤い波線で示し、エラーを解決するコード修正を表示しました。また、なじみのある対象領域である正規表現内で処理しながら、構文ツリーを変換し、その過程で新しい構文ノードを生成しました。

作成する診断とコード修正は継続的に調整できますが、.NET コンパイラ プラットフォームを使用して構築したアナライザーは、短い時間で非常に多くの処理が可能です。アナライザーの構築に慣れたら、日常的なコーディング生活でよく遭遇するあらゆる種類の問題に狙いを定めて、自動化できる繰り返しの修正の発見に着手します。

何を分析するかは皆さん次第です。


Alex Turner は、マイクロソフトのマネージ言語チームでシニア プログラム マネージャーを務め、.NET コンパイラ プラットフォーム ("Roslyn") プロジェクトで C# と Visual Basic の強みを考えています。ストーニー ブルック大学を卒業し、コンピューター サイエンスの理学修士号を取得しています。また、Build、PDC、TechEd、TechDays、および MIX. での講演経験があります。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Bill Chiles および Lucian Wischik に心より感謝いたします。
Lucian Wischik は、マイクロソフトの VB/C# 言語設計チームに所属し、特に VB を担当しています。マイクロソフトに入社する以前は、大学で同時実行の理論と非同期について学んでいました。熱心なセーリング愛好家で、長距離の水泳選手でもあります。

Bill Chiles は、言語 (CMU Common Lisp、Dylan、IronPython、および C#) と開発者ツールにそのキャリアの大半を費やしてきました。マイクロソフトの開発部門で過ごした最後の 17 年間は、Visual Studio の中核機能から動的言語ランタイムや C# まであらゆるものに携わりました。