//= unittests/ASTMatchers/ASTMatchersTraversalTest.cpp - matchers unit tests =// // // The LLVM Compiler Infrastructure //` // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #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"))), llvm::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"))), llvm::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"))), llvm::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, 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")))))))); } TEST(HasDeclaration, HasDeclarationOfCXXNewExpr) { EXPECT_TRUE( matches("int *A = new int();", cxxNewExpr(hasDeclaration(functionDecl(parameterCountIs(1)))))); } 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 = 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) { StatementMatcher CallArgumentY = callExpr( hasAnyArgument( ignoringParenImpCasts(declRefExpr(to(varDecl(hasName("y"))))))); 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(notMatches("void x(int, int) { x(1, 2); }", CallArgumentY)); StatementMatcher ImplicitCastedArgument = callExpr( hasAnyArgument(implicitCastExpr())); EXPECT_TRUE(matches("void x(long) { int y; x(y); }", ImplicitCastedArgument)); } 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, llvm::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, llvm::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, llvm::make_unique>( "param"))); EXPECT_TRUE( matchAndVerifyResultTrue("void f(int i) { int y; f(y); }", CallExpr, llvm::make_unique>( "arg"))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, llvm::make_unique>("param", 2))); EXPECT_TRUE(matchAndVerifyResultTrue( "void f(int i, int j) { int y; f(y, y); }", CallExpr, llvm::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 = cxxConstructExpr(forEachArgumentWithParam(ArgumentY, IntParam)); EXPECT_TRUE(matchAndVerifyResultTrue( "struct C {" " C(int i) {}" "};" "int y = 0;" "C Obj(y);", ConstructExpr, llvm::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())))), llvm::make_unique>("v", 4))); } 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"))))); } 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"))))))); } 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