LINUX.ORG.RU

Проход по Clang AST

 , , ,


0

2

Всех приветствую.
Задача такая - найти бинарные операторы и проверить не кастится ли один из операндов signed<->unsigned. Например выражение int res = u + i. Для него AST будет иметь вид:

DeclStmt 0x56320fa43ef0 <line:4:2, col:17>
    | `-VarDecl 0x56320fa43dc8 <col:2, col:16> col:6 res 'int' cinit
    |   `-ImplicitCastExpr 0x56320fa43ed8 <col:12, col:16> 'int' <IntegralCast>
    |     `-BinaryOperator 0x56320fa43eb8 <col:12, col:16> 'unsigned int' '+'
    |       |-ImplicitCastExpr 0x56320fa43e70 <col:12> 'unsigned int' <LValueToRValue>
    |       | `-DeclRefExpr 0x56320fa43e30 <col:12> 'unsigned int' lvalue Var 0x56320fa43cf8 'u' 'unsigned int'
    |       `-ImplicitCastExpr 0x56320fa43ea0 <col:16> 'unsigned int' <IntegralCast>
    |         `-ImplicitCastExpr 0x56320fa43e88 <col:16> 'int' <LValueToRValue>
    |           `-DeclRefExpr 0x56320fa43e50 <col:16> 'int' lvalue Var 0x56320fa43c40 'i' 'int'

Пишу Visitor для парсинг шланговского дерева:

class Visitor
: public RecursiveASTVisitor<Visitor> {
public:
	explicit FindNamedClassVisitor(ASTContext *Context)
		: Context(Context) {}
	bool VisitBinaryOperator(BinaryOperator *bo) {
		clang::Expr* lhs = bo->getLHS();
		clang::Expr* rhs = bo->getRHS();
		auto lt = lhs->getType();
		auto rt = rhs->getType();

		return true;
	}
private:
	ASTContext *Context;
};

В lhs/rhs содержатся левая/правая часть выражения, в lt/rt - квалифицированный тип, но уже после каста, т.е. левый и правый тип совпадают (я ищу операции для встроенных типов). Мне нужно взять lhs и rhs и проверить там наличие signed<->unsigned (третья строка снизу в дампе дерева -ImplicitCastExpr 0x56320fa43ea0 col:16 ‘unsigned int’ ).

Проверил все методы у clang::Expr и родственников, ничего не подходит, вроде. Может нужно кастануть Expr в какой-то Layout compatible тип? А может я вовсе не так парсить начал, а надо идти по всем нодам и сохранять интересующие пути.

Запутался немного, как сделать, может кто уже копал шланг.

★★

Последнее исправление: pavlick (всего исправлений: 1)

Может нужно кастануть Expr в какой-то Layout compatible тип?

Надо кастовать используя llvm::dyn_cast<clang::ImplicitCastExpr>(rhs) и проверять на nullptr. Там у них своя система идентификации типов.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Спасибо, это оно.

Я еще когда обработчик вешал на обработку АСТ модуля:

class FindNamedClassConsumer : public clang::ASTConsumer {
public:
	void HandleTranslationUnit(clang::ASTContext &Context) override {
		Visitor.TraverseDecl(Context.getTranslationUnitDecl());
	}
};

Просто скопипастил с примеров. И вот странно - передаю декларации в TraverseDecl, в исходниках написано, что TraverseDecl вызывает *Decl методы в Визиторе, как бы не должно работать и передавать нужно какие-нибудь Statement’ы (в Контексте они есть), но работает, не понимаю почему.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

работает, не понимаю почему.

Так это вся единица трансляции со всеми классами и функциями. Определения функций также являются объявлениями, так что их тела обрабатываются. Классы включают объявления функций. Объявления переменных включают их инициализаторы.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

ок, понял. Значит визитор сам делает проход по нодам вглубь. Ну да, название RecursiveASTVisitor намекает.

pavlick ★★
() автор топика
Ответ на: комментарий от xaizek

Я пытаюсь обойти дерево вверх по направлению к родительским нодам, такой тестовый костылек:

    bool f_check_parents(Expr *e) {
        for (auto it = m_context->getParents(*e);
                ! it.empty();
                it = m_context->getParents(*it.begin())) {
            it.begin()->print(llvm::errs(), PrintingPolicy{{}});
            llvm::errs() << "\n\n\n";
        }
            return false;
    }

Всё обходится, но есть один вопрос - getParents() возвращает контейнер, получается, что там может быть более одного нода родительского. Я не могу понять как это возможно, всё что я видел - у каждого нода один родитель, у того еще родитель и так вплоть до нода «единица трансляции». Я всегда получаю контейнер с рамзером == 1.

В каких случаях возможен возврат контейнера с размером > 1? В каких конструкциях?

pavlick ★★
() автор топика
Ответ на: Бан в гугле — это не шутки от anonymous

Спасибо. Но ответ на троечку если честно. Вот такая конструкция даёт два родителя у оператора +

struct S{
	int i;
	int a[2];
};
int main(){
	S s{2,{4+4,5}};
}

При этом оба родителя - одинаковый лист (тот, что вложенный), но с разным адресом, оба эти родителя, в свою очередь, имеют одинакового родителя с одинаковым адресом - лист, который внешний. Я хз что это за финт такой и по какой из ветвей надо дерево обходить, слишком умная фраза, не осиливается:

RecursiveASTVisitor visits both the syntactic and semantic forms of InitListExpr, and the parent-child relationships are different between the two forms.
pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Возможно это про какие-то такие списки инициализации:

struct A { A(int) {} };

int
main()
{
    int i[4][2] = { 1, 2, 3, 4, 6, 7, 8 };
    A a[3] = { 1, 2, 3 };
}

Надо копаться с исходниках, может есть пример обработки родителей, который покажет как отсекать ненужное.

xaizek ★★★★★
()
Ответ на: комментарий от pavlick

Я хз что это за финт такой и по какой из ветвей надо дерево обходить

Почему ты спрашиваешь у нас? По какой тебе надо, по такой и обходи.

anonymous
()
Ответ на: комментарий от xaizek

А с таким кодом получилось аж 3 родителя у оператора + (массив сделал unsigned, с signed массивом родителя 2):

struct S{
		int i;
		unsigned a[2];
};
int main(){
		S s{2,{4+4,5}};
}

Дамп дерева. Потом распринтовал каждого родителя и их родителей рекурсивно, получилось три ветви. Все ветви начинают совпадать с нода 0x5620b6a47af0. Очевидно, что самая нормальная и логичная вторая ветвь (родитель с индексом 1). Когда массив был знаковый и родителя было 2, то ветвь порожденная родителем с индексом 1 тоже была годной.

В общем на бред похоже, но направшивается вывод: родитель один - бери с индексом 0, более 1 - бери с индексом 1.

pavlick ★★
() автор топика
Ответ на: комментарий от pavlick

Ты Ъ? Документацию принципиально не читаешь?

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.