Update RecursiveASTVisitor so that it visits attributes. This is currently

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
This commit is contained in:
DeLesley Hutchins 2013-12-30 17:24:36 +00:00
parent d86672037b
commit c4a82438a7
8 changed files with 191 additions and 3 deletions

View File

@ -13,6 +13,11 @@ clang_tablegen(AttrDump.inc -gen-clang-attr-dump
SOURCE ../Basic/Attr.td SOURCE ../Basic/Attr.td
TARGET ClangAttrDump) 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 clang_tablegen(StmtNodes.inc -gen-clang-stmt-nodes
SOURCE ../Basic/StmtNodes.td SOURCE ../Basic/StmtNodes.td
TARGET ClangStmtNodes) TARGET ClangStmtNodes)

View File

@ -1,6 +1,6 @@
CLANG_LEVEL := ../../.. CLANG_LEVEL := ../../..
TD_SRC_DIR = $(PROJ_SRC_DIR)/../Basic 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 \ StmtNodes.inc DeclNodes.inc \
CommentNodes.inc CommentHTMLTags.inc \ CommentNodes.inc CommentHTMLTags.inc \
CommentHTMLTagsProperties.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, $@) \ $(Verb) $(ClangTableGen) -gen-clang-attr-dump -o $(call SYSPATH, $@) \
-I $(PROJ_SRC_DIR)/../../ $< -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)/StmtNodes.inc.tmp : $(TD_SRC_DIR)/StmtNodes.td $(CLANG_TBLGEN) \
$(ObjDir)/.dir $(ObjDir)/.dir
$(Echo) "Building Clang statement node tables with tblgen" $(Echo) "Building Clang statement node tables with tblgen"

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_AST_RECURSIVEASTVISITOR_H #ifndef LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
#define LLVM_CLANG_AST_RECURSIVEASTVISITOR_H #define LLVM_CLANG_AST_RECURSIVEASTVISITOR_H
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h" #include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h" #include "clang/AST/DeclFriend.h"
@ -182,6 +183,13 @@ public:
/// otherwise (including when the argument is a Null type location). /// otherwise (including when the argument is a Null type location).
bool TraverseTypeLoc(TypeLoc TL); 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 /// \brief Recursively visit a declaration, by dispatching to
/// Traverse*Decl() based on the argument's dynamic type. /// Traverse*Decl() based on the argument's dynamic type.
/// ///
@ -254,6 +262,16 @@ public:
/// \returns false if the visitation was terminated early, true otherwise. /// \returns false if the visitation was terminated early, true otherwise.
bool TraverseLambdaBody(LambdaExpr *LE); 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 ---- // ---- Methods on Stmts ----
// Declare Traverse*() for all concrete Stmt classes. // 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> template<typename Derived>
bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) { bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
if (!D) if (!D)
@ -636,10 +658,18 @@ bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
switch (D->getKind()) { switch (D->getKind()) {
#define ABSTRACT_DECL(DECL) #define ABSTRACT_DECL(DECL)
#define DECL(CLASS, BASE) \ #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" #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; return true;
} }

View File

@ -65,6 +65,7 @@ add_dependencies(clangAST
ClangAttrList ClangAttrList
ClangAttrImpl ClangAttrImpl
ClangAttrDump ClangAttrDump
ClangAttrVisitor
ClangCommentCommandInfo ClangCommentCommandInfo
ClangCommentCommandList ClangCommentCommandList
ClangCommentNodes ClangCommentNodes

View File

@ -577,4 +577,43 @@ TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses()); 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 } // end namespace clang

View File

@ -149,6 +149,7 @@ namespace {
// These functions print the argument contents formatted in different ways. // These functions print the argument contents formatted in different ways.
virtual void writeAccessors(raw_ostream &OS) const = 0; virtual void writeAccessors(raw_ostream &OS) const = 0;
virtual void writeAccessorDefinitions(raw_ostream &OS) const {} virtual void writeAccessorDefinitions(raw_ostream &OS) const {}
virtual void writeASTVisitorTraversal(raw_ostream &OS) const {}
virtual void writeCloneArgs(raw_ostream &OS) const = 0; virtual void writeCloneArgs(raw_ostream &OS) const = 0;
virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0; virtual void writeTemplateInstantiationArgs(raw_ostream &OS) const = 0;
virtual void writeTemplateInstantiation(raw_ostream &OS) const {} virtual void writeTemplateInstantiation(raw_ostream &OS) const {}
@ -794,6 +795,12 @@ namespace {
: SimpleArgument(Arg, Attr, "Expr *") : 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 { void writeTemplateInstantiationArgs(raw_ostream &OS) const {
OS << "tempInst" << getUpperName(); OS << "tempInst" << getUpperName();
} }
@ -826,6 +833,19 @@ namespace {
: VariadicArgument(Arg, Attr, "Expr *") : 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 { void writeTemplateInstantiationArgs(raw_ostream &OS) const {
OS << "tempInst" << getUpperName() << ", " OS << "tempInst" << getUpperName() << ", "
<< "A->" << getLowerName() << "_size()"; << "A->" << getLowerName() << "_size()";
@ -1568,6 +1588,85 @@ void EmitClangAttrSpellingListIndex(RecordKeeper &Records, raw_ostream &OS) {
OS << " return 0;\n"; 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. // Emits the LateParsed property for attributes.
void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) { void EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS) {
emitSourceFileHeader("llvm::StringSwitch code to match late parsed " emitSourceFileHeader("llvm::StringSwitch code to match late parsed "

View File

@ -32,6 +32,7 @@ enum ActionType {
GenClangAttrPCHWrite, GenClangAttrPCHWrite,
GenClangAttrSpellingList, GenClangAttrSpellingList,
GenClangAttrSpellingListIndex, GenClangAttrSpellingListIndex,
GenClangAttrASTVisitor,
GenClangAttrLateParsedList, GenClangAttrLateParsedList,
GenClangAttrTemplateInstantiate, GenClangAttrTemplateInstantiate,
GenClangAttrParsedAttrList, GenClangAttrParsedAttrList,
@ -82,6 +83,9 @@ cl::opt<ActionType> Action(
clEnumValN(GenClangAttrSpellingListIndex, clEnumValN(GenClangAttrSpellingListIndex,
"gen-clang-attr-spelling-index", "gen-clang-attr-spelling-index",
"Generate a clang attribute 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, clEnumValN(GenClangAttrLateParsedList,
"gen-clang-attr-late-parsed-list", "gen-clang-attr-late-parsed-list",
"Generate a clang attribute LateParsed list"), "Generate a clang attribute LateParsed list"),
@ -171,6 +175,9 @@ bool ClangTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
case GenClangAttrSpellingListIndex: case GenClangAttrSpellingListIndex:
EmitClangAttrSpellingListIndex(Records, OS); EmitClangAttrSpellingListIndex(Records, OS);
break; break;
case GenClangAttrASTVisitor:
EmitClangAttrASTVisitor(Records, OS);
break;
case GenClangAttrLateParsedList: case GenClangAttrLateParsedList:
EmitClangAttrLateParsedList(Records, OS); EmitClangAttrLateParsedList(Records, OS);
break; break;

View File

@ -38,6 +38,7 @@ void EmitClangAttrPCHRead(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrPCHWrite(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrSpellingList(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrSpellingList(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrSpellingListIndex(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 EmitClangAttrLateParsedList(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrTemplateInstantiate(RecordKeeper &Records, raw_ostream &OS);
void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS); void EmitClangAttrParsedAttrList(RecordKeeper &Records, raw_ostream &OS);