c++ - ClangTool如何在变量声明中获取模板参数的位置?
问题描述
我正在使用 aRecursiveASTVisitor
来捕获变量声明。对于如下模板,如何使用 clang 工具获取每个参数在源代码中的位置?
TMyTemplate<t1, t2> foo;
尝试1:创建VisitVarDecl
访客
使用这种方法,我能够识别变量是否是模板并将类型设为clang::TemplateSpecializationType
. 它允许我使用 迭代参数getArg
,但是返回类型 ( TemplateArgument
) 不实现getLocation
。
virtual bool VisitVarDecl(VarDecl *var) {
const TemplateSpecializationType *ts = var->getType()->getAs<TemplateSpecializationType>();
if( ts != nullptr ) {
for(uint32_t i=0; i< ts->getNumArgs(); i++) {
TemplateArgument arg = ts->getArg(i);
// I want to get arg.getLocation() - but no getLocation in TemplateArgument.
}
}
}
我尝试将类型转换为TemplateArgument
,TemplateArgumentLoc
但任何方法都会导致我进入一个虚拟位置(即:总是返回第 1 行和第 1 列,甚至是段错误)。
尝试2:使用VisitVarTemplateSpecializationDecl
访问者
在 Clang 中,类型VarTemplateSpecializationDecl
允许我将模板参数作为clang::TemplateArgumentListInfo
. 这种类型似乎更方便,因为我们可以 TemplateArgumentLoc
使用 function getArgumentArray
。
问题是模板声明不会触发此访问者。我不太明白其中的原因。
尝试3:获取VarDecl
为TemplateSpecializationTypeLoc
我也试过得到VarDecl
asTemplateSpecializationTypeLoc
但它失败了。
TemplateSpecializationTypeLoc loc = var->getTypeSourceInfo()->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
快速参考
- 我正在使用这个博客的参考代码,这里是完整的代码。
- clang::VarTemplateSpecializationDecl
- clang::TemplateArgumentListInfo
- clang::TemplateArgumentLoc
- clang::TemplateArgument
- clang::TemplateSpecializationType
麻烦的测试用例
Valeriy 提供的解决方案几乎可以完美运行。他的解决方案可以捕获变量声明的位置,TemplateType<ABC, XYZ> Decl;
但如果我明确命名空间:它会失败FOO::TemplateType<ABC, XYZ> Decl;
。
这是一个简单的测试用例。该变量Decl
在他的解决方案中找到,但Decl2
不是。由于auto Specialization = DeclarationTypeLoc.getAs<clang::TemplateSpecializationTypeLoc>()
某种原因,转换失败(返回 nullptr)。
namespace FOO {
class ABC {};
class XYZ {};
template <class T, class U> class TemplateType {};
}
using namespace FOO;
int main() {
TemplateType<ABC, XYZ> Decl;
FOO::TemplateType<ABC, XYZ> Decl2;
return 0;
}
解决方案
第一次尝试是最接近您需要的。您要查找的节点是VarDecl
及其类型位置。VarTemplateSpecializationDecl
是一个变量模板(C++14 特性)特化。
表示变量模板特化,它指的是具有给定模板参数集的变量模板。
变量模板特化表示变量模板的显式特化(如下例所示)和变量模板的隐式实例化。
Clang 区分类型和类型位置。类型更多的是一个抽象实体,而TypeLoc代表了一个类型在源代码中的实际入口。当您A
在代码中多次编写 type 时,它们都是不同的TypeLoc,但相同的Type。
以下是如何为您的声明获取TypeLoc的示例:
bool VisitVarDecl(clang::VarDecl *Decl) {
auto DeclarationTypeLoc = Decl->getTypeSourceInfo()->getTypeLoc();
if (auto Specialization =
DeclarationTypeLoc.getAs<clang::TemplateSpecializationTypeLoc>()) {
for (auto i : llvm::seq<unsigned>(0, Specialization.getNumArgs())) {
auto ArgumentLoc = Specialization.getArgLoc(i);
auto &SM = Context.getSourceManager();
llvm::errs() << ArgumentLoc.getLocation().printToString(SM) << "\n";
}
}
return true;
}
在以下代码段上运行此代码
// main.cpp
template <class T, class U> class TemplateType {};
class ABC {};
class XYZ {};
int main() {
TemplateType<ABC, XYZ> Decl;
return 0;
}
产生这个结果:
.../main.cpp:9:16
.../main.cpp:9:21
注意 1:根据 Clang 的版本,您可能不使用Decl->getTypeSourceInfo()->getTypeLoc()
,但是Decl->getTypeLoc()
。
注意 2:小提示 - 不要将访问者功能设为虚拟。Clang 的访问者是CRTP。
更新 1
对于更新的测试片段,声明的TypeLoc不完全是TemplateSpecializationTypeLoc,而是围绕它的包装器。手动获取它可能很麻烦且容易出错,因此最好根据 Clang 的遍历器来实现它。
这是更新后的代码(请注意SpecificationArgumentVisitor是主要访问者内部的嵌套类):
class SpecificationArgumentVisitor
: public clang::RecursiveASTVisitor<SpecificationArgumentVisitor> {
public:
SpecificationArgumentVisitor(clang::ASTContext &Context)
: SM(Context.getSourceManager()) {}
bool VisitTemplateSpecializationTypeLoc(
clang::TemplateSpecializationTypeLoc Specialization) {
for (auto i : llvm::seq<unsigned>(0, Specialization.getNumArgs())) {
auto ArgumentLoc = Specialization.getArgLoc(i);
llvm::errs() << ArgumentLoc.getLocation().printToString(SM) << "\n";
}
return true;
}
private:
SourceManager &SM;
};
bool VisitVarDecl(clang::VarDecl *Decl) {
SpecificationArgumentVisitor ArgumentVisitor(Context);
ArgumentVisitor.TraverseDecl(Decl);
return true;
}
对于新的测试片段:
// main.cpp
namespace FOO {
class ABC {};
class XYZ {};
template <class T, class U> class TemplateType {};
} // namespace FOO
using namespace FOO;
int main() {
TemplateType<ABC, XYZ> Decl;
FOO::TemplateType<ABC, XYZ> Decl2;
return 0;
}
它产生以下输出:
.../main.cpp:13:16
.../main.cpp:13:21
.../main.cpp:14:21
.../main.cpp:14:26
注意 3:它是一个嵌套的RecursiveASTVisitor,因为在原始问题中,您只想遍历变量声明中的类型的模板特化。如果您希望它适用于每种情况,只需使用此访问功能并且只有一个访问者。
我希望这些信息有用。与 Clang 一起愉快地黑客攻击!
推荐阅读
- javascript - Highcharts stock-tools Clicks 导致 ASP.NET 回发?
- asp.net-mvc - 未捕获的类型错误:c(...).tScrollable 不是函数
- python - 分类编码后处理不平衡数据
- java - 使用 Apache Camel 进行 PGP 加密
- dynamics-crm-2011 - 为什么每次创建新记录时,我发现 ID 增加 2 不是正常方式?
- python - 替换字典列表中字典的特定项目?
- spring-boot - 有没有办法用假装弹簧客户端只记录身体?
- rest - 通过角度6访问rest-api时出现CORS错误
- batch-file - 根据命名约定删除所有目录
- delphi - Codesite 日志记录中的错误时间戳