Index: cfe/trunk/include/clang/AST/RecursiveASTVisitor.h =================================================================== --- cfe/trunk/include/clang/AST/RecursiveASTVisitor.h +++ cfe/trunk/include/clang/AST/RecursiveASTVisitor.h @@ -264,10 +264,12 @@ /// \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 @@ 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 @@ // 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(); Index: cfe/trunk/lib/Index/IndexBody.cpp =================================================================== --- cfe/trunk/lib/Index/IndexBody.cpp +++ cfe/trunk/lib/Index/IndexBody.cpp @@ -276,7 +276,8 @@ 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; Index: cfe/trunk/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp =================================================================== --- cfe/trunk/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp +++ cfe/trunk/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp @@ -161,10 +161,21 @@ 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 @@ "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; Index: cfe/trunk/unittests/Tooling/TestVisitor.h =================================================================== --- cfe/trunk/unittests/Tooling/TestVisitor.h +++ cfe/trunk/unittests/Tooling/TestVisitor.h @@ -43,6 +43,7 @@ Lang_C, Lang_CXX98, Lang_CXX11, + Lang_CXX14, Lang_OBJC, Lang_OBJCXX11, Lang_CXX = Lang_CXX98 @@ -55,6 +56,7 @@ 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 @@ /// \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 @@ }; 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 @@ } void ExpectFound() const { - EXPECT_TRUE(Found) + EXPECT_EQ(TimesExpected, TimesSeen) << "Expected \"" << Candidate.ExpectedName << "\" at " << Candidate.LineNumber << ":" << Candidate.ColumnNumber << PartialMatches; @@ -223,7 +231,8 @@ MatchCandidate Candidate; std::string PartialMatches; - bool Found; + unsigned TimesExpected; + unsigned TimesSeen; }; std::vector DisallowedMatches;