基于RecursiveASTVisitor
的ASTFrontendActions
.
创建用RecursiveASTVisitor
查找特定名字的CXXRecordDeclAST
节点的FrontendAction
.
创建FrontendAction
编写基于clang
的工具(如Clang
插件或基于LibTooling
的独立工具)时,常见入口
是允许在编译过程
中执行用户特定操作的FrontendAction
接口.
为了在ASTclang
上运行工具,提供了方便的负责执行操作的ASTFrontendAction
接口.你只需要实现对每个转换单元
返回一个ASTConsumer
的CreateASTConsumer
方法.
class FindNamedClassAction : public clang::ASTFrontendAction {
public:
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
return std::make_unique<FindNamedClassConsumer>();
}//FindNamedClassAction
};
创建ASTConsumer
ASTConsumer
是一个,不管如何生成的AST
,在AST
上编写
的通用操作接口
.ASTConsumer
提供了许多不同的入口,但在此,只需要用ASTContext
调用翻译单元
的HandleTranslationUnit
.
class FindNamedClassConsumer : public clang::ASTConsumer {
public:
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
//通过`RecursiveASTVisitor`遍历翻译单元声明,会访问`AST`中的所有节点.
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
private:
//`RecursiveASTVisitor`实现.
FindNamedClassVisitor Visitor;
};
使用RecursiveASTVisitor
现在已连接了,下一步是实现RecursiveASTVisitor
以从AST
中提取相关信息.
除了按值传递的TypeLoc
节点,RecursiveASTVisitor
为大多数AST
节点提供bool VisitNodeType(NodeType*)
形式的勾挂.只需要为相关节点类型
实现方法,就可以了.
首先编写
一个访问所有CXXRecordDecl
的RecursiveASTVisitor
.
class FindNamedClassVisitor
: public RecursiveASTVisitor<FindNamedClassVisitor> {
public:
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
//为了调试,转储`AST`节点,会显示已访问的节点.
Declaration->dump();
//返回值指示是否想继续访问.返回`假`以停止`AST`的遍历.
return true;
}
};
在RecursiveASTVisitor
的方法中,现在可用ClangAST
的全部功能
来深入感兴趣
部分.如,要查找带特定名字的所有类声明
,可检查全名
:
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
if (Declaration->getQualifiedNameAsString() == "n::m::C")
Declaration->dump();
return true;
}
访问SourceManager
和ASTContext
有关AST
的某些信息(如源位置和全局标识信息
)不在AST
节点自身中,而是在ASTContext
及其关联的源管理器
中存储.
要提取它们,需要传递ASTContext
进RecursiveASTVisitor
实现中.
调用CreateASTConsumer
时,CompilerInstance
可访问ASTContext
.因此,可从那里提取
,并把它交给新创建的FindNamedClassConsumer
:
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());
}
现在在RecursiveASTVisitor
中,可访问ASTContext
,可利用AST
节点,查找源位置等:
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
if (Declaration->getQualifiedNameAsString() == "n::m::C") {
//`getFullLoc`使用`ASTContext`的`SourceManager`来解析源位置,并分解为`行和列`部分.
FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());
if (FullLocation.isValid())
llvm::outs() << "Found declaration at "
<< FullLocation.getSpellingLineNumber() << ":"
<< FullLocation.getSpellingColumnNumber() << "\n";
}
return true;
}
组合在一起
如下:
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"
using namespace clang;
class FindNamedClassVisitor
: public RecursiveASTVisitor<FindNamedClassVisitor> {
public:
explicit FindNamedClassVisitor(ASTContext *Context)
: Context(Context) {
}
bool VisitCXXRecordDecl(CXXRecordDecl *Declaration) {
if (Declaration->getQualifiedNameAsString() == "n::m::C") {
FullSourceLoc FullLocation = Context->getFullLoc(Declaration->getBeginLoc());
if (FullLocation.isValid())
llvm::outs() << "Found declaration at "
<< FullLocation.getSpellingLineNumber() << ":"
<< FullLocation.getSpellingColumnNumber() << "\n";
}
return true;
}
private:
ASTContext *Context;
};
class FindNamedClassConsumer : public clang::ASTConsumer {
public:
explicit FindNamedClassConsumer(ASTContext *Context)
: Visitor(Context) {
}
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
}
private:
FindNamedClassVisitor Visitor;
};
class FindNamedClassAction : public clang::ASTFrontendAction {
public:
virtual std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
clang::CompilerInstance &Compiler, llvm::StringRef InFile) {
return std::make_unique<FindNamedClassConsumer>(&Compiler.getASTContext());
}
};
int main(int argc, char **argv) {
if (argc > 1) {
clang::tooling::runToolOnCode(std::make_unique<FindNamedClassAction>(), argv[1]);
}
}
在FindClassDecls.cpp
文件中存储
它,并创建以下CMakeLists.txt
来链接它:
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_executable(find-class-decls FindClassDecls.cpp)
target_link_libraries(find-class-decls
PRIVATE
clangAST
clangBasic
clangFrontend
clangSerialization
clangTooling
)
对代码片运行此工具时,输出找到的n::m::C
类的所有声明
:
$ ./bin/find-class-decls "namespace n { namespace m { class C {}; } }"
在1:29
找到声明