ぷろじぇくと、みすじら。

Source Generator の Visual Studio 2019 v16.9 での新 API

Created at:

Source Generator を触っていて気づいたのですが Visual Studio 2019 version 16.9 の段階でいくつか API が追加されていました。

追加されたものは次のようなあるといいよねといった API が増えている感じです。

Visual Studio 2019 であれば 16.9 以降、NuGet パッケージであれば Microsoft.CodeAnalysis.CSharp 3.9.0 を参照すれば使用できます。

GeneratorInitializationContext.RegisterForPostInitialization メソッド

GeneratorInitializationContext.RegisterForPostInitialization メソッドは Source Generator が読み込まれて初期化された後に呼び出されるコールバックを登録できます。

これは Source Generator で必要となる属性用のコードを追加したいパターンに役立ちます。例えば次のようなコードがサンプルにあります

[Generator]
public class AutoNotifyGenerator : ISourceGenerator
{
    private const string attributeText = @"
using System;
namespace AutoNotify
{
[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
[System.Diagnostics.Conditional(""AutoNotifyGenerator_DEBUG"")]
sealed class AutoNotifyAttribute : Attribute
{
    public AutoNotifyAttribute()
    {
    }
    public string PropertyName { get; set; }
}
}
";


    public void Initialize(GeneratorInitializationContext context)
    {
        // Register the attribute source
        context.RegisterForPostInitialization((i) => i.AddSource("AutoNotifyAttribute", attributeText));

        // Register a syntax receiver that will be created for each generation pass
        context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
    }

...

}

今までソースで指定したい属性をどうするかという問題があった(一度生成させてその属性を使うのか、手で追加するのかとか)のがこれで解決できそうです。

ISyntaxContextReceiver インターフェース

コンパイラーがソースコードを処理する際に Source Generator を呼び出す ISyntaxReceiver インターフェースがあります。

例えばクラス定義に対するコードジェネレートを行いたいときには自分でシンタックスツリーを走査して集めるのではなく、ツリーの走査はコンパイラーに任せて、 ClassDeclarationSyntax を集めておいて最後に処理するといった使い方です。

ISyntaxReceiver インターフェースは OnVisitSyntaxNode メソッドのみをもち引数として SyntaxNode を受ける形でしたが、 ISyntaxContextReceiver では GeneratorSyntaxContext を受け取る形になります。

このコンテキストオブジェクトにはセマンティックモデルが含まれているので、Receiver で必要なものをかき集めるときに、コードの解析された情報にアクセスできるようになります。

例えば属性はセマンティックモデルを通してシンボルを取得して、GetAttributes で取得できるので、Source Generator 用の属性がついていることを Receiver が集める段階で確認できます。こちらもサンプルコードが例としてわかりやすいです。

注意

16.9 で追加された API なので古い Visual Studio や .NET SDK では動かない可能性があります(試していない)。

Rider 2021.1.2 ではビルドは問題ないのですが Rider 側が対応していないようでエディター上では正しく動作しなかったりしたので、2021年5月頭時点では使うにはちょっと早いかもしれません。