Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -47,6 +47,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclFriend.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/ASTMatchers/ASTMatchersInternal.h" #include "clang/ASTMatchers/ASTMatchersMacros.h" @@ -872,6 +873,20 @@ Stmt, CXXMemberCallExpr> memberCallExpr; +/// \brief Matches ObjectiveC Message invocation expressions. +/// +/// The innermost message send invokes the "alloc" class method on the +/// NSString class, while the outermost message send invokes the +/// "initWithString" instance method on the object returned from +/// NSString's "alloc". This matcher should match both message sends. +/// \code +/// [[NSString alloc] initWithString:@"Hello"] +/// \endcode +const internal::VariadicDynCastAllOfMatcher< + Stmt, + ObjCMessageExpr> objcMessageExpr; + + /// \brief Matches expressions that introduce cleanups to be run at the end /// of the sub-expression's evaluation. /// @@ -2011,6 +2026,51 @@ InnerMatcher.matches(*ExprNode, Finder, Builder)); } + +/// \brief Matches on the receiver of an ObjectiveC Message expression. +AST_MATCHER_P(ObjCMessageExpr, hasReceiverType, internal::Matcher, + InnerMatcher) { + const QualType TypeDecl = Node.getReceiverType(); + return InnerMatcher.matches(TypeDecl, Finder, Builder); +} + +/// \brief Overloaded method matches when BaseName == Selector.getAsString() +/// \c hasSelector(getAsString(...)) + AST_MATCHER_P(ObjCMessageExpr, hasSelector, std::string, BaseName) { + Selector Sel = Node.getSelector(); + return BaseName.compare(Sel.getAsString()) == 0; +} + + +/// \brief Matches ObjC selectors whose name contains +/// a substring matched by the given RegExp. +AST_MATCHER_P(ObjCMessageExpr, matchesSelector, std::string, RegExp) { + assert(!RegExp.empty()); + std::string SelectorString = Node.getSelector().getAsString(); + llvm::Regex RE(RegExp); + return RE.match(SelectorString); +} + +/// \brief Matches when the selector is the empty selector +AST_MATCHER(ObjCMessageExpr, hasNullSelector) { + return Node.getSelector().isNull(); +} + +/// \brief Matches when the selector is a Unary Selector +AST_MATCHER(ObjCMessageExpr, hasUnarySelector) { + return Node.getSelector().isUnarySelector(); +} + +/// \brief Matches when the selector is a keyword selector +AST_MATCHER(ObjCMessageExpr, hasKeywordSelector) { + return Node.getSelector().isKeywordSelector(); +} + +/// \brief Matches when the selector has the specified number of arguments +AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, unsigned, N) { + return Node.getSelector().getNumArgs() == N; +} + /// \brief Matches if the call expression's callee expression matches. /// /// Given @@ -2317,8 +2377,10 @@ /// void f(int x, int y); /// f(0, 0); /// \endcode -AST_POLYMORPHIC_MATCHER_P(argumentCountIs, AST_POLYMORPHIC_SUPPORTED_TYPES_2( - CallExpr, CXXConstructExpr), +AST_POLYMORPHIC_MATCHER_P( + argumentCountIs, + AST_POLYMORPHIC_SUPPORTED_TYPES_3(CallExpr, CXXConstructExpr, + ObjCMessageExpr), unsigned, N) { return Node.getNumArgs() == N; } @@ -2333,7 +2395,8 @@ /// \endcode AST_POLYMORPHIC_MATCHER_P2( hasArgument, - AST_POLYMORPHIC_SUPPORTED_TYPES_2(CallExpr, CXXConstructExpr), + AST_POLYMORPHIC_SUPPORTED_TYPES_3(CallExpr, CXXConstructExpr, + ObjCMessageExpr), unsigned, N, internal::Matcher, InnerMatcher) { return (N < Node.getNumArgs() && InnerMatcher.matches( 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 @@ -62,7 +62,8 @@ testing::AssertionResult matchesConditionally( const std::string &Code, const T &AMatcher, bool ExpectMatch, llvm::StringRef CompileArg, - const FileContentMappings &VirtualMappedFiles = FileContentMappings()) { + const FileContentMappings &VirtualMappedFiles = FileContentMappings(), + const std::string &Filename = "input.cc") { bool Found = false, DynamicFound = false; MatchFinder Finder; VerifyMatch VerifyFound(nullptr, &Found); @@ -74,7 +75,7 @@ newFrontendActionFactory(&Finder)); // Some tests use typeof, which is a gnu extension. std::vector Args(1, CompileArg); - if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, "input.cc", + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, Filename, VirtualMappedFiles)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } @@ -105,6 +106,23 @@ return matchesConditionally(Code, AMatcher, false, "-std=c++11"); } +template +testing::AssertionResult matchesObjC(const std::string &Code, + const T &AMatcher) { + return matchesConditionally( + Code, AMatcher, true, + "", FileContentMappings(), "input.m"); +} + +template +testing::AssertionResult notMatchesObjC(const std::string &Code, + const T &AMatcher) { + return matchesConditionally( + Code, AMatcher, false, + "", FileContentMappings(), "input.m"); +} + + // Function based on matchesConditionally with "-x cuda" argument added and // small CUDA header prepended to the code string. template Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -4712,5 +4712,50 @@ #endif // LLVM_ON_WIN32 + +TEST(ObjCMessageExprMatcher, SimpleExprs) { + // don't find ObjCMessageExpr where none are present + EXPECT_TRUE(notMatchesObjC("", objcMessageExpr(anything()))); + + std::string Objc1String = + "@interface Str " + " - (Str *)uppercaseString:(Str *)str;" + "@end " + "@interface foo " + "- (void)meth:(Str *)text;" + "@end " + " " + "@implementation foo " + "- (void) meth:(Str *)text { " + " [self contents];" + " Str *up = [text uppercaseString];" + "} " + "@end "; + EXPECT_TRUE(matchesObjC( + Objc1String, + objcMessageExpr(anything()))); + EXPECT_TRUE(matchesObjC( + Objc1String, + objcMessageExpr(hasSelector("contents")))); + EXPECT_TRUE(matchesObjC( + Objc1String, + objcMessageExpr(matchesSelector("cont*")))); + EXPECT_FALSE(matchesObjC( + Objc1String, + objcMessageExpr(matchesSelector("?cont*")))); + EXPECT_TRUE(notMatchesObjC( + Objc1String, + objcMessageExpr(hasSelector("contents"), hasNullSelector()))); + EXPECT_TRUE(matchesObjC( + Objc1String, + objcMessageExpr(hasSelector("contents"), hasUnarySelector()))); + EXPECT_TRUE(matchesObjC( + Objc1String, + objcMessageExpr(matchesSelector("uppercase*"), + argumentCountIs(0) + ))); + +} + } // end namespace ast_matchers } // end namespace clang