Я экспериментирую с интерфейсом LLVM и был бы признателен за помощь в общих методах обработки AST. В каталоге CodeGen у меня есть метод, обрабатывающий экземпляр класса CodeGenFunction. Я знаю, что этот CGF содержит информацию о вызове функции foo() в моем исходном файле, поэтому, насколько я понимаю, этот вызов будет соответствовать узлу в AST.
Внутри этого метода CodeGen я хотел бы идентифицировать этот узел и обработать содержащуюся в нем информацию. Когда я дамп всего AST для моего исходного файла, я могу определить интересующий меня узел, но мне нужен более методичный способ обхода AST.
В моем методе CodeGen я определил переменную Expr, и после ее сохранения в stderr я вижу что-то вроде:
MemberExpr /*address*/ '<bound member function type>' -> foo /*address*/
`-ImplicitCastExpr /*address*/ 'class Base *' <UncheckedDerivedToBase> (Base)>
`-ImplicitCastExpr /*address*/ 'Derived1 *' <LValueToRValue>
`-DeclRefExpr /*address*/ 'Derived1 *' lvalue ParmVar /*address*/ 'obj1' 'Derived1 *'
Я хотел бы получить доступ и обработать строку, начинающуюся с -DeclRefExpr, но не знаю, как это сделать. Вся помощь очень ценится!
Кроме того, я все еще новичок в этом, поэтому, если я не думаю об этом правильно, пожалуйста, дайте мне знать. Любые объяснения приветствуются!





В качестве предварительного условия для прохождения AST следует начать с понимание самой конструкции.
В документации Clang есть две обзорные статьи:
Знакомство с Clang AST есть немного информации плюс ссылка на разговор, который сейчас несколько старый, но все еще в основном точный.
В Руководстве по внутреннему устройству Clang CFE, есть раздел Библиотека АСТ, который идет гораздо более подробно, хотя организация и выбор тем для освещения немного бессвязный.
После прочтения вышеизложенного я рекомендую распечатать AST для небольших
примеры, скормив их
clang -fsyntax-only -Xclang -ast-dump filename.cc, который производит
вывод аналогичен дампу, который вы показали для MemberExpr. Затем каждый
классов узлов AST имеет собственную страницу документации API, для
пример:
Я считаю, что лучше всего работает https://clang.llvm.org/doxygen/namespaceclang.html а затем напишите текст найти интересующее имя.
Однако документация API охватывает только общедоступные методы, тогда как что вы действительно хотите понять, так это личные данные. Для этого вы нужно открыть или перейти к исходному коду («Еще», затем строка номер файла определения) и посмотрите личные данные декларации. Частные данные — это «основная истина» того, что AST узел содержит и часто содержит полезные комментарии, но, к сожалению, ни частные заявления, ни их комментарии не копируются в Выход доксигена.
Теперь мы можем применить эти знания. Вопрос в том, как ориентироваться
от MemberExpr до содержащегося DeclRefExpr. На основе дампа AST
вы показали, исходный исходный код был примерно таким:
// Preliminary declarations.
struct Base {
void foo();
};
struct Derived1 : Base {};
Derived1 *obj1;
void f()
{
obj1->foo();
//^^^^^^^^^ Expression whose AST we are examining.
}
Следуя документации и исходному коду, указанным выше, мы находим следующий путь доступа:
У MemberExpr есть частный член Stmt *Base, то есть obj1
в obj1->foo. Этот частный член извлекается путем вызова
getBase(), что дает Expr*. (Примечание: Expr получено из
Stmt.)
Мы можем опустить Expr* до ImplicitCastExpr*, используя
dyn_cast.
ImplicitCastExpr происходит от CastExpr, который имеет частное
элемент данных Stmt *Op, который не задокументирован, но взят из контекста и
его имя должно быть операндом. Этот член извлекается путем вызова
getSubExpr(), снова уступая Expr*.
Повторите для вложенных ImplicitCastExpr.
DeclRefExpr имеет частный член ValueDecl *D, который является
объявление obj1, и его можно получить с помощью getDecl().
Если поместить эти шаги в код, это будет что-то вроде (не проверено):
using namespace clang;
MemberExpr *memberExpr = ...;
ImplicitCastExpr *outerICE = dyn_cast<ImplicitCastExpr>(memberExpr->getBase());
ImplicitCastExpr *innerICE = dyn_cast<ImplicitCastExpr>(outerICE->getSubExpr());
DeclRefExpr *dre = dyn_cast<DeclRefExpr>(innerICE->getSubExpr());
ValueDecl *objValueDecl = dre->getDecl();
Конечно, при реальном анализе не следует слепо предполагать, что вы знаете структуру AST заранее. Вместо этого на каждом этапе вам необходимо проверьте тип вложенных узлов и обработайте их соответствующим образом.
Существует три основных способа проверить тип узла:
dyn_cast возвращает указатель, допускающий значение NULL, поэтому распространенной идиомой является if (auto derived = dyn_cast<DerivedType>(base)) {...}, которая входит в блок только тогда, когда base равно DerivedType.
isa, еще один шаблон из семейства dyn_cast, возвращает логическое значение (что по сути то же самое, что проверять, возвращает ли dyn_cast ненулевое значение).
Все корневые суперклассы AST (из которых три наиболее важных — Stmt, Decl и Type) имеют getKind() или аналогичный метод, возвращающий перечисление. Таким образом, еще одна распространенная идиома — использовать switch (base->getKind()) {...} и иметь один регистр для каждого типа узла AST. Имейте в виду, что перечислители соответствуют наиболее производному типу, поэтому, например, вам понадобится несколько операторов case, чтобы получить все типы, производные от CastExpr, тогда как dyn_cast и isa отлично работают с такими «промежуточными» классами. Под капотом dyn_cast звонит getKind().
Наконец, обратите внимание, что вы не можете использовать обычный C++ dynamic_cast. Исходный код Clang обычно компилируется с отключенным RTTI, и даже если вы измените это, некоторые классы AST не будут иметь виртуальных таблиц.
RecursiveASTVisitorВ зависимости от того, какую обработку вы выполняете, иногда бывает удобно использовать РекурсивныйASTVisitor для автоматического обхода дерева и распределения по типам узлов. Его неоднозначная ситуация, поскольку поначалу она обеспечивает множество удобных средств автоматизации, но может быть довольно сложно работать, если ваш анализ не подходит идеально в свою систему обхода.
Чтобы использовать этот метод, вы инициируете обход от MemberExpr и
затем в какой-то момент во время этого будет вызван VisitDeclRefExpr. Видеть
Как написать ASTFrontendActions на основе RecursiveASTVisitor
для получения подробной информации. Если этого достаточно, отлично! В противном случае, и особенно если вы
намерены выполнять генерацию кода (что часто необходимо делать в
определенном порядке и в разных контекстах), возможно, лучше придерживаться
с прямым перемещением для максимального контроля.
Другой метод проверки AST — запись Соответствующие выражения AST, которые позволяют написать шаблон, который затем сравнивается со всеми входные данные с соответствующими фрагментами, передаваемыми обработчику. Статья Учебное пособие по созданию инструментов с использованием LibTooling и LibASTMatchers обсуждает этот метод довольно подробно.
Этот метод не кажется подходящим для генератора кода, но он хорош для таких вещей, как поиск проблемных шаблонов кода.
Если вы попробуете путь с выражением соответствия, будет удобно проверить сопоставьте выражения, используя clang-запрос инструмент, который сам по себе является полезным общим инструментом поиска/grep AST.
Это очень полезно, спасибо за подробное объяснение!