Index: cfe/trunk/lib/AST/ASTContext.cpp =================================================================== --- cfe/trunk/lib/AST/ASTContext.cpp +++ cfe/trunk/lib/AST/ASTContext.cpp @@ -8159,10 +8159,12 @@ if (!Node) return true; if (ParentStack.size() > 0) { - // FIXME: Currently we add the same parent multiple times, for example - // when we visit all subexpressions of template instantiations; this is - // suboptimal, bug benign: the only way to visit those is with - // hasAncestor / hasParent, and those do not create new matches. + // FIXME: Currently we add the same parent multiple times, but only + // when no memoization data is available for the type. + // For example when we visit all subexpressions of template + // instantiations; this is suboptimal, but benign: the only way to + // visit those is with hasAncestor / hasParent, and those do not create + // new matches. // The plan is to enable DynTypedNode to be storable in a map or hash // map. The main problem there is to implement hash functions / // comparison operators for all types that DynTypedNode supports that @@ -8170,18 +8172,27 @@ auto &NodeOrVector = (*Parents)[Node]; if (NodeOrVector.isNull()) { NodeOrVector = new ast_type_traits::DynTypedNode(ParentStack.back()); - } else if (NodeOrVector - .template is()) { - auto *Node = - NodeOrVector.template get(); - auto *Vector = new ASTContext::ParentVector(1, *Node); - Vector->push_back(ParentStack.back()); - NodeOrVector = Vector; - delete Node; } else { + if (NodeOrVector.template is()) { + auto *Node = + NodeOrVector.template get(); + auto *Vector = new ASTContext::ParentVector(1, *Node); + NodeOrVector = Vector; + delete Node; + } assert(NodeOrVector.template is()); - NodeOrVector.template get()->push_back( - ParentStack.back()); + + auto *Vector = + NodeOrVector.template get(); + // Skip duplicates for types that have memoization data. + // We must check that the type has memoization data before calling + // std::find() because DynTypedNode::operator== can't compare all + // types. + bool Found = ParentStack.back().getMemoizationData() && + std::find(Vector->begin(), Vector->end(), + ParentStack.back()) != Vector->end(); + if (!Found) + Vector->push_back(ParentStack.back()); } } ParentStack.push_back(ast_type_traits::DynTypedNode::create(*Node)); Index: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp +++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -3623,6 +3623,27 @@ compoundStmt(hasParent(recordDecl())))); } +TEST(HasParent, NoDuplicateParents) { + class HasDuplicateParents : public BoundNodesCallback { + public: + bool run(const BoundNodes *Nodes) override { return false; } + bool run(const BoundNodes *Nodes, ASTContext *Context) override { + const Stmt *Node = Nodes->getNodeAs("node"); + std::set Parents; + for (const auto &Parent : Context->getParents(*Node)) { + if (!Parents.insert(Parent.getMemoizationData()).second) { + return true; + } + } + return false; + } + }; + EXPECT_FALSE(matchAndVerifyResultTrue( + "template int Foo() { return 1 + 2; }\n" + "int x = Foo() + Foo();", + stmt().bind("node"), new HasDuplicateParents())); +} + TEST(TypeMatching, MatchesTypes) { EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); }