Roslynで数値リテラルを扱う備忘録

.NET Compiler Platform(Roslyn)で数値リテラルを扱うときのメモ。

Syntax Treeでは難しく、Semantic Modelでは簡単、というお話。

by Syntax Tree

Syntax Treeを使って数値リテラルを見る際に気を付けなければならないのは、負数の扱いとサフィックス(数字の後ろにつくfとかmとか)です。

負の定数の構文木

負の定数の構文木

リテラルだけを見た場合

リテラルだけを見た場合

Syntax Treeでは、マイナスの符号はUnaryMinusExpressionとして、LiteralExpressionとは独立しています。そのため、LiteralExpressionだけを持ってくると符号が消えて、その正の値のノードのみが処理対象になってしまいます。

また、NumericLiteralExpressionをToFullString()してdouble.Parseなどすると、サフィックスがついている場合はエラーになります。この際は、LiteralExpressionのTokenプロパティのValueによって実際の値を取得できるので、それを使います。(キャスト等はされていませんが)

by Semantic Model

Semantic Modelを使って数値リテラルを調べるのは簡単で、CompilationやContextから取得したSemanticModelの、GetConstantValueメソッドを使います。ここに定数、またはそれを判定したいノードを与えます。そうするとNullable<型>っぽいOptional<型>で値を返してくれるようです。

ノードの選別が面倒なこともあり、配列の初期化部分で値を定義しています。ソースコード等はこちらから

			var code = @"
			class Class1
			{
				void Method1()
				{
					const int A = 1;
					var foo = 1;
					var array = new object[]
					{
						-1.2f*2+A,
						1.0+foo,
						10.1,
						20m,
						1,
						(byte)255,
						'c',
						nameof(Class1)+""!""
					};
				}
			}";

			var tree = CSharpSyntaxTree.ParseText(code);
			var mscorlib = MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location);
			var compilation = CSharpCompilation.Create("NumericLiteral", new[] { tree }, new[] { mscorlib });
			var model = compilation.GetSemanticModel(tree);

			var values = tree.GetRoot().DescendantNodes().OfType<ArrayCreationExpressionSyntax>().First()
				.ChildNodes().OfType<InitializerExpressionSyntax>().First()
				.ChildNodes();
			foreach (var item in values)
			{
				var constant = model.GetConstantValue(item);
				Console.WriteLine($"{item.WithoutTrivia().ToFullString()}\n" +
					$" -> {(constant.HasValue ? $"{constant.Value} <{constant.Value.GetType()}>" : "null")}\n");
			}
-1.2f*2+A
 -> -1.4 <System.Single>

1.0+foo
 -> null

10.1
 -> 10.1 <System.Double>

20m
 -> 20 <System.Decimal>

1
 -> 1 <System.Int32>

(byte)255
 -> 255 <System.Byte>

'c'
 -> c <System.Char>

nameof(Class1)+"!"
 -> Class1! <System.String>

複雑な値でも型までしっかり判定してくれます。nullは、見たノードが定数でないと判断された場合に出力(Valueプロパティがnull)されます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です