bcraig created this revision. bcraig added reviewers: zaks.anna, rsmith. bcraig added a subscriber: cfe-commits. Herald added a subscriber: klimek.
RecursiveASTVisitor allows visitors to specify whether they wish to visit implicitly created code or not. I have ported that code over to DataRecursiveASTVisitor. DataRecursiveASTVisitor didn't have any tests before this change, so I forked RecursiveASTVisitorTestExprVisitor. This test exercised some defaultArgumentWasInherited() cases, so I got those working as well. I plan on using this new feature in a checker that searches for excess padding. http://reviews.llvm.org/D14506 Files: include/clang/AST/DataRecursiveASTVisitor.h unittests/Tooling/CMakeLists.txt unittests/Tooling/DataRecursiveASTVisitorTestExprVisitor.cpp unittests/Tooling/LookupTest.cpp unittests/Tooling/TestVisitor.h
Index: unittests/Tooling/TestVisitor.h =================================================================== --- unittests/Tooling/TestVisitor.h +++ unittests/Tooling/TestVisitor.h @@ -17,6 +17,7 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/DataRecursiveASTVisitor.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -33,11 +34,11 @@ /// /// Visits template instantiations and implicit code by default. template <typename T> -class TestVisitor : public RecursiveASTVisitor<T> { +class TestVisitorImpl : public T { public: - TestVisitor() { } + TestVisitorImpl() { } - virtual ~TestVisitor() { } + virtual ~TestVisitorImpl() { } enum Language { Lang_C, @@ -80,7 +81,7 @@ class FindConsumer : public ASTConsumer { public: - FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {} + FindConsumer(TestVisitorImpl *Visitor) : Visitor(Visitor) {} void HandleTranslationUnit(clang::ASTContext &Context) override { Visitor->Context = &Context; @@ -88,12 +89,12 @@ } private: - TestVisitor *Visitor; + TestVisitorImpl *Visitor; }; class TestAction : public ASTFrontendAction { public: - TestAction(TestVisitor *Visitor) : Visitor(Visitor) {} + TestAction(TestVisitorImpl *Visitor) : Visitor(Visitor) {} std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(CompilerInstance &, llvm::StringRef dummy) override { @@ -102,12 +103,17 @@ } protected: - TestVisitor *Visitor; + TestVisitorImpl *Visitor; }; ASTContext *Context; }; +template <typename T> +using TestVisitor = TestVisitorImpl<RecursiveASTVisitor<T>>; +template <typename T> +using TestDataVisitor = TestVisitorImpl<DataRecursiveASTVisitor<T>>; + /// \brief A RecursiveASTVisitor to check that certain matches are (or are /// not) observed during visitation. /// @@ -229,6 +235,10 @@ std::vector<MatchCandidate> DisallowedMatches; std::vector<ExpectedMatch> ExpectedMatches; }; + +template <typename T> +using ExpectedLocationDataVisitor = ExpectedLocationVisitor<T, TestDataVisitor>; + } #endif Index: unittests/Tooling/LookupTest.cpp =================================================================== --- unittests/Tooling/LookupTest.cpp +++ unittests/Tooling/LookupTest.cpp @@ -23,7 +23,7 @@ bool TraverseDecl(Decl *D) { DeclStack.push_back(D); - bool Ret = TestVisitor::TraverseDecl(D); + bool Ret = TestVisitor<GetDeclsVisitor>::TraverseDecl(D); DeclStack.pop_back(); return Ret; } Index: unittests/Tooling/DataRecursiveASTVisitorTestExprVisitor.cpp =================================================================== --- /dev/null +++ unittests/Tooling/DataRecursiveASTVisitorTestExprVisitor.cpp @@ -0,0 +1,224 @@ +//===- unittest/Tooling/DataRecursiveASTVisitorTestExprVisitor.cpp ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" +#include <stack> + +using namespace clang; + +namespace { + +class ParenExprVisitor : public ExpectedLocationDataVisitor<ParenExprVisitor> { +public: + bool VisitParenExpr(ParenExpr *Parens) { + Match("", Parens->getExprLoc()); + return true; + } +}; + +TEST(DataRecursiveASTVisitor, VisitsParensDuringDataRecursion) { + ParenExprVisitor Visitor; + Visitor.ExpectMatch("", 1, 9); + EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n")); +} + +class TemplateArgumentLocTraverser + : public ExpectedLocationDataVisitor<TemplateArgumentLocTraverser> { +public: + bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) { + std::string ArgStr; + llvm::raw_string_ostream Stream(ArgStr); + const TemplateArgument &Arg = ArgLoc.getArgument(); + + Arg.print(Context->getPrintingPolicy(), Stream); + Match(Stream.str(), ArgLoc.getLocation()); + return ExpectedLocationDataVisitor<TemplateArgumentLocTraverser>:: + TraverseTemplateArgumentLoc(ArgLoc); + } +}; + +TEST(DataRecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) { + TemplateArgumentLocTraverser Visitor; + Visitor.ExpectMatch("X", 2, 40); + EXPECT_TRUE(Visitor.runOver( + "template<typename T> class X;\n" + "template<template <typename> class T = X> class Y;\n" + "template<template <typename> class T> class Y {};\n")); +} + +class CXXBoolLiteralExprVisitor + : public ExpectedLocationDataVisitor<CXXBoolLiteralExprVisitor> { +public: + bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) { + if (BE->getValue()) + Match("true", BE->getLocation()); + else + Match("false", BE->getLocation()); + return true; + } +}; + +TEST(DataRecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) { + CXXBoolLiteralExprVisitor Visitor; + Visitor.ExpectMatch("true", 2, 19); + EXPECT_TRUE(Visitor.runOver( + "template<bool B> class X;\n" + "template<bool B = true> class Y;\n" + "template<bool B> class Y {};\n")); +} + +// A visitor that visits implicit declarations and matches constructors. +class ImplicitCtorVisitor + : public ExpectedLocationDataVisitor<ImplicitCtorVisitor> { +public: + bool shouldVisitImplicitCode() const { return true; } + + bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) { + if (Ctor->isImplicit()) { // Was not written in source code + if (const CXXRecordDecl* Class = Ctor->getParent()) { + Match(Class->getName(), Ctor->getLocation()); + } + } + return true; + } +}; + +TEST(DataRecursiveASTVisitor, VisitsImplicitCopyConstructors) { + ImplicitCtorVisitor Visitor; + Visitor.ExpectMatch("Simple", 2, 8); + // 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 { Simple(); WithCtor w; }; \n" + "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 ExpectedLocationDataVisitor<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(DataRecursiveASTVisitor, 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(DataRecursiveASTVisitor, 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")); +} + +class DeclRefExprVisitor : public ExpectedLocationDataVisitor<DeclRefExprVisitor> { +public: + bool VisitDeclRefExpr(DeclRefExpr *Reference) { + Match(Reference->getNameInfo().getAsString(), Reference->getLocation()); + return true; + } +}; + +TEST(DataRecursiveASTVisitor, VisitsBaseClassTemplateArguments) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 2, 3); + EXPECT_TRUE(Visitor.runOver( + "void x(); template <void (*T)()> class X {};\nX<x> y;")); +} + +TEST(DataRecursiveASTVisitor, VisitsCXXForRangeStmtRange) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 2, 25); + Visitor.ExpectMatch("x", 2, 30); + EXPECT_TRUE(Visitor.runOver( + "int x[5];\n" + "void f() { for (int i : x) { x[0] = 1; } }", + DeclRefExprVisitor::Lang_CXX11)); +} + +TEST(DataRecursiveASTVisitor, VisitsCallExpr) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 1, 22); + EXPECT_TRUE(Visitor.runOver( + "void x(); void y() { x(); }")); +} + +/* FIXME: According to Richard Smith this is a bug in the AST. +TEST(DataRecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 3, 43); + EXPECT_TRUE(Visitor.runOver( + "template <typename T> void x();\n" + "template <void (*T)()> class X {};\n" + "template <typename T> class Y : public X< x<T> > {};\n" + "Y<int> y;")); +} +*/ + +TEST(DataRecursiveASTVisitor, VisitsExtension) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("s", 1, 24); + EXPECT_TRUE(Visitor.runOver( + "int s = __extension__ (s);\n")); +} + +TEST(DataRecursiveASTVisitor, VisitsCopyExprOfBlockDeclCapture) { + DeclRefExprVisitor Visitor; + Visitor.ExpectMatch("x", 3, 24); + EXPECT_TRUE(Visitor.runOver("void f(int(^)(int)); \n" + "void g() { \n" + " f([&](int x){ return x; }); \n" + "}", + DeclRefExprVisitor::Lang_OBJCXX11)); +} + +} // end anonymous namespace Index: unittests/Tooling/CMakeLists.txt =================================================================== --- unittests/Tooling/CMakeLists.txt +++ unittests/Tooling/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_unittest(ToolingTests CommentHandlerTest.cpp CompilationDatabaseTest.cpp + DataRecursiveASTVisitorTestExprVisitor.cpp LookupTest.cpp ToolingTest.cpp RecursiveASTVisitorTest.cpp Index: include/clang/AST/DataRecursiveASTVisitor.h =================================================================== --- include/clang/AST/DataRecursiveASTVisitor.h +++ include/clang/AST/DataRecursiveASTVisitor.h @@ -151,6 +151,10 @@ /// TypeLocs. bool shouldWalkTypesOfTypeLocs() const { return true; } + /// \brief Return whether this visitor should recurse into implicit + /// code, e.g., implicit constructors and destructors. + bool shouldVisitImplicitCode() const { return false; } + /// \brief Recursively visit a statement or expression, by /// dispatching to Traverse*() based on the argument's dynamic type. /// @@ -582,10 +586,9 @@ if (!D) return true; - // As a syntax visitor, we want to ignore declarations for - // implicitly-defined declarations (ones not typed explicitly by the - // user). - if (D->isImplicit()) + // As a syntax visitor, by default we want to ignore declarations for + // implicit declarations (ones not typed explicitly by the user). + if (!getDerived().shouldVisitImplicitCode() && D->isImplicit()) return true; switch (D->getKind()) { @@ -780,7 +783,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; } @@ -1559,7 +1562,7 @@ // D is the "T" in something like // template <template <typename> class T> class container { }; TRY_TO(TraverseDecl(D->getTemplatedDecl())); - if (D->hasDefaultArgument()) { + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) { TRY_TO(TraverseTemplateArgumentLoc(D->getDefaultArgument())); } TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters())); @@ -1569,7 +1572,7 @@ // D is the "T" in something like "template<typename T> class vector;" if (D->getTypeForDecl()) TRY_TO(TraverseType(QualType(D->getTypeForDecl(), 0))); - if (D->hasDefaultArgument()) + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) TRY_TO(TraverseTypeLoc(D->getDefaultArgumentInfo()->getTypeLoc())); }) @@ -1762,7 +1765,18 @@ // FunctionNoProtoType or FunctionProtoType, or a typedef. This // also covers the return type and the function parameters, // including exception specifications. - TRY_TO(TraverseTypeLoc(D->getTypeSourceInfo()->getTypeLoc())); + if (TypeSourceInfo *TSI = D->getTypeSourceInfo()) { + TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); + } else if (getDerived().shouldVisitImplicitCode()) { + // Visit parameter variable declarations of the implicit function + // if the traverser is visiting implicit code. Parameter variable + // declarations do not have valid TypeSourceInfo, so to visit them + // we need to traverse the declarations explicitly. + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); + I != E; ++I) + TRY_TO(TraverseDecl(*I)); + } if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) { // Constructor initializers. @@ -1813,7 +1827,8 @@ bool RecursiveASTVisitor<Derived>::TraverseVarHelper(VarDecl *D) { TRY_TO(TraverseDeclaratorHelper(D)); // Default params are taken care of when we traverse the ParmVarDecl. - if (!isa<ParmVarDecl>(D)) + if (!isa<ParmVarDecl>(D) && + (!D->isCXXForRangeDecl() || getDerived().shouldVisitImplicitCode())) TRY_TO(TraverseStmt(D->getInit())); return true; } @@ -1862,7 +1877,8 @@ DEF_TRAVERSE_DECL(NonTypeTemplateParmDecl, { // A non-type template parameter, e.g. "S" in template<int S> class Foo ... TRY_TO(TraverseDeclaratorHelper(D)); - TRY_TO(TraverseStmt(D->getDefaultArgument())); + if (D->hasDefaultArgument() && !D->defaultArgumentWasInherited()) + TRY_TO(TraverseStmt(D->getDefaultArgument())); }) DEF_TRAVERSE_DECL(ParmVarDecl, { @@ -1961,7 +1977,15 @@ DEF_TRAVERSE_STMT(ObjCAtTryStmt, {}) DEF_TRAVERSE_STMT(ObjCForCollectionStmt, {}) DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, {}) -DEF_TRAVERSE_STMT(CXXForRangeStmt, {}) +DEF_TRAVERSE_STMT(CXXForRangeStmt, { + if (!getDerived().shouldVisitImplicitCode()) { + TRY_TO(TraverseStmt(S->getLoopVarStmt())); + TRY_TO(TraverseStmt(S->getRangeInit())); + TRY_TO(TraverseStmt(S->getBody())); + // Visit everything else only if shouldVisitImplicitCode(). + return true; + } +}) DEF_TRAVERSE_STMT(MSDependentExistsStmt, { TRY_TO(TraverseNestedNameSpecifierLoc(S->getQualifierLoc())); TRY_TO(TraverseDeclarationNameInfo(S->getNameInfo()));
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits