diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -1190,6 +1190,10 @@ +Matcher<LambdaCapture>lambdaCaptureMatcher<LambdaCapture>... +

+
+
 Matcher<NestedNameSpecifierLoc>nestedNameSpecifierLocMatcher<NestedNameSpecifierLoc>...
 
Same as nestedNameSpecifier but matches NestedNameSpecifierLoc.
 
@@ -4514,6 +4518,41 @@

 
 
+Matcher<LambdaCapture>isImplicit
+
Matches an entity that has been implicitly added by the compiler (e.g.
+implicit default/copy constructors).
+
+ + +Matcher<LambdaCapture>refersToThis +
Matches a `LambdaCapture` that refers to 'this'.
+
+Given
+class C {
+  int cc;
+  int f() {
+    auto l = [this]() { return cc; };
+    return l();
+  }
+};
+lambdaExpr(hasAnyCapture(lambdaCapture(refersToThis())))
+  matches `[this]() { return cc; }`.
+
+ + +Matcher<LambdaExpr>hasAnyCaptureLambdaCaptureMatcher InnerMatcher +
Matches any capture of a lambda expression.
+
+Given
+  void foo() {
+    int t = 5;
+    auto f = [=](){ return t; };
+  }
+lambdaExpr(hasAnyCapture(lambdaCapture()))
+  matches `[=](){ return t; }`.
+
+ + Matcher<MemberExpr>isArrow
Matches member expressions that are called with '->' as opposed
 to '.'.
@@ -8314,8 +8353,21 @@
 
