Skip to content

Commit e60151c

Browse files
committed
[AST] RecursiveASTVisitor visits lambda classes when implicit visitation is on.
Summary: This fixes ASTContext's parent map for nodes in such classes (e.g. operator()). https://bugs.llvm.org/show_bug.cgi?id=39949 This also changes the observed shape of the AST for implicit RAVs. - this includes AST MatchFinder: cxxRecordDecl() now matches lambda classes, functionDecl() matches the call operator, and the parent chain is body -> call operator -> lambda class -> lambdaexpr rather than body -> lambdaexpr. - this appears not to matter for the ASTImporterLookupTable builder - this doesn't matter for the other RAVs in-tree. In order to do this, we remove the TraverseLambdaBody hook. The problem is it's hard/weird to ensure this hook is called when traversing via the implicit class. There were just two users of this hook in-tree, who use it to skip bodies. I replaced these with explicitly traversing the captures only. Another approach would be recording the bodies when the lambda is visited, and then recognizing them later. I'd be open to suggestion on how to preserve this hook, instead. Reviewers: aaron.ballman, JonasToth Subscribers: cfe-commits, rsmith, jdennett Differential Revision: https://reviews.llvm.org/D56444 llvm-svn: 351047
1 parent 7d370a3 commit e60151c

File tree

5 files changed

+82
-46
lines changed

5 files changed

+82
-46
lines changed

clang/include/clang/AST/RecursiveASTVisitor.h

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -298,14 +298,6 @@ template <typename Derived> class RecursiveASTVisitor {
298298
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
299299
Expr *Init);
300300

301-
/// Recursively visit the body of a lambda expression.
302-
///
303-
/// This provides a hook for visitors that need more context when visiting
304-
/// \c LE->getBody().
305-
///
306-
/// \returns false if the visitation was terminated early, true otherwise.
307-
bool TraverseLambdaBody(LambdaExpr *LE, DataRecursionQueue *Queue = nullptr);
308-
309301
/// Recursively visit the syntactic or semantic form of an
310302
/// initialization list.
311303
///
@@ -936,13 +928,6 @@ RecursiveASTVisitor<Derived>::TraverseLambdaCapture(LambdaExpr *LE,
936928
return true;
937929
}
938930

939-
template <typename Derived>
940-
bool RecursiveASTVisitor<Derived>::TraverseLambdaBody(
941-
LambdaExpr *LE, DataRecursionQueue *Queue) {
942-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(LE->getBody());
943-
return true;
944-
}
945-
946931
// ----------------- Type traversal -----------------
947932

948933
// This macro makes available a variable T, the passed-in type.
@@ -2404,32 +2389,39 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, {
24042389

24052390
// Walk only the visible parts of lambda expressions.
24062391
DEF_TRAVERSE_STMT(LambdaExpr, {
2392+
// Visit the capture list.
24072393
for (unsigned I = 0, N = S->capture_size(); I != N; ++I) {
24082394
const LambdaCapture *C = S->capture_begin() + I;
24092395
if (C->isExplicit() || getDerived().shouldVisitImplicitCode()) {
24102396
TRY_TO(TraverseLambdaCapture(S, C, S->capture_init_begin()[I]));
24112397
}
24122398
}
24132399

2414-
TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
2415-
FunctionProtoTypeLoc Proto = TL.getAsAdjusted<FunctionProtoTypeLoc>();
2400+
if (getDerived().shouldVisitImplicitCode()) {
2401+
// The implicit model is simple: everything else is in the lambda class.
2402+
TRY_TO(TraverseDecl(S->getLambdaClass()));
2403+
} else {
2404+
// We need to poke around to find the bits that might be explicitly written.
2405+
TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
2406+
FunctionProtoTypeLoc Proto = TL.getAsAdjusted<FunctionProtoTypeLoc>();
24162407

2417-
if (S->hasExplicitParameters()) {
2418-
// Visit parameters.
2419-
for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I)
2420-
TRY_TO(TraverseDecl(Proto.getParam(I)));
2421-
}
2422-
if (S->hasExplicitResultType())
2423-
TRY_TO(TraverseTypeLoc(Proto.getReturnLoc()));
2408+
if (S->hasExplicitParameters()) {
2409+
// Visit parameters.
2410+
for (unsigned I = 0, N = Proto.getNumParams(); I != N; ++I)
2411+
TRY_TO(TraverseDecl(Proto.getParam(I)));
2412+
}
2413+
if (S->hasExplicitResultType())
2414+
TRY_TO(TraverseTypeLoc(Proto.getReturnLoc()));
24242415

2425-
auto *T = Proto.getTypePtr();
2426-
for (const auto &E : T->exceptions())
2427-
TRY_TO(TraverseType(E));
2416+
auto *T = Proto.getTypePtr();
2417+
for (const auto &E : T->exceptions())
2418+
TRY_TO(TraverseType(E));
24282419

2429-
if (Expr *NE = T->getNoexceptExpr())
2430-
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(NE);
2420+
if (Expr *NE = T->getNoexceptExpr())
2421+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(NE);
24312422

2432-
ReturnValue = TRAVERSE_STMT_BASE(LambdaBody, LambdaExpr, S, Queue);
2423+
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getBody());
2424+
}
24332425
ShouldVisitChildren = false;
24342426
})
24352427

clang/lib/CodeGen/CodeGenPGO.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,12 @@ struct MapRegionCounters : public RecursiveASTVisitor<MapRegionCounters> {
165165
// Blocks and lambdas are handled as separate functions, so we need not
166166
// traverse them in the parent context.
167167
bool TraverseBlockExpr(BlockExpr *BE) { return true; }
168-
bool TraverseLambdaBody(LambdaExpr *LE) { return true; }
168+
bool TraverseLambdaExpr(LambdaExpr *LE) {
169+
// Traverse the captures, but not the body.
170+
for (const auto &C : zip(LE->captures(), LE->capture_inits()))
171+
TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C));
172+
return true;
173+
}
169174
bool TraverseCapturedStmt(CapturedStmt *CS) { return true; }
170175

171176
bool VisitDecl(const Decl *D) {

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1153,7 +1153,12 @@ namespace {
11531153
bool TraverseDecl(Decl *D) { return true; }
11541154

11551155
// We analyze lambda bodies separately. Skip them here.
1156-
bool TraverseLambdaBody(LambdaExpr *LE) { return true; }
1156+
bool TraverseLambdaExpr(LambdaExpr *LE) {
1157+
// Traverse the captures, but not the body.
1158+
for (const auto &C : zip(LE->captures(), LE->capture_inits()))
1159+
TraverseLambdaCapture(LE, &std::get<0>(C), std::get<1>(C));
1160+
return true;
1161+
}
11571162

11581163
private:
11591164

clang/unittests/AST/ASTContextParentMapTest.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,16 @@ TEST(GetParents, RespectsTraversalScope) {
106106
EXPECT_THAT(Ctx.getParents(Foo), ElementsAre(DynTypedNode::create(TU)));
107107
}
108108

109+
TEST(GetParents, ImplicitLambdaNodes) {
110+
MatchVerifier<Decl> LambdaVerifier;
111+
EXPECT_TRUE(LambdaVerifier.match(
112+
"auto x = []{int y;};",
113+
varDecl(hasName("y"), hasAncestor(functionDecl(
114+
hasOverloadedOperatorName("()"),
115+
hasParent(cxxRecordDecl(
116+
isImplicit(), hasParent(lambdaExpr())))))),
117+
Lang_CXX11));
118+
}
119+
109120
} // end namespace ast_matchers
110121
} // end namespace clang

clang/unittests/Tooling/RecursiveASTVisitorTests/LambdaExpr.cpp

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,39 +17,62 @@ namespace {
1717
class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> {
1818
public:
1919
bool VisitLambdaExpr(LambdaExpr *Lambda) {
20-
PendingBodies.push(Lambda);
20+
PendingBodies.push(Lambda->getBody());
21+
PendingClasses.push(Lambda->getLambdaClass());
2122
Match("", Lambda->getIntroducerRange().getBegin());
2223
return true;
2324
}
24-
/// For each call to VisitLambdaExpr, we expect a subsequent call (with
25-
/// proper nesting) to TraverseLambdaBody.
26-
bool TraverseLambdaBody(LambdaExpr *Lambda) {
27-
EXPECT_FALSE(PendingBodies.empty());
28-
EXPECT_EQ(PendingBodies.top(), Lambda);
29-
PendingBodies.pop();
30-
return TraverseStmt(Lambda->getBody());
25+
/// For each call to VisitLambdaExpr, we expect a subsequent call to visit
26+
/// the body (and maybe the lambda class, which is implicit).
27+
bool VisitStmt(Stmt *S) {
28+
if (!PendingBodies.empty() && S == PendingBodies.top())
29+
PendingBodies.pop();
30+
return true;
3131
}
32-
/// Determine whether TraverseLambdaBody has been called for every call to
33-
/// VisitLambdaExpr.
34-
bool allBodiesHaveBeenTraversed() const {
35-
return PendingBodies.empty();
32+
bool VisitDecl(Decl *D) {
33+
if (!PendingClasses.empty() && D == PendingClasses.top())
34+
PendingClasses.pop();
35+
return true;
3636
}
37+
/// Determine whether parts of lambdas (VisitLambdaExpr) were later traversed.
38+
bool allBodiesHaveBeenTraversed() const { return PendingBodies.empty(); }
39+
bool allClassesHaveBeenTraversed() const { return PendingClasses.empty(); }
40+
41+
bool VisitImplicitCode = false;
42+
bool shouldVisitImplicitCode() const { return VisitImplicitCode; }
43+
3744
private:
38-
std::stack<LambdaExpr *> PendingBodies;
45+
std::stack<Stmt *> PendingBodies;
46+
std::stack<Decl *> PendingClasses;
3947
};
4048

4149
TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
4250
LambdaExprVisitor Visitor;
4351
Visitor.ExpectMatch("", 1, 12);
4452
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
4553
LambdaExprVisitor::Lang_CXX11));
54+
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
55+
EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
56+
}
57+
58+
TEST(RecursiveASTVisitor, LambdaInLambda) {
59+
LambdaExprVisitor Visitor;
60+
Visitor.ExpectMatch("", 1, 12);
61+
Visitor.ExpectMatch("", 1, 16);
62+
EXPECT_TRUE(Visitor.runOver("void f() { []{ []{ return; }; }(); }",
63+
LambdaExprVisitor::Lang_CXX11));
64+
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
65+
EXPECT_FALSE(Visitor.allClassesHaveBeenTraversed());
4666
}
4767

48-
TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
68+
TEST(RecursiveASTVisitor, VisitsLambdaExprAndImplicitClass) {
4969
LambdaExprVisitor Visitor;
70+
Visitor.VisitImplicitCode = true;
71+
Visitor.ExpectMatch("", 1, 12);
5072
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
5173
LambdaExprVisitor::Lang_CXX11));
5274
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
75+
EXPECT_TRUE(Visitor.allClassesHaveBeenTraversed());
5376
}
5477

5578
TEST(RecursiveASTVisitor, VisitsAttributedLambdaExpr) {

0 commit comments

Comments
 (0)