Added 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, reviewed by Manuel Klimek. From http://reviews.llvm.org/D7710

llvm-svn: 232034
This commit is contained in:
Aaron Ballman 2015-03-12 13:21:19 +00:00
parent 4952a0cba2
commit 12865302b7
5 changed files with 193 additions and 4 deletions

View File

@ -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(

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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>