//= 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/PrettyPrinter.h" #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 { 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 struct A {" " template struct F {};" "};" "template struct B : A::template F {};" "B 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>("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>("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>("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 void f(T t);", parmVarDecl(hasType(templateTypeParmType())))); // TemplateTypeParmType -> TemplateTypeParmDecl EXPECT_TRUE(matches("template void f(T t);", parmVarDecl(hasType(namedDecl(hasName("T")))))); // InjectedClassNameType EXPECT_TRUE(matches("template struct S {" " void f(S s);" "};", parmVarDecl(hasType(injectedClassNameType())))); EXPECT_TRUE(notMatches("template struct S {" " void g(S s);" "};", parmVarDecl(hasType(injectedClassNameType())))); // InjectedClassNameType -> CXXRecordDecl EXPECT_TRUE(matches("template struct S {" " void f(S s);" "};", parmVarDecl(hasType(namedDecl(hasName("S")))))); static const char Using[] = "template " "struct Base {" " typedef T Foo;" "};" "" "template " "struct S : private Base {" " using typename Base::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::value, "Expected TypedefType to have a getDecl."); static_assert(internal::has_getDecl::value, "Expected RecordType to have a getDecl."); static_assert(!internal::has_getDecl::value, "Expected TemplateSpecializationType to *not* have a getDecl."); } TEST(HasDeclaration, ElaboratedType) { EXPECT_TRUE(matches( "namespace n { template struct X {}; }" "void f(n::X);", parmVarDecl(hasType(qualType(hasDeclaration(cxxRecordDecl())))))); EXPECT_TRUE(matches( "namespace n { template struct X {}; }" "void f(n::X);", parmVarDecl(hasType(elaboratedType(hasDeclaration(cxxRecordDecl())))))); } TEST(HasDeclaration, HasDeclarationOfTypeWithDecl) { EXPECT_TRUE(matches("typedef int X; X a;", varDecl(hasName("a"), hasType(typedefType(hasDeclaration(decl())))))); // FIXME: Add tests for other types with getDecl() (e.g. RecordType) } TEST(HasDeclaration, HasDeclarationOfTemplateSpecializationType) { EXPECT_TRUE(matches("template class A {}; A a;", varDecl(hasType(templateSpecializationType( hasDeclaration(namedDecl(hasName("A")))))))); EXPECT_TRUE(matches("template class A {};" "template class B { A a; };", fieldDecl(hasType(templateSpecializationType( hasDeclaration(namedDecl(hasName("A")))))))); EXPECT_TRUE(matches("template class A {}; A a;", varDecl(hasType(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 using C = T; C c;", varDecl(hasType(templateSpecializationType( hasDeclaration(typeAliasTemplateDecl())))))); } 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 void f(T t); }" "template void g() { using N::f; f(T()); }", unresolvedLookupExpr(hasAnyDeclaration( namedDecl(hasUnderlyingDecl(hasName("::N::f"))))))); EXPECT_TRUE(matches( "namespace N { template void f(T t); }" "template void g() { N::f(T()); }", unresolvedLookupExpr(hasAnyDeclaration(namedDecl(hasName("::N::f")))))); EXPECT_TRUE(notMatches( "namespace N { template void f(T t); }" "template 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, 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, 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, MatchesDeclaratorDecls) { EXPECT_TRUE(matches("int x;", varDecl(hasName("x"), hasTypeLoc(loc(asString("int")))))); // Make sure we don't crash on implicit constructors. EXPECT_TRUE(notMatches("class X {}; X x;", declaratorDecl(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 void x() { int y; (void)Y(1, y); }", UnresolvedCtorArgumentY)); EXPECT_TRUE(matches("template 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 " "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, HasAnyCapture) { auto HasCaptureX = lambdaExpr(hasAnyCapture(varDecl(hasName("x")))); EXPECT_TRUE(matches("void f() { int x = 3; [x](){}; }", HasCaptureX)); EXPECT_TRUE(matches("void f() { int x = 3; [&x](){}; }", HasCaptureX)); EXPECT_TRUE(notMatches("void f() { [](){}; }", HasCaptureX)); EXPECT_TRUE(notMatches("void f() { int z = 3; [&z](){}; }", HasCaptureX)); EXPECT_TRUE( notMatches("struct a { void f() { [this](){}; }; };", HasCaptureX)); } TEST(Matcher, CapturesThis) { auto HasCaptureThis = lambdaExpr(hasAnyCapture(cxxThisExpr())); EXPECT_TRUE( matches("struct a { void f() { [this](){}; }; };", HasCaptureThis)); EXPECT_TRUE(notMatches("void f() { [](){}; }", HasCaptureThis)); EXPECT_TRUE(notMatches("void f() { int x = 3; [x](){}; }", HasCaptureThis)); EXPECT_TRUE(notMatches("void f() { int x = 3; [&x](){}; }", HasCaptureThis)); EXPECT_TRUE(notMatches("void f() { int z = 3; [&z](){}; }", HasCaptureThis)); } 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, 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>("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>("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>( "param"))); EXPECT_TRUE( matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr, std::make_unique>( "arg"))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, std::make_unique>("param", 2))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, std::make_unique>("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>("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>("v", 4))); } 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>("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>("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>("type"))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i) { int y; f(y); }", CallExpr, std::make_unique>("arg"))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, std::make_unique>("type", 2))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, std::make_unique>("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>("type"))); EXPECT_TRUE(matchAndVerifyResultTrue( "struct C {" " C(int i) {}" "};" "int y = 0;" "C Obj(y);", ConstructExpr, std::make_unique>("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>("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>("type"))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i) {" "void (*f_ptr)(int) = f; int y; f_ptr(y); }", CallExpr, std::make_unique>("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>("type"))); EXPECT_TRUE(matchAndVerifyResultTrue( S, CallExpr, std::make_unique>("arg"))); } 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 struct B {};" "B b;", classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType( asString("int")))))); } TEST(Matcher, MatchesTemplateTemplateArgument) { EXPECT_TRUE(matches("template