-Matcher<LambdaExpr>hasAnyCaptureMatcher<CXXThisExpr> InnerMatcher -
Matches any capture of 'this' in a lambda expression.
+Matcher<LambdaCapture>refersToVarDeclMatcher<VarDecl> InnerMatcher
+
Matches a `LambdaCapture` that refers to the specified `VarDecl`
+
+Given
+  void foo() {
+    int x;
+    auto f = [x](){};
+  }
+lambdaExpr(hasAnyCapture(lambdaCapture(refersToVarDecl(hasName("x"))))
+  matches `[x](){}`.
+
+ + +Matcher<LambdaExpr>hasAnyCaptureMatcher<CXXThisExpr> InnerMatcher +
Matches any capture of 'this' in a lambda expression.
 
 Given
   struct foo {
@@ -8328,8 +8380,8 @@
 
-Matcher<LambdaExpr>hasAnyCaptureMatcher<VarDecl> InnerMatcher -
Matches any capture of a lambda expression.
+Matcher<LambdaExpr>hasAnyCaptureMatcher<VarDecl> InnerMatcher
+
Matches any capture of a lambda expression.
 
 Given
   void foo() {
@@ -8337,7 +8389,7 @@
     auto f = [x](){};
   }
 lambdaExpr(hasAnyCapture(anything()))
-  matches [x](){};
+  matches `[x](){}`.
 
diff --git a/clang/include/clang/AST/ASTTypeTraits.h b/clang/include/clang/AST/ASTTypeTraits.h --- a/clang/include/clang/AST/ASTTypeTraits.h +++ b/clang/include/clang/AST/ASTTypeTraits.h @@ -17,6 +17,7 @@ #include "clang/AST/ASTFwd.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TemplateBase.h" #include "clang/AST/TypeLoc.h" @@ -64,6 +65,7 @@ static ASTNodeKind getFromNode(const Stmt &S); static ASTNodeKind getFromNode(const Type &T); static ASTNodeKind getFromNode(const TypeLoc &T); + static ASTNodeKind getFromNode(const LambdaCapture &L); static ASTNodeKind getFromNode(const OMPClause &C); static ASTNodeKind getFromNode(const Attr &A); /// \} @@ -131,6 +133,7 @@ NKI_None, NKI_TemplateArgument, NKI_TemplateArgumentLoc, + NKI_LambdaCapture, NKI_TemplateName, NKI_NestedNameSpecifierLoc, NKI_QualType, @@ -197,6 +200,7 @@ KIND_TO_KIND_ID(CXXCtorInitializer) KIND_TO_KIND_ID(TemplateArgument) KIND_TO_KIND_ID(TemplateArgumentLoc) +KIND_TO_KIND_ID(LambdaCapture) KIND_TO_KIND_ID(TemplateName) KIND_TO_KIND_ID(NestedNameSpecifier) KIND_TO_KIND_ID(NestedNameSpecifierLoc) @@ -540,6 +544,10 @@ struct DynTypedNode::BaseConverter : public ValueConverter {}; +template <> +struct DynTypedNode::BaseConverter + : public ValueConverter {}; + template <> struct DynTypedNode::BaseConverter< TemplateName, void> : public ValueConverter {}; diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -148,6 +148,7 @@ using CXXCtorInitializerMatcher = internal::Matcher; using TemplateArgumentMatcher = internal::Matcher; using TemplateArgumentLocMatcher = internal::Matcher; +using LambdaCaptureMatcher = internal::Matcher; using AttrMatcher = internal::Matcher; /// @} @@ -756,7 +757,8 @@ /// Matches an entity that has been implicitly added by the compiler (e.g. /// implicit default/copy constructors). AST_POLYMORPHIC_MATCHER(isImplicit, - AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Attr)) { + AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Attr, + LambdaCapture)) { return Node.isImplicit(); } @@ -4588,6 +4590,64 @@ return false; } +extern const internal::VariadicAllOfMatcher lambdaCapture; + +/// Matches any capture in a lambda expression. +/// +/// Given +/// \code +/// void foo() { +/// int t = 5; +/// auto f = [=](){ return t; }; +/// } +/// \endcode +/// lambdaExpr(hasAnyCapture(lambdaCapture())) +/// matches `[=](){ return t; }`. +AST_MATCHER_P_OVERLOAD(LambdaExpr, hasAnyCapture, LambdaCaptureMatcher, + InnerMatcher, 2) { + for (const LambdaCapture &Capture : Node.captures()) { + clang::ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); + if (InnerMatcher.matches(Capture, Finder, &Result)) { + *Builder = std::move(Result); + return true; + } + } + return false; +} + +/// Matches a `LambdaCapture` that refers to the specified `VarDecl` +/// +/// Given +/// \code +/// void foo() { +/// int x; +/// auto f = [x](){}; +/// } +/// \endcode +/// lambdaExpr(hasAnyCapture(lambdaCapture(refersToVarDecl(hasName("x")))) +/// matches `[x](){}`. +AST_MATCHER_P(LambdaCapture, refersToVarDecl, internal::Matcher, + InnerMatcher) { + auto *capturedVar = Node.getCapturedVar(); + return capturedVar && InnerMatcher.matches(*capturedVar, Finder, Builder); +} + +/// Matches a `LambdaCapture` that refers to 'this'. +/// +/// Given +/// \code +/// class C { +/// int cc; +/// int f() { +/// auto l = [this]() { return cc; }; +/// return l(); +/// } +/// }; +/// \endcode +/// lambdaExpr(hasAnyCapture(lambdaCapture(refersToThis()))) +/// matches `[this]() { return cc; }`. +AST_MATCHER(LambdaCapture, refersToThis) { return Node.capturesThis(); } + /// Matches any capture of a lambda expression. /// /// Given @@ -4598,7 +4658,7 @@ /// } /// \endcode /// lambdaExpr(hasAnyCapture(anything())) -/// matches [x](){}; +/// matches `[x](){}`. AST_MATCHER_P_OVERLOAD(LambdaExpr, hasAnyCapture, internal::Matcher, InnerMatcher, 0) { for (const LambdaCapture &Capture : Node.captures()) { diff --git a/clang/lib/AST/ASTTypeTraits.cpp b/clang/lib/AST/ASTTypeTraits.cpp --- a/clang/lib/AST/ASTTypeTraits.cpp +++ b/clang/lib/AST/ASTTypeTraits.cpp @@ -26,6 +26,7 @@ {NKI_None, ""}, {NKI_None, "TemplateArgument"}, {NKI_None, "TemplateArgumentLoc"}, + {NKI_None, "LambdaCapture"}, {NKI_None, "TemplateName"}, {NKI_None, "NestedNameSpecifierLoc"}, {NKI_None, "QualType"}, diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -106,7 +106,6 @@ std::make_unique()); REGISTER_OVERLOADED_2(callee); - REGISTER_OVERLOADED_2(hasAnyCapture); REGISTER_OVERLOADED_2(hasPrefix); REGISTER_OVERLOADED_2(hasType); REGISTER_OVERLOADED_2(ignoringParens); @@ -125,6 +124,13 @@ }; REGISTER_MATCHER_OVERLOAD(equals); + std::unique_ptr hasAnyCaptureCallbacks[] = { + MATCHER_OVERLOAD_ENTRY(hasAnyCapture, 0), + MATCHER_OVERLOAD_ENTRY(hasAnyCapture, 1), + MATCHER_OVERLOAD_ENTRY(hasAnyCapture, 2), + }; + REGISTER_MATCHER_OVERLOAD(hasAnyCapture); + REGISTER_REGEX_MATCHER(isExpansionInFileMatching); REGISTER_REGEX_MATCHER(matchesName); REGISTER_REGEX_MATCHER(matchesSelector); @@ -465,6 +471,7 @@ REGISTER_MATCHER(lValueReferenceType); REGISTER_MATCHER(labelDecl); REGISTER_MATCHER(labelStmt); + REGISTER_MATCHER(lambdaCapture); REGISTER_MATCHER(lambdaExpr); REGISTER_MATCHER(linkageSpecDecl); REGISTER_MATCHER(materializeTemporaryExpr); @@ -525,7 +532,9 @@ REGISTER_MATCHER(refersToDeclaration); REGISTER_MATCHER(refersToIntegralType); REGISTER_MATCHER(refersToTemplate); + REGISTER_MATCHER(refersToThis); REGISTER_MATCHER(refersToType); + REGISTER_MATCHER(refersToVarDecl); REGISTER_MATCHER(requiresZeroInitialization); REGISTER_MATCHER(returnStmt); REGISTER_MATCHER(returns); 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 @@ -4445,5 +4445,42 @@ cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl(hasName("Base"))))))); } + +TEST_P(ASTMatchersTest, RefersToThis) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr(hasAnyCapture(lambdaCapture(refersToThis()))); + EXPECT_TRUE(matches("class C { int cc; int f() { auto l = [this](){ return " + "cc; }; return l(); } };", + matcher)); + EXPECT_TRUE(matches("class C { int cc; int f() { auto l = [=](){ return cc; " + "}; return l(); } };", + matcher)); + EXPECT_TRUE(matches("class C { int cc; int f() { auto l = [&](){ return cc; " + "}; return l(); } };", + matcher)); + EXPECT_FALSE(matches("class C { int cc; int f() { auto l = [cc](){ return " + "cc; }; return l(); } };", + matcher)); + EXPECT_FALSE(matches("class C { int this; int f() { auto l = [this](){ " + "return this; }; return l(); } };", + matcher)); +} + +TEST_P(ASTMatchersTest, IsImplicit_LambdaCapture) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr(hasAnyCapture( + lambdaCapture(isImplicit(), refersToVarDecl(varDecl(hasName("cc")))))); + EXPECT_TRUE( + matches("int main() { int cc; auto f = [&](){ return cc; }; }", matcher)); + EXPECT_TRUE( + matches("int main() { int cc; auto f = [=](){ return cc; }; }", matcher)); + EXPECT_FALSE(matches("int main() { int cc; auto f = [cc](){ return cc; }; }", + matcher)); +} + } // namespace ast_matchers } // namespace clang diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -2237,6 +2237,52 @@ varDecl(hasName("ss"), hasTypeLoc(elaboratedTypeLoc())))); } +TEST_P(ASTMatchersTest, LambdaCaptureTest) { + if (!GetParam().isCXX11OrLater()) { + return; + } + EXPECT_TRUE(matches("int main() { int cc; auto f = [cc](){ return cc; }; }", + lambdaExpr(hasAnyCapture(lambdaCapture())))); +} + +TEST_P(ASTMatchersTest, LambdaCaptureTest_BindsToCaptureReferringToVarDecl) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr( + hasAnyCapture(lambdaCapture(refersToVarDecl(varDecl(hasName("cc")))))); + EXPECT_TRUE(matches("int main() { int cc; auto f = [cc](){ return cc; }; }", + matcher)); + EXPECT_TRUE(matches("int main() { int cc; auto f = [&cc](){ return cc; }; }", + matcher)); + EXPECT_TRUE( + matches("int main() { int cc; auto f = [=](){ return cc; }; }", matcher)); + EXPECT_TRUE( + matches("int main() { int cc; auto f = [&](){ return cc; }; }", matcher)); + if (!GetParam().isCXX14OrLater()) { + return; + } + EXPECT_TRUE( + matches("int main() { auto lambda = [cc = 1] {return cc;}; }", matcher)); +} + +TEST_P(ASTMatchersTest, + LambdaCaptureTest_DoesNotBindToCaptureReferringToVarDecl) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr( + hasAnyCapture(lambdaCapture(refersToVarDecl(varDecl(hasName("cc")))))); + EXPECT_FALSE(matches("int main() { auto f = [](){ return 5; }; }", matcher)); + EXPECT_FALSE(matches("int main() { int xx; auto f = [xx](){ return xx; }; }", + matcher)); + if (!GetParam().isCXX14OrLater()) { + return; + } + EXPECT_FALSE( + matches("int main() { auto lambda = [xx = 1] {return xx;}; }", matcher)); +} + TEST(ASTMatchersTestObjC, ObjCMessageExpr) { // Don't find ObjCMessageExpr where none are present. EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything())));