2

如何匹配在 lambda 之外定义并通过引用捕获的 lambda 中的变量?

我要解决的问题:我有一个数据库事务系统,其代码如下所示:

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"))))))))))))

上面将找到一个具有正确签名的 lambda,其中包含一个集合插入,但没有明确调用同一集合。

我希望它不会在 lambda 内声明的集合上触发。所以我希望匹配器仅在集合被 lambda 捕获时才匹配。

4

1 回答 1

0

我找到了解决方案。

我使用否定匹配器(除非)来说明变量的声明不是 lambda 主体的后代。这并不完全符合我的要求(确定变量是捕获),但它只会匹配捕获和全局变量,因此它适用于我的用例。

这是我的整个匹配器:

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")))), ...)

然后说声明不是body的后代(所以必须在外面):

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

希望这个解决方案可以为其他人节省一些时间。

于 2019-04-10T18:56:39.230 回答