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,42 @@

 
 
+Matcher<LambdaCapture>capturesThis
+
Matches a `LambdaCapture` that refers to 'this'.
+
+Given
+class C {
+  int cc;
+  int f() {
+    auto l = [this]() { return cc; };
+    return l();
+  }
+};
+lambdaExpr(hasAnyCapture(lambdaCapture(capturesThis())))
+  matches `[this]() { return cc; }`.
+
+ + +Matcher<LambdaCapture>isImplicit +
Matches an entity that has been implicitly added by the compiler (e.g.
+implicit default/copy constructors).
+
+ + +Matcher<LambdaExpr>hasAnyCaptureLambdaCaptureMatcher InnerMatcher +
Matches any capture in a lambda expression.
+
+Given
+  void foo() {
+    int t = 5;
+    auto f = [=](){ return t; };
+  }
+lambdaExpr(hasAnyCapture(lambdaCapture())) and
+lambdaExpr(hasAnyCapture(lambdaCapture(refersToVarDecl(hasName("t")))))
+  both match `[=](){ return t; }`.
+
+ + Matcher<MemberExpr>isArrow
Matches member expressions that are called with '->' as opposed
 to '.'.
@@ -8314,30 +8354,20 @@
 
-Matcher<LambdaExpr>hasAnyCaptureMatcher<CXXThisExpr> InnerMatcher -
Matches any capture of 'this' in a lambda expression.
-
-Given
-  struct foo {
-    void bar() {
-      auto f = [this](){};
-    }
-  }
-lambdaExpr(hasAnyCapture(cxxThisExpr()))
-  matches [this](){};
-
- - -Matcher<LambdaExpr>hasAnyCaptureMatcher<VarDecl> InnerMatcher -
Matches any capture of a lambda expression.
+Matcher<LambdaCapture>capturesVarMatcher<VarDecl> InnerMatcher
+
Matches a `LambdaCapture` that refers to the specified `VarDecl`. The
+`VarDecl` can be a separate variable that is captured by value or
+reference, or a synthesized variable if the capture has an initializer.
 
 Given
   void foo() {
     int x;
     auto f = [x](){};
+    auto g = [x = 1](){};
   }
-lambdaExpr(hasAnyCapture(anything()))
-  matches [x](){};
+In the matcher
+lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(hasName("x")))),
+capturesVar(hasName("x")) matches `x` and `x = 1`.
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -220,6 +220,13 @@ matcher or the ``hasReturnTypeLoc`` matcher. The addition of these matchers was made possible by changes to the handling of ``TypeLoc`` nodes that allows them to enjoy the same static type checking as other AST node kinds. +- ``LambdaCapture`` AST Matchers are now available. These matchers allow for + the binding of ``LambdaCapture`` nodes. The ``LambdaCapture`` matchers added + include the ``lambdaCapture`` node matcher, the ``capturesVar`` traversal + matcher, and ``capturesThis`` narrowing matcher. +- The ``hasAnyCapture`` matcher now only accepts an inner matcher of type + ``Matcher``. The matcher originally accepted an inner matcher + of type ``Matcher`` or ``Matcher``. clang-format ------------ 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,50 +4590,68 @@ return false; } -/// Matches any capture of a lambda expression. +extern const internal::VariadicAllOfMatcher lambdaCapture; + +/// Matches any capture in a lambda expression. /// /// Given /// \code /// void foo() { -/// int x; -/// auto f = [x](){}; +/// int t = 5; +/// auto f = [=](){ return t; }; /// } /// \endcode -/// lambdaExpr(hasAnyCapture(anything())) -/// matches [x](){}; -AST_MATCHER_P_OVERLOAD(LambdaExpr, hasAnyCapture, internal::Matcher, - InnerMatcher, 0) { +/// lambdaExpr(hasAnyCapture(lambdaCapture())) and +/// lambdaExpr(hasAnyCapture(lambdaCapture(refersToVarDecl(hasName("t"))))) +/// both match `[=](){ return t; }`. +AST_MATCHER_P(LambdaExpr, hasAnyCapture, LambdaCaptureMatcher, InnerMatcher) { for (const LambdaCapture &Capture : Node.captures()) { - if (Capture.capturesVariable()) { - BoundNodesTreeBuilder Result(*Builder); - if (InnerMatcher.matches(*Capture.getCapturedVar(), Finder, &Result)) { - *Builder = std::move(Result); - return true; - } + clang::ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); + if (InnerMatcher.matches(Capture, Finder, &Result)) { + *Builder = std::move(Result); + return true; } } return false; } -/// Matches any capture of 'this' in a lambda expression. +/// Matches a `LambdaCapture` that refers to the specified `VarDecl`. The +/// `VarDecl` can be a separate variable that is captured by value or +/// reference, or a synthesized variable if the capture has an initializer. /// /// Given /// \code -/// struct foo { -/// void bar() { -/// auto f = [this](){}; -/// } +/// void foo() { +/// int x; +/// auto f = [x](){}; +/// auto g = [x = 1](){}; /// } /// \endcode -/// lambdaExpr(hasAnyCapture(cxxThisExpr())) -/// matches [this](){}; -AST_MATCHER_P_OVERLOAD(LambdaExpr, hasAnyCapture, - internal::Matcher, InnerMatcher, 1) { - return llvm::any_of(Node.captures(), [](const LambdaCapture &LC) { - return LC.capturesThis(); - }); +/// In the matcher +/// lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(hasName("x")))), +/// capturesVar(hasName("x")) matches `x` and `x = 1`. +AST_MATCHER_P(LambdaCapture, capturesVar, 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(capturesThis()))) +/// matches `[this]() { return cc; }`. +AST_MATCHER(LambdaCapture, capturesThis) { return Node.capturesThis(); } + /// Matches a constructor call expression which uses list initialization. AST_MATCHER(CXXConstructExpr, isListInitialization) { return Node.isListInitialization(); 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); @@ -157,6 +156,8 @@ REGISTER_MATCHER(builtinType); REGISTER_MATCHER(cStyleCastExpr); REGISTER_MATCHER(callExpr); + REGISTER_MATCHER(capturesThis); + REGISTER_MATCHER(capturesVar); REGISTER_MATCHER(caseStmt); REGISTER_MATCHER(castExpr); REGISTER_MATCHER(characterLiteral); @@ -264,6 +265,7 @@ REGISTER_MATCHER(hasAnyBase); REGISTER_MATCHER(hasAnyBinding); REGISTER_MATCHER(hasAnyBody); + REGISTER_MATCHER(hasAnyCapture); REGISTER_MATCHER(hasAnyClause); REGISTER_MATCHER(hasAnyConstructorInitializer); REGISTER_MATCHER(hasAnyDeclaration); @@ -465,6 +467,7 @@ REGISTER_MATCHER(lValueReferenceType); REGISTER_MATCHER(labelDecl); REGISTER_MATCHER(labelStmt); + REGISTER_MATCHER(lambdaCapture); REGISTER_MATCHER(lambdaExpr); REGISTER_MATCHER(linkageSpecDecl); REGISTER_MATCHER(materializeTemporaryExpr); 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, CapturesThis) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr(hasAnyCapture(lambdaCapture(capturesThis()))); + 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(), capturesVar(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,65 @@ 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_BindsToCaptureOfVarDecl) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr( + hasAnyCapture(lambdaCapture(capturesVar(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)); +} + +TEST_P(ASTMatchersTest, LambdaCaptureTest_BindsToCaptureWithInitializer) { + if (!GetParam().isCXX14OrLater()) { + return; + } + auto matcher = lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar( + varDecl(hasName("cc"), hasInitializer(integerLiteral(equals(1)))))))); + EXPECT_TRUE( + matches("int main() { auto lambda = [cc = 1] {return cc;}; }", matcher)); + EXPECT_TRUE( + matches("int main() { int cc = 2; auto lambda = [cc = 1] {return cc;}; }", + matcher)); +} + +TEST_P(ASTMatchersTest, LambdaCaptureTest_DoesNotBindToCaptureOfVarDecl) { + if (!GetParam().isCXX11OrLater()) { + return; + } + auto matcher = lambdaExpr( + hasAnyCapture(lambdaCapture(capturesVar(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)); +} + +TEST_P(ASTMatchersTest, + LambdaCaptureTest_DoesNotBindToCaptureWithInitializerAndDifferentName) { + if (!GetParam().isCXX14OrLater()) { + return; + } + EXPECT_FALSE(matches( + "int main() { auto lambda = [xx = 1] {return xx;}; }", + lambdaExpr(hasAnyCapture(lambdaCapture(capturesVar(varDecl( + hasName("cc"), hasInitializer(integerLiteral(equals(1)))))))))); +} + TEST(ASTMatchersTestObjC, ObjCMessageExpr) { // Don't find ObjCMessageExpr where none are present. EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything()))); diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -563,26 +563,6 @@ objcMessageExpr(hasReceiver(declRefExpr(to(varDecl(hasName("x")))))))); } -TEST(Matcher, HasAnyCapture) { - auto HasCaptureX = lambdaExpr(hasAnyCapture(varDecl(hasName("x")))); - EXPECT_TRUE(matches("void f() { int x = 3; [x](){}; }", HasCaptureX)); - EXPECT_TRUE(matches("void f() { int x = 3; [&x](){}; }", HasCaptureX)); - EXPECT_TRUE(notMatches("void f() { [](){}; }", HasCaptureX)); - EXPECT_TRUE(notMatches("void f() { int z = 3; [&z](){}; }", HasCaptureX)); - EXPECT_TRUE( - notMatches("struct a { void f() { [this](){}; }; };", HasCaptureX)); -} - -TEST(Matcher, CapturesThis) { - auto HasCaptureThis = lambdaExpr(hasAnyCapture(cxxThisExpr())); - EXPECT_TRUE( - matches("struct a { void f() { [this](){}; }; };", HasCaptureThis)); - EXPECT_TRUE(notMatches("void f() { [](){}; }", HasCaptureThis)); - EXPECT_TRUE(notMatches("void f() { int x = 3; [x](){}; }", HasCaptureThis)); - EXPECT_TRUE(notMatches("void f() { int x = 3; [&x](){}; }", HasCaptureThis)); - EXPECT_TRUE(notMatches("void f() { int z = 3; [&z](){}; }", HasCaptureThis)); -} - TEST(Matcher, MatchesMethodsOnLambda) { StringRef Code = R"cpp( struct A {