3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Roslynが作るコードツリーを覗く

Last updated at Posted at 2020-04-29

前書き

.net coreでコンパイル時の自動コード生成をする上で、Roslyn APIに触る必要がある。
その手始めとして、Roslynが吐くASTを除いて見たメモ。

環境

今回の実験環境

% dotnet --info
.NET Core SDK (global.json を反映):
 Version:   3.1.201
 Commit:    b1768b4ae7

ランタイム環境:
 OS Name:     Mac OS X
 OS Version:  10.15
 OS Platform: Darwin
 RID:         osx.10.15-x64
 Base Path:   /usr/local/share/dotnet/sdk/3.1.201/

Host (useful for support):
  Version: 3.1.3
  Commit:  4a9f85e9f8

.NET Core SDKs installed:
  3.1.201 [/usr/local/share/dotnet/sdk]

.NET Core runtimes installed:
  Microsoft.AspNetCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.3 [/usr/local/share/dotnet/shared/Microsoft.NETCore.App]

プロジェクト準備

コンソールアプリとして作成

% dotnet new console -o CodeGenDemo

Roslyn APIのパッケージを加える

% dotnet add CodeGenDemo package Microsoft.CodeAnalysis.CSharp
% dotnet list CodeGenDemo package
プロジェクト 'CodeGenDemo' に次のパッケージ参照が含まれています
   [netcoreapp3.1]: 
   最上位レベル パッケージ                         要求済み    解決済み 
   > Microsoft.CodeAnalysis.CSharp      3.5.0   3.5.0

作成

例として、以下のインターフェースのASTを覗く

using System;
                
namespace DemoGenDao {
    public interface IColorDao {
        ColorData? FindById(int id);
    }
}

ASTの作成は、Microsoft.CodeAnalysis.CSharp名前空間のCSharpSyntaxTreeクラスのParseTextメソッドを使用する。

see: CSharpSyntaxTree.ParseText Method

ASTのルートツリーは、CSharpSyntaxTreeGetCompilationUnitRootメソッドで取得可能。

see: CSharpSyntaxTree.GetCompilationUnitRoot(CancellationToken) Method

using System;
using Microsoft.CodeAnalysis.CSharp;

// (snip)

static void Main(string[] args) {
    var programText = @"
        using System;
                
        namespace DemoGenDao {
            public interface IColorDao {
                ColorData? FindById(int id);
            }
        }
        ";

    var tree = CSharpSyntaxTree.ParseText(programText);
    var root = tree.GetCompilationUnitRoot();
}

usingで取り込んだ名前空間の情報は、Usingsプロパティから辿る。
その他クラスユニット等は、Membersプロパティから辿る。

static void Main(string[] args) {
    // 続き

    System.Console.WriteLine($"The tree has {root.Usings.Count} using statements. They are:");

    foreach (var e in root.Usings) {
        System.Console.WriteLine($"\t{e.Name}");
    }

    System.Console.WriteLine($"The tree is {root.Kind()} node.");
    System.Console.WriteLine($"The tree has {root.Members.Count} elements in it.");
}

ASTのノードは、Microsoft.CodeAnalysis.CSharp.Syntax名前空間で定義されている。
適宜キャストして扱う。

see: Microsoft.CodeAnalysis.CSharp.Syntax

今回お世話になったのは、

using Microsoft.CodeAnalysis.CSharp.Syntax; // 追加

// (snip)

static void Main(string[] args) {
    // 続き
    foreach (var m in root.Members) {
        System.Console.WriteLine($"\tThe member in root is {m.Kind()}.");

        var ns = (NamespaceDeclarationSyntax)m; // Microsoft.CodeAnalysis.CSharp.Syntax

        System.Console.WriteLine($"\tThere are {ns.Members.Count} members declared in this namespace.");

        foreach (var t in ns.Members) {
            System.Console.WriteLine($"\t\tThe member in this namespace is {t.Kind()}.");

            if (t is InterfaceDeclarationSyntax) {
                var intf = (InterfaceDeclarationSyntax)t;
                System.Console.WriteLine($"\t\tThis type is {intf.Identifier} interface.");
                System.Console.WriteLine($"\t\tThis type has {intf.Members.Count} members.");

                foreach (var mm in intf.Members) {
                    System.Console.WriteLine($"\t\t\tThe member in this type is {mm.Kind()}.");

                    if (mm is MethodDeclarationSyntax) {
                        var method = (MethodDeclarationSyntax)mm;
                        System.Console.WriteLine($"\t\t\tThis method name is {method.Identifier}.");
                        System.Console.WriteLine($"\t\t\tThis method has {method.Modifiers.Count} modifiers.");
                        System.Console.WriteLine($"\t\t\tThis method returns {method.ReturnType}.");
                        System.Console.WriteLine($"\t\t\t\tThe return type nullable in method is {method.ReturnType.IsNotNull}.");
                        System.Console.WriteLine($"\t\t\tThis method receives {method.ParameterList.Parameters.Count} args.");

                        foreach (ParameterSyntax arg in method.ParameterList.Parameters) {
                            System.Console.WriteLine($"\t\t\t\tThe arg in this method is {arg.Kind()}.");
                            System.Console.WriteLine($"\t\t\t\tThe arg name is {arg.Identifier}.");
                            System.Console.WriteLine($"\t\t\t\tThe arg type is {arg.Type}.");
                            System.Console.WriteLine($"\t\t\t\t\tThe arg nullable is {arg.Type.IsNotNull}.");                                
                        }
                    }
                    else {
                        System.Console.WriteLine($"\t\t???? This member syntax is {t.GetType().FullName}.");
                    }
                }
            }
            else {
                System.Console.WriteLine($"\t\t???? This type syntax is {t.GetType().FullName}.");
            }
        }
    }
}

実行結果

実行結果は以下の通り

% dotnet run -p CodeGenDemo
The tree is CompilationUnit node.
The tree has 1 using statements. They are:
        System
The tree has 1 elements in it.
        The member in root is NamespaceDeclaration.
        There are 1 members declared in this namespace.
                The member in this namespace is InterfaceDeclaration.
                This type is IColorDao interface.
                This type has 1 members.
                        The member in this type is MethodDeclaration.
                        This method name is FindById.
                        This method has 0 modifiers.
                        This method returns ColorData?.
                                The return type nullable in method is False.
                        This method receives 1 args.
                                The arg in this method is Parameter.
                                The arg name is id.
                                The arg type is int.
                                        The arg nullable is False.
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?