libclangのドキュメント等を読んで使い方を調べたり実際にプログラムを書いて色々確認したりしたのでメモ。
本文中では関数名の頭についてるclang_
は省略してあります。
CXIndex
翻訳単位(translation unit)の集まり。createIndex
で作ってdisposeIndex
で削除する。
CXIndex clang_createIndex(
int excludeDeclarationsFromPCH,
int displayDiagnostics
);
void clang_disposeIndex(
CXIndex index
);
CXTranslationUnit
clang -emit-ast
で生成されるastファイルか普通のソースファイルから作る。
disposeTranslationUnit
で削除。
void clang_disposeTranslationUnit(
CXTranslationUnit
);
astファイルから作る時
createTranslationUnit
かcreateTranslationUnit2
を使う。
最後に2
が付いている方はエラーコードを返す。
エラーコードが不要なら2
がない方を使えば良さそう。
CXTranslationUnit clang_createTranslationUnit(
CXIndex CIdx,
const char *ast_filename
);
enum CXErrorCode clang_createTranslationUnit2(
CXIndex CIdx,
const char *ast_filename,
CXTranslationUnit *out_TU
);
ソースファイルから作る時
parseTranslationUnit
かparseTranslationUnit2
かcreateTranslationUnitFromSourceFile
を使う。
どれもclangを実行する時と同じコマンドライン引数を渡せる。
コマンドライン引数にソースファイル名が含まれる時は2つ目の引数をNULL
にできる。
parseTranslationUnit
のunsigned options
にCXTranslationUnit_SkipFunctionBodies
を渡すと関数本体を無視できる。
CXTranslationUnit clang_parseTranslationUnit(
CXIndex CIdx,
const char *source_filename,
const char *const *command_line_args,
int num_command_line_args,
struct CXUnsavedFile *unsaved_files,
unsigned num_unsaved_files,
unsigned options
);
enum CXErrorCode clang_parseTranslationUnit2(
CXIndex CIdx,
const char *source_filename,
const char *const *command_line_args,
int num_command_line_args,
struct CXUnsavedFile *unsaved_files,
unsigned num_unsaved_files,
unsigned options,
CXTranslationUnit *out_TU
);
CXTranslationUnit clang_createTranslationUnitFromSourceFile(
CXIndex CIdx,
const char *source_filename,
int num_clang_command_line_args,
const char *const *clang_command_line_args,
unsigned num_unsaved_files,
struct CXUnsavedFile *unsaved_files
);
CXCursor
ある翻訳単位が持つASTのある要素を指す。
getTranslationUnitCursor
でASTの根を指すCXCursor
が得られる。
getCursorKind
で要素の種類が得られる。
根に対してgetCursorKind
を呼ぶとCXCursor_TranslationUnit
が返る。
CXCursor clang_getTranslationUnitCursor(
CXTranslationUnit
);
enum CXCursorKind clang_getCursorKind(
CXCursor
);
ASTの走査
走査を開始したい要素を指すCXCursor
を引数にしてvisitCursor
を呼び出す。
visitCursor
の3つ目の引数の値が2つ目の引数に渡すコールバック関数の最後の引数になる。
コールバックの戻り値で
- 走査をやめる(
CXChildVisit_Break
) - 次の要素に移る(
CXChildVisit_Continue
) - 子要素に移る(
CXChildVisit_Recurse
)
のいずれかの動作を行うことができる。
typedef enum CXChildVisitResult(* CXCursorVisitor )(
CXCursor cursor,
CXCursor parent,
CXClientData client_data
);
unsigned clang_visitChildren(
CXCursor parent,
CXCursorVisitor visitor,
CXClientData client_data
);
関数の構造
void f(int, float);
このプログラムのASTは
-
FunctionDecl
ParmDecl
ParmDecl
のようになる。各値はgetCursorKind
で得られるenum CXCursorKind
の各値の頭に付いてるCXCursor_
を省略したもの。
ParmDecl
の数はgetNumArguments
で得られる。
各ParmDecl
はCursor_getArgument
を使っても得られる。
getCursorType
でvoid (int, float)
、getCursorDisplayName
でf(int, float)
、getCursorSpelling
でf
が得られる。
int clang_Cursor_getNumArguments(
CXCursor C
);
CXCursor clang_Cursor_getArgument(
CXCursor C,
unsigned i
);
CXType clang_getCursorType(
CXCursor C
);
CXString clang_getCursorDisplayName(
CXCursor
);
CXString clang_getCursorSpelling(
CXCursor
);
変数の構造
変数はVarDecl
となる。
関数ポインタの場合、FunctoinDecl
と同様に子要素にParmDecl
ができる。
クラスの構造
class Base1{};
class Base2{};
class Derived : public Base1, private Base2{};
このようなプログラムがあった時、Derived
クラスのASTは
-
ClassDecl
-
CXXBaseSpecifier
TypeRef
-
CXXBaseSpecifier
TypeRef
-
のようになる。
CXXBaseSpecifier
を引数にしてgetCXXAccessSpecifier
を呼ぶと継承のアクセス指定子が得られる。
getTypeSpelling(getCursorType(CXXBaseSpecifierもしくはTypeRefなCXCursor))
とすると基底クラスの名前が得られる。
enum CX_CXXAccessSpecifier clang_getCXXAccessSpecifier(
CXCursor
);
CXString clang_getTypeSpelling(
CXType CT
);
メンバ関数
class Base{
public:
virtual void func(int);
};
class Derived : public Base{
public:
void func(int) override;
};
とすると、Derived
クラスのASTは
-
ClassDecl
-
CXXBaseSpecifier
TypeRef
-
CXXMethod
CXXOverrideAttr
ParmDecl
-
のようになる。
メンバ関数の部分はFunctionDecl
と同じ構造。
override
とfinal
はCXXMethod
の子要素になる。
virtual
やstatic
等はCXXMethod_isVirtual
等で確認できる。
メンバ変数
メンバ変数はFieldDecl
となる。
構造はVarDecl
と同じ。
getCXXAccessSpecifier
でアクセス指定子が分かる。