Первая попытка была ближе всего к тому, что вам нужно. Узел, который вы ищете, это VarDecl
и его тип местоположения. VarTemplateSpecializationDecl
- это специализация шаблона переменной ( C ++ 14 ).
Представляет переменную шаблонную специализацию, которая относится к
шаблон переменной с заданным набором аргументов шаблона.
Специализации переменных шаблонов представляют оба явных
специализации переменных шаблонов, как в примере ниже, и
неявные экземпляры шаблонов переменных.
Clang проводит различие между типами и типами локаций. Тип является скорее абстрактной сущностью, в то время как TypeLoc представляет фактический вход типа в исходный код. Когда вы много раз пишете A
в своем коде, все это отличается TypeLoc s, но тот же Тип .
Вот пример того, как получить 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 , но обертка вокруг него. Добраться до него вручную может быть хлопотно и подвержено ошибкам, поэтому лучше реализовать его в терминах траверс Кланга.
Вот обновленный код (обратите внимание, что 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!