// optimize this on.
static const unsigned MaxMemoizationEntries = 10000;
+enum class MatchType {
+ Ancestors,
+ Descendants
+};
+
// 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
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);
}
};
BoundNodesTreeBuilder *Builder, int MaxDepth,
TraversalKind Traversal, BindKind Bind) {
// For AST-nodes that don't have an identity, we can't memoize.
- if (!Node.getMemoizationData() || !Builder->isComparable())
+ // When doing a single-level match, we don't need to memoize
+ if (!Node.getMemoizationData() || !Builder->isComparable() || MaxDepth == 1)
return matchesRecursively(Node, Matcher, Builder, MaxDepth, Traversal,
Bind);
// Note that we key on the bindings *before* the match.
Key.BoundNodes = *Builder;
Key.Traversal = Ctx.getParentMapContext().getTraversalKind();
-
+ Key.Type = MatchType::Descendants;
MemoizationMap::iterator I = ResultCache.find(Key);
if (I != ResultCache.end()) {
*Builder = I->second.Nodes;
BoundNodesTreeBuilder *Builder,
AncestorMatchMode MatchMode) {
// For AST-nodes that don't have an identity, we can't memoize.
- if (!Builder->isComparable())
+ // When doing a single-level match, we don't need to memoize
+ if (!Builder->isComparable() || MatchMode == AncestorMatchMode::AMM_ParentOnly)
return matchesAncestorOfRecursively(Node, Ctx, Matcher, Builder,
MatchMode);
Key.Node = Node;
Key.BoundNodes = *Builder;
Key.Traversal = Ctx.getParentMapContext().getTraversalKind();
+ Key.Type = MatchType::Ancestors;
// Note that we cannot use insert and reuse the iterator, as recursive
// calls to match might invalidate the result cache iterators.
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 <typename T> struct C { static void f() { 42; } };"