Skip to content

Commit a755b29

Browse files
committed
[clang][dataflow]Add parameters of other functions to LambdaCapturedParams.
"Other functions" here are functions other than the FunctionDecl provided to `getReferencedDecls`. Adding the parameters to `LambdaCapturedParams` because the only known triggering circumstance is a synthetic Stmt used for dataflow analysis of lambda init-captures where the initializer references the parameter(s) of the function surrounding the LambdaExpr.
1 parent 0c2701f commit a755b29

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ struct ReferencedDecls {
154154
/// When analyzing a lambda's call operator, the set of all parameters (from
155155
/// the surrounding function) that the lambda captures. Captured local
156156
/// variables are already included in `Locals` above.
157+
///
158+
/// This set also includes any referenced parameters of functions other than
159+
/// the function whose body is targeted for visitation. When calling
160+
/// `getReferencedDecls(const Stmt& S)`, there is no such targeted function,
161+
/// so any referenced function parameters are included in this set. This
162+
/// supports the collection of ReferencedDecls from a DeclStmt constructed for
163+
/// lambda init-capture VarDecls for the purpose of performing a dataflow
164+
/// analysis on the declaration/initialization.
157165
llvm::SetVector<const ParmVarDecl *> LambdaCapturedParams;
158166
};
159167

clang/lib/Analysis/FlowSensitive/ASTOps.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,17 @@ static void insertIfFunction(const Decl &D,
183183
Funcs.insert(FD);
184184
}
185185

186+
static void insertIfParamOfNotThePrimaryFunction(
187+
const Decl &D, llvm::SetVector<const ParmVarDecl *> &Locals,
188+
const FunctionDecl *PrimaryFunction) {
189+
if (auto *PVD = dyn_cast<ParmVarDecl>(&D)) {
190+
if (!PrimaryFunction ||
191+
PVD->getParentFunctionOrMethod() != PrimaryFunction) {
192+
Locals.insert(PVD);
193+
}
194+
}
195+
}
196+
186197
static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
187198
// Use getCalleeDecl instead of getMethodDecl in order to handle
188199
// pointer-to-member calls.
@@ -200,8 +211,9 @@ static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {
200211

201212
class ReferencedDeclsVisitor : public AnalysisASTVisitor {
202213
public:
203-
ReferencedDeclsVisitor(ReferencedDecls &Referenced)
204-
: Referenced(Referenced) {}
214+
ReferencedDeclsVisitor(ReferencedDecls &Referenced,
215+
const FunctionDecl *PrimaryFunction)
216+
: Referenced(Referenced), PrimaryFunction(PrimaryFunction) {}
205217

206218
void traverseConstructorInits(const CXXConstructorDecl *Ctor) {
207219
for (const CXXCtorInitializer *Init : Ctor->inits()) {
@@ -235,6 +247,8 @@ class ReferencedDeclsVisitor : public AnalysisASTVisitor {
235247
insertIfGlobal(*E->getDecl(), Referenced.Globals);
236248
insertIfLocal(*E->getDecl(), Referenced.Locals);
237249
insertIfFunction(*E->getDecl(), Referenced.Functions);
250+
insertIfParamOfNotThePrimaryFunction(
251+
*E->getDecl(), Referenced.LambdaCapturedParams, PrimaryFunction);
238252
return true;
239253
}
240254

@@ -271,11 +285,13 @@ class ReferencedDeclsVisitor : public AnalysisASTVisitor {
271285

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

276292
ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
277293
ReferencedDecls Result;
278-
ReferencedDeclsVisitor Visitor(Result);
294+
ReferencedDeclsVisitor Visitor(Result, &FD);
279295
Visitor.TraverseStmt(FD.getBody());
280296
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))
281297
Visitor.traverseConstructorInits(CtorDecl);
@@ -307,7 +323,7 @@ ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {
307323

308324
ReferencedDecls getReferencedDecls(const Stmt &S) {
309325
ReferencedDecls Result;
310-
ReferencedDeclsVisitor Visitor(Result);
326+
ReferencedDeclsVisitor Visitor(Result, nullptr);
311327
Visitor.TraverseStmt(const_cast<Stmt *>(&S));
312328
return Result;
313329
}

clang/unittests/Analysis/FlowSensitive/ASTOpsTest.cpp

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "TestingSupport.h"
1111
#include "clang/AST/Decl.h"
1212
#include "clang/AST/DeclCXX.h"
13+
#include "clang/AST/DeclGroup.h"
14+
#include "clang/AST/Stmt.h"
1315
#include "clang/ASTMatchers/ASTMatchers.h"
1416
#include "clang/Basic/LLVM.h"
1517
#include "clang/Frontend/ASTUnit.h"
@@ -29,8 +31,11 @@ using ast_matchers::cxxRecordDecl;
2931
using ast_matchers::hasName;
3032
using ast_matchers::hasType;
3133
using ast_matchers::initListExpr;
34+
using ast_matchers::isInitCapture;
3235
using ast_matchers::match;
36+
using ast_matchers::parmVarDecl;
3337
using ast_matchers::selectFirst;
38+
using ast_matchers::varDecl;
3439
using test::findValueDecl;
3540
using testing::IsEmpty;
3641
using testing::UnorderedElementsAre;
@@ -118,9 +123,8 @@ TEST(ASTOpsTest, ReferencedDeclsLocalsNotParamsOrStatics) {
118123
TEST(ASTOpsTest, LambdaCaptures) {
119124
std::string Code = R"cc(
120125
void func(int CapturedByRef, int CapturedByValue, int NotCaptured) {
121-
int Local;
122-
auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {
123-
};
126+
int Local;
127+
auto Lambda = [&CapturedByRef, CapturedByValue, &Local](int LambdaParam) {};
124128
}
125129
)cc";
126130
std::unique_ptr<ASTUnit> Unit =
@@ -148,4 +152,32 @@ TEST(ASTOpsTest, LambdaCaptures) {
148152
EXPECT_THAT(ForLambda.Locals, IsEmpty());
149153
}
150154

155+
TEST(ASTOpsTest, LambdaInitCapture) {
156+
std::string Code = R"cc(
157+
void func(int I) {
158+
auto Lambda = [C = I]() {};
159+
}
160+
)cc";
161+
std::unique_ptr<ASTUnit> Unit =
162+
tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
163+
auto &ASTCtx = Unit->getASTContext();
164+
165+
ASSERT_EQ(ASTCtx.getDiagnostics().getClient()->getNumErrors(), 0U);
166+
167+
auto *IDecl = selectFirst<ParmVarDecl>(
168+
"i", match(parmVarDecl(hasName("I")).bind("i"), ASTCtx));
169+
170+
// Synthesize a temporary DeclStmt for the assignment of Q to
171+
// its initializing expression. This is an unusual pattern that does not
172+
// perfectly reflect the CFG or AST for declaration or assignment of an
173+
// init-capture, but is used for dataflow analysis, which requires a Stmt and
174+
// not just a VarDecl with an initializer.
175+
auto *CDecl = selectFirst<VarDecl>(
176+
"c", match(varDecl(isInitCapture()).bind("c"), ASTCtx));
177+
DeclStmt DS(DeclGroupRef(const_cast<VarDecl *>(CDecl)), CDecl->getBeginLoc(),
178+
CDecl->getEndLoc());
179+
EXPECT_THAT(getReferencedDecls(DS).LambdaCapturedParams,
180+
UnorderedElementsAre(IDecl));
181+
}
182+
151183
} // namespace

0 commit comments

Comments
 (0)