なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

Roslyn Analyzer で特定プリプロセッサを有効にした状態で解析を行いたい

前回に続き、今回も Roslyn Analyzer のお話。
今回はちょっと通常使用外?な感じの使い方です。

Roslyn Analyzer では、基本的には自前の Analyzer コードが実行される時点で、現在有効となっているプリプロセッサが適用されたものが渡されます。
例えば、 Unity プロジェクトを Visual Studio で開いて作業する場合、以下のようなプリプロセッサが有効になっています。

  • UNITY_2018_4_OR_NEWER
  • UNITY_2018_4_20
  • UNITY_2018_4
  • DEBUG (Debug ビルド時)

この状態で、特定プリプロセッサを追加で有効にしたい場合などは、以下のようなコードを Analyzer 内部で実行することで、該当部分を有効・無効にした状態での SyntaxTree が取得できます(まぁそこそこ重い処理だから、あまり毎回毎回するのもやめたい気持ちはあるけども、他の方法をしらない)。

SyntaxNode node;

var preprocessors = new List<string>(...existingPreprocessors);
preprocessors.Add("COMPILER_UDONSHARP");

var options = CSharpParserOptions.Default
                                 .WithDocumentationMode(DocumentationMode.None)
                                 .WithPreprocessorSymbols(preprocessors);

var tree = CSharpSyntaxTree.ParseText(node.SyntaxTree.GetText(), options);

この状態で取得した SyntaxTree に対して、特定の Node を含む Span を検索し、得られたものが一致するかどうかを求めることで、追加したプリプロセッサが無効の場合は除外する、といったことが可能になります。

var matched = tree.GetRoot().FindNode(node.Span);
var enabled = node.Span.Start == matched.Span.Start && node.Span.End == matched.Span.End;

if (!enabled)
{
    // COMPILER_UDONSHARP が無効になっている部分 (#if !COMPILER_UDONSHARP など)
}

これは、 Roslyn Analyzer では無効となっている部分は後続の Node の LeadingTrivia として追加される仕様を利用したものです。
完全に Span に一致する部分が取得できているならば、該当部分追加したプリプロセッサに対しても有効な状態であり、一致していなければ無効な状態になっているということになります。
ちなみに Trivia は簡単に言うとソースコードの付加情報で、まぁ実行には関係が無い部分です。
#if ... ~ #endif で除外されると、実行には関係なくなるので、そこに含まれるようになる、というわけですね。

ということで、今回のメモでした。
もっと良い方法あったら教えてください。ではでは。