[clang] 9809c6c - Add `isInitCapture` and `forEachLambdaCapture` matchers.
Yitzhak Mandelbaum via cfe-commits
cfe-commits at lists.llvm.org
Mon Nov 15 14:55:41 PST 2021
Author: James King
Date: 2021-11-15T22:55:28Z
New Revision: 9809c6c61cebbfcd100a3afd30fc9009f68d4678
URL: https://github.com/llvm/llvm-project/commit/9809c6c61cebbfcd100a3afd30fc9009f68d4678
DIFF: https://github.com/llvm/llvm-project/commit/9809c6c61cebbfcd100a3afd30fc9009f68d4678.diff
LOG: Add `isInitCapture` and `forEachLambdaCapture` matchers.
This contributes follow-up work from https://reviews.llvm.org/D112491, which
allows for increased control over the matching of lambda captures. This also
updates the documentation for the `lambdaCapture` matcher.
Reviewed By: ymandel, aaron.ballman
Differential Revision: https://reviews.llvm.org/D113575
Added:
Modified:
clang/docs/LibASTMatchersReference.html
clang/include/clang/ASTMatchers/ASTMatchers.h
clang/lib/ASTMatchers/Dynamic/Registry.cpp
clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 9d2398effff10..8f310d42be1c9 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1191,7 +1191,17 @@ <h2 id="decl-matchers">Node Matchers</h2>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LambdaCapture.html">LambdaCapture</a>></td><td class="name" onclick="toggle('lambdaCapture0')"><a name="lambdaCapture0Anchor">lambdaCapture</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LambdaCapture.html">LambdaCapture</a>>...</td></tr>
-<tr><td colspan="4" class="doc" id="lambdaCapture0"><pre></pre></td></tr>
+<tr><td colspan="4" class="doc" id="lambdaCapture0"><pre>Matches lambda captures.
+
+Given
+ int main() {
+ int x;
+ auto f = [x](){};
+ auto g = [x = 1](){};
+ }
+In the matcher `lambdaExpr(hasAnyCapture(lambdaCapture()))`,
+`lambdaCapture()` matches `x` and `x=1`.
+</pre></td></tr>
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>></td><td class="name" onclick="toggle('nestedNameSpecifierLoc0')"><a name="nestedNameSpecifierLoc0Anchor">nestedNameSpecifierLoc</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1NestedNameSpecifierLoc.html">NestedNameSpecifierLoc</a>>...</td></tr>
@@ -4540,6 +4550,21 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LambdaExpr.html">LambdaExpr</a>></td><td class="name" onclick="toggle('forEachLambdaCapture0')"><a name="forEachLambdaCapture0Anchor">forEachLambdaCapture</a></td><td>LambdaCaptureMatcher InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="forEachLambdaCapture0"><pre>Matches each lambda capture in a lambda expression.
+
+Given
+ int main() {
+ int x, y;
+ float z;
+ auto f = [=]() { return x + y + z; };
+ }
+lambdaExpr(forEachLambdaCapture(
+ lambdaCapture(capturesVar(varDecl(hasType(isInteger()))))))
+will trigger two matches, binding for 'x' and 'y' respectively.
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1LambdaExpr.html">LambdaExpr</a>></td><td class="name" onclick="toggle('hasAnyCapture0')"><a name="hasAnyCapture0Anchor">hasAnyCapture</a></td><td>LambdaCaptureMatcher InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasAnyCapture0"><pre>Matches any capture in a lambda expression.
@@ -5666,6 +5691,15 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
</pre></td></tr>
+<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>></td><td class="name" onclick="toggle('isInitCapture0')"><a name="isInitCapture0Anchor">isInitCapture</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isInitCapture0"><pre>Matches a variable serving as the implicit variable for a lambda init-
+capture.
+
+Example matches x (matcher = varDecl(isInitCapture()))
+auto f = [x=3]() { return x; };
+</pre></td></tr>
+
+
<tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1VarDecl.html">VarDecl</a>></td><td class="name" onclick="toggle('isStaticLocal0')"><a name="isStaticLocal0Anchor">isStaticLocal</a></td><td></td></tr>
<tr><td colspan="4" class="doc" id="isStaticLocal0"><pre>Matches a static variable with local scope.
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index aa49d4cf5e962..d6e5b215462b2 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -4205,6 +4205,45 @@ AST_MATCHER_P(
InnerMatcher.matches(*Initializer, Finder, Builder));
}
+/// Matches a variable serving as the implicit variable for a lambda init-
+/// capture.
+///
+/// Example matches x (matcher = varDecl(isInitCapture()))
+/// \code
+/// auto f = [x=3]() { return x; };
+/// \endcode
+AST_MATCHER(VarDecl, isInitCapture) { return Node.isInitCapture(); }
+
+/// Matches each lambda capture in a lambda expression.
+///
+/// Given
+/// \code
+/// int main() {
+/// int x, y;
+/// float z;
+/// auto f = [=]() { return x + y + z; };
+/// }
+/// \endcode
+/// lambdaExpr(forEachLambdaCapture(
+/// lambdaCapture(capturesVar(varDecl(hasType(isInteger()))))))
+/// will trigger two matches, binding for 'x' and 'y' respectively.
+AST_MATCHER_P(LambdaExpr, forEachLambdaCapture, LambdaCaptureMatcher,
+ InnerMatcher) {
+ BoundNodesTreeBuilder Result;
+ bool Matched = false;
+ for (const auto &Capture : Node.captures()) {
+ if (Finder->isTraversalIgnoringImplicitNodes() && Capture.isImplicit())
+ continue;
+ BoundNodesTreeBuilder CaptureBuilder(*Builder);
+ if (InnerMatcher.matches(Capture, Finder, &CaptureBuilder)) {
+ Matched = true;
+ Result.addMatch(CaptureBuilder);
+ }
+ }
+ *Builder = std::move(Result);
+ return Matched;
+}
+
/// \brief Matches a static variable with local scope.
///
/// Example matches y (matcher = varDecl(isStaticLocal()))
@@ -4590,6 +4629,18 @@ AST_POLYMORPHIC_MATCHER_P(hasAnyArgument,
return false;
}
+/// Matches lambda captures.
+///
+/// Given
+/// \code
+/// int main() {
+/// int x;
+/// auto f = [x](){};
+/// auto g = [x = 1](){};
+/// }
+/// \endcode
+/// In the matcher `lambdaExpr(hasAnyCapture(lambdaCapture()))`,
+/// `lambdaCapture()` matches `x` and `x=1`.
extern const internal::VariadicAllOfMatcher<LambdaCapture> lambdaCapture;
/// Matches any capture in a lambda expression.
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 395051e0dfae6..878547923d27e 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -246,6 +246,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(forEachArgumentWithParamType);
REGISTER_MATCHER(forEachConstructorInitializer);
REGISTER_MATCHER(forEachDescendant);
+ REGISTER_MATCHER(forEachLambdaCapture);
REGISTER_MATCHER(forEachOverridden);
REGISTER_MATCHER(forEachSwitchCase);
REGISTER_MATCHER(forField);
@@ -424,6 +425,7 @@ RegistryMaps::RegistryMaps() {
REGISTER_MATCHER(isImplicit);
REGISTER_MATCHER(isInStdNamespace);
REGISTER_MATCHER(isInTemplateInstantiation);
+ REGISTER_MATCHER(isInitCapture);
REGISTER_MATCHER(isInline);
REGISTER_MATCHER(isInstanceMessage);
REGISTER_MATCHER(isInstanceMethod);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index 05a01f6735a8c..f604d0a19e18f 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -1439,6 +1439,22 @@ TEST_P(ASTMatchersTest, IsStaticLocal) {
EXPECT_TRUE(notMatches("int X;", M));
}
+TEST_P(ASTMatchersTest, IsInitCapture) {
+ if (!GetParam().isCXX11OrLater()) {
+ return;
+ }
+ auto M = varDecl(hasName("vd"), isInitCapture());
+ EXPECT_TRUE(notMatches(
+ "int main() { int vd = 3; auto f = [vd]() { return vd; }; }", M));
+
+ if (!GetParam().isCXX14OrLater()) {
+ return;
+ }
+ EXPECT_TRUE(matches("int main() { auto f = [vd=3]() { return vd; }; }", M));
+ EXPECT_TRUE(matches(
+ "int main() { int x = 3; auto f = [vd=x]() { return vd; }; }", M));
+}
+
TEST_P(ASTMatchersTest, StorageDuration) {
StringRef T =
"void f() { int x; static int y; } int a;static int b;extern int c;";
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index e1540e9096e63..e6ddf200c369c 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -4769,6 +4769,66 @@ TEST(ForEachConstructorInitializer, MatchesInitializers) {
cxxConstructorDecl(forEachConstructorInitializer(cxxCtorInitializer()))));
}
+TEST(ForEachLambdaCapture, MatchesCaptures) {
+ EXPECT_TRUE(matches(
+ "int main() { int x, y; auto f = [x, y]() { return x + y; }; }",
+ lambdaExpr(forEachLambdaCapture(lambdaCapture())), langCxx11OrLater()));
+ auto matcher = lambdaExpr(forEachLambdaCapture(
+ lambdaCapture(capturesVar(varDecl(hasType(isInteger())))).bind("LC")));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y; float z; auto f = [=]() { return x + y + z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y; float z; auto f = [x, y, z]() { return x + y + "
+ "z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 2)));
+}
+
+TEST(ForEachLambdaCapture, IgnoreUnlessSpelledInSource) {
+ auto matcher =
+ traverse(TK_IgnoreUnlessSpelledInSource,
+ lambdaExpr(forEachLambdaCapture(
+ lambdaCapture(capturesVar(varDecl(hasType(isInteger()))))
+ .bind("LC"))));
+ EXPECT_TRUE(
+ notMatches("int main() { int x, y; auto f = [=]() { return x + y; }; }",
+ matcher, langCxx11OrLater()));
+ EXPECT_TRUE(
+ notMatches("int main() { int x, y; auto f = [&]() { return x + y; }; }",
+ matcher, langCxx11OrLater()));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ R"cc(
+ int main() {
+ int x, y;
+ float z;
+ auto f = [=, &y]() { return x + y + z; };
+ }
+ )cc",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 1)));
+}
+
+TEST(ForEachLambdaCapture, MatchImplicitCapturesOnly) {
+ auto matcher =
+ lambdaExpr(forEachLambdaCapture(lambdaCapture(isImplicit()).bind("LC")));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y, z; auto f = [=, &z]() { return x + y + z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y, z; auto f = [&, z]() { return x + y + z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 2)));
+}
+
+TEST(ForEachLambdaCapture, MatchExplicitCapturesOnly) {
+ auto matcher = lambdaExpr(
+ forEachLambdaCapture(lambdaCapture(unless(isImplicit())).bind("LC")));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y, z; auto f = [=, &z]() { return x + y + z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "int main() { int x, y, z; auto f = [&, z]() { return x + y + z; }; }",
+ matcher, std::make_unique<VerifyIdIsBoundTo<LambdaCapture>>("LC", 1)));
+}
+
TEST(HasConditionVariableStatement, DoesNotMatchCondition) {
EXPECT_TRUE(notMatches(
"void x() { if(true) {} }",
More information about the cfe-commits
mailing list