summaryrefslogtreecommitdiff
path: root/unittests/ASTMatchers/ASTMatchersTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/ASTMatchers/ASTMatchersTest.cpp')
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.cpp280
1 files changed, 232 insertions, 48 deletions
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index cc13c01fec39..bd7a5a6df8f3 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -12,6 +12,8 @@
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/Triple.h"
+#include "llvm/Support/Host.h"
#include "gtest/gtest.h"
namespace clang {
@@ -42,14 +44,15 @@ TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
TEST(Finder, DynamicOnlyAcceptsSomeMatchers) {
MatchFinder Finder;
- EXPECT_TRUE(Finder.addDynamicMatcher(decl(), NULL));
- EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), NULL));
- EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), NULL));
+ EXPECT_TRUE(Finder.addDynamicMatcher(decl(), nullptr));
+ EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), nullptr));
+ EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)),
+ nullptr));
// Do not accept non-toplevel matchers.
- EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), NULL));
- EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), NULL));
- EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), NULL));
+ EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), nullptr));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), nullptr));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr));
}
TEST(Decl, MatchesDeclarations) {
@@ -103,12 +106,13 @@ TEST(NameableDeclaration, REMatchesVariousDecls) {
TEST(DeclarationMatcher, MatchClass) {
DeclarationMatcher ClassMatcher(recordDecl());
-#if !defined(_MSC_VER)
- EXPECT_FALSE(matches("", ClassMatcher));
-#else
- // Matches class type_info.
- EXPECT_TRUE(matches("", ClassMatcher));
-#endif
+ llvm::Triple Triple(llvm::sys::getDefaultTargetTriple());
+ if (Triple.getOS() != llvm::Triple::Win32 ||
+ Triple.getEnvironment() != llvm::Triple::MSVC)
+ EXPECT_FALSE(matches("", ClassMatcher));
+ else
+ // Matches class type_info.
+ EXPECT_TRUE(matches("", ClassMatcher));
DeclarationMatcher ClassX = recordDecl(recordDecl(hasName("X")));
EXPECT_TRUE(matches("class X;", ClassX));
@@ -663,7 +667,7 @@ public:
: Id(Id), ExpectedCount(ExpectedCount), Count(0),
ExpectedName(ExpectedName) {}
- void onEndOfTranslationUnit() LLVM_OVERRIDE {
+ void onEndOfTranslationUnit() override {
if (ExpectedCount != -1)
EXPECT_EQ(ExpectedCount, Count);
if (!ExpectedName.empty())
@@ -694,7 +698,8 @@ public:
EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>());
return true;
}
- EXPECT_TRUE(M.count(Id) == 0 || M.find(Id)->second.template get<T>() == 0);
+ EXPECT_TRUE(M.count(Id) == 0 ||
+ M.find(Id)->second.template get<T>() == nullptr);
return false;
}
@@ -1000,7 +1005,7 @@ TEST(Matcher, Call) {
}
TEST(Matcher, Lambda) {
- EXPECT_TRUE(matches("auto f = [&] (int i) { return i; };",
+ EXPECT_TRUE(matches("auto f = [] (int i) { return i; };",
lambdaExpr()));
}
@@ -1012,6 +1017,17 @@ TEST(Matcher, ForRange) {
forRangeStmt()));
}
+TEST(Matcher, SubstNonTypeTemplateParm) {
+ EXPECT_FALSE(matches("template<int N>\n"
+ "struct A { static const int n = 0; };\n"
+ "struct B : public A<42> {};",
+ substNonTypeTemplateParmExpr()));
+ EXPECT_TRUE(matches("template<int N>\n"
+ "struct A { static const int n = N; };\n"
+ "struct B : public A<42> {};",
+ substNonTypeTemplateParmExpr()));
+}
+
TEST(Matcher, UserDefinedLiteral) {
EXPECT_TRUE(matches("constexpr char operator \"\" _inc (const char i) {"
" return i + 1;"
@@ -1038,7 +1054,7 @@ TEST(HasType, MatchesAsString) {
EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };",
fieldDecl(hasType(asString("ns::A")))));
EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };",
- fieldDecl(hasType(asString("struct <anonymous>::A")))));
+ fieldDecl(hasType(asString("struct (anonymous namespace)::A")))));
}
TEST(Matcher, OverloadedOperatorCall) {
@@ -1079,12 +1095,19 @@ TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) {
"bool operator&&(Y x, Y y) { return true; }; "
"Y a; Y b; bool c = a && b;",
OpCallLessLess));
+ StatementMatcher OpStarCall =
+ operatorCallExpr(hasOverloadedOperatorName("*"));
+ EXPECT_TRUE(matches("class Y; int operator*(Y &); void f(Y &y) { *y; }",
+ OpStarCall));
DeclarationMatcher ClassWithOpStar =
recordDecl(hasMethod(hasOverloadedOperatorName("*")));
EXPECT_TRUE(matches("class Y { int operator*(); };",
ClassWithOpStar));
EXPECT_TRUE(notMatches("class Y { void myOperator(); };",
ClassWithOpStar)) ;
+ DeclarationMatcher AnyOpStar = functionDecl(hasOverloadedOperatorName("*"));
+ EXPECT_TRUE(matches("class Y; int operator*(Y &);", AnyOpStar));
+ EXPECT_TRUE(matches("class Y { int operator*(); };", AnyOpStar));
}
TEST(Matcher, NestedOverloadedOperatorCalls) {
@@ -1162,6 +1185,18 @@ TEST(Matcher, VariableUsage) {
"}", Reference));
}
+TEST(Matcher, VarDecl_Storage) {
+ auto M = varDecl(hasName("X"), hasLocalStorage());
+ EXPECT_TRUE(matches("void f() { int X; }", M));
+ EXPECT_TRUE(notMatches("int X;", M));
+ EXPECT_TRUE(notMatches("void f() { static int X; }", M));
+
+ M = varDecl(hasName("X"), hasGlobalStorage());
+ EXPECT_TRUE(notMatches("void f() { int X; }", M));
+ EXPECT_TRUE(matches("int X;", M));
+ EXPECT_TRUE(matches("void f() { static int X; }", M));
+}
+
TEST(Matcher, FindsVarDeclInFunctionParameter) {
EXPECT_TRUE(matches(
"void f(int i) {}",
@@ -1300,15 +1335,16 @@ TEST(Function, MatchesFunctionDeclarations) {
EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF));
EXPECT_TRUE(notMatches("void f() { }", CallFunctionF));
-#if !defined(_MSC_VER)
- // FIXME: Make this work for MSVC.
- // Dependent contexts, but a non-dependent call.
- EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }",
- CallFunctionF));
- EXPECT_TRUE(
- matches("void f(); template <int N> struct S { void g() { f(); } };",
- CallFunctionF));
-#endif
+ if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() !=
+ llvm::Triple::Win32) {
+ // FIXME: Make this work for MSVC.
+ // Dependent contexts, but a non-dependent call.
+ EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }",
+ CallFunctionF));
+ EXPECT_TRUE(
+ matches("void f(); template <int N> struct S { void g() { f(); } };",
+ CallFunctionF));
+ }
// Depedent calls don't match.
EXPECT_TRUE(
@@ -1479,7 +1515,7 @@ TEST(HasAnyParameter, DoesNotMatchThisPointer) {
recordDecl(hasName("X"))))))));
}
-TEST(HasName, MatchesParameterVariableDeclartions) {
+TEST(HasName, MatchesParameterVariableDeclarations) {
EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };",
methodDecl(hasAnyParameter(hasName("x")))));
EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
@@ -1527,6 +1563,19 @@ TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) {
"A<int> a;",
classTemplateSpecializationDecl(hasAnyTemplateArgument(
refersToDeclaration(decl())))));
+
+ EXPECT_TRUE(matches(
+ "struct B { int next; };"
+ "template<int(B::*next_ptr)> struct A {};"
+ "A<&B::next> a;",
+ templateSpecializationType(hasAnyTemplateArgument(isExpr(
+ hasDescendant(declRefExpr(to(fieldDecl(hasName("next"))))))))));
+
+ EXPECT_TRUE(notMatches(
+ "template <typename T> struct A {};"
+ "A<int> a;",
+ templateSpecializationType(hasAnyTemplateArgument(
+ refersToDeclaration(decl())))));
}
TEST(Matcher, MatchesSpecificArgument) {
@@ -1540,6 +1589,17 @@ TEST(Matcher, MatchesSpecificArgument) {
"A<int, bool> a;",
classTemplateSpecializationDecl(hasTemplateArgument(
1, refersToType(asString("int"))))));
+
+ EXPECT_TRUE(matches(
+ "template<typename T, typename U> class A {};"
+ "A<bool, int> a;",
+ templateSpecializationType(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
+ EXPECT_TRUE(notMatches(
+ "template<typename T, typename U> class A {};"
+ "A<int, bool> a;",
+ templateSpecializationType(hasTemplateArgument(
+ 1, refersToType(asString("int"))))));
}
TEST(Matcher, MatchesAccessSpecDecls) {
@@ -1561,6 +1621,13 @@ TEST(Matcher, MatchesVirtualMethod) {
methodDecl(isVirtual())));
}
+TEST(Matcher, MatchesPureMethod) {
+ EXPECT_TRUE(matches("class X { virtual int f() = 0; };",
+ methodDecl(isPure(), hasName("::X::f"))));
+ EXPECT_TRUE(notMatches("class X { int f(); };",
+ methodDecl(isPure())));
+}
+
TEST(Matcher, MatchesConstMethod) {
EXPECT_TRUE(matches("struct A { void foo() const; };",
methodDecl(isConst())));
@@ -1637,6 +1704,17 @@ TEST(Matcher, ConstructorArgumentCount) {
Constructor1Arg));
}
+TEST(Matcher, ConstructorListInitialization) {
+ StatementMatcher ConstructorListInit = constructExpr(isListInitialization());
+
+ EXPECT_TRUE(
+ matches("class X { public: X(int); }; void x() { X x{0}; }",
+ ConstructorListInit));
+ EXPECT_FALSE(
+ matches("class X { public: X(int); }; void x() { X x(0); }",
+ ConstructorListInit));
+}
+
TEST(Matcher,ThisExpr) {
EXPECT_TRUE(
matches("struct X { int a; int f () { return a; } };", thisExpr()));
@@ -1727,6 +1805,9 @@ TEST(ConstructorDeclaration, IsImplicit) {
constructorDecl(isImplicit())));
EXPECT_TRUE(matches("class Foo { Foo(){} };",
constructorDecl(unless(isImplicit()))));
+ // The compiler added an implicit assignment operator.
+ EXPECT_TRUE(matches("struct A { int x; } a = {0}, b = a; void f() { a = b; }",
+ methodDecl(isImplicit(), hasName("operator="))));
}
TEST(DestructorDeclaration, MatchesVirtualDestructor) {
@@ -1917,6 +1998,17 @@ TEST(Matcher, Conditions) {
EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition));
}
+TEST(IfStmt, ChildTraversalMatchers) {
+ EXPECT_TRUE(matches("void f() { if (false) true; else false; }",
+ ifStmt(hasThen(boolLiteral(equals(true))))));
+ EXPECT_TRUE(notMatches("void f() { if (false) false; else true; }",
+ ifStmt(hasThen(boolLiteral(equals(true))))));
+ EXPECT_TRUE(matches("void f() { if (false) false; else true; }",
+ ifStmt(hasElse(boolLiteral(equals(true))))));
+ EXPECT_TRUE(notMatches("void f() { if (false) true; else false; }",
+ ifStmt(hasElse(boolLiteral(equals(true))))));
+}
+
TEST(MatchBinaryOperator, HasOperatorName) {
StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||"));
@@ -2339,6 +2431,14 @@ TEST(For, ForLoopInternals) {
forStmt(hasLoopInit(anything()))));
}
+TEST(For, ForRangeLoopInternals) {
+ EXPECT_TRUE(matches("void f(){ int a[] {1, 2}; for (int i : a); }",
+ forRangeStmt(hasLoopVariable(anything()))));
+ EXPECT_TRUE(matches(
+ "void f(){ int a[] {1, 2}; for (int i : a); }",
+ forRangeStmt(hasRangeInit(declRefExpr(to(varDecl(hasName("a"))))))));
+}
+
TEST(For, NegativeForLoopInternals) {
EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }",
forStmt(hasCondition(expr()))));
@@ -2375,6 +2475,8 @@ TEST(HasBody, FindsBodyOfForWhileDoLoops) {
whileStmt(hasBody(compoundStmt()))));
EXPECT_TRUE(matches("void f() { do {} while(true); }",
doStmt(hasBody(compoundStmt()))));
+ EXPECT_TRUE(matches("void f() { int p[2]; for (auto x : p) {} }",
+ forRangeStmt(hasBody(compoundStmt()))));
}
TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {
@@ -2608,13 +2710,13 @@ TEST(ReinterpretCast, DoesNotMatchOtherCasts) {
}
TEST(FunctionalCast, MatchesSimpleCase) {
- std::string foo_class = "class Foo { public: Foo(char*); };";
+ std::string foo_class = "class Foo { public: Foo(const char*); };";
EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }",
functionalCastExpr()));
}
TEST(FunctionalCast, DoesNotMatchOtherCasts) {
- std::string FooClass = "class Foo { public: Foo(char*); };";
+ std::string FooClass = "class Foo { public: Foo(const char*); };";
EXPECT_TRUE(
notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }",
functionalCastExpr()));
@@ -2892,6 +2994,15 @@ TEST(DeclarationStatement, MatchesVariableDeclarationStatements) {
EXPECT_TRUE(matches("void x() { int a; }", declStmt()));
}
+TEST(ExprWithCleanups, MatchesExprWithCleanups) {
+ EXPECT_TRUE(matches("struct Foo { ~Foo(); };"
+ "const Foo f = Foo();",
+ varDecl(hasInitializer(exprWithCleanups()))));
+ EXPECT_FALSE(matches("struct Foo { };"
+ "const Foo f = Foo();",
+ varDecl(hasInitializer(exprWithCleanups()))));
+}
+
TEST(InitListExpression, MatchesInitListExpression) {
EXPECT_TRUE(matches("int a[] = { 1, 2 };",
initListExpr(hasType(asString("int [2]")))));
@@ -2927,6 +3038,13 @@ TEST(UsingDeclaration, ThroughUsingDeclaration) {
declRefExpr(throughUsingDecl(anything()))));
}
+TEST(UsingDirectiveDeclaration, MatchesUsingNamespace) {
+ EXPECT_TRUE(matches("namespace X { int x; } using namespace X;",
+ usingDirectiveDecl()));
+ EXPECT_FALSE(
+ matches("namespace X { int x; } using X::x;", usingDirectiveDecl()));
+}
+
TEST(SingleDecl, IsSingleDecl) {
StatementMatcher SingleDeclStmt =
declStmt(hasSingleDecl(varDecl(hasInitializer(anything()))));
@@ -3530,6 +3648,27 @@ TEST(HasParent, MatchesAllParents) {
compoundStmt(hasParent(recordDecl()))));
}
+TEST(HasParent, NoDuplicateParents) {
+ class HasDuplicateParents : public BoundNodesCallback {
+ public:
+ bool run(const BoundNodes *Nodes) override { return false; }
+ bool run(const BoundNodes *Nodes, ASTContext *Context) override {
+ const Stmt *Node = Nodes->getNodeAs<Stmt>("node");
+ std::set<const void *> Parents;
+ for (const auto &Parent : Context->getParents(*Node)) {
+ if (!Parents.insert(Parent.getMemoizationData()).second) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ EXPECT_FALSE(matchAndVerifyResultTrue(
+ "template <typename T> int Foo() { return 1 + 2; }\n"
+ "int x = Foo<int>() + Foo<unsigned>();",
+ stmt().bind("node"), new HasDuplicateParents()));
+}
+
TEST(TypeMatching, MatchesTypes) {
EXPECT_TRUE(matches("struct S {};", qualType().bind("loc")));
}
@@ -3617,12 +3756,16 @@ TEST(TypeMatching, MatchesVariableArrayType) {
}
TEST(TypeMatching, MatchesAtomicTypes) {
- EXPECT_TRUE(matches("_Atomic(int) i;", atomicType()));
-
- EXPECT_TRUE(matches("_Atomic(int) i;",
- atomicType(hasValueType(isInteger()))));
- EXPECT_TRUE(notMatches("_Atomic(float) f;",
- atomicType(hasValueType(isInteger()))));
+ if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() !=
+ llvm::Triple::Win32) {
+ // FIXME: Make this work for MSVC.
+ EXPECT_TRUE(matches("_Atomic(int) i;", atomicType()));
+
+ EXPECT_TRUE(matches("_Atomic(int) i;",
+ atomicType(hasValueType(isInteger()))));
+ EXPECT_TRUE(notMatches("_Atomic(float) f;",
+ atomicType(hasValueType(isInteger()))));
+ }
}
TEST(TypeMatching, MatchesAutoTypes) {
@@ -4021,7 +4164,7 @@ public:
virtual bool run(const BoundNodes *Nodes, ASTContext *Context) {
const T *Node = Nodes->getNodeAs<T>(Id);
return selectFirst<const T>(InnerId,
- match(InnerMatcher, *Node, *Context)) != NULL;
+ match(InnerMatcher, *Node, *Context)) !=nullptr;
}
private:
std::string Id;
@@ -4075,24 +4218,32 @@ public:
}
bool verify(const BoundNodes &Nodes, ASTContext &Context, const Stmt *Node) {
+ // Use the original typed pointer to verify we can pass pointers to subtypes
+ // to equalsNode.
+ const T *TypedNode = cast<T>(Node);
return selectFirst<const T>(
- "", match(stmt(hasParent(stmt(has(stmt(equalsNode(Node)))).bind(""))),
- *Node, Context)) != NULL;
+ "", match(stmt(hasParent(
+ stmt(has(stmt(equalsNode(TypedNode)))).bind(""))),
+ *Node, Context)) != nullptr;
}
bool verify(const BoundNodes &Nodes, ASTContext &Context, const Decl *Node) {
+ // Use the original typed pointer to verify we can pass pointers to subtypes
+ // to equalsNode.
+ const T *TypedNode = cast<T>(Node);
return selectFirst<const T>(
- "", match(decl(hasParent(decl(has(decl(equalsNode(Node)))).bind(""))),
- *Node, Context)) != NULL;
+ "", match(decl(hasParent(
+ decl(has(decl(equalsNode(TypedNode)))).bind(""))),
+ *Node, Context)) != nullptr;
}
};
TEST(IsEqualTo, MatchesNodesByIdentity) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""),
- new VerifyAncestorHasChildIsEqual<Decl>()));
- EXPECT_TRUE(
- matchAndVerifyResultTrue("void f() { if(true) {} }", ifStmt().bind(""),
- new VerifyAncestorHasChildIsEqual<Stmt>()));
+ new VerifyAncestorHasChildIsEqual<CXXRecordDecl>()));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f() { if (true) if(true) {} }", ifStmt().bind(""),
+ new VerifyAncestorHasChildIsEqual<IfStmt>()));
}
class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback {
@@ -4111,12 +4262,13 @@ TEST(MatchFinder, InterceptsStartOfTranslationUnit) {
MatchFinder Finder;
VerifyStartOfTranslationUnit VerifyCallback;
Finder.addMatcher(decl(), &VerifyCallback);
- OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_TRUE(VerifyCallback.Called);
VerifyCallback.Called = false;
- OwningPtr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
+ std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
ASSERT_TRUE(AST.get());
Finder.matchAST(AST->getASTContext());
EXPECT_TRUE(VerifyCallback.Called);
@@ -4138,12 +4290,13 @@ TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
MatchFinder Finder;
VerifyEndOfTranslationUnit VerifyCallback;
Finder.addMatcher(decl(), &VerifyCallback);
- OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ std::unique_ptr<FrontendActionFactory> Factory(
+ newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_TRUE(VerifyCallback.Called);
VerifyCallback.Called = false;
- OwningPtr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
+ std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
ASSERT_TRUE(AST.get());
Finder.matchAST(AST->getASTContext());
EXPECT_TRUE(VerifyCallback.Called);
@@ -4204,7 +4357,6 @@ TEST(EqualsBoundNodeMatcher, Type) {
}
TEST(EqualsBoundNodeMatcher, UsingForEachDescendant) {
-
EXPECT_TRUE(matchAndVerifyResultTrue(
"int f() {"
" if (1) {"
@@ -4238,5 +4390,37 @@ TEST(EqualsBoundNodeMatcher, FiltersMatchedCombinations) {
new VerifyIdIsBoundTo<VarDecl>("d", 5)));
}
+TEST(EqualsBoundNodeMatcher, UnlessDescendantsOfAncestorsMatch) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "struct StringRef { int size() const; const char* data() const; };"
+ "void f(StringRef v) {"
+ " v.data();"
+ "}",
+ memberCallExpr(
+ callee(methodDecl(hasName("data"))),
+ on(declRefExpr(to(varDecl(hasType(recordDecl(hasName("StringRef"))))
+ .bind("var")))),
+ unless(hasAncestor(stmt(hasDescendant(memberCallExpr(
+ callee(methodDecl(anyOf(hasName("size"), hasName("length")))),
+ on(declRefExpr(to(varDecl(equalsBoundNode("var")))))))))))
+ .bind("data"),
+ new VerifyIdIsBoundTo<Expr>("data", 1)));
+
+ EXPECT_FALSE(matches(
+ "struct StringRef { int size() const; const char* data() const; };"
+ "void f(StringRef v) {"
+ " v.data();"
+ " v.size();"
+ "}",
+ memberCallExpr(
+ callee(methodDecl(hasName("data"))),
+ on(declRefExpr(to(varDecl(hasType(recordDecl(hasName("StringRef"))))
+ .bind("var")))),
+ unless(hasAncestor(stmt(hasDescendant(memberCallExpr(
+ callee(methodDecl(anyOf(hasName("size"), hasName("length")))),
+ on(declRefExpr(to(varDecl(equalsBoundNode("var")))))))))))
+ .bind("data")));
+}
+
} // end namespace ast_matchers
} // end namespace clang