はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】SourceGeneratorでAttributeArgumentSyntaxがNameColonを持つ場合とnullな場合の違い

はじめに

今回はSourceGeneratorにてAttributeArgumentSyntax.NameColonがnullな場合と値が格納されている場合の違いについて書きたいと思います。

learn.microsoft.com

その前に

まず前提としてSyntaxTreeは構文解析によって得られた情報であり、解析対象の文字列に含まれていないものは取得できません。今回もそれが原因になってます。

またNameColonは何かというと、AttributeArgumentSyntaxの場合はAttributeのコンストラクタの引数名を表しています。

// 構文解析の対象
// 今回の場合、NameColonは「flag1」や「flag2」といったコンストラクタの引数名を示す
[SampleMarker(flag1: true, flag2: false)]
public class SampleClass
{

}
// 補足 : 定義側 (構文解析の対象外)
using System;
                         
public class SampleMarkerAttribute : Attribute
{
public bool Flag1 { get; }
public bool Flag2 { get; }

public SampleMarkerAttribute(bool flag1, bool flag2)
{
    Flag1 = flag1;
    Flag2 = flag2;
}

取得するには

Symbolを利用して解析する人が多いと思うので、あまりSyntaxから解析するケースは少ないかもしれませんが、引数名・値を取得しようとしたら以下のようなコードになります

// ----------------------------
// ---- Syntaxを利用した場合 ----
// ----------------------------
// classSyntaxに付与されているAttributeについて、引数名と値を取得する (なんともめんどくさい...)
// NOTE: NameColonは名前を明示的に指定しないと、nullが格納されているので注意
var attributeParams = classSyntax.AttributeLists
    .SelectMany(x => x.Attributes)
    .Where(x => x.Name.ToFullString() == "SampleMarker")
    .SelectMany(x => x.ArgumentList.Arguments)
    .Select(x => new { Name = x.NameColon.Expression.ToFullString(), Value = x.Expression.ToFullString() });

違い

SharpLabを利用すると簡単にわかります。

NameColonがある場合
NameColonがない場合

引数名を明示的に指定すればNameColonがあり、指定しないとnullが格納されます。

// NameColonあり
[SampleMarker(flag1: true, flag2: false)]
// NameColonなし
[SampleMarker(true, false)]


原理を考えれば当たり前なことが分かるのですが、そもそもコードに記述されていない情報は構文解析では取得できません。つまり明示的に引数名を指定しないと、構文解析では引数の順番しか分からないのです。

補足

ちなみにコンストラクタではなくsetterで設定した場合はNameEqualsが設定されます。

NameEqualsが設定されている場合