]> granicus.if.org Git - clang/commitdiff
Visit lambda capture inits from RecursiveASTVisitor::TraverseLambdaCapture().
authorMartin Bohme <mboehme@google.com>
Wed, 17 Aug 2016 14:59:53 +0000 (14:59 +0000)
committerMartin Bohme <mboehme@google.com>
Wed, 17 Aug 2016 14:59:53 +0000 (14:59 +0000)
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
lib/Index/IndexBody.cpp
unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp
unittests/Tooling/TestVisitor.h

index 1812b5509450ad532b1e51ca95310e9b538331c3..84abbb762d64407ad111b7b6d3a5fbe9e991d2ed 100644 (file)
@@ -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<Derived>::TraverseConstructorInitializer(
 template <typename Derived>
 bool
 RecursiveASTVisitor<Derived>::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();
index 62f4e8802ae177737f33ada9393fbc086904bec6..0606873167d29119caae058fd4184271ed7f2917 100644 (file)
@@ -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;
 
index d39ca4b39a7f302bec823da89d11e5f892457cf2..5f1dd65222ba11e485b7bb7826423e1cf0c8508a 100644 (file)
@@ -161,10 +161,21 @@ TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
 
 class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> {
 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;
index f4a00394487bcfbe0e2e4ae460947246a0e0afe5..a762ec8b1453052d82e6d67cd6f5d0a6bfe0c89e 100644 (file)
@@ -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<MatchCandidate> DisallowedMatches;