Update RecursiveASTVisitor so that it visits attributes. This is currently
authorDeLesley Hutchins <delesley@google.com>
Mon, 30 Dec 2013 17:24:36 +0000 (17:24 +0000)
committerDeLesley Hutchins <delesley@google.com>
Mon, 30 Dec 2013 17:24:36 +0000 (17:24 +0000)
important for thread safety attributes, which contain expressions that were
not being visited, and were thus invisible to various tools.  There are now
Visit*Attr methods that can be overridden for every attribute.

llvm-svn: 198224

clang/include/clang/AST/CMakeLists.txt
clang/include/clang/AST/Makefile
clang/include/clang/AST/RecursiveASTVisitor.h
clang/lib/AST/CMakeLists.txt
clang/unittests/Tooling/RecursiveASTVisitorTest.cpp
clang/utils/TableGen/ClangAttrEmitter.cpp
clang/utils/TableGen/TableGen.cpp
clang/utils/TableGen/TableGenBackends.h

index ba54fa2..260734f 100644 (file)
@@ -13,6 +13,11 @@ clang_tablegen(AttrDump.inc -gen-clang-attr-dump
   SOURCE ../Basic/Attr.td
   TARGET ClangAttrDump)
 
+clang_tablegen(AttrVisitor.inc -gen-clang-attr-ast-visitor
+  -I ${CMAKE_CURRENT_SOURCE_DIR}/../../
+  SOURCE ../Basic/Attr.td
+  TARGET ClangAttrVisitor)
+
 clang_tablegen(StmtNodes.inc -gen-clang-stmt-nodes
   SOURCE ../Basic/StmtNodes.td
   TARGET ClangStmtNodes)
index ae84bcf..85e6449 100644 (file)
@@ -1,6 +1,6 @@
 CLANG_LEVEL := ../../..
 TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic
-BUILT_SOURCES = Attrs.inc AttrImpl.inc AttrDump.inc \
+BUILT_SOURCES = Attrs.inc AttrImpl.inc AttrDump.inc AttrVisitor.inc \
                 StmtNodes.inc DeclNodes.inc \
                 CommentNodes.inc CommentHTMLTags.inc \
                 CommentHTMLTagsProperties.inc \
@@ -30,6 +30,12 @@ $(ObjDir)/AttrDump.inc.tmp : $(TD_SRC_DIR)/Attr.td $(CLANG_TBLGEN) \
        $(Verb) $(ClangTableGen) -gen-clang-attr-dump -o $(call SYSPATH, $@) \
                -I $(PROJ_SRC_DIR)/../../ $<
 
+$(ObjDir)/AttrVisitor.inc.tmp : $(TD_SRC_DIR)/Attr.td $(CLANG_TBLGEN) \
+                                $(ObjDir)/.dir
+       $(Echo) "Building Clang attribute AST visitor with tblgen"
+       $(Verb) $(ClangTableGen) -gen-clang-attr-ast-visitor -o $(call SYSPATH, $@) \
+               -I $(PROJ_SRC_DIR)/../../ $<
+
 $(ObjDir)/StmtNodes.inc.tmp : $(TD_SRC_DIR)/StmtNodes.td $(CLANG_TBLGEN) \
                               $(ObjDir)/.dir
        $(Echo) "Building Clang statement node tables with tblgen"
index 58034e8..aa15f9f 100644 (file)
@@ -14,6 +14,7 @@
 #ifndef LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
 #define LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
 
+#include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclFriend.h"
@@ -182,6 +183,13 @@ public:
   /// otherwise (including when the argument is a Null type location).
   bool TraverseTypeLoc(TypeLoc TL);
 
+  /// \brief Recursively visit an attribute, by dispatching to
+  /// Traverse*Attr() based on the argument's dynamic type.
+  ///
+  /// \returns false if the visitation was terminated early, true
+  /// otherwise (including when the argument is a Null type location).
+  bool TraverseAttr(Attr *At);
+
   /// \brief Recursively visit a declaration, by dispatching to
   /// Traverse*Decl() based on the argument's dynamic type.
   ///
@@ -254,6 +262,16 @@ public:
   /// \returns false if the visitation was terminated early, true otherwise.
   bool TraverseLambdaBody(LambdaExpr *LE);
 
+  // ---- Methods on Attrs ----
+
+  // \brief Visit an attribute.
+  bool VisitAttr(Attr *A) { return true; }
+
+  // Declare Traverse* and empty Visit* for all Attr classes.
+#define ATTR_VISITOR_DECLS_ONLY
+#include "clang/AST/AttrVisitor.inc"
+#undef ATTR_VISITOR_DECLS_ONLY
+
   // ---- Methods on Stmts ----
 
   // Declare Traverse*() for all concrete Stmt classes.
@@ -623,6 +641,10 @@ bool RecursiveASTVisitor<Derived>::TraverseTypeLoc(TypeLoc TL) {
 }
 
 
