ClangTool, как получить расположение параметров шаблона в объявлении переменной? - PullRequest
1 голос
/ 05 июня 2019

Я использую 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. Этот тип кажется более удобным, поскольку мы можем получить TemplateArgumentLoc с помощью функции getArgumentArray.

Проблема в том, что объявление шаблона не вызывает этого посетителя. Я не совсем понимаю причину.

Попытка 3: получить VarDecl как TemplateSpecializationTypeLoc

Я также пытался получить VarDecl как TemplateSpecializationTypeLoc, но это не удалось.

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

Краткие справки

проблемный контрольный пример

Решение от Валерия работает практически идеально. Его решение может захватить местоположение для объявления переменной 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;
}

1 Ответ

2 голосов
/ 05 июня 2019

Первая попытка была ближе всего к тому, что вам нужно. Узел, который вы ищете, это 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!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...