blob: 75d6ca5ba17f8bd65b98ec81cc022021a0946e15 [file] [log] [blame]
//= unittests/ASTMatchers/ASTMatchersTraversalTest.cpp - matchers unit tests =//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ASTMatchersTest.h"
#include "clang/AST/Attrs.inc"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include "gtest/gtest.h"
namespace clang {
namespace ast_matchers {
TEST(DeclarationMatcher, hasMethod) {
EXPECT_TRUE(matches("class A { void func(); };",
cxxRecordDecl(hasMethod(hasName("func")))));
EXPECT_TRUE(notMatches("class A { void func(); };",
cxxRecordDecl(hasMethod(isPublic()))));
}
TEST(DeclarationMatcher, ClassDerivedFromDependentTemplateSpecialization) {
EXPECT_TRUE(matches(
"template <typename T> struct A {"
" template <typename T2> struct F {};"
"};"
"template <typename T> struct B : A<T>::template F<T> {};"
"B<int> b;",
cxxRecordDecl(hasName("B"), isDerivedFrom(recordDecl()))));
}
TEST(DeclarationMatcher, hasDeclContext) {
EXPECT_TRUE(matches(
"namespace N {"
" namespace M {"
" class D {};"
" }"
"}",
recordDecl(hasDeclContext(namespaceDecl(hasName("M"))))));
EXPECT_TRUE(notMatches(
"namespace N {"
" namespace M {"
" class D {};"
" }"
"}",
recordDecl(hasDeclContext(namespaceDecl(hasName("N"))))));
EXPECT_TRUE(matches("namespace {"
" namespace M {"
" class D {};"
" }"
"}",
recordDecl(hasDeclContext(namespaceDecl(
hasName("M"), hasDeclContext(namespaceDecl()))))));
EXPECT_TRUE(matches("class D{};", decl(hasDeclContext(decl()))));
}
TEST(HasDescendant, MatchesDescendantTypes) {
EXPECT_TRUE(matches("void f() { int i = 3; }",
decl(hasDescendant(loc(builtinType())))));
EXPECT_TRUE(matches("void f() { int i = 3; }",
stmt(hasDescendant(builtinType()))));
EXPECT_TRUE(matches("void f() { int i = 3; }",
stmt(hasDescendant(loc(builtinType())))));
EXPECT_TRUE(matches("void f() { int i = 3; }",
stmt(hasDescendant(qualType(builtinType())))));
EXPECT_TRUE(notMatches("void f() { float f = 2.0f; }",
stmt(hasDescendant(isInteger()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f() { int a; float c; int d; int e; }",
functionDecl(forEachDescendant(
varDecl(hasDescendant(isInteger())).bind("x"))),
std::make_unique<VerifyIdIsBoundTo<Decl>>("x", 3)));
}
TEST(HasDescendant, MatchesDescendantsOfTypes) {
EXPECT_TRUE(matches("void f() { int*** i; }",
qualType(hasDescendant(builtinType()))));
EXPECT_TRUE(matches("void f() { int*** i; }",
qualType(hasDescendant(
pointerType(pointee(builtinType()))))));
EXPECT_TRUE(matches("void f() { int*** i; }",
typeLoc(hasDescendant(loc(builtinType())))));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f() { int*** i; }",
qualType(asString("int ***"), forEachDescendant(pointerType().bind("x"))),
std::make_unique<VerifyIdIsBoundTo<Type>>("x", 2)));
}
TEST(Has, MatchesChildrenOfTypes) {
EXPECT_TRUE(matches("int i;",
varDecl(hasName("i"), has(isInteger()))));
EXPECT_TRUE(notMatches("int** i;",
varDecl(hasName("i"), has(isInteger()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
"int (*f)(float, int);",
qualType(functionType(), forEach(qualType(isInteger()).bind("x"))),
std::make_unique<VerifyIdIsBoundTo<QualType>>("x", 2)));
}
TEST(Has, MatchesChildTypes) {
EXPECT_TRUE(matches(
"int* i;",
varDecl(hasName("i"), hasType(qualType(has(builtinType()))))));
EXPECT_TRUE(notMatches(
"int* i;",
varDecl(hasName("i"), hasType(qualType(has(pointerType()))))));
}
TEST(StatementMatcher, Has) {
StatementMatcher HasVariableI =
expr(hasType(pointsTo(recordDecl(hasName("X")))),
has(ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("i")))))));
EXPECT_TRUE(matches(
"class X; X *x(int); void c() { int i; x(i); }", HasVariableI));
EXPECT_TRUE(notMatches(
"class X; X *x(int); void c() { int i; x(42); }", HasVariableI));
}
TEST(StatementMatcher, HasDescendant) {
StatementMatcher HasDescendantVariableI =
expr(hasType(pointsTo(recordDecl(hasName("X")))),
hasDescendant(declRefExpr(to(varDecl(hasName("i"))))));
EXPECT_TRUE(matches(
"class X; X *x(bool); bool b(int); void c() { int i; x(b(i)); }",
HasDescendantVariableI));
EXPECT_TRUE(notMatches(
"class X; X *x(bool); bool b(int); void c() { int i; x(b(42)); }",
HasDescendantVariableI));
}
TEST(TypeMatcher, MatchesClassType) {
TypeMatcher TypeA = hasDeclaration(recordDecl(hasName("A")));
EXPECT_TRUE(matches("class A { public: A *a; };", TypeA));
EXPECT_TRUE(notMatches("class A {};", TypeA));
TypeMatcher TypeDerivedFromA =
hasDeclaration(cxxRecordDecl(isDerivedFrom("A")));
EXPECT_TRUE(matches("class A {}; class B : public A { public: B *b; };",
TypeDerivedFromA));
EXPECT_TRUE(notMatches("class A {};", TypeA));
TypeMatcher TypeAHasClassB = hasDeclaration(
recordDecl(hasName("A"), has(recordDecl(hasName("B")))));
EXPECT_TRUE(
matches("class A { public: A *a; class B {}; };", TypeAHasClassB));
EXPECT_TRUE(matchesC("struct S {}; void f(void) { struct S s; }",
varDecl(hasType(namedDecl(hasName("S"))))));
}
TEST(TypeMatcher, MatchesDeclTypes) {
// TypedefType -> TypedefNameDecl
EXPECT_TRUE(matches("typedef int I; void f(I i);",
parmVarDecl(hasType(namedDecl(hasName("I"))))));
// ObjCObjectPointerType
EXPECT_TRUE(matchesObjC("@interface Foo @end void f(Foo *f);",
parmVarDecl(hasType(objcObjectPointerType()))));
// ObjCObjectPointerType -> ObjCInterfaceType -> ObjCInterfaceDecl
EXPECT_TRUE(matchesObjC(
"@interface Foo @end void f(Foo *f);",
parmVarDecl(hasType(pointsTo(objcInterfaceDecl(hasName("Foo")))))));
// TemplateTypeParmType
EXPECT_TRUE(matches("template <typename T> void f(T t);",
parmVarDecl(hasType(templateTypeParmType()))));
// TemplateTypeParmType -> TemplateTypeParmDecl
EXPECT_TRUE(matches("template <typename T> void f(T t);",
parmVarDecl(hasType(namedDecl(hasName("T"))))));
// InjectedClassNameType
EXPECT_TRUE(matches("template <typename T> struct S {"
" void f(S s);"
"};",
parmVarDecl(hasType(elaboratedType(
namesType(injectedClassNameType()))))));
EXPECT_TRUE(notMatches("template <typename T> struct S {"
" void g(S<T> s);"
"};",
parmVarDecl(hasType(elaboratedType(
namesType(injectedClassNameType()))))));
// InjectedClassNameType -> CXXRecordDecl
EXPECT_TRUE(matches("template <typename T> struct S {"
" void f(S s);"
"};",
parmVarDecl(hasType(namedDecl(hasName("S"))))));
static const char Using[] = "template <typename T>"
"struct Base {"
" typedef T Foo;"
"};"
""
"template <typename T>"
"struct S : private Base<T> {"
" using typename Base<T>::Foo;"
" void f(Foo);"
"};";
// UnresolvedUsingTypenameDecl
EXPECT_TRUE(matches(Using, unresolvedUsingTypenameDecl(hasName("Foo"))));
// UnresolvedUsingTypenameType -> UnresolvedUsingTypenameDecl
EXPECT_TRUE(matches(Using, parmVarDecl(hasType(namedDecl(hasName("Foo"))))));
}
TEST(HasDeclaration, HasDeclarationOfEnumType) {
EXPECT_TRUE(matches("enum X {}; void y(X *x) { x; }",
expr(hasType(pointsTo(
qualType(hasDeclaration(enumDecl(hasName("X")))))))));
}
TEST(HasDeclaration, HasGetDeclTraitTest) {
static_assert(internal::has_getDecl<TypedefType>::value,
"Expected TypedefType to have a getDecl.");
static_assert(internal::has_getDecl<RecordType>::value,
"Expected RecordType to have a getDecl.");
static_assert(!internal::has_getDecl<TemplateSpecializationType>::value,
"Expected TemplateSpecializationType to *not* have a getDecl.");
}
TEST(HasDeclaration, ElaboratedType) {
EXPECT_TRUE(matches(
"namespace n { template <typename T> struct X {}; }"
"void f(n::X<int>);",
parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl()))))));
EXPECT_TRUE(matches(
"namespace n { template <typename T> struct X {}; }"
"void f(n::X<int>);",
parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl()))))));
}
TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) {
EXPECT_TRUE(matches(
"typedef int X; X a;",
varDecl(hasName("a"), hasType(elaboratedType(namesType(
typedefType(hasDeclaration(decl()))))))));
// FIXME: Add tests for other types with getDecl() (e.g. RecordType)
}
TEST(HasDeclaration, HasDeclarationOfTemplateSpecializationType) {
EXPECT_TRUE(matches(
"template <typename T> class A {}; A<int> a;",
varDecl(hasType(elaboratedType(namesType(templateSpecializationType(
hasDeclaration(namedDecl(hasName("A"))))))))));
EXPECT_TRUE(matches(
"template <typename T> class A {};"
"template <typename T> class B { A<T> a; };",
fieldDecl(hasType(elaboratedType(namesType(templateSpecializationType(
hasDeclaration(namedDecl(hasName("A"))))))))));
EXPECT_TRUE(matches(
"template <typename T> class A {}; A<int> a;",
varDecl(hasType(elaboratedType(namesType(
templateSpecializationType(hasDeclaration(cxxRecordDecl()))))))));
}
TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) {
EXPECT_TRUE(
matches("int *A = new int();",
cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1))))));
}
TEST(HasDeclaration, HasDeclarationOfTypeAlias) {
EXPECT_TRUE(matches(
"template <typename T> using C = T; C<int> c;",
varDecl(hasType(elaboratedType(namesType(templateSpecializationType(
hasDeclaration(typeAliasTemplateDecl()))))))));
}
TEST(HasDeclaration, HasDeclarationOfObjCInterface) {
EXPECT_TRUE(matchesObjC("@interface BaseClass @end void f() {BaseClass* b;}",
varDecl(hasType(objcObjectPointerType(
pointee(hasDeclaration(objcInterfaceDecl())))))));
}
TEST(HasUnqualifiedDesugaredType, DesugarsUsing) {
EXPECT_TRUE(
matches("struct A {}; using B = A; B b;",
varDecl(hasType(hasUnqualifiedDesugaredType(recordType())))));
EXPECT_TRUE(
matches("struct A {}; using B = A; using C = B; C b;",
varDecl(hasType(hasUnqualifiedDesugaredType(recordType())))));
}
TEST(HasUnderlyingDecl, Matches) {
EXPECT_TRUE(matches("namespace N { template <class T> void f(T t); }"
"template <class T> void g() { using N::f; f(T()); }",
unresolvedLookupExpr(hasAnyDeclaration(
namedDecl(hasUnderlyingDecl(hasName("::N::f")))))));
EXPECT_TRUE(matches(
"namespace N { template <class T> void f(T t); }"
"template <class T> void g() { N::f(T()); }",
unresolvedLookupExpr(hasAnyDeclaration(namedDecl(hasName("::N::f"))))));
EXPECT_TRUE(notMatches(
"namespace N { template <class T> void f(T t); }"
"template <class T> void g() { using N::f; f(T()); }",
unresolvedLookupExpr(hasAnyDeclaration(namedDecl(hasName("::N::f"))))));
}
TEST(HasType, TakesQualTypeMatcherAndMatchesExpr) {
TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X")));
EXPECT_TRUE(
matches("class X {}; void y(X &x) { x; }", expr(hasType(ClassX))));
EXPECT_TRUE(
notMatches("class X {}; void y(X *x) { x; }",
expr(hasType(ClassX))));
EXPECT_TRUE(
matches("class X {}; void y(X *x) { x; }",
expr(hasType(pointsTo(ClassX)))));
}
TEST(HasType, TakesQualTypeMatcherAndMatchesValueDecl) {
TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X")));
EXPECT_TRUE(
matches("class X {}; void y() { X x; }", varDecl(hasType(ClassX))));
EXPECT_TRUE(
notMatches("class X {}; void y() { X *x; }", varDecl(hasType(ClassX))));
EXPECT_TRUE(
matches("class X {}; void y() { X *x; }",
varDecl(hasType(pointsTo(ClassX)))));
}
TEST(HasType, TakesQualTypeMatcherAndMatchesCXXBaseSpecifier) {
TypeMatcher ClassX = hasDeclaration(recordDecl(hasName("X")));
CXXBaseSpecifierMatcher BaseClassX = cxxBaseSpecifier(hasType(ClassX));
DeclarationMatcher ClassHasBaseClassX =
cxxRecordDecl(hasDirectBase(BaseClassX));
EXPECT_TRUE(matches("class X {}; class Y : X {};", ClassHasBaseClassX));
EXPECT_TRUE(notMatches("class Z {}; class Y : Z {};", ClassHasBaseClassX));
}
TEST(HasType, TakesDeclMatcherAndMatchesExpr) {
DeclarationMatcher ClassX = recordDecl(hasName("X"));
EXPECT_TRUE(
matches("class X {}; void y(X &x) { x; }", expr(hasType(ClassX))));
EXPECT_TRUE(
notMatches("class X {}; void y(X *x) { x; }",
expr(hasType(ClassX))));
}
TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) {
DeclarationMatcher ClassX = recordDecl(hasName("X"));
EXPECT_TRUE(
matches("class X {}; void y() { X x; }", varDecl(hasType(ClassX))));
EXPECT_TRUE(
notMatches("class X {}; void y() { X *x; }", varDecl(hasType(ClassX))));
}
TEST(HasType, TakesDeclMatcherAndMatchesCXXBaseSpecifier) {
DeclarationMatcher ClassX = recordDecl(hasName("X"));
CXXBaseSpecifierMatcher BaseClassX = cxxBaseSpecifier(hasType(ClassX));
DeclarationMatcher ClassHasBaseClassX =
cxxRecordDecl(hasDirectBase(BaseClassX));
EXPECT_TRUE(matches("class X {}; class Y : X {};", ClassHasBaseClassX));
EXPECT_TRUE(notMatches("class Z {}; class Y : Z {};", ClassHasBaseClassX));
}
TEST(HasType, MatchesTypedefDecl) {
EXPECT_TRUE(matches("typedef int X;", typedefDecl(hasType(asString("int")))));
EXPECT_TRUE(matches("typedef const int T;",
typedefDecl(hasType(asString("const int")))));
EXPECT_TRUE(notMatches("typedef const int T;",
typedefDecl(hasType(asString("int")))));
EXPECT_TRUE(matches("typedef int foo; typedef foo bar;",
typedefDecl(hasType(asString("foo")), hasName("bar"))));
}
TEST(HasType, MatchesTypedefNameDecl) {
EXPECT_TRUE(matches("using X = int;", typedefNameDecl(hasType(asString("int")))));
EXPECT_TRUE(matches("using T = const int;",
typedefNameDecl(hasType(asString("const int")))));
EXPECT_TRUE(notMatches("using T = const int;",
typedefNameDecl(hasType(asString("int")))));
EXPECT_TRUE(matches("using foo = int; using bar = foo;",
typedefNameDecl(hasType(asString("foo")), hasName("bar"))));
}
TEST(HasTypeLoc, MatchesBlockDecl) {
EXPECT_TRUE(matchesConditionally(
"auto x = ^int (int a, int b) { return a + b; };",
blockDecl(hasTypeLoc(loc(asString("int (int, int)")))), true,
{"-fblocks"}));
}
TEST(HasTypeLoc, MatchesCXXBaseSpecifierAndCtorInitializer) {
llvm::StringRef code = R"cpp(
class Foo {};
class Bar : public Foo {
Bar() : Foo() {}
};
)cpp";
EXPECT_TRUE(matches(
code, cxxRecordDecl(hasAnyBase(hasTypeLoc(loc(asString("Foo")))))));
EXPECT_TRUE(
matches(code, cxxCtorInitializer(hasTypeLoc(loc(asString("Foo"))))));
}
TEST(HasTypeLoc, MatchesCXXFunctionalCastExpr) {
EXPECT_TRUE(matches("auto x = int(3);",
cxxFunctionalCastExpr(hasTypeLoc(loc(asString("int"))))));
}
TEST(HasTypeLoc, MatchesCXXNewExpr) {
EXPECT_TRUE(matches("auto* x = new int(3);",
cxxNewExpr(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("class Foo{}; auto* x = new Foo();",
cxxNewExpr(hasTypeLoc(loc(asString("Foo"))))));
}
TEST(HasTypeLoc, MatchesCXXTemporaryObjectExpr) {
EXPECT_TRUE(
matches("struct Foo { Foo(int, int); }; auto x = Foo(1, 2);",
cxxTemporaryObjectExpr(hasTypeLoc(loc(asString("Foo"))))));
}
TEST(HasTypeLoc, MatchesCXXUnresolvedConstructExpr) {
EXPECT_TRUE(
matches("template <typename T> T make() { return T(); }",
cxxUnresolvedConstructExpr(hasTypeLoc(loc(asString("T"))))));
}
TEST(HasTypeLoc, MatchesCompoundLiteralExpr) {
EXPECT_TRUE(
matches("int* x = (int[2]) { 0, 1 };",
compoundLiteralExpr(hasTypeLoc(loc(asString("int[2]"))))));
}
TEST(HasTypeLoc, MatchesDeclaratorDecl) {
EXPECT_TRUE(matches("int x;",
varDecl(hasName("x"), hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("int x(3);",
varDecl(hasName("x"), hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("struct Foo { Foo(int, int); }; Foo x(1, 2);",
varDecl(hasName("x"), hasTypeLoc(loc(asString("Foo"))))));
// Make sure we don't crash on implicit constructors.
EXPECT_TRUE(notMatches("class X {}; X x;",
declaratorDecl(hasTypeLoc(loc(asString("int"))))));
}
TEST(HasTypeLoc, MatchesExplicitCastExpr) {
EXPECT_TRUE(matches("auto x = (int) 3;",
explicitCastExpr(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("auto x = static_cast<int>(3);",
explicitCastExpr(hasTypeLoc(loc(asString("int"))))));
}
TEST(HasTypeLoc, MatchesObjCPropertyDecl) {
EXPECT_TRUE(matchesObjC(R"objc(
@interface Foo
@property int enabled;
@end
)objc",
objcPropertyDecl(hasTypeLoc(loc(asString("int"))))));
}
TEST(HasTypeLoc, MatchesTemplateArgumentLoc) {
EXPECT_TRUE(matches("template <typename T> class Foo {}; Foo<int> x;",
templateArgumentLoc(hasTypeLoc(loc(asString("int"))))));
}
TEST(HasTypeLoc, MatchesTypedefNameDecl) {
EXPECT_TRUE(matches("typedef int X;",
typedefNameDecl(hasTypeLoc(loc(asString("int"))))));
EXPECT_TRUE(matches("using X = int;",
typedefNameDecl(hasTypeLoc(loc(asString("int"))))));
}
TEST(Callee, MatchesDeclarations) {
StatementMatcher CallMethodX = callExpr(callee(cxxMethodDecl(hasName("x"))));
EXPECT_TRUE(matches("class Y { void x() { x(); } };", CallMethodX));
EXPECT_TRUE(notMatches("class Y { void x() {} };", CallMethodX));
CallMethodX = traverse(TK_AsIs, callExpr(callee(cxxConversionDecl())));
EXPECT_TRUE(
matches("struct Y { operator int() const; }; int i = Y();", CallMethodX));
EXPECT_TRUE(notMatches("struct Y { operator int() const; }; Y y = Y();",
CallMethodX));
}
TEST(Callee, MatchesMemberExpressions) {
EXPECT_TRUE(matches("class Y { void x() { this->x(); } };",
callExpr(callee(memberExpr()))));
EXPECT_TRUE(
notMatches("class Y { void x() { this->x(); } };", callExpr(callee(callExpr()))));
}
TEST(Matcher, Argument) {
StatementMatcher CallArgumentY = callExpr(
hasArgument(0, declRefExpr(to(varDecl(hasName("y"))))));
EXPECT_TRUE(matches("void x(int) { int y; x(y); }", CallArgumentY));
EXPECT_TRUE(
matches("class X { void x(int) { int y; x(y); } };", CallArgumentY));
EXPECT_TRUE(notMatches("void x(int) { int z; x(z); }", CallArgumentY));
StatementMatcher WrongIndex = callExpr(
hasArgument(42, declRefExpr(to(varDecl(hasName("y"))))));
EXPECT_TRUE(notMatches("void x(int) { int y; x(y); }", WrongIndex));
}
TEST(Matcher, AnyArgument) {
auto HasArgumentY = hasAnyArgument(
ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"))))));
StatementMatcher CallArgumentY = callExpr(HasArgumentY);
StatementMatcher CtorArgumentY = cxxConstructExpr(HasArgumentY);
StatementMatcher UnresolvedCtorArgumentY =
cxxUnresolvedConstructExpr(HasArgumentY);
StatementMatcher ObjCCallArgumentY = objcMessageExpr(HasArgumentY);
EXPECT_TRUE(matches("void x(int, int) { int y; x(1, y); }", CallArgumentY));
EXPECT_TRUE(matches("void x(int, int) { int y; x(y, 42); }", CallArgumentY));
EXPECT_TRUE(matches("struct Y { Y(int, int); };"
"void x() { int y; (void)Y(1, y); }",
CtorArgumentY));
EXPECT_TRUE(matches("struct Y { Y(int, int); };"
"void x() { int y; (void)Y(y, 42); }",
CtorArgumentY));
EXPECT_TRUE(matches("template <class Y> void x() { int y; (void)Y(1, y); }",
UnresolvedCtorArgumentY));
EXPECT_TRUE(matches("template <class Y> void x() { int y; (void)Y(y, 42); }",
UnresolvedCtorArgumentY));
EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) y; @end "
"void x(I* i) { int y; [i f:y]; }",
ObjCCallArgumentY));
EXPECT_FALSE(matchesObjC("@interface I -(void)f:(int) z; @end "
"void x(I* i) { int z; [i f:z]; }",
ObjCCallArgumentY));
EXPECT_TRUE(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY));
EXPECT_TRUE(notMatches("struct Y { Y(int, int); };"
"void x() { int y; (void)Y(1, 2); }",
CtorArgumentY));
EXPECT_TRUE(notMatches("template <class Y>"
"void x() { int y; (void)Y(1, 2); }",
UnresolvedCtorArgumentY));
StatementMatcher ImplicitCastedArgument =
traverse(TK_AsIs, callExpr(hasAnyArgument(implicitCastExpr())));
EXPECT_TRUE(matches("void x(long) { int y; x(y); }", ImplicitCastedArgument));
}
TEST(Matcher, HasReceiver) {
EXPECT_TRUE(matchesObjC(
"@interface NSString @end "
"void f(NSString *x) {"
"[x containsString];"
"}",
objcMessageExpr(hasReceiver(declRefExpr(to(varDecl(hasName("x"))))))));
EXPECT_FALSE(matchesObjC(
"@interface NSString +(NSString *) stringWithFormat; @end "
"void f() { [NSString stringWithFormat]; }",
objcMessageExpr(hasReceiver(declRefExpr(to(varDecl(hasName("x"))))))));
}
TEST(Matcher, MatchesMethodsOnLambda) {
StringRef Code = R"cpp(
struct A {
~A() {}
};
void foo()
{
A a;
auto l = [a] { };
auto lCopy = l;
auto lPtrDecay = +[] { };
(void)lPtrDecay;
}
)cpp";
EXPECT_TRUE(matches(
Code, cxxConstructorDecl(
hasBody(compoundStmt()),
hasAncestor(lambdaExpr(hasAncestor(varDecl(hasName("l"))))),
isCopyConstructor())));
EXPECT_TRUE(matches(
Code, cxxConstructorDecl(
hasBody(compoundStmt()),
hasAncestor(lambdaExpr(hasAncestor(varDecl(hasName("l"))))),
isMoveConstructor())));
EXPECT_TRUE(matches(
Code, cxxDestructorDecl(
hasBody(compoundStmt()),
hasAncestor(lambdaExpr(hasAncestor(varDecl(hasName("l"))))))));
EXPECT_TRUE(matches(
Code, cxxConversionDecl(hasBody(compoundStmt(has(returnStmt(
hasReturnValue(implicitCastExpr()))))),
hasAncestor(lambdaExpr(hasAncestor(
varDecl(hasName("lPtrDecay"))))))));
}
TEST(Matcher, MatchesCoroutine) {
FileContentMappings M;
M.push_back(std::make_pair("/coro_header", R"cpp(
namespace std {
template <class... Args>
struct void_t_imp {
using type = void;
};
template <class... Args>
using void_t = typename void_t_imp<Args...>::type;
template <class T, class = void>
struct traits_sfinae_base {};
template <class T>
struct traits_sfinae_base<T, void_t<typename T::promise_type>> {
using promise_type = typename T::promise_type;
};
template <class Ret, class... Args>
struct coroutine_traits : public traits_sfinae_base<Ret> {};
} // namespace std
struct awaitable {
bool await_ready() noexcept;
template <typename F>
void await_suspend(F) noexcept;
void await_resume() noexcept;
} a;
struct promise {
void get_return_object();
awaitable initial_suspend();
awaitable final_suspend() noexcept;
awaitable yield_value(int); // expected-note 2{{candidate}}
void return_value(int); // expected-note 2{{here}}
void unhandled_exception();
};
template <typename... T>
struct std::coroutine_traits<void, T...> { using promise_type = promise; };
namespace std {
template <class PromiseType = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
} // namespace std
)cpp"));
StringRef CoReturnCode = R"cpp(
#include <coro_header>
void check_match_co_return() {
co_return 1;
}
)cpp";
EXPECT_TRUE(matchesConditionally(CoReturnCode,
coreturnStmt(isExpansionInMainFile()), true,
{"-std=c++20", "-I/"}, M));
StringRef CoAwaitCode = R"cpp(
#include <coro_header>
void check_match_co_await() {
co_await a;
}
)cpp";
EXPECT_TRUE(matchesConditionally(CoAwaitCode,
coawaitExpr(isExpansionInMainFile()), true,
{"-std=c++20", "-I/"}, M));
StringRef CoYieldCode = R"cpp(
#include <coro_header>
void check_match_co_yield() {
co_yield 1.0;
}
)cpp";
EXPECT_TRUE(matchesConditionally(CoYieldCode,
coyieldExpr(isExpansionInMainFile()), true,
{"-std=c++20", "-I/"}, M));
StringRef NonCoroCode = R"cpp(
#include <coro_header>
void non_coro_function() {
}
)cpp";
EXPECT_TRUE(matchesConditionally(CoReturnCode, coroutineBodyStmt(), true,
{"-std=c++20", "-I/"}, M));
EXPECT_TRUE(matchesConditionally(CoAwaitCode, coroutineBodyStmt(), true,
{"-std=c++20", "-I/"}, M));
EXPECT_TRUE(matchesConditionally(CoYieldCode, coroutineBodyStmt(), true,
{"-std=c++20", "-I/"}, M));
EXPECT_FALSE(matchesConditionally(NonCoroCode, coroutineBodyStmt(), true,
{"-std=c++20", "-I/"}, M));
StringRef CoroWithDeclCode = R"cpp(
#include <coro_header>
void coro() {
int thevar;
co_return 1;
}
)cpp";
EXPECT_TRUE(matchesConditionally(
CoroWithDeclCode,
coroutineBodyStmt(hasBody(compoundStmt(
has(declStmt(containsDeclaration(0, varDecl(hasName("thevar")))))))),
true, {"-std=c++20", "-I/"}, M));
StringRef CoroWithTryCatchDeclCode = R"cpp(
#include <coro_header>
void coro() try {
int thevar;
co_return 1;
} catch (...) {}
)cpp";
EXPECT_TRUE(matchesConditionally(
CoroWithTryCatchDeclCode,
coroutineBodyStmt(hasBody(compoundStmt(has(cxxTryStmt(has(compoundStmt(has(
declStmt(containsDeclaration(0, varDecl(hasName("thevar")))))))))))),
true, {"-std=c++20", "-I/"}, M));
}
TEST(Matcher, isClassMessage) {
EXPECT_TRUE(matchesObjC(
"@interface NSString +(NSString *) stringWithFormat; @end "
"void f() { [NSString stringWithFormat]; }",
objcMessageExpr(isClassMessage())));
EXPECT_FALSE(matchesObjC(
"@interface NSString @end "
"void f(NSString *x) {"
"[x containsString];"
"}",
objcMessageExpr(isClassMessage())));
}
TEST(Matcher, isInstanceMessage) {
EXPECT_TRUE(matchesObjC(
"@interface NSString @end "
"void f(NSString *x) {"
"[x containsString];"
"}",
objcMessageExpr(isInstanceMessage())));
EXPECT_FALSE(matchesObjC(
"@interface NSString +(NSString *) stringWithFormat; @end "
"void f() { [NSString stringWithFormat]; }",
objcMessageExpr(isInstanceMessage())));
}
TEST(Matcher, isClassMethod) {
EXPECT_TRUE(matchesObjC(
"@interface Bar + (void)bar; @end",
objcMethodDecl(isClassMethod())));
EXPECT_TRUE(matchesObjC(
"@interface Bar @end"
"@implementation Bar + (void)bar {} @end",
objcMethodDecl(isClassMethod())));
EXPECT_FALSE(matchesObjC(
"@interface Foo - (void)foo; @end",
objcMethodDecl(isClassMethod())));
EXPECT_FALSE(matchesObjC(
"@interface Foo @end "
"@implementation Foo - (void)foo {} @end",
objcMethodDecl(isClassMethod())));
}
TEST(Matcher, isInstanceMethod) {
EXPECT_TRUE(matchesObjC(
"@interface Foo - (void)foo; @end",
objcMethodDecl(isInstanceMethod())));
EXPECT_TRUE(matchesObjC(
"@interface Foo @end "
"@implementation Foo - (void)foo {} @end",
objcMethodDecl(isInstanceMethod())));
EXPECT_FALSE(matchesObjC(
"@interface Bar + (void)bar; @end",
objcMethodDecl(isInstanceMethod())));
EXPECT_FALSE(matchesObjC(
"@interface Bar @end"
"@implementation Bar + (void)bar {} @end",
objcMethodDecl(isInstanceMethod())));
}
TEST(MatcherCXXMemberCallExpr, On) {
StringRef Snippet1 = R"cc(
struct Y {
void m();
};
void z(Y y) { y.m(); }
)cc";
StringRef Snippet2 = R"cc(
struct Y {
void m();
};
struct X : public Y {};
void z(X x) { x.m(); }
)cc";
auto MatchesY = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("Y")))));
EXPECT_TRUE(matches(Snippet1, MatchesY));
EXPECT_TRUE(notMatches(Snippet2, MatchesY));
auto MatchesX = cxxMemberCallExpr(on(hasType(cxxRecordDecl(hasName("X")))));
EXPECT_TRUE(matches(Snippet2, MatchesX));
// Parens are ignored.
StringRef Snippet3 = R"cc(
struct Y {
void m();
};
Y g();
void z(Y y) { (g()).m(); }
)cc";
auto MatchesCall = cxxMemberCallExpr(on(callExpr()));
EXPECT_TRUE(matches(Snippet3, MatchesCall));
}
TEST(MatcherCXXMemberCallExpr, OnImplicitObjectArgument) {
StringRef Snippet1 = R"cc(
struct Y {
void m();
};
void z(Y y) { y.m(); }
)cc";
StringRef Snippet2 = R"cc(
struct Y {
void m();
};
struct X : public Y {};
void z(X x) { x.m(); }
)cc";
auto MatchesY = traverse(TK_AsIs, cxxMemberCallExpr(onImplicitObjectArgument(
hasType(cxxRecordDecl(hasName("Y"))))));
EXPECT_TRUE(matches(Snippet1, MatchesY));
EXPECT_TRUE(matches(Snippet2, MatchesY));
auto MatchesX = traverse(TK_AsIs, cxxMemberCallExpr(onImplicitObjectArgument(
hasType(cxxRecordDecl(hasName("X"))))));
EXPECT_TRUE(notMatches(Snippet2, MatchesX));
// Parens are not ignored.
StringRef Snippet3 = R"cc(
struct Y {
void m();
};
Y g();
void z(Y y) { (g()).m(); }
)cc";
auto MatchesCall = traverse(
TK_AsIs, cxxMemberCallExpr(onImplicitObjectArgument(callExpr())));
EXPECT_TRUE(notMatches(Snippet3, MatchesCall));
}
TEST(Matcher, HasObjectExpr) {
StringRef Snippet1 = R"cc(
struct X {
int m;
int f(X x) { return x.m; }
};
)cc";
StringRef Snippet2 = R"cc(
struct X {
int m;
int f(X x) { return m; }
};
)cc";
auto MatchesX =
memberExpr(hasObjectExpression(hasType(cxxRecordDecl(hasName("X")))));
EXPECT_TRUE(matches(Snippet1, MatchesX));
EXPECT_TRUE(notMatches(Snippet2, MatchesX));
auto MatchesXPointer = memberExpr(
hasObjectExpression(hasType(pointsTo(cxxRecordDecl(hasName("X"))))));
EXPECT_TRUE(notMatches(Snippet1, MatchesXPointer));
EXPECT_TRUE(matches(Snippet2, MatchesXPointer));
}
TEST(ForEachArgumentWithParam, ReportsNoFalsePositives) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParam(ArgumentY, IntParam));
// IntParam does not match.
EXPECT_TRUE(notMatches("void f(int* i) { int* y; f(y); }", CallExpr));
// ArgumentY does not match.
EXPECT_TRUE(notMatches("void f(int i) { int x; f(x); }", CallExpr));
}
TEST(ForEachArgumentWithParam, MatchesCXXMemberCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParam(ArgumentY, IntParam));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" const S& operator[](int i) { return *this; }"
"};"
"void f(S S1) {"
" int y = 1;"
" S1[y];"
"}",
CallExpr, std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>("param", 1)));
StatementMatcher CallExpr2 =
callExpr(forEachArgumentWithParam(ArgumentY, IntParam));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" static void g(int i);"
"};"
"void f() {"
" int y = 1;"
" S::g(y);"
"}",
CallExpr2, std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>("param", 1)));
}
TEST(ForEachArgumentWithParam, MatchesCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParam(ArgumentY, IntParam));
EXPECT_TRUE(
matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>(
"param")));
EXPECT_TRUE(
matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>(
"arg")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>("param", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg", 2)));
}
TEST(ForEachArgumentWithParam, MatchesConstructExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
DeclarationMatcher IntParam = parmVarDecl(hasType(isInteger())).bind("param");
StatementMatcher ConstructExpr = traverse(
TK_AsIs, cxxConstructExpr(forEachArgumentWithParam(ArgumentY, IntParam)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct C {"
" C(int i) {}"
"};"
"int y = 0;"
"C Obj(y);",
ConstructExpr,
std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>("param")));
}
TEST(ForEachArgumentWithParam, HandlesBoundNodesForNonMatches) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"void g(int i, int j) {"
" int a;"
" int b;"
" int c;"
" g(a, 0);"
" g(a, b);"
" g(0, b);"
"}",
functionDecl(
forEachDescendant(varDecl().bind("v")),
forEachDescendant(callExpr(forEachArgumentWithParam(
declRefExpr(to(decl(equalsBoundNode("v")))), parmVarDecl())))),
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("v", 4)));
}
TEST_P(ASTMatchersTest,
ForEachArgumentWithParamMatchesExplicitObjectParamOnOperatorCalls) {
if (!GetParam().isCXX23OrLater()) {
return;
}
auto DeclRef = declRefExpr(to(varDecl().bind("declOfArg"))).bind("arg");
auto SelfParam = parmVarDecl().bind("param");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParam(DeclRef, SelfParam));
StringRef S = R"cpp(
struct A {
int operator()(this const A &self);
};
A obj;
int global = obj();
)cpp";
auto Args = GetParam().getCommandLineArgs();
auto Filename = getFilenameForTesting(GetParam().Language);
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr,
std::make_unique<VerifyIdIsBoundTo<ParmVarDecl>>("param", "self"), Args,
Filename));
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr,
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("declOfArg", "obj"), Args,
Filename));
}
TEST(ForEachArgumentWithParamType, ReportsNoFalsePositives) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
// IntParam does not match.
EXPECT_TRUE(notMatches("void f(int* i) { int* y; f(y); }", CallExpr));
// ArgumentY does not match.
EXPECT_TRUE(notMatches("void f(int i) { int x; f(x); }", CallExpr));
}
TEST(ForEachArgumentWithParamType, MatchesCXXMemberCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" const S& operator[](int i) { return *this; }"
"};"
"void f(S S1) {"
" int y = 1;"
" S1[y];"
"}",
CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 1)));
StatementMatcher CallExpr2 =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct S {"
" static void g(int i);"
"};"
"void f() {"
" int y = 1;"
" S::g(y);"
"}",
CallExpr2, std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 1)));
}
TEST(ForEachArgumentWithParamType, MatchesCallExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) { int y; f(y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<QualType>>("type", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i, int j) { int y; f(y, y); }", CallExpr,
std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg", 2)));
}
TEST(ForEachArgumentWithParamType, MatchesConstructExpr) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher ConstructExpr =
cxxConstructExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct C {"
" C(int i) {}"
"};"
"int y = 0;"
"C Obj(y);",
ConstructExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"struct C {"
" C(int i) {}"
"};"
"int y = 0;"
"C Obj(y);",
ConstructExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(ForEachArgumentWithParamType, HandlesKandRFunctions) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(isInteger()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchesC("void f();\n"
"void call_it(void) { int x, y; f(x, y); }\n"
"void f(a, b) int a, b; {}\n"
"void call_it2(void) { int x, y; f(x, y); }",
CallExpr));
}
TEST(ForEachArgumentWithParamType, HandlesBoundNodesForNonMatches) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"void g(int i, int j) {"
" int a;"
" int b;"
" int c;"
" g(a, 0);"
" g(a, b);"
" g(0, b);"
"}",
functionDecl(
forEachDescendant(varDecl().bind("v")),
forEachDescendant(callExpr(forEachArgumentWithParamType(
declRefExpr(to(decl(equalsBoundNode("v")))), qualType())))),
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("v", 4)));
}
TEST(ForEachArgumentWithParamType, MatchesFunctionPtrCalls) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(builtinType()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) {"
"void (*f_ptr)(int) = f; int y; f_ptr(y); }",
CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(int i) {"
"void (*f_ptr)(int) = f; int y; f_ptr(y); }",
CallExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(ForEachArgumentWithParamType, MatchesMemberFunctionPtrCalls) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(builtinType()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
StringRef S = "struct A {\n"
" int f(int i) { return i + 1; }\n"
" int (A::*x)(int);\n"
"};\n"
"void f() {\n"
" int y = 42;\n"
" A a;\n"
" a.x = &A::f;\n"
" (a.*(a.x))(y);\n"
"}";
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST(ForEachArgumentWithParamType, MatchesVariadicFunctionPtrCalls) {
StatementMatcher ArgumentY =
declRefExpr(to(varDecl(hasName("y")))).bind("arg");
TypeMatcher IntType = qualType(builtinType()).bind("type");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(ArgumentY, IntType));
StringRef S = R"cpp(
void fcntl(int fd, int cmd, ...) {}
template <typename Func>
void f(Func F) {
int y = 42;
F(y, 1, 3);
}
void g() { f(fcntl); }
)cpp";
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("type")));
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<DeclRefExpr>>("arg")));
}
TEST_P(ASTMatchersTest,
ForEachArgumentWithParamTypeMatchesExplicitObjectParamOnOperatorCalls) {
if (!GetParam().isCXX23OrLater()) {
return;
}
auto DeclRef = declRefExpr(to(varDecl().bind("declOfArg"))).bind("arg");
auto SelfTy = qualType(asString("const A &")).bind("selfType");
StatementMatcher CallExpr =
callExpr(forEachArgumentWithParamType(DeclRef, SelfTy));
StringRef S = R"cpp(
struct A {
int operator()(this const A &self);
};
A obj;
int global = obj();
)cpp";
auto Args = GetParam().getCommandLineArgs();
auto Filename = getFilenameForTesting(GetParam().Language);
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr, std::make_unique<VerifyIdIsBoundTo<QualType>>("selfType"),
Args, Filename));
EXPECT_TRUE(matchAndVerifyResultTrue(
S, CallExpr,
std::make_unique<VerifyIdIsBoundTo<VarDecl>>("declOfArg", "obj"), Args,
Filename));
}
TEST(QualType, hasCanonicalType) {
EXPECT_TRUE(notMatches("typedef int &int_ref;"
"int a;"
"int_ref b = a;",
varDecl(hasType(qualType(referenceType())))));
EXPECT_TRUE(
matches("typedef int &int_ref;"
"int a;"
"int_ref b = a;",
varDecl(hasType(qualType(hasCanonicalType(referenceType()))))));
}
TEST(HasParameter, CallsInnerMatcher) {
EXPECT_TRUE(matches("class X { void x(int) {} };",
cxxMethodDecl(hasParameter(0, varDecl()))));
EXPECT_TRUE(notMatches("class X { void x(int) {} };",
cxxMethodDecl(hasParameter(0, hasName("x")))));
EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) x; @end",
objcMethodDecl(hasParameter(0, hasName("x")))));
EXPECT_TRUE(matchesObjC("int main() { void (^b)(int) = ^(int p) {}; }",
blockDecl(hasParameter(0, hasName("p")))));
}
TEST(HasParameter, DoesNotMatchIfIndexOutOfBounds) {
EXPECT_TRUE(notMatches("class X { void x(int) {} };",
cxxMethodDecl(hasParameter(42, varDecl()))));
}
TEST(HasType, MatchesParameterVariableTypesStrictly) {
EXPECT_TRUE(matches(
"class X { void x(X x) {} };",
cxxMethodDecl(hasParameter(0, hasType(recordDecl(hasName("X")))))));
EXPECT_TRUE(notMatches(
"class X { void x(const X &x) {} };",
cxxMethodDecl(hasParameter(0, hasType(recordDecl(hasName("X")))))));
EXPECT_TRUE(matches("class X { void x(const X *x) {} };",
cxxMethodDecl(hasParameter(
0, hasType(pointsTo(recordDecl(hasName("X"))))))));
EXPECT_TRUE(matches("class X { void x(const X &x) {} };",
cxxMethodDecl(hasParameter(
0, hasType(references(recordDecl(hasName("X"))))))));
}
TEST(HasAnyParameter, MatchesIndependentlyOfPosition) {
EXPECT_TRUE(matches(
"class Y {}; class X { void x(X x, Y y) {} };",
cxxMethodDecl(hasAnyParameter(hasType(recordDecl(hasName("X")))))));
EXPECT_TRUE(matches(
"class Y {}; class X { void x(Y y, X x) {} };",
cxxMethodDecl(hasAnyParameter(hasType(recordDecl(hasName("X")))))));
EXPECT_TRUE(matchesObjC("@interface I -(void)f:(int) x; @end",
objcMethodDecl(hasAnyParameter(hasName("x")))));
EXPECT_TRUE(matchesObjC("int main() { void (^b)(int) = ^(int p) {}; }",
blockDecl(hasAnyParameter(hasName("p")))));
}
TEST(Returns, MatchesReturnTypes) {
EXPECT_TRUE(matches("class Y { int f() { return 1; } };",
functionDecl(returns(asString("int")))));
EXPECT_TRUE(notMatches("class Y { int f() { return 1; } };",
functionDecl(returns(asString("float")))));
EXPECT_TRUE(matches("class Y { Y getMe() { return *this; } };",
functionDecl(returns(hasDeclaration(
recordDecl(hasName("Y")))))));
}
TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) {
EXPECT_TRUE(notMatches(
"class Y {}; class X { void x(int) {} };",
cxxMethodDecl(hasAnyParameter(hasType(recordDecl(hasName("X")))))));
}
TEST(HasAnyParameter, DoesNotMatchThisPointer) {
EXPECT_TRUE(notMatches("class Y {}; class X { void x() {} };",
cxxMethodDecl(hasAnyParameter(
hasType(pointsTo(recordDecl(hasName("X"))))))));
}
TEST(HasName, MatchesParameterVariableDeclarations) {
EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };",
cxxMethodDecl(hasAnyParameter(hasName("x")))));
EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };",
cxxMethodDecl(hasAnyParameter(hasName("x")))));
}
TEST(Matcher, MatchesTypeTemplateArgument) {
EXPECT_TRUE(matches(
"template<typename T> struct B {};"
"B<int> b;",
classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType(
asString("int"))))));
}
TEST(Matcher, MatchesTemplateTemplateArgument) {
EXPECT_TRUE(matches("template<template <typename> class S> class X {};"
"template<typename T> class Y {};"
"X<Y> xi;",
classTemplateSpecializationDecl(hasAnyTemplateArgument(
refersToTemplate(templateName())))));
}
TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) {
EXPECT_TRUE(matches(
"struct B { int next; };"
"template<int(B::*next_ptr)> struct A {};"
"A<&B::next> a;",
classTemplateSpecializationDecl(hasAnyTemplateArgument(
refersToDeclaration(fieldDecl(hasName("next")))))));
EXPECT_TRUE(notMatches(
"template <typename T> struct A {};"
"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) {
EXPECT_TRUE(matches(
"template<typename T, typename U> class A {};"
"A<bool, int> a;",
classTemplateSpecializationDecl(hasTemplateArgument(
1, refersToType(asString("int"))))));
EXPECT_TRUE(notMatches(
"template<typename T, typename U> class A {};"
"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"))))));
EXPECT_TRUE(matches(
"template<typename T> void f() {};"
"void func() { f<int>(); }",
functionDecl(hasTemplateArgument(0, refersToType(asString("int"))))));
EXPECT_TRUE(notMatches(
"template<typename T> void f() {};",
functionDecl(hasTemplateArgument(0, refersToType(asString("int"))))));
}
TEST(TemplateArgument, Matches) {
EXPECT_TRUE(matches("template<typename T> struct C {}; C<int> c;",
classTemplateSpecializationDecl(
hasAnyTemplateArgument(templateArgument()))));
EXPECT_TRUE(matches(
"template<typename T> struct C {}; C<int> c;",
templateSpecializationType(hasAnyTemplateArgument(templateArgument()))));
EXPECT_TRUE(matches(
"template<typename T> void f() {};"
"void func() { f<int>(); }",
functionDecl(hasAnyTemplateArgument(templateArgument()))));
}
TEST(TemplateTypeParmDecl, CXXMethodDecl) {
const char input[] =
"template<typename T>\n"
"class Class {\n"
" void method();\n"
"};\n"
"template<typename U>\n"
"void Class<U>::method() {}\n";
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"))));
}
TEST(TemplateTypeParmDecl, VarDecl) {
const char input[] =
"template<typename T>\n"
"class Class {\n"
" static T pi;\n"
"};\n"
"template<typename U>\n"
"U Class<U>::pi = U(3.1415926535897932385);\n";
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"))));
}
TEST(TemplateTypeParmDecl, VarTemplatePartialSpecializationDecl) {
const char input[] =
"template<typename T>\n"
"struct Struct {\n"
" template<typename T2> static int field;\n"
"};\n"
"template<typename U>\n"
"template<typename U2>\n"
"int Struct<U>::field<U2*> = 123;\n";
EXPECT_TRUE(
matches(input, templateTypeParmDecl(hasName("T")), langCxx14OrLater()));
EXPECT_TRUE(
matches(input, templateTypeParmDecl(hasName("T2")), langCxx14OrLater()));
EXPECT_TRUE(
matches(input, templateTypeParmDecl(hasName("U")), langCxx14OrLater()));
EXPECT_TRUE(
matches(input, templateTypeParmDecl(hasName("U2")), langCxx14OrLater()));
}
TEST(TemplateTypeParmDecl, ClassTemplatePartialSpecializationDecl) {
const char input[] =
"template<typename T>\n"
"class Class {\n"
" template<typename T2> struct Struct;\n"
"};\n"
"template<typename U>\n"
"template<typename U2>\n"
"struct Class<U>::Struct<U2*> {};\n";
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T2"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U2"))));
}
TEST(TemplateTypeParmDecl, EnumDecl) {
const char input[] =
"template<typename T>\n"
"struct Struct {\n"
" enum class Enum : T;\n"
"};\n"
"template<typename U>\n"
"enum class Struct<U>::Enum : U {\n"
" e1,\n"
" e2\n"
"};\n";
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"))));
}
TEST(TemplateTypeParmDecl, RecordDecl) {
const char input[] =
"template<typename T>\n"
"class Class {\n"
" struct Struct;\n"
"};\n"
"template<typename U>\n"
"struct Class<U>::Struct {\n"
" U field;\n"
"};\n";
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("T"))));
EXPECT_TRUE(matches(input, templateTypeParmDecl(hasName("U"))));
}
TEST(RefersToIntegralType, Matches) {
EXPECT_TRUE(matches("template<int T> struct C {}; C<42> c;",
classTemplateSpecializationDecl(
hasAnyTemplateArgument(refersToIntegralType(
asString("int"))))));
EXPECT_TRUE(notMatches("template<unsigned T> struct C {}; C<42> c;",
classTemplateSpecializationDecl(hasAnyTemplateArgument(
refersToIntegralType(asString("int"))))));
}
TEST(ConstructorDeclaration, SimpleCase) {
EXPECT_TRUE(matches("class Foo { Foo(int i); };",
cxxConstructorDecl(ofClass(hasName("Foo")))));
EXPECT_TRUE(notMatches("class Foo { Foo(int i); };",
cxxConstructorDecl(ofClass(hasName("Bar")))));
}
TEST(DestructorDeclaration, MatchesVirtualDestructor) {
EXPECT_TRUE(matches("class Foo { virtual ~Foo(); };",
cxxDestructorDecl(ofClass(hasName("Foo")))));
}
TEST(DestructorDeclaration, DoesNotMatchImplicitDestructor) {
EXPECT_TRUE(notMatches("class Foo {};",
cxxDestructorDecl(ofClass(hasName("Foo")))));
}
TEST(HasAnyConstructorInitializer, SimpleCase) {
EXPECT_TRUE(
notMatches("class Foo { Foo() { } };",
cxxConstructorDecl(hasAnyConstructorInitializer(anything()))));
EXPECT_TRUE(
matches("class Foo {"
" Foo() : foo_() { }"
" int foo_;"
"};",
cxxConstructorDecl(hasAnyConstructorInitializer(anything()))));
}
TEST(HasAnyConstructorInitializer, ForField) {
static const char Code[] =
"class Baz { };"
"class Foo {"
" Foo() : foo_(), bar_() { }"
" Baz foo_;"
" struct {"
" Baz bar_;"
" };"
"};";
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
forField(hasType(recordDecl(hasName("Baz"))))))));
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
forField(hasName("foo_"))))));
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
forField(hasName("bar_"))))));
EXPECT_TRUE(notMatches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
forField(hasType(recordDecl(hasName("Bar"))))))));
}
TEST(HasAnyConstructorInitializer, WithInitializer) {
static const char Code[] =
"class Foo {"
" Foo() : foo_(0) { }"
" int foo_;"
"};";
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
withInitializer(integerLiteral(equals(0)))))));
EXPECT_TRUE(notMatches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
withInitializer(integerLiteral(equals(1)))))));
}
TEST(HasAnyConstructorInitializer, IsWritten) {
static const char Code[] =
"struct Bar { Bar(){} };"
"class Foo {"
" Foo() : foo_() { }"
" Bar foo_;"
" Bar bar_;"
"};";
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
allOf(forField(hasName("foo_")), isWritten())))));
EXPECT_TRUE(notMatches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
allOf(forField(hasName("bar_")), isWritten())))));
EXPECT_TRUE(matches(Code, cxxConstructorDecl(hasAnyConstructorInitializer(
allOf(forField(hasName("bar_")), unless(isWritten()))))));
}
TEST(HasAnyConstructorInitializer, IsBaseInitializer) {
static const char Code[] =
"struct B {};"
"struct D : B {"
" int I;"
" D(int i) : I(i) {}"
"};"
"struct E : B {"
" E() : B() {}"
"};";
EXPECT_TRUE(matches(Code, cxxConstructorDecl(allOf(
hasAnyConstructorInitializer(allOf(isBaseInitializer(), isWritten())),
hasName("E")))));
EXPECT_TRUE(notMatches(Code, cxxConstructorDecl(allOf(
hasAnyConstructorInitializer(allOf(isBaseInitializer(), isWritten())),
hasName("D")))));
EXPECT_TRUE(matches(Code, cxxConstructorDecl(allOf(
hasAnyConstructorInitializer(allOf(isMemberInitializer(), isWritten())),
hasName("D")))));
EXPECT_TRUE(notMatches(Code, cxxConstructorDecl(allOf(
hasAnyConstructorInitializer(allOf(isMemberInitializer(), isWritten())),
hasName("E")))));
}
TEST(IfStmt, ChildTraversalMatchers) {
EXPECT_TRUE(matches("void f() { if (false) true; else false; }",
ifStmt(hasThen(cxxBoolLiteral(equals(true))))));
EXPECT_TRUE(notMatches("void f() { if (false) false; else true; }",
ifStmt(hasThen(cxxBoolLiteral(equals(true))))));
EXPECT_TRUE(matches("void f() { if (false) false; else true; }",
ifStmt(hasElse(cxxBoolLiteral(equals(true))))));
EXPECT_TRUE(notMatches("void f() { if (false) true; else false; }",
ifStmt(hasElse(cxxBoolLiteral(equals(true))))));
}
TEST(MatchBinaryOperator, HasOperatorName) {
StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||"));
EXPECT_TRUE(matches("void x() { true || false; }", OperatorOr));
EXPECT_TRUE(notMatches("void x() { true && false; }", OperatorOr));
}
TEST(MatchBinaryOperator, HasAnyOperatorName) {
StatementMatcher Matcher =
binaryOperator(hasAnyOperatorName("+", "-", "*", "/"));
EXPECT_TRUE(matches("int x(int I) { return I + 2; }", Matcher));
EXPECT_TRUE(matches("int x(int I) { return I - 2; }", Matcher));
EXPECT_TRUE(matches("int x(int I) { return I * 2; }", Matcher));
EXPECT_TRUE(matches("int x(int I) { return I / 2; }", Matcher));
EXPECT_TRUE(notMatches("int x(int I) { return I % 2; }", Matcher));
// Ensure '+= isn't mistaken.
EXPECT_TRUE(notMatches("void x(int &I) { I += 1; }", Matcher));
}
TEST(MatchBinaryOperator, HasLHSAndHasRHS) {
StatementMatcher OperatorTrueFalse =
binaryOperator(hasLHS(cxxBoolLiteral(equals(true))),
hasRHS(cxxBoolLiteral(equals(false))));
EXPECT_TRUE(matches("void x() { true || false; }", OperatorTrueFalse));
EXPECT_TRUE(matches("void x() { true && false; }", OperatorTrueFalse));
EXPECT_TRUE(notMatches("void x() { false || true; }", OperatorTrueFalse));
StatementMatcher OperatorIntPointer = arraySubscriptExpr(
hasLHS(hasType(isInteger())),
traverse(TK_AsIs, hasRHS(hasType(pointsTo(qualType())))));
EXPECT_TRUE(matches("void x() { 1[\"abc\"]; }", OperatorIntPointer));
EXPECT_TRUE(notMatches("void x() { \"abc\"[1]; }", OperatorIntPointer));
StringRef Code = R"cpp(
struct HasOpEqMem
{
bool operator==(const HasOpEqMem& other) const
{
return true;
}
};
struct HasOpFree
{
};
bool operator==(const HasOpFree& lhs, const HasOpFree& rhs)
{
return true;
}
void opMem()
{
HasOpEqMem s1;
HasOpEqMem s2;
if (s1 == s2)
return;
}
void opFree()
{
HasOpFree s1;
HasOpFree s2;
if (s1 == s2)
return;
}
)cpp";
auto s1Expr = declRefExpr(to(varDecl(hasName("s1"))));
auto s2Expr = declRefExpr(to(varDecl(hasName("s2"))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))),
hasOperatorName("=="), hasLHS(s1Expr),
hasRHS(s2Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opMem"))),
hasAnyOperatorName("!=", "=="), hasLHS(s1Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opMem"))),
hasOperatorName("=="), hasOperands(s1Expr, s2Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opMem"))),
hasOperatorName("=="), hasEitherOperand(s2Expr)))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))),
hasOperatorName("=="), hasLHS(s1Expr),
hasRHS(s2Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opFree"))),
hasAnyOperatorName("!=", "=="), hasLHS(s1Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opFree"))),
hasOperatorName("=="), hasOperands(s1Expr, s2Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opFree"))),
hasOperatorName("=="), hasEitherOperand(s2Expr)))));
}
TEST(MatchBinaryOperator, HasEitherOperand) {
StatementMatcher HasOperand =
binaryOperator(hasEitherOperand(cxxBoolLiteral(equals(false))));
EXPECT_TRUE(matches("void x() { true || false; }", HasOperand));
EXPECT_TRUE(matches("void x() { false && true; }", HasOperand));
EXPECT_TRUE(notMatches("void x() { true || true; }", HasOperand));
}
TEST(MatchBinaryOperator, HasOperands) {
StatementMatcher HasOperands = binaryOperator(
hasOperands(integerLiteral(equals(1)), integerLiteral(equals(2))));
EXPECT_TRUE(matches("void x() { 1 + 2; }", HasOperands));
EXPECT_TRUE(matches("void x() { 2 + 1; }", HasOperands));
EXPECT_TRUE(notMatches("void x() { 1 + 1; }", HasOperands));
EXPECT_TRUE(notMatches("void x() { 2 + 2; }", HasOperands));
EXPECT_TRUE(notMatches("void x() { 0 + 0; }", HasOperands));
EXPECT_TRUE(notMatches("void x() { 0 + 1; }", HasOperands));
}
TEST(MatchBinaryOperator, HasOperandsEnsureOrdering) {
StatementMatcher HasOperandsWithBindings = binaryOperator(hasOperands(
cStyleCastExpr(has(declRefExpr(hasDeclaration(valueDecl().bind("d"))))),
declRefExpr(hasDeclaration(valueDecl(equalsBoundNode("d"))))));
EXPECT_TRUE(matches(
"int a; int b = ((int) a) + a;",
traverse(TK_IgnoreUnlessSpelledInSource, HasOperandsWithBindings)));
EXPECT_TRUE(matches(
"int a; int b = a + ((int) a);",
traverse(TK_IgnoreUnlessSpelledInSource, HasOperandsWithBindings)));
}
TEST(Matcher, BinaryOperatorTypes) {
// Integration test that verifies the AST provides all binary operators in
// a way we expect.
// FIXME: Operator ','
EXPECT_TRUE(
matches("void x() { 3, 4; }", binaryOperator(hasOperatorName(","))));
EXPECT_TRUE(
matches("bool b; bool c = (b = true);",
binaryOperator(hasOperatorName("="))));
EXPECT_TRUE(
matches("bool b = 1 != 2;", binaryOperator(hasOperatorName("!="))));
EXPECT_TRUE(
matches("bool b = 1 == 2;", binaryOperator(hasOperatorName("=="))));
EXPECT_TRUE(matches("bool b = 1 < 2;", binaryOperator(hasOperatorName("<"))));
EXPECT_TRUE(
matches("bool b = 1 <= 2;", binaryOperator(hasOperatorName("<="))));
EXPECT_TRUE(
matches("int i = 1 << 2;", binaryOperator(hasOperatorName("<<"))));
EXPECT_TRUE(
matches("int i = 1; int j = (i <<= 2);",
binaryOperator(hasOperatorName("<<="))));
EXPECT_TRUE(matches("bool b = 1 > 2;", binaryOperator(hasOperatorName(">"))));
EXPECT_TRUE(
matches("bool b = 1 >= 2;", binaryOperator(hasOperatorName(">="))));
EXPECT_TRUE(
matches("int i = 1 >> 2;", binaryOperator(hasOperatorName(">>"))));
EXPECT_TRUE(
matches("int i = 1; int j = (i >>= 2);",
binaryOperator(hasOperatorName(">>="))));
EXPECT_TRUE(
matches("int i = 42 ^ 23;", binaryOperator(hasOperatorName("^"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i ^= 42);",
binaryOperator(hasOperatorName("^="))));
EXPECT_TRUE(
matches("int i = 42 % 23;", binaryOperator(hasOperatorName("%"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i %= 42);",
binaryOperator(hasOperatorName("%="))));
EXPECT_TRUE(
matches("bool b = 42 &23;", binaryOperator(hasOperatorName("&"))));
EXPECT_TRUE(
matches("bool b = true && false;",
binaryOperator(hasOperatorName("&&"))));
EXPECT_TRUE(
matches("bool b = true; bool c = (b &= false);",
binaryOperator(hasOperatorName("&="))));
EXPECT_TRUE(
matches("bool b = 42 | 23;", binaryOperator(hasOperatorName("|"))));
EXPECT_TRUE(
matches("bool b = true || false;",
binaryOperator(hasOperatorName("||"))));
EXPECT_TRUE(
matches("bool b = true; bool c = (b |= false);",
binaryOperator(hasOperatorName("|="))));
EXPECT_TRUE(
matches("int i = 42 *23;", binaryOperator(hasOperatorName("*"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i *= 23);",
binaryOperator(hasOperatorName("*="))));
EXPECT_TRUE(
matches("int i = 42 / 23;", binaryOperator(hasOperatorName("/"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i /= 23);",
binaryOperator(hasOperatorName("/="))));
EXPECT_TRUE(
matches("int i = 42 + 23;", binaryOperator(hasOperatorName("+"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i += 23);",
binaryOperator(hasOperatorName("+="))));
EXPECT_TRUE(
matches("int i = 42 - 23;", binaryOperator(hasOperatorName("-"))));
EXPECT_TRUE(
matches("int i = 42; int j = (i -= 23);",
binaryOperator(hasOperatorName("-="))));
EXPECT_TRUE(
matches("struct A { void x() { void (A::*a)(); (this->*a)(); } };",
binaryOperator(hasOperatorName("->*"))));
EXPECT_TRUE(
matches("struct A { void x() { void (A::*a)(); ((*this).*a)(); } };",
binaryOperator(hasOperatorName(".*"))));
// Member expressions as operators are not supported in matches.
EXPECT_TRUE(
notMatches("struct A { void x(A *a) { a->x(this); } };",
binaryOperator(hasOperatorName("->"))));
// Initializer assignments are not represented as operator equals.
EXPECT_TRUE(
notMatches("bool b = true;", binaryOperator(hasOperatorName("="))));
// Array indexing is not represented as operator.
EXPECT_TRUE(notMatches("int a[42]; void x() { a[23]; }", unaryOperator()));
// Overloaded operators do not match at all.
EXPECT_TRUE(notMatches(
"struct A { bool operator&&(const A &a) const { return false; } };"
"void x() { A a, b; a && b; }",
binaryOperator()));
}
TEST(MatchUnaryOperator, HasOperatorName) {
StatementMatcher OperatorNot = unaryOperator(hasOperatorName("!"));
EXPECT_TRUE(matches("void x() { !true; } ", OperatorNot));
EXPECT_TRUE(notMatches("void x() { true; } ", OperatorNot));
}
TEST(MatchUnaryOperator, HasAnyOperatorName) {
StatementMatcher Matcher = unaryOperator(hasAnyOperatorName("-", "*", "++"));
EXPECT_TRUE(matches("int x(int *I) { return *I; }", Matcher));
EXPECT_TRUE(matches("int x(int I) { return -I; }", Matcher));
EXPECT_TRUE(matches("void x(int &I) { I++; }", Matcher));
EXPECT_TRUE(matches("void x(int &I) { ++I; }", Matcher));
EXPECT_TRUE(notMatches("void x(int &I) { I--; }", Matcher));
EXPECT_TRUE(notMatches("void x(int &I) { --I; }", Matcher));
EXPECT_TRUE(notMatches("int *x(int &I) { return &I; }", Matcher));
}
TEST(MatchUnaryOperator, HasUnaryOperand) {
StatementMatcher OperatorOnFalse =
unaryOperator(hasUnaryOperand(cxxBoolLiteral(equals(false))));
EXPECT_TRUE(matches("void x() { !false; }", OperatorOnFalse));
EXPECT_TRUE(notMatches("void x() { !true; }", OperatorOnFalse));
StringRef Code = R"cpp(
struct HasOpBangMem
{
bool operator!() const
{
return false;
}
};
struct HasOpBangFree
{
};
bool operator!(HasOpBangFree const&)
{
return false;
}
void opMem()
{
HasOpBangMem s1;
if (!s1)
return;
}
void opFree()
{
HasOpBangFree s1;
if (!s1)
return;
}
)cpp";
auto s1Expr = declRefExpr(to(varDecl(hasName("s1"))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opMem"))),
hasOperatorName("!"), hasUnaryOperand(s1Expr)))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(forFunction(functionDecl(hasName("opMem"))),
hasAnyOperatorName("+", "!"),
hasUnaryOperand(s1Expr)))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("opFree"))),
hasOperatorName("!"), hasUnaryOperand(s1Expr)))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(forFunction(functionDecl(hasName("opFree"))),
hasAnyOperatorName("+", "!"),
hasUnaryOperand(s1Expr)))));
Code = R"cpp(
struct HasIncOperatorsMem
{
HasIncOperatorsMem& operator++();
HasIncOperatorsMem operator++(int);
};
struct HasIncOperatorsFree
{
};
HasIncOperatorsFree& operator++(HasIncOperatorsFree&);
HasIncOperatorsFree operator++(HasIncOperatorsFree&, int);
void prefixIncOperatorMem()
{
HasIncOperatorsMem s1;
++s1;
}
void prefixIncOperatorFree()
{
HasIncOperatorsFree s1;
++s1;
}
void postfixIncOperatorMem()
{
HasIncOperatorsMem s1;
s1++;
}
void postfixIncOperatorFree()
{
HasIncOperatorsFree s1;
s1++;
}
struct HasOpPlusInt
{
HasOpPlusInt& operator+(int);
};
void plusIntOperator()
{
HasOpPlusInt s1;
s1+1;
}
)cpp";
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("prefixIncOperatorMem"))),
hasOperatorName("++"), hasUnaryOperand(declRefExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("prefixIncOperatorFree"))),
hasOperatorName("++"), hasUnaryOperand(declRefExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("postfixIncOperatorMem"))),
hasOperatorName("++"), hasUnaryOperand(declRefExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("postfixIncOperatorFree"))),
hasOperatorName("++"), hasUnaryOperand(declRefExpr())))));
EXPECT_FALSE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(
forFunction(functionDecl(hasName("plusIntOperator"))),
hasOperatorName("+"), hasUnaryOperand(expr())))));
Code = R"cpp(
struct HasOpArrow
{
int& operator*();
};
void foo()
{
HasOpArrow s1;
*s1;
}
)cpp";
EXPECT_TRUE(
matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
cxxOperatorCallExpr(hasOperatorName("*"),
hasUnaryOperand(expr())))));
}
TEST(Matcher, UnaryOperatorTypes) {
// Integration test that verifies the AST provides all unary operators in
// a way we expect.
EXPECT_TRUE(matches("bool b = !true;", unaryOperator(hasOperatorName("!"))));
EXPECT_TRUE(
matches("bool b; bool *p = &b;", unaryOperator(hasOperatorName("&"))));
EXPECT_TRUE(matches("int i = ~ 1;", unaryOperator(hasOperatorName("~"))));
EXPECT_TRUE(
matches("bool *p; bool b = *p;", unaryOperator(hasOperatorName("*"))));
EXPECT_TRUE(
matches("int i; int j = +i;", unaryOperator(hasOperatorName("+"))));
EXPECT_TRUE(
matches("int i; int j = -i;", unaryOperator(hasOperatorName("-"))));
EXPECT_TRUE(
matches("int i; int j = ++i;", unaryOperator(hasOperatorName("++"))));
EXPECT_TRUE(
matches("int i; int j = i++;", unaryOperator(hasOperatorName("++"))));
EXPECT_TRUE(
matches("int i; int j = --i;", unaryOperator(hasOperatorName("--"))));
EXPECT_TRUE(
matches("int i; int j = i--;", unaryOperator(hasOperatorName("--"))));
// We don't match conversion operators.
EXPECT_TRUE(notMatches("int i; double d = (double)i;", unaryOperator()));
// Function calls are not represented as operator.
EXPECT_TRUE(notMatches("void f(); void x() { f(); }", unaryOperator()));
// Overloaded operators do not match at all.
// FIXME: We probably want to add that.
EXPECT_TRUE(notMatches(
"struct A { bool operator!() const { return false; } };"
"void x() { A a; !a; }", unaryOperator(hasOperatorName("!"))));
}
TEST_P(ASTMatchersTest, HasInit) {
if (!GetParam().isCXX11OrLater()) {
// FIXME: Add a test for `hasInit()` that does not depend on C++.
return;
}
EXPECT_TRUE(matches("int x{0};", initListExpr(hasInit(0, expr()))));
EXPECT_FALSE(matches("int x{0};", initListExpr(hasInit(1, expr()))));
EXPECT_FALSE(matches("int x;", initListExpr(hasInit(0, expr()))));
}
TEST_P(ASTMatchersTest, HasFoldInit) {
if (!GetParam().isCXX17OrLater()) {
return;
}
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasFoldInit(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasFoldInit(expr()))));
EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasFoldInit(expr()))));
}
TEST_P(ASTMatchersTest, HasPattern) {
if (!GetParam().isCXX17OrLater()) {
return;
}
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasPattern(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasPattern(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasPattern(expr()))));
}
TEST_P(ASTMatchersTest, HasLHSAndHasRHS) {
if (!GetParam().isCXX17OrLater()) {
return;
}
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasLHS(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasLHS(expr()))));
EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasLHS(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ...); };",
cxxFoldExpr(hasLHS(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasRHS(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasRHS(expr()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasRHS(expr()))));
EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ...); };",
cxxFoldExpr(hasRHS(expr()))));
}
TEST_P(ASTMatchersTest, HasEitherOperandAndHasOperands) {
if (!GetParam().isCXX17OrLater()) {
return;
}
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasEitherOperand(integerLiteral()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasEitherOperand(integerLiteral()))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasEitherOperand(
declRefExpr(to(namedDecl(hasName("args"))))))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasEitherOperand(
declRefExpr(to(namedDecl(hasName("args"))))))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasEitherOperand(
declRefExpr(to(namedDecl(hasName("args"))))))));
EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
"return (args + ...); };",
cxxFoldExpr(hasEitherOperand(
declRefExpr(to(namedDecl(hasName("args"))))))));
EXPECT_TRUE(matches(
"template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(hasOperands(declRefExpr(to(namedDecl(hasName("args")))),
integerLiteral()))));
EXPECT_TRUE(matches(
"template <typename... Args> auto sum(Args... args) { "
"return (args + ... + 0); }",
cxxFoldExpr(hasOperands(declRefExpr(to(namedDecl(hasName("args")))),
integerLiteral()))));
EXPECT_FALSE(matches(
"template <typename... Args> auto sum(Args... args) { "
"return (... + args); };",
cxxFoldExpr(hasOperands(declRefExpr(to(namedDecl(hasName("args")))),
integerLiteral()))));
EXPECT_FALSE(matches(
"template <typename... Args> auto sum(Args... args) { "
"return (args + ...); };",
cxxFoldExpr(hasOperands(declRefExpr(to(namedDecl(hasName("args")))),
integerLiteral()))));
}
TEST_P(ASTMatchersTest, Callee) {
if (!GetParam().isCXX17OrLater()) {
return;
}
EXPECT_TRUE(matches(
"struct Dummy {}; Dummy operator+(Dummy, Dummy); template "
"<typename... Args> auto sum(Args... args) { return (0 + ... + args); }",
cxxFoldExpr(callee(expr()))));
EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
"return (0 + ... + args); }",
cxxFoldExpr(callee(expr()))));
}
TEST(ArraySubscriptMatchers, ArrayIndex) {
EXPECT_TRUE(matches(
"int i[2]; void f() { i[1] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
EXPECT_TRUE(matches(
"int i[2]; void f() { 1[i] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(1))))));
EXPECT_TRUE(notMatches(
"int i[2]; void f() { i[1] = 1; }",
arraySubscriptExpr(hasIndex(integerLiteral(equals(0))))));
}
TEST(ArraySubscriptMatchers, MatchesArrayBase) {
EXPECT_TRUE(
matches("int i[2]; void f() { i[1] = 2; }",
traverse(TK_AsIs, arraySubscriptExpr(hasBase(implicitCastExpr(
hasSourceExpression(declRefExpr())))))));
}
TEST(Matcher, OfClass) {
StatementMatcher Constructor = cxxConstructExpr(hasDeclaration(cxxMethodDecl(
ofClass(hasName("X")))));
EXPECT_TRUE(
matches("class X { public: X(); }; void x(int) { X x; }", Constructor));
EXPECT_TRUE(
matches("class X { public: X(); }; void x(int) { X x = X(); }",
Constructor));
EXPECT_TRUE(
notMatches("class Y { public: Y(); }; void x(int) { Y y; }",
Constructor));
}
TEST(Matcher, VisitsTemplateInstantiations) {
EXPECT_TRUE(matches(
"class A { public: void x(); };"
"template <typename T> class B { public: void y() { T t; t.x(); } };"
"void f() { B<A> b; b.y(); }",
callExpr(callee(cxxMethodDecl(hasName("x"))))));
EXPECT_TRUE(matches(
"class A { public: void x(); };"
"class C {"
" public:"
" template <typename T> class B { public: void y() { T t; t.x(); } };"
"};"
"void f() {"
" C::B<A> b; b.y();"
"}",
recordDecl(hasName("C"), hasDescendant(callExpr(
callee(cxxMethodDecl(hasName("x"))))))));
}
TEST(Matcher, HasCondition) {
StatementMatcher IfStmt =
ifStmt(hasCondition(cxxBoolLiteral(equals(true))));
EXPECT_TRUE(matches("void x() { if (true) {} }", IfStmt));
EXPECT_TRUE(notMatches("void x() { if (false) {} }", IfStmt));
StatementMatcher ForStmt =
forStmt(hasCondition(cxxBoolLiteral(equals(true))));
EXPECT_TRUE(matches("void x() { for (;true;) {} }", ForStmt));
EXPECT_TRUE(notMatches("void x() { for (;false;) {} }", ForStmt));
StatementMatcher WhileStmt =
whileStmt(hasCondition(cxxBoolLiteral(equals(true))));
EXPECT_TRUE(matches("void x() { while (true) {} }", WhileStmt));
EXPECT_TRUE(notMatches("void x() { while (false) {} }", WhileStmt));
StatementMatcher SwitchStmt =
switchStmt(hasCondition(integerLiteral(equals(42))));
EXPECT_TRUE(matches("void x() { switch (42) {case 42:;} }", SwitchStmt));
EXPECT_TRUE(notMatches("void x() { switch (43) {case 43:;} }", SwitchStmt));
}
TEST(For, ForLoopInternals) {
EXPECT_TRUE(matches("void f(){ int i; for (; i < 3 ; ); }",
forStmt(hasCondition(anything()))));
EXPECT_TRUE(matches("void f() { for (int i = 0; ;); }",
forStmt(hasLoopInit(anything()))));
}
TEST(For, ForRangeLoopInternals) {
EXPECT_TRUE(matches("void f(){ int a[] {1, 2}; for (int i : a); }",
cxxForRangeStmt(hasLoopVariable(anything()))));
EXPECT_TRUE(matches(
"void f(){ int a[] {1, 2}; for (int i : a); }",
cxxForRangeStmt(hasRangeInit(declRefExpr(to(varDecl(hasName("a"))))))));
}
TEST(For, NegativeForLoopInternals) {
EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }",
forStmt(hasCondition(expr()))));
EXPECT_TRUE(notMatches("void f() {int i; for (; i < 4; ++i) {} }",
forStmt(hasLoopInit(anything()))));
}
TEST(HasBody, FindsBodyOfForWhileDoLoops) {
EXPECT_TRUE(matches("void f() { for(;;) {} }",
forStmt(hasBody(compoundStmt()))));
EXPECT_TRUE(notMatches("void f() { for(;;); }",
forStmt(hasBody(compoundStmt()))));
EXPECT_TRUE(matches("void f() { while(true) {} }",
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) {} }",
cxxForRangeStmt(hasBody(compoundStmt()))));
}
TEST(HasBody, FindsBodyOfFunctions) {
EXPECT_TRUE(matches("void f() {}", functionDecl(hasBody(compoundStmt()))));
EXPECT_TRUE(notMatches("void f();", functionDecl(hasBody(compoundStmt()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(); void f() {}",
functionDecl(hasBody(compoundStmt())).bind("func"),
std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("func", 1)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { void f(); }; void C::f() {}",
cxxMethodDecl(hasBody(compoundStmt())).bind("met"),
std::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("met", 1)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { C(); }; C::C() {}",
cxxConstructorDecl(hasBody(compoundStmt())).bind("ctr"),
std::make_unique<VerifyIdIsBoundTo<CXXConstructorDecl>>("ctr", 1)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { ~C(); }; C::~C() {}",
cxxDestructorDecl(hasBody(compoundStmt())).bind("dtr"),
std::make_unique<VerifyIdIsBoundTo<CXXDestructorDecl>>("dtr", 1)));
}
TEST(HasAnyBody, FindsAnyBodyOfFunctions) {
EXPECT_TRUE(matches("void f() {}", functionDecl(hasAnyBody(compoundStmt()))));
EXPECT_TRUE(notMatches("void f();",
functionDecl(hasAnyBody(compoundStmt()))));
EXPECT_TRUE(matchAndVerifyResultTrue(
"void f(); void f() {}",
functionDecl(hasAnyBody(compoundStmt())).bind("func"),
std::make_unique<VerifyIdIsBoundTo<FunctionDecl>>("func", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { void f(); }; void C::f() {}",
cxxMethodDecl(hasAnyBody(compoundStmt())).bind("met"),
std::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("met", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { C(); }; C::C() {}",
cxxConstructorDecl(hasAnyBody(compoundStmt())).bind("ctr"),
std::make_unique<VerifyIdIsBoundTo<CXXConstructorDecl>>("ctr", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { ~C(); }; C::~C() {}",
cxxDestructorDecl(hasAnyBody(compoundStmt())).bind("dtr"),
std::make_unique<VerifyIdIsBoundTo<CXXDestructorDecl>>("dtr", 2)));
}
TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) {
// The simplest case: every compound statement is in a function
// definition, and the function body itself must be a compound
// statement.
EXPECT_TRUE(matches("void f() { for (;;); }",
compoundStmt(hasAnySubstatement(forStmt()))));
}
TEST(HasAnySubstatement, IsNotRecursive) {
// It's really "has any immediate substatement".
EXPECT_TRUE(notMatches("void f() { if (true) for (;;); }",
compoundStmt(hasAnySubstatement(forStmt()))));
}
TEST(HasAnySubstatement, MatchesInNestedCompoundStatements) {
EXPECT_TRUE(matches("void f() { if (true) { for (;;); } }",
compoundStmt(hasAnySubstatement(forStmt()))));
}
TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) {
EXPECT_TRUE(matches("void f() { 1; 2; 3; for (;;); 4; 5; 6; }",
compoundStmt(hasAnySubstatement(forStmt()))));
}
TEST(Member, MatchesMemberAllocationFunction) {
// Fails in C++11 mode
EXPECT_TRUE(matchesConditionally(
"namespace std { typedef typeof(sizeof(int)) size_t; }"
"class X { void *operator new(std::size_t); };",
cxxMethodDecl(ofClass(hasName("X"))), true, {"-std=gnu++03"}));
EXPECT_TRUE(matches("class X { void operator delete(void*); };",
cxxMethodDecl(ofClass(hasName("X")))));
// Fails in C++11 mode
EXPECT_TRUE(matchesConditionally(
"namespace std { typedef typeof(sizeof(int)) size_t; }"
"class X { void operator delete[](void*, std::size_t); };",
cxxMethodDecl(ofClass(hasName("X"))), true, {"-std=gnu++03"}));
}
TEST(HasDestinationType, MatchesSimpleCase) {
EXPECT_TRUE(matches("char* p = static_cast<char*>(0);",
cxxStaticCastExpr(hasDestinationType(
pointsTo(TypeMatcher(anything()))))));
}
TEST(HasImplicitDestinationType, MatchesSimpleCase) {
// This test creates an implicit const cast.
EXPECT_TRUE(matches(
"int x; const int i = x;",
traverse(TK_AsIs,
implicitCastExpr(hasImplicitDestinationType(isInteger())))));
// This test creates an implicit array-to-pointer cast.
EXPECT_TRUE(
matches("int arr[3]; int *p = arr;",
traverse(TK_AsIs, implicitCastExpr(hasImplicitDestinationType(
pointsTo(TypeMatcher(anything())))))));
}
TEST(HasImplicitDestinationType, DoesNotMatchIncorrectly) {
// This test creates an implicit cast from int to char.
EXPECT_TRUE(notMatches("char c = 0;",
implicitCastExpr(hasImplicitDestinationType(
unless(anything())))));
// This test creates an implicit array-to-pointer cast.
EXPECT_TRUE(notMatches("int arr[3]; int *p = arr;",
implicitCastExpr(hasImplicitDestinationType(
unless(anything())))));
}
TEST(Matcher, IgnoresElidableConstructors) {
EXPECT_TRUE(
matches("struct H {};"
"template<typename T> H B(T A);"
"void f() {"
" H D1;"
" D1 = B(B(1));"
"}",
cxxOperatorCallExpr(hasArgument(
1, callExpr(hasArgument(
0, ignoringElidableConstructorCall(callExpr()))))),
langCxx11OrLater()));
EXPECT_TRUE(
matches("struct H {};"
"template<typename T> H B(T A);"
"void f() {"
" H D1;"
" D1 = B(1);"
"}",
cxxOperatorCallExpr(hasArgument(
1, callExpr(hasArgument(0, ignoringElidableConstructorCall(
integerLiteral()))))),
langCxx11OrLater()));
EXPECT_TRUE(matches(
"struct H {};"
"H G();"
"void f() {"
" H D = G();"
"}",
varDecl(hasInitializer(anyOf(
ignoringElidableConstructorCall(callExpr()),
exprWithCleanups(has(ignoringElidableConstructorCall(callExpr())))))),
langCxx11OrLater()));
}
TEST(Matcher, IgnoresElidableInReturn) {
auto matcher = expr(ignoringElidableConstructorCall(declRefExpr()));
EXPECT_TRUE(matches("struct H {};"
"H f() {"
" H g;"
" return g;"
"}",
matcher, langCxx11OrLater()));
EXPECT_TRUE(notMatches("struct H {};"
"H f() {"
" return H();"
"}",
matcher, langCxx11OrLater()));
}
TEST(Matcher, IgnoreElidableConstructorDoesNotMatchConstructors) {
EXPECT_TRUE(matches("struct H {};"
"void f() {"
" H D;"
"}",
varDecl(hasInitializer(
ignoringElidableConstructorCall(cxxConstructExpr()))),
langCxx11OrLater()));
}
TEST(Matcher, IgnoresElidableDoesNotPreventMatches) {
EXPECT_TRUE(matches("void f() {"
" int D = 10;"
"}",
expr(ignoringElidableConstructorCall(integerLiteral())),
langCxx11OrLater()));
}
TEST(Matcher, IgnoresElidableInVarInit) {
auto matcher =
varDecl(hasInitializer(ignoringElidableConstructorCall(callExpr())));
EXPECT_TRUE(matches("struct H {};"
"H G();"
"void f(H D = G()) {"
" return;"
"}",
matcher, langCxx11OrLater()));
EXPECT_TRUE(matches("struct H {};"
"H G();"
"void f() {"
" H D = G();"
"}",
matcher, langCxx11OrLater()));
}
TEST(IgnoringImplicit, MatchesImplicit) {
EXPECT_TRUE(matches("class C {}; C a = C();",
varDecl(has(ignoringImplicit(cxxConstructExpr())))));
}
TEST(IgnoringImplicit, MatchesNestedImplicit) {
StringRef Code = R"(
struct OtherType;
struct SomeType
{
SomeType() {}
SomeType(const OtherType&) {}
SomeType& operator=(OtherType const&) { return *this; }
};
struct OtherType
{
OtherType() {}
~OtherType() {}
};
OtherType something()
{
return {};
}
int main()
{
SomeType i = something();
}
)";
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs,
varDecl(hasName("i"),
hasInitializer(exprWithCleanups(has(cxxConstructExpr(
has(expr(ignoringImplicit(cxxConstructExpr(has(
expr(ignoringImplicit(callExpr())))))))))))))));
}
TEST(IgnoringImplicit, DoesNotMatchIncorrectly) {
EXPECT_TRUE(notMatches("class C {}; C a = C();",
traverse(TK_AsIs, varDecl(has(cxxConstructExpr())))));
}
TEST(Traversal, traverseMatcher) {
StringRef VarDeclCode = R"cpp(
void foo()
{
int i = 3.0;
}
)cpp";
auto Matcher = varDecl(hasInitializer(floatLiteral()));
EXPECT_TRUE(notMatches(VarDeclCode, traverse(TK_AsIs, Matcher)));
EXPECT_TRUE(
matches(VarDeclCode, traverse(TK_IgnoreUnlessSpelledInSource, Matcher)));
auto ParentMatcher = floatLiteral(hasParent(varDecl(hasName("i"))));
EXPECT_TRUE(notMatches(VarDeclCode, traverse(TK_AsIs, ParentMatcher)));
EXPECT_TRUE(matches(VarDeclCode,
traverse(TK_IgnoreUnlessSpelledInSource, ParentMatcher)));
EXPECT_TRUE(matches(
VarDeclCode, decl(traverse(TK_AsIs, anyOf(cxxRecordDecl(), varDecl())))));
EXPECT_TRUE(
matches(VarDeclCode,
floatLiteral(traverse(TK_AsIs, hasParent(implicitCastExpr())))));
EXPECT_TRUE(
matches(VarDeclCode, floatLiteral(traverse(TK_IgnoreUnlessSpelledInSource,
hasParent(varDecl())))));
EXPECT_TRUE(
matches(VarDeclCode, varDecl(traverse(TK_IgnoreUnlessSpelledInSource,
unless(parmVarDecl())))));
EXPECT_TRUE(
notMatches(VarDeclCode, varDecl(traverse(TK_IgnoreUnlessSpelledInSource,
has(implicitCastExpr())))));
EXPECT_TRUE(matches(VarDeclCode,
varDecl(traverse(TK_AsIs, has(implicitCastExpr())))));
EXPECT_TRUE(matches(
VarDeclCode, traverse(TK_IgnoreUnlessSpelledInSource,
// The has() below strips away the ImplicitCastExpr
// before the traverse(AsIs) gets to process it.
varDecl(has(traverse(TK_AsIs, floatLiteral()))))));
EXPECT_TRUE(
matches(VarDeclCode, functionDecl(traverse(TK_AsIs, hasName("foo")))));
EXPECT_TRUE(matches(
VarDeclCode,
functionDecl(traverse(TK_IgnoreUnlessSpelledInSource, hasName("foo")))));
EXPECT_TRUE(matches(
VarDeclCode, functionDecl(traverse(TK_AsIs, hasAnyName("foo", "bar")))));
EXPECT_TRUE(
matches(VarDeclCode, functionDecl(traverse(TK_IgnoreUnlessSpelledInSource,
hasAnyName("foo", "bar")))));
StringRef Code = R"cpp(
void foo(int a)
{
int i = 3.0 + a;
}
void bar()
{
foo(7.0);
}
)cpp";
EXPECT_TRUE(
matches(Code, callExpr(traverse(TK_IgnoreUnlessSpelledInSource,
hasArgument(0, floatLiteral())))));
EXPECT_TRUE(
matches(Code, callExpr(traverse(TK_IgnoreUnlessSpelledInSource,
hasAnyArgument(floatLiteral())))));
EXPECT_TRUE(matches(
R"cpp(
void takesBool(bool){}
template <typename T>
void neverInstantiatedTemplate() {
takesBool(T{});
}
)cpp",
traverse(TK_IgnoreUnlessSpelledInSource,
callExpr(unless(callExpr(hasDeclaration(functionDecl())))))));
EXPECT_TRUE(
matches(VarDeclCode, varDecl(traverse(TK_IgnoreUnlessSpelledInSource,
hasType(builtinType())))));
EXPECT_TRUE(
matches(VarDeclCode,
functionDecl(hasName("foo"),
traverse(TK_AsIs, hasDescendant(floatLiteral())))));
EXPECT_TRUE(notMatches(
Code, traverse(TK_AsIs, floatLiteral(hasParent(callExpr(
callee(functionDecl(hasName("foo")))))))));
EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
floatLiteral(hasParent(callExpr(callee(
functionDecl(hasName("foo")))))))));
Code = R"cpp(
void foo()
{
int i = (3);
}
)cpp";
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasInitializer(integerLiteral(equals(3)))))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
integerLiteral(equals(3), hasParent(varDecl(hasName("i")))))));
Code = R"cpp(
const char *SomeString{"str"};
)cpp";
EXPECT_TRUE(
matches(Code, traverse(TK_AsIs, stringLiteral(hasParent(implicitCastExpr(
hasParent(initListExpr())))))));
EXPECT_TRUE(
matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
stringLiteral(hasParent(initListExpr())))));
Code = R"cpp(
struct String
{
String(const char*, int = -1) {}
};
void stringConstruct()
{
String s = "foo";
s = "bar";
}
)cpp";
EXPECT_TRUE(matches(
Code,
traverse(
TK_AsIs,
functionDecl(
hasName("stringConstruct"),
hasDescendant(varDecl(
hasName("s"),
hasInitializer(ignoringImplicit(cxxConstructExpr(hasArgument(
0, ignoringImplicit(cxxConstructExpr(hasArgument(
0, ignoringImplicit(stringLiteral()))))))))))))));
EXPECT_TRUE(matches(
Code,
traverse(
TK_AsIs,
functionDecl(hasName("stringConstruct"),
hasDescendant(cxxOperatorCallExpr(
isAssignmentOperator(),
hasArgument(1, ignoringImplicit(
cxxConstructExpr(hasArgument(
0, ignoringImplicit(stringLiteral())))))
))))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
functionDecl(hasName("stringConstruct"),
hasDescendant(varDecl(
hasName("s"),
hasInitializer(stringLiteral())))))));
EXPECT_TRUE(
matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
functionDecl(hasName("stringConstruct"),
hasDescendant(cxxOperatorCallExpr(
isAssignmentOperator(),
hasArgument(1, stringLiteral())))))));
Code = R"cpp(
struct C1 {};
struct C2 { operator C1(); };
void conversionOperator()
{
C2* c2;
C1 c1 = (*c2);
}
)cpp";
EXPECT_TRUE(matches(
Code,
traverse(
TK_AsIs,
functionDecl(
hasName("conversionOperator"),
hasDescendant(
varDecl(
hasName("c1"),
hasInitializer(
ignoringImplicit(cxxConstructExpr(hasArgument(
0, ignoringImplicit(
cxxMemberCallExpr(onImplicitObjectArgument(
ignoringParenImpCasts(unaryOperator(
hasOperatorName("*")))))))))))
.bind("c1"))))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
functionDecl(hasName("conversionOperator"),
hasDescendant(varDecl(
hasName("c1"), hasInitializer(unaryOperator(
hasOperatorName("*")))))))));
Code = R"cpp(
template <unsigned alignment>
void template_test() {
static_assert(alignment, "");
}
void actual_template_test() {
template_test<4>();
}
)cpp";
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs,
staticAssertDecl(has(implicitCastExpr(has(
substNonTypeTemplateParmExpr(has(integerLiteral())))))))));
EXPECT_TRUE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
staticAssertDecl(has(declRefExpr(
to(nonTypeTemplateParmDecl(hasName("alignment"))),
hasType(asString("unsigned int"))))))));
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, staticAssertDecl(hasDescendant(
integerLiteral())))));
EXPECT_FALSE(matches(
Code, traverse(TK_IgnoreUnlessSpelledInSource,
staticAssertDecl(hasDescendant(integerLiteral())))));
Code = R"cpp(
struct OneParamCtor {
explicit OneParamCtor(int);
};
struct TwoParamCtor {
explicit TwoParamCtor(int, int);
};
void varDeclCtors() {
{
auto var1 = OneParamCtor(5);
auto var2 = TwoParamCtor(6, 7);
}
{
OneParamCtor var3(5);
TwoParamCtor var4(6, 7);
}
int i = 0;
{
auto var5 = OneParamCtor(i);
auto var6 = TwoParamCtor(i, 7);
}
{
OneParamCtor var7(i);
TwoParamCtor var8(i, 7);
}
}
)cpp";
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs, varDecl(hasName("var1"), hasInitializer(hasDescendant(
cxxConstructExpr()))))));
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs, varDecl(hasName("var2"), hasInitializer(hasDescendant(
cxxConstructExpr()))))));
EXPECT_TRUE(matches(
Code, traverse(TK_AsIs, varDecl(hasName("var3"),
hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code, traverse(TK_AsIs, varDecl(hasName("var4"),
hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs, varDecl(hasName("var5"), hasInitializer(hasDescendant(
cxxConstructExpr()))))));
EXPECT_TRUE(matches(
Code,
traverse(TK_AsIs, varDecl(hasName("var6"), hasInitializer(hasDescendant(
cxxConstructExpr()))))));
EXPECT_TRUE(matches(
Code, traverse(TK_AsIs, varDecl(hasName("var7"),
hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code, traverse(TK_AsIs, varDecl(hasName("var8"),
hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var1"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var2"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var3"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var4"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var5"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var6"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var7"), hasInitializer(cxxConstructExpr())))));
EXPECT_TRUE(matches(
Code,
traverse(TK_IgnoreUnlessSpelledInSource,
varDecl(hasName("var8"), hasInitializer(cxxConstructExpr())))));
Code = R"cpp(
template<typename T>
struct TemplStruct {
TemplStruct() {}
~TemplStruct() {}
void outOfLine(T);
private:
T m_t;
};
template<typename T>
void TemplStruct<T>::outOfLine(T)
{
}
template<typename T>
T timesTwo(T input)
{
return input * 2;
}
void instantiate()
{
TemplStruct<int> ti;
TemplStruct<double> td;
(void)timesTwo<int>(2);
(void)timesTwo<double>(2);
}
template class TemplStruct<float>;
extern template class TemplStruct<long>;
template<> class TemplStruct<bool> {
TemplStruct() {}
~TemplStruct() {}
void boolSpecializationMethodOnly() {}
private:
bool m_t;
};
template float timesTwo(float);
template<> bool timesTwo<bool>(bool){
return true;
}
)cpp";
{
auto M = cxxRecordDecl(hasName("TemplStruct"),
has(fieldDecl(hasType(asString("int")))));
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
}
{
auto M = cxxRecordDecl(hasName("TemplStruct"),
has(fieldDecl(hasType(asString("double")))));
EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M)));
EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M)));
}
{
auto M =
functionDecl(hasName("timesTwo"),
hasParameter(0, parmVarDecl(hasType(asString("int")))));
EXPECT_TRUE(matches(Code, traverse