Skip to content
Closed
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
8 changes: 8 additions & 0 deletions clang/include/clang/Analysis/FlowSensitive/ASTOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ struct ReferencedDecls {
/// When analyzing a lambda's call operator, the set of all parameters (from
/// the surrounding function) that the lambda captures. Captured local
/// variables are already included in `Locals` above.
///
/// This set also includes any referenced parameters of functions other than
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wording feels general (and hence is a bit hard to parse). Could you be more specific, like

"of the surrounding function that introduces the lambda whose body is targeted for visitation"

? Or, is there actually more general functionality here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't affect the visitation of lambda bodies.

The code change is quite general here, as you can see from the limited use of conditionals. The expected impact is quite narrow, though, because the triggering circumstances are rare and very specific. I rewrote this block of comments to try and clarify. Does that help?

/// the function whose body is targeted for visitation. When calling
/// `getReferencedDecls(const Stmt& S)`, there is no such targeted function,
/// so any referenced function parameters are included in this set. This
/// supports the collection of ReferencedDecls from a DeclStmt constructed for
/// lambda init-capture VarDecls for the purpose of performing a dataflow
/// analysis on the declaration/initialization.
llvm::SetVector<const ParmVarDecl *> LambdaCapturedParams;
};

Expand Down
24 changes: 20 additions & 4 deletions clang/lib/Analysis/FlowSensitive/ASTOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ static void insertIfFunction(const Decl &D,
Funcs.insert(FD);
}

static void insertIfParamOfNotThePrimaryFunction(
const Decl &D, llvm::SetVector<const ParmVarDecl *> &Locals,
const FunctionDecl *PrimaryFunction) {
if (auto *PVD = dyn_cast<ParmVarDecl>(&D)) {
if (!PrimaryFunction ||
PVD->getParentFunctionOrMethod() != PrimaryFunction) {
Locals.insert(PVD);
}
}
}

static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
// Use getCalleeDecl instead of getMethodDecl in order to handle
// pointer-to-member calls.
Expand All @@ -200,8 +211,9 @@ static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {

class ReferencedDeclsVisitor : public AnalysisASTVisitor {
public:
ReferencedDeclsVisitor(ReferencedDecls &Referenced)
: Referenced(Referenced) {}
ReferencedDeclsVisitor(ReferencedDecls &Referenced,
const FunctionDecl *PrimaryFunction)
: Referenced(Referenced), PrimaryFunction(PrimaryFunction) {}

void traverseConstructorInits(const CXXConstructorDecl *Ctor) {
for (const CXXCtorInitializer *Init : Ctor->inits()) {
Expand Down Expand Up @@ -235,6 +247,8 @@ class ReferencedDeclsVisitor : public AnalysisASTVisitor {
insertIfGlobal(*E->getDecl(), Referenced.Globals);
insertIfLocal(*E->getDecl(), Referenced.Locals);
insertIfFunction(*E->getDecl(), Referenced.Functions);
insertIfParamOfNotThePrimaryFunction(
*E->getDecl(), Referenced.LambdaCapturedParams, PrimaryFunction);
return true;
}

Expand Down Expand Up @@ -271,11 +285,13 @@ class ReferencedDeclsVisitor : public AnalysisASTVisitor {

private:
ReferencedDecls &Referenced;
// May be null, if we are visiting a statement that is not a function body.
const FunctionDecl *const PrimaryFunction;
};

ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
ReferencedDecls Result;
ReferencedDeclsVisitor Visitor(Result);
ReferencedDeclsVisitor Visitor(Result, &FD);
Visitor.TraverseStmt(FD.getBody());
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
Visitor.traverseConstructorInits(CtorDecl);
Expand Down Expand Up @@ -307,7 +323,7 @@ ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {

ReferencedDecls getReferencedDecls(const Stmt &S) {
ReferencedDecls Result;
ReferencedDeclsVisitor Visitor(Result);
ReferencedDeclsVisitor Visitor(Result, nullptr);
Visitor.TraverseStmt(const_cast<Stmt *>(&S));
return Result;
}
Expand Down
38 changes: 35 additions & 3 deletions clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include "TestingSupport.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclGroup.h"
#include "clang/AST/Stmt.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Basic/LLVM.h"
#include "clang/Frontend/ASTUnit.h"
Expand All @@ -29,8 +31,11 @@ using ast_matchers::cxxRecordDecl;
using ast_matchers::hasName;
using ast_matchers::hasType;
using ast_matchers::initListExpr;
using ast_matchers::isInitCapture;
using ast_matchers::match;
using ast_matchers::parmVarDecl;
using ast_matchers::selectFirst;
using ast_matchers::varDecl;
using test::findValueDecl;
using testing::IsEmpty;
using testing::UnorderedElementsAre;
Expand Down Expand Up @@ -118,9 +123,8 @@ TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) {
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) {
};
int Local;
auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {};
}
)cc";
std::unique_ptr<ASTUnit> Unit =
Expand Down Expand Up @@ -148,4 +152,32 @@ TEST(ASTOpsTest, LambdaCaptures) {
EXPECT_THAT(ForLambda.Locals, IsEmpty());
}

TEST(ASTOpsTest, LambdaInitCapture) {
std::string Code = R"cc(
void func(int I) {
auto Lambda = [C = I]() {};
}
)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 *IDecl = selectFirst<ParmVarDecl>(
"i", match(parmVarDecl(hasName("I")).bind("i"), ASTCtx));

// Synthesize a temporary DeclStmt for the assignment of Q to
// its initializing expression. This is an unusual pattern that does not
// perfectly reflect the CFG or AST for declaration or assignment of an
// init-capture, but is used for dataflow analysis, which requires a Stmt and
// not just a VarDecl with an initializer.
auto *CDecl = selectFirst<VarDecl>(
"c", match(varDecl(isInitCapture()).bind("c"), ASTCtx));
DeclStmt DS(DeclGroupRef(const_cast<VarDecl *>(CDecl)), CDecl->getBeginLoc(),
CDecl->getEndLoc());
EXPECT_THAT(getReferencedDecls(DS).LambdaCapturedParams,
UnorderedElementsAre(IDecl));
}

} // namespace
Loading