Index: clang/lib/ASTMatchers/ASTMatchFinder.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchFinder.cpp +++ clang/lib/ASTMatchers/ASTMatchFinder.cpp @@ -43,6 +43,13 @@ // optimize this on. static const unsigned MaxMemoizationEntries = 10000; +enum class MatchType { + Ancestors, + Descendants, + Parent, + Child +}; + // We use memoization to avoid running the same matcher on the same // AST node twice. This struct is the key for looking up match // result. It consists of an ID of the MatcherInterface (for @@ -60,10 +67,11 @@ DynTypedNode Node; BoundNodesTreeBuilder BoundNodes; TraversalKind Traversal = TK_AsIs; + MatchType Type; bool operator<(const MatchKey &Other) const { - return std::tie(Traversal, MatcherID, Node, BoundNodes) < - std::tie(Other.Traversal, Other.MatcherID, Other.Node, + return std::tie(Traversal, Type, MatcherID, Node, BoundNodes) < + std::tie(Other.Traversal, Other.Type, Other.MatcherID, Other.Node, Other.BoundNodes); } }; @@ -456,7 +464,7 @@ // Note that we key on the bindings *before* the match. Key.BoundNodes = *Builder; Key.Traversal = Ctx.getParentMapContext().getTraversalKind(); - + Key.Type = MaxDepth == 1 ? MatchType::Child : MatchType::Descendants; MemoizationMap::iterator I = ResultCache.find(Key); if (I != ResultCache.end()) { *Builder = I->second.Nodes; @@ -702,6 +710,8 @@ Key.Node = Node; Key.BoundNodes = *Builder; Key.Traversal = Ctx.getParentMapContext().getTraversalKind(); + Key.Type = MatchMode == AncestorMatchMode::AMM_ParentOnly ? + MatchType::Parent : MatchType::Ancestors; // Note that we cannot use insert and reuse the iterator, as recursive // calls to match might invalidate the result cache iterators. Index: clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2689,6 +2689,34 @@ compoundStmt(hasParent(ifStmt())))); } +TEST(MatcherMemoize, HasParentDiffersFromHas) { + // Test introduced after detecting a bug in memoization + constexpr auto code = "void f() { throw 1; }"; + EXPECT_TRUE(notMatches( + code, + cxxThrowExpr(hasParent(expr())))); + EXPECT_TRUE(matches( + code, + cxxThrowExpr(has(expr())))); + EXPECT_TRUE(matches( + code, + cxxThrowExpr(anyOf(hasParent(expr()), has(expr()))))); +} + +TEST(MatcherMemoize, HasDiffersFromHasDescendant) { + // Test introduced after detecting a bug in memoization + constexpr auto code = "void f() { throw 1+1; }"; + EXPECT_TRUE(notMatches( + code, + cxxThrowExpr(has(integerLiteral())))); + EXPECT_TRUE(matches( + code, + cxxThrowExpr(hasDescendant(integerLiteral())))); + EXPECT_TRUE(notMatches(code, + cxxThrowExpr(allOf( + hasDescendant(integerLiteral()), + has(integerLiteral()))))); +} TEST(HasAncestor, MatchesAllAncestors) { EXPECT_TRUE(matches( "template struct C { static void f() { 42; } };"