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 $