From: Daniel Jasper Date: Tue, 17 Jul 2012 07:39:27 +0000 (+0000) Subject: Make the isDerivedFrom matcher more generic. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=20b802d186dfc5db9b4a9ce83e9f31fa5aa4efcc;p=clang Make the isDerivedFrom matcher more generic. It now accepts an arbitrary inner matcher but is fully backwards compatible. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160348 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/ASTMatchers/ASTMatchers.h b/include/clang/ASTMatchers/ASTMatchers.h index fceea21171..d2ff604946 100644 --- a/include/clang/ASTMatchers/ASTMatchers.h +++ b/include/clang/ASTMatchers/ASTMatchers.h @@ -839,14 +839,11 @@ AST_MATCHER_P(CXXOperatorCallExpr, } /// \brief Matches C++ classes that are directly or indirectly derived from -/// the given base class. +/// a class matching \c Base. /// /// Note that a class is considered to be also derived from itself. -/// The parameter specified the name of the base type (either a class or a -/// typedef), and does not allow structural matches for namespaces or template -/// type parameters. /// -/// Example matches X, Y, Z, C (Base == "X") +/// Example matches X, Y, Z, C (Base == hasName("X")) /// class X; // A class is considered to be derived from itself /// class Y : public X {}; // directly derived /// class Z : public Y {}; // indirectly derived @@ -854,13 +851,19 @@ AST_MATCHER_P(CXXOperatorCallExpr, /// typedef A B; /// class C : public B {}; // derived from a typedef of X /// -/// In the following example, Bar matches isDerivedFrom("X"): +/// In the following example, Bar matches isDerivedFrom(hasName("X")): /// class Foo; /// typedef Foo X; /// class Bar : public Foo {}; // derived from a type that X is a typedef of -AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, std::string, Base) { - assert(!Base.empty()); - return Finder->classIsDerivedFrom(&Node, Base); +AST_MATCHER_P(CXXRecordDecl, isDerivedFrom, + internal::Matcher, Base) { + return Finder->classIsDerivedFrom(&Node, Base, Builder); +} + +/// \brief Overloaded method as shortcut for \c isDerivedFrom(hasName(...)). +inline internal::Matcher isDerivedFrom(StringRef BaseName) { + assert(!BaseName.empty()); + return isDerivedFrom(hasName(BaseName)); } /// \brief Matches AST nodes that have child AST nodes that match the diff --git a/include/clang/ASTMatchers/ASTMatchersInternal.h b/include/clang/ASTMatchers/ASTMatchersInternal.h index 5039c52915..4d044018a9 100644 --- a/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -411,11 +411,12 @@ public: virtual ~ASTMatchFinder() {} /// \brief Returns true if the given class is directly or indirectly derived - /// from a base type with the given name. + /// from a base type matching \c base. /// /// A class is considered to be also derived from itself. virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, - StringRef BaseName) const = 0; + const Matcher &Base, + BoundNodesTreeBuilder *Builder) = 0; // FIXME: Implement for other base nodes. virtual bool matchesChildOf(const Decl &DeclNode, diff --git a/lib/ASTMatchers/ASTMatchFinder.cpp b/lib/ASTMatchers/ASTMatchFinder.cpp index 8aad8dd834..a58d4f0fb4 100644 --- a/lib/ASTMatchers/ASTMatchFinder.cpp +++ b/lib/ASTMatchers/ASTMatchFinder.cpp @@ -27,15 +27,6 @@ namespace ast_matchers { namespace internal { namespace { -// Returns the value that 'AMap' maps 'Key' to, or NULL if 'Key' is -// not in 'AMap'. -template -static const typename Map::mapped_type * -find(const Map &AMap, const typename Map::key_type &Key) { - typename Map::const_iterator It = AMap.find(Key); - return It == AMap.end() ? NULL : &It->second; -} - // We use memoization to avoid running the same matcher on the same // AST node twice. This pair is the key for looking up match // result. It consists of an ID of the MatcherInterface (for @@ -244,8 +235,7 @@ public: const Type *TypeNode = DeclNode->getUnderlyingType().getTypePtr(); const Type *CanonicalType = // root of the typedef tree ActiveASTContext->getCanonicalType(TypeNode); - TypeToUnqualifiedAliases[CanonicalType].insert( - DeclNode->getName().str()); + TypeAliases[CanonicalType].insert(DeclNode); return true; } @@ -289,7 +279,8 @@ public: } virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, - StringRef BaseName) const; + const Matcher &Base, + BoundNodesTreeBuilder *Builder); // Implements ASTMatchFinder::MatchesChildOf. virtual bool matchesChildOf(const Decl &DeclNode, @@ -347,16 +338,20 @@ private: MatchFinder::MatchCallback* Callback; }; - // Returns true if 'TypeNode' is also known by the name 'Name'. In other - // words, there is a type (including typedef) with the name 'Name' - // that is equal to 'TypeNode'. - bool typeHasAlias(const Type *TypeNode, - StringRef Name) const { + // Returns true if 'TypeNode' has an alias that matches the given matcher. + bool typeHasMatchingAlias(const Type *TypeNode, + const Matcher Matcher, + BoundNodesTreeBuilder *Builder) { const Type *const CanonicalType = ActiveASTContext->getCanonicalType(TypeNode); - const std::set *UnqualifiedAlias = - find(TypeToUnqualifiedAliases, CanonicalType); - return UnqualifiedAlias != NULL && UnqualifiedAlias->count(Name) > 0; + const std::set &Aliases = TypeAliases[CanonicalType]; + for (std::set::const_iterator + It = Aliases.begin(), End = Aliases.end(); + It != End; ++It) { + if (Matcher.matches(**It, this, Builder)) + return true; + } + return false; } // Matches all registered matchers on the given node and calls the @@ -380,9 +375,8 @@ private: MatchFinder::MatchCallback*> > *const Triggers; ASTContext *ActiveASTContext; - // Maps a canonical type to the names of its typedefs. - llvm::DenseMap > - TypeToUnqualifiedAliases; + // Maps a canonical type to its TypedefDecls. + llvm::DenseMap > TypeAliases; // Maps (matcher, node) -> the match result for memoization. typedef llvm::DenseMap MemoizationMap; @@ -392,39 +386,36 @@ private: // Returns true if the given class is directly or indirectly derived // from a base type with the given name. A class is considered to be // also derived from itself. -bool -MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, - StringRef BaseName) const { - if (Declaration->getName() == BaseName) { +bool MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, + const Matcher &Base, + BoundNodesTreeBuilder *Builder) { + if (Base.matches(*Declaration, this, Builder)) return true; - } - if (!Declaration->hasDefinition()) { + if (!Declaration->hasDefinition()) return false; - } typedef CXXRecordDecl::base_class_const_iterator BaseIterator; for (BaseIterator It = Declaration->bases_begin(), End = Declaration->bases_end(); It != End; ++It) { const Type *TypeNode = It->getType().getTypePtr(); - if (typeHasAlias(TypeNode, BaseName)) + if (typeHasMatchingAlias(TypeNode, Base, Builder)) return true; // Type::getAs<...>() drills through typedefs. if (TypeNode->getAs() != NULL || - TypeNode->getAs() != NULL) { + TypeNode->getAs() != NULL) // Dependent names and template TypeNode parameters will be matched when // the template is instantiated. continue; - } CXXRecordDecl *ClassDecl = NULL; TemplateSpecializationType const *TemplateType = TypeNode->getAs(); if (TemplateType != NULL) { - if (TemplateType->getTemplateName().isDependent()) { + if (TemplateType->getTemplateName().isDependent()) // Dependent template specializations will be matched when the // template is instantiated. continue; - } + // For template specialization types which are specializing a template // declaration which is an explicit or partial specialization of another // template declaration, getAsCXXRecordDecl() returns the corresponding @@ -448,9 +439,8 @@ MatchASTVisitor::classIsDerivedFrom(const CXXRecordDecl *Declaration, } assert(ClassDecl != NULL); assert(ClassDecl != Declaration); - if (classIsDerivedFrom(ClassDecl, BaseName)) { + if (classIsDerivedFrom(ClassDecl, Base, Builder)) return true; - } } return false; } diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index 597cf6fe4d..5791932b17 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -245,7 +245,7 @@ TEST(DeclarationMatcher, ClassIsDerived) { variable( hasName("z_char"), hasInitializer(hasType(record(isDerivedFrom("Base1"), - isDerivedFrom("Base2"))))))); + isDerivedFrom("Base2"))))))); const char *RecursiveTemplateTwoParameters = "class Base1 {}; class Base2 {};" @@ -273,7 +273,17 @@ TEST(DeclarationMatcher, ClassIsDerived) { variable( hasName("z_char"), hasInitializer(hasType(record(isDerivedFrom("Base1"), - isDerivedFrom("Base2"))))))); + isDerivedFrom("Base2"))))))); + EXPECT_TRUE(matches( + "namespace ns { class X {}; class Y : public X {}; }", + record(isDerivedFrom("::ns::X")))); + EXPECT_TRUE(notMatches( + "class X {}; class Y : public X {};", + record(isDerivedFrom("::ns::X")))); + + EXPECT_TRUE(matches( + "class X {}; class Y : public X {};", + record(isDerivedFrom(id("test", record(hasName("X"))))))); } TEST(AllOf, AllOverloadsWork) {