| // unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp - AST matcher 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(AllOf, AllOverloadsWork) { |
| const char Program[] = |
| "struct T { };" |
| "int f(int, T*, int, int);" |
| "void g(int x) { T t; f(x, &t, 3, 4); }"; |
| EXPECT_TRUE(matches(Program, |
| callExpr(allOf(callee(functionDecl(hasName("f"))), |
| hasArgument(0, declRefExpr(to(varDecl()))))))); |
| EXPECT_TRUE(matches(Program, |
| callExpr(allOf(callee(functionDecl(hasName("f"))), |
| hasArgument(0, declRefExpr(to(varDecl()))), |
| hasArgument(1, hasType(pointsTo( |
| recordDecl(hasName("T"))))))))); |
| EXPECT_TRUE(matches(Program, |
| callExpr(allOf(callee(functionDecl(hasName("f"))), |
| hasArgument(0, declRefExpr(to(varDecl()))), |
| hasArgument(1, hasType(pointsTo( |
| recordDecl(hasName("T"))))), |
| hasArgument(2, integerLiteral(equals(3))))))); |
| EXPECT_TRUE(matches(Program, |
| callExpr(allOf(callee(functionDecl(hasName("f"))), |
| hasArgument(0, declRefExpr(to(varDecl()))), |
| hasArgument(1, hasType(pointsTo( |
| recordDecl(hasName("T"))))), |
| hasArgument(2, integerLiteral(equals(3))), |
| hasArgument(3, integerLiteral(equals(4))))))); |
| } |
| |
| TEST(DeclarationMatcher, MatchHas) { |
| DeclarationMatcher HasClassX = recordDecl(has(recordDecl(hasName("X")))); |
| EXPECT_TRUE(matches("class Y { class X {}; };", HasClassX)); |
| EXPECT_TRUE(matches("class X {};", HasClassX)); |
| |
| DeclarationMatcher YHasClassX = |
| recordDecl(hasName("Y"), has(recordDecl(hasName("X")))); |
| EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX)); |
| EXPECT_TRUE(notMatches("class X {};", YHasClassX)); |
| EXPECT_TRUE( |
| notMatches("class Y { class Z { class X {}; }; };", YHasClassX)); |
| } |
| |
| TEST(DeclarationMatcher, MatchHasRecursiveAllOf) { |
| DeclarationMatcher Recursive = |
| recordDecl( |
| has(recordDecl( |
| has(recordDecl(hasName("X"))), |
| has(recordDecl(hasName("Y"))), |
| hasName("Z"))), |
| has(recordDecl( |
| has(recordDecl(hasName("A"))), |
| has(recordDecl(hasName("B"))), |
| hasName("C"))), |
| hasName("F")); |
| |
| EXPECT_TRUE(matches( |
| "class F {" |
| " class Z {" |
| " class X {};" |
| " class Y {};" |
| " };" |
| " class C {" |
| " class A {};" |
| " class B {};" |
| " };" |
| "};", Recursive)); |
| |
| EXPECT_TRUE(matches( |
| "class F {" |
| " class Z {" |
| " class A {};" |
| " class X {};" |
| " class Y {};" |
| " };" |
| " class C {" |
| " class X {};" |
| " class A {};" |
| " class B {};" |
| " };" |
| "};", Recursive)); |
| |
| EXPECT_TRUE(matches( |
| "class O1 {" |
| " class O2 {" |
| " class F {" |
| " class Z {" |
| " class A {};" |
| " class X {};" |
| " class Y {};" |
| " };" |
| " class C {" |
| " class X {};" |
| " class A {};" |
| " class B {};" |
| " };" |
| " };" |
| " };" |
| "};", Recursive)); |
| } |
| |
| TEST(DeclarationMatcher, MatchHasRecursiveAnyOf) { |
| DeclarationMatcher Recursive = |
| recordDecl( |
| anyOf( |
| has(recordDecl( |
| anyOf( |
| has(recordDecl( |
| hasName("X"))), |
| has(recordDecl( |
| hasName("Y"))), |
| hasName("Z")))), |
| has(recordDecl( |
| anyOf( |
| hasName("C"), |
| has(recordDecl( |
| hasName("A"))), |
| has(recordDecl( |
| hasName("B")))))), |
| hasName("F"))); |
| |
| EXPECT_TRUE(matches("class F {};", Recursive)); |
| EXPECT_TRUE(matches("class Z {};", Recursive)); |
| EXPECT_TRUE(matches("class C {};", Recursive)); |
| EXPECT_TRUE(matches("class M { class N { class X {}; }; };", Recursive)); |
| EXPECT_TRUE(matches("class M { class N { class B {}; }; };", Recursive)); |
| EXPECT_TRUE( |
| matches("class O1 { class O2 {" |
| " class M { class N { class B {}; }; }; " |
| "}; };", Recursive)); |
| } |
| |
| TEST(DeclarationMatcher, MatchNot) { |
| DeclarationMatcher NotClassX = |
| cxxRecordDecl( |
| isDerivedFrom("Y"), |
| unless(hasName("X"))); |
| EXPECT_TRUE(notMatches("", NotClassX)); |
| EXPECT_TRUE(notMatches("class Y {};", NotClassX)); |
| EXPECT_TRUE(matches("class Y {}; class Z : public Y {};", NotClassX)); |
| EXPECT_TRUE(notMatches("class Y {}; class X : public Y {};", NotClassX)); |
| EXPECT_TRUE( |
| notMatches("class Y {}; class Z {}; class X : public Y {};", |
| NotClassX)); |
| |
| DeclarationMatcher ClassXHasNotClassY = |
| recordDecl( |
| hasName("X"), |
| has(recordDecl(hasName("Z"))), |
| unless( |
| has(recordDecl(hasName("Y"))))); |
| EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY)); |
| EXPECT_TRUE(notMatches("class X { class Y {}; class Z {}; };", |
| ClassXHasNotClassY)); |
| |
| DeclarationMatcher NamedNotRecord = |
| namedDecl(hasName("Foo"), unless(recordDecl())); |
| EXPECT_TRUE(matches("void Foo(){}", NamedNotRecord)); |
| EXPECT_TRUE(notMatches("struct Foo {};", NamedNotRecord)); |
| } |
| |
| TEST(CastExpression, HasCastKind) { |
| EXPECT_TRUE(matches("char *p = 0;", |
| castExpr(hasCastKind(CK_NullToPointer)))); |
| EXPECT_TRUE(notMatches("char *p = 0;", |
| castExpr(hasCastKind(CK_DerivedToBase)))); |
| EXPECT_TRUE(matches("char *p = 0;", |
| implicitCastExpr(hasCastKind(CK_NullToPointer)))); |
| } |
| |
| TEST(DeclarationMatcher, HasDescendant) { |
| DeclarationMatcher ZDescendantClassX = |
| recordDecl( |
| hasDescendant(recordDecl(hasName("X"))), |
| hasName("Z")); |
| EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX)); |
| EXPECT_TRUE( |
| matches("class Z { class Y { class X {}; }; };", ZDescendantClassX)); |
| EXPECT_TRUE( |
| matches("class Z { class A { class Y { class X {}; }; }; };", |
| ZDescendantClassX)); |
| EXPECT_TRUE( |
| matches("class Z { class A { class B { class Y { class X {}; }; }; }; };", |
| ZDescendantClassX)); |
| EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX)); |
| |
| DeclarationMatcher ZDescendantClassXHasClassY = |
| recordDecl( |
| hasDescendant(recordDecl(has(recordDecl(hasName("Y"))), |
| hasName("X"))), |
| hasName("Z")); |
| EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };", |
| ZDescendantClassXHasClassY)); |
| EXPECT_TRUE( |
| matches("class Z { class A { class B { class X { class Y {}; }; }; }; };", |
| ZDescendantClassXHasClassY)); |
| EXPECT_TRUE(notMatches( |
| "class Z {" |
| " class A {" |
| " class B {" |
| " class X {" |
| " class C {" |
| " class Y {};" |
| " };" |
| " };" |
| " }; " |
| " };" |
| "};", ZDescendantClassXHasClassY)); |
| |
| DeclarationMatcher ZDescendantClassXDescendantClassY = |
| recordDecl( |
| hasDescendant(recordDecl(hasDescendant(recordDecl(hasName("Y"))), |
| hasName("X"))), |
| hasName("Z")); |
| EXPECT_TRUE( |
| matches("class Z { class A { class X { class B { class Y {}; }; }; }; };", |
| ZDescendantClassXDescendantClassY)); |
| EXPECT_TRUE(matches( |
| "class Z {" |
| " class A {" |
| " class X {" |
| " class B {" |
| " class Y {};" |
| " };" |
| " class Y {};" |
| " };" |
| " };" |
| "};", ZDescendantClassXDescendantClassY)); |
| } |
| |
| TEST(DeclarationMatcher, HasDescendantMemoization) { |
| DeclarationMatcher CannotMemoize = |
| decl(hasDescendant(typeLoc().bind("x")), has(decl())); |
| EXPECT_TRUE(matches("void f() { int i; }", CannotMemoize)); |
| } |
| |
| TEST(DeclarationMatcher, HasDescendantMemoizationUsesRestrictKind) { |
| auto Name = hasName("i"); |
| auto VD = internal::Matcher<VarDecl>(Name).dynCastTo<Decl>(); |
| auto RD = internal::Matcher<RecordDecl>(Name).dynCastTo<Decl>(); |
| // Matching VD first should not make a cache hit for RD. |
| EXPECT_TRUE(notMatches("void f() { int i; }", |
| decl(hasDescendant(VD), hasDescendant(RD)))); |
| EXPECT_TRUE(notMatches("void f() { int i; }", |
| decl(hasDescendant(RD), hasDescendant(VD)))); |
| // Not matching RD first should not make a cache hit for VD either. |
| EXPECT_TRUE(matches("void f() { int i; }", |
| decl(anyOf(hasDescendant(RD), hasDescendant(VD))))); |
| } |
| |
| TEST(DeclarationMatcher, HasAncestorMemoization) { |
| // This triggers an hasAncestor with a TemplateArgument in the bound nodes. |
| // That node can't be memoized so we have to check for it before trying to put |
| // it on the cache. |
| DeclarationMatcher CannotMemoize = classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(templateArgument().bind("targ")), |
| forEach(fieldDecl(hasAncestor(forStmt())))); |
| |
| EXPECT_TRUE(notMatches("template <typename T> struct S;" |
| "template <> struct S<int>{ int i; int j; };", |
| CannotMemoize)); |
| } |
| |
| TEST(DeclarationMatcher, HasAttr) { |
| EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};", |
| decl(hasAttr(clang::attr::WarnUnused)))); |
| EXPECT_FALSE(matches("struct X {};", |
| decl(hasAttr(clang::attr::WarnUnused)))); |
| } |
| |
| |
| TEST(DeclarationMatcher, MatchAnyOf) { |
| DeclarationMatcher YOrZDerivedFromX = cxxRecordDecl( |
| anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z")))); |
| EXPECT_TRUE(matches("class X {}; class Z : public X {};", YOrZDerivedFromX)); |
| EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("class X {}; class W : public X {};", YOrZDerivedFromX)); |
| EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX)); |
| |
| DeclarationMatcher XOrYOrZOrU = |
| recordDecl(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"))); |
| EXPECT_TRUE(matches("class X {};", XOrYOrZOrU)); |
| EXPECT_TRUE(notMatches("class V {};", XOrYOrZOrU)); |
| |
| DeclarationMatcher XOrYOrZOrUOrV = |
| recordDecl(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"), |
| hasName("V"))); |
| EXPECT_TRUE(matches("class X {};", XOrYOrZOrUOrV)); |
| EXPECT_TRUE(matches("class Y {};", XOrYOrZOrUOrV)); |
| EXPECT_TRUE(matches("class Z {};", XOrYOrZOrUOrV)); |
| EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV)); |
| EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV)); |
| EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV)); |
| |
| StatementMatcher MixedTypes = stmt(anyOf(ifStmt(), binaryOperator())); |
| EXPECT_TRUE(matches("int F() { return 1 + 2; }", MixedTypes)); |
| EXPECT_TRUE(matches("int F() { if (true) return 1; }", MixedTypes)); |
| EXPECT_TRUE(notMatches("int F() { return 1; }", MixedTypes)); |
| |
| EXPECT_TRUE( |
| matches("void f() try { } catch (int) { } catch (...) { }", |
| cxxCatchStmt(anyOf(hasDescendant(varDecl()), isCatchAll())))); |
| } |
| |
| TEST(DeclarationMatcher, ClassIsDerived) { |
| DeclarationMatcher IsDerivedFromX = cxxRecordDecl(isDerivedFrom("X")); |
| |
| EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class X {};", IsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class X;", IsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX)); |
| EXPECT_TRUE(notMatches("", IsDerivedFromX)); |
| |
| DeclarationMatcher IsAX = cxxRecordDecl(isSameOrDerivedFrom("X")); |
| |
| EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsAX)); |
| EXPECT_TRUE(matches("class X {};", IsAX)); |
| EXPECT_TRUE(matches("class X;", IsAX)); |
| EXPECT_TRUE(notMatches("class Y;", IsAX)); |
| EXPECT_TRUE(notMatches("", IsAX)); |
| |
| DeclarationMatcher ZIsDerivedFromX = |
| cxxRecordDecl(hasName("Z"), isDerivedFrom("X")); |
| EXPECT_TRUE( |
| matches("class X {}; class Y : public X {}; class Z : public Y {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {};" |
| "template<class T> class Y : public X {};" |
| "class Z : public Y<int> {};", ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T> class X {}; " |
| "template<class T> class Z : public X<T> {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T, class U=T> class X {}; " |
| "template<class T> class Z : public X<T> {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class X> class A { public: class Z : public X {}; }; " |
| "class X{}; void y() { A<X>::Z z; }", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template <class T> class X {}; " |
| "template<class Y> class A { class Z : public X<Y> {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<template<class T> class X> class A { " |
| " class Z : public X<int> {}; };", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<template<class T> class X> class A { " |
| " public: class Z : public X<int> {}; }; " |
| "template<class T> class X {}; void y() { A<X>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X::D {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class X> class A { public: " |
| " class Z : public X::D {}; }; " |
| "class Y { public: class X {}; typedef X D; }; " |
| "void y() { A<Y>::Z z; }", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; typedef X Y; class Z : public Y {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T> class Y { typedef typename T::U X; " |
| " class Z : public X {}; };", ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; class Z : public ::X {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class T> class X {}; " |
| "template<class T> class A { class Z : public X<T>::D {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T> class X { public: typedef X<T> D; }; " |
| "template<class T> class A { public: " |
| " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X::D::E {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; typedef X V; typedef V W; class Z : public W {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; class Y : public X {}; " |
| "typedef Y V; typedef V W; class Z : public W {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T, class U> class X {}; " |
| "template<class T> class A { class Z : public X<T, int> {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class D { typedef X A; typedef A B; " |
| " typedef B C; class Z : public C {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; typedef X A; typedef A B; " |
| "class Z : public B {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; typedef X A; typedef A B; typedef B C; " |
| "class Z : public C {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class U {}; typedef U X; typedef X V; " |
| "class Z : public V {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class Base {}; typedef Base X; " |
| "class Z : public Base {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class Base {}; typedef Base Base2; typedef Base2 X; " |
| "class Z : public Base {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("class Base {}; class Base2 {}; typedef Base2 X; " |
| "class Z : public Base {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class A {}; typedef A X; typedef A Y; " |
| "class Z : public Y {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template <typename T> class Z;" |
| "template <> class Z<void> {};" |
| "template <typename T> class Z : public Z<void> {};", |
| IsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template <typename T> class X;" |
| "template <> class X<void> {};" |
| "template <typename T> class X : public X<void> {};", |
| IsDerivedFromX)); |
| EXPECT_TRUE(matches( |
| "class X {};" |
| "template <typename T> class Z;" |
| "template <> class Z<void> {};" |
| "template <typename T> class Z : public Z<void>, public X {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<int> struct X;" |
| "template<int i> struct X : public X<i-1> {};", |
| cxxRecordDecl(isDerivedFrom(recordDecl(hasName("Some")))))); |
| EXPECT_TRUE(matches( |
| "struct A {};" |
| "template<int> struct X;" |
| "template<int i> struct X : public X<i-1> {};" |
| "template<> struct X<0> : public A {};" |
| "struct B : public X<42> {};", |
| cxxRecordDecl(hasName("B"), isDerivedFrom(recordDecl(hasName("A")))))); |
| |
| // FIXME: Once we have better matchers for template type matching, |
| // get rid of the Variable(...) matching and match the right template |
| // declarations directly. |
| const char *RecursiveTemplateOneParameter = |
| "class Base1 {}; class Base2 {};" |
| "template <typename T> class Z;" |
| "template <> class Z<void> : public Base1 {};" |
| "template <> class Z<int> : public Base2 {};" |
| "template <> class Z<float> : public Z<void> {};" |
| "template <> class Z<double> : public Z<int> {};" |
| "template <typename T> class Z : public Z<float>, public Z<double> {};" |
| "void f() { Z<float> z_float; Z<double> z_double; Z<char> z_char; }"; |
| EXPECT_TRUE(matches( |
| RecursiveTemplateOneParameter, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); |
| EXPECT_TRUE(notMatches( |
| RecursiveTemplateOneParameter, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE(matches( |
| RecursiveTemplateOneParameter, |
| varDecl(hasName("z_char"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"), |
| isDerivedFrom("Base2"))))))); |
| |
| const char *RecursiveTemplateTwoParameters = |
| "class Base1 {}; class Base2 {};" |
| "template <typename T1, typename T2> class Z;" |
| "template <typename T> class Z<void, T> : public Base1 {};" |
| "template <typename T> class Z<int, T> : public Base2 {};" |
| "template <typename T> class Z<float, T> : public Z<void, T> {};" |
| "template <typename T> class Z<double, T> : public Z<int, T> {};" |
| "template <typename T1, typename T2> class Z : " |
| " public Z<float, T2>, public Z<double, T2> {};" |
| "void f() { Z<float, void> z_float; Z<double, void> z_double; " |
| " Z<char, void> z_char; }"; |
| EXPECT_TRUE(matches( |
| RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); |
| EXPECT_TRUE(notMatches( |
| RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE(matches( |
| RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_char"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"), |
| isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE(matches( |
| "namespace ns { class X {}; class Y : public X {}; }", |
| cxxRecordDecl(isDerivedFrom("::ns::X")))); |
| EXPECT_TRUE(notMatches( |
| "class X {}; class Y : public X {};", |
| cxxRecordDecl(isDerivedFrom("::ns::X")))); |
| |
| EXPECT_TRUE(matches( |
| "class X {}; class Y : public X {};", |
| cxxRecordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test"))))); |
| |
| EXPECT_TRUE(matches( |
| "template<typename T> class X {};" |
| "template<typename T> using Z = X<T>;" |
| "template <typename T> class Y : Z<T> {};", |
| cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); |
| } |
| |
| TEST(DeclarationMatcher, IsLambda) { |
| const auto IsLambda = cxxMethodDecl(ofClass(cxxRecordDecl(isLambda()))); |
| EXPECT_TRUE(matches("auto x = []{};", IsLambda)); |
| EXPECT_TRUE(notMatches("struct S { void operator()() const; };", IsLambda)); |
| } |
| |
| TEST(Matcher, BindMatchedNodes) { |
| DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x")); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue("class X {};", |
| ClassX, llvm::make_unique<VerifyIdIsBoundTo<CXXRecordDecl>>("x"))); |
| |
| EXPECT_TRUE(matchAndVerifyResultFalse("class X {};", |
| ClassX, llvm::make_unique<VerifyIdIsBoundTo<CXXRecordDecl>>("other-id"))); |
| |
| TypeMatcher TypeAHasClassB = hasDeclaration( |
| recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b")))); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };", |
| TypeAHasClassB, |
| llvm::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); |
| |
| StatementMatcher MethodX = |
| callExpr(callee(cxxMethodDecl(hasName("x")))).bind("x"); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };", |
| MethodX, |
| llvm::make_unique<VerifyIdIsBoundTo<CXXMemberCallExpr>>("x"))); |
| } |
| |
| TEST(Matcher, BindTheSameNameInAlternatives) { |
| StatementMatcher matcher = anyOf( |
| binaryOperator(hasOperatorName("+"), |
| hasLHS(expr().bind("x")), |
| hasRHS(integerLiteral(equals(0)))), |
| binaryOperator(hasOperatorName("+"), |
| hasLHS(integerLiteral(equals(0))), |
| hasRHS(expr().bind("x")))); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| // The first branch of the matcher binds x to 0 but then fails. |
| // The second branch binds x to f() and succeeds. |
| "int f() { return 0 + f(); }", |
| matcher, |
| llvm::make_unique<VerifyIdIsBoundTo<CallExpr>>("x"))); |
| } |
| |
| TEST(Matcher, BindsIDForMemoizedResults) { |
| // Using the same matcher in two match expressions will make memoization |
| // kick in. |
| DeclarationMatcher ClassX = recordDecl(hasName("X")).bind("x"); |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { class B { class X {}; }; };", |
| DeclarationMatcher(anyOf( |
| recordDecl(hasName("A"), hasDescendant(ClassX)), |
| recordDecl(hasName("B"), hasDescendant(ClassX)))), |
| llvm::make_unique<VerifyIdIsBoundTo<Decl>>("x", 2))); |
| } |
| |
| TEST(HasType, MatchesAsString) { |
| EXPECT_TRUE( |
| matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }", |
| cxxMemberCallExpr(on(hasType(asString("class Y *")))))); |
| EXPECT_TRUE( |
| matches("class X { void x(int x) {} };", |
| cxxMethodDecl(hasParameter(0, hasType(asString("int")))))); |
| EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };", |
| fieldDecl(hasType(asString("ns::A"))))); |
| EXPECT_TRUE(matches("namespace { struct A {}; } struct B { A a; };", |
| fieldDecl(hasType(asString("struct (anonymous namespace)::A"))))); |
| } |
| |
| TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) { |
| StatementMatcher OpCallAndAnd = |
| cxxOperatorCallExpr(hasOverloadedOperatorName("&&")); |
| EXPECT_TRUE(matches("class Y { }; " |
| "bool operator&&(Y x, Y y) { return true; }; " |
| "Y a; Y b; bool c = a && b;", OpCallAndAnd)); |
| StatementMatcher OpCallLessLess = |
| cxxOperatorCallExpr(hasOverloadedOperatorName("<<")); |
| EXPECT_TRUE(notMatches("class Y { }; " |
| "bool operator&&(Y x, Y y) { return true; }; " |
| "Y a; Y b; bool c = a && b;", |
| OpCallLessLess)); |
| StatementMatcher OpStarCall = |
| cxxOperatorCallExpr(hasOverloadedOperatorName("*")); |
| EXPECT_TRUE(matches("class Y; int operator*(Y &); void f(Y &y) { *y; }", |
| OpStarCall)); |
| DeclarationMatcher ClassWithOpStar = |
| cxxRecordDecl(hasMethod(hasOverloadedOperatorName("*"))); |
| EXPECT_TRUE(matches("class Y { int operator*(); };", |
| ClassWithOpStar)); |
| EXPECT_TRUE(notMatches("class Y { void myOperator(); };", |
| ClassWithOpStar)) ; |
| DeclarationMatcher AnyOpStar = functionDecl(hasOverloadedOperatorName("*")); |
| EXPECT_TRUE(matches("class Y; int operator*(Y &);", AnyOpStar)); |
| EXPECT_TRUE(matches("class Y { int operator*(); };", AnyOpStar)); |
| } |
| |
| |
| TEST(Matcher, NestedOverloadedOperatorCalls) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class Y { }; " |
| "Y& operator&&(Y& x, Y& y) { return x; }; " |
| "Y a; Y b; Y c; Y d = a && b && c;", |
| cxxOperatorCallExpr(hasOverloadedOperatorName("&&")).bind("x"), |
| llvm::make_unique<VerifyIdIsBoundTo<CXXOperatorCallExpr>>("x", 2))); |
| EXPECT_TRUE(matches("class Y { }; " |
| "Y& operator&&(Y& x, Y& y) { return x; }; " |
| "Y a; Y b; Y c; Y d = a && b && c;", |
| cxxOperatorCallExpr(hasParent(cxxOperatorCallExpr())))); |
| EXPECT_TRUE( |
| matches("class Y { }; " |
| "Y& operator&&(Y& x, Y& y) { return x; }; " |
| "Y a; Y b; Y c; Y d = a && b && c;", |
| cxxOperatorCallExpr(hasDescendant(cxxOperatorCallExpr())))); |
| } |
| |
| TEST(Matcher, VarDecl_Storage) { |
| auto M = varDecl(hasName("X"), hasLocalStorage()); |
| EXPECT_TRUE(matches("void f() { int X; }", M)); |
| EXPECT_TRUE(notMatches("int X;", M)); |
| EXPECT_TRUE(notMatches("void f() { static int X; }", M)); |
| |
| M = varDecl(hasName("X"), hasGlobalStorage()); |
| EXPECT_TRUE(notMatches("void f() { int X; }", M)); |
| EXPECT_TRUE(matches("int X;", M)); |
| EXPECT_TRUE(matches("void f() { static int X; }", M)); |
| } |
| |
| TEST(Matcher, VarDecl_StorageDuration) { |
| std::string T = |
| "void f() { int x; static int y; } int a;static int b;extern int c;"; |
| |
| EXPECT_TRUE(matches(T, varDecl(hasName("x"), hasAutomaticStorageDuration()))); |
| EXPECT_TRUE( |
| notMatches(T, varDecl(hasName("y"), hasAutomaticStorageDuration()))); |
| EXPECT_TRUE( |
| notMatches(T, varDecl(hasName("a"), hasAutomaticStorageDuration()))); |
| |
| EXPECT_TRUE(matches(T, varDecl(hasName("y"), hasStaticStorageDuration()))); |
| EXPECT_TRUE(matches(T, varDecl(hasName("a"), hasStaticStorageDuration()))); |
| EXPECT_TRUE(matches(T, varDecl(hasName("b"), hasStaticStorageDuration()))); |
| EXPECT_TRUE(matches(T, varDecl(hasName("c"), hasStaticStorageDuration()))); |
| EXPECT_TRUE(notMatches(T, varDecl(hasName("x"), hasStaticStorageDuration()))); |
| |
| // FIXME: It is really hard to test with thread_local itself because not all |
| // targets support TLS, which causes this to be an error depending on what |
| // platform the test is being run on. We do not have access to the TargetInfo |
| // object to be able to test whether the platform supports TLS or not. |
| EXPECT_TRUE(notMatches(T, varDecl(hasName("x"), hasThreadStorageDuration()))); |
| EXPECT_TRUE(notMatches(T, varDecl(hasName("y"), hasThreadStorageDuration()))); |
| EXPECT_TRUE(notMatches(T, varDecl(hasName("a"), hasThreadStorageDuration()))); |
| } |
| |
| TEST(Matcher, FindsVarDeclInFunctionParameter) { |
| EXPECT_TRUE(matches( |
| "void f(int i) {}", |
| varDecl(hasName("i")))); |
| } |
| |
| TEST(UnaryExpressionOrTypeTraitExpression, MatchesCorrectType) { |
| EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", sizeOfExpr( |
| hasArgumentOfType(asString("int"))))); |
| EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr( |
| hasArgumentOfType(asString("float"))))); |
| EXPECT_TRUE(matches( |
| "struct A {}; void x() { A a; int b = sizeof(a); }", |
| sizeOfExpr(hasArgumentOfType(hasDeclaration(recordDecl(hasName("A"))))))); |
| EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr( |
| hasArgumentOfType(hasDeclaration(recordDecl(hasName("string"))))))); |
| } |
| |
| TEST(IsInteger, MatchesIntegers) { |
| EXPECT_TRUE(matches("int i = 0;", varDecl(hasType(isInteger())))); |
| EXPECT_TRUE(matches( |
| "long long i = 0; void f(long long) { }; void g() {f(i);}", |
| callExpr(hasArgument(0, declRefExpr( |
| to(varDecl(hasType(isInteger())))))))); |
| } |
| |
| TEST(IsInteger, ReportsNoFalsePositives) { |
| EXPECT_TRUE(notMatches("int *i;", varDecl(hasType(isInteger())))); |
| EXPECT_TRUE(notMatches("struct T {}; T t; void f(T *) { }; void g() {f(&t);}", |
| callExpr(hasArgument(0, declRefExpr( |
| to(varDecl(hasType(isInteger())))))))); |
| } |
| |
| TEST(IsSignedInteger, MatchesSignedIntegers) { |
| EXPECT_TRUE(matches("int i = 0;", varDecl(hasType(isSignedInteger())))); |
| EXPECT_TRUE(notMatches("unsigned i = 0;", |
| varDecl(hasType(isSignedInteger())))); |
| } |
| |
| TEST(IsUnsignedInteger, MatchesUnsignedIntegers) { |
| EXPECT_TRUE(notMatches("int i = 0;", varDecl(hasType(isUnsignedInteger())))); |
| EXPECT_TRUE(matches("unsigned i = 0;", |
| varDecl(hasType(isUnsignedInteger())))); |
| } |
| |
| TEST(IsAnyPointer, MatchesPointers) { |
| EXPECT_TRUE(matches("int* i = nullptr;", varDecl(hasType(isAnyPointer())))); |
| } |
| |
| TEST(IsAnyPointer, MatchesObjcPointer) { |
| EXPECT_TRUE(matchesObjC("@interface Foo @end Foo *f;", |
| varDecl(hasType(isAnyPointer())))); |
| } |
| |
| TEST(IsAnyPointer, ReportsNoFalsePositives) { |
| EXPECT_TRUE(notMatches("int i = 0;", varDecl(hasType(isAnyPointer())))); |
| } |
| |
| TEST(IsAnyCharacter, MatchesCharacters) { |
| EXPECT_TRUE(matches("char i = 0;", varDecl(hasType(isAnyCharacter())))); |
| } |
| |
| TEST(IsAnyCharacter, ReportsNoFalsePositives) { |
| EXPECT_TRUE(notMatches("int i;", varDecl(hasType(isAnyCharacter())))); |
| } |
| |
| TEST(IsArrow, MatchesMemberVariablesViaArrow) { |
| EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(matches("class Y { void x() { y; } int y; };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };", |
| memberExpr(isArrow()))); |
| } |
| |
| TEST(IsArrow, MatchesStaticMemberVariablesViaArrow) { |
| EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };", |
| memberExpr(isArrow()))); |
| } |
| |
| TEST(IsArrow, MatchesMemberCallsViaArrow) { |
| EXPECT_TRUE(matches("class Y { void x() { this->x(); } };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(matches("class Y { void x() { x(); } };", |
| memberExpr(isArrow()))); |
| EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };", |
| memberExpr(isArrow()))); |
| } |
| |
| TEST(ConversionDeclaration, IsExplicit) { |
| EXPECT_TRUE(matches("struct S { explicit operator int(); };", |
| cxxConversionDecl(isExplicit()))); |
| EXPECT_TRUE(notMatches("struct S { operator int(); };", |
| cxxConversionDecl(isExplicit()))); |
| } |
| |
| TEST(Matcher, ArgumentCount) { |
| StatementMatcher Call1Arg = callExpr(argumentCountIs(1)); |
| |
| EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg)); |
| EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg)); |
| EXPECT_TRUE(notMatches("void x(int, int) { x(0, 0); }", Call1Arg)); |
| } |
| |
| TEST(Matcher, ParameterCount) { |
| DeclarationMatcher Function1Arg = functionDecl(parameterCountIs(1)); |
| EXPECT_TRUE(matches("void f(int i) {}", Function1Arg)); |
| EXPECT_TRUE(matches("class X { void f(int i) {} };", Function1Arg)); |
| EXPECT_TRUE(notMatches("void f() {}", Function1Arg)); |
| EXPECT_TRUE(notMatches("void f(int i, int j, int k) {}", Function1Arg)); |
| EXPECT_TRUE(matches("void f(int i, ...) {};", Function1Arg)); |
| } |
| |
| TEST(Matcher, References) { |
| DeclarationMatcher ReferenceClassX = varDecl( |
| hasType(references(recordDecl(hasName("X"))))); |
| EXPECT_TRUE(matches("class X {}; void y(X y) { X &x = y; }", |
| ReferenceClassX)); |
| EXPECT_TRUE( |
| matches("class X {}; void y(X y) { const X &x = y; }", ReferenceClassX)); |
| // The match here is on the implicit copy constructor code for |
| // class X, not on code 'X x = y'. |
| EXPECT_TRUE( |
| matches("class X {}; void y(X y) { X x = y; }", ReferenceClassX)); |
| EXPECT_TRUE( |
| notMatches("class X {}; extern X x;", ReferenceClassX)); |
| EXPECT_TRUE( |
| notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX)); |
| } |
| |
| TEST(QualType, hasLocalQualifiers) { |
| EXPECT_TRUE(notMatches("typedef const int const_int; const_int i = 1;", |
| varDecl(hasType(hasLocalQualifiers())))); |
| EXPECT_TRUE(matches("int *const j = nullptr;", |
| varDecl(hasType(hasLocalQualifiers())))); |
| EXPECT_TRUE(matches("int *volatile k;", |
| varDecl(hasType(hasLocalQualifiers())))); |
| EXPECT_TRUE(notMatches("int m;", |
| varDecl(hasType(hasLocalQualifiers())))); |
| } |
| |
| TEST(IsExternC, MatchesExternCFunctionDeclarations) { |
| EXPECT_TRUE(matches("extern \"C\" void f() {}", functionDecl(isExternC()))); |
| EXPECT_TRUE(matches("extern \"C\" { void f() {} }", |
| functionDecl(isExternC()))); |
| EXPECT_TRUE(notMatches("void f() {}", functionDecl(isExternC()))); |
| } |
| |
| TEST(IsExternC, MatchesExternCVariableDeclarations) { |
| EXPECT_TRUE(matches("extern \"C\" int i;", varDecl(isExternC()))); |
| EXPECT_TRUE(matches("extern \"C\" { int i; }", varDecl(isExternC()))); |
| EXPECT_TRUE(notMatches("int i;", varDecl(isExternC()))); |
| } |
| |
| TEST(IsStaticStorageClass, MatchesStaticDeclarations) { |
| EXPECT_TRUE( |
| matches("static void f() {}", functionDecl(isStaticStorageClass()))); |
| EXPECT_TRUE(matches("static int i = 1;", varDecl(isStaticStorageClass()))); |
| EXPECT_TRUE(notMatches("int i = 1;", varDecl(isStaticStorageClass()))); |
| EXPECT_TRUE(notMatches("extern int i;", varDecl(isStaticStorageClass()))); |
| EXPECT_TRUE(notMatches("void f() {}", functionDecl(isStaticStorageClass()))); |
| } |
| |
| TEST(IsDefaulted, MatchesDefaultedFunctionDeclarations) { |
| EXPECT_TRUE(notMatches("class A { ~A(); };", |
| functionDecl(hasName("~A"), isDefaulted()))); |
| EXPECT_TRUE(matches("class B { ~B() = default; };", |
| functionDecl(hasName("~B"), isDefaulted()))); |
| } |
| |
| TEST(IsDeleted, MatchesDeletedFunctionDeclarations) { |
| EXPECT_TRUE( |
| notMatches("void Func();", functionDecl(hasName("Func"), isDeleted()))); |
| EXPECT_TRUE(matches("void Func() = delete;", |
| functionDecl(hasName("Func"), isDeleted()))); |
| } |
| |
| TEST(IsNoThrow, MatchesNoThrowFunctionDeclarations) { |
| EXPECT_TRUE(notMatches("void f();", functionDecl(isNoThrow()))); |
| EXPECT_TRUE(notMatches("void f() throw(int);", functionDecl(isNoThrow()))); |
| EXPECT_TRUE( |
| notMatches("void f() noexcept(false);", functionDecl(isNoThrow()))); |
| EXPECT_TRUE(matches("void f() throw();", functionDecl(isNoThrow()))); |
| EXPECT_TRUE(matches("void f() noexcept;", functionDecl(isNoThrow()))); |
| |
| EXPECT_TRUE(notMatches("void f();", functionProtoType(isNoThrow()))); |
| EXPECT_TRUE(notMatches("void f() throw(int);", functionProtoType(isNoThrow()))); |
| EXPECT_TRUE( |
| notMatches("void f() noexcept(false);", functionProtoType(isNoThrow()))); |
| EXPECT_TRUE(matches("void f() throw();", functionProtoType(isNoThrow()))); |
| EXPECT_TRUE(matches("void f() noexcept;", functionProtoType(isNoThrow()))); |
| } |
| |
| TEST(isConstexpr, MatchesConstexprDeclarations) { |
| EXPECT_TRUE(matches("constexpr int foo = 42;", |
| varDecl(hasName("foo"), isConstexpr()))); |
| EXPECT_TRUE(matches("constexpr int bar();", |
| functionDecl(hasName("bar"), isConstexpr()))); |
| EXPECT_TRUE(matchesConditionally("void baz() { if constexpr(1 > 0) {} }", |
| ifStmt(isConstexpr()), true, "-std=c++17")); |
| EXPECT_TRUE(matchesConditionally("void baz() { if (1 > 0) {} }", |
| ifStmt(isConstexpr()), false, "-std=c++17")); |
| } |
| |
| TEST(TemplateArgumentCountIs, Matches) { |
| EXPECT_TRUE( |
| matches("template<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(templateArgumentCountIs(1)))); |
| EXPECT_TRUE( |
| notMatches("template<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(templateArgumentCountIs(2)))); |
| |
| EXPECT_TRUE(matches("template<typename T> struct C {}; C<int> c;", |
| templateSpecializationType(templateArgumentCountIs(1)))); |
| EXPECT_TRUE( |
| notMatches("template<typename T> struct C {}; C<int> c;", |
| templateSpecializationType(templateArgumentCountIs(2)))); |
| } |
| |
| TEST(IsIntegral, Matches) { |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(isIntegral())))); |
| EXPECT_TRUE(notMatches("template<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(hasAnyTemplateArgument( |
| templateArgument(isIntegral()))))); |
| } |
| |
| TEST(EqualsIntegralValue, Matches) { |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("42"))))); |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<-42> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("-42"))))); |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<-0042> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("-34"))))); |
| EXPECT_TRUE(notMatches("template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl(hasAnyTemplateArgument( |
| equalsIntegralValue("0042"))))); |
| } |
| |
| TEST(Matcher, MatchesAccessSpecDecls) { |
| EXPECT_TRUE(matches("class C { public: int i; };", accessSpecDecl())); |
| EXPECT_TRUE( |
| matches("class C { public: int i; };", accessSpecDecl(isPublic()))); |
| EXPECT_TRUE( |
| notMatches("class C { public: int i; };", accessSpecDecl(isProtected()))); |
| EXPECT_TRUE( |
| notMatches("class C { public: int i; };", accessSpecDecl(isPrivate()))); |
| |
| EXPECT_TRUE(notMatches("class C { int i; };", accessSpecDecl())); |
| } |
| |
| TEST(Matcher, MatchesFinal) { |
| EXPECT_TRUE(matches("class X final {};", cxxRecordDecl(isFinal()))); |
| EXPECT_TRUE(matches("class X { virtual void f() final; };", |
| cxxMethodDecl(isFinal()))); |
| EXPECT_TRUE(notMatches("class X {};", cxxRecordDecl(isFinal()))); |
| EXPECT_TRUE( |
| notMatches("class X { virtual void f(); };", cxxMethodDecl(isFinal()))); |
| } |
| |
| TEST(Matcher, MatchesVirtualMethod) { |
| EXPECT_TRUE(matches("class X { virtual int f(); };", |
| cxxMethodDecl(isVirtual(), hasName("::X::f")))); |
| EXPECT_TRUE(notMatches("class X { int f(); };", cxxMethodDecl(isVirtual()))); |
| } |
| |
| TEST(Matcher, MatchesVirtualAsWrittenMethod) { |
| EXPECT_TRUE(matches("class A { virtual int f(); };" |
| "class B : public A { int f(); };", |
| cxxMethodDecl(isVirtualAsWritten(), hasName("::A::f")))); |
| EXPECT_TRUE( |
| notMatches("class A { virtual int f(); };" |
| "class B : public A { int f(); };", |
| cxxMethodDecl(isVirtualAsWritten(), hasName("::B::f")))); |
| } |
| |
| TEST(Matcher, MatchesPureMethod) { |
| EXPECT_TRUE(matches("class X { virtual int f() = 0; };", |
| cxxMethodDecl(isPure(), hasName("::X::f")))); |
| EXPECT_TRUE(notMatches("class X { int f(); };", cxxMethodDecl(isPure()))); |
| } |
| |
| TEST(Matcher, MatchesCopyAssignmentOperator) { |
| EXPECT_TRUE(matches("class X { X &operator=(X); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(X &); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(const X &); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(volatile X &); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(const volatile X &); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| EXPECT_TRUE(notMatches("class X { X &operator=(X &&); };", |
| cxxMethodDecl(isCopyAssignmentOperator()))); |
| } |
| |
| TEST(Matcher, MatchesMoveAssignmentOperator) { |
| EXPECT_TRUE(notMatches("class X { X &operator=(X); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(X &&); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(const X &&); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(volatile X &&); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| EXPECT_TRUE(matches("class X { X &operator=(const volatile X &&); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| EXPECT_TRUE(notMatches("class X { X &operator=(X &); };", |
| cxxMethodDecl(isMoveAssignmentOperator()))); |
| } |
| |
| TEST(Matcher, MatchesConstMethod) { |
| EXPECT_TRUE( |
| matches("struct A { void foo() const; };", cxxMethodDecl(isConst()))); |
| EXPECT_TRUE( |
| notMatches("struct A { void foo(); };", cxxMethodDecl(isConst()))); |
| } |
| |
| TEST(Matcher, MatchesOverridingMethod) { |
| EXPECT_TRUE(matches("class X { virtual int f(); }; " |
| "class Y : public X { int f(); };", |
| cxxMethodDecl(isOverride(), hasName("::Y::f")))); |
| EXPECT_TRUE(notMatches("class X { virtual int f(); }; " |
| "class Y : public X { int f(); };", |
| cxxMethodDecl(isOverride(), hasName("::X::f")))); |
| EXPECT_TRUE(notMatches("class X { int f(); }; " |
| "class Y : public X { int f(); };", |
| cxxMethodDecl(isOverride()))); |
| EXPECT_TRUE(notMatches("class X { int f(); int f(int); }; ", |
| cxxMethodDecl(isOverride()))); |
| EXPECT_TRUE( |
| matches("template <typename Base> struct Y : Base { void f() override;};", |
| cxxMethodDecl(isOverride(), hasName("::Y::f")))); |
| } |
| |
| TEST(Matcher, ConstructorArgument) { |
| StatementMatcher Constructor = cxxConstructExpr( |
| hasArgument(0, declRefExpr(to(varDecl(hasName("y")))))); |
| |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { int y; X x(y); }", |
| Constructor)); |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { int y; X x = X(y); }", |
| Constructor)); |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { int y; X x = y; }", |
| Constructor)); |
| EXPECT_TRUE( |
| notMatches("class X { public: X(int); }; void x() { int z; X x(z); }", |
| Constructor)); |
| |
| StatementMatcher WrongIndex = cxxConstructExpr( |
| hasArgument(42, declRefExpr(to(varDecl(hasName("y")))))); |
| EXPECT_TRUE( |
| notMatches("class X { public: X(int); }; void x() { int y; X x(y); }", |
| WrongIndex)); |
| } |
| |
| TEST(Matcher, ConstructorArgumentCount) { |
| StatementMatcher Constructor1Arg = cxxConstructExpr(argumentCountIs(1)); |
| |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { X x(0); }", |
| Constructor1Arg)); |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { X x = X(0); }", |
| Constructor1Arg)); |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { X x = 0; }", |
| Constructor1Arg)); |
| EXPECT_TRUE( |
| notMatches("class X { public: X(int, int); }; void x() { X x(0, 0); }", |
| Constructor1Arg)); |
| } |
| |
| TEST(Matcher, ConstructorListInitialization) { |
| StatementMatcher ConstructorListInit = |
| cxxConstructExpr(isListInitialization()); |
| |
| EXPECT_TRUE( |
| matches("class X { public: X(int); }; void x() { X x{0}; }", |
| ConstructorListInit)); |
| EXPECT_FALSE( |
| matches("class X { public: X(int); }; void x() { X x(0); }", |
| ConstructorListInit)); |
| } |
| |
| TEST(ConstructorDeclaration, IsImplicit) { |
| // This one doesn't match because the constructor is not added by the |
| // compiler (it is not needed). |
| EXPECT_TRUE(notMatches("class Foo { };", |
| cxxConstructorDecl(isImplicit()))); |
| // The compiler added the implicit default constructor. |
| EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();", |
| cxxConstructorDecl(isImplicit()))); |
| EXPECT_TRUE(matches("class Foo { Foo(){} };", |
| cxxConstructorDecl(unless(isImplicit())))); |
| // The compiler added an implicit assignment operator. |
| EXPECT_TRUE(matches("struct A { int x; } a = {0}, b = a; void f() { a = b; }", |
| cxxMethodDecl(isImplicit(), hasName("operator=")))); |
| } |
| |
| TEST(ConstructorDeclaration, IsExplicit) { |
| EXPECT_TRUE(matches("struct S { explicit S(int); };", |
| cxxConstructorDecl(isExplicit()))); |
| EXPECT_TRUE(notMatches("struct S { S(int); };", |
| cxxConstructorDecl(isExplicit()))); |
| } |
| |
| TEST(ConstructorDeclaration, Kinds) { |
| EXPECT_TRUE(matches( |
| "struct S { S(); };", |
| cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(notMatches( |
| "struct S { S(); };", |
| cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(notMatches( |
| "struct S { S(); };", |
| cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); |
| |
| EXPECT_TRUE(notMatches( |
| "struct S { S(const S&); };", |
| cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(matches( |
| "struct S { S(const S&); };", |
| cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(notMatches( |
| "struct S { S(const S&); };", |
| cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); |
| |
| EXPECT_TRUE(notMatches( |
| "struct S { S(S&&); };", |
| cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(notMatches( |
| "struct S { S(S&&); };", |
| cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); |
| EXPECT_TRUE(matches( |
| "struct S { S(S&&); };", |
| cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); |
| } |
| |
| TEST(ConstructorDeclaration, IsUserProvided) { |
| EXPECT_TRUE(notMatches("struct S { int X = 0; };", |
| cxxConstructorDecl(isUserProvided()))); |
| EXPECT_TRUE(notMatches("struct S { S() = default; };", |
| cxxConstructorDecl(isUserProvided()))); |
| EXPECT_TRUE(notMatches("struct S { S() = delete; };", |
| cxxConstructorDecl(isUserProvided()))); |
| EXPECT_TRUE( |
| matches("struct S { S(); };", cxxConstructorDecl(isUserProvided()))); |
| EXPECT_TRUE(matches("struct S { S(); }; S::S(){}", |
| cxxConstructorDecl(isUserProvided()))); |
| } |
| |
| TEST(ConstructorDeclaration, IsDelegatingConstructor) { |
| EXPECT_TRUE(notMatches("struct S { S(); S(int); int X; };", |
| cxxConstructorDecl(isDelegatingConstructor()))); |
| EXPECT_TRUE(notMatches("struct S { S(){} S(int X) : X(X) {} int X; };", |
| cxxConstructorDecl(isDelegatingConstructor()))); |
| EXPECT_TRUE(matches( |
| "struct S { S() : S(0) {} S(int X) : X(X) {} int X; };", |
| cxxConstructorDecl(isDelegatingConstructor(), parameterCountIs(0)))); |
| EXPECT_TRUE(matches( |
| "struct S { S(); S(int X); int X; }; S::S(int X) : S() {}", |
| cxxConstructorDecl(isDelegatingConstructor(), parameterCountIs(1)))); |
| } |
| |
| TEST(StringLiteral, HasSize) { |
| StatementMatcher Literal = stringLiteral(hasSize(4)); |
| EXPECT_TRUE(matches("const char *s = \"abcd\";", Literal)); |
| // wide string |
| EXPECT_TRUE(matches("const wchar_t *s = L\"abcd\";", Literal)); |
| // with escaped characters |
| EXPECT_TRUE(matches("const char *s = \"\x05\x06\x07\x08\";", Literal)); |
| // no matching, too small |
| EXPECT_TRUE(notMatches("const char *s = \"ab\";", Literal)); |
| } |
| |
| TEST(Matcher, HasNameSupportsNamespaces) { |
| EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("a::b::C")))); |
| EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("::a::b::C")))); |
| EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("b::C")))); |
| EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("c::b::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("a::c::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("a::b::A")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("::b::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("z::a::b::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", |
| recordDecl(hasName("a+b::C")))); |
| EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }", |
| recordDecl(hasName("C")))); |
| } |
| |
| TEST(Matcher, HasNameSupportsOuterClasses) { |
| EXPECT_TRUE( |
| matches("class A { class B { class C; }; };", |
| recordDecl(hasName("A::B::C")))); |
| EXPECT_TRUE( |
| matches("class A { class B { class C; }; };", |
| recordDecl(hasName("::A::B::C")))); |
| EXPECT_TRUE( |
| matches("class A { class B { class C; }; };", |
| recordDecl(hasName("B::C")))); |
| EXPECT_TRUE( |
| matches("class A { class B { class C; }; };", |
| recordDecl(hasName("C")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("c::B::C")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("A::c::C")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("A::B::A")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("::C")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("::B::C")))); |
| EXPECT_TRUE(notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("z::A::B::C")))); |
| EXPECT_TRUE( |
| notMatches("class A { class B { class C; }; };", |
| recordDecl(hasName("A+B::C")))); |
| } |
| |
| TEST(Matcher, HasNameSupportsInlinedNamespaces) { |
| std::string code = "namespace a { inline namespace b { class C; } }"; |
| EXPECT_TRUE(matches(code, recordDecl(hasName("a::b::C")))); |
| EXPECT_TRUE(matches(code, recordDecl(hasName("a::C")))); |
| EXPECT_TRUE(matches(code, recordDecl(hasName("::a::b::C")))); |
| EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C")))); |
| } |
| |
| TEST(Matcher, HasNameSupportsAnonymousNamespaces) { |
| std::string code = "namespace a { namespace { class C; } }"; |
| EXPECT_TRUE( |
| matches(code, recordDecl(hasName("a::(anonymous namespace)::C")))); |
| EXPECT_TRUE(matches(code, recordDecl(hasName("a::C")))); |
| EXPECT_TRUE( |
| matches(code, recordDecl(hasName("::a::(anonymous namespace)::C")))); |
| EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C")))); |
| } |
| |
| TEST(Matcher, HasNameSupportsAnonymousOuterClasses) { |
| EXPECT_TRUE(matches("class A { class { class C; } x; };", |
| recordDecl(hasName("A::(anonymous class)::C")))); |
| EXPECT_TRUE(matches("class A { class { class C; } x; };", |
| recordDecl(hasName("::A::(anonymous class)::C")))); |
| EXPECT_FALSE(matches("class A { class { class C; } x; };", |
| recordDecl(hasName("::A::C")))); |
| EXPECT_TRUE(matches("class A { struct { class C; } x; };", |
| recordDecl(hasName("A::(anonymous struct)::C")))); |
| EXPECT_TRUE(matches("class A { struct { class C; } x; };", |
| recordDecl(hasName("::A::(anonymous struct)::C")))); |
| EXPECT_FALSE(matches("class A { struct { class C; } x; };", |
| recordDecl(hasName("::A::C")))); |
| } |
| |
| TEST(Matcher, HasNameSupportsFunctionScope) { |
| std::string code = |
| "namespace a { void F(int a) { struct S { int m; }; int i; } }"; |
| EXPECT_TRUE(matches(code, varDecl(hasName("i")))); |
| EXPECT_FALSE(matches(code, varDecl(hasName("F()::i")))); |
| |
| EXPECT_TRUE(matches(code, fieldDecl(hasName("m")))); |
| EXPECT_TRUE(matches(code, fieldDecl(hasName("S::m")))); |
| EXPECT_TRUE(matches(code, fieldDecl(hasName("F(int)::S::m")))); |
| EXPECT_TRUE(matches(code, fieldDecl(hasName("a::F(int)::S::m")))); |
| EXPECT_TRUE(matches(code, fieldDecl(hasName("::a::F(int)::S::m")))); |
| } |
| |
| TEST(Matcher, HasAnyName) { |
| const std::string Code = "namespace a { namespace b { class C; } }"; |
| |
| EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "a::b::C")))); |
| EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("a::b::C", "XX")))); |
| EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX::C", "a::b::C")))); |
| EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "C")))); |
| |
| EXPECT_TRUE(notMatches(Code, recordDecl(hasAnyName("::C", "::b::C")))); |
| EXPECT_TRUE( |
| matches(Code, recordDecl(hasAnyName("::C", "::b::C", "::a::b::C")))); |
| |
| std::vector<StringRef> Names = {"::C", "::b::C", "::a::b::C"}; |
| EXPECT_TRUE(matches(Code, recordDecl(hasAnyName(Names)))); |
| } |
| |
| TEST(Matcher, IsDefinition) { |
| DeclarationMatcher DefinitionOfClassA = |
| recordDecl(hasName("A"), isDefinition()); |
| EXPECT_TRUE(matches("class A {};", DefinitionOfClassA)); |
| EXPECT_TRUE(notMatches("class A;", DefinitionOfClassA)); |
| |
| DeclarationMatcher DefinitionOfVariableA = |
| varDecl(hasName("a"), isDefinition()); |
| EXPECT_TRUE(matches("int a;", DefinitionOfVariableA)); |
| EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA)); |
| |
| DeclarationMatcher DefinitionOfMethodA = |
| cxxMethodDecl(hasName("a"), isDefinition()); |
| EXPECT_TRUE(matches("class A { void a() {} };", DefinitionOfMethodA)); |
| EXPECT_TRUE(notMatches("class A { void a(); };", DefinitionOfMethodA)); |
| |
| DeclarationMatcher DefinitionOfObjCMethodA = |
| objcMethodDecl(hasName("a"), isDefinition()); |
| EXPECT_TRUE(matchesObjC("@interface A @end " |
| "@implementation A; -(void)a {} @end", |
| DefinitionOfObjCMethodA)); |
| EXPECT_TRUE(notMatchesObjC("@interface A; - (void)a; @end", |
| DefinitionOfObjCMethodA)); |
| } |
| |
| TEST(Matcher, HandlesNullQualTypes) { |
| // FIXME: Add a Type matcher so we can replace uses of this |
| // variable with Type(True()) |
| const TypeMatcher AnyType = anything(); |
| |
| // We don't really care whether this matcher succeeds; we're testing that |
| // it completes without crashing. |
| EXPECT_TRUE(matches( |
| "struct A { };" |
| "template <typename T>" |
| "void f(T t) {" |
| " T local_t(t /* this becomes a null QualType in the AST */);" |
| "}" |
| "void g() {" |
| " f(0);" |
| "}", |
| expr(hasType(TypeMatcher( |
| anyOf( |
| TypeMatcher(hasDeclaration(anything())), |
| pointsTo(AnyType), |
| references(AnyType) |
| // Other QualType matchers should go here. |
| )))))); |
| } |
| |
| TEST(ObjCIvarRefExprMatcher, IvarExpr) { |
| std::string ObjCString = |
| "@interface A @end " |
| "@implementation A { A *x; } - (void) func { x = 0; } @end"; |
| EXPECT_TRUE(matchesObjC(ObjCString, objcIvarRefExpr())); |
| EXPECT_TRUE(matchesObjC(ObjCString, objcIvarRefExpr( |
| hasDeclaration(namedDecl(hasName("x")))))); |
| EXPECT_FALSE(matchesObjC(ObjCString, objcIvarRefExpr( |
| hasDeclaration(namedDecl(hasName("y")))))); |
| } |
| |
| TEST(StatementCountIs, FindsNoStatementsInAnEmptyCompoundStatement) { |
| EXPECT_TRUE(matches("void f() { }", |
| compoundStmt(statementCountIs(0)))); |
| EXPECT_TRUE(notMatches("void f() {}", |
| compoundStmt(statementCountIs(1)))); |
| } |
| |
| TEST(StatementCountIs, AppearsToMatchOnlyOneCount) { |
| EXPECT_TRUE(matches("void f() { 1; }", |
| compoundStmt(statementCountIs(1)))); |
| EXPECT_TRUE(notMatches("void f() { 1; }", |
| compoundStmt(statementCountIs(0)))); |
| EXPECT_TRUE(notMatches("void f() { 1; }", |
| compoundStmt(statementCountIs(2)))); |
| } |
| |
| TEST(StatementCountIs, WorksWithMultipleStatements) { |
| EXPECT_TRUE(matches("void f() { 1; 2; 3; }", |
| compoundStmt(statementCountIs(3)))); |
| } |
| |
| TEST(StatementCountIs, WorksWithNestedCompoundStatements) { |
| EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", |
| compoundStmt(statementCountIs(1)))); |
| EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", |
| compoundStmt(statementCountIs(2)))); |
| EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }", |
| compoundStmt(statementCountIs(3)))); |
| EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", |
| compoundStmt(statementCountIs(4)))); |
| } |
| |
| TEST(Member, WorksInSimplestCase) { |
| EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);", |
| memberExpr(member(hasName("first"))))); |
| } |
| |
| TEST(Member, DoesNotMatchTheBaseExpression) { |
| // Don't pick out the wrong part of the member expression, this should |
| // be checking the member (name) only. |
| EXPECT_TRUE(notMatches("struct { int i; } first; int i(first.i);", |
| memberExpr(member(hasName("first"))))); |
| } |
| |
| TEST(Member, MatchesInMemberFunctionCall) { |
| EXPECT_TRUE(matches("void f() {" |
| " struct { void first() {}; } s;" |
| " s.first();" |
| "};", |
| memberExpr(member(hasName("first"))))); |
| } |
| |
| TEST(Member, MatchesMember) { |
| EXPECT_TRUE(matches( |
| "struct A { int i; }; void f() { A a; a.i = 2; }", |
| memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); |
| EXPECT_TRUE(notMatches( |
| "struct A { float f; }; void f() { A a; a.f = 2.0f; }", |
| memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); |
| } |
| |
| TEST(Member, BitFields) { |
| EXPECT_TRUE(matches("class C { int a : 2; int b; };", |
| fieldDecl(isBitField(), hasName("a")))); |
| EXPECT_TRUE(notMatches("class C { int a : 2; int b; };", |
| fieldDecl(isBitField(), hasName("b")))); |
| EXPECT_TRUE(matches("class C { int a : 2; int b : 4; };", |
| fieldDecl(isBitField(), hasBitWidth(2), hasName("a")))); |
| } |
| |
| TEST(Member, InClassInitializer) { |
| EXPECT_TRUE( |
| matches("class C { int a = 2; int b; };", |
| fieldDecl(hasInClassInitializer(integerLiteral(equals(2))), |
| hasName("a")))); |
| EXPECT_TRUE( |
| notMatches("class C { int a = 2; int b; };", |
| fieldDecl(hasInClassInitializer(anything()), hasName("b")))); |
| } |
| |
| TEST(Member, UnderstandsAccess) { |
| EXPECT_TRUE(matches( |
| "struct A { int i; };", fieldDecl(isPublic(), hasName("i")))); |
| EXPECT_TRUE(notMatches( |
| "struct A { int i; };", fieldDecl(isProtected(), hasName("i")))); |
| EXPECT_TRUE(notMatches( |
| "struct A { int i; };", fieldDecl(isPrivate(), hasName("i")))); |
| |
| EXPECT_TRUE(notMatches( |
| "class A { int i; };", fieldDecl(isPublic(), hasName("i")))); |
| EXPECT_TRUE(notMatches( |
| "class A { int i; };", fieldDecl(isProtected(), hasName("i")))); |
| EXPECT_TRUE(matches( |
| "class A { int i; };", fieldDecl(isPrivate(), hasName("i")))); |
| |
| EXPECT_TRUE(notMatches( |
| "class A { protected: int i; };", fieldDecl(isPublic(), hasName("i")))); |
| EXPECT_TRUE(matches("class A { protected: int i; };", |
| fieldDecl(isProtected(), hasName("i")))); |
| EXPECT_TRUE(notMatches( |
| "class A { protected: int i; };", fieldDecl(isPrivate(), hasName("i")))); |
| |
| // Non-member decls have the AccessSpecifier AS_none and thus aren't matched. |
| EXPECT_TRUE(notMatches("int i;", varDecl(isPublic(), hasName("i")))); |
| EXPECT_TRUE(notMatches("int i;", varDecl(isProtected(), hasName("i")))); |
| EXPECT_TRUE(notMatches("int i;", varDecl(isPrivate(), hasName("i")))); |
| } |
| |
| TEST(hasDynamicExceptionSpec, MatchesDynamicExceptionSpecifications) { |
| EXPECT_TRUE(notMatches("void f();", functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void g() noexcept;", |
| functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void h() noexcept(true);", |
| functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void i() noexcept(false);", |
| functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void j() throw();", functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void k() throw(int);", functionDecl(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void l() throw(...);", functionDecl(hasDynamicExceptionSpec()))); |
| |
| EXPECT_TRUE(notMatches("void f();", functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void g() noexcept;", |
| functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void h() noexcept(true);", |
| functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE(notMatches("void i() noexcept(false);", |
| functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void j() throw();", functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void k() throw(int);", functionProtoType(hasDynamicExceptionSpec()))); |
| EXPECT_TRUE( |
| matches("void l() throw(...);", functionProtoType(hasDynamicExceptionSpec()))); |
| } |
| |
| TEST(HasObjectExpression, DoesNotMatchMember) { |
| EXPECT_TRUE(notMatches( |
| "class X {}; struct Z { X m; }; void f(Z z) { z.m; }", |
| memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); |
| } |
| |
| TEST(HasObjectExpression, MatchesBaseOfVariable) { |
| EXPECT_TRUE(matches( |
| "struct X { int m; }; void f(X x) { x.m; }", |
| memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); |
| EXPECT_TRUE(matches( |
| "struct X { int m; }; void f(X* x) { x->m; }", |
| memberExpr(hasObjectExpression( |
| hasType(pointsTo(recordDecl(hasName("X")))))))); |
| } |
| |
| TEST(HasObjectExpression, |
| MatchesObjectExpressionOfImplicitlyFormedMemberExpression) { |
| EXPECT_TRUE(matches( |
| "class X {}; struct S { X m; void f() { this->m; } };", |
| memberExpr(hasObjectExpression( |
| hasType(pointsTo(recordDecl(hasName("S")))))))); |
| EXPECT_TRUE(matches( |
| "class X {}; struct S { X m; void f() { m; } };", |
| memberExpr(hasObjectExpression( |
| hasType(pointsTo(recordDecl(hasName("S")))))))); |
| } |
| |
| TEST(Field, DoesNotMatchNonFieldMembers) { |
| EXPECT_TRUE(notMatches("class X { void m(); };", fieldDecl(hasName("m")))); |
| EXPECT_TRUE(notMatches("class X { class m {}; };", fieldDecl(hasName("m")))); |
| EXPECT_TRUE(notMatches("class X { enum { m }; };", fieldDecl(hasName("m")))); |
| EXPECT_TRUE(notMatches("class X { enum m {}; };", fieldDecl(hasName("m")))); |
| } |
| |
| TEST(Field, MatchesField) { |
| EXPECT_TRUE(matches("class X { int m; };", fieldDecl(hasName("m")))); |
| } |
| |
| TEST(IsVolatileQualified, QualifiersMatch) { |
| EXPECT_TRUE(matches("volatile int i = 42;", |
| varDecl(hasType(isVolatileQualified())))); |
| EXPECT_TRUE(notMatches("volatile int *i;", |
| varDecl(hasType(isVolatileQualified())))); |
| EXPECT_TRUE(matches("typedef volatile int v_int; v_int i = 42;", |
| varDecl(hasType(isVolatileQualified())))); |
| } |
| |
| TEST(IsConstQualified, MatchesConstInt) { |
| EXPECT_TRUE(matches("const int i = 42;", |
| varDecl(hasType(isConstQualified())))); |
| } |
| |
| TEST(IsConstQualified, MatchesConstPointer) { |
| EXPECT_TRUE(matches("int i = 42; int* const p(&i);", |
| varDecl(hasType(isConstQualified())))); |
| } |
| |
| TEST(IsConstQualified, MatchesThroughTypedef) { |
| EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;", |
| varDecl(hasType(isConstQualified())))); |
| EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p(0);", |
| varDecl(hasType(isConstQualified())))); |
| } |
| |
| TEST(IsConstQualified, DoesNotMatchInappropriately) { |
| EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;", |
| varDecl(hasType(isConstQualified())))); |
| EXPECT_TRUE(notMatches("int const* p;", |
| varDecl(hasType(isConstQualified())))); |
| } |
| |
| TEST(DeclCount, DeclCountIsCorrect) { |
| EXPECT_TRUE(matches("void f() {int i,j;}", |
| declStmt(declCountIs(2)))); |
| EXPECT_TRUE(notMatches("void f() {int i,j; int k;}", |
| declStmt(declCountIs(3)))); |
| EXPECT_TRUE(notMatches("void f() {int i,j, k, l;}", |
| declStmt(declCountIs(3)))); |
| } |
| |
| |
| TEST(EachOf, TriggersForEachMatch) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { int a; int b; };", |
| recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), |
| has(fieldDecl(hasName("b")).bind("v")))), |
| llvm::make_unique<VerifyIdIsBoundTo<FieldDecl>>("v", 2))); |
| } |
| |
| TEST(EachOf, BehavesLikeAnyOfUnlessBothMatch) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { int a; int c; };", |
| recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), |
| has(fieldDecl(hasName("b")).bind("v")))), |
| llvm::make_unique<VerifyIdIsBoundTo<FieldDecl>>("v", 1))); |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { int c; int b; };", |
| recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), |
| has(fieldDecl(hasName("b")).bind("v")))), |
| llvm::make_unique<VerifyIdIsBoundTo<FieldDecl>>("v", 1))); |
| EXPECT_TRUE(notMatches( |
| "class A { int c; int d; };", |
| recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), |
| has(fieldDecl(hasName("b")).bind("v")))))); |
| } |
| |
| TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) { |
| // Make sure that we can both match the class by name (::X) and by the type |
| // the template was instantiated with (via a field). |
| |
| EXPECT_TRUE(matches( |
| "template <typename T> class X {}; class A {}; X<A> x;", |
| cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); |
| |
| EXPECT_TRUE(matches( |
| "template <typename T> class X { T t; }; class A {}; X<A> x;", |
| cxxRecordDecl(isTemplateInstantiation(), hasDescendant( |
| fieldDecl(hasType(recordDecl(hasName("A")))))))); |
| } |
| |
| TEST(IsTemplateInstantiation, MatchesImplicitFunctionTemplateInstantiation) { |
| EXPECT_TRUE(matches( |
| "template <typename T> void f(T t) {} class A {}; void g() { f(A()); }", |
| functionDecl(hasParameter(0, hasType(recordDecl(hasName("A")))), |
| isTemplateInstantiation()))); |
| } |
| |
| TEST(IsTemplateInstantiation, MatchesExplicitClassTemplateInstantiation) { |
| EXPECT_TRUE(matches( |
| "template <typename T> class X { T t; }; class A {};" |
| "template class X<A>;", |
| cxxRecordDecl(isTemplateInstantiation(), hasDescendant( |
| fieldDecl(hasType(recordDecl(hasName("A")))))))); |
| |
| // Make sure that we match the instantiation instead of the template |
| // definition by checking whether the member function is present. |
| EXPECT_TRUE( |
| matches("template <typename T> class X { void f() { T t; } };" |
| "extern template class X<int>;", |
| cxxRecordDecl(isTemplateInstantiation(), |
| unless(hasDescendant(varDecl(hasName("t"))))))); |
| } |
| |
| TEST(IsTemplateInstantiation, |
| MatchesInstantiationOfPartiallySpecializedClassTemplate) { |
| EXPECT_TRUE(matches( |
| "template <typename T> class X {};" |
| "template <typename T> class X<T*> {}; class A {}; X<A*> x;", |
| cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); |
| } |
| |
| TEST(IsTemplateInstantiation, |
| MatchesInstantiationOfClassTemplateNestedInNonTemplate) { |
| EXPECT_TRUE(matches( |
| "class A {};" |
| "class X {" |
| " template <typename U> class Y { U u; };" |
| " Y<A> y;" |
| "};", |
| cxxRecordDecl(hasName("::X::Y"), isTemplateInstantiation()))); |
| } |
| |
| TEST(IsTemplateInstantiation, DoesNotMatchInstantiationsInsideOfInstantiation) { |
| // FIXME: Figure out whether this makes sense. It doesn't affect the |
| // normal use case as long as the uppermost instantiation always is marked |
| // as template instantiation, but it might be confusing as a predicate. |
| EXPECT_TRUE(matches( |
| "class A {};" |
| "template <typename T> class X {" |
| " template <typename U> class Y { U u; };" |
| " Y<T> y;" |
| "}; X<A> x;", |
| cxxRecordDecl(hasName("::X<A>::Y"), unless(isTemplateInstantiation())))); |
| } |
| |
| TEST(IsTemplateInstantiation, DoesNotMatchExplicitClassTemplateSpecialization) { |
| EXPECT_TRUE(notMatches( |
| "template <typename T> class X {}; class A {};" |
| "template <> class X<A> {}; X<A> x;", |
| cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); |
| } |
| |
| TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) { |
| EXPECT_TRUE(notMatches( |
| "class A {}; class Y { A a; };", |
| cxxRecordDecl(isTemplateInstantiation()))); |
| } |
| |
| TEST(IsInstantiated, MatchesInstantiation) { |
| EXPECT_TRUE( |
| matches("template<typename T> class A { T i; }; class Y { A<int> a; };", |
| cxxRecordDecl(isInstantiated()))); |
| } |
| |
| TEST(IsInstantiated, NotMatchesDefinition) { |
| EXPECT_TRUE(notMatches("template<typename T> class A { T i; };", |
| cxxRecordDecl(isInstantiated()))); |
| } |
| |
| TEST(IsInTemplateInstantiation, MatchesInstantiationStmt) { |
| EXPECT_TRUE(matches("template<typename T> struct A { A() { T i; } };" |
| "class Y { A<int> a; }; Y y;", |
| declStmt(isInTemplateInstantiation()))); |
| } |
| |
| TEST(IsInTemplateInstantiation, NotMatchesDefinitionStmt) { |
| EXPECT_TRUE(notMatches("template<typename T> struct A { void x() { T i; } };", |
| declStmt(isInTemplateInstantiation()))); |
| } |
| |
| TEST(IsInstantiated, MatchesFunctionInstantiation) { |
| EXPECT_TRUE( |
| matches("template<typename T> void A(T t) { T i; } void x() { A(0); }", |
| functionDecl(isInstantiated()))); |
| } |
| |
| TEST(IsInstantiated, NotMatchesFunctionDefinition) { |
| EXPECT_TRUE(notMatches("template<typename T> void A(T t) { T i; }", |
| varDecl(isInstantiated()))); |
| } |
| |
| TEST(IsInTemplateInstantiation, MatchesFunctionInstantiationStmt) { |
| EXPECT_TRUE( |
| matches("template<typename T> void A(T t) { T i; } void x() { A(0); }", |
| declStmt(isInTemplateInstantiation()))); |
| } |
| |
| TEST(IsInTemplateInstantiation, NotMatchesFunctionDefinitionStmt) { |
| EXPECT_TRUE(notMatches("template<typename T> void A(T t) { T i; }", |
| declStmt(isInTemplateInstantiation()))); |
| } |
| |
| TEST(IsInTemplateInstantiation, Sharing) { |
| auto Matcher = binaryOperator(unless(isInTemplateInstantiation())); |
| // FIXME: Node sharing is an implementation detail, exposing it is ugly |
| // and makes the matcher behave in non-obvious ways. |
| EXPECT_TRUE(notMatches( |
| "int j; template<typename T> void A(T t) { j += 42; } void x() { A(0); }", |
| Matcher)); |
| EXPECT_TRUE(matches( |
| "int j; template<typename T> void A(T t) { j += t; } void x() { A(0); }", |
| Matcher)); |
| } |
| |
| TEST(IsExplicitTemplateSpecialization, |
| DoesNotMatchPrimaryTemplate) { |
| EXPECT_TRUE(notMatches( |
| "template <typename T> class X {};", |
| cxxRecordDecl(isExplicitTemplateSpecialization()))); |
| EXPECT_TRUE(notMatches( |
| "template <typename T> void f(T t);", |
| functionDecl(isExplicitTemplateSpecialization()))); |
| } |
| |
| TEST(IsExplicitTemplateSpecialization, |
| DoesNotMatchExplicitTemplateInstantiations) { |
| EXPECT_TRUE(notMatches( |
| "template <typename T> class X {};" |
| "template class X<int>; extern template class X<long>;", |
| cxxRecordDecl(isExplicitTemplateSpecialization()))); |
| EXPECT_TRUE(notMatches( |
| "template <typename T> void f(T t) {}" |
| "template void f(int t); extern template void f(long t);", |
| functionDecl(isExplicitTemplateSpecialization()))); |
| } |
| |
| TEST(IsExplicitTemplateSpecialization, |
| DoesNotMatchImplicitTemplateInstantiations) { |
| EXPECT_TRUE(notMatches( |
| "template <typename T> class X {}; X<int> x;", |
| cxxRecordDecl(isExplicitTemplateSpecialization()))); |
| EXPECT_TRUE(notMatches( |
| "template <typename T> void f(T t); void g() { f(10); }", |
| functionDecl(isExplicitTemplateSpecialization()))); |
| } |
| |
| TEST(IsExplicitTemplateSpecialization, |
| MatchesExplicitTemplateSpecializations) { |
| EXPECT_TRUE(matches( |
| "template <typename T> class X {};" |
| "template<> class X<int> {};", |
| cxxRecordDecl(isExplicitTemplateSpecialization()))); |
| EXPECT_TRUE(matches( |
| "template <typename T> void f(T t) {}" |
| "template<> void f(int t) {}", |
| functionDecl(isExplicitTemplateSpecialization()))); |
| } |
| |
| TEST(TypeMatching, MatchesNoReturn) { |
| EXPECT_TRUE(notMatches("void func();", functionDecl(isNoReturn()))); |
| EXPECT_TRUE(notMatches("void func() {}", functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(notMatchesC("void func();", functionDecl(isNoReturn()))); |
| EXPECT_TRUE(notMatchesC("void func() {}", functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE( |
| notMatches("struct S { void func(); };", functionDecl(isNoReturn()))); |
| EXPECT_TRUE( |
| notMatches("struct S { void func() {} };", functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(notMatches("struct S { static void func(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(notMatches("struct S { static void func() {} };", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(notMatches("struct S { S(); };", functionDecl(isNoReturn()))); |
| EXPECT_TRUE(notMatches("struct S { S() {} };", functionDecl(isNoReturn()))); |
| |
| // --- |
| |
| EXPECT_TRUE(matches("[[noreturn]] void func();", functionDecl(isNoReturn()))); |
| EXPECT_TRUE( |
| matches("[[noreturn]] void func() {}", functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(matches("struct S { [[noreturn]] void func(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("struct S { [[noreturn]] void func() {} };", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(matches("struct S { [[noreturn]] static void func(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("struct S { [[noreturn]] static void func() {} };", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE( |
| matches("struct S { [[noreturn]] S(); };", functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("struct S { [[noreturn]] S() {} };", |
| functionDecl(isNoReturn()))); |
| |
| // --- |
| |
| EXPECT_TRUE(matches("__attribute__((noreturn)) void func();", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("__attribute__((noreturn)) void func() {}", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) void func(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) void func() {} };", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE( |
| matches("struct S { __attribute__((noreturn)) static void func(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE( |
| matches("struct S { __attribute__((noreturn)) static void func() {} };", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) S(); };", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) S() {} };", |
| functionDecl(isNoReturn()))); |
| |
| // --- |
| |
| EXPECT_TRUE(matchesC("__attribute__((noreturn)) void func();", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matchesC("__attribute__((noreturn)) void func() {}", |
| functionDecl(isNoReturn()))); |
| |
| EXPECT_TRUE(matchesC("_Noreturn void func();", |
| functionDecl(isNoReturn()))); |
| EXPECT_TRUE(matchesC("_Noreturn void func() {}", |
| functionDecl(isNoReturn()))); |
| } |
| |
| TEST(TypeMatching, MatchesBool) { |
| EXPECT_TRUE(matches("struct S { bool func(); };", |
| cxxMethodDecl(returns(booleanType())))); |
| EXPECT_TRUE(notMatches("struct S { void func(); };", |
| cxxMethodDecl(returns(booleanType())))); |
| } |
| |
| TEST(TypeMatching, MatchesVoid) { |
| EXPECT_TRUE(matches("struct S { void func(); };", |
| cxxMethodDecl(returns(voidType())))); |
| } |
| |
| TEST(TypeMatching, MatchesRealFloats) { |
| EXPECT_TRUE(matches("struct S { float func(); };", |
| cxxMethodDecl(returns(realFloatingPointType())))); |
| EXPECT_TRUE(notMatches("struct S { int func(); };", |
| cxxMethodDecl(returns(realFloatingPointType())))); |
| EXPECT_TRUE(matches("struct S { long double func(); };", |
| cxxMethodDecl(returns(realFloatingPointType())))); |
| } |
| |
| TEST(TypeMatching, MatchesArrayTypes) { |
| EXPECT_TRUE(matches("int a[] = {2,3};", arrayType())); |
| EXPECT_TRUE(matches("int a[42];", arrayType())); |
| EXPECT_TRUE(matches("void f(int b) { int a[b]; }", arrayType())); |
| |
| EXPECT_TRUE(notMatches("struct A {}; A a[7];", |
| arrayType(hasElementType(builtinType())))); |
| |
| EXPECT_TRUE(matches( |
| "int const a[] = { 2, 3 };", |
| qualType(arrayType(hasElementType(builtinType()))))); |
| EXPECT_TRUE(matches( |
| "int const a[] = { 2, 3 };", |
| qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); |
| EXPECT_TRUE(matches( |
| "typedef const int T; T x[] = { 1, 2 };", |
| qualType(isConstQualified(), arrayType()))); |
| |
| EXPECT_TRUE(notMatches( |
| "int a[] = { 2, 3 };", |
| qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); |
| EXPECT_TRUE(notMatches( |
| "int a[] = { 2, 3 };", |
| qualType(arrayType(hasElementType(isConstQualified(), builtinType()))))); |
| EXPECT_TRUE(notMatches( |
| "int const a[] = { 2, 3 };", |
| qualType(arrayType(hasElementType(builtinType())), |
| unless(isConstQualified())))); |
| |
| EXPECT_TRUE(matches("int a[2];", |
| constantArrayType(hasElementType(builtinType())))); |
| EXPECT_TRUE(matches("const int a = 0;", qualType(isInteger()))); |
| } |
| |
| TEST(TypeMatching, DecayedType) { |
| EXPECT_TRUE(matches("void f(int i[]);", valueDecl(hasType(decayedType(hasDecayedType(pointerType())))))); |
| EXPECT_TRUE(notMatches("int i[7];", decayedType())); |
| } |
| |
| TEST(TypeMatching, MatchesComplexTypes) { |
| EXPECT_TRUE(matches("_Complex float f;", complexType())); |
| EXPECT_TRUE(matches( |
| "_Complex float f;", |
| complexType(hasElementType(builtinType())))); |
| EXPECT_TRUE(notMatches( |
| "_Complex float f;", |
| complexType(hasElementType(isInteger())))); |
| } |
| |
| TEST(NS, Anonymous) { |
| EXPECT_TRUE(notMatches("namespace N {}", namespaceDecl(isAnonymous()))); |
| EXPECT_TRUE(matches("namespace {}", namespaceDecl(isAnonymous()))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, QualType) { |
| EXPECT_TRUE(matches( |
| "int i = 1;", varDecl(hasType(qualType().bind("type")), |
| hasInitializer(ignoringParenImpCasts( |
| hasType(qualType(equalsBoundNode("type")))))))); |
| EXPECT_TRUE(notMatches("int i = 1.f;", |
| varDecl(hasType(qualType().bind("type")), |
| hasInitializer(ignoringParenImpCasts(hasType( |
| qualType(equalsBoundNode("type")))))))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, NonMatchingTypes) { |
| EXPECT_TRUE(notMatches( |
| "int i = 1;", varDecl(namedDecl(hasName("i")).bind("name"), |
| hasInitializer(ignoringParenImpCasts( |
| hasType(qualType(equalsBoundNode("type")))))))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, Stmt) { |
| EXPECT_TRUE( |
| matches("void f() { if(true) {} }", |
| stmt(allOf(ifStmt().bind("if"), |
| hasParent(stmt(has(stmt(equalsBoundNode("if"))))))))); |
| |
| EXPECT_TRUE(notMatches( |
| "void f() { if(true) { if (true) {} } }", |
| stmt(allOf(ifStmt().bind("if"), has(stmt(equalsBoundNode("if"))))))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, Decl) { |
| EXPECT_TRUE(matches( |
| "class X { class Y {}; };", |
| decl(allOf(recordDecl(hasName("::X::Y")).bind("record"), |
| hasParent(decl(has(decl(equalsBoundNode("record"))))))))); |
| |
| EXPECT_TRUE(notMatches("class X { class Y {}; };", |
| decl(allOf(recordDecl(hasName("::X")).bind("record"), |
| has(decl(equalsBoundNode("record"))))))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, Type) { |
| EXPECT_TRUE(matches( |
| "class X { int a; int b; };", |
| recordDecl( |
| has(fieldDecl(hasName("a"), hasType(type().bind("t")))), |
| has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t")))))))); |
| |
| EXPECT_TRUE(notMatches( |
| "class X { int a; double b; };", |
| recordDecl( |
| has(fieldDecl(hasName("a"), hasType(type().bind("t")))), |
| has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t")))))))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, UsingForEachDescendant) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "int f() {" |
| " if (1) {" |
| " int i = 9;" |
| " }" |
| " int j = 10;" |
| " {" |
| " float k = 9.0;" |
| " }" |
| " return 0;" |
| "}", |
| // Look for variable declarations within functions whose type is the same |
| // as the function return type. |
| functionDecl(returns(qualType().bind("type")), |
| forEachDescendant(varDecl(hasType( |
| qualType(equalsBoundNode("type")))).bind("decl"))), |
| // Only i and j should match, not k. |
| llvm::make_unique<VerifyIdIsBoundTo<VarDecl>>("decl", 2))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, FiltersMatchedCombinations) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "void f() {" |
| " int x;" |
| " double d;" |
| " x = d + x - d + x;" |
| "}", |
| functionDecl( |
| hasName("f"), forEachDescendant(varDecl().bind("d")), |
| forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d")))))), |
| llvm::make_unique<VerifyIdIsBoundTo<VarDecl>>("d", 5))); |
| } |
| |
| TEST(EqualsBoundNodeMatcher, UnlessDescendantsOfAncestorsMatch) { |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "struct StringRef { int size() const; const char* data() const; };" |
| "void f(StringRef v) {" |
| " v.data();" |
| "}", |
| cxxMemberCallExpr( |
| callee(cxxMethodDecl(hasName("data"))), |
| on(declRefExpr(to( |
| varDecl(hasType(recordDecl(hasName("StringRef")))).bind("var")))), |
| unless(hasAncestor(stmt(hasDescendant(cxxMemberCallExpr( |
| callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))), |
| on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) |
| .bind("data"), |
| llvm::make_unique<VerifyIdIsBoundTo<Expr>>("data", 1))); |
| |
| EXPECT_FALSE(matches( |
| "struct StringRef { int size() const; const char* data() const; };" |
| "void f(StringRef v) {" |
| " v.data();" |
| " v.size();" |
| "}", |
| cxxMemberCallExpr( |
| callee(cxxMethodDecl(hasName("data"))), |
| on(declRefExpr(to( |
| varDecl(hasType(recordDecl(hasName("StringRef")))).bind("var")))), |
| unless(hasAncestor(stmt(hasDescendant(cxxMemberCallExpr( |
| callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))), |
| on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) |
| .bind("data"))); |
| } |
| |
| TEST(NullPointerConstants, Basic) { |
| EXPECT_TRUE(matches("#define NULL ((void *)0)\n" |
| "void *v1 = NULL;", expr(nullPointerConstant()))); |
| EXPECT_TRUE(matches("void *v2 = nullptr;", expr(nullPointerConstant()))); |
| EXPECT_TRUE(matches("void *v3 = __null;", expr(nullPointerConstant()))); |
| EXPECT_TRUE(matches("char *cp = (char *)0;", expr(nullPointerConstant()))); |
| EXPECT_TRUE(matches("int *ip = 0;", expr(nullPointerConstant()))); |
| EXPECT_TRUE(notMatches("int i = 0;", expr(nullPointerConstant()))); |
| } |
| |
| TEST(HasExternalFormalLinkage, Basic) { |
| EXPECT_TRUE(matches("int a = 0;", namedDecl(hasExternalFormalLinkage()))); |
| EXPECT_TRUE( |
| notMatches("static int a = 0;", namedDecl(hasExternalFormalLinkage()))); |
| EXPECT_TRUE(notMatches("static void f(void) { int a = 0; }", |
| namedDecl(hasExternalFormalLinkage()))); |
| EXPECT_TRUE(matches("void f(void) { int a = 0; }", |
| namedDecl(hasExternalFormalLinkage()))); |
| |
| // Despite having internal semantic linkage, the anonymous namespace member |
| // has external linkage because the member has a unique name in all |
| // translation units. |
| EXPECT_TRUE(matches("namespace { int a = 0; }", |
| namedDecl(hasExternalFormalLinkage()))); |
| } |
| |
| TEST(HasDefaultArgument, Basic) { |
| EXPECT_TRUE(matches("void x(int val = 0) {}", |
| parmVarDecl(hasDefaultArgument()))); |
| EXPECT_TRUE(notMatches("void x(int val) {}", |
| parmVarDecl(hasDefaultArgument()))); |
| } |
| |
| TEST(IsArray, Basic) { |
| EXPECT_TRUE(matches("struct MyClass {}; MyClass *p1 = new MyClass[10];", |
| cxxNewExpr(isArray()))); |
| } |
| |
| TEST(HasArraySize, Basic) { |
| EXPECT_TRUE(matches("struct MyClass {}; MyClass *p1 = new MyClass[10];", |
| cxxNewExpr(hasArraySize(integerLiteral(equals(10)))))); |
| } |
| |
| TEST(HasDefinition, MatchesStructDefinition) { |
| EXPECT_TRUE(matches("struct x {};", |
| cxxRecordDecl(hasDefinition()))); |
| EXPECT_TRUE(notMatches("struct x;", |
| cxxRecordDecl(hasDefinition()))); |
| } |
| |
| TEST(HasDefinition, MatchesClassDefinition) { |
| EXPECT_TRUE(matches("class x {};", |
| cxxRecordDecl(hasDefinition()))); |
| EXPECT_TRUE(notMatches("class x;", |
| cxxRecordDecl(hasDefinition()))); |
| } |
| |
| TEST(HasDefinition, MatchesUnionDefinition) { |
| EXPECT_TRUE(matches("union x {};", |
| cxxRecordDecl(hasDefinition()))); |
| EXPECT_TRUE(notMatches("union x;", |
| cxxRecordDecl(hasDefinition()))); |
| } |
| |
| TEST(IsScopedEnum, MatchesScopedEnum) { |
| EXPECT_TRUE(matches("enum class X {};", enumDecl(isScoped()))); |
| EXPECT_TRUE(notMatches("enum X {};", enumDecl(isScoped()))); |
| } |
| |
| TEST(HasTrailingReturn, MatchesTrailingReturn) { |
| EXPECT_TRUE(matches("auto Y() -> int { return 0; }", |
| functionDecl(hasTrailingReturn()))); |
| EXPECT_TRUE(matches("auto X() -> int;", functionDecl(hasTrailingReturn()))); |
| EXPECT_TRUE(notMatches("int X() { return 0; }", |
| functionDecl(hasTrailingReturn()))); |
| EXPECT_TRUE(notMatches("int X();", functionDecl(hasTrailingReturn()))); |
| EXPECT_TRUE(notMatchesC("void X();", functionDecl(hasTrailingReturn()))); |
| } |
| |
| TEST(HasTrailingReturn, MatchesLambdaTrailingReturn) { |
| EXPECT_TRUE(matches( |
| "auto lambda2 = [](double x, double y) -> double {return x + y;};", |
| functionDecl(hasTrailingReturn()))); |
| EXPECT_TRUE(notMatches( |
| "auto lambda2 = [](double x, double y) {return x + y;};", |
| functionDecl(hasTrailingReturn()))); |
| } |
| |
| TEST(IsAssignmentOperator, Basic) { |
| StatementMatcher BinAsgmtOperator = binaryOperator(isAssignmentOperator()); |
| StatementMatcher CXXAsgmtOperator = |
| cxxOperatorCallExpr(isAssignmentOperator()); |
| |
| EXPECT_TRUE(matches("void x() { int a; a += 1; }", BinAsgmtOperator)); |
| EXPECT_TRUE(matches("void x() { int a; a = 2; }", BinAsgmtOperator)); |
| EXPECT_TRUE(matches("void x() { int a; a &= 3; }", BinAsgmtOperator)); |
| EXPECT_TRUE(matches("struct S { S& operator=(const S&); };" |
| "void x() { S s1, s2; s1 = s2; }", |
| CXXAsgmtOperator)); |
| EXPECT_TRUE( |
| notMatches("void x() { int a; if(a == 0) return; }", BinAsgmtOperator)); |
| } |
| |
| TEST(Matcher, isMain) { |
| EXPECT_TRUE( |
| matches("int main() {}", functionDecl(isMain()))); |
| |
| EXPECT_TRUE( |
| notMatches("int main2() {}", functionDecl(isMain()))); |
| } |
| |
| } // namespace ast_matchers |
| } // namespace clang |