r232051 - Add support for a few Objective-C matchers.
Manuel Klimek
klimek at google.com
Thu Mar 12 08:48:16 PDT 2015
Author: klimek
Date: Thu Mar 12 10:48:15 2015
New Revision: 232051
URL: http://llvm.org/viewvc/llvm-project?rev=232051&view=rev
Log:
Add support for a few Objective-C matchers.
Add some matchers for Objective-C selectors and messages to
ASTMatchers.h. Minor mods to ASTMatchersTest.h to allow test files with
".m" extension in addition to ".cpp". New tests added to
ASTMatchersTest.c.
Patch by Dean Sutherland.
Modified:
cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp
cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.h
Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h?rev=232051&r1=232050&r2=232051&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchers.h Thu Mar 12 10:48:15 2015
@@ -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"
@@ -869,6 +870,20 @@ const internal::VariadicDynCastAllOfMatc
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.
///
@@ -2007,6 +2022,104 @@ AST_MATCHER_P(CXXMemberCallExpr, on, int
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<QualType>,
+ 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
@@ -2316,7 +2429,8 @@ AST_MATCHER(VarDecl, hasGlobalStorage) {
/// \endcode
AST_POLYMORPHIC_MATCHER_P(argumentCountIs,
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
- CXXConstructExpr),
+ CXXConstructExpr,
+ ObjCMessageExpr),
unsigned, N) {
return Node.getNumArgs() == N;
}
@@ -2331,7 +2445,8 @@ AST_POLYMORPHIC_MATCHER_P(argumentCountI
/// \endcode
AST_POLYMORPHIC_MATCHER_P2(hasArgument,
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
- CXXConstructExpr),
+ CXXConstructExpr,
+ ObjCMessageExpr),
unsigned, N, internal::Matcher<Expr>, InnerMatcher) {
return (N < Node.getNumArgs() &&
InnerMatcher.matches(
Modified: cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h?rev=232051&r1=232050&r2=232051&view=diff
==============================================================================
--- cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h (original)
+++ cfe/trunk/include/clang/ASTMatchers/ASTMatchersInternal.h Thu Mar 12 10:48:15 2015
@@ -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"
Modified: cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp?rev=232051&r1=232050&r2=232051&view=diff
==============================================================================
--- cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp (original)
+++ cfe/trunk/lib/ASTMatchers/Dynamic/Registry.cpp Thu Mar 12 10:48:15 2015
@@ -198,6 +198,7 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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 @@ RegistryMaps::RegistryMaps() {
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);
Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp?rev=232051&r1=232050&r2=232051&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp (original)
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.cpp Thu Mar 12 10:48:15 2015
@@ -4714,5 +4714,50 @@ TEST(Matcher, IsExpansionInFileMatching)
#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
Modified: cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.h?rev=232051&r1=232050&r2=232051&view=diff
==============================================================================
--- cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.h (original)
+++ cfe/trunk/unittests/ASTMatchers/ASTMatchersTest.h Thu Mar 12 10:48:15 2015
@@ -62,7 +62,8 @@ template <typename T>
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);
@@ -78,7 +79,7 @@ testing::AssertionResult matchesConditio
// Some tests need rtti/exceptions on
Args.push_back("-frtti");
Args.push_back("-fexceptions");
- if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, "input.cc",
+ if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, Filename,
VirtualMappedFiles)) {
return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
}
@@ -109,6 +110,23 @@ testing::AssertionResult notMatches(cons
return matchesConditionally(Code, AMatcher, false, "-std=c++11");
}
+template <typename T>
+testing::AssertionResult matchesObjC(const std::string &Code,
+ const T &AMatcher) {
+ return matchesConditionally(
+ Code, AMatcher, true,
+ "", FileContentMappings(), "input.m");
+}
+
+template <typename T>
+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 <typename T>
More information about the cfe-commits
mailing list