Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -3700,6 +3700,121 @@ return Node.hasAttr(); } +/// \brief Matches Objective-C method expressions. +/// +/// Given +/// \code +/// [_ivar release]; +/// [super dealloc]; +/// [NSArray alloc]; +/// \endcode +/// objCMessageExpr() +/// matches '[_ivar release]', '[super dealloc]' and '[NSArray alloc]'. +const internal::VariadicDynCastAllOfMatcher + objCMessageExpr; + +/// \brief Matches ObjCMessageExpr nodes that have the specified selector. +/// +/// Given +/// \code +/// [_ivar release]; +/// [super dealloc]; +/// [NSArray alloc]; +/// \endcode +/// objCMessageExpr(hasSelector("alloc")) +/// matches '[NSArray alloc]' +AST_MATCHER_P(ObjCMessageExpr, hasSelector, std::string, ExpectedSelector) { + assert(!ExpectedSelector.empty()); + const std::string ActualSelectorString = Node.getSelector().getAsString(); + const StringRef ActualSelector = ActualSelectorString; + return ExpectedSelector == ActualSelector; +} + +/// \brief Matches ObjCMessageExpr nodes that have an instance receiver. +/// +/// Given +/// \code +/// @interface MyClass : NSObject +/// - (void)_invalidate; +/// @end +/// @implementation MyClass +/// - (void)dealloc { +/// [self _invalidate]; +/// [super dealloc]; +/// } +/// @end +/// \endcode +/// objCMessageExpr(isInstanceReceiver()) +/// matches '[self _invalidate]' +AST_MATCHER(ObjCMessageExpr, isInstanceReceiver) { + return Node.getReceiverKind() == ObjCMessageExpr::Instance; +} + +/// \brief Matches ObjCMessageExpr nodes that have a super instance receiver. +/// +/// Given +/// \code +/// @interface MyClass : NSObject +/// - (void)_invalidate; +/// @end +/// @implementation MyClass +/// - (void)dealloc { +/// [self _invalidate]; +/// [super dealloc]; +/// } +/// @end +/// \endcode +/// objCMessageExpr(isSuperInstanceReceiver()) +/// matches '[super dealloc]' +AST_MATCHER(ObjCMessageExpr, isSuperInstanceReceiver) { + return Node.getReceiverKind() == ObjCMessageExpr::SuperInstance; +} + +/// \brief Matches ObjCMessageExpr nodes that have a class receiver. +/// +/// Given +/// \code +/// @interface MyClass : NSObject { +/// NSObject *_ivar; +/// } +/// @end +/// @implementation MyClass +/// - (instancetype)init { +/// self = [super init]; +/// if (self) +/// _ivar = [NSObject new]; +/// return self; +/// } +/// @end +/// \endcode +/// objCMessageExpr(isClassReceiver()) +/// matches '[NSObject new]' +AST_MATCHER(ObjCMessageExpr, isClassReceiver) { + return Node.getReceiverKind() == ObjCMessageExpr::Class; +} + +/// \brief Matches ObjCMessageExpr nodes that have a super class receiver. +/// +/// Given +/// \code +/// @interface MyClass : NSObject +/// @end +/// @implementation MyClass +/// + (void)initialize { +/// [super initialize]; +/// } +/// - (instancetype)init { +/// self = [super init]; +/// return self; +/// } +/// @end +/// \endcode +/// objCMessageExpr(isSuperClassReceiver()) +/// matches '[super initialize]' +AST_MATCHER(ObjCMessageExpr, isSuperClassReceiver) { + return Node.getReceiverKind() == ObjCMessageExpr::SuperClass; +} + } // end namespace ast_matchers } // end namespace clang Index: include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- include/clang/ASTMatchers/ASTMatchersInternal.h +++ include/clang/ASTMatchers/ASTMatchersInternal.h @@ -38,9 +38,12 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" #include "clang/AST/Type.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/VariadicFunction.h" Index: unittests/ASTMatchers/ASTMatchersTest.h =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.h +++ unittests/ASTMatchers/ASTMatchersTest.h @@ -172,6 +172,103 @@ return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11"); } +enum ObjCMemoryManagement { MRR, MRR_GC, GCOnly, ARC }; +typedef enum ObjCMemoryManagement ObjCMemoryManagement; + +// Function based on matchesConditionally with "-x objective-c" argument added +// and small Objective-C header prepended to the code string. +template +testing::AssertionResult +matchesConditionallyWithObjC(ObjCMemoryManagement memoryManagement, + const std::string &Code, const T &AMatcher, + bool ExpectMatch) { + const std::string ObjCHeader = + "#if __has_feature(objc_arc)\n" + "# define OBJC_ARC_UNAVAILABLE __attribute__((unavailable))\n" + "#else\n" + "# define OBJC_ARC_UNAVAILABLE\n" + "#endif\n" + "#define nil ((id)0)\n" + "typedef signed char BOOL;\n" + "typedef struct objc_selector *SEL;\n" + "@protocol NSObject\n" + "- (Class)class;\n" + "- (BOOL)isEqual:(id)object;\n" + "- (instancetype)retain OBJC_ARC_UNAVAILABLE;\n" + "- (oneway void)release OBJC_ARC_UNAVAILABLE;\n" + "@end\n" + "@interface NSObject {}\n" + "+ (instancetype)alloc;\n" + "- (void)dealloc;\n" + "- (instancetype)init;\n" + "+ (void)initialize;\n" + "+ (instancetype)new;\n" + "@end\n"; + + bool Found = false, DynamicFound = false; + MatchFinder Finder; + VerifyMatch VerifyFound(nullptr, &Found); + Finder.addMatcher(AMatcher, &VerifyFound); + VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); + if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) + return testing::AssertionFailure() << "Could not add dynamic matcher"; + std::unique_ptr Factory( + newFrontendActionFactory(&Finder)); + + std::vector Args; + Args.push_back("-x"); + Args.push_back("objective-c"); + switch (memoryManagement) { + case MRR: + break; + case MRR_GC: { + Args.push_back("-fobjc-gc"); + break; + } + case GCOnly: { + Args.push_back("-fobjc-gc-only"); + break; + } + case ARC: { + Args.push_back("-fobjc-arc"); + break; + } + } + + const std::string CodeToRun = ObjCHeader + Code; + if (!runToolOnCodeWithArgs(Factory->create(), CodeToRun, Args)) { + return testing::AssertionFailure() << "Parsing error in \"" << CodeToRun + << "\""; + } + if (Found != DynamicFound) { + return testing::AssertionFailure() + << "Dynamic match result (" << DynamicFound + << ") does not match static result (" << Found << ")"; + } + if (!Found && ExpectMatch) { + return testing::AssertionFailure() << "Could not find match in \"" + << CodeToRun << "\""; + } else if (Found && !ExpectMatch) { + return testing::AssertionFailure() << "Found unexpected match in \"" + << CodeToRun << "\""; + } + return testing::AssertionSuccess(); +} + +template +testing::AssertionResult +matchesWithObjC(const std::string &Code, const T &AMatcher, + ObjCMemoryManagement memoryManagement = MRR) { + return matchesConditionallyWithObjC(memoryManagement, Code, AMatcher, true); +} + +template +testing::AssertionResult +notMatchesWithObjC(const std::string &Code, const T &AMatcher, + ObjCMemoryManagement memoryManagement = MRR) { + return matchesConditionallyWithObjC(memoryManagement, Code, AMatcher, false); +} + template testing::AssertionResult matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -4453,5 +4453,116 @@ .bind("data"))); } +TEST(ObjectiveC, ObjCMessageExpr) { + EXPECT_TRUE(matchesWithObjC( + "void f() { [NSObject alloc]; }", + functionDecl(hasName("f"), hasDescendant(objCMessageExpr())))); + + EXPECT_TRUE(notMatchesWithObjC( + "void f() { (void)0; }", + functionDecl(hasName("f"), hasDescendant(objCMessageExpr())))); +} + +TEST(ObjectiveC, ObjCMessageExpr_HasSelector) { + const char *code = "void f() { [NSObject alloc]; }"; + + EXPECT_TRUE(matchesWithObjC( + code, functionDecl(hasName("f"), hasDescendant(objCMessageExpr( + hasSelector("alloc")))))); + + EXPECT_TRUE(notMatchesWithObjC( + code, functionDecl(hasName("f"), + hasDescendant(objCMessageExpr(hasSelector("init")))))); + + code = "BOOL f() { " + " NSObject *o1 = [NSObject new]; " + " NSObject *o2 = [[NSObject alloc] init]; " + " return [o1 isEqual:o2]; " + "}"; + + EXPECT_TRUE(matchesWithObjC( + code, functionDecl(hasName("f"), hasDescendant(objCMessageExpr( + hasSelector("isEqual:")))))); +} + +TEST(ObjectiveC, ObjCMessageExpr_IsInstanceReceiver) { + const char *code = "@interface MyClass : NSObject\n" + "- (void)_invalidate;\n" + "@end\n" + "@implementation MyClass\n" + "- (void)dealloc {\n" + " [self _invalidate];\n" + " [super dealloc];\n" + "}\n" + "@end"; + + EXPECT_TRUE(matchesWithObjC( + code, objCMessageExpr(isInstanceReceiver(), hasSelector("_invalidate")))); + + EXPECT_TRUE(notMatchesWithObjC( + code, objCMessageExpr(isInstanceReceiver(), hasSelector("dealloc")))); +} + +TEST(ObjectiveC, ObjCMessageExpr_IsSuperInstanceReceiver) { + const char *code = "@interface MyClass : NSObject\n" + "- (void)_invalidate;\n" + "@end\n" + "@implementation MyClass\n" + "- (void)dealloc {\n" + " [self _invalidate];\n" + " [super dealloc];\n" + "}\n" + "@end"; + + EXPECT_TRUE(matchesWithObjC(code, objCMessageExpr(isSuperInstanceReceiver(), + hasSelector("dealloc")))); + + EXPECT_TRUE( + notMatchesWithObjC(code, objCMessageExpr(isSuperInstanceReceiver(), + hasSelector("_invalidate")))); +} + +TEST(ObjectiveC, ObjCMessageExpr_IsClassReceiver) { + const char *code = "@interface MyClass : NSObject {\n" + " NSObject *_ivar;\n" + "}\n" + "@end\n" + "@implementation MyClass\n" + "- (instancetype)init {\n" + " self = [super init];\n" + " if (self)\n" + " _ivar = [NSObject new];\n" + " return self;\n" + "}\n" + "@end"; + + EXPECT_TRUE(matchesWithObjC( + code, objCMessageExpr(isClassReceiver(), hasSelector("new")))); + + EXPECT_TRUE(notMatchesWithObjC( + code, objCMessageExpr(isClassReceiver(), hasSelector("init")))); +} + +TEST(ObjectiveC, ObjCMessageExpr_IsSuperClassReceiver) { + const char *code = "@interface MyClass : NSObject\n" + "@end\n" + "@implementation MyClass\n" + "+ (void)initialize {\n" + " [super initialize];\n" + "}\n" + "- (instancetype)init {\n" + " self = [super init];\n" + " return self;\n" + "}\n" + "@end"; + + EXPECT_TRUE( + matchesWithObjC(code, objCMessageExpr(isSuperClassReceiver(), + hasSelector("initialize")))); + + EXPECT_TRUE(notMatchesWithObjC( + code, objCMessageExpr(isSuperClassReceiver(), hasSelector("init")))); +} + } // end namespace ast_matchers } // end namespace clang