Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/include/clang/Analysis/FlowSensitive/ASTOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ struct ReferencedDecls {
/// Free functions and member functions which are referenced (but not
/// necessarily called).
llvm::DenseSet<const FunctionDecl *> Functions;
/// Parameters of other functions, captured by reference by a lambda. This is
/// empty except when ReferencedDecls are computed for a lambda's call
/// operator.
llvm::DenseSet<const ParmVarDecl *> LambdaCapturedParams;
};

/// Returns declarations that are declared in or referenced from `FD`.
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Analysis/FlowSensitive/ASTOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//

#include "clang/Analysis/FlowSensitive/ASTOps.h"
#include "clang/AST/ASTLambda.h"
#include "clang/AST/ComputeDependence.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
Expand Down Expand Up @@ -281,6 +282,17 @@ ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
Visitor.TraverseStmt(FD.getBody());
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
Visitor.traverseConstructorInits(CtorDecl);
if (const auto *Method = dyn_cast<CXXMethodDecl>(&FD);
Method && isLambdaCallOperator(Method)) {
for (const auto &Capture : Method->getParent()->captures()) {
if (Capture.capturesVariable()) {
if (const auto *Param =
dyn_cast<ParmVarDecl>(Capture.getCapturedVar())) {
Result.LambdaCapturedParams.insert(Param);
}
}
}
}

return Result;
}
Expand Down
41 changes: 41 additions & 0 deletions clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,23 @@

#include "clang/Analysis/FlowSensitive/ASTOps.h"
#include "TestingSupport.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/LLVM.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Tooling/Tooling.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <memory>
#include <string>

namespace {

using namespace clang;
using namespace dataflow;

using ast_matchers::cxxMethodDecl;
using ast_matchers::cxxRecordDecl;
using ast_matchers::hasName;
using ast_matchers::hasType;
Expand Down Expand Up @@ -107,4 +115,37 @@ TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) {
UnorderedElementsAre(LocalDecl));
}

TEST(ASTOpsTest, LambdaCaptures) {
std::string Code = R"cc(
void func(int CapturedByRef, int CapturedByValue, int NotCaptured) {
int Local;
auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {
};
}
)cc";
std::unique_ptr<ASTUnit> Unit =
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
auto &ASTCtx = Unit->getASTContext();

ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);

auto *LambdaCallOp = selectFirst<CXXMethodDecl>(
"l", match(cxxMethodDecl(hasName("operator()")).bind("l"), ASTCtx));
ASSERT_NE(LambdaCallOp, nullptr);
auto *Func = cast<FunctionDecl>(findValueDecl(ASTCtx, "func"));
ASSERT_NE(Func, nullptr);
auto *CapturedByRefDecl = Func->getParamDecl(0);
ASSERT_NE(CapturedByRefDecl, nullptr);
auto *CapturedByValueDecl = Func->getParamDecl(1);
ASSERT_NE(CapturedByValueDecl, nullptr);

EXPECT_THAT(getReferencedDecls(*Func).LambdaCapturedParams, IsEmpty());
ReferencedDecls ForLambda = getReferencedDecls(*LambdaCallOp);
EXPECT_THAT(ForLambda.LambdaCapturedParams,
UnorderedElementsAre(CapturedByRefDecl, CapturedByValueDecl));
// Captured locals must be seen in the body for them to appear in
// ReferencedDecls.
EXPECT_THAT(ForLambda.Locals, IsEmpty());
}

} // namespace