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. llvm-svn: 232051
This commit is contained in:
parent
637a6862d6
commit
bfa4357271
|
@ -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::VariadicDynCastAllOfMatcher<
|
|||
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, internal::Matcher<Expr>,
|
|||
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(argumentCountIs,
|
|||
/// \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(
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 matchesConditionally(
|
|||
// 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(const std::string &Code,
|
|||
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>
|
||||
|
|
Loading…
Reference in New Issue