How can I match against variables in a lambda that are defined outside of the lambda and captured by reference?
The problem I'm trying to solve: I have a database transaction system whose code looks something like 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);
The code above has a subtle bug because f doesn't clear values. TransactionRunner::Run can call f multiple times until the transaction succeeds. If f doesn't clear values then values will have garbage values from previous attempts.
I'm writing a clang-tidy check to find bugs like this and stop new ones from being written.
So far I have something like:
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"))))))))))))
The above will find a lambda with the right signature that has a set insertion inside it but no clear call to the same set.
I want it to not trigger on sets declared inside the lambda. So I'd like the matcher to only match if the set is captured by the lambda.