【问题标题】:ClangTool how to get location of template parameters in variable declaration?ClangTool如何在变量声明中获取模板参数的位置?
【发布时间】:2019-06-05 10:30:13
【问题描述】:

我正在使用RecursiveASTVisitor 来捕获变量声明。对于如下模板,如何使用 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。这种类型似乎更方便,因为我们可以通过函数getArgumentArray 得到TemplateArgumentLoc

问题在于模板声明不会触发此访问者。我不太明白原因。

尝试 3:VarDecl 设为 TemplateSpecializationTypeLoc

我也尝试将VarDecl 设为TemplateSpecializationTypeLoc,但失败了。

TemplateSpecializationTypeLoc loc = var->getTypeSourceInfo()->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();

快速参考

麻烦的测试用例

Valeriy 提供的解决方案几乎可以完美运行。他的解决方案可以捕获变量声明 TemplateType&lt;ABC, XYZ&gt; Decl; 的位置,但如果我明确命名空间:FOO::TemplateType&lt;ABC, XYZ&gt; Decl;,它将失败。

这是一个简单的测试用例。变量 Decl 在他的解决方案中找到,但 Decl2 不是。演员 auto Specialization = DeclarationTypeLoc.getAs&lt;clang::TemplateSpecializationTypeLoc&gt;() 因某种原因失败(返回 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;
}

【问题讨论】:

    标签: c++ clang


    【解决方案1】:

    第一次尝试最接近您的需要。您要查找的节点是VarDecl,它是类型位置。 VarTemplateSpecializationDecl 是一个变量模板 (C++14 feature) 特化。

    表示一个变量模板特化,指的是 具有给定模板参数集的变量模板。

    变量模板特化既代表显式 变量模板的特化,如下例所示,以及 变量模板的隐式实例化。

    Clang 区分类型和类型位置。 Type 更像是一个抽象实体,而 TypeLoc 代表了一个类型在源代码中的实际入口。当您在代码中多次写入 A 类型时,它们都是不同的 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;
    }
    

    在下面的sn-p上运行这段代码

    // 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-&gt;getTypeSourceInfo()-&gt;getTypeLoc(),而是Decl-&gt;getTypeLoc()

    注意 2:小提示 - 不要将访问者功能设为虚拟。 Clang 的访问者是CRTP


    更新 1

    对于更新后的测试 sn-p,声明的 TypeLoc 并不完全是 TemplateSpecializationTypeLoc,而是围绕它的一个包装器。手动获取它可能很麻烦且容易出错,因此最好根据 Clang 的遍历器来实现它。

    这是更新后的代码(请注意,SpecificationArgumentVisitor 是您的主要访问者的嵌套类inside):

    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;
    }
    

    对于新的测试sn-p:

    // 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 进行黑客攻击!

    【讨论】:

    • 对了,我相信你忘了return trueVisitVarDecl函数的末尾。
    • @rkioji 哦,是的。我忘了复制它... 哎呀 :) 我很高兴它有帮助
    • 有一个特定情况无法转换为TemplateSpecializationTypeLoc。当我们显式类型后面的命名空间(例如:std::vector)而不是(vector)(如果我们之前声明了using namespace)时,就会发生这种情况。我用麻烦的测试用例更新了我的问题。你有什么我可以寻找的起点吗?
    • 我没有在实际代码中检查它,但我希望在您的代码 sn-p 中找到一个围绕 TemplateSpecializationTypeLocElaboratedTypeLoc 包装器。
    • 我已经更新了在最通用情况下工作的答案。希望对你有帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-06
    • 2016-04-15
    • 1970-01-01
    • 2018-08-04
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多