Index: include/clang/AST/ASTTypeTraits.h =================================================================== --- include/clang/AST/ASTTypeTraits.h +++ include/clang/AST/ASTTypeTraits.h @@ -191,6 +191,8 @@ return BaseConverter::get(NodeKind, Storage.buffer); } + ASTNodeKind getNodeKind() const { return NodeKind; } + /// \brief Returns a pointer that identifies the stored AST node. /// /// Note that this is not supported by all AST nodes. For AST nodes Index: include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -61,9 +61,8 @@ /// \brief Adds \c Node to the map with key \c ID. /// /// The node's base type should be in NodeBaseType or it will be unaccessible. - template - void addNode(StringRef ID, const T* Node) { - NodeMap[ID] = ast_type_traits::DynTypedNode::create(*Node); + void addNode(StringRef ID, const ast_type_traits::DynTypedNode& DynNode) { + NodeMap[ID] = DynNode; } /// \brief Returns the AST node bound to \c ID. @@ -136,11 +135,12 @@ }; /// \brief Add a binding from an id to a node. - template void setBinding(const std::string &Id, const T *Node) { + void setBinding(const std::string &Id, + const ast_type_traits::DynTypedNode &DynNode) { if (Bindings.empty()) Bindings.push_back(BoundNodesMap()); for (unsigned i = 0, e = Bindings.size(); i != e; ++i) - Bindings[i].addNode(Id, Node); + Bindings[i].addNode(Id, DynNode); } /// \brief Adds a branch in the tree. @@ -179,6 +179,22 @@ class ASTMatchFinder; +/// \brief Generic interface for all matchers. +/// +/// Used by the implementation of Matcher and DynTypedMatcher. +/// In general, implement MatcherInterface or SingleNodeMatcherInterface +/// instead. +class DynMatcherInterface : public RefCountedBaseVPTR { +public: + /// \brief Returns true if \p DynNode can be matched. + /// + /// May bind \p DynNode to an ID via \p Builder, or recurse into + /// the AST via \p Finder. + virtual bool matches(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const = 0; +}; + /// \brief Generic interface for matchers on an AST node of type T. /// /// Implement this if your matcher may need to inspect the children or @@ -187,7 +203,7 @@ /// current node and doesn't care about its children or descendants, /// implement SingleNodeMatcherInterface instead. template -class MatcherInterface : public RefCountedBaseVPTR { +class MatcherInterface : public DynMatcherInterface { public: virtual ~MatcherInterface() {} @@ -198,6 +214,15 @@ virtual bool matches(const T &Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const = 0; + + bool matches(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + if (const T *Node = DynNode.get()) { + return matches(*Node, Finder, Builder); + } + return false; + } }; /// \brief Interface for matchers that only evaluate properties on a single @@ -219,111 +244,7 @@ } }; -/// \brief Wrapper of a MatcherInterface *that allows copying. -/// -/// A Matcher can be used anywhere a Matcher is -/// required. This establishes an is-a relationship which is reverse -/// to the AST hierarchy. In other words, Matcher is contravariant -/// with respect to T. The relationship is built via a type conversion -/// operator rather than a type hierarchy to be able to templatize the -/// type hierarchy instead of spelling it out. -template -class Matcher { -public: - /// \brief Takes ownership of the provided implementation pointer. - explicit Matcher(MatcherInterface *Implementation) - : Implementation(Implementation) {} - - /// \brief Implicitly converts \c Other to a Matcher. - /// - /// Requires \c T to be derived from \c From. - template - Matcher(const Matcher &Other, - typename std::enable_if::value && - !std::is_same::value>::type * = 0) - : Implementation(new ImplicitCastMatcher(Other)) {} - - /// \brief Implicitly converts \c Matcher to \c Matcher. - /// - /// The resulting matcher is not strict, i.e. ignores qualifiers. - template - Matcher(const Matcher &Other, - typename std::enable_if< - std::is_same::value && - std::is_same::value>::type* = 0) - : Implementation(new TypeToQualType(Other)) {} - - /// \brief Forwards the call to the underlying MatcherInterface pointer. - bool matches(const T &Node, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const { - if (Implementation->matches(Node, Finder, Builder)) - return true; - // Delete all bindings when a matcher does not match. - // This prevents unexpected exposure of bound nodes in unmatches - // branches of the match tree. - Builder->removeBindings([](const BoundNodesMap &) { return true; }); - return false; - } - - /// \brief Returns an ID that uniquely identifies the matcher. - uint64_t getID() const { - /// FIXME: Document the requirements this imposes on matcher - /// implementations (no new() implementation_ during a Matches()). - return reinterpret_cast(Implementation.get()); - } - - /// \brief Allows the conversion of a \c Matcher to a \c - /// Matcher. - /// - /// Depending on the constructor argument, the matcher is either strict, i.e. - /// does only matches in the absence of qualifiers, or not, i.e. simply - /// ignores any qualifiers. - template - class TypeToQualType : public MatcherInterface { - public: - TypeToQualType(const Matcher &InnerMatcher) - : InnerMatcher(InnerMatcher) {} - - bool matches(const QualType &Node, ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const override { - if (Node.isNull()) - return false; - return InnerMatcher.matches(*Node, Finder, Builder); - } - private: - const Matcher InnerMatcher; - }; - -private: - /// \brief Allows conversion from Matcher to Matcher if T - /// is derived from Base. - template - class ImplicitCastMatcher : public MatcherInterface { - public: - explicit ImplicitCastMatcher(const Matcher &From) - : From(From) {} - - bool matches(const T &Node, ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const override { - return From.matches(Node, Finder, Builder); - } - - private: - const Matcher From; - }; - - IntrusiveRefCntPtr< MatcherInterface > Implementation; -}; // class Matcher - -/// \brief A convenient helper for creating a Matcher without specifying -/// the template type argument. -template -inline Matcher makeMatcher(MatcherInterface *Implementation) { - return Matcher(Implementation); -} - -template class BindableMatcher; +template class Matcher; /// \brief Matcher that works on a \c DynTypedNode. /// @@ -334,13 +255,12 @@ /// return false if it is not convertible. class DynTypedMatcher { public: - /// \brief Construct from a \c Matcher. Copies the matcher. - template inline DynTypedMatcher(const Matcher &M); - - /// \brief Construct from a bindable \c Matcher. Copies the matcher. - /// - /// This version enables \c tryBind() on the \c DynTypedMatcher. - template inline DynTypedMatcher(const BindableMatcher &M); + /// \brief Takes ownership of the provided implementation pointer. + template + DynTypedMatcher(MatcherInterface *Implementation) + : AllowBind(false), + SupportedKind(ast_type_traits::ASTNodeKind::getFromNodeKind()), + RestrictKind(SupportedKind), Implementation(Implementation) {} /// \brief Construct from a variadic function. typedef bool (*VariadicOperatorFunction)( @@ -348,33 +268,42 @@ BoundNodesTreeBuilder *Builder, ArrayRef InnerMatchers); static DynTypedMatcher constructVariadic(VariadicOperatorFunction Func, - ArrayRef InnerMatchers) { - assert(InnerMatchers.size() > 0 && "Array must not be empty."); - return DynTypedMatcher(new VariadicStorage(Func, InnerMatchers)); - } + std::vector InnerMatchers); + + void setAllowBind(bool AB) { AllowBind = AB; } + + /// \brief Return a matcher that points to the same implementation, but + /// restricts the node types for \p Kind. + DynTypedMatcher dynCastTo(const ast_type_traits::ASTNodeKind Kind) const; /// \brief Returns true if the matcher matches the given \c DynNode. bool matches(const ast_type_traits::DynTypedNode DynNode, - ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { - return Storage->matches(DynNode, Finder, Builder); - } + ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const; /// \brief Bind the specified \p ID to the matcher. /// \return A new matcher with the \p ID bound to it if this matcher supports /// binding. Otherwise, returns an empty \c Optional<>. - llvm::Optional tryBind(StringRef ID) const { - return Storage->tryBind(ID); - } + llvm::Optional tryBind(StringRef ID) const; /// \brief Returns a unique \p ID for the matcher. - uint64_t getID() const { return Storage->getID(); } + /// + /// Different matchers might have the same \c Implementation pointer with a + /// different \c RestrictKind, so we need to include both in the ID. + /// \c MatcherIDType supports operator< and provides strict weak ordering. + typedef std::pair MatcherIDType; + MatcherIDType getID() const { + /// FIXME: Document the requirements this imposes on matcher + /// implementations (no new() implementation_ during a Matches()). + return std::make_pair(RestrictKind, + reinterpret_cast(Implementation.get())); + } /// \brief Returns the type this matcher works on. /// /// \c matches() will always return false unless the node passed is of this /// or a derived type. ast_type_traits::ASTNodeKind getSupportedKind() const { - return Storage->getSupportedKind(); + return SupportedKind; } /// \brief Returns \c true if the passed \c DynTypedMatcher can be converted @@ -404,96 +333,119 @@ template Matcher unconditionalConvertTo() const; private: - class MatcherStorage : public RefCountedBaseVPTR { - public: - MatcherStorage(ast_type_traits::ASTNodeKind SupportedKind, uint64_t ID) - : SupportedKind(SupportedKind), ID(ID) {} - virtual ~MatcherStorage(); - - virtual bool matches(const ast_type_traits::DynTypedNode DynNode, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const = 0; - - virtual llvm::Optional tryBind(StringRef ID) const = 0; - - ast_type_traits::ASTNodeKind getSupportedKind() const { - return SupportedKind; - } - - uint64_t getID() const { return ID; } + bool AllowBind; + ast_type_traits::ASTNodeKind SupportedKind; + /// \brief A potentially stricter node kind. + /// + /// It allows to perform implicit and dynamic cast of matchers without + /// needing to change \c Implementation. + ast_type_traits::ASTNodeKind RestrictKind; + IntrusiveRefCntPtr Implementation; +}; - private: - const ast_type_traits::ASTNodeKind SupportedKind; - const uint64_t ID; - }; +/// \brief Wrapper of a MatcherInterface *that allows copying. +/// +/// A Matcher can be used anywhere a Matcher is +/// required. This establishes an is-a relationship which is reverse +/// to the AST hierarchy. In other words, Matcher is contravariant +/// with respect to T. The relationship is built via a type conversion +/// operator rather than a type hierarchy to be able to templatize the +/// type hierarchy instead of spelling it out. +template +class Matcher { +public: + /// \brief Takes ownership of the provided implementation pointer. + explicit Matcher(MatcherInterface *Implementation) + : Implementation(Implementation) {} - class VariadicStorage : public MatcherStorage { - public: - VariadicStorage(VariadicOperatorFunction Func, - ArrayRef InnerMatchers) - : MatcherStorage(InnerMatchers[0].getSupportedKind(), - reinterpret_cast(this)), - Func(Func), InnerMatchers(InnerMatchers) {} - - bool matches(const ast_type_traits::DynTypedNode DynNode, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const override { - return Func(DynNode, Finder, Builder, InnerMatchers); - } + /// \brief Implicitly converts \c Other to a Matcher. + /// + /// Requires \c T to be derived from \c From. + template + Matcher(const Matcher &Other, + typename std::enable_if::value && + !std::is_same::value>::type * = 0) + : Matcher(Other.Implementation) {} - llvm::Optional tryBind(StringRef ID) const override { - return llvm::None; - } + /// \brief Implicitly converts \c Matcher to \c Matcher. + /// + /// The resulting matcher is not strict, i.e. ignores qualifiers. + template + Matcher(const Matcher &Other, + typename std::enable_if< + std::is_same::value && + std::is_same::value>::type* = 0) + : Implementation(new TypeToQualType(Other)) {} - private: - VariadicOperatorFunction Func; - std::vector InnerMatchers; - }; + /// \brief Convert \c this into a \c Matcher by applying dyn_cast<> to the + /// argument. + /// \c To must be a base class of \c T. + template ::value>::type * = nullptr> + Matcher dynCastTo() const { + return Matcher(Implementation); + } - /// \brief Typed implementation of \c MatcherStorage. - template class TypedMatcherStorage; + /// \brief Forwards the call to the underlying MatcherInterface pointer. + bool matches(const T &Node, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + return Implementation.matches(ast_type_traits::DynTypedNode::create(Node), + Finder, Builder); + } - /// \brief Internal constructor for \c constructVariadic. - DynTypedMatcher(MatcherStorage *Storage) : Storage(Storage) {} + /// \brief Returns an ID that uniquely identifies the matcher. + DynTypedMatcher::MatcherIDType getID() const { + return Implementation.getID(); + } - IntrusiveRefCntPtr Storage; -}; + /// \brief Extract the dynamic matcher. + /// + /// The returned matcher keeps the same restrictions as \c this and remembers + /// that it is meant to support nodes of type \c T. + operator DynTypedMatcher() const { return Implementation; } -template -class DynTypedMatcher::TypedMatcherStorage : public MatcherStorage { -public: - TypedMatcherStorage(const Matcher &Other, bool AllowBind) - : MatcherStorage(ast_type_traits::ASTNodeKind::getFromNodeKind(), - Other.getID()), - InnerMatcher(Other), AllowBind(AllowBind) {} + /// \brief Allows the conversion of a \c Matcher to a \c + /// Matcher. + /// + /// Depending on the constructor argument, the matcher is either strict, i.e. + /// does only matches in the absence of qualifiers, or not, i.e. simply + /// ignores any qualifiers. + template + class TypeToQualType : public MatcherInterface { + public: + TypeToQualType(const Matcher &InnerMatcher) + : InnerMatcher(InnerMatcher) {} - bool matches(const ast_type_traits::DynTypedNode DynNode, - ASTMatchFinder *Finder, - BoundNodesTreeBuilder *Builder) const override { - if (const T *Node = DynNode.get()) { + bool matches(const QualType &Node, ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + if (Node.isNull()) + return false; return InnerMatcher.matches(*Node, Finder, Builder); } - return false; - } - - llvm::Optional tryBind(StringRef ID) const override { - if (!AllowBind) - return llvm::Optional(); - return DynTypedMatcher(BindableMatcher(InnerMatcher).bind(ID)); - } + private: + const Matcher InnerMatcher; + }; private: - const Matcher InnerMatcher; - const bool AllowBind; -}; + template friend class Matcher; -template -inline DynTypedMatcher::DynTypedMatcher(const Matcher &M) - : Storage(new TypedMatcherStorage(M, false)) {} + explicit Matcher(const DynTypedMatcher &Implementation) + : Implementation(Implementation.dynCastTo( + ast_type_traits::ASTNodeKind::getFromNodeKind())) { + assert(this->Implementation.getSupportedKind() + .isSame(ast_type_traits::ASTNodeKind::getFromNodeKind())); + } + DynTypedMatcher Implementation; +}; // class Matcher + +/// \brief A convenient helper for creating a Matcher without specifying +/// the template type argument. template -inline DynTypedMatcher::DynTypedMatcher(const BindableMatcher &M) - : Storage(new TypedMatcherStorage(M, true)) {} +inline Matcher makeMatcher(MatcherInterface *Implementation) { + return Matcher(Implementation); +} /// \brief Specialization of the conversion functions for QualType. /// @@ -1054,7 +1006,7 @@ BoundNodesTreeBuilder *Builder) const override { bool Result = InnerMatcher.matches(Node, Finder, Builder); if (Result) { - Builder->setBinding(ID, &Node); + Builder->setBinding(ID, ast_type_traits::DynTypedNode::create(Node)); } return Result; } @@ -1080,8 +1032,18 @@ /// The returned matcher is equivalent to this matcher, but will /// bind the matched node on a match. Matcher bind(StringRef ID) const { + // FIXME: Use DynTypedMatcher's IdMatcher instead. No need for a template + // version anymore. return Matcher(new IdMatcher(ID, *this)); } + + /// \brief Same as Matcher's conversion operator, but enables binding on + /// the returned matcher. + operator DynTypedMatcher() const { + DynTypedMatcher Result = static_cast&>(*this); + Result.setAllowBind(true); + return Result; + } }; /// \brief Matches nodes of type T that have child nodes of type ChildT for @@ -1206,6 +1168,7 @@ addMatcher(Param7, Matchers); addMatcher(Param8, Matchers); addMatcher(Param9, Matchers); + // FIXME: Use DynTypedMatcher::constructVariadic() instead. return Matcher( new VariadicOperatorMatcherInterface(Func, std::move(Matchers))); } @@ -1344,6 +1307,7 @@ template inline Matcher DynTypedMatcher::unconditionalConvertTo() const { + // FIXME: Remove this extra indirection and connect directly to Matcher(). return Matcher(new VariadicOperatorMatcherInterface( AllOfVariadicOperator, llvm::makeArrayRef(*this))); } @@ -1352,10 +1316,12 @@ template BindableMatcher makeAllOfComposite( ArrayRef *> InnerMatchers) { + // FIXME: Optimize for the cases of size()==0 and size()==1 std::vector DynMatchers; for (size_t i = 0, e = InnerMatchers.size(); i != e; ++i) { DynMatchers.push_back(*InnerMatchers[i]); } + // FIXME: Use DynTypedMatcher::constructVariadic() instead. return BindableMatcher(new VariadicOperatorMatcherInterface( AllOfVariadicOperator, std::move(DynMatchers))); } @@ -1369,8 +1335,8 @@ template BindableMatcher makeDynCastAllOfComposite( ArrayRef *> InnerMatchers) { - return BindableMatcher(DynTypedMatcher(makeAllOfComposite(InnerMatchers)) - .unconditionalConvertTo()); + return BindableMatcher( + makeAllOfComposite(InnerMatchers).template dynCastTo()); } /// \brief Matches nodes of type T that have at least one descendant node of Index: lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- lib/ASTMatchers/ASTMatchFinder.cpp +++ lib/ASTMatchers/ASTMatchFinder.cpp @@ -53,7 +53,7 @@ // FIXME: Benchmark whether memoization of non-pointer typed nodes // provides enough benefit for the additional amount of code. struct MatchKey { - uint64_t MatcherID; + DynTypedMatcher::MatcherIDType MatcherID; ast_type_traits::DynTypedNode Node; BoundNodesTreeBuilder BoundNodes; Index: lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- lib/ASTMatchers/ASTMatchersInternal.cpp +++ lib/ASTMatchers/ASTMatchersInternal.cpp @@ -26,6 +26,113 @@ } } +namespace { + +class VariadicMatcher : public DynMatcherInterface { + public: + VariadicMatcher(VariadicOperatorFunction Func, + std::vector InnerMatchers) + : Func(Func), InnerMatchers(std::move(InnerMatchers)) {} + + bool matches(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + return Func(DynNode, Finder, Builder, InnerMatchers); + } + + private: + VariadicOperatorFunction Func; + std::vector InnerMatchers; +}; + +class IdDynMatcher : public DynMatcherInterface { + public: + IdDynMatcher(StringRef ID, + const IntrusiveRefCntPtr &InnerMatcher) + : ID(ID), InnerMatcher(InnerMatcher) {} + + bool matches(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const override { + bool Result = InnerMatcher->matches(DynNode, Finder, Builder); + if (Result) Builder->setBinding(ID, DynNode); + return Result; + } + + private: + const std::string ID; + const IntrusiveRefCntPtr InnerMatcher; +}; + +/// \brief Return the most derived type between \p Kind1 and \p Kind2. +/// +/// Return the null type if they are not related. +ast_type_traits::ASTNodeKind getMostDerivedType( + const ast_type_traits::ASTNodeKind Kind1, + const ast_type_traits::ASTNodeKind Kind2) { + if (Kind1.isBaseOf(Kind2)) return Kind2; + if (Kind2.isBaseOf(Kind1)) return Kind1; + return ast_type_traits::ASTNodeKind(); +} + +/// \brief Return the least derived type between \p Kind1 and \p Kind2. +/// +/// Return the null type if they are not related. +static ast_type_traits::ASTNodeKind getLeastDerivedType( + const ast_type_traits::ASTNodeKind Kind1, + const ast_type_traits::ASTNodeKind Kind2) { + if (Kind1.isBaseOf(Kind2)) return Kind1; + if (Kind2.isBaseOf(Kind1)) return Kind2; + return ast_type_traits::ASTNodeKind(); +} + +} // namespace + +DynTypedMatcher DynTypedMatcher::constructVariadic( + VariadicOperatorFunction Func, std::vector InnerMatchers) { + assert(InnerMatchers.size() > 0 && "Array must not be empty."); + DynTypedMatcher Result = InnerMatchers[0]; + // Use the least derived type as the restriction for the wrapper. + // This allows mismatches to be resolved on the inner matchers. + for (const DynTypedMatcher &M : InnerMatchers) { + assert(Result.SupportedKind.isSame(M.SupportedKind) && + "SupportedKind must match!"); + Result.RestrictKind = + getLeastDerivedType(Result.RestrictKind, M.RestrictKind); + } + Result.Implementation = new VariadicMatcher(Func, std::move(InnerMatchers)); + return Result; +} + +DynTypedMatcher DynTypedMatcher::dynCastTo( + const ast_type_traits::ASTNodeKind Kind) const { + auto Copy = *this; + Copy.SupportedKind = Kind; + Copy.RestrictKind = getMostDerivedType(Kind, RestrictKind); + return Copy; +} + +bool DynTypedMatcher::matches(const ast_type_traits::DynTypedNode DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder) const { + if (RestrictKind.isBaseOf(DynNode.getNodeKind()) && + Implementation->matches(DynNode, Finder, Builder)) { + return true; + } + // Delete all bindings when a matcher does not match. + // This prevents unexpected exposure of bound nodes in unmatches + // branches of the match tree. + Builder->removeBindings([](const BoundNodesMap &) { return true; }); + return false; +} + +llvm::Optional DynTypedMatcher::tryBind(StringRef ID) const { + if (!AllowBind) return llvm::None; + auto Result = *this; + Result.Implementation = new IdDynMatcher(ID, Result.Implementation); + return Result; +} + bool DynTypedMatcher::canConvertTo(ast_type_traits::ASTNodeKind To) const { const auto From = getSupportedKind(); auto QualKind = ast_type_traits::ASTNodeKind::getFromNodeKind(); @@ -37,8 +144,6 @@ return From.isBaseOf(To); } -DynTypedMatcher::MatcherStorage::~MatcherStorage() {} - void BoundNodesTreeBuilder::addMatch(const BoundNodesTreeBuilder &Other) { for (unsigned i = 0, e = Other.Bindings.size(); i != e; ++i) { Bindings.push_back(Other.Bindings[i]); Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -654,6 +654,20 @@ EXPECT_TRUE(matches("void f() { int i; }", CannotMemoize)); } +TEST(DeclarationMatcher, HasDescendantMemoizationUsesRestrictKind) { + auto Name = hasName("i"); + auto VD = internal::Matcher(Name).dynCastTo(); + auto RD = internal::Matcher(Name).dynCastTo(); + // Matching VD first should not make a cache hit for RD. + EXPECT_TRUE(notMatches("void f() { int i; }", + decl(hasDescendant(VD), hasDescendant(RD)))); + EXPECT_TRUE(notMatches("void f() { int i; }", + decl(hasDescendant(RD), hasDescendant(VD)))); + // Not matching RD first should not make a cache hit for VD either. + EXPECT_TRUE(matches("void f() { int i; }", + decl(anyOf(hasDescendant(RD), hasDescendant(VD))))); +} + TEST(DeclarationMatcher, HasAttr) { EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};", decl(hasAttr(clang::attr::WarnUnused)))); Index: unittests/ASTMatchers/Dynamic/ParserTest.cpp =================================================================== --- unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -28,7 +28,7 @@ uint64_t expectMatcher(StringRef MatcherName) { ast_matchers::internal::Matcher M = stmt(); ExpectedMatchers.insert(std::make_pair(MatcherName, M)); - return M.getID(); + return M.getID().second; } void parse(StringRef Code) { @@ -125,8 +125,12 @@ EXPECT_EQ("", Sema.Errors[i]); } + EXPECT_NE(ExpectedFoo, ExpectedBar); + EXPECT_NE(ExpectedFoo, ExpectedBaz); + EXPECT_NE(ExpectedBar, ExpectedBaz); + EXPECT_EQ(1ULL, Sema.Values.size()); - EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID()); + EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID().second); EXPECT_EQ(3ULL, Sema.Matchers.size()); const MockSema::MatcherInfo Bar = Sema.Matchers[0]; @@ -145,8 +149,8 @@ EXPECT_EQ("Foo", Foo.MatcherName); EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); EXPECT_EQ(2ULL, Foo.Args.size()); - EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID()); - EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID()); + EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID().second); + EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID().second); EXPECT_EQ("Yo!", Foo.BoundID); }