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,104 @@ InnerMatcher.matches(*ExprNode, Finder, Builder)); } + +/// \brief Matches on the receiver of an ObjectiveC Message expression. +/// +/// Example +/// matcher = objCMessageExpr(hasRecieverType(asString("UIWebView *"))); +/// matches the [webView ...] message invocation. +/// \code +/// NSString *webViewJavaScript = ... +/// UIWebView *webView = ... +/// [webView stringByEvaluatingJavaScriptFromString:webViewJavascript]; +/// \endcode +AST_MATCHER_P(ObjCMessageExpr, hasReceiverType, internal::Matcher, + InnerMatcher) { + const QualType TypeDecl = Node.getReceiverType(); + return InnerMatcher.matches(TypeDecl, Finder, Builder); +} + +/// \brief Matches when BaseName == Selector.getAsString() +/// +/// matcher = objCMessageExpr(hasSelector("loadHTMLString:baseURL:")); +/// matches the outer message expr in the code below, but NOT the message +/// invocation for self.bodyView. +/// \code +/// [self.bodyView loadHTMLString:html baseURL:NULL]; +/// \endcode + 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. +/// matcher = objCMessageExpr(matchesSelector("loadHTMLString\:baseURL?")); +/// matches the outer message expr in the code below, but NOT the message +/// invocation for self.bodyView. +/// \code +/// [self.bodyView loadHTMLString:html baseURL:NULL]; +/// \endcode +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 +/// +/// Matches only when the selector of the objCMessageExpr is NULL. This may +/// represent an error condition in the tree! +AST_MATCHER(ObjCMessageExpr, hasNullSelector) { + return Node.getSelector().isNull(); +} + +/// \brief Matches when the selector is a Unary Selector +/// +/// matcher = objCMessageExpr(matchesSelector(hasUnarySelector()); +/// matches self.bodyView in the code below, but NOT the outer message +/// invocation of "loadHTMLString:baseURL:". +/// \code +/// [self.bodyView loadHTMLString:html baseURL:NULL]; +/// \endcode +AST_MATCHER(ObjCMessageExpr, hasUnarySelector) { + return Node.getSelector().isUnarySelector(); +} + +/// \brief Matches when the selector is a keyword selector +/// +/// objCMessageExpr(hasKeywordSelector()) matches the generated setFrame +/// message expression in +/// +/// \code +/// UIWebView *webView = ...; +/// CGRect bodyFrame = webView.frame; +/// bodyFrame.size.height = self.bodyContentHeight; +/// webView.frame = bodyFrame; +/// // ^---- matches here +/// \endcode + +AST_MATCHER(ObjCMessageExpr, hasKeywordSelector) { + return Node.getSelector().isKeywordSelector(); +} + +/// \brief Matches when the selector has the specified number of arguments +/// +/// matcher = objCMessageExpr(numSelectorArgs(1)); +/// matches self.bodyView in the code below +/// +/// matcher = objCMessageExpr(numSelectorArgs(2)); +/// matches the invocation of "loadHTMLString:baseURL:" but not that +/// of self.bodyView +/// \code +/// [self.bodyView loadHTMLString:html baseURL:NULL]; +/// \endcode +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 +2430,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 +2448,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: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -198,6 +198,7 @@ REGISTER_MATCHER(hasIncrement); REGISTER_MATCHER(hasIndex); REGISTER_MATCHER(hasInitializer); + REGISTER_MATCHER(hasKeywordSelector); REGISTER_MATCHER(hasLHS); REGISTER_MATCHER(hasLocalQualifiers); REGISTER_MATCHER(hasLocalStorage); @@ -205,6 +206,7 @@ REGISTER_MATCHER(hasLoopVariable); REGISTER_MATCHER(hasMethod); REGISTER_MATCHER(hasName); + REGISTER_MATCHER(hasNullSelector); REGISTER_MATCHER(hasObjectExpression); REGISTER_MATCHER(hasOperatorName); REGISTER_MATCHER(hasOverloadedOperatorName); @@ -212,7 +214,9 @@ REGISTER_MATCHER(hasParent); REGISTER_MATCHER(hasQualifier); REGISTER_MATCHER(hasRangeInit); + REGISTER_MATCHER(hasReceiverType); REGISTER_MATCHER(hasRHS); + REGISTER_MATCHER(hasSelector); REGISTER_MATCHER(hasSingleDecl); REGISTER_MATCHER(hasSize); REGISTER_MATCHER(hasSizeExpr); @@ -223,6 +227,7 @@ REGISTER_MATCHER(hasTrueExpression); REGISTER_MATCHER(hasTypeLoc); REGISTER_MATCHER(hasUnaryOperand); + REGISTER_MATCHER(hasUnarySelector); REGISTER_MATCHER(hasValueType); REGISTER_MATCHER(ifStmt); REGISTER_MATCHER(ignoringImpCasts); @@ -262,6 +267,7 @@ REGISTER_MATCHER(lambdaExpr); REGISTER_MATCHER(lValueReferenceType); REGISTER_MATCHER(matchesName); + REGISTER_MATCHER(matchesSelector); REGISTER_MATCHER(materializeTemporaryExpr); REGISTER_MATCHER(member); REGISTER_MATCHER(memberCallExpr); @@ -276,7 +282,9 @@ REGISTER_MATCHER(newExpr); REGISTER_MATCHER(nullPtrLiteralExpr); REGISTER_MATCHER(nullStmt); + REGISTER_MATCHER(numSelectorArgs); REGISTER_MATCHER(ofClass); + REGISTER_MATCHER(objcMessageExpr); REGISTER_MATCHER(on); REGISTER_MATCHER(onImplicitObjectArgument); REGISTER_MATCHER(operatorCallExpr); Index: lib/Sema/SemaExprObjC.cpp =================================================================== --- lib/Sema/SemaExprObjC.cpp +++ lib/Sema/SemaExprObjC.cpp @@ -218,9 +218,7 @@ S.Diag(Loc, diag::err_undeclared_nsnumber); return nullptr; } - } - - if (S.NSNumberPointer.isNull()) { + // generate the pointer to NSNumber type. QualType NSNumberObject = CX.getObjCInterfaceType(S.NSNumberDecl); S.NSNumberPointer = CX.getObjCObjectPointerType(NSNumberObject); 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