diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -347,12 +347,18 @@ BoundNodesTreeBuilder *Builder, ArrayRef InnerMatchers) { BoundNodesTreeBuilder Result; + bool Matched = false; for (const DynTypedMatcher &InnerMatcher : InnerMatchers) { BoundNodesTreeBuilder BuilderInner(*Builder); - if (InnerMatcher.matches(DynNode, Finder, &BuilderInner)) + if (InnerMatcher.matches(DynNode, Finder, &BuilderInner)) { + Matched = true; Result.addMatch(BuilderInner); + } } - *Builder = std::move(Result); + // If there were no matches, we can't assign to `*Builder`; we'd (incorrectly) + // clear it because `Result` is empty. + if (Matched) + *Builder = std::move(Result); return true; } diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -2012,6 +2012,19 @@ std::make_unique>("v"))); } +// Regression test. +TEST(Optionally, SubmatchersDoNotMatchButPreserveBindings) { + std::string Code = "class A { int a; int b; };"; + auto Matcher = recordDecl(decl().bind("decl"), + optionally(has(fieldDecl(hasName("c")).bind("v")))); + // "decl" is still bound. + EXPECT_TRUE(matchAndVerifyResultTrue( + Code, Matcher, std::make_unique>("decl"))); + // "v" is not bound, but the match still suceeded. + EXPECT_TRUE(matchAndVerifyResultFalse( + Code, Matcher, std::make_unique>("v"))); +} + TEST(Optionally, SubmatchersMatch) { EXPECT_TRUE(matchAndVerifyResultTrue( "class A { int a; int c; };",