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