ASTImporter :合并ClangAST
这里,假设你对
介绍
如,想在同一个
而
这样,
导入算法
导入一个
此外,如果
导入算法必须
此外,必须发现
因此,要
//导入的伪代码(!): ErrorOrDecl Import(Decl *FromD) { Decl *ToDecl = nullptr; Found声明List = 用FromD相同名,在`to`环境中查找所有声明 for (auto FoundDecl : Found声明List) { if (StructurallyEquivalent声明(FoundDecl, FromD)) { ToDecl = FoundDecl; Mark FromD as imported; break; } else { Report ODR violation; return error; } } if (Found声明List is empty) { 导入依赖声明及to声明的类型 ToDecl = 在`to`环境创建新AST; Mark FromD as imported; } return ToDecl; }
如果两个
1,
2,
3,
4,
可把定义
应用接口
创建一个使用
std::unique_ptr<ASTUnit> ToUnit = buildASTFromCode("", "to.cc"); //空文件 std::unique_ptr<ASTUnit> FromUnit = buildASTFromCode( R"( class MyClass { int m1; int m2; }; )", "from.cc");
第一个
auto Matcher = cxxRecordDecl(hasName("MyClass")); auto *From = getFirstDecl<CXXRecordDecl>(Matcher, FromUnit);
现在创建
ASTImporter Importer(ToUnit->getASTContext(), ToUnit->getFileManager(), FromUnit->getASTContext(), FromUnit->getFileManager(), /*`MinimalImport=`*/true); llvm::Expected<Decl *> ImportedOrErr = Importer.Import(From);
if (!ImportedOrErr) { llvm::Error Err = ImportedOrErr.takeError(); llvm::errs() << "ERROR: " << Err << " "; consumeError(std::move(Err)); return 1; }
如果正确,则可得到
Decl *Imported = *ImportedOrErr; Imported->getTranslationUnitDecl()->dump();
因为在
要想得到成员,所以,用
if (llvm::Error Err = Importer.ImportDefinition(From)) { llvm::errs() << "ERROR: " << Err << " "; consumeError(std::move(Err)); return 1; } llvm::errs() << "Imported definition. "; Imported->getTranslationUnitDecl()->dump();
这一次,
如果把
ASTImporter Importer( .... /*`MinimalImport=`*/false);
用
放在一起:
#include "clang/AST/ASTImporter.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" using namespace clang; using namespace tooling; using namespace ast_matchers; template <typename Node, typename Matcher> Node *getFirstDecl(Matcher M, const std::unique_ptr<ASTUnit> &Unit) { auto MB = M.bind("bindStr"); //把`待匹配节点`绑定到`串键`. auto MatchRes = match(MB, Unit->getASTContext()); //至少应该有一个匹配. assert(MatchRes.size() >= 1); //取第一个`匹配`及绑定节点. Node *Result = const_cast<Node *>(MatchRes[0].template getNodeAs<Node>("bindStr")); assert(Result); return Result; } int main() { std::unique_ptr<ASTUnit> ToUnit = buildASTFromCode("", "to.cc"); std::unique_ptr<ASTUnit> FromUnit = buildASTFromCode( R"( class MyClass { int m1; int m2; }; )", "from.cc"); auto Matcher = cxxRecordDecl(hasName("MyClass")); auto *From = getFirstDecl<CXXRecordDecl>(Matcher, FromUnit); ASTImporter Importer(ToUnit->getASTContext(), ToUnit->getFileManager(), FromUnit->getASTContext(), FromUnit->getFileManager(), /*`MinimalImport=`*/true); llvm::Expected<Decl *> ImportedOrErr = Importer.Import(From); if (!ImportedOrErr) { llvm::Error Err = ImportedOrErr.takeError(); llvm::errs() << "ERROR: " << Err << " "; consumeError(std::move(Err)); return 1; } Decl *Imported = *ImportedOrErr; Imported->getTranslationUnitDecl()->dump(); if (llvm::Error Err = Importer.ImportDefinition(From)) { llvm::errs() << "ERROR: " << Err << " "; consumeError(std::move(Err)); return 1; } llvm::errs() << "Imported definition. "; Imported->getTranslationUnitDecl()->dump(); return 0; };
假定
add_clang_executable(astimporter-demo ASTImporterDemo.cpp) clang_target_link_libraries(astimporter-demo PRIVATE LLVMSupport clangAST clangASTMatchers clangBasic clangFrontend clangSerialization clangTooling )
然后,可
$ ninja astimporter-demo && ./bin/astimporter-demo
导入过程中的错误
一般,
修改之前编写的
int main() { std::unique_ptr<ASTUnit> ToUnit = buildASTFromCode( R"( //主模板 template <typename T> struct X {}; //显式特化 template<> struct X<int> { int i; }; )", "to.cc"); ToUnit->enableSourceFileDiagnostics(); std::unique_ptr<ASTUnit> FromUnit = buildASTFromCode( R"( //主模板 template <typename T> struct X {}; //显式特化 template<> struct X<int> { int i2; }; //字段不匹配:^^ )", "from.cc"); FromUnit->enableSourceFileDiagnostics(); auto Matcher = classTemplateSpecializationDecl(hasName("X")); auto *From = getFirstDecl<ClassTemplateSpecializationDecl>(Matcher, FromUnit); auto *To = getFirstDecl<ClassTemplateSpecializationDecl>(Matcher, ToUnit); ASTImporter Importer(ToUnit->getASTContext(), ToUnit->getFileManager(), FromUnit->getASTContext(), FromUnit->getFileManager(), /*`MinimalImport=`*/false); llvm::Expected<Decl *> ImportedOrErr = Importer.Import(From); if (!ImportedOrErr) { llvm::Error Err = ImportedOrErr.takeError(); llvm::errs() << "ERROR: " << Err << " "; consumeError(std::move(Err)); To->getTranslationUnitDecl()->dump(); return 1; } return 0; };
`to.cc:7:14:`警告:`"X<int>"`类型在不同的翻译单元中有不兼容的定义`[-Wodr]` `构X<int>{inti;` ^ `to.cc:7:27:`注意:此处的`字段名`叫`"i"` `构X<int>{inti;` ^ `from.cc:7:27:`注意:此处的`字段名`叫`"i2"` `structX<int>{inti2;` ^
注意,因为这些诊断,必须在
因为
错误传播
如果在导入
auto Matcher = fieldDecl(hasName("i2")); auto *From = getFirstDecl<FieldDecl>(Matcher, FromUnit);
本例中,可见
llvm::Expected<Decl *> ImportedOrErr = Importer.Import(From); if (!ImportedOrErr) { llvm::Error Err = ImportedOrErr.takeError(); consumeError(std::move(Err)); //检查是否也按错误标记`ClassTemplateSpecializationDecl`. auto *FromSpec = getFirstDecl<ClassTemplateSpecializationDecl>( classTemplateSpecializationDecl(hasName("X")), FromUnit); assert(Importer.getImportDeclErrorIfAny(FromSpec)); //顺便,也为`FieldDecl`设置错误. assert(Importer.getImportDeclErrorIfAny(From)); return 1; }
污染的AST
可能会在导入
用另一个
std::unique_ptr<ASTUnit> ToUnit = buildASTFromCode( R"( //主模板 template <typename T> struct X {}; //显式特化 template<> struct X<int> { int i; }; class Y; )", "to.cc"); ToUnit->enableSourceFileDiagnostics(); std::unique_ptr<ASTUnit> FromUnit = buildASTFromCode( R"( //主模板 template <typename T> struct X {}; //显式特化 template<> struct X<int> { int i2; }; //字段不匹配:^^ class Y { void f() { X<int> xi; } }; )", "from.cc"); FromUnit->enableSourceFileDiagnostics(); auto Matcher = cxxRecordDecl(hasName("Y")); auto *From = getFirstDecl<CXXRecordDecl>(Matcher, FromUnit); auto *To = getFirstDecl<CXXRecordDecl>(Matcher, ToUnit);
这一次,为
它们应
auto ImporterState = std::make_shared<ASTImporterSharedState>(); ASTImporter Importer(ToUnit->getASTContext(), ToUnit->getFileManager(), FromUnit->getASTContext(), FromUnit->getFileManager(), /*`MinimalImport=`*/false, ImporterState); llvm::Expected<Decl *> ImportedOrErr = Importer.Import(From); if (!ImportedOrErr) { llvm::Error Err = ImportedOrErr.takeError(); consumeError(std::move(Err)); //...但是已创建`节点`. auto *ToYDef = getFirstDecl<CXXRecordDecl>( cxxRecordDecl(hasName("Y"), isDefinition()), ToUnit); ToYDef->dump(); //在共享状态下,已为`"ToYDef"`设置了错误. Optional<ASTImportError> OptErr = ImporterState->getImportDeclErrorIfAny(ToYDef); assert(OptErr); return 1; }
如果看一下
不会
这与
因此,
使用-ast-merge 的Clang 前端操作
如果
C示例
考虑以下
//bar.h #ifndef BAR_H #define BAR_H int bar(); #endif /*`BAR_H`*/ //`bar.c` #include "bar.h" int bar() { return 41; } //`main.c` #include "bar.h" int main() { return bar(); }
为两个
$ clang -cc1 -emit-pch -o bar.ast bar.c $ clang -cc1 -emit-pch -o main.ast main.c
然后,如果只考虑
$ clang -cc1 -ast-merge bar.ast -ast-merge main.ast /dev/null -ast-dump
可检查函数的
函数的
前
现在,从合并的
$ clang -cc1 -ast-merge bar.ast -ast-merge main.ast /dev/null -emit-obj -o main.o Next, we may call the linker and execute the created binary file. $ clang -o a.out main.o $ ./a.out $ echo $ 41 $
C++ 示例
在
//`foo.h` #ifndef FOO_H #define FOO_H struct foo { virtual int fun(); }; #endif /*`FOO_H`*/ //`foo.cpp` #include "foo.h" int foo::fun() { return 42; } //`main.cpp` #include "foo.h" int main() { return foo().fun(); }
生成
$ clang++ -x c++-header -o foo.ast foo.cpp $ clang++ -x c++-header -o main.ast main.cpp $ clang++ -cc1 -x c++ -ast-merge foo.ast -ast-merge main.ast /dev/null -ast-dump $ clang++ -cc1 -x c++ -ast-merge foo.ast -ast-merge main.ast /dev/null -emit-obj -o main.o $ clang++ -o a.out main.o $ ./a.out $ echo $ 42 $