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 @@ -55,6 +55,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/LambdaCapture.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/OpenMPClause.h" #include "clang/AST/OperationKinds.h" @@ -4014,6 +4015,53 @@ return false; } +/// Matches any capture of a lambda expression. +/// +/// Given +/// \code +/// void foo() { +/// int x; +/// auto f = [x](){}; +/// } +/// \endcode +/// lambdaExpr(hasAnyCapture(anything())) +/// matches [x](){}; +AST_MATCHER_P(LambdaExpr, hasAnyCapture, internal::Matcher, + InnerMatcher) { + for (const LambdaCapture &Capture : Node.captures()) { + if (Capture.capturesThis()) { + continue; + } + BoundNodesTreeBuilder Result(*Builder); + if (InnerMatcher.matches(*Capture.getCapturedVar(), Finder, &Result)) { + *Builder = std::move(Result); + return true; + } + } + return false; +} + +/// Matches any capture of 'this' in a lambda expression. +/// +/// Given +/// \code +/// struct foo { +/// void bar() { +/// auto f = [this](){}; +/// } +/// } +/// \endcode +/// lambdaExpr(capturesThis()) +/// matches [this](){}; +AST_MATCHER(LambdaExpr, capturesThis) { + for (const LambdaCapture &Capture : Node.captures()) { + if (Capture.capturesThis()) { + return true; + } + } + return false; +} + /// Matches a constructor call expression which uses list initialization. AST_MATCHER(CXXConstructExpr, isListInitialization) { return Node.isListInitialization(); 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 @@ -454,6 +454,26 @@ 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(capturesThis()); + 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, isClassMessage) { EXPECT_TRUE(matchesObjC( "@interface NSString +(NSString *) stringWithFormat; @end "