LoginSignup
3
3

More than 5 years have passed since last update.

libTooling でソースコード(トークン)を文字列として取り出す

Posted at

そんなに難しくないですが、よく忘れるのでメモです。

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;
3
3
1

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