そんなに難しくないですが、よく忘れるのでメモです。
clang::FunctionDecl など、clang::NamedDecl のサブクラスであれば、次のメンバ関数が使えます。
- getNameAsString()
- getQualifiedNameAsString()
ただしこれらはシンボルとしての名前なので、例えば関数が定義されている付近のソースコードを文字列として取り出したいときはもう少し低いレベルのクラスたちを使います。
前提条件
- clang 5.0.0
- RecursiveASTVisitor や DeclVisitor を使ってソースコードを解析するケースを想定しています。
登場人物
SourceLocation
翻訳単位 (TranslationUnit) 内での文字の位置を表すクラスです。
入力ソースファイルから引っ張られるヘッダファイルなどを全て結合した超巨大な文字列のインデックスのようなものです。
(内部的にはもっと効率良くメモリを使うためのIDですが)
SourceRange
SourceLocation 2つをメンバ変数として持ち、範囲(begin - end)を表すクラスです。
SourceManager
翻訳単位に関係するすべてのファイルを管理しているクラスです。
SourceLocation の位置と合わせて使うと、その場所の文字が得られる、という感じです。
LangOptions
字句解析のための設定クラスです。
純粋なC言語として解析するか?MSVCとの互換性を持たせるか?といった情報を持ちます。
Lexer
SourceRange, SourceManager, LangOptions を使って文字列をトークンに分割します。
SourceManager を得る
実体は CompilerInstance クラスが持っています。
ただ、それ以外にも参照を持っているクラスがたくさんいるので、アクセスしやすいところから選んでも良いです。
- clang::CompilerInstance::getSourceManager()
- clang::ASTContext::getSourceManager()
- clang::Preprocessor::getSourceManager()
例えば CompilerInstance のインスタンスは ASTConsumer のコンストラクタへ渡されてきます。
SourceLocation や SourceRange を得る
Token や Decl、MacroInfo など、何らかの定義を表すクラスは大抵 SourceRange を取得するメンバ関数を持っています。
関数名が統一されていないので注意が必要ですが、以下のような名前になっているのでエディタの入力補間などを利用しつつ探せると思います。
- getLocation()
- getLoc()
- getBeginLoc()
- getBegin()
- getLocStart()
- getEndLoc()
- getRange()
- getSourceRange()
- getDifinitionLoc()
文字列を取得する
Lexer クラスを使います。
指定された位置の字句を解析し、識別子や演算子といったトークンとして正しい範囲を文字列として返してくれます。
CompilerInstance& ci = /* ASTConsumer のコンストラクタなどから持ってくる */;
SourceManager& sm = ci.getSourceManager();
StringRef stringRef = Lexer::getSourceText(
CharSourceRange::getTokenRange(/* SourceLocation または SourceRange */),
ci.getSourceManager(),
ci.getLangOpts());
std::string str = stringRef.str();
マクロは展開してくれないようです。内部的にはトークンがマクロであるかをチェックするような処理があるけど・・・。https://github.com/llvm-mirror/clang/blob/master/lib/Lex/Lexer.cpp#L882-L883
Preprocessor を使うのかな?未調査。
[おまけ] SourceLocation のファイル名・行番号・列番号・オフセットを取得する
auto ploc = sm.getPresumedLoc(/* SourceLocation */);
auto eloc = sm.getDecomposedExpansionLoc(/* SourceLocation */);
/* ファイル名 */ = ploc.getFilename();
/* 行番号 */ = ploc.getLine();
/* 列番号 */ = ploc.getColumn();
/* ファイル先頭からのオフセット */ = eloc.second;