+// Define the Traverse*Attr(Attr* A) methods
+#include "clang/AST/AttrVisitor.inc"
+
+
 template<typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
   if (!D)
@@ -636,10 +658,18 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
   switch (D->getKind()) {
 #define ABSTRACT_DECL(DECL)
 #define DECL(CLASS, BASE) \
-  case Decl::CLASS: DISPATCH(CLASS##Decl, CLASS##Decl, D);
+  case Decl::CLASS: \
+    if (!getDerived().Traverse##CLASS##Decl(static_cast<CLASS##Decl*>(D))) \
+      return false; \
+    break;
 #include "clang/AST/DeclNodes.inc"
- }
 }
 
+  // Visit any attributes attached to this declaration.
+  for (Decl::attr_iterator I=D->attr_begin(), E=D->attr_end(); I != E; ++I) {
+    if (!getDerived().TraverseAttr(*I))
+      return false;
+  }
   return true;
 }
 
index 461e8b3..e0b3c33 100644 (file)
@@ -65,6 +65,7 @@ add_dependencies(clangAST
   ClangAttrList
   ClangAttrImpl
   ClangAttrDump
+  ClangAttrVisitor
   ClangCommentCommandInfo
   ClangCommentCommandList
   ClangCommentNodes
index 3234767..cd5767b 100644 (file)
@@ -577,4 +577,43 @@ TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
   EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
 }
 
+
+
+// Check to ensure that attributes and expressions within them are being
+// visited.
+class AttrVisitor : public ExpectedLocationVisitor<AttrVisitor> {
+public:
+  bool VisitMemberExpr(MemberExpr *ME) {
+    Match(ME->getMemberDecl()->getNameAsString(), ME->getLocStart());
+    return true;
+  }
+  bool VisitAttr(Attr *A) {
+    Match("Attr", A->getLocation());
+    return true;
+  }
+  bool VisitGuardedByAttr(GuardedByAttr *A) {
+    Match("guarded_by", A->getLocation());
+    return true;
+  }
+};
+
+
+TEST(RecursiveASTVisitor, AttributesAreVisited) {
+  AttrVisitor Visitor;
+  Visitor.ExpectMatch("Attr", 4, 24);
+  Visitor.ExpectMatch("guarded_by", 4, 24);
+  Visitor.ExpectMatch("mu1",  4, 35);
+  Visitor.ExpectMatch("Attr", 5, 29);
+  Visitor.ExpectMatch("mu1",  5, 54);
+  Visitor.ExpectMatch("mu2",  5, 59);
+  EXPECT_TRUE(Visitor.runOver(
+    "class Foo {\n"
+    "  int mu1;\n"
+    "  int mu2;\n"
+    "  int a __attribute__((guarded_by(mu1)));\n"
+    "  void bar() __attribute__((exclusive_locks_required(mu1, mu2)));\n"
+    "};\n"));
+}
+
+
 } // end namespace clang
index ef75394..be4ae62 100644 (file)
@@ -149,6 +149,7 @@ namespace {
     // These functions print the argument contents formatted in different ways.
     virtual void writeAccessors(raw_ostream &OS) const = 0;
     virtual void writeAccessorDefinitions(raw_ostream &OS) const {}
+    virtual void writeASTVisitorTraversal(raw_ostream &OS) const {}
     virtual void writeCloneArgs(raw_ostream &OS) const = 0;
     virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0;
     virtual void writeTemplateInstantiation(raw_ostream &OS) const {}
@@ -794,6 +795,12 @@ namespace {
       : SimpleArgument(Arg, Attr, "Expr *")
     {}
 
+    virtual void writeASTVisitorTraversal(raw_ostream &OS) const {
+      OS << "  if (!"
+         << "getDerived().TraverseStmt(A->get" << getUpperName() << "()))\n";
+      OS << "    return false;\n";
+    }
+
     void writeTemplateInstantiationArgs(raw_ostream &OS) const {
       OS << "tempInst" << getUpperName();
     }
@@ -826,6 +833,19 @@ namespace {
       : VariadicArgument(Arg, Attr, "Expr *")
     {}
 
+    virtual void writeASTVisitorTraversal(raw_ostream &OS) const {
+      OS << "  {\n";
+      OS << "    " << getType() << " *I = A->" << getLowerName()
+         << "_begin();\n";
+      OS << "    " << getType() << " *E = A->" << getLowerName()
+         << "_end();\n";
+      OS << "    for (; I != E; ++I) {\n";
+      OS << "      if (!getDerived().TraverseStmt(*I))\n";
+      OS << "        return false;\n";
+      OS << "    }\n";
+      OS << "  }\n";
+    }
+
     void writeTemplateInstantiationArgs(raw_ostream &OS) const {
       OS << "tempInst" << getUpperName() << ", "
          << "A->" << getLowerName() << "_size()";
@@ -1568,6 +1588,85 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) {
   OS << "  return 0;\n";
 }
 
+// Emits code used by RecursiveASTVisitor to visit attributes
+void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS) {
+  emitSourceFileHeader("Used by RecursiveASTVisitor to visit attributes.", OS);
+
+  std::vector<Record*> Attrs = Records.getAllDerivedDefinitions("Attr");
+
+  // Write method declarations for Traverse* methods.
+  // We emit this here because we only generate methods for attributes that
+  // are declared as ASTNodes.
+  OS << "#ifdef ATTR_VISITOR_DECLS_ONLY\n\n";
+  for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
+       I != E; ++I) {
+    Record &R = **I;
+    if (!R.getValueAsBit("ASTNode"))
+      continue;
+    OS << "  bool Traverse"
+       << R.getName() << "Attr(" << R.getName() << "Attr *A);\n";
+    OS << "  bool Visit"
+       << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n"
+       << "    return true; \n"
+       << "  };\n";
+  }
+  OS << "\n#else // ATTR_VISITOR_DECLS_ONLY\n\n";
+
+  // Write individual Traverse* methods for each attribute class.
+  for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
+       I != E; ++I) {
+    Record &R = **I;
+    if (!R.getValueAsBit("ASTNode"))
+      continue;
+
+    OS << "template <typename Derived>\n"
+       << "bool RecursiveASTVisitor<Derived>::Traverse"
+       << R.getName() << "Attr(" << R.getName() << "Attr *A) {\n"
+       << "  if (!getDerived().VisitAttr(A))\n"
+       << "    return false;\n"
+       << "  if (!getDerived().Visit" << R.getName() << "Attr(A))\n"
+       << "    return false;\n";
+
+    std::vector<Record*> ArgRecords = R.getValueAsListOfDefs("Args");
+    for (std::vector<Record*>::iterator ri = ArgRecords.begin(),
+                                        re = ArgRecords.end();
+         ri != re; ++ri) {
+      Record &ArgRecord = **ri;
+      Argument *Arg = createArgument(ArgRecord, R.getName());
+      assert(Arg);
+      Arg->writeASTVisitorTraversal(OS);
+    }
+
+    OS << "  return true;\n";
+    OS << "}\n\n";
+  }
+
+  // Write generic Traverse routine
+  OS << "template <typename Derived>\n"
+     << "bool RecursiveASTVisitor<Derived>::TraverseAttr(Attr *A) {\n"
+     << "  if (!A)\n"
+     << "    return true;\n"
+     << "\n"
+     << "  switch (A->getKind()) {\n"
+     << "    default:\n"
+     << "      return true;\n";
+
+  for (std::vector<Record*>::iterator I = Attrs.begin(), E = Attrs.end();
+       I != E; ++I) {
+    Record &R = **I;
+    if (!R.getValueAsBit("ASTNode"))
+      continue;
+
+    OS << "    case attr::" << R.getName() << ":\n"
+       << "      return getDerived().Traverse" << R.getName() << "Attr("
+       << "cast<" << R.getName() << "Attr>(A));\n";
+  }
+  OS << "  }\n";  // end case
+  OS << "}\n";  // end function
+  OS << "#endif  // ATTR_VISITOR_DECLS_ONLY\n";
+}
+
+
 // Emits the LateParsed property for attributes.
 void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) {
   emitSourceFileHeader("llvm::StringSwitch code to match late parsed "
index 0e45d81..e0cd027 100644 (file)
@@ -32,6 +32,7 @@ enum ActionType {
   GenClangAttrPCHWrite,
   GenClangAttrSpellingList,
   GenClangAttrSpellingListIndex,
+  GenClangAttrASTVisitor,
   GenClangAttrLateParsedList,
   GenClangAttrTemplateInstantiate,
   GenClangAttrParsedAttrList,
@@ -82,6 +83,9 @@ cl::opt<ActionType> Action(
         clEnumValN(GenClangAttrSpellingListIndex,
                    "gen-clang-attr-spelling-index",
                    "Generate a clang attribute spelling index"),
+        clEnumValN(GenClangAttrASTVisitor,
+                   "gen-clang-attr-ast-visitor",
+                   "Generate a recursive AST visitor for clang attributes"),
         clEnumValN(GenClangAttrLateParsedList,
                    "gen-clang-attr-late-parsed-list",
                    "Generate a clang attribute LateParsed list"),
@@ -171,6 +175,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
   case GenClangAttrSpellingListIndex:
     EmitClangAttrSpellingListIndex(Records, OS);
     break;
+  case GenClangAttrASTVisitor:
+    EmitClangAttrASTVisitor(Records, OS);
+    break;
   case GenClangAttrLateParsedList:
     EmitClangAttrLateParsedList(Records, OS);
     break;
index 8904287..d9256d8 100644 (file)
@@ -38,6 +38,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrSpellingList(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS);
+void EmitClangAttrASTVisitor(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS);
 void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS);