Clang AST Matchers: как сопоставить лямбда-захваченные переменные? - PullRequest
1 голос
/ 10 апреля 2019

Как сопоставить переменные в лямбда-выражениях, которые определены вне лямбда-выражения и захвачены ссылкой?

Проблема, которую я пытаюсь решить: у меня есть система транзакций базы данных, код которой выглядит примерно так:this:

std::set<int> values;
auto f = [&](TransactionOp* op) -> Status {
  for (auto v : readColumn("values")) 
     values.insert(v);
  return Ok();
}
Status s = TransactionRunner::Run(f);

В приведенном выше коде есть небольшая ошибка, потому что f не очищает значения.TransactionRunner :: Run может вызывать f несколько раз, пока транзакция не будет успешной.Если f не очищает значения, то значения будут иметь значения мусора от предыдущих попыток.

Я пишу проверку clang-tidy, чтобы найти подобные ошибки и остановить новые записи.

Пока у меня есть что-то вроде:

cxxRecordDecl(isLambda(), hasDescendant(cxxMethodDecl(returns(hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(hasName("Status")))))), parameterCountIs(1), hasParameter(0, hasType(pointsTo(cxxRecordDecl(hasName("TransactionOp"))))), hasBody(compoundStmt(allOf(hasDescendant(cxxMemberCallExpr(on(declRefExpr(to(varDecl().bind("insertee")))), thisPointerType(cxxRecordDecl(hasName("set"))), callee(cxxMethodDecl(hasName("insert"))))), unless(hasDescendant(cxxMemberCallExpr(on(declRefExpr(to(equalsBoundNode("insertee")))), thisPointerType(cxxRecordDecl(hasName("set"))), callee(cxxMethodDecl(hasName("clear"))))))))))))

Выше будет найдена лямбда с правильной сигнатурой, в которой есть вставка набора, но нет явного вызова к тому же набору.

Iхотите, чтобы он не срабатывал на наборах, объявленных внутри лямбды.Поэтому я бы хотел, чтобы совпадение совпадало только в том случае, если набор захватывается лямбдой.

1 Ответ

0 голосов
/ 10 апреля 2019

Я нашел решение.

Я использую отрицательное совпадение (если только), чтобы сказать, что объявление переменной не является потомком тела лямбды.Это не совсем то, что я просил (определите, что переменная является захватом), но он будет соответствовать только захватам и глобальным переменным, поэтому он работает для моего варианта использования.

Вот весь мой подход:

cxxRecordDecl(isLambda(), hasDescendant(cxxMethodDecl(returns(hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(hasName("Status")))))), parameterCountIs(1), hasParameter(0, hasType(pointsTo(cxxRecordDecl(hasName("TransactionOp"))))), hasBody(compoundStmt(allOf(hasDescendant(cxxMemberCallExpr(on(declRefExpr(to(varDecl().bind("insertee")))), thisPointerType(cxxRecordDecl(hasName("set"))), callee(cxxMethodDecl(hasName("insert"))))), unless(hasDescendant(cxxMemberCallExpr(on(declRefExpr(to(equalsBoundNode("insertee")))), thisPointerType(cxxRecordDecl(hasName("set"))), callee(cxxMethodDecl(hasName("clear")))))), unless(hasDescendant(decl(equalsBoundNode("insertee"))))))))))

Интересно, где я связываю объявление набора, вставляемого в cxxMethodDecl:

cxxMethodDecl(on(declRefExpr(to(varDecl().bind("insertee")))), ...)

И затем говорю, что объявление не является потомком тела (поэтому оно должнобыть снаружи):

unless(hasDescendant(decl(equalsBoundNode("insertee")))))))

Надеюсь, это решение может сэкономить кому-то еще время.

...