From 70e216b530344da3d450434455198f28aee852b0 Mon Sep 17 00:00:00 2001 From: Martin Bohme Date: Wed, 17 Aug 2016 14:59:53 +0000 Subject: [PATCH] Visit lambda capture inits from RecursiveASTVisitor::TraverseLambdaCapture(). Summary: rL277342 made RecursiveASTVisitor visit lambda capture initialization expressions (these are the Exprs in LambdaExpr::capture_inits()). jdennett identified two issues with rL277342 (see comments there for details): - It visits initialization expressions for implicit lambda captures, even if shouldVisitImplicitCode() returns false. - It visits initialization expressions for init captures twice (because these were already traveresed in TraverseLambdaCapture() before rL277342) This patch fixes these issues and moves the code for traversing initialization expressions into TraverseLambdaCapture(). This patch also makes two changes required for the tests: - It adds Lang_CXX14 to the Language enum in TestVisitor. - It adds a parameter to ExpectedLocationVisitor::ExpectMatch() that specifies the number of times a match is expected to be seen. Reviewers: klimek, jdennett, alexfh Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D23204 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@278933 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/RecursiveASTVisitor.h | 23 +++++----- lib/Index/IndexBody.cpp | 3 +- .../RecursiveASTVisitorTestExprVisitor.cpp | 44 ++++++++++++++++++- unittests/Tooling/TestVisitor.h | 29 +++++++----- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 1812b55094..84abbb762d 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -264,10 +264,12 @@ public: /// \returns false if the visitation was terminated early, true otherwise. bool TraverseConstructorInitializer(CXXCtorInitializer *Init); - /// \brief Recursively visit a lambda capture. + /// \brief Recursively visit a lambda capture. \c Init is the expression that + /// will be used to initialize the capture. /// /// \returns false if the visitation was terminated early, true otherwise. - bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C); + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, + Expr *Init); /// \brief Recursively visit the body of a lambda expression. /// @@ -885,9 +887,12 @@ bool RecursiveASTVisitor::TraverseConstructorInitializer( template bool RecursiveASTVisitor::TraverseLambdaCapture(LambdaExpr *LE, - const LambdaCapture *C) { + const LambdaCapture *C, + Expr *Init) { if (LE->isInitCapture(C)) TRY_TO(TraverseDecl(C->getCapturedVar())); + else + TRY_TO(TraverseStmt(Init)); return true; } @@ -2261,13 +2266,11 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, { // Walk only the visible parts of lambda expressions. DEF_TRAVERSE_STMT(LambdaExpr, { - for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(), - CEnd = S->explicit_capture_end(); - C != CEnd; ++C) { - TRY_TO(TraverseLambdaCapture(S, C)); - } - for (Expr *Init : S->capture_inits()) { - TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(Init); + for (unsigned I = 0, N = S->capture_size(); I != N; ++I) { + const LambdaCapture *C = S->capture_begin() + I; + if (C->isExplicit() || getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseLambdaCapture(S, C, S->capture_init_begin()[I])); + } } TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc(); diff --git a/lib/Index/IndexBody.cpp b/lib/Index/IndexBody.cpp index 62f4e8802a..0606873167 100644 --- a/lib/Index/IndexBody.cpp +++ b/lib/Index/IndexBody.cpp @@ -276,7 +276,8 @@ public: return true; } - bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) { + bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C, + Expr *Init) { if (C->capturesThis() || C->capturesVLAType()) return true; diff --git a/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp b/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp index d39ca4b39a..5f1dd65222 100644 --- a/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp +++ b/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp @@ -161,10 +161,21 @@ TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) { class DeclRefExprVisitor : public ExpectedLocationVisitor { public: + DeclRefExprVisitor() : ShouldVisitImplicitCode(false) {} + + bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; } + + void setShouldVisitImplicitCode(bool NewValue) { + ShouldVisitImplicitCode = NewValue; + } + bool VisitDeclRefExpr(DeclRefExpr *Reference) { Match(Reference->getNameInfo().getAsString(), Reference->getLocation()); return true; } + +private: + bool ShouldVisitImplicitCode; }; TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) { @@ -191,14 +202,43 @@ TEST(RecursiveASTVisitor, VisitsCallExpr) { "void x(); void y() { x(); }")); } -TEST(RecursiveASTVisitor, VisitsLambdaCaptureInit) { +TEST(RecursiveASTVisitor, VisitsExplicitLambdaCaptureInit) { DeclRefExprVisitor Visitor; Visitor.ExpectMatch("i", 1, 20); EXPECT_TRUE(Visitor.runOver( - "void f() { int i; [i]{}; };", + "void f() { int i; [i]{}; }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(RecursiveASTVisitor, VisitsUseOfImplicitLambdaCapture) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("i", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [=]{ i; }; }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(RecursiveASTVisitor, VisitsImplicitLambdaCaptureInit) { + DeclRefExprVisitor Visitor; + Visitor.setShouldVisitImplicitCode(true); + // We're expecting the "i" in the lambda to be visited twice: + // - Once for the DeclRefExpr in the lambda capture initialization (whose + // source code location is set to the first use of the variable). + // - Once for the DeclRefExpr for the use of "i" inside the lambda. + Visitor.ExpectMatch("i", 1, 24, /*Times=*/2); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [=]{ i; }; }", DeclRefExprVisitor::Lang_CXX11)); } +TEST(RecursiveASTVisitor, VisitsLambdaInitCaptureInit) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("i", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "void f() { int i; [a = i + 1]{}; }", + DeclRefExprVisitor::Lang_CXX14)); +} + /* FIXME: According to Richard Smith this is a bug in the AST. TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) { DeclRefExprVisitor Visitor; diff --git a/unittests/Tooling/TestVisitor.h b/unittests/Tooling/TestVisitor.h index f4a0039448..a762ec8b14 100644 --- a/unittests/Tooling/TestVisitor.h +++ b/unittests/Tooling/TestVisitor.h @@ -43,6 +43,7 @@ public: Lang_C, Lang_CXX98, Lang_CXX11, + Lang_CXX14, Lang_OBJC, Lang_OBJCXX11, Lang_CXX = Lang_CXX98 @@ -55,6 +56,7 @@ public: case Lang_C: Args.push_back("-std=c99"); break; case Lang_CXX98: Args.push_back("-std=c++98"); break; case Lang_CXX11: Args.push_back("-std=c++11"); break; + case Lang_CXX14: Args.push_back("-std=c++14"); break; case Lang_OBJC: Args.push_back("-ObjC"); break; case Lang_OBJCXX11: Args.push_back("-ObjC++"); @@ -127,9 +129,12 @@ public: /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'. /// /// Any number of expected matches can be set by calling this repeatedly. - /// Each is expected to be matched exactly once. - void ExpectMatch(Twine Match, unsigned Line, unsigned Column) { - ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column)); + /// Each is expected to be matched 'Times' number of times. (This is useful in + /// cases in which different AST nodes can match at the same source code + /// location.) + void ExpectMatch(Twine Match, unsigned Line, unsigned Column, + unsigned Times = 1) { + ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times)); } /// \brief Checks that all expected matches have been found. @@ -200,14 +205,17 @@ protected: }; struct ExpectedMatch { - ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber) - : Candidate(Name, LineNumber, ColumnNumber), Found(false) {} + ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber, + unsigned Times) + : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times), + TimesSeen(0) {} void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) { if (Candidate.Matches(Name, Location)) { - EXPECT_TRUE(!Found); - Found = true; - } else if (!Found && Candidate.PartiallyMatches(Name, Location)) { + EXPECT_LT(TimesSeen, TimesExpected); + ++TimesSeen; + } else if (TimesSeen < TimesExpected && + Candidate.PartiallyMatches(Name, Location)) { llvm::raw_string_ostream Stream(PartialMatches); Stream << ", partial match: \"" << Name << "\" at "; Location.print(Stream, SM); @@ -215,7 +223,7 @@ protected: } void ExpectFound() const { - EXPECT_TRUE(Found) + EXPECT_EQ(TimesExpected, TimesSeen) << "Expected \"" << Candidate.ExpectedName << "\" at " << Candidate.LineNumber << ":" << Candidate.ColumnNumber << PartialMatches; @@ -223,7 +231,8 @@ protected: MatchCandidate Candidate; std::string PartialMatches; - bool Found; + unsigned TimesExpected; + unsigned TimesSeen; }; std::vector DisallowedMatches; -- 2.40.0