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は
-
FunctionDeclParmDeclParmDecl
のようになる。各値は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-
CXXBaseSpecifierTypeRef
-
CXXBaseSpecifierTypeRef
-
のようになる。
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-
CXXBaseSpecifierTypeRef
-
CXXMethodCXXOverrideAttrParmDecl
-
のようになる。
メンバ関数の部分はFunctionDeclと同じ構造。
overrideとfinalはCXXMethodの子要素になる。
virtualやstatic等はCXXMethod_isVirtual等で確認できる。
メンバ変数
メンバ変数はFieldDeclとなる。
構造はVarDeclと同じ。
getCXXAccessSpecifierでアクセス指定子が分かる。