ClangNet(libclang)で構文解析サンプル - その1 (関数コールツリー、関数クロスリファレンス)
まえがき
今回はClangNet(libclangの.NET(C#) Binding)を使った実用的な構文解析のサンプルを作成してみます。
今回作成するサンプルでは、以下の2つの情報を出力する機能を実装します。
- 関数コールツリー
- 関数クロスリファレンス(宣言/定義/コール)情報
ClangNetのインストールや使用方法については過去記事をご参照ください。
an-embedded-engineer.hateblo.jp
解析対象ソース
まずは、今回解析対象にするC/C++用ソースコードを用意します。
今回は以下の種類の関数を解析対象にします。
解析対象にするソースのサンプルを以下に示します。
解析対象ソースの詳しい説明は割愛します。
[TestClass.h]
#pragma once namespace root { namespace sub1 { namespace sub2 { class TestClass { public: TestClass(); ~TestClass(); void PublicMethod(int a, int b); int GetA() { return this->m_a; } int GetB() { return this->m_b; } int GetResult() { return this->m_result; } void SetA(int a) { this->m_a = a; } void SetB(int b) { this->m_b = b; } void SetResult(int result) { this->m_result = result; } private: int PrivateMethod(int a, int b); private: int m_a; int m_b; int m_result; }; } } }
[TestClass.cpp]
#include "TestClass.h" namespace root { namespace sub1 { namespace sub2 { TestClass::TestClass() : m_a(0) , m_b(0) , m_result(0) { } TestClass::~TestClass() { } void TestClass::PublicMethod(int a, int b) { int result = this->PrivateMethod(a, b); this->SetResult(result); } int TestClass::PrivateMethod(int a, int b) { this->SetA(a); this->SetB(b); return this->m_a + this->m_b; } } } }
#include <iostream> #include "TestClass.h" void RecursiveCallTest(int i); int main() { root::sub1::sub2::TestClass test_instance; int a = 10; int b = 5; int result = 0; test_instance.SetA(a); test_instance.SetB(b); std::cout << "a : " << test_instance.GetA() << std::endl; std::cout << "b : " << test_instance.GetB() << std::endl; test_instance.PublicMethod(a, b); result = test_instance.GetResult(); std::cout << "result : " << result << std::endl; RecursiveCallTest(10); } void RecursiveCallTest(int i) { if (i < 0) { return; } else { std::cout << "i : " << i << std::endl; RecursiveCallTest(i - 1); } }
構文解析サンプル
上記解析対象ソースに対して、関数コールツリー(Invokation Tree)と関数クロスリファレンス(Cross Reference)情報を生成して出力するサンプルを実装します。
実現方法
実現方法を簡単に説明します。
大きく分けると次の3ステップで実現します。
- Translation Unit(ソースファイル)解析
- Invokation Tree(関数コールツリー)生成
- Cross Reference(関数参照情報)生成
1.のTranslation Unit(ソースファイル)解析では、ClangNetの構文解析機能を使って各ソースファイルのAST(抽象構文木)を辿っていき、以下のような情報を収集します。
種別 | 意味 | 保持する情報 | 備考 |
---|---|---|---|
Translation Unit情報 | 変換対象(ソースファイル)ごとに保持する情報 | ソースファイルパス、宣言・定義されているBehavior情報マップ | |
Behavior情報マップ | Behavior(関数)の宣言・定義位置をキーにした、Behavior情報のハッシュマップ | Key : 関数宣言・定義位置、Value : Behavior情報 | |
Behavior情報 | 種別(宣言/定義)、関数宣言、名前空間、ソース位置、呼び出している関数のInvokation情報リスト | ||
Invokation情報 | 呼び出している関数名、ソース位置、参照している関数宣言のBehavior情報 |
のInvokation Tree(関数コールツリー)生成では、1.で収集した情報をもとに、関数定義内で呼び出している関数を再帰的に辿っていきます。
の Cross Reference(関数参照情報)生成では、1.で収集した情報をもとに、関数ごとに宣言箇所、定義箇所、呼び出し箇所のテーブルを生成します。
1. Translation Unit(ソースファイル)解析
ソースファイルからClangNetを使ってASTを解析し、必要な情報を収集します。
1-1. データ構造定義
まずは収集して構築するデータ構造を定義して行きます。
型名 | ベース型 | 意味 | 保持する情報 | 備考 |
---|---|---|---|---|
AstNodeInfo | - | ASTノードベース型 | ソース位置情報 | |
TranslationUnitInfo | AstNodeInfo | 変換対象ソース情報 | ファイルパス、BehaviorLocationMap | |
BehaviorInfo | AstNodeInfo | 関数ノードベース型 | 種別(宣言/定義)、関数名、パラメータ、名前空間、定義、ID、InvokationInfoリスト | |
ClassBehaviorInfo | BehaviorInfo | クラスに所属する関数ノードベース型 | クラス名 | |
ConstructorInfo | ClassBehaviorInfo | コンストラクタ情報 | - | |
DestructorInfo | ClassBehaviorInfo | デストラクタ情報 | - | |
CppMethodInfo | ClassBehaviorInfo | クラスメンバ関数情報 | 返り値 | |
FunctionInfo | BehaviorInfo | Cスタイル関数情報 | 返り値 | |
InvokationInfo | AstNodeInfo | 関数呼び出し情報 | 関数名、ID、参照先関数BehaviorInfo | |
BehaviorLocationMap | Dictionary<string,BehaviorInfo> | ソース位置をキーとしたBehaviorInfoのDictionary | Key : ソース位置 / Value : BehaviorInfo | |
TranslationUnitMap | Dictionary<string,TranslationUnitInfo> | ソースパスをキーとしたTranslationUnitInfoのDictionary | Key : ソースパス / Value : TranslationUnitInfo |
それぞれのソースを示します。
[AstNodeInfo.cs]
public abstract class AstNodeInfo { public string Location { get; } public AstNodeInfo(ClangCursor cursor) { /* ソース上の位置情報を取得 */ this.Location = this.GetLocation(cursor); } public string GetLocation(ClangCursor cursor) { /* カーソル位置を取得 */ var loc = cursor.Location; /* カーソル位置がnullでない */ if (loc != null) { /* ファイル位置を取得 */ var floc = loc.FileLocation; /* ファイル位置がnullでない */ if (floc != null) { /* ファイル情報がnullでない */ if(floc.File != null) { /* ファイルパスを取得 */ var path = Path.GetFullPath(floc.File.FileName); /* ファイル上の行番号を取得 */ var line = floc.Line; /* ファイル上の列番号を取得 */ var col = floc.Column; /* 位置情報文字列を生成 */ var location = $"{path}[L.{line},C.{col}]"; return location; } else { /* ファイル上の行番号を取得 */ var line = floc.Line; /* ファイル上の列番号を取得 */ var col = floc.Column; /* 位置情報文字列を生成 */ var location = $"[L.{line},C.{col}]"; return location; } } else { /* 位置情報なし */ return string.Empty; } } else { /* 位置情報なし */ return string.Empty; } } }
[TranslationUnitInfo.cs]
public class TranslationUnitInfo : AstNodeInfo { public string Path { get; } public BehaviorLocationMap BehaviorMap { get; } = new BehaviorLocationMap(); public TranslationUnitInfo(ClangCursor cursor) : base(cursor) { /* ソースファイルのフルパスを取得 */ this.Path = System.IO.Path.GetFullPath(cursor.Spelling); } public void AddBehavior(BehaviorInfo behavior) { this.BehaviorMap.Add(behavior); } public override string ToString() { return $"[TranslationUnit]{this.Path}"; } }
[BehaviorInfo.cs]
public abstract class BehaviorInfo : AstNodeInfo { public string Type { get; } public string Name { get; } public string Parameters { get; } public string NameSpace { get; } /* 関数定義は継承先で生成 */ public string Definition => this.GetDefinition(); /* 関数のユニークなIDを生成 */ public string ID => this.GenerateBehaviorID(); public List<InvokationInfo> Invokations { get; } = new List<InvokationInfo>(); protected abstract string GetDefinition(); public BehaviorInfo(ClangCursor cursor) : base(cursor) { /* 関数の種別を判定 */ this.Type = cursor.IsDefinition ? "Definition" : "Declaration"; /* 関数名を取得 */ this.Name = cursor.Spelling; /* 関数パラメータを取得 */ this.Parameters = cursor.DisplayName.Replace(cursor.Spelling, ""); /* 名前空間を取得 */ this.NameSpace = this.GetNamespace(cursor); } public void AddInvokation(InvokationInfo invokation_info) { this.Invokations.Add(invokation_info); } private string GenerateBehaviorID() { /* 関数のIDには関数定義を使用 */ return this.Definition; } /* 名前空間を取得 */ private string GetNamespace(ClangCursor cursor) { /* 名前空間保持用スタック */ var namespace_stack = new Stack<string>(); /* 名前空間を再帰的にパース */ this.ParseNamespace(cursor, namespace_stack); /* スタックの要素数が0 */ if (namespace_stack.Count == 0) { /* 名前空間なし */ return string.Empty; } else { var sb = new StringBuilder(); /* 一番上の名前空間を追加 */ sb.Append(namespace_stack.Pop()); /* スタックが空になるまで */ while(namespace_stack.Count != 0) { /* 階層化された名前空間を連結 */ sb.Append("::"); sb.Append(namespace_stack.Pop()); } return sb.ToString(); } } /* 名前空間を再帰的にパース */ private void ParseNamespace(ClangCursor cursor, Stack<string> namespace_stack) { /* 関数の意味的な親を示すカーソルを取得 */ var parent = cursor.SemanticParent; /* 親カーソルがnullでない */ if (parent != null) { /* 親カーソルがTranslation Unit */ if (parent.Kind == CursorKind.TranslationUnit) { /* 最上位のためパース終了 */ } /* 親カーソルがNamespace */ else if(parent.Kind == CursorKind.Namespace) { /* 名前空間保持用スタックに追加 */ namespace_stack.Push(parent.Spelling); /* 親カーソルの名前空間を再帰的にパース */ this.ParseNamespace(parent, namespace_stack); } /* それ以外(クラスなど) */ else { /* 親カーソルの名前空間を再帰的にパース */ this.ParseNamespace(parent, namespace_stack); } } } public override string ToString() { return $"[Behavior][{this.Type}] {this.Definition}"; } }
[ClassBehaviorInfo.cs]
public abstract class ClassBehaviorInfo : BehaviorInfo { public string ClassName { get; } public ClassBehaviorInfo(ClangCursor cursor) : base(cursor) { /* 意味的な親カーソル(クラス)を取得 */ var parent_class = cursor.SemanticParent; /* クラス名を取得 */ this.ClassName = parent_class.Spelling; } public override string ToString() { return $"[ClassBehavior][{this.Type}] {this.Definition}"; } }
[ConstructorInfo.cs]
public class ConstructorInfo : ClassBehaviorInfo { public ConstructorInfo(ClangCursor cursor) : base(cursor) { } protected override string GetDefinition() { var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::"; /* コンストラクタ定義 : [<名前空間>::]<クラス名>::<コンストラクタ名>(<パラメータ>) */ return $"{ns}{this.ClassName}::{this.Name}{this.Parameters}"; } public override string ToString() { return $"[Constructor][{this.Type}] {this.Definition}"; } }
[DestructorInfo.cs]
public class DestructorInfo : ClassBehaviorInfo { public DestructorInfo(ClangCursor cursor) : base(cursor) { } protected override string GetDefinition() { var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::"; /* デストラクタ定義 : [<名前空間>::]<クラス名>::<デストラクタ名>(<パラメータ>) */ return $"{ns}{this.ClassName}::{this.Name}{this.Parameters}"; } public override string ToString() { return $"[Destructor][{this.Type}] {this.Definition}"; } }
[CppMethodInfo.cs]
public class CppMethodInfo : ClassBehaviorInfo { public string ReturnType { get; } public CppMethodInfo(ClangCursor cursor) : base(cursor) { var rtype = cursor.ResultType; this.ReturnType = rtype.Spelling; } protected override string GetDefinition() { var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::"; /* C++クラスメンバ関数定義 : <返り値型> [<名前空間>::]<クラス名>::<関数名>(<パラメータ>) */ return $"{this.ReturnType} {ns}{this.ClassName}::{this.Name}{this.Parameters}"; } public override string ToString() { return $"[Function][{this.Type}] {this.Definition}"; } }
[FunctionInfo.cs]
public class FunctionInfo : BehaviorInfo { public string ReturnType { get; } public FunctionInfo(ClangCursor cursor) : base(cursor) { var rtype = cursor.ResultType; this.ReturnType = rtype.Spelling; } protected override string GetDefinition() { var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::"; /* Cスタイル関数定義 : <返り値型> [<名前空間>::]<関数名>(<パラメータ>) */ return $"{this.ReturnType} {ns}{this.Name}{this.Parameters}"; } public override string ToString() { return $"[Function][{this.Type}] {this.Definition}"; } }
[InvokationInfo.cs]
public class InvokationInfo : AstNodeInfo { public string ID { get; } public string Definition { get; } public string Name { get; } public BehaviorInfo Declaration { get; } public InvokationInfo(ClangCursor cursor) : base(cursor) { /* コール関数名を取得 */ this.Name = cursor.Spelling; /* コール関数の参照先がnullでない */ if (cursor.Referenced != null) { /* 参照先カーソルから関数宣言のBehavior Infoを生成 */ this.Declaration = BehaviorInfoFactory.Create(cursor.Referenced); /* 関数宣言のBehavior Infoから関数IDを取得 */ this.ID = this.Declaration.ID; /* 関数宣言のBehavior Infoから関数定義を取得 */ this.Definition = this.Declaration.Definition; } else { throw new FieldAccessException($"Behavior Declaration Not Found : {this.Name}"); } } public override string ToString() { return $"[Invokation]{this.Name}"; } }
[BehaviorlocationMap.cs]
public class BehaviorLocationMap : Dictionary<string, BehaviorInfo> { public void Add(BehaviorInfo behavior) { if (this.ContainsKey(behavior.Location) == false) { this.Add(behavior.Location, behavior); } else { throw new InvalidOperationException($"Behavior Already Registered : {behavior.ID}"); } } public void AddIfNotRegistered(BehaviorInfo behavior) { if (this.ContainsKey(behavior.Location) == false) { this.Add(behavior.Location, behavior); } else { /* Nothing to do */ } } }
[BehaviorlocationMap.cs]
public class TranslationUnitMap : Dictionary<string, TranslationUnitInfo> { public void Add(TranslationUnitInfo tu) { if (this.ContainsKey(tu.Path) == false) { this.Add(tu.Path, tu); } else { throw new InvalidOperationException($"Translation Unit Already Registered : {tu.Path}"); } } }
1-2. AST解析
続いて、ASTを解析してTranslationUnitMapを生成するロジックを実現するクラスを定義します。
また、ASTを解析する部分は、様々なソースファイルの入力に対応できるように設定情報を保持するクラスを定義し、それをパラメータとして渡すようにします。
さらに、関数情報(BehaviorInfo)はカーソルの種別に対応したクラスのインスタンスを生成できるようにFactoryクラスを定義します。
それぞれのソースを以下に示します。
[TranslationUnitParseSetting.cs]
public class TranslationUnitParseSetting { public List<string> Sources { get; set; } = new List<string>(); public List<string> CommandLineArgs { get; set; } = new List<string>(); public TranslationUnitFlags ParseOptions { get; set; } = TranslationUnitFlags.None; public bool DisplayDiag { get; set; } = true; public bool DumpAST { get; set; } = true; }
[BehaviorInfoFactory.cs]
public static class BehaviorInfoFactory { public static BehaviorInfo Create(ClangCursor cursor) { switch(cursor.Kind) { /* コンストラクタ */ case CursorKind.Constructor: return new ConstructorInfo(cursor); /* デストラクタ */ case CursorKind.Destructor: return new DestructorInfo(cursor); /* Cスタイル関数 */ case CursorKind.FunctionDeclaration: return new FunctionInfo(cursor); /* C++クラスメンバ関数 */ case CursorKind.CXXMethod: return new CppMethodInfo(cursor); default: throw new ArgumentException($"Not Behavior Cursor"); } } }
[TranslationUnitsParser.cs]
public static class TranslationUnitsParser { public static TranslationUnitMap Parse(TranslationUnitParseSetting setting) { var impl = new TranslationUnitsParserImpl(); var map = impl.Execute(setting); return map; } public class TranslationUnitsParserImpl : AMessageable { private TranslationUnitParseSetting Setting { get; set; } private TranslationUnitInfo CurrentTranslationUnit { get; set; } private BehaviorInfo CurrentBehavior { get; set; } private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap(); public TranslationUnitMap Execute(TranslationUnitParseSetting setting) { this.Setting = setting; var display_diag = setting.DisplayDiag; var src_path_list = setting.Sources; var command_line_args = setting.CommandLineArgs.ToArray(); var options = setting.ParseOptions; /* Indexを生成 */ using (var index = Clang.CreateIndex(false, display_diag)) { /* 解析対象のソースファイルパスリストを走査 */ foreach (var src_path in src_path_list) { /* ソースファイルパスのファイルが存在するかを確認 */ if (File.Exists(src_path)) { /* ソースファイルをパースし、Translation Unitを生成 */ using (var tu = index.ParseTranslationUnit(src_path, command_line_args, new ClangUnsavedFile[0], options)) { /* 解析実行 */ this.ExecuteCore(index, tu); } } else { throw new InvalidOperationException($"Source File Not Found :{src_path}"); } } } return this.TranslationUnitMap; } private void ExecuteCore(ClangIndex index, ClangTranslationUnit tu) { if (this.Setting.DumpAST) { this.SendMessage($"AST Dump:"); } /* ASTを再帰的にパース */ this.VisitChild(tu.Cursor, 0); } private ChildVisitResult Visitor(ClangCursor cursor, ClangCursor parent, int depth) { /* カーソル位置を取得 */ var loc = cursor.Location; /* 解析対象ソースファイル内 */ if (loc.IsFromMainFile == true) { /* 子要素をパース */ this.VisitChild(cursor, depth); } else { /* システムヘッダ内 */ if (loc.IsInSystemHeader == true) { /* Nothing to do */ } /* ユーザヘッダ内 */ else { /* 子要素をパース */ this.VisitChild(cursor, depth); } } return ChildVisitResult.Continue; } private void VisitChild(ClangCursor cursor, int depth) { /* デバッグ用にASTをダンプ */ if (this.Setting.DumpAST) { this.DumpAstInfo(cursor, depth); } /* 関数コール情報を解析 */ this.AnalyseInvokationInfo(cursor); cursor.VisitChildren(this.Visitor, depth + 1); } private void DumpAstInfo(ClangCursor cursor, int depth) { var indent = new string(' ', depth * 2); var kind = cursor.Kind; var name = cursor.Spelling; var loc = cursor.Location.ToStringEx(); this.SendMessage($"{indent}[{kind}] {name} @ {loc}"); } private void AnalyseInvokationInfo(ClangCursor cursor) { switch (cursor.Kind) { /* 解析対象ソースファイル */ case CursorKind.TranslationUnit: /* Translation Unit Infoを生成し、Translation Unit Mapに追加 */ this.CurrentTranslationUnit = new TranslationUnitInfo(cursor); this.TranslationUnitMap.Add(this.CurrentTranslationUnit); break; /* コンストラクタ */ case CursorKind.Constructor: /* Constructor Infoを生成し、Translation Unit Infoに追加 */ this.CurrentBehavior = BehaviorInfoFactory.Create(cursor); this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior); break; /* デストラクタ */ case CursorKind.Destructor: /* Destructor Infoを生成し、Translation Unit Infoに追加 */ this.CurrentBehavior = BehaviorInfoFactory.Create(cursor); this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior); break; /* Cスタイル関数*/ case CursorKind.FunctionDeclaration: /* Function Infoを生成し、Translation Unit Infoに追加 */ this.CurrentBehavior = BehaviorInfoFactory.Create(cursor); this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior); break; /* C++メンバ関数 */ case CursorKind.CXXMethod: /* Cpp Method Infoを生成し、Translation Unit Infoに追加 */ this.CurrentBehavior = BehaviorInfoFactory.Create(cursor); this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior); break; /* 関数コール */ case CursorKind.CallExpression: /* Invokation Infoを生成し、Translation Unit Infoに追加 */ var invokation_info = new InvokationInfo(cursor); this.CurrentBehavior.AddInvokation(invokation_info); break; default: break; } } } }
2. Invokation Tree(関数コールツリー)生成
次は、1.で生成したTranslationUnitMapの情報から関数コールツリー情報を生成し、出力する機能を持つクラスを実装します。
関数コールツリーを出力するために、TranslationUnitMapに登録されている各TranslationUnitInfoのBehaviorMapから、種別がDefinition(関数定義)のBehaviorInfoを抽出し、Behavior IDをキーとしたハッシュマップを生成します。
生成するハッシュマップ(BehaviorDefinitionMapクラス)の定義と、ハッシュマップ生成処理の実装コードを以下に示します。
[BehaviorDefinitionMap.cs]
public class BehaviorDefinitionMap : Dictionary<string, BehaviorInfo> { public void Add(BehaviorInfo behavior) { if (this.ContainsKey(behavior.ID) == false) { this.Add(behavior.ID, behavior); } else { throw new InvalidOperationException($"Behavior Already Registered : {behavior.ID}"); } } public void AddIfNotRegistered(BehaviorInfo behavior) { if (this.ContainsKey(behavior.ID) == false) { this.Add(behavior.ID, behavior); } else { /* Nothing to do */ } } }
[ハッシュマップ生成処理]
private void CreateBehaviorDefinitionMap() { /* Translation Unit Infoを走査 */ foreach (var tu in this.TranslationUnitMap.Values) { /* Translation Unit InfoのBehavior MapからTypeがDefinitionのBehavior Infoを抽出 */ var behavior_definitions = tu.BehaviorMap.Values.Where(b => b.Type == "Definition").ToList(); /* 抽出したBehavior Infoを走査 */ foreach (var behavior_definition in behavior_definitions) { /* Behavior Infoが登録されていなければ、Behavior Definition Mapに登録 */ this.BehaviorDefinitionMap.AddIfNotRegistered(behavior_definition); } } }
生成したBehaviorDefinitionMapを用いて、関数が呼び出している関数情報を辿っていき、関数のコールツリーを出力します。
実装コードを以下に示します。
[関数コールツリー出力]
private void DumpInvokationTrees() { /* Behavior Definition MapのValue(BehaviorInfo)リストを取得 */ var behavior_defnitions = this.BehaviorDefinitionMap.Values; /* Behavior Definitionリストを走査 */ foreach (var behavior in behavior_defnitions) { /* 関数定義と定義位置を出力 */ this.SendMessage($"{behavior.Definition} @ {behavior.Location}"); /* 関数のコールツリーを出力 */ this.DumpInvokationTree(behavior); /* 改行 */ this.SendMessage(); } } private void DumpInvokationTree(BehaviorInfo behavior, int depth = 1) { /* Behavior InfoのInvokation Infoリストを走査 */ foreach (var invokation in behavior.Invokations) { /* インデント(1段階)を生成 */ var indent = new string(' ', depth * 2); /* インデント(2段階)を生成 */ var indent2 = new string(' ', (depth + 1) * 2); /* コール関数定義とコール位置を出力 */ this.SendMessage($"{indent}{invokation.Definition} @ {invokation.Location}"); /* 親関数とコール関数のIDが同一(再帰呼び出し) */ if (behavior.ID == invokation.ID) { /* 再帰呼び出しの旨を出力 */ this.SendMessage($"{indent2} <Recursive Call...>"); } else { /* Behavior Definition Mapにコール関数と同一IDのBehavior Infoがあるかを確認 */ if (this.BehaviorDefinitionMap.ContainsKey(invokation.ID)) { /* コール関数のBehavior Infoを取得 */ var child_behavior = this.BehaviorDefinitionMap[invokation.ID]; /* コール関数を親にしてコールツリーを出力 */ this.DumpInvokationTree(child_behavior, depth + 1); } else { /* コール関数の関数定義が見つからない旨を出力 */ this.SendMessage($"{indent2} <Behavior Definition Not Found...>"); } } } }
ソース全文を以下に示します。
[InvokationTreeDumper.cs]
public static class InvokationTreeDumper { public static void Dump(TranslationUnitMap map) { var impl = new InvokationTreeDumperImpl(); impl.Execute(map); } public class InvokationTreeDumperImpl : AMessageable { private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap(); private BehaviorDefinitionMap BehaviorDefinitionMap { get; set; } = new BehaviorDefinitionMap(); public void Execute(TranslationUnitMap map) { this.TranslationUnitMap = map; this.CreateBehaviorDefinitionMap(); this.SendMessage(); this.SendMessage("--------------------------------------"); this.SendMessage("Invokation Tree:"); this.DumpInvokationTrees(); } private void CreateBehaviorDefinitionMap() { foreach (var tu in this.TranslationUnitMap.Values) { var behavior_definitions = tu.BehaviorMap.Values.Where(b => b.Type == "Definition").ToList(); foreach (var behavior_definition in behavior_definitions) { this.BehaviorDefinitionMap.AddIfNotRegistered(behavior_definition); } } } private void DumpInvokationTrees() { var behavior_defnitions = this.BehaviorDefinitionMap.Values; foreach (var behavior in behavior_defnitions) { this.SendMessage($"{behavior.Definition} @ {behavior.Location}"); this.DumpInvokationTree(behavior); this.SendMessage(); } } private void DumpInvokationTree(BehaviorInfo behavior, int depth = 1) { foreach (var invokation in behavior.Invokations) { var indent = new string(' ', depth * 2); var indent2 = new string(' ', (depth + 1) * 2); this.SendMessage($"{indent}{invokation.Definition} @ {invokation.Location}"); if (behavior.ID == invokation.ID) { this.SendMessage($"{indent2} <Recursive Call...>"); } else { if (this.BehaviorDefinitionMap.ContainsKey(invokation.ID)) { var child_behavior = this.BehaviorDefinitionMap[invokation.ID]; this.DumpInvokationTree(child_behavior, depth + 1); } else { this.SendMessage($"{indent2} <Behavior Definition Not Found...>"); } } } } } }
3. Cross Reference(関数参照情報)生成
続いて1.で生成したTranslationUnitMapの情報から関数クロスリファレンス(宣言/定義/コール)情報を生成し、出力する機能を持つクラスを実装します。
クロスリファレンス情報を保持するためのクラス(BehaviorCrossReferenceInfo)と、それを関数ごとにまとめるコンテナ(BehaviorCrossReferenceMap)を定義します。
なお、BehaviorCrossReferenceInfoは、重複を排除するためにValueObjectパターンで実装します。
それぞれの実装コードを以下に示します。
[ValueObject.cs]
public abstract class ValueObject<T> where T : ValueObject<T> { /* データの同一性を判定する関数 -> 継承先で実装 */ protected abstract bool EqualsCore(T other); /* ハッシュコード生成に使用するパラメータを取得する関数 -> 継承先で実装 */ protected abstract IEnumerable<object> GetHashCodeParameters(); public static bool operator ==(ValueObject<T> left, ValueObject<T> right) { return Equals(left, right); } public static bool operator !=(ValueObject<T> left, ValueObject<T> right) { return !Equals(left, right); } public override bool Equals(object obj) { var other = obj as T; if (other == null) { return false; } else { return this.EqualsCore(other); } } public override int GetHashCode() { var objs = this.GetHashCodeParameters(); var hash = 0; foreach(var obj in objs) { hash ^= obj.GetHashCode(); } return hash; } }
[BehaviorCrossReferenceInfo.cs]
public class BehaviorCrossReferenceInfo : ValueObject<BehaviorCrossReferenceInfo> { public string ID { get; } public string Type { get; } public string Definition { get; } public string Location { get; } /* Behavior Infoからクロスリファレンス情報を生成 */ public BehaviorCrossReferenceInfo(BehaviorInfo info) { this.ID = info.ID; this.Type = info.Type; this.Definition = info.Definition; this.Location = info.Location; } /* Invokation Infoからクロスリファレンス情報を生成 */ public BehaviorCrossReferenceInfo(InvokationInfo info) { this.ID = info.ID; this.Type = "Call"; this.Definition = info.Definition; this.Location = info.Location; } public override string ToString() { return $"[{this.Type}]{this.Definition} @ {this.Location}"; } /* 各プロパティの値が全て一致する場合、同一のオブジェクトとみなす */ protected override bool EqualsCore(BehaviorCrossReferenceInfo other) { return (this.ID == other.ID && this.Type == other.Type && this.Definition == other.Definition && this.Location == other.Location); } /* 各プロパティの値をもとにハッシュコードを生成 */ protected override IEnumerable<object> GetHashCodeParameters() { var objs = new List<object>() { this.ID, this.Definition, this.Type, this.Location, }; return objs; } }
[BehaviorCrossReferenceMap.cs]
/* 同一ID(同一関数)ごとにクロスリファレンス情報をまとめるコンテナ(重複は排除) */ public class BehaviorCrossReferenceMap : Dictionary<string, HashSet<BehaviorCrossReferenceInfo>> { public void Add(BehaviorCrossReferenceInfo info) { if (this.ContainsKey(info.ID) == false) { this.Add(info.ID, new HashSet<BehaviorCrossReferenceInfo>()); } this[info.ID].Add(info); } }
上記で示したクロスリファレンス情報を生成するコードを実装します。
[クロスリファレンス情報生成]
private void AnalyseCrossReferences() { var map1 = new BehaviorCrossReferenceMap(); /* Translation Unit Infoを走査 */ foreach (var tu in this.TranslationUnitMap.Values) { /* Translation Unit InfoのBehavior Mapの値(Behavior Info)を走査 */ foreach (var behavior in tu.BehaviorMap.Values) { /* Behavior InfoからBehavior Cross Reference Infoを生成 */ var xref_behavior = new BehaviorCrossReferenceInfo(behavior); /* Cross Reference Mapに登録 */ map1.Add(xref_behavior); /* Behavior InfoのInvokation Infoリストを走査 */ foreach (var invoke in behavior.Invokations) { /* Invokation InfoからBehavior Cross Reference Infoを生成 */ var xref_invoke = new BehaviorCrossReferenceInfo(invoke); /* Cross Reference Mapに登録 */ map1.Add(xref_invoke); } } } /* 新規Behavior Cross Reference Mapを生成 */ var map2 = new BehaviorCrossReferenceMap(); /* 前段で生成したBehavior Cross Reference Mapの各キーを走査 */ foreach (var key in map1.Keys) { /* キーを指定してハッシュセットを取得 */ var set = map1[key]; /* Behavior Cross Reference InfoのTypeでソート */ var sorted_set = set.OrderBy(x => x.Type).ToHashSet(); /* ソート後のハッシュセットを登録 */ map2.Add(key, sorted_set); } this.CrossReferenceMap = map2; }
次に、生成したクロスリファレンス情報を順番に出力します。
実装コードを以下に示します。
[クロスリファレンス情報出力]
private void DumpCrossReferences() { /* Behavior Cross Reference Mapの各キー(Behavior ID)を走査 */ foreach (var xref_id in this.CrossReferenceMap.Keys) { /* Behavior IDを出力 */ this.SendMessage($"{xref_id}"); /* キーを指定してBehavior Cross Reference Infoのハッシュセットを取得 */ var xref_hash_set = this.CrossReferenceMap[xref_id]; /* ハッシュセットの要素(Behavior Cross Reference Info)を走査 */ foreach (var xref_info in xref_hash_set) { var type = xref_info.Type; var loc = xref_info.Location; this.SendMessage($" [{type}] @ {loc}"); } this.SendMessage(); } }
ソース全文を以下に示します。
[CrossReferencesDumper.cs]
public static class CrossReferencesDumper { public static void Dump(TranslationUnitMap map) { var impl = new CrossReferencesDumperImpl(); impl.Execute(map); } public class CrossReferencesDumperImpl : AMessageable { private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap(); private BehaviorCrossReferenceMap CrossReferenceMap { get; set; } = new BehaviorCrossReferenceMap(); public void Execute(TranslationUnitMap map) { this.TranslationUnitMap = map; this.AnalyseCrossReferences(); this.SendMessage(); this.SendMessage("--------------------------------------"); this.SendMessage("Cross References:"); this.DumpCrossReferences(); } private void AnalyseCrossReferences() { var map1 = new BehaviorCrossReferenceMap(); foreach (var tu in this.TranslationUnitMap.Values) { foreach (var behavior in tu.BehaviorMap.Values) { var xref_behavior = new BehaviorCrossReferenceInfo(behavior); map1.Add(xref_behavior); foreach (var invoke in behavior.Invokations) { var xref_invoke = new BehaviorCrossReferenceInfo(invoke); map1.Add(xref_invoke); } } } var map2 = new BehaviorCrossReferenceMap(); foreach (var key in map1.Keys) { var set = map1[key]; var sorted_set = set.OrderBy(x => x.Type).ToHashSet(); map2.Add(key, sorted_set); } this.CrossReferenceMap = map2; } private void DumpCrossReferences() { foreach (var xref_id in this.CrossReferenceMap.Keys) { this.SendMessage($"{xref_id}"); var xref_hash_set = this.CrossReferenceMap[xref_id]; foreach (var xref_info in xref_hash_set) { var type = xref_info.Type; var loc = xref_info.Location; this.SendMessage($" [{type}] @ {loc}"); } this.SendMessage(); } } } }
4. 各処理呼び出し
最後に、1-3.で実装した各機能を呼び出す処理を実装します。
public static class BehaviorRelationsAnalyser { public static void Execute() { /* Translation Unitのパース設定情報を生成 */ var setting = new TranslationUnitParseSetting() { /* パース対象ソースパスの指定 */ Sources = new List<string>() { "./Code2/src/main.cpp", "./Code2/src/TestClass.cpp", }, /* libclangに渡すコマンドライン引数(インクルードパスなど)の指定 */ CommandLineArgs = new List<string>() { "-I./Code2/inc", }, /* libclangのTranslation Unitパースオプション */ ParseOptions = TranslationUnitFlags.None, /* ダイアグ情報(構文エラーなど)を表示 */ DisplayDiag = true, /* AST情報を出力(デバッグ用) */ DumpAST = true, }; /* Translation Unitをパースし、Translation Unit Mapを生成 */ var map = TranslationUnitsParser.Parse(setting); /* Translation Unit Mapから関数コールツリーを生成して出力 */ InvokationTreeDumper.Dump(map); /* Translation Unit Mapから関数クロスリファレンス情報を生成して出力 */ CrossReferencesDumper.Dump(map); } }
解析対象サンプルソースを入力して実行した結果を示します。
上から、ASTのダンプ結果、関数コールツリー、関数クロスリファレンス情報が出力されています。
[実行結果]
AST Dump: [TranslationUnit] ./Code2/src/main.cpp @ [Namespace] root @ ./Code2/inc/TestClass.h[L.3,C.11] [Namespace] sub1 @ ./Code2/inc/TestClass.h[L.5,C.15] [Namespace] sub2 @ ./Code2/inc/TestClass.h[L.7,C.19] [ClassDeclaration] TestClass @ ./Code2/inc/TestClass.h[L.9,C.19] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.11,C.13] [Constructor] TestClass @ ./Code2/inc/TestClass.h[L.12,C.17] [Destructor] ~TestClass @ ./Code2/inc/TestClass.h[L.13,C.17] [CXXMethod] PublicMethod @ ./Code2/inc/TestClass.h[L.15,C.22] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.15,C.39] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.15,C.46] [CXXMethod] GetA @ ./Code2/inc/TestClass.h[L.17,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.17,C.28] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.17,C.30] [UnexposedExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43] [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.17,C.37] [CXXMethod] GetB @ ./Code2/inc/TestClass.h[L.18,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.18,C.28] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.18,C.30] [UnexposedExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43] [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.18,C.37] [CXXMethod] GetResult @ ./Code2/inc/TestClass.h[L.19,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.19,C.33] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.19,C.35] [UnexposedExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48] [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.19,C.42] [CXXMethod] SetA @ ./Code2/inc/TestClass.h[L.21,C.22] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.21,C.31] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.21,C.34] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.21,C.36] [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.21,C.42] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.21,C.36] [UnexposedExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48] [DeclarationReferenceExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48] [CXXMethod] SetB @ ./Code2/inc/TestClass.h[L.22,C.22] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.22,C.31] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.22,C.34] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.22,C.36] [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.22,C.42] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.22,C.36] [UnexposedExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48] [DeclarationReferenceExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48] [CXXMethod] SetResult @ ./Code2/inc/TestClass.h[L.23,C.22] [ParmDeclaration] result @ ./Code2/inc/TestClass.h[L.23,C.36] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.23,C.44] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.23,C.46] [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.23,C.52] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.23,C.46] [UnexposedExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63] [DeclarationReferenceExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.25,C.13] [CXXMethod] PrivateMethod @ ./Code2/inc/TestClass.h[L.26,C.21] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.26,C.39] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.26,C.46] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.28,C.13] [FieldDeclaration] m_a @ ./Code2/inc/TestClass.h[L.29,C.21] [FieldDeclaration] m_b @ ./Code2/inc/TestClass.h[L.30,C.21] [FieldDeclaration] m_result @ ./Code2/inc/TestClass.h[L.31,C.21] [FunctionDeclaration] RecursiveCallTest @ ./Code2/src/main.cpp[L.5,C.6] [ParmDeclaration] i @ ./Code2/src/main.cpp[L.5,C.28] [FunctionDeclaration] main @ ./Code2/src/main.cpp[L.7,C.5] [CompoundStatement] @ ./Code2/src/main.cpp[L.8,C.1] [DeclarationStatement] @ ./Code2/src/main.cpp[L.9,C.5] [VarDeclaration] test_instance @ ./Code2/src/main.cpp[L.9,C.33] [NamespaceReference] root @ ./Code2/src/main.cpp[L.9,C.5] [NamespaceReference] sub1 @ ./Code2/src/main.cpp[L.9,C.11] [NamespaceReference] sub2 @ ./Code2/src/main.cpp[L.9,C.17] [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/main.cpp[L.9,C.23] [CallExpression] TestClass @ ./Code2/src/main.cpp[L.9,C.33] [DeclarationStatement] @ ./Code2/src/main.cpp[L.10,C.5] [VarDeclaration] a @ ./Code2/src/main.cpp[L.10,C.9] [IntegerLiteral] @ ./Code2/src/main.cpp[L.10,C.13] [DeclarationStatement] @ ./Code2/src/main.cpp[L.11,C.5] [VarDeclaration] b @ ./Code2/src/main.cpp[L.11,C.9] [IntegerLiteral] @ ./Code2/src/main.cpp[L.11,C.13] [DeclarationStatement] @ ./Code2/src/main.cpp[L.12,C.5] [VarDeclaration] result @ ./Code2/src/main.cpp[L.12,C.9] [IntegerLiteral] @ ./Code2/src/main.cpp[L.12,C.18] [CallExpression] SetA @ ./Code2/src/main.cpp[L.14,C.5] [MemberReferenceExpression] SetA @ ./Code2/src/main.cpp[L.14,C.19] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.14,C.5] [UnexposedExpression] a @ ./Code2/src/main.cpp[L.14,C.24] [DeclarationReferenceExpression] a @ ./Code2/src/main.cpp[L.14,C.24] [CallExpression] SetB @ ./Code2/src/main.cpp[L.16,C.5] [MemberReferenceExpression] SetB @ ./Code2/src/main.cpp[L.16,C.19] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.16,C.5] [UnexposedExpression] b @ ./Code2/src/main.cpp[L.16,C.24] [DeclarationReferenceExpression] b @ ./Code2/src/main.cpp[L.16,C.24] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5] [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.18,C.10] [NamespaceReference] std @ ./Code2/src/main.cpp[L.18,C.5] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.15] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.15] [UnexposedExpression] @ ./Code2/src/main.cpp[L.18,C.18] [StringLiteral] "a : " @ ./Code2/src/main.cpp[L.18,C.18] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.25] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.25] [CallExpression] GetA @ ./Code2/src/main.cpp[L.18,C.28] [MemberReferenceExpression] GetA @ ./Code2/src/main.cpp[L.18,C.42] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.18,C.28] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.49] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.49] [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.18,C.57] [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.18,C.57] [NamespaceReference] std @ ./Code2/src/main.cpp[L.18,C.52] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5] [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.20,C.10] [NamespaceReference] std @ ./Code2/src/main.cpp[L.20,C.5] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.15] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.15] [UnexposedExpression] @ ./Code2/src/main.cpp[L.20,C.18] [StringLiteral] "b : " @ ./Code2/src/main.cpp[L.20,C.18] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.25] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.25] [CallExpression] GetB @ ./Code2/src/main.cpp[L.20,C.28] [MemberReferenceExpression] GetB @ ./Code2/src/main.cpp[L.20,C.42] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.20,C.28] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.49] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.49] [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.20,C.57] [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.20,C.57] [NamespaceReference] std @ ./Code2/src/main.cpp[L.20,C.52] [CallExpression] PublicMethod @ ./Code2/src/main.cpp[L.22,C.5] [MemberReferenceExpression] PublicMethod @ ./Code2/src/main.cpp[L.22,C.19] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.22,C.5] [UnexposedExpression] a @ ./Code2/src/main.cpp[L.22,C.32] [DeclarationReferenceExpression] a @ ./Code2/src/main.cpp[L.22,C.32] [UnexposedExpression] b @ ./Code2/src/main.cpp[L.22,C.35] [DeclarationReferenceExpression] b @ ./Code2/src/main.cpp[L.22,C.35] [BinaryOperator] @ ./Code2/src/main.cpp[L.24,C.5] [DeclarationReferenceExpression] result @ ./Code2/src/main.cpp[L.24,C.5] [CallExpression] GetResult @ ./Code2/src/main.cpp[L.24,C.14] [MemberReferenceExpression] GetResult @ ./Code2/src/main.cpp[L.24,C.28] [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.24,C.14] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5] [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.26,C.10] [NamespaceReference] std @ ./Code2/src/main.cpp[L.26,C.5] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.15] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.15] [UnexposedExpression] @ ./Code2/src/main.cpp[L.26,C.18] [StringLiteral] "result : " @ ./Code2/src/main.cpp[L.26,C.18] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.30] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.30] [UnexposedExpression] result @ ./Code2/src/main.cpp[L.26,C.33] [DeclarationReferenceExpression] result @ ./Code2/src/main.cpp[L.26,C.33] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.40] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.40] [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.26,C.48] [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.26,C.48] [NamespaceReference] std @ ./Code2/src/main.cpp[L.26,C.43] [CallExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5] [UnexposedExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5] [DeclarationReferenceExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5] [IntegerLiteral] @ ./Code2/src/main.cpp[L.28,C.23] [FunctionDeclaration] RecursiveCallTest @ ./Code2/src/main.cpp[L.31,C.6] [ParmDeclaration] i @ ./Code2/src/main.cpp[L.31,C.28] [CompoundStatement] @ ./Code2/src/main.cpp[L.32,C.1] [IfStatement] @ ./Code2/src/main.cpp[L.33,C.5] [BinaryOperator] @ ./Code2/src/main.cpp[L.33,C.9] [UnexposedExpression] i @ ./Code2/src/main.cpp[L.33,C.9] [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.33,C.9] [IntegerLiteral] @ ./Code2/src/main.cpp[L.33,C.13] [CompoundStatement] @ ./Code2/src/main.cpp[L.34,C.5] [ReturnStatement] @ ./Code2/src/main.cpp[L.35,C.9] [CompoundStatement] @ ./Code2/src/main.cpp[L.38,C.5] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9] [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9] [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.39,C.14] [NamespaceReference] std @ ./Code2/src/main.cpp[L.39,C.9] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.19] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.19] [UnexposedExpression] @ ./Code2/src/main.cpp[L.39,C.22] [StringLiteral] "i : " @ ./Code2/src/main.cpp[L.39,C.22] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.29] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.29] [UnexposedExpression] i @ ./Code2/src/main.cpp[L.39,C.32] [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.39,C.32] [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.34] [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.34] [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.39,C.42] [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.39,C.42] [NamespaceReference] std @ ./Code2/src/main.cpp[L.39,C.37] [CallExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9] [UnexposedExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9] [DeclarationReferenceExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9] [BinaryOperator] @ ./Code2/src/main.cpp[L.41,C.27] [UnexposedExpression] i @ ./Code2/src/main.cpp[L.41,C.27] [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.41,C.27] [IntegerLiteral] @ ./Code2/src/main.cpp[L.41,C.31] AST Dump: [TranslationUnit] ./Code2/src/TestClass.cpp @ [Namespace] root @ ./Code2/inc/TestClass.h[L.3,C.11] [Namespace] sub1 @ ./Code2/inc/TestClass.h[L.5,C.15] [Namespace] sub2 @ ./Code2/inc/TestClass.h[L.7,C.19] [ClassDeclaration] TestClass @ ./Code2/inc/TestClass.h[L.9,C.19] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.11,C.13] [Constructor] TestClass @ ./Code2/inc/TestClass.h[L.12,C.17] [Destructor] ~TestClass @ ./Code2/inc/TestClass.h[L.13,C.17] [CXXMethod] PublicMethod @ ./Code2/inc/TestClass.h[L.15,C.22] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.15,C.39] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.15,C.46] [CXXMethod] GetA @ ./Code2/inc/TestClass.h[L.17,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.17,C.28] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.17,C.30] [UnexposedExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43] [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.17,C.37] [CXXMethod] GetB @ ./Code2/inc/TestClass.h[L.18,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.18,C.28] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.18,C.30] [UnexposedExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43] [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.18,C.37] [CXXMethod] GetResult @ ./Code2/inc/TestClass.h[L.19,C.21] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.19,C.33] [ReturnStatement] @ ./Code2/inc/TestClass.h[L.19,C.35] [UnexposedExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48] [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.19,C.42] [CXXMethod] SetA @ ./Code2/inc/TestClass.h[L.21,C.22] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.21,C.31] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.21,C.34] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.21,C.36] [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.21,C.42] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.21,C.36] [UnexposedExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48] [DeclarationReferenceExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48] [CXXMethod] SetB @ ./Code2/inc/TestClass.h[L.22,C.22] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.22,C.31] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.22,C.34] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.22,C.36] [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.22,C.42] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.22,C.36] [UnexposedExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48] [DeclarationReferenceExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48] [CXXMethod] SetResult @ ./Code2/inc/TestClass.h[L.23,C.22] [ParmDeclaration] result @ ./Code2/inc/TestClass.h[L.23,C.36] [CompoundStatement] @ ./Code2/inc/TestClass.h[L.23,C.44] [BinaryOperator] @ ./Code2/inc/TestClass.h[L.23,C.46] [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.23,C.52] [CXXThisExpression] @ ./Code2/inc/TestClass.h[L.23,C.46] [UnexposedExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63] [DeclarationReferenceExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.25,C.13] [CXXMethod] PrivateMethod @ ./Code2/inc/TestClass.h[L.26,C.21] [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.26,C.39] [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.26,C.46] [LastDeclaration] @ ./Code2/inc/TestClass.h[L.28,C.13] [FieldDeclaration] m_a @ ./Code2/inc/TestClass.h[L.29,C.21] [FieldDeclaration] m_b @ ./Code2/inc/TestClass.h[L.30,C.21] [FieldDeclaration] m_result @ ./Code2/inc/TestClass.h[L.31,C.21] [Namespace] root @ ./Code2/src/TestClass.cpp[L.3,C.11] [Namespace] sub1 @ ./Code2/src/TestClass.cpp[L.5,C.15] [Namespace] sub2 @ ./Code2/src/TestClass.cpp[L.7,C.19] [Constructor] TestClass @ ./Code2/src/TestClass.cpp[L.9,C.24] [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.9,C.13] [MemberReference] m_a @ ./Code2/src/TestClass.cpp[L.10,C.19] [IntegerLiteral] @ ./Code2/src/TestClass.cpp[L.10,C.23] [MemberReference] m_b @ ./Code2/src/TestClass.cpp[L.11,C.19] [IntegerLiteral] @ ./Code2/src/TestClass.cpp[L.11,C.23] [MemberReference] m_result @ ./Code2/src/TestClass.cpp[L.12,C.19] [IntegerLiteral] @ ./Code2/src/TestClass.cpp[L.12,C.28] [CompoundStatement] @ ./Code2/src/TestClass.cpp[L.13,C.13] [Destructor] ~TestClass @ ./Code2/src/TestClass.cpp[L.16,C.24] [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.16,C.13] [CompoundStatement] @ ./Code2/src/TestClass.cpp[L.17,C.13] [CXXMethod] PublicMethod @ ./Code2/src/TestClass.cpp[L.21,C.29] [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.21,C.18] [ParmDeclaration] a @ ./Code2/src/TestClass.cpp[L.21,C.46] [ParmDeclaration] b @ ./Code2/src/TestClass.cpp[L.21,C.53] [CompoundStatement] @ ./Code2/src/TestClass.cpp[L.22,C.13] [DeclarationStatement] @ ./Code2/src/TestClass.cpp[L.23,C.17] [VarDeclaration] result @ ./Code2/src/TestClass.cpp[L.23,C.21] [CallExpression] PrivateMethod @ ./Code2/src/TestClass.cpp[L.23,C.30] [MemberReferenceExpression] PrivateMethod @ ./Code2/src/TestClass.cpp[L.23,C.36] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.23,C.30] [UnexposedExpression] a @ ./Code2/src/TestClass.cpp[L.23,C.50] [DeclarationReferenceExpression] a @ ./Code2/src/TestClass.cpp[L.23,C.50] [UnexposedExpression] b @ ./Code2/src/TestClass.cpp[L.23,C.53] [DeclarationReferenceExpression] b @ ./Code2/src/TestClass.cpp[L.23,C.53] [CallExpression] SetResult @ ./Code2/src/TestClass.cpp[L.25,C.17] [MemberReferenceExpression] SetResult @ ./Code2/src/TestClass.cpp[L.25,C.23] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.25,C.17] [UnexposedExpression] result @ ./Code2/src/TestClass.cpp[L.25,C.33] [DeclarationReferenceExpression] result @ ./Code2/src/TestClass.cpp[L.25,C.33] [CXXMethod] PrivateMethod @ ./Code2/src/TestClass.cpp[L.28,C.28] [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.28,C.17] [ParmDeclaration] a @ ./Code2/src/TestClass.cpp[L.28,C.46] [ParmDeclaration] b @ ./Code2/src/TestClass.cpp[L.28,C.53] [CompoundStatement] @ ./Code2/src/TestClass.cpp[L.29,C.13] [CallExpression] SetA @ ./Code2/src/TestClass.cpp[L.30,C.17] [MemberReferenceExpression] SetA @ ./Code2/src/TestClass.cpp[L.30,C.23] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.30,C.17] [UnexposedExpression] a @ ./Code2/src/TestClass.cpp[L.30,C.28] [DeclarationReferenceExpression] a @ ./Code2/src/TestClass.cpp[L.30,C.28] [CallExpression] SetB @ ./Code2/src/TestClass.cpp[L.31,C.17] [MemberReferenceExpression] SetB @ ./Code2/src/TestClass.cpp[L.31,C.23] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.31,C.17] [UnexposedExpression] b @ ./Code2/src/TestClass.cpp[L.31,C.28] [DeclarationReferenceExpression] b @ ./Code2/src/TestClass.cpp[L.31,C.28] [ReturnStatement] @ ./Code2/src/TestClass.cpp[L.33,C.17] [BinaryOperator] @ ./Code2/src/TestClass.cpp[L.33,C.24] [UnexposedExpression] m_a @ ./Code2/src/TestClass.cpp[L.33,C.30] [MemberReferenceExpression] m_a @ ./Code2/src/TestClass.cpp[L.33,C.30] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.33,C.24] [UnexposedExpression] m_b @ ./Code2/src/TestClass.cpp[L.33,C.42] [MemberReferenceExpression] m_b @ ./Code2/src/TestClass.cpp[L.33,C.42] [CXXThisExpression] @ ./Code2/src/TestClass.cpp[L.33,C.36] -------------------------------------- Invokation Tree: int root::sub1::sub2::TestClass::GetA() @ C:\Works\Test\Code2\inc\TestClass.h[L.17,C.21] int root::sub1::sub2::TestClass::GetB() @ C:\Works\Test\Code2\inc\TestClass.h[L.18,C.21] int root::sub1::sub2::TestClass::GetResult() @ C:\Works\Test\Code2\inc\TestClass.h[L.19,C.21] void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.21,C.22] void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.22,C.22] void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.23,C.22] main() @ C:\Works\Test\Code2\src\main.cpp[L.7,C.5] root::sub1::sub2::TestClass::TestClass() @ C:\Works\Test\Code2\src\main.cpp[L.9,C.33] void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\main.cpp[L.14,C.5] void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\main.cpp[L.16,C.5] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] <Behavior Definition Not Found...> std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] <Behavior Definition Not Found...> std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] <Behavior Definition Not Found...> int root::sub1::sub2::TestClass::GetA() @ C:\Works\Test\Code2\src\main.cpp[L.18,C.28] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] <Behavior Definition Not Found...> std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] <Behavior Definition Not Found...> std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] <Behavior Definition Not Found...> int root::sub1::sub2::TestClass::GetB() @ C:\Works\Test\Code2\src\main.cpp[L.20,C.28] void root::sub1::sub2::TestClass::PublicMethod(int, int) @ C:\Works\Test\Code2\src\main.cpp[L.22,C.5] int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30] void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17] void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17] void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17] int root::sub1::sub2::TestClass::GetResult() @ C:\Works\Test\Code2\src\main.cpp[L.24,C.14] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] <Behavior Definition Not Found...> std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] <Behavior Definition Not Found...> std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] <Behavior Definition Not Found...> RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.28,C.5] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9] <Recursive Call...> RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.31,C.6] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] <Behavior Definition Not Found...> RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9] <Recursive Call...> root::sub1::sub2::TestClass::TestClass() @ C:\Works\Test\Code2\src\TestClass.cpp[L.9,C.24] root::sub1::sub2::TestClass::~TestClass() @ C:\Works\Test\Code2\src\TestClass.cpp[L.16,C.24] void root::sub1::sub2::TestClass::PublicMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.21,C.29] int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30] void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17] void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17] void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17] int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.28,C.28] void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17] void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17] -------------------------------------- Cross References: root::sub1::sub2::TestClass::TestClass() [Call] @ C:\Works\Test\Code2\src\main.cpp[L.9,C.33] [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.12,C.17] [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.9,C.24] root::sub1::sub2::TestClass::~TestClass() [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.13,C.17] [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.16,C.24] void root::sub1::sub2::TestClass::PublicMethod(int, int) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.22,C.5] [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.15,C.22] [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.21,C.29] int root::sub1::sub2::TestClass::GetA() [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.28] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.17,C.21] int root::sub1::sub2::TestClass::GetB() [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.28] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.18,C.21] int root::sub1::sub2::TestClass::GetResult() [Call] @ C:\Works\Test\Code2\src\main.cpp[L.24,C.14] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.19,C.21] void root::sub1::sub2::TestClass::SetA(int) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.14,C.5] [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.21,C.22] void root::sub1::sub2::TestClass::SetB(int) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.16,C.5] [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.22,C.22] void root::sub1::sub2::TestClass::SetResult(int) [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17] [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.23,C.22] int root::sub1::sub2::TestClass::PrivateMethod(int, int) [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30] [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.26,C.21] [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.28,C.28] RecursiveCallTest(int) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.28,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9] [Declaration] @ C:\Works\Test\Code2\src\main.cpp[L.5,C.6] [Definition] @ C:\Works\Test\Code2\src\main.cpp[L.31,C.6] main() [Definition] @ C:\Works\Test\Code2\src\main.cpp[L.7,C.5] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9] std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5] [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
あとがき
ClangNetを使って以下の情報を出力する機能を実現するサンプルを紹介しました。
- 関数コールツリー
- 関数クロスリファレンス(宣言/定義/コール)情報
作成したサンプルの全ソースはGitHubにおいてあります。
GitHub : Link
今回作成したサンプルには、テンプレート関数など対応していないケースがあるので、次回はそれらに対応できるように拡張していきたいと思います。