Skip to content

Commit cc7305a

Browse files
committed
[clang][dataflow] Add captured parameters to ReferencedDecls for lambda call operators.
This doesn't require that they be used in the operator's body, unlike other ReferencedDecls. This is most obviously different from captured local variables, which can be captured but will not appear in ReferencedDecls unless they appear in the operator's body. This difference simplifies the collection of the captured parameters, but probably could be eliminated if desirable.
1 parent c0192a0 commit cc7305a

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

clang/include/clang/Analysis/FlowSensitive/ASTOps.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ struct ReferencedDecls {
146146
/// Free functions and member functions which are referenced (but not
147147
/// necessarily called).
148148
llvm::DenseSet<const FunctionDecl *> Functions;
149+
/// Parameters of other functions, captured by reference by a lambda. This is
150+
/// empty except when ReferencedDecls are computed for a lambda's call
151+
/// operator.
152+
llvm::DenseSet<const ParmVarDecl *> LambdaCapturedParams;
149153
};
150154

151155
/// Returns declarations that are declared in or referenced from `FD`.

clang/lib/Analysis/FlowSensitive/ASTOps.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "clang/Analysis/FlowSensitive/ASTOps.h"
14+
#include "clang/AST/ASTLambda.h"
1415
#include "clang/AST/ComputeDependence.h"
1516
#include "clang/AST/Decl.h"
1617
#include "clang/AST/DeclBase.h"
@@ -281,6 +282,17 @@ ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
281282
Visitor.TraverseStmt(FD.getBody());
282283
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
283284
Visitor.traverseConstructorInits(CtorDecl);
285+
if (const auto *Method = dyn_cast<CXXMethodDecl>(&FD);
286+
Method && isLambdaCallOperator(Method)) {
287+
for (const auto &Capture : Method->getParent()->captures()) {
288+
if (Capture.capturesVariable()) {
289+
if (const auto *Param =
290+
dyn_cast<ParmVarDecl>(Capture.getCapturedVar())) {
291+
Result.LambdaCapturedParams.insert(Param);
292+
}
293+
}
294+
}
295+
}
284296

285297
return Result;
286298
}

clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,23 @@
88

99
#include "clang/Analysis/FlowSensitive/ASTOps.h"
1010
#include "TestingSupport.h"
11+
#include "clang/AST/Decl.h"
12+
#include "clang/AST/DeclCXX.h"
13+
#include "clang/ASTMatchers/ASTMatchers.h"
14+
#include "clang/Basic/LLVM.h"
15+
#include "clang/Frontend/ASTUnit.h"
16+
#include "clang/Tooling/Tooling.h"
1117
#include "gmock/gmock.h"
1218
#include "gtest/gtest.h"
1319
#include <memory>
20+
#include <string>
1421

1522
namespace {
1623

1724
using namespace clang;
1825
using namespace dataflow;
1926

27+
using ast_matchers::cxxMethodDecl;
2028
using ast_matchers::cxxRecordDecl;
2129
using ast_matchers::hasName;
2230
using ast_matchers::hasType;
@@ -107,4 +115,37 @@ TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) {
107115
UnorderedElementsAre(LocalDecl));
108116
}
109117

118+
TEST(ASTOpsTest, LambdaCaptures) {
119+
std::string Code = R"cc(
120+
void func(int CapturedByRef, int CapturedByValue, int NotCaptured) {
121+
int Local;
122+
auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {
123+
};
124+
}
125+
)cc";
126+
std::unique_ptr<ASTUnit> Unit =
127+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
128+
auto &ASTCtx = Unit->getASTContext();
129+
130+
ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);
131+
132+
auto *LambdaCallOp = selectFirst<CXXMethodDecl>(
133+
"l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx));
134+
ASSERT_NE(LambdaCallOp, nullptr);
135+
auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func"));
136+
ASSERT_NE(Func, nullptr);
137+
auto *CapturedByRefDecl = Func->getParamDecl(0);
138+
ASSERT_NE(CapturedByRefDecl, nullptr);
139+
auto *CapturedByValueDecl = Func->getParamDecl(1);
140+
ASSERT_NE(CapturedByValueDecl, nullptr);
141+
142+
EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty());
143+
ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp);
144+
EXPECT_THAT(ForLambda.LambdaCapturedParams,
145+
UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl));
146+
// Captured locals must be seen in the body for them to appear in
147+
// ReferencedDecls.
148+
EXPECT_THAT(ForLambda.Locals, IsEmpty());
149+
}
150+
110151
} // namespace

0 commit comments

Comments
 (0)