Hi jordan_rose, krememek,
After implementing D5042 using ASTMatchers for part of the work,
I realized how much easier my life would have been had
ASTMatchers been available for Objective-C, so I started
implementing them.
Here are the commits landed so far:
ASTMatchers: Add support for objCMessageExpr()
ASTMatchers: Add support for hasSelector() within objCMessageExpr()
ASTMatchers: Add support for is{,Super}{Instance,Class}Receiver() with
objCMessageExpr()
http://reviews.llvm.org/D5056
Files:
include/clang/ASTMatchers/ASTMatchers.h
include/clang/ASTMatchers/ASTMatchersInternal.h
unittests/ASTMatchers/ASTMatchersTest.cpp
unittests/ASTMatchers/ASTMatchersTest.h
Index: include/clang/ASTMatchers/ASTMatchers.h
===================================================================
--- include/clang/ASTMatchers/ASTMatchers.h
+++ include/clang/ASTMatchers/ASTMatchers.h
@@ -3700,6 +3700,121 @@
return Node.hasAttr<clang::CUDAGlobalAttr>();
}
+/// \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<Stmt, ObjCMessageExpr>
+ 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.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
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 <typename T>
+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 <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<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
+
+ std::vector<std::string> 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 <typename T>
+testing::AssertionResult
+matchesWithObjC(const std::string &Code, const T &AMatcher,
+ ObjCMemoryManagement memoryManagement = MRR) {
+ return matchesConditionallyWithObjC(memoryManagement, Code, AMatcher, true);
+}
+
+template <typename T>
+testing::AssertionResult
+notMatchesWithObjC(const std::string &Code, const T &AMatcher,
+ ObjCMemoryManagement memoryManagement = MRR) {
+ return matchesConditionallyWithObjC(memoryManagement, Code, AMatcher, false);
+}
+
template <typename T>
testing::AssertionResult
matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits