This is an updated approach based on feedback: I generalized the pre-existing 
template ExpectedLocationVisitor and unified the two specific 
ConstructExprVisitor classes by adding a bool ShouldVisitImplicitCode -- that 
could be moved up into a base class if we wanted.

Hi rsmith, klimek,

http://llvm-reviews.chandlerc.com/D26

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D26?vs=56&id=60#differential-review-toc

Files:
  include/clang/AST/RecursiveASTVisitor.h
  unittests/Tooling/RecursiveASTVisitorTest.cpp
  unittests/Tooling/TestVisitor.h
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -799,7 +799,7 @@
   if (TypeSourceInfo *TInfo = Init->getTypeSourceInfo())
     TRY_TO(TraverseTypeLoc(TInfo->getTypeLoc()));
 
-  if (Init->isWritten())
+  if (Init->isWritten() || getDerived().shouldVisitImplicitCode())
     TRY_TO(TraverseStmt(Init->getInit()));
   return true;
 }
Index: unittests/Tooling/RecursiveASTVisitorTest.cpp
===================================================================
--- unittests/Tooling/RecursiveASTVisitorTest.cpp
+++ unittests/Tooling/RecursiveASTVisitorTest.cpp
@@ -385,4 +385,64 @@
       "int main() { Simple s; Simple t(s); }\n"));
 }
 
+/// \brief A visitor that optionally includes implicit code and matches
+/// CXXConstructExpr.
+///
+/// The name recorded for the match is the name of the class whose constructor
+/// is invoked by the CXXConstructExpr, not the name of the class whose
+/// constructor the CXXConstructExpr is contained in.
+class ConstructExprVisitor
+    : public ExpectedLocationVisitor<ConstructExprVisitor> {
+public:
+  ConstructExprVisitor() : ShouldVisitImplicitCode(false) {}
+
+  bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
+
+  void setShouldVisitImplicitCode(bool NewValue) {
+    ShouldVisitImplicitCode = NewValue;
+  }
+
+  bool VisitCXXConstructExpr(CXXConstructExpr* Expr) {
+    if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) {
+      if (const CXXRecordDecl* Class = Ctor->getParent()) {
+        Match(Class->getName(), Expr->getLocation());
+      }
+    }
+    return true;
+  }
+
+ private:
+  bool ShouldVisitImplicitCode;
+};
+
+TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) {
+  ConstructExprVisitor Visitor;
+  Visitor.setShouldVisitImplicitCode(true);
+  Visitor.ExpectMatch("WithCtor", 2, 8);
+  // Simple has a constructor that implicitly initializes 'w'.  Test
+  // that a visitor that visits implicit code visits that initialization.
+  // Note: Clang lazily instantiates implicit declarations, so we need
+  // to use them in order to force them to appear in the AST.
+  EXPECT_TRUE(Visitor.runOver(
+      "struct WithCtor { WithCtor(); }; \n"
+      "struct Simple { WithCtor w; }; \n"
+      "int main() { Simple s; }\n"));
+}
+
+// The same as CanVisitImplicitMemberInitializations, but checking that the
+// visits are omitted when the visitor does not include implicit code.
+TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
+  ConstructExprVisitor Visitor;
+  Visitor.setShouldVisitImplicitCode(false);
+  Visitor.DisallowMatch("WithCtor", 2, 8);
+  // Simple has a constructor that implicitly initializes 'w'.  Test
+  // that a visitor that skips implicit code skips that initialization.
+  // Note: Clang lazily instantiates implicit declarations, so we need
+  // to use them in order to force them to appear in the AST.
+  EXPECT_TRUE(Visitor.runOver(
+      "struct WithCtor { WithCtor(); }; \n"
+      "struct Simple { WithCtor w; }; \n"
+      "int main() { Simple s; }\n"));
+}
+
 } // end namespace clang
Index: unittests/Tooling/TestVisitor.h
===================================================================
--- unittests/Tooling/TestVisitor.h
+++ unittests/Tooling/TestVisitor.h
@@ -6,14 +6,17 @@
 // License. See LICENSE.TXT for details.
 //
 //===----------------------------------------------------------------------===//
-//
-//  This file defines a utility class for RecursiveASTVisitor related tests.
-//
+///
+/// \file
+/// \brief Defines utility templates for RecursiveASTVisitor related tests.
+///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_CLANG_TEST_VISITOR_H
 #define LLVM_CLANG_TEST_VISITOR_H
 
+#include <vector>
+
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/RecursiveASTVisitor.h"
@@ -29,7 +32,7 @@
 /// This is a drop-in replacement for RecursiveASTVisitor itself, with the
 /// additional capability of running it over a snippet of code.
 ///
-/// Visits template instantiations by default.
+/// Visits template instantiations (but not implicit code) by default.
 template <typename T>
 class TestVisitor : public RecursiveASTVisitor<T> {
 public:
@@ -81,63 +84,126 @@
   ASTContext *Context;
 };
 
-
-/// \brief A RecursiveASTVisitor for testing the RecursiveASTVisitor itself.
+/// \brief A RecursiveASTVisitor to check that certain matches are (or are
+/// not) observed during visitation.
 ///
-/// Allows simple creation of test visitors running matches on only a small
+/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
+/// and allows simple creation of test visitors running matches on only a small
 /// subset of the Visit* methods.
 template <typename T, template <typename> class Visitor = TestVisitor>
 class ExpectedLocationVisitor : public Visitor<T> {
 public:
-  ExpectedLocationVisitor()
-    : ExpectedLine(0), ExpectedColumn(0), Found(false) {}
-
-  virtual ~ExpectedLocationVisitor() {
-    EXPECT_TRUE(Found)
-      << "Expected \"" << ExpectedMatch << "\" at " << ExpectedLine
-      << ":" << ExpectedColumn << PartialMatches;
+  /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
+  ///
+  /// Any number of matches can be disallowed.
+  void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
+    DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
   }
 
   /// \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) {
-    ExpectedMatch = Match.str();
-    ExpectedLine = Line;
-    ExpectedColumn = Column;
+    ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
+  }
+
+  /// \brief Checks that all expected matches have been found.
+  virtual ~ExpectedLocationVisitor() {
+    for (typename std::vector<ExpectedMatch>::const_iterator
+             it = ExpectedMatches.begin(), end = ExpectedMatches.end();
+         it != end; ++it) {
+      it->ExpectFound();
+    }
   }
 
 protected:
-  /// \brief Convenience method to simplify writing test visitors.
-  ///
-  /// Sets 'Found' to true if 'Name' and 'Location' match the expected
-  /// values. If only a partial match is found, record the information
-  /// to produce nice error output when a test fails.
+  /// \brief Checks an actual match against expected and disallowed matches.
   ///
   /// Implementations are required to call this with appropriate values
   /// for 'Name' during visitation.
   void Match(StringRef Name, SourceLocation Location) {
-    FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
-    if (Name == ExpectedMatch &&
-        FullLocation.isValid() &&
-        FullLocation.getSpellingLineNumber() == ExpectedLine &&
-        FullLocation.getSpellingColumnNumber() == ExpectedColumn) {
-      EXPECT_TRUE(!Found);
-      Found = true;
-    } else if (Name == ExpectedMatch ||
-               (FullLocation.isValid() &&
-                FullLocation.getSpellingLineNumber() == ExpectedLine &&
-                FullLocation.getSpellingColumnNumber() == ExpectedColumn)) {
-      // If we did not match, record information about partial matches.
-      llvm::raw_string_ostream Stream(PartialMatches);
-      Stream << ", partial match: \"" << Name << "\" at ";
-      Location.print(Stream, this->Context->getSourceManager());
+    const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
+
+    for (typename std::vector<MatchCandidate>::const_iterator
+             it = DisallowedMatches.begin(), end = DisallowedMatches.end();
+         it != end; ++it) {
+      EXPECT_FALSE(it->Matches(Name, FullLocation))
+          << "Matched disallowed " << *it;
+    }
+
+    for (typename std::vector<ExpectedMatch>::iterator
+             it = ExpectedMatches.begin(), end = ExpectedMatches.end();
+         it != end; ++it) {
+      it->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
     }
   }
 
-  std::string ExpectedMatch;
-  unsigned ExpectedLine;
-  unsigned ExpectedColumn;
-  std::string PartialMatches;
-  bool Found;
+ private:
+  struct MatchCandidate {
+    std::string ExpectedName;
+    unsigned LineNumber;
+    unsigned ColumnNumber;
+
+    MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
+      : ExpectedName(Name.str()), LineNumber(LineNumber),
+        ColumnNumber(ColumnNumber) {
+    }
+
+    bool Matches(StringRef Name, FullSourceLoc const &Location) const {
+      return MatchesName(Name) && MatchesLocation(Location);
+    }
+
+    bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
+      return MatchesName(Name) || MatchesLocation(Location);
+    }
+
+    bool MatchesName(StringRef Name) const {
+      return Name == ExpectedName;
+    }
+
+    bool MatchesLocation(FullSourceLoc const &Location) const {
+      return Location.isValid() &&
+          Location.getSpellingLineNumber() == LineNumber &&
+          Location.getSpellingColumnNumber() == ColumnNumber;
+    }
+
+    friend std::ostream &operator<<(std::ostream &Stream,
+                                    MatchCandidate const &Match) {
+      return Stream << Match.ExpectedName
+                    << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
+    }
+  };
+
+  struct ExpectedMatch {
+    ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
+      : Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
+
+    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)) {
+        llvm::raw_string_ostream Stream(PartialMatches);
+        Stream << ", partial match: \"" << Name << "\" at ";
+        Location.print(Stream, SM);
+      }
+    }
+
+    void ExpectFound() const {
+      EXPECT_TRUE(Found)
+          << "Expected \"" << Candidate.ExpectedName
+          << "\" at " << Candidate.LineNumber
+          << ":" << Candidate.ColumnNumber << PartialMatches;
+    }
+
+    MatchCandidate Candidate;
+    std::string PartialMatches;
+    bool Found;
+  };
+
+  std::vector<MatchCandidate> DisallowedMatches;
+  std::vector<ExpectedMatch> ExpectedMatches;
 };
 }
 
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to