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

Reply via email to