2011年11月30日水曜日

Clangでマクロの展開箇所を調べる

C++ソースコード上にマクロで表現されたDSLをclangで調べるために、マクロ展開箇所を調べたくなった。

マクロはclang::Preprocessorの範疇であるが、Preprocessorはマクロの展開情報を直接扱わない。clangにはPreprocessingRecordおよびPreprocessedEntryというクラスがあり、これらを利用する。

まず、Preprocessorが処理においてPreprocessingEntryを保存してくれるよう、PreprocessingRecordをセットアップする。
clang::InitializePreprocessor(pp, ppOpts, headerSearchOpts, frontendOpts);
pp.createPreprocessingRecord(true);

そうすると、プリプロセッシングの後でPreprocessorに設定されたPreprocessingRecordが、PreprocessedEntryを反復してくれる。PreprocessedEntryの派生クラスとしてMacroExpansionがあるので、キャストして情報を得ればよい。
clang::PreprocessingRecord* ppRecord = pp.getPreprocessingRecord();
clang::PreprocessingRecord::iterator ppr = ppRecord->begin();
clang::PreprocessingRecord::iterator eoppr = ppRecord->end();
for(; ppr != eoppr; ++ppr)
{
    if((*ppr)->getKind() == clang::PreprocessedEntity::MacroExpansionKind)
    {
        clang::MacroExpansion* expansion
            = llvm::dyn_cast<clang::MacroExpansion>(*ppr);
        if(expansion->getName()->getName() == "MY_MACRO")
        {
            clang::SourceRange range = expansion->getSourceRange();
            range.getBegin().dump(srcMgr);
        }
    }
}

2011年11月20日日曜日

CLangでクラスの中身を調べる:所属パッケージを調べる

前回までで、複数のcppをまとめて処理することが(たぶん)できないので、列挙されたクラスをあとで重複排除する必要があることがわかった。

clangにはリンク等の時点で判別する方法があると思うが、その機能を調べるのが非常にめんどくさい。そこで、各クラスの所属パッケージを調べておいて、その名前を文字列比較することで判定しようと思う。

自分の名前(クラス名)はCXXRecordDecl::getName()で取得できる。なので所属するパッケージ(名前空間)を取得できればよいはずである。

名前空間はclang::Type::dump()にてダンプされているので、そこで利用されているルーチンclang::TypePrinter::AppendScope()を調べてみた。以下のような実装である:

void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) {
  if (DC->isTranslationUnit()) return;
  AppendScope(DC->getParent(), Buffer);

  unsigned OldSize = Buffer.size();

  if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(DC)) {
    if (NS->getIdentifier())
      Buffer += NS->getNameAsString();
    else
      Buffer += "<anonymous>";
  } else if (ClassTemplateSpecializationDecl *Spec
               = dyn_cast<ClassTemplateSpecializationDecl>(DC)) {
    IncludeStrongLifetimeRAII Strong(Policy);
    const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs();
    std::string TemplateArgsStr
      = TemplateSpecializationType::PrintTemplateArgumentList(
                                            TemplateArgs.data(),
                                            TemplateArgs.size(),
                                            Policy);
    Buffer += Spec->getIdentifier()->getName();
    Buffer += TemplateArgsStr;
  } else if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) {
    if (TypedefNameDecl *Typedef = Tag->getTypedefNameForAnonDecl())
      Buffer += Typedef->getIdentifier()->getName();
    else if (Tag->getIdentifier())
      Buffer += Tag->getIdentifier()->getName();
  }

  if (Buffer.size() != OldSize)
    Buffer += "::";
}

本来の目的を考えると、上のルーチンをそのまま使えばいいのだが、それはできない。このTypePrinterは匿名の名前空間(namespace {} と宣言されているやつ)内で定義されており、かつAST/TypePrinter.cppにクラス定義があるのである。
なのでこのルーチンを解析して移植する。
std::string getPackagePath(clang::DeclContext* context)
{
    if(context == nullptr)
    {
        return "";
    }

    if(context->isTranslationUnit())
    {
        return "";
    }

    std::string path = getPackagePath(context->getParent());
    std::string::size_type oldLen = path.length();

    clang::NamespaceDecl* nsDecl = llvm::dyn_cast<clang::NamespaceDecl>(context);
    if(nsDecl == nullptr)
    {
        clang::TagDecl* tag = llvm::dyn_cast<clang::TagDecl>(context);
        if(tag != nullptr)
        {
            clang::TypedefNameDecl* tdef = tag->getTypedefNameForAnonDecl();
            if(tdef == nullptr)
            {
                path += tag->getIdentifier()->getName();
            }
            else
            {
                path += tdef->getIdentifier()->getName();
            }
        }
    }
    else
    {
        if(nsDecl->getIdentifier() == nullptr)
        {
            path += "<anonymous>";
        }
        else
        {
            path += nsDecl->getName();
        }
    }

    if(path.length() != oldLen)
    {
        path += "::";
    }

    return path;
}

これで通常の名前空間やインナークラスは処理できるようになった。TypePrinter側の実装でテンプレート用?ルーチンがあるのが気になる。

追記:
クラステンプレートの定義はASTContextによるclang::Typeの反復においては反復されない。

CLangでクラスの中身を調べる:複数のソースコードをまとめてパース・・・はできない

前回で単cppの重複定義を取り除くことができた。が、複数h/cppをパースさせた場合にどうなるのか心配になってきた。
なので今回は複数のh/cppを入力してみる。

cppをテキトーに追加してみる

ソースコードをメモリに展開するのはclang::SourceManagerである。しかしSourceManagerに(mainではない)ソースファイルを追加するであろうcreateFileID()は、インクルードパスのSourceLocationを要求する。createMainFileID()は要求しないのに。

この違いは何かと思って、createMainFileID()を使わずに以下のようにファイルを追加してみる。

srcMgr.createFileID(fileMgr.getFile("D:\\develop\\Clang\\SampleProj\\Package1\\Super.cpp"),
                    clang::SourceLocation(), clang::SrcMgr::C_User);

すると以下のようなエラーメッセージが出てしまう。

>fatal error: error opening file '<invalid loc>':

うーん、そうですか。ダメですか。でもcreateMainFileID()を複数回呼ぶのもダメですよね。

じゃあってことで、1つめのコードはcreateMainFileID()を、2つめ以降はcreateFileID()を使ってみる。

srcMgr.createMainFileID(fileMgr.getFile("D:\\develop\\Clang\\SampleProj\\Package1\\Super.cpp"));
srcMgr.createFileID(fileMgr.getFile("D:\\develop\\Clang\\SampleProj\\Package2\\Sub.cpp"),
                    clang::SourceLocation(), clang::SrcMgr::C_User);

実行すると、Sub.cppは解析結果に含まれない。

SourceLocationの生成を調べる

SourceLocationがデフォルト生成のままだとダメなのか、と思ってSourceLocationを調べると、
  • メンバ変数はunsigned ID1つだけ。
  • コンストラクタは引数なし。
  • IDを初期化できそうなメンバ関数はない。
となっている。つまりSourceLocation自身は初期化機能を提供せず、他のクラスが初期化するのだ。

これまで使ってきたクラスの中から生成しそうなやつをピックアップすると
  • FileManager
  • HeaderSearch
  • SourceManager
くらいなのだが、このうちFileManagerとHeaderSearchはSourceLocationを全く参照していない。なのでSourceManager.hをあさってみる。
  • clang::SrcMgrのインナークラスであるFileInfoにgetIncludeLoc()というAPIがある。
  • SourceManager自身にgetIncludeLoc()というAPIがある。
とりあえず後者を採用して実行・・・

clang::FileID mainId = srcMgr.createMainFileID(fileMgr.getFile("D:\\develop\\Clang\\SampleProj\\Package1\\Super.cpp"));
srcMgr.createFileID(fileMgr.getFile("D:\\develop\\Clang\\SampleProj\\Package2\\Sub.cpp"),
                    srcMgr.getIncludeLoc(mainId), clang::SrcMgr::C_User);

結果、やっぱりダメ。SourceLocationの問題ではないらしい。

複数cppをまとめてコンパイルできると誰が言った?

ここであることが頭をよぎる。gcc等では1回の起動で1つの.cppしか処理しない。clangのクラス群もそのように設計されているのではないか?
SourceManagerのMainという概念は、.cpp1ファイルのことを指すのではないか?

もしそうだとすると、非常に面倒なことである。ASTContextにおけるclang::Typeの反復の重複除去も意味がない。
そこで、1つのASTContextで複数の.cppを処理できるのか、試してみることにする。

まずSourceManagerのリセットである。調べたところ、MainFileIDはclearIDTable()メンバ関数でのみリセットされるようである。

ということで、以下のように実装してみる。

clang::IdentifierTable identifierTable(langOpts);
clang::SelectorTable selectorTable;
clang::Builtin::Context builtinContext;
clang::ASTContext astContext(langOpts, srcMgr, targetInfo, identifierTable,
                                selectorTable, builtinContext, 0);

clang::ASTConsumer astConsumer;
clang::Sema sema(pp, astContext, astConsumer);
sema.Initialize();

const char* targets[2] =
{
    "D:\\develop\\Clang\\SampleProj\\Package1\\Super.cpp",
    "D:\\develop\\Clang\\SampleProj\\Package2\\Sub.cpp"
};
for(const char** target = targets; target != (targets + 2); ++target)
{
    srcMgr.clearIDTables();
    srcMgr.createMainFileID(fileMgr.getFile(*target));
    clang::ParseAST(pp, &astConsumer, astContext);
}

// 構文解析結果を確認する。
clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
    if((*t)->getTypeClass() != clang::Type::Record)
    {
        continue;
    }

    clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
    if( (pDecl != nullptr) && pDecl->hasDefinition() && pDecl->isClass() )
    {
        std::printf("\n---------------------------------------------------------\n");

        (*t)->dump();
    }
}

実行した結果、次のようなエラーメッセージが。

Assertion failed: NumEnteredSourceFiles == 0 && "Cannot reenter the main file!", file D:\develop\Clang\llvm\tools\clang\lib\Lex\Preprocessor.cpp, line 388

うーむ、Preprocessorの時点で再利用できないのね。

CLangでクラスの中身を調べる:クラスの重複反復を抑制する

ちょっと時間が空いた。llvmとclangを3.0 RC4にして継続。

clang::ASTContext::types_begin()だと、クラスが重複して反復されてしまう。これをなんとかしたい。でもclang::Typeやclang::CXXRecordDeclにはoperator==()がないので、反復済みかどうかの判定では対処できない。

clang::Typeのメンバ関数に、getTypeClass()というものがあった。これを調べてみると重複されて反復されるclang::Typeは片方がRecordでもう片方がElaboratedであるようだ。
clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
    if((*t)->getTypeClass() == clang::Type::Elaborated)
    {
        continue;
    }

これでOKになった。clang::Type::Recordのみ通すようにすれば確実性は上がるだろう。
※問題は、これが何を意味するのかさっぱり理解していないところだ・・・。

ところで、今年12/3のboost勉強会(#7)ではclangやるセッションがいくつかあるのね・・・。カミさんと日程調整していたら参加が締め切られてorz。

2011年10月27日木曜日

メモ:頭にCXXがついているClangのクラス

clang/lib/AST/CXXABI.h(26):class CXXABI {
clang/include/clang/AST/CXXInheritance.h(69):class CXXBasePath : public SmallVector<CXXBasePathElement, 4> {
clang/include/clang/AST/CXXInheritance.h(117):class CXXBasePaths {
clang/include/clang/AST/CXXInheritance.h(361):class CXXFinalOverriderMap 
clang/include/clang/AST/CXXInheritance.h(365):class CXXIndirectPrimaryBaseSet
clang/include/clang/AST/DeclCXX.h(164):class CXXBaseSpecifier {
clang/include/clang/AST/DeclCXX.h(265):class CXXRecordDecl : public RecordDecl {
clang/include/clang/AST/DeclCXX.h(1315):class CXXMethodDecl : public FunctionDecl {
clang/include/clang/AST/DeclCXX.h(1447):class CXXCtorInitializer {
clang/include/clang/AST/DeclCXX.h(1693):class CXXConstructorDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclCXX.h(1924):class CXXDestructorDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclCXX.h(1993):class CXXConversionDecl : public CXXMethodDecl {
clang/include/clang/AST/DeclFriend.h(130):class CXXRecordDecl::friend_iterator {
clang/include/clang/AST/ExprCXX.h(48):class CXXOperatorCallExpr : public CallExpr {
clang/include/clang/AST/ExprCXX.h(91):class CXXMemberCallExpr : public CallExpr {
clang/include/clang/AST/ExprCXX.h(157):class CXXNamedCastExpr : public ExplicitCastExpr {
clang/include/clang/AST/ExprCXX.h(206):class CXXStaticCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(237):class CXXDynamicCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(271):class CXXReinterpretCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(302):class CXXConstCastExpr : public CXXNamedCastExpr {
clang/include/clang/AST/ExprCXX.h(327):class CXXBoolLiteralExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(357):class CXXNullPtrLiteralExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(386):class CXXTypeidExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(467):class CXXUuidofExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(549):class CXXThisExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(586):class CXXThrowExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(639):class CXXDefaultArgExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(725):class CXXTemporary {
clang/include/clang/AST/ExprCXX.h(753):class CXXBindTemporaryExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(796):class CXXConstructExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(928):class CXXFunctionalCastExpr : public ExplicitCastExpr {
clang/include/clang/AST/ExprCXX.h(984):class CXXTemporaryObjectExpr : public CXXConstructExpr {
clang/include/clang/AST/ExprCXX.h(1013):class CXXScalarValueInitExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1051):class CXXNewExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1258):class CXXDeleteExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(1380):class CXXPseudoDestructorExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2252):class CXXUnresolvedConstructExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2356):class CXXDependentScopeMemberExpr : public Expr {
clang/include/clang/AST/ExprCXX.h(2762):class CXXNoexceptExpr : public Expr {
clang/include/clang/AST/StmtCXX.h(25):class CXXCatchStmt : public Stmt {
clang/include/clang/AST/StmtCXX.h(61):class CXXTryStmt : public Stmt {
clang/include/clang/AST/StmtCXX.h(128):class CXXForRangeStmt : public Stmt {
clang/lib/AST/DeclarationName.cpp(31):class CXXSpecialName
clang/lib/AST/DeclarationName.cpp(49):class CXXOperatorIdName : public DeclarationNameExtra {
clang/lib/AST/DeclarationName.cpp(62):class CXXLiteralOperatorIdName
clang/lib/AST/ItaniumMangle.cpp(137):class CXXNameMangler {
clang/lib/CodeGen/CGBlocks.cpp(1363):class CXXByrefHelpers : public CodeGenModule::ByrefHelpers {
clang/include/clang/Sema/CXXFieldCollector.h(25):class CXXFieldCollector {
clang/include/clang/Sema/DeclSpec.h(60):class CXXScopeSpec {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(703):class CXXThisRegion : public TypedValueRegion {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(847):class CXXTempObjectRegion : public TypedValueRegion {
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h(876):class CXXBaseObjectRegion : public TypedValueRegion {

CLangでクラスの中身を調べる: クラスとそのメンバ関数の列挙

クラスのメンバ関数はclang::CXXRecordDecl::method_begin(), method_end()で反復できる。この反復でclang::CXXMethodDeclが得られる。
CXXMethodDeclはヘッダを見ると、各関数の属性、例えば可視性であったり、戻り値の型であったりを取得できるようだ。

とりあえず、自分で明示的に定義したメンバ関数をリストアップしてみよう。

コード:
clang::ASTContext::const_type_iterator t = astContext.types_begin();
clang::ASTContext::const_type_iterator eot = astContext.types_end();
for(; t != eot; ++t)
{
    clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
    if( (pDecl != nullptr) && pDecl->hasDefinition() && pDecl->isClass() )
    {
        pDecl->getDeclName().dump();
        //pDecl->getDeclName().printName(llvm::outs());

        // 基本クラスを確認する。
        std::printf(">>>> base class >>>>\n");
        clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
        clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
        for(; base != eobase; ++base)
        {
            // CXXRecordDecl::base_class_iterator は CXXBaseSpecifier* 。
            base->getType().dump();
        }
        std::printf("<<<<\n");

        // public なメンバ関数を列挙する。
        std::printf(">>>> method >>>>\n");
        clang::CXXRecordDecl::method_iterator method = pDecl->method_begin();
        clang::CXXRecordDecl::method_iterator eomethod = pDecl->method_end();
        for(; method != eomethod; ++method)
        {
            if( !method->isImplicit() && (method->getAccess() == clang::AS_public) )
            {
                method->dump();
            }
        }
        std::printf("<<<<\n");

        std::printf("\n---------------------------------------------------------\n");
    }
}

調べるcppはこんな感じ。
#include <cstdio>

namespace SuperNs
{
    class Super
    {
    public:
        Super()
        {
        }

        void super_public_method()
        {
        }
        
        virtual void super_public_virtual_method()
        {
        }

        virtual void super_public_virtual_method_nooverride()
        {
        }

    private:
        void super_private_method()
        {
        }
    };
}

namespace SubNs
{
    class Sub
        : public SuperNs::Super
    {
    public:
        Sub()
            : Super()
        {
        }

        void sub_public_method()
        {
        }
        
        virtual void super_public_virtual_method()
        {
        }

    private:
        void sub_private_method()
        {
        }
    };
}

int main(int, char**)
{
    std::printf("Hello,world\n");
    std::getchar();
    return 0;
}
結果:
_Lockit
>>>> base class >>>>
<<<<
>>>> method >>>>
_Lockit()_Lockit(int)void ~_Lockit() noexceptstatic void _Lockit_ctor(int)static void _Lockit_dtor(int)<<<<

---------------------------------------------------------
_Mutex
>>>> base class >>>>
<<<<
>>>> method >>>>
_Mutex()void ~_Mutex() noexceptvoid _Lock()void _Unlock()<<<<

---------------------------------------------------------
_Init_locks
>>>> base class >>>>
<<<<
>>>> method >>>>
_Init_locks()void ~_Init_locks() noexcept<<<<

---------------------------------------------------------
Super
>>>> base class >>>>
<<<<
>>>> method >>>>
Super() {
}

void super_public_method() {
}

virtual void super_public_virtual_method() {
}

virtual void super_public_virtual_method_nooverride() {
}

<<<<

---------------------------------------------------------
Sub
>>>> base class >>>>
: SuperNs::Super identifier
<<<<
>>>> method >>>>
Sub() : SuperNs::Super() {
}

void sub_public_method() {
}

virtual void super_public_virtual_method() {
}

<<<<

---------------------------------------------------------
Super
>>>> base class >>>>
<<<<
>>>> method >>>>
Super() {
}

void super_public_method() {
}

virtual void super_public_virtual_method() {
}

virtual void super_public_virtual_method_nooverride() {
}

<<<<

---------------------------------------------------------

なんでか、Superが2回反復されている(コピペミスではない)。その他は一応意図通り動いているようだ。ただ「親クラスのメンバ関数は子クラスで反復されない」のはちょっと困った。

C++コード表示の調整テスト中・・・

C++コードが見にくいので調整中。 以前からPrettyPrintを導入しようとしていたが、うまくいかずにモヤモヤしていた。どうもBloggerのテンプレートとして動的ビューを選択しているとうまくいかないようなので、シンプルに戻してみた。
#include <iostream>

#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"

#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "clang/Basic/LangOptions.h"
#include "clang/Basic/FileSystemOptions.h"

#include "clang/Basic/SourceManager.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Basic/FileManager.h"

#include "clang/Frontend/HeaderSearchOptions.h"
#include "clang/Frontend/Utils.h"

#include "clang/Basic/TargetOptions.h"
#include "clang/Basic/TargetInfo.h"

#include "clang/Lex/Preprocessor.h"
#include "clang/Frontend/PreprocessorOptions.h"
#include "clang/Frontend/FrontendOptions.h"
#include "clang/Frontend/CompilerInstance.h"

#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/Builtins.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/Sema/Sema.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Type.h"
#include "clang/AST/Decl.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Ownership.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/CXXInheritance.h"

#include "clang/Parse/Parser.h"

#include "clang/Parse/ParseAST.h"

int main()
{
    // 言語オプションをセットアップする。
    clang::LangOptions langOpts;
    langOpts.MicrosoftExt            = true;
    langOpts.MicrosoftMode            = true;
    langOpts.CPlusPlus                = true;
    langOpts.CPlusPlus0x            = true;
    langOpts.Bool                    = true;
    langOpts.GNUMode                = false;
    langOpts.GNUKeywords            = false;
    langOpts.LaxVectorConversions    = false;
    langOpts.Exceptions                = true;
    langOpts.CXXExceptions            = true;
    langOpts.MSBitfields            = true;
    langOpts.NeXTRuntime            = false;
    langOpts.MSCVersion                = _MSC_VER;

    // Diagnostic をセットアップする。
    clang::DiagnosticOptions diagOpts;
    clang::TextDiagnosticPrinter diagPrinter(llvm::outs(), diagOpts);
    diagPrinter.BeginSourceFile(langOpts, nullptr);

    llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs>
        diagIds(new clang::DiagnosticIDs());
    clang::DiagnosticsEngine diagEngine(diagIds, &diagPrinter, false);

    // 動作環境オプションをセットアップする。
    llvm::Triple triple;
    triple.setArch(llvm::Triple::x86);
    triple.setVendor(llvm::Triple::PC);
    triple.setOS(llvm::Triple::Win32);
    clang::TargetOptions targetOpts;
    targetOpts.Triple = triple.getTriple();
    clang::TargetInfo* targetInfo
        = clang::TargetInfo::CreateTargetInfo(diagEngine, targetOpts);

    // ソースファイル管理をセットアップする。
    clang::FileSystemOptions fileSysOpts;
    clang::FileManager fileMgr(fileSysOpts);
    clang::SourceManager srcMgr(diagEngine, fileMgr);

    // ヘッダ検索をセットアップする。
    clang::HeaderSearch headerSearch(fileMgr);
    clang::HeaderSearchOptions headerSearchOpts;
    headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\include",
                                clang::frontend::Angled, false, false, false);
    headerSearchOpts.AddPath("C:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\PlatformSDK\\include",
                                clang::frontend::Angled, false, false, false);
    clang::ApplyHeaderSearchOptions(headerSearch, headerSearchOpts, langOpts,
                                    triple);

    // プリプロセッサをセットアップする。
    clang::CompilerInstance compiler;
    clang::Preprocessor pp(diagEngine, langOpts, targetInfo, srcMgr, headerSearch, compiler);
    clang::PreprocessorOptions ppOpts;
    clang::FrontendOptions frontendOpts;
    clang::InitializePreprocessor(pp, ppOpts, headerSearchOpts, frontendOpts);


    const clang::FileEntry *pFile = fileMgr.getFile("D:\\develop\\Clang\\temp.cpp");
    srcMgr.createMainFileID(pFile);

    clang::IdentifierTable identifierTable(langOpts);
    clang::SelectorTable selectorTable;
    clang::Builtin::Context builtinContext;
    clang::ASTContext astContext(langOpts, srcMgr, targetInfo, identifierTable,
                                    selectorTable, builtinContext, 0);

    clang::ASTConsumer astConsumer;
    clang::Sema sema(pp, astContext, astConsumer);
    sema.Initialize();

    clang::ParseAST(pp, &astConsumer, astContext);

    // 構文解析結果を確認する。
    clang::ASTContext::const_type_iterator t = astContext.types_begin();
    clang::ASTContext::const_type_iterator eot = astContext.types_end();
    for(; t != eot; ++t)
    {
        clang::CXXRecordDecl* pDecl = (*t)->getAsCXXRecordDecl();
        if( (pDecl != nullptr) && pDecl->hasDefinition() && pDecl->isClass() )
        {
            //pDecl->getDeclName().printName(llvm::outs());

            // 基本クラスを確認する。
            std::printf(">>>> base class >>>>\n");
            clang::CXXRecordDecl::base_class_iterator base = pDecl->bases_begin();
            clang::CXXRecordDecl::base_class_iterator eobase = pDecl->bases_end();
            for(; base != eobase; ++base)
            {
                // CXXRecordDecl::base_class_iterator は CXXBaseSpecifier* 。
                base->getType().dump();
            }
            std::printf("<<<<\n");

            // public なメンバ関数を列挙する。
            std::printf(">>>> method >>>>\n");
            clang::CXXRecordDecl::method_iterator method = pDecl->method_begin();
            clang::CXXRecordDecl::method_iterator eomethod = pDecl->method_end();
            for(; method != eomethod; ++method)
            {
                method->dump();
                std::printf("\n-#-#-#-#\n");
            }
            std::printf("<<<<\n");

            std::printf("\n---------------------------------------------------------\n");
        }
    }

    std::getchar();
    return 0;
}