// unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp - AST matcher unit tests// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "ASTMatchersTest.h" #include "clang/AST/PrettyPrinter.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/Host.h" #include "gtest/gtest.h" namespace clang { namespace ast_matchers { TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesInFile) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) void Test() { MY_MACRO(4); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesNested) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) #define WRAPPER(a) MY_MACRO(a) void Test() { WRAPPER(4); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesIntermediate) { StringRef input = R"cc( #define IMPL(a) (4 + (a)) #define MY_MACRO(a) IMPL(a) #define WRAPPER(a) MY_MACRO(a) void Test() { WRAPPER(4); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesTransitive) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) #define WRAPPER(a) MY_MACRO(a) void Test() { WRAPPER(4); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("WRAPPER")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesArgument) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) void Test() { int x = 5; MY_MACRO(x); } )cc"; EXPECT_TRUE(matches(input, declRefExpr(isExpandedFromMacro("MY_MACRO")))); } // Like IsExpandedFromMacro_MatchesArgument, but the argument is itself a // macro. TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesArgumentMacroExpansion) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) #define IDENTITY(a) (a) void Test() { IDENTITY(MY_MACRO(2)); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("IDENTITY")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesWhenInArgument) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) #define IDENTITY(a) (a) void Test() { IDENTITY(MY_MACRO(2)); } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("MY_MACRO")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_MatchesObjectMacro) { StringRef input = R"cc( #define PLUS (2 + 2) void Test() { PLUS; } )cc"; EXPECT_TRUE(matches(input, binaryOperator(isExpandedFromMacro("PLUS")))); } TEST(IsExpandedFromMacro, MatchesFromCommandLine) { StringRef input = R"cc( void Test() { FOUR_PLUS_FOUR; } )cc"; EXPECT_TRUE(matchesConditionally( input, binaryOperator(isExpandedFromMacro("FOUR_PLUS_FOUR")), true, {"-std=c++11", "-DFOUR_PLUS_FOUR=4+4"})); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_NotMatchesBeginOnly) { StringRef input = R"cc( #define ONE_PLUS 1+ void Test() { ONE_PLUS 4; } )cc"; EXPECT_TRUE( notMatches(input, binaryOperator(isExpandedFromMacro("ONE_PLUS")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_NotMatchesEndOnly) { StringRef input = R"cc( #define PLUS_ONE +1 void Test() { 4 PLUS_ONE; } )cc"; EXPECT_TRUE( notMatches(input, binaryOperator(isExpandedFromMacro("PLUS_ONE")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_NotMatchesDifferentMacro) { StringRef input = R"cc( #define MY_MACRO(a) (4 + (a)) void Test() { MY_MACRO(4); } )cc"; EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("OTHER")))); } TEST_P(ASTMatchersTest, IsExpandedFromMacro_NotMatchesDifferentInstances) { StringRef input = R"cc( #define FOUR 4 void Test() { FOUR + FOUR; } )cc"; EXPECT_TRUE(notMatches(input, binaryOperator(isExpandedFromMacro("FOUR")))); } TEST(IsExpandedFromMacro, IsExpandedFromMacro_MatchesDecls) { StringRef input = R"cc( #define MY_MACRO(a) int i = a; void Test() { MY_MACRO(4); } )cc"; EXPECT_TRUE(matches(input, varDecl(isExpandedFromMacro("MY_MACRO")))); } TEST(IsExpandedFromMacro, IsExpandedFromMacro_MatchesTypelocs) { StringRef input = R"cc( #define MY_TYPE int void Test() { MY_TYPE i = 4; } )cc"; EXPECT_TRUE(matches(input, typeLoc(isExpandedFromMacro("MY_TYPE")))); } TEST_P(ASTMatchersTest, AllOf) { const char Program[] = "struct T { };" "int f(int, struct T*, int, int);" "void g(int x) { struct T t; f(x, &t, 3, 4); }"; EXPECT_TRUE(matches( Program, callExpr(allOf(callee(functionDecl(hasName("f"))), hasArgument(0, declRefExpr(to(varDecl()))))))); EXPECT_TRUE(matches( Program, callExpr( allOf(callee(functionDecl(hasName("f"))), hasArgument(0, declRefExpr(to(varDecl()))), hasArgument(1, hasType(pointsTo(recordDecl(hasName("T"))))))))); EXPECT_TRUE(matches( Program, callExpr(allOf( callee(functionDecl(hasName("f"))), hasArgument(0, declRefExpr(to(varDecl()))), hasArgument(1, hasType(pointsTo(recordDecl(hasName("T"))))), hasArgument(2, integerLiteral(equals(3))))))); EXPECT_TRUE(matches( Program, callExpr(allOf( callee(functionDecl(hasName("f"))), hasArgument(0, declRefExpr(to(varDecl()))), hasArgument(1, hasType(pointsTo(recordDecl(hasName("T"))))), hasArgument(2, integerLiteral(equals(3))), hasArgument(3, integerLiteral(equals(4))))))); } TEST_P(ASTMatchersTest, Has) { if (!GetParam().isCXX()) { // FIXME: Add a test for `has()` that does not depend on C++. return; } DeclarationMatcher HasClassX = recordDecl(has(recordDecl(hasName("X")))); EXPECT_TRUE(matches("class Y { class X {}; };", HasClassX)); EXPECT_TRUE(matches("class X {};", HasClassX)); DeclarationMatcher YHasClassX = recordDecl(hasName("Y"), has(recordDecl(hasName("X")))); EXPECT_TRUE(matches("class Y { class X {}; };", YHasClassX)); EXPECT_TRUE(notMatches("class X {};", YHasClassX)); EXPECT_TRUE(notMatches("class Y { class Z { class X {}; }; };", YHasClassX)); } TEST_P(ASTMatchersTest, Has_RecursiveAllOf) { if (!GetParam().isCXX()) { return; } DeclarationMatcher Recursive = recordDecl(has(recordDecl(has(recordDecl(hasName("X"))), has(recordDecl(hasName("Y"))), hasName("Z"))), has(recordDecl(has(recordDecl(hasName("A"))), has(recordDecl(hasName("B"))), hasName("C"))), hasName("F")); EXPECT_TRUE(matches("class F {" " class Z {" " class X {};" " class Y {};" " };" " class C {" " class A {};" " class B {};" " };" "};", Recursive)); EXPECT_TRUE(matches("class F {" " class Z {" " class A {};" " class X {};" " class Y {};" " };" " class C {" " class X {};" " class A {};" " class B {};" " };" "};", Recursive)); EXPECT_TRUE(matches("class O1 {" " class O2 {" " class F {" " class Z {" " class A {};" " class X {};" " class Y {};" " };" " class C {" " class X {};" " class A {};" " class B {};" " };" " };" " };" "};", Recursive)); } TEST_P(ASTMatchersTest, Has_RecursiveAnyOf) { if (!GetParam().isCXX()) { return; } DeclarationMatcher Recursive = recordDecl( anyOf(has(recordDecl(anyOf(has(recordDecl(hasName("X"))), has(recordDecl(hasName("Y"))), hasName("Z")))), has(recordDecl(anyOf(hasName("C"), has(recordDecl(hasName("A"))), has(recordDecl(hasName("B")))))), hasName("F"))); EXPECT_TRUE(matches("class F {};", Recursive)); EXPECT_TRUE(matches("class Z {};", Recursive)); EXPECT_TRUE(matches("class C {};", Recursive)); EXPECT_TRUE(matches("class M { class N { class X {}; }; };", Recursive)); EXPECT_TRUE(matches("class M { class N { class B {}; }; };", Recursive)); EXPECT_TRUE(matches("class O1 { class O2 {" " class M { class N { class B {}; }; }; " "}; };", Recursive)); } TEST_P(ASTMatchersTest, Unless) { if (!GetParam().isCXX()) { // FIXME: Add a test for `unless()` that does not depend on C++. return; } DeclarationMatcher NotClassX = cxxRecordDecl(isDerivedFrom("Y"), unless(hasName("X"))); EXPECT_TRUE(notMatches("", NotClassX)); EXPECT_TRUE(notMatches("class Y {};", NotClassX)); EXPECT_TRUE(matches("class Y {}; class Z : public Y {};", NotClassX)); EXPECT_TRUE(notMatches("class Y {}; class X : public Y {};", NotClassX)); EXPECT_TRUE( notMatches("class Y {}; class Z {}; class X : public Y {};", NotClassX)); DeclarationMatcher ClassXHasNotClassY = recordDecl(hasName("X"), has(recordDecl(hasName("Z"))), unless(has(recordDecl(hasName("Y"))))); EXPECT_TRUE(matches("class X { class Z {}; };", ClassXHasNotClassY)); EXPECT_TRUE( notMatches("class X { class Y {}; class Z {}; };", ClassXHasNotClassY)); DeclarationMatcher NamedNotRecord = namedDecl(hasName("Foo"), unless(recordDecl())); EXPECT_TRUE(matches("void Foo(){}", NamedNotRecord)); EXPECT_TRUE(notMatches("struct Foo {};", NamedNotRecord)); } TEST_P(ASTMatchersTest, HasCastKind) { EXPECT_TRUE( matches("char *p = 0;", traverse(TK_AsIs, varDecl(has(castExpr(hasCastKind(CK_NullToPointer))))))); EXPECT_TRUE(notMatches( "char *p = 0;", traverse(TK_AsIs, varDecl(has(castExpr(hasCastKind(CK_DerivedToBase))))))); EXPECT_TRUE(matches("char *p = 0;", traverse(TK_AsIs, varDecl(has(implicitCastExpr( hasCastKind(CK_NullToPointer))))))); } TEST_P(ASTMatchersTest, HasDescendant) { if (!GetParam().isCXX()) { // FIXME: Add a test for `hasDescendant()` that does not depend on C++. return; } DeclarationMatcher ZDescendantClassX = recordDecl(hasDescendant(recordDecl(hasName("X"))), hasName("Z")); EXPECT_TRUE(matches("class Z { class X {}; };", ZDescendantClassX)); EXPECT_TRUE( matches("class Z { class Y { class X {}; }; };", ZDescendantClassX)); EXPECT_TRUE(matches("class Z { class A { class Y { class X {}; }; }; };", ZDescendantClassX)); EXPECT_TRUE( matches("class Z { class A { class B { class Y { class X {}; }; }; }; };", ZDescendantClassX)); EXPECT_TRUE(notMatches("class Z {};", ZDescendantClassX)); DeclarationMatcher ZDescendantClassXHasClassY = recordDecl( hasDescendant(recordDecl(has(recordDecl(hasName("Y"))), hasName("X"))), hasName("Z")); EXPECT_TRUE(matches("class Z { class X { class Y {}; }; };", ZDescendantClassXHasClassY)); EXPECT_TRUE( matches("class Z { class A { class B { class X { class Y {}; }; }; }; };", ZDescendantClassXHasClassY)); EXPECT_TRUE(notMatches("class Z {" " class A {" " class B {" " class X {" " class C {" " class Y {};" " };" " };" " }; " " };" "};", ZDescendantClassXHasClassY)); DeclarationMatcher ZDescendantClassXDescendantClassY = recordDecl(hasDescendant(recordDecl( hasDescendant(recordDecl(hasName("Y"))), hasName("X"))), hasName("Z")); EXPECT_TRUE( matches("class Z { class A { class X { class B { class Y {}; }; }; }; };", ZDescendantClassXDescendantClassY)); EXPECT_TRUE(matches("class Z {" " class A {" " class X {" " class B {" " class Y {};" " };" " class Y {};" " };" " };" "};", ZDescendantClassXDescendantClassY)); } TEST_P(ASTMatchersTest, HasDescendant_Memoization) { DeclarationMatcher CannotMemoize = decl(hasDescendant(typeLoc().bind("x")), has(decl())); EXPECT_TRUE(matches("void f() { int i; }", CannotMemoize)); } TEST_P(ASTMatchersTest, HasDescendant_MemoizationUsesRestrictKind) { auto Name = hasName("i"); auto VD = internal::Matcher(Name).dynCastTo(); auto RD = internal::Matcher(Name).dynCastTo(); // Matching VD first should not make a cache hit for RD. EXPECT_TRUE(notMatches("void f() { int i; }", decl(hasDescendant(VD), hasDescendant(RD)))); EXPECT_TRUE(notMatches("void f() { int i; }", decl(hasDescendant(RD), hasDescendant(VD)))); // Not matching RD first should not make a cache hit for VD either. EXPECT_TRUE(matches("void f() { int i; }", decl(anyOf(hasDescendant(RD), hasDescendant(VD))))); } TEST_P(ASTMatchersTest, HasAncestor_Memoization) { if (!GetParam().isCXX()) { return; } // This triggers an hasAncestor with a TemplateArgument in the bound nodes. // That node can't be memoized so we have to check for it before trying to put // it on the cache. DeclarationMatcher CannotMemoize = classTemplateSpecializationDecl( hasAnyTemplateArgument(templateArgument().bind("targ")), forEach(fieldDecl(hasAncestor(forStmt())))); EXPECT_TRUE(notMatches("template struct S;" "template <> struct S{ int i; int j; };", CannotMemoize)); } TEST_P(ASTMatchersTest, HasAttr) { EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};", decl(hasAttr(clang::attr::WarnUnused)))); EXPECT_FALSE(matches("struct X {};", decl(hasAttr(clang::attr::WarnUnused)))); } TEST_P(ASTMatchersTest, AnyOf) { if (!GetParam().isCXX()) { // FIXME: Add a test for `anyOf()` that does not depend on C++. return; } DeclarationMatcher YOrZDerivedFromX = cxxRecordDecl( anyOf(hasName("Y"), allOf(isDerivedFrom("X"), hasName("Z")))); EXPECT_TRUE(matches("class X {}; class Z : public X {};", YOrZDerivedFromX)); EXPECT_TRUE(matches("class Y {};", YOrZDerivedFromX)); EXPECT_TRUE( notMatches("class X {}; class W : public X {};", YOrZDerivedFromX)); EXPECT_TRUE(notMatches("class Z {};", YOrZDerivedFromX)); DeclarationMatcher XOrYOrZOrU = recordDecl(anyOf(hasName("X"), hasName("Y"), hasName("Z"), hasName("U"))); EXPECT_TRUE(matches("class X {};", XOrYOrZOrU)); EXPECT_TRUE(notMatches("class V {};", XOrYOrZOrU)); DeclarationMatcher XOrYOrZOrUOrV = recordDecl(anyOf( hasName("X"), hasName("Y"), hasName("Z"), hasName("U"), hasName("V"))); EXPECT_TRUE(matches("class X {};", XOrYOrZOrUOrV)); EXPECT_TRUE(matches("class Y {};", XOrYOrZOrUOrV)); EXPECT_TRUE(matches("class Z {};", XOrYOrZOrUOrV)); EXPECT_TRUE(matches("class U {};", XOrYOrZOrUOrV)); EXPECT_TRUE(matches("class V {};", XOrYOrZOrUOrV)); EXPECT_TRUE(notMatches("class A {};", XOrYOrZOrUOrV)); StatementMatcher MixedTypes = stmt(anyOf(ifStmt(), binaryOperator())); EXPECT_TRUE(matches("int F() { return 1 + 2; }", MixedTypes)); EXPECT_TRUE(matches("int F() { if (true) return 1; }", MixedTypes)); EXPECT_TRUE(notMatches("int F() { return 1; }", MixedTypes)); EXPECT_TRUE( matches("void f() try { } catch (int) { } catch (...) { }", cxxCatchStmt(anyOf(hasDescendant(varDecl()), isCatchAll())))); } TEST_P(ASTMatchersTest, MapAnyOf) { if (!GetParam().isCXX()) { return; } if (GetParam().hasDelayedTemplateParsing()) { return; } StringRef Code = R"cpp( void F() { if (true) {} for ( ; false; ) {} } )cpp"; auto trueExpr = cxxBoolLiteral(equals(true)); auto falseExpr = cxxBoolLiteral(equals(false)); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(ifStmt, forStmt).with(hasCondition(trueExpr))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(ifStmt, forStmt).with(hasCondition(falseExpr))))); EXPECT_TRUE( matches(Code, cxxBoolLiteral(equals(true), hasAncestor(mapAnyOf(ifStmt, forStmt))))); EXPECT_TRUE( matches(Code, cxxBoolLiteral(equals(false), hasAncestor(mapAnyOf(ifStmt, forStmt))))); EXPECT_TRUE( notMatches(Code, floatLiteral(hasAncestor(mapAnyOf(ifStmt, forStmt))))); Code = R"cpp( void func(bool b) {} struct S { S(bool b) {} }; void F() { func(false); S s(true); } )cpp"; EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr) .with(hasArgument(0, trueExpr))))); EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr) .with(hasArgument(0, falseExpr))))); EXPECT_TRUE( matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr) .with(hasArgument(0, expr()), hasDeclaration(functionDecl()))))); EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr)))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(callExpr, cxxConstructExpr).bind("call")))); Code = R"cpp( struct HasOpNeqMem { bool operator!=(const HasOpNeqMem& other) const { return true; } }; struct HasOpFree { }; bool operator!=(const HasOpFree& lhs, const HasOpFree& rhs) { return true; } void binop() { int s1; int s2; if (s1 != s2) return; } void opMem() { HasOpNeqMem s1; HasOpNeqMem s2; if (s1 != s2) return; } void opFree() { HasOpFree s1; HasOpFree s2; if (s1 != s2) return; } template void templ() { T s1; T s2; if (s1 != s2) return; } )cpp"; EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("binop"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opMem"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opFree"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("binop"))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s1"))))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opMem"))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s1"))))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opFree"))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s1"))))), hasEitherOperand( declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse( TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("binop"))), hasOperands(declRefExpr(to(varDecl(hasName("s1")))), declRefExpr(to(varDecl(hasName("s2"))))), hasOperands(declRefExpr(to(varDecl(hasName("s2")))), declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse( TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opMem"))), hasOperands(declRefExpr(to(varDecl(hasName("s1")))), declRefExpr(to(varDecl(hasName("s2"))))), hasOperands(declRefExpr(to(varDecl(hasName("s2")))), declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse( TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!="), forFunction(functionDecl(hasName("opFree"))), hasOperands(declRefExpr(to(varDecl(hasName("s1")))), declRefExpr(to(varDecl(hasName("s2"))))), hasOperands(declRefExpr(to(varDecl(hasName("s2")))), declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("==", "!="), forFunction(functionDecl(hasName("binop"))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("==", "!="), forFunction(functionDecl(hasName("opMem"))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(binaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("==", "!="), forFunction(functionDecl(hasName("opFree"))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, binaryOperation( hasOperatorName("!="), forFunction(functionDecl(hasName("binop"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, binaryOperation( hasOperatorName("!="), forFunction(functionDecl(hasName("opMem"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, binaryOperation( hasOperatorName("!="), forFunction(functionDecl(hasName("opFree"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, binaryOperation( hasOperatorName("!="), forFunction(functionDecl(hasName("templ"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))))); Code = R"cpp( struct HasOpEq { bool operator==(const HasOpEq &) const; }; void inverse() { HasOpEq s1; HasOpEq s2; if (s1 != s2) return; } namespace std { struct strong_ordering { int n; constexpr operator int() const { return n; } static const strong_ordering equal, greater, less; }; constexpr strong_ordering strong_ordering::equal = {0}; constexpr strong_ordering strong_ordering::greater = {1}; constexpr strong_ordering strong_ordering::less = {-1}; } struct HasSpaceshipMem { int a; constexpr auto operator<=>(const HasSpaceshipMem&) const = default; }; void rewritten() { HasSpaceshipMem s1; HasSpaceshipMem s2; if (s1 != s2) return; } )cpp"; EXPECT_TRUE(matchesConditionally( Code, traverse( TK_IgnoreUnlessSpelledInSource, binaryOperation(hasOperatorName("!="), forFunction(functionDecl(hasName("inverse"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))), true, {"-std=c++20"})); EXPECT_TRUE(matchesConditionally( Code, traverse( TK_IgnoreUnlessSpelledInSource, binaryOperation(hasOperatorName("!="), forFunction(functionDecl(hasName("rewritten"))), hasLHS(declRefExpr(to(varDecl(hasName("s1"))))), hasRHS(declRefExpr(to(varDecl(hasName("s2"))))))), true, {"-std=c++20"})); Code = R"cpp( struct HasOpBangMem { bool operator!() const { return false; } }; struct HasOpBangFree { }; bool operator!(HasOpBangFree const&) { return false; } void unop() { int s1; if (!s1) return; } void opMem() { HasOpBangMem s1; if (!s1) return; } void opFree() { HasOpBangFree s1; if (!s1) return; } )cpp"; EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!"), forFunction(functionDecl(hasName("unop"))), hasUnaryOperand( declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!"), forFunction(functionDecl(hasName("opMem"))), hasUnaryOperand( declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasOperatorName("!"), forFunction(functionDecl(hasName("opFree"))), hasUnaryOperand( declRefExpr(to(varDecl(hasName("s1"))))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("+", "!"), forFunction(functionDecl(hasName("unop"))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("+", "!"), forFunction(functionDecl(hasName("opMem"))))))); EXPECT_TRUE(matches( Code, traverse(TK_IgnoreUnlessSpelledInSource, mapAnyOf(unaryOperator, cxxOperatorCallExpr) .with(hasAnyOperatorName("+", "!"), forFunction(functionDecl(hasName("opFree"))))))); } TEST_P(ASTMatchersTest, IsDerivedFrom) { if (!GetParam().isCXX()) { return; } DeclarationMatcher IsDerivedFromX = cxxRecordDecl(isDerivedFrom("X")); EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsDerivedFromX)); EXPECT_TRUE(notMatches("class X {};", IsDerivedFromX)); EXPECT_TRUE(notMatches("class X;", IsDerivedFromX)); EXPECT_TRUE(notMatches("class Y;", IsDerivedFromX)); EXPECT_TRUE(notMatches("", IsDerivedFromX)); EXPECT_TRUE(matches("class X {}; template class Y : Y, X {};", IsDerivedFromX)); EXPECT_TRUE(matches("class X {}; template class Y : X, Y {};", IsDerivedFromX)); DeclarationMatcher IsZDerivedFromX = cxxRecordDecl(hasName("Z"), isDerivedFrom("X")); EXPECT_TRUE(matches("class X {};" "template class Y : Y {};" "template<> class Y<0> : X {};" "class Z : Y<1> {};", IsZDerivedFromX)); DeclarationMatcher IsDirectlyDerivedFromX = cxxRecordDecl(isDirectlyDerivedFrom("X")); EXPECT_TRUE( matches("class X {}; class Y : public X {};", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatches("class X {};", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatches("class X;", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatches("class Y;", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatches("", IsDirectlyDerivedFromX)); DeclarationMatcher IsAX = cxxRecordDecl(isSameOrDerivedFrom("X")); EXPECT_TRUE(matches("class X {}; class Y : public X {};", IsAX)); EXPECT_TRUE(matches("class X {};", IsAX)); EXPECT_TRUE(matches("class X;", IsAX)); EXPECT_TRUE(notMatches("class Y;", IsAX)); EXPECT_TRUE(notMatches("", IsAX)); DeclarationMatcher ZIsDerivedFromX = cxxRecordDecl(hasName("Z"), isDerivedFrom("X")); DeclarationMatcher ZIsDirectlyDerivedFromX = cxxRecordDecl(hasName("Z"), isDirectlyDerivedFrom("X")); EXPECT_TRUE( matches("class X {}; class Y : public X {}; class Z : public Y {};", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("class X {}; class Y : public X {}; class Z : public Y {};", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matches("class X {};" "template class Y : public X {};" "class Z : public Y {};", ZIsDerivedFromX)); EXPECT_TRUE(notMatches("class X {};" "template class Y : public X {};" "class Z : public Y {};", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matches("class X {}; template class Z : public X {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("template class X {}; " "template class Z : public X {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("template class X {}; " "template class Z : public X {};", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X {}; };", ZIsDerivedFromX)); EXPECT_TRUE( matches("template class A { public: class Z : public X {}; }; " "class X{}; void y() { A::Z z; }", ZIsDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class A { class Z : public X {}; };", ZIsDerivedFromX)); EXPECT_TRUE(notMatches("template class X> class A { " " class Z : public X {}; };", ZIsDerivedFromX)); EXPECT_TRUE(matches("template class X> class A { " " public: class Z : public X {}; }; " "template class X {}; void y() { A::Z z; }", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X::D {}; };", ZIsDerivedFromX)); EXPECT_TRUE(matches("template class A { public: " " class Z : public X::D {}; }; " "class Y { public: class X {}; typedef X D; }; " "void y() { A::Z z; }", ZIsDerivedFromX)); EXPECT_TRUE(matches("class X {}; typedef X Y; class Z : public Y {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("template class Y { typedef typename T::U X; " " class Z : public X {}; };", ZIsDerivedFromX)); EXPECT_TRUE(matches("class X {}; class Z : public ::X {};", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template class X {}; " "template class A { class Z : public X::D {}; };", ZIsDerivedFromX)); EXPECT_TRUE( matches("template class X { public: typedef X D; }; " "template class A { public: " " class Z : public X::D {}; }; void y() { A::Z z; }", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template class A { class Z : public X::D::E {}; };", ZIsDerivedFromX)); EXPECT_TRUE( matches("class X {}; typedef X V; typedef V W; class Z : public W {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class X {}; class Y : public X {}; " "typedef Y V; typedef V W; class Z : public W {};", ZIsDerivedFromX)); EXPECT_TRUE(notMatches("class X {}; class Y : public X {}; " "typedef Y V; typedef V W; class Z : public W {};", ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matches("template class X {}; " "template class A { class Z : public X {}; };", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template class D { typedef X A; typedef A B; " " typedef B C; class Z : public C {}; };", ZIsDerivedFromX)); EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; " "class Z : public B {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; typedef B C; " "class Z : public C {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class U {}; typedef U X; typedef X V; " "class Z : public V {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class Base {}; typedef Base X; " "class Z : public Base {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class Base {}; typedef Base Base2; typedef Base2 X; " "class Z : public Base {};", ZIsDerivedFromX)); EXPECT_TRUE(notMatches("class Base {}; class Base2 {}; typedef Base2 X; " "class Z : public Base {};", ZIsDerivedFromX)); EXPECT_TRUE(matches("class A {}; typedef A X; typedef A Y; " "class Z : public Y {};", ZIsDerivedFromX)); EXPECT_TRUE(notMatches("template class Z;" "template <> class Z {};" "template class Z : public Z {};", IsDerivedFromX)); EXPECT_TRUE(matches("template class X;" "template <> class X {};" "template class X : public X {};", IsDerivedFromX)); EXPECT_TRUE( matches("class X {};" "template class Z;" "template <> class Z {};" "template class Z : public Z, public X {};", ZIsDerivedFromX)); EXPECT_TRUE( notMatches("template struct X;" "template struct X : public X {};", cxxRecordDecl(isDerivedFrom(recordDecl(hasName("Some")))))); EXPECT_TRUE(matches( "struct A {};" "template struct X;" "template struct X : public X {};" "template<> struct X<0> : public A {};" "struct B : public X<42> {};", cxxRecordDecl(hasName("B"), isDerivedFrom(recordDecl(hasName("A")))))); EXPECT_TRUE(notMatches( "struct A {};" "template struct X;" "template struct X : public X {};" "template<> struct X<0> : public A {};" "struct B : public X<42> {};", cxxRecordDecl(hasName("B"), isDirectlyDerivedFrom(recordDecl(hasName("A")))))); // FIXME: Once we have better matchers for template type matching, // get rid of the Variable(...) matching and match the right template // declarations directly. const char *RecursiveTemplateOneParameter = "class Base1 {}; class Base2 {};" "template class Z;" "template <> class Z : public Base1 {};" "template <> class Z : public Base2 {};" "template <> class Z : public Z {};" "template <> class Z : public Z {};" "template class Z : public Z, public Z {};" "void f() { Z z_float; Z z_double; Z z_char; }"; EXPECT_TRUE(matches( RecursiveTemplateOneParameter, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateOneParameter, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); EXPECT_TRUE( matches(RecursiveTemplateOneParameter, varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl( isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); const char *RecursiveTemplateTwoParameters = "class Base1 {}; class Base2 {};" "template class Z;" "template class Z : public Base1 {};" "template class Z : public Base2 {};" "template class Z : public Z {};" "template class Z : public Z {};" "template class Z : " " public Z, public Z {};" "void f() { Z z_float; Z z_double; " " Z z_char; }"; EXPECT_TRUE(matches( RecursiveTemplateTwoParameters, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); EXPECT_TRUE(notMatches( RecursiveTemplateTwoParameters, varDecl(hasName("z_float"), hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); EXPECT_TRUE( matches(RecursiveTemplateTwoParameters, varDecl(hasName("z_char"), hasInitializer(hasType(cxxRecordDecl( isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); EXPECT_TRUE(matches("namespace ns { class X {}; class Y : public X {}; }", cxxRecordDecl(isDerivedFrom("::ns::X")))); EXPECT_TRUE(notMatches("class X {}; class Y : public X {};", cxxRecordDecl(isDerivedFrom("::ns::X")))); EXPECT_TRUE(matches( "class X {}; class Y : public X {};", cxxRecordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test"))))); EXPECT_TRUE(matches("template class X {};" "template using Z = X;" "template class Y : Z {};", cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); } TEST_P(ASTMatchersTest, IsDerivedFrom_EmptyName) { if (!GetParam().isCXX()) { return; } const char *const Code = "class X {}; class Y : public X {};"; EXPECT_TRUE(notMatches(Code, cxxRecordDecl(isDerivedFrom("")))); EXPECT_TRUE(notMatches(Code, cxxRecordDecl(isDirectlyDerivedFrom("")))); EXPECT_TRUE(notMatches(Code, cxxRecordDecl(isSameOrDerivedFrom("")))); } TEST_P(ASTMatchersTest, IsDerivedFrom_ObjC) { DeclarationMatcher IsDerivedFromX = objcInterfaceDecl(isDerivedFrom("X")); EXPECT_TRUE( matchesObjC("@interface X @end @interface Y : X @end", IsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @interface Y<__covariant ObjectType> : X @end", IsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @compatibility_alias Y X; @interface Z : Y @end", IsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end typedef X Y; @interface Z : Y @end", IsDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@interface X @end", IsDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@class X;", IsDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@class Y;", IsDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@interface X @end @compatibility_alias Y X;", IsDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@interface X @end typedef X Y;", IsDerivedFromX)); DeclarationMatcher IsDirectlyDerivedFromX = objcInterfaceDecl(isDirectlyDerivedFrom("X")); EXPECT_TRUE(matchesObjC("@interface X @end @interface Y : X @end", IsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @interface Y<__covariant ObjectType> : X @end", IsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @compatibility_alias Y X; @interface Z : Y @end", IsDirectlyDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface X @end typedef X Y; @interface Z : Y @end", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@interface X @end", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@class X;", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@class Y;", IsDirectlyDerivedFromX)); EXPECT_TRUE(notMatchesObjC("@interface X @end @compatibility_alias Y X;", IsDirectlyDerivedFromX)); EXPECT_TRUE( notMatchesObjC("@interface X @end typedef X Y;", IsDirectlyDerivedFromX)); DeclarationMatcher IsAX = objcInterfaceDecl(isSameOrDerivedFrom("X")); EXPECT_TRUE(matchesObjC("@interface X @end @interface Y : X @end", IsAX)); EXPECT_TRUE(matchesObjC("@interface X @end", IsAX)); EXPECT_TRUE(matchesObjC("@class X;", IsAX)); EXPECT_TRUE(notMatchesObjC("@interface Y @end", IsAX)); EXPECT_TRUE(notMatchesObjC("@class Y;", IsAX)); DeclarationMatcher ZIsDerivedFromX = objcInterfaceDecl(hasName("Z"), isDerivedFrom("X")); DeclarationMatcher ZIsDirectlyDerivedFromX = objcInterfaceDecl(hasName("Z"), isDirectlyDerivedFrom("X")); EXPECT_TRUE(matchesObjC( "@interface X @end @interface Y : X @end @interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC("@interface X @end @interface Y : X @end typedef Y " "V; typedef V W; @interface Z : W @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end typedef X Y; @interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface X @end typedef X Y; @interface Z : Y @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface A @end typedef A X; typedef A Y; @interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface A @end typedef A X; typedef A Y; @interface Z : Y @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @compatibility_alias Y X; @interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface X @end @compatibility_alias Y X; @interface Z : Y @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface Y @end @compatibility_alias X Y; @interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface Y @end @compatibility_alias X Y; @interface Z : Y @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface A @end @compatibility_alias X A; @compatibility_alias Y A;" "@interface Z : Y @end", ZIsDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface A @end @compatibility_alias X A; @compatibility_alias Y A;" "@interface Z : Y @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE(matchesObjC( "@interface Y @end typedef Y X; @interface Z : X @end", ZIsDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface Y @end typedef Y X; @interface Z : X @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface A @end @compatibility_alias Y A; typedef Y X;" "@interface Z : A @end", ZIsDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface A @end @compatibility_alias Y A; typedef Y X;" "@interface Z : A @end", ZIsDirectlyDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface A @end typedef A Y; @compatibility_alias X Y;" "@interface Z : A @end", ZIsDerivedFromX)); EXPECT_TRUE( matchesObjC("@interface A @end typedef A Y; @compatibility_alias X Y;" "@interface Z : A @end", ZIsDirectlyDerivedFromX)); } TEST_P(ASTMatchersTest, IsLambda) { if (!GetParam().isCXX11OrLater()) { return; } const auto IsLambda = cxxMethodDecl(ofClass(cxxRecordDecl(isLambda()))); EXPECT_TRUE(matches("auto x = []{};", IsLambda)); EXPECT_TRUE(notMatches("struct S { void operator()() const; };", IsLambda)); } TEST_P(ASTMatchersTest, Bind) { DeclarationMatcher ClassX = has(recordDecl(hasName("::X")).bind("x")); EXPECT_TRUE(matchAndVerifyResultTrue( "class X {};", ClassX, std::make_unique>("x"))); EXPECT_TRUE(matchAndVerifyResultFalse( "class X {};", ClassX, std::make_unique>("other-id"))); TypeMatcher TypeAHasClassB = hasDeclaration( recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b")))); EXPECT_TRUE(matchAndVerifyResultTrue( "class A { public: A *a; class B {}; };", TypeAHasClassB, std::make_unique>("b"))); StatementMatcher MethodX = callExpr(callee(cxxMethodDecl(hasName("x")))).bind("x"); EXPECT_TRUE(matchAndVerifyResultTrue( "class A { void x() { x(); } };", MethodX, std::make_unique>("x"))); } TEST_P(ASTMatchersTest, Bind_SameNameInAlternatives) { StatementMatcher matcher = anyOf( binaryOperator(hasOperatorName("+"), hasLHS(expr().bind("x")), hasRHS(integerLiteral(equals(0)))), binaryOperator(hasOperatorName("+"), hasLHS(integerLiteral(equals(0))), hasRHS(expr().bind("x")))); EXPECT_TRUE(matchAndVerifyResultTrue( // The first branch of the matcher binds x to 0 but then fails. // The second branch binds x to f() and succeeds. "int f() { return 0 + f(); }", matcher, std::make_unique>("x"))); } TEST_P(ASTMatchersTest, Bind_BindsIDForMemoizedResults) { // Using the same matcher in two match expressions will make memoization // kick in. DeclarationMatcher ClassX = recordDecl(hasName("X")).bind("x"); EXPECT_TRUE(matchAndVerifyResultTrue( "class A { class B { class X {}; }; };", DeclarationMatcher( anyOf(recordDecl(hasName("A"), hasDescendant(ClassX)), recordDecl(hasName("B"), hasDescendant(ClassX)))), std::make_unique>("x", 2))); } TEST_P(ASTMatchersTest, HasType_MatchesAsString) { if (!GetParam().isCXX()) { // FIXME: Add a test for `hasType()` that does not depend on C++. return; } EXPECT_TRUE( matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }", cxxMemberCallExpr(on(hasType(asString("class Y *")))))); EXPECT_TRUE( matches("class X { void x(int x) {} };", cxxMethodDecl(hasParameter(0, hasType(asString("int")))))); EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };", fieldDecl(hasType(asString("ns::A"))))); EXPECT_TRUE( matches("namespace { struct A {}; } struct B { A a; };", fieldDecl(hasType(asString("struct (anonymous namespace)::A"))))); } TEST_P(ASTMatchersTest, HasOverloadedOperatorName) { if (!GetParam().isCXX()) { return; } StatementMatcher OpCallAndAnd = cxxOperatorCallExpr(hasOverloadedOperatorName("&&")); EXPECT_TRUE(matches("class Y { }; " "bool operator&&(Y x, Y y) { return true; }; " "Y a; Y b; bool c = a && b;", OpCallAndAnd)); StatementMatcher OpCallLessLess = cxxOperatorCallExpr(hasOverloadedOperatorName("<<")); EXPECT_TRUE(notMatches("class Y { }; " "bool operator&&(Y x, Y y) { return true; }; " "Y a; Y b; bool c = a && b;", OpCallLessLess)); StatementMatcher OpStarCall = cxxOperatorCallExpr(hasOverloadedOperatorName("*")); EXPECT_TRUE( matches("class Y; int operator*(Y &); void f(Y &y) { *y; }", OpStarCall)); DeclarationMatcher ClassWithOpStar = cxxRecordDecl(hasMethod(hasOverloadedOperatorName("*"))); EXPECT_TRUE(matches("class Y { int operator*(); };", ClassWithOpStar)); EXPECT_TRUE(notMatches("class Y { void myOperator(); };", ClassWithOpStar)); DeclarationMatcher AnyOpStar = functionDecl(hasOverloadedOperatorName("*")); EXPECT_TRUE(matches("class Y; int operator*(Y &);", AnyOpStar)); EXPECT_TRUE(matches("class Y { int operator*(); };", AnyOpStar)); DeclarationMatcher AnyAndOp = functionDecl(hasAnyOverloadedOperatorName("&", "&&")); EXPECT_TRUE(matches("class Y; Y operator&(Y &, Y &);", AnyAndOp)); EXPECT_TRUE(matches("class Y; Y operator&&(Y &, Y &);", AnyAndOp)); EXPECT_TRUE(matches("class Y { Y operator&(Y &); };", AnyAndOp)); EXPECT_TRUE(matches("class Y { Y operator&&(Y &); };", AnyAndOp)); } TEST_P(ASTMatchersTest, HasOverloadedOperatorName_MatchesNestedCalls) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matchAndVerifyResultTrue( "class Y { }; " "Y& operator&&(Y& x, Y& y) { return x; }; " "Y a; Y b; Y c; Y d = a && b && c;", cxxOperatorCallExpr(hasOverloadedOperatorName("&&")).bind("x"), std::make_unique>("x", 2))); EXPECT_TRUE(matches("class Y { }; " "Y& operator&&(Y& x, Y& y) { return x; }; " "Y a; Y b; Y c; Y d = a && b && c;", cxxOperatorCallExpr(hasParent(cxxOperatorCallExpr())))); EXPECT_TRUE( matches("class Y { }; " "Y& operator&&(Y& x, Y& y) { return x; }; " "Y a; Y b; Y c; Y d = a && b && c;", cxxOperatorCallExpr(hasDescendant(cxxOperatorCallExpr())))); } TEST_P(ASTMatchersTest, HasLocalStorage) { auto M = varDecl(hasName("X"), hasLocalStorage()); EXPECT_TRUE(matches("void f() { int X; }", M)); EXPECT_TRUE(notMatches("int X;", M)); EXPECT_TRUE(notMatches("void f() { static int X; }", M)); } TEST_P(ASTMatchersTest, HasGlobalStorage) { auto M = varDecl(hasName("X"), hasGlobalStorage()); EXPECT_TRUE(notMatches("void f() { int X; }", M)); EXPECT_TRUE(matches("int X;", M)); EXPECT_TRUE(matches("void f() { static int X; }", M)); } TEST_P(ASTMatchersTest, IsStaticLocal) { auto M = varDecl(isStaticLocal()); EXPECT_TRUE(matches("void f() { static int X; }", M)); EXPECT_TRUE(notMatches("static int X;", M)); EXPECT_TRUE(notMatches("void f() { int X; }", M)); EXPECT_TRUE(notMatches("int X;", M)); } TEST_P(ASTMatchersTest, StorageDuration) { StringRef T = "void f() { int x; static int y; } int a;static int b;extern int c;"; EXPECT_TRUE(matches(T, varDecl(hasName("x"), hasAutomaticStorageDuration()))); EXPECT_TRUE( notMatches(T, varDecl(hasName("y"), hasAutomaticStorageDuration()))); EXPECT_TRUE( notMatches(T, varDecl(hasName("a"), hasAutomaticStorageDuration()))); EXPECT_TRUE(matches(T, varDecl(hasName("y"), hasStaticStorageDuration()))); EXPECT_TRUE(matches(T, varDecl(hasName("a"), hasStaticStorageDuration()))); EXPECT_TRUE(matches(T, varDecl(hasName("b"), hasStaticStorageDuration()))); EXPECT_TRUE(matches(T, varDecl(hasName("c"), hasStaticStorageDuration()))); EXPECT_TRUE(notMatches(T, varDecl(hasName("x"), hasStaticStorageDuration()))); // FIXME: Add thread_local variables to the source code snippet. EXPECT_TRUE(notMatches(T, varDecl(hasName("x"), hasThreadStorageDuration()))); EXPECT_TRUE(notMatches(T, varDecl(hasName("y"), hasThreadStorageDuration()))); EXPECT_TRUE(notMatches(T, varDecl(hasName("a"), hasThreadStorageDuration()))); } TEST_P(ASTMatchersTest, VarDecl_MatchesFunctionParameter) { EXPECT_TRUE(matches("void f(int i) {}", varDecl(hasName("i")))); } TEST_P(ASTMatchersTest, SizeOfExpr_MatchesCorrectType) { EXPECT_TRUE(matches("void x() { int a = sizeof(a); }", sizeOfExpr(hasArgumentOfType(asString("int"))))); EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(hasArgumentOfType(asString("float"))))); EXPECT_TRUE(matches( "struct A {}; void x() { struct A a; int b = sizeof(a); }", sizeOfExpr(hasArgumentOfType(hasDeclaration(recordDecl(hasName("A"))))))); EXPECT_TRUE(notMatches("void x() { int a = sizeof(a); }", sizeOfExpr(hasArgumentOfType( hasDeclaration(recordDecl(hasName("string"))))))); } TEST_P(ASTMatchersTest, IsInteger_MatchesIntegers) { EXPECT_TRUE(matches("int i = 0;", varDecl(hasType(isInteger())))); EXPECT_TRUE( matches("long long i = 0; void f(long long) { }; void g() {f(i);}", callExpr(hasArgument( 0, declRefExpr(to(varDecl(hasType(isInteger())))))))); } TEST_P(ASTMatchersTest, IsInteger_ReportsNoFalsePositives) { if (!GetParam().isCXX()) { // FIXME: Add a similar negative test for `isInteger()` that does not depend // on C++. return; } EXPECT_TRUE(notMatches("int *i;", varDecl(hasType(isInteger())))); EXPECT_TRUE( notMatches("struct T {}; T t; void f(T *) { }; void g() {f(&t);}", callExpr(hasArgument( 0, declRefExpr(to(varDecl(hasType(isInteger())))))))); } TEST_P(ASTMatchersTest, IsSignedInteger_MatchesSignedIntegers) { EXPECT_TRUE(matches("int i = 0;", varDecl(hasType(isSignedInteger())))); EXPECT_TRUE( notMatches("unsigned i = 0;", varDecl(hasType(isSignedInteger())))); } TEST_P(ASTMatchersTest, IsUnsignedInteger_MatchesUnsignedIntegers) { EXPECT_TRUE(notMatches("int i = 0;", varDecl(hasType(isUnsignedInteger())))); EXPECT_TRUE( matches("unsigned i = 0;", varDecl(hasType(isUnsignedInteger())))); } TEST_P(ASTMatchersTest, IsAnyPointer_MatchesPointers) { if (!GetParam().isCXX11OrLater()) { // FIXME: Add a test for `isAnyPointer()` that does not depend on C++. return; } EXPECT_TRUE(matches("int* i = nullptr;", varDecl(hasType(isAnyPointer())))); } TEST_P(ASTMatchersTest, IsAnyPointer_MatchesObjcPointer) { EXPECT_TRUE(matchesObjC("@interface Foo @end Foo *f;", varDecl(hasType(isAnyPointer())))); } TEST_P(ASTMatchersTest, IsAnyPointer_ReportsNoFalsePositives) { EXPECT_TRUE(notMatches("int i = 0;", varDecl(hasType(isAnyPointer())))); } TEST_P(ASTMatchersTest, IsAnyCharacter_MatchesCharacters) { EXPECT_TRUE(matches("char i = 0;", varDecl(hasType(isAnyCharacter())))); } TEST_P(ASTMatchersTest, IsAnyCharacter_ReportsNoFalsePositives) { EXPECT_TRUE(notMatches("int i;", varDecl(hasType(isAnyCharacter())))); } TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) { if (!GetParam().isCXX()) { // FIXME: Add a test for `isArrow()` that does not depend on C++. return; } if (GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("class Y { void x() { this->y; } int y; };", memberExpr(isArrow()))); EXPECT_TRUE( matches("class Y { void x() { y; } int y; };", memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } int y; };", memberExpr(isArrow()))); EXPECT_TRUE(matches("template class Y { void x() { this->m; } };", cxxDependentScopeMemberExpr(isArrow()))); EXPECT_TRUE( notMatches("template class Y { void x() { (*this).m; } };", cxxDependentScopeMemberExpr(isArrow()))); } TEST_P(ASTMatchersTest, IsArrow_MatchesStaticMemberVariablesViaArrow) { if (!GetParam().isCXX()) { // FIXME: Add a test for `isArrow()` that does not depend on C++. return; } EXPECT_TRUE(matches("class Y { void x() { this->y; } static int y; };", memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { y; } static int y; };", memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { (*this).y; } static int y; };", memberExpr(isArrow()))); } TEST_P(ASTMatchersTest, IsArrow_MatchesMemberCallsViaArrow) { if (!GetParam().isCXX()) { // FIXME: Add a test for `isArrow()` that does not depend on C++. return; } if (GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE( matches("class Y { void x() { this->x(); } };", memberExpr(isArrow()))); EXPECT_TRUE(matches("class Y { void x() { x(); } };", memberExpr(isArrow()))); EXPECT_TRUE(notMatches("class Y { void x() { Y y; y.x(); } };", memberExpr(isArrow()))); EXPECT_TRUE( matches("class Y { template void x() { this->x(); } };", unresolvedMemberExpr(isArrow()))); EXPECT_TRUE(matches("class Y { template void x() { x(); } };", unresolvedMemberExpr(isArrow()))); EXPECT_TRUE( notMatches("class Y { template void x() { (*this).x(); } };", unresolvedMemberExpr(isArrow()))); } TEST_P(ASTMatchersTest, IsExplicit_CXXConversionDecl) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("struct S { explicit operator int(); };", cxxConversionDecl(isExplicit()))); EXPECT_TRUE(notMatches("struct S { operator int(); };", cxxConversionDecl(isExplicit()))); } TEST_P(ASTMatchersTest, IsExplicit_CXXConversionDecl_CXX20) { if (!GetParam().isCXX20OrLater()) { return; } EXPECT_TRUE( notMatches("template struct S { explicit(b) operator int(); };", cxxConversionDecl(isExplicit()))); EXPECT_TRUE(matches("struct S { explicit(true) operator int(); };", cxxConversionDecl(isExplicit()))); EXPECT_TRUE(notMatches("struct S { explicit(false) operator int(); };", cxxConversionDecl(isExplicit()))); } TEST_P(ASTMatchersTest, ArgumentCountIs_CallExpr) { StatementMatcher Call1Arg = callExpr(argumentCountIs(1)); EXPECT_TRUE(matches("void x(int) { x(0); }", Call1Arg)); EXPECT_TRUE(notMatches("void x(int, int) { x(0, 0); }", Call1Arg)); } TEST_P(ASTMatchersTest, ArgumentCountIs_CallExpr_CXX) { if (!GetParam().isCXX()) { return; } StatementMatcher Call1Arg = callExpr(argumentCountIs(1)); EXPECT_TRUE(matches("class X { void x(int) { x(0); } };", Call1Arg)); } TEST_P(ASTMatchersTest, ParameterCountIs) { DeclarationMatcher Function1Arg = functionDecl(parameterCountIs(1)); EXPECT_TRUE(matches("void f(int i) {}", Function1Arg)); EXPECT_TRUE(notMatches("void f() {}", Function1Arg)); EXPECT_TRUE(notMatches("void f(int i, int j, int k) {}", Function1Arg)); EXPECT_TRUE(matches("void f(int i, ...) {};", Function1Arg)); } TEST_P(ASTMatchersTest, ParameterCountIs_CXX) { if (!GetParam().isCXX()) { return; } DeclarationMatcher Function1Arg = functionDecl(parameterCountIs(1)); EXPECT_TRUE(matches("class X { void f(int i) {} };", Function1Arg)); } TEST_P(ASTMatchersTest, References) { if (!GetParam().isCXX()) { // FIXME: Add a test for `references()` that does not depend on C++. return; } DeclarationMatcher ReferenceClassX = varDecl(hasType(references(recordDecl(hasName("X"))))); EXPECT_TRUE( matches("class X {}; void y(X y) { X &x = y; }", ReferenceClassX)); EXPECT_TRUE( matches("class X {}; void y(X y) { const X &x = y; }", ReferenceClassX)); // The match here is on the implicit copy constructor code for // class X, not on code 'X x = y'. EXPECT_TRUE(matches("class X {}; void y(X y) { X x = y; }", ReferenceClassX)); EXPECT_TRUE(notMatches("class X {}; extern X x;", ReferenceClassX)); EXPECT_TRUE( notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX)); } TEST_P(ASTMatchersTest, HasLocalQualifiers) { if (!GetParam().isCXX11OrLater()) { // FIXME: Add a test for `hasLocalQualifiers()` that does not depend on C++. return; } EXPECT_TRUE(notMatches("typedef const int const_int; const_int i = 1;", varDecl(hasType(hasLocalQualifiers())))); EXPECT_TRUE(matches("int *const j = nullptr;", varDecl(hasType(hasLocalQualifiers())))); EXPECT_TRUE( matches("int *volatile k;", varDecl(hasType(hasLocalQualifiers())))); EXPECT_TRUE(notMatches("int m;", varDecl(hasType(hasLocalQualifiers())))); } TEST_P(ASTMatchersTest, IsExternC_MatchesExternCFunctionDeclarations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("extern \"C\" void f() {}", functionDecl(isExternC()))); EXPECT_TRUE( matches("extern \"C\" { void f() {} }", functionDecl(isExternC()))); EXPECT_TRUE(notMatches("void f() {}", functionDecl(isExternC()))); } TEST_P(ASTMatchersTest, IsExternC_MatchesExternCVariableDeclarations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("extern \"C\" int i;", varDecl(isExternC()))); EXPECT_TRUE(matches("extern \"C\" { int i; }", varDecl(isExternC()))); EXPECT_TRUE(notMatches("int i;", varDecl(isExternC()))); } TEST_P(ASTMatchersTest, IsStaticStorageClass) { EXPECT_TRUE( matches("static void f() {}", functionDecl(isStaticStorageClass()))); EXPECT_TRUE(matches("static int i = 1;", varDecl(isStaticStorageClass()))); EXPECT_TRUE(notMatches("int i = 1;", varDecl(isStaticStorageClass()))); EXPECT_TRUE(notMatches("extern int i;", varDecl(isStaticStorageClass()))); EXPECT_TRUE(notMatches("void f() {}", functionDecl(isStaticStorageClass()))); } TEST_P(ASTMatchersTest, IsDefaulted) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class A { ~A(); };", functionDecl(hasName("~A"), isDefaulted()))); EXPECT_TRUE(matches("class B { ~B() = default; };", functionDecl(hasName("~B"), isDefaulted()))); } TEST_P(ASTMatchersTest, IsDeleted) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( notMatches("void Func();", functionDecl(hasName("Func"), isDeleted()))); EXPECT_TRUE(matches("void Func() = delete;", functionDecl(hasName("Func"), isDeleted()))); } TEST_P(ASTMatchersTest, IsNoThrow_DynamicExceptionSpec) { if (!GetParam().supportsCXXDynamicExceptionSpecification()) { return; } EXPECT_TRUE(notMatches("void f();", functionDecl(isNoThrow()))); EXPECT_TRUE(notMatches("void f() throw(int);", functionDecl(isNoThrow()))); EXPECT_TRUE(matches("void f() throw();", functionDecl(isNoThrow()))); EXPECT_TRUE(notMatches("void f();", functionProtoType(isNoThrow()))); EXPECT_TRUE( notMatches("void f() throw(int);", functionProtoType(isNoThrow()))); EXPECT_TRUE(matches("void f() throw();", functionProtoType(isNoThrow()))); } TEST_P(ASTMatchersTest, IsNoThrow_CXX11) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE( notMatches("void f() noexcept(false);", functionDecl(isNoThrow()))); EXPECT_TRUE(matches("void f() noexcept;", functionDecl(isNoThrow()))); EXPECT_TRUE( notMatches("void f() noexcept(false);", functionProtoType(isNoThrow()))); EXPECT_TRUE(matches("void f() noexcept;", functionProtoType(isNoThrow()))); } TEST_P(ASTMatchersTest, IsConstexpr) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("constexpr int foo = 42;", varDecl(hasName("foo"), isConstexpr()))); EXPECT_TRUE(matches("constexpr int bar();", functionDecl(hasName("bar"), isConstexpr()))); } TEST_P(ASTMatchersTest, IsConstexpr_MatchesIfConstexpr) { if (!GetParam().isCXX17OrLater()) { return; } EXPECT_TRUE( matches("void baz() { if constexpr(1 > 0) {} }", ifStmt(isConstexpr()))); EXPECT_TRUE( notMatches("void baz() { if (1 > 0) {} }", ifStmt(isConstexpr()))); } TEST_P(ASTMatchersTest, HasInitStatement_MatchesSelectionInitializers) { EXPECT_TRUE(notMatches("void baz() { if (1 > 0) {} }", ifStmt(hasInitStatement(anything())))); EXPECT_TRUE(notMatches("void baz(int i) { switch (i) { default: break; } }", switchStmt(hasInitStatement(anything())))); } TEST_P(ASTMatchersTest, HasInitStatement_MatchesSelectionInitializers_CXX) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("void baz() { if (int i = 1) {} }", ifStmt(hasInitStatement(anything())))); } TEST_P(ASTMatchersTest, HasInitStatement_MatchesSelectionInitializers_CXX17) { if (!GetParam().isCXX17OrLater()) { return; } EXPECT_TRUE(matches("void baz() { if (int i = 1; i > 0) {} }", ifStmt(hasInitStatement(anything())))); EXPECT_TRUE( matches("void baz(int i) { switch (int j = i; j) { default: break; } }", switchStmt(hasInitStatement(anything())))); } TEST_P(ASTMatchersTest, HasInitStatement_MatchesRangeForInitializers) { if (!GetParam().isCXX20OrLater()) { return; } EXPECT_TRUE(matches("void baz() {" "int items[] = {};" "for (auto &arr = items; auto &item : arr) {}" "}", cxxForRangeStmt(hasInitStatement(anything())))); EXPECT_TRUE(notMatches("void baz() {" "int items[] = {};" "for (auto &item : items) {}" "}", cxxForRangeStmt(hasInitStatement(anything())))); } TEST_P(ASTMatchersTest, TemplateArgumentCountIs) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("template struct C {}; C c;", classTemplateSpecializationDecl(templateArgumentCountIs(1)))); EXPECT_TRUE( notMatches("template struct C {}; C c;", classTemplateSpecializationDecl(templateArgumentCountIs(2)))); EXPECT_TRUE(matches("template struct C {}; C c;", templateSpecializationType(templateArgumentCountIs(1)))); EXPECT_TRUE( notMatches("template struct C {}; C c;", templateSpecializationType(templateArgumentCountIs(2)))); } TEST_P(ASTMatchersTest, IsIntegral) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( "template struct C {}; C<42> c;", classTemplateSpecializationDecl(hasAnyTemplateArgument(isIntegral())))); EXPECT_TRUE(notMatches("template struct C {}; C c;", classTemplateSpecializationDecl(hasAnyTemplateArgument( templateArgument(isIntegral()))))); } TEST_P(ASTMatchersTest, EqualsIntegralValue) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("template struct C {}; C<42> c;", classTemplateSpecializationDecl( hasAnyTemplateArgument(equalsIntegralValue("42"))))); EXPECT_TRUE(matches("template struct C {}; C<-42> c;", classTemplateSpecializationDecl( hasAnyTemplateArgument(equalsIntegralValue("-42"))))); EXPECT_TRUE(matches("template struct C {}; C<-0042> c;", classTemplateSpecializationDecl( hasAnyTemplateArgument(equalsIntegralValue("-34"))))); EXPECT_TRUE(notMatches("template struct C {}; C<42> c;", classTemplateSpecializationDecl(hasAnyTemplateArgument( equalsIntegralValue("0042"))))); } TEST_P(ASTMatchersTest, AccessSpecDecl) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class C { public: int i; };", accessSpecDecl())); EXPECT_TRUE( matches("class C { public: int i; };", accessSpecDecl(isPublic()))); EXPECT_TRUE( notMatches("class C { public: int i; };", accessSpecDecl(isProtected()))); EXPECT_TRUE( notMatches("class C { public: int i; };", accessSpecDecl(isPrivate()))); EXPECT_TRUE(notMatches("class C { int i; };", accessSpecDecl())); } TEST_P(ASTMatchersTest, IsFinal) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("class X final {};", cxxRecordDecl(isFinal()))); EXPECT_TRUE(matches("class X { virtual void f() final; };", cxxMethodDecl(isFinal()))); EXPECT_TRUE(notMatches("class X {};", cxxRecordDecl(isFinal()))); EXPECT_TRUE( notMatches("class X { virtual void f(); };", cxxMethodDecl(isFinal()))); } TEST_P(ASTMatchersTest, IsVirtual) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class X { virtual int f(); };", cxxMethodDecl(isVirtual(), hasName("::X::f")))); EXPECT_TRUE(notMatches("class X { int f(); };", cxxMethodDecl(isVirtual()))); } TEST_P(ASTMatchersTest, IsVirtualAsWritten) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class A { virtual int f(); };" "class B : public A { int f(); };", cxxMethodDecl(isVirtualAsWritten(), hasName("::A::f")))); EXPECT_TRUE( notMatches("class A { virtual int f(); };" "class B : public A { int f(); };", cxxMethodDecl(isVirtualAsWritten(), hasName("::B::f")))); } TEST_P(ASTMatchersTest, IsPure) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class X { virtual int f() = 0; };", cxxMethodDecl(isPure(), hasName("::X::f")))); EXPECT_TRUE(notMatches("class X { int f(); };", cxxMethodDecl(isPure()))); } TEST_P(ASTMatchersTest, IsCopyAssignmentOperator) { if (!GetParam().isCXX()) { return; } auto CopyAssignment = cxxMethodDecl(isCopyAssignmentOperator(), unless(isImplicit())); EXPECT_TRUE(matches("class X { X &operator=(X); };", CopyAssignment)); EXPECT_TRUE(matches("class X { X &operator=(X &); };", CopyAssignment)); EXPECT_TRUE(matches("class X { X &operator=(const X &); };", CopyAssignment)); EXPECT_TRUE(matches("class X { X &operator=(volatile X &); };", // CopyAssignment)); EXPECT_TRUE(matches("class X { X &operator=(const volatile X &); };", CopyAssignment)); EXPECT_TRUE(notMatches("class X { X &operator=(X &&); };", CopyAssignment)); } TEST_P(ASTMatchersTest, IsMoveAssignmentOperator) { if (!GetParam().isCXX()) { return; } auto MoveAssignment = cxxMethodDecl(isMoveAssignmentOperator(), unless(isImplicit())); EXPECT_TRUE(notMatches("class X { X &operator=(X); };", MoveAssignment)); EXPECT_TRUE(matches("class X { X &operator=(X &&); };", MoveAssignment)); EXPECT_TRUE(matches("class X { X &operator=(const X &&); };", // MoveAssignment)); EXPECT_TRUE(matches("class X { X &operator=(volatile X &&); };", // MoveAssignment)); EXPECT_TRUE(matches("class X { X &operator=(const volatile X &&); };", MoveAssignment)); EXPECT_TRUE(notMatches("class X { X &operator=(X &); };", MoveAssignment)); } TEST_P(ASTMatchersTest, IsConst) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("struct A { void foo() const; };", cxxMethodDecl(isConst()))); EXPECT_TRUE( notMatches("struct A { void foo(); };", cxxMethodDecl(isConst()))); } TEST_P(ASTMatchersTest, IsOverride) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class X { virtual int f(); }; " "class Y : public X { int f(); };", cxxMethodDecl(isOverride(), hasName("::Y::f")))); EXPECT_TRUE(notMatches("class X { virtual int f(); }; " "class Y : public X { int f(); };", cxxMethodDecl(isOverride(), hasName("::X::f")))); EXPECT_TRUE(notMatches("class X { int f(); }; " "class Y : public X { int f(); };", cxxMethodDecl(isOverride()))); EXPECT_TRUE(notMatches("class X { int f(); int f(int); }; ", cxxMethodDecl(isOverride()))); EXPECT_TRUE( matches("template struct Y : Base { void f() override;};", cxxMethodDecl(isOverride(), hasName("::Y::f")))); } TEST_P(ASTMatchersTest, HasArgument_CXXConstructorDecl) { if (!GetParam().isCXX()) { return; } auto Constructor = traverse( TK_AsIs, cxxConstructExpr(hasArgument(0, declRefExpr(to(varDecl(hasName("y"))))))); EXPECT_TRUE(matches( "class X { public: X(int); }; void x() { int y; X x(y); }", Constructor)); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { int y; X x = X(y); }", Constructor)); EXPECT_TRUE( matches("class X { public: X(int); }; void x() { int y; X x = y; }", Constructor)); EXPECT_TRUE(notMatches( "class X { public: X(int); }; void x() { int z; X x(z); }", Constructor)); StatementMatcher WrongIndex = traverse(TK_AsIs, cxxConstructExpr(hasArgument( 42, declRefExpr(to(varDecl(hasName("y"))))))); EXPECT_TRUE(notMatches( "class X { public: X(int); }; void x() { int y; X x(y); }", WrongIndex)); } TEST_P(ASTMatchersTest, ArgumentCountIs_CXXConstructExpr) { if (!GetParam().isCXX()) { return; } auto Constructor1Arg = traverse(TK_AsIs, cxxConstructExpr(argumentCountIs(1))); EXPECT_TRUE(matches("class X { public: X(int); }; void x() { X x(0); }", Constructor1Arg)); EXPECT_TRUE(matches("class X { public: X(int); }; void x() { X x = X(0); }", Constructor1Arg)); EXPECT_TRUE(matches("class X { public: X(int); }; void x() { X x = 0; }", Constructor1Arg)); EXPECT_TRUE( notMatches("class X { public: X(int, int); }; void x() { X x(0, 0); }", Constructor1Arg)); } TEST(ASTMatchersTest, NamesMember_CXXDependentScopeMemberExpr) { // Member functions: { auto Code = "template struct S{ void mem(); }; template " " void x() { S s; s.mem(); }"; EXPECT_TRUE(matches( Code, cxxDependentScopeMemberExpr( hasObjectExpression(declRefExpr(hasType(templateSpecializationType( hasDeclaration(classTemplateDecl(has(cxxRecordDecl( has(cxxMethodDecl(hasName("mem")).bind("templMem")))))))))), memberHasSameNameAsBoundNode("templMem")))); EXPECT_TRUE( matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); } // Member variables: { auto Code = "template struct S{ int mem; }; template " " void x() { S s; s.mem; }"; EXPECT_TRUE( matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); EXPECT_TRUE(matches( Code, cxxDependentScopeMemberExpr( hasObjectExpression(declRefExpr(hasType(templateSpecializationType( hasDeclaration(classTemplateDecl(has(cxxRecordDecl( has(fieldDecl(hasName("mem")).bind("templMem")))))))))), memberHasSameNameAsBoundNode("templMem")))); } // static member variables: { auto Code = "template struct S{ static int mem; }; template " " void x() { S s; s.mem; }"; EXPECT_TRUE( matches(Code, cxxDependentScopeMemberExpr(hasMemberName("mem")))); EXPECT_TRUE(matches( Code, cxxDependentScopeMemberExpr( hasObjectExpression(declRefExpr(hasType(templateSpecializationType( hasDeclaration(classTemplateDecl(has(cxxRecordDecl( has(varDecl(hasName("mem")).bind("templMem")))))))))), memberHasSameNameAsBoundNode("templMem")))); } { auto Code = R"cpp( template struct S { bool operator==(int) const { return true; } }; template void func(T t) { S s; s.operator==(1); } )cpp"; EXPECT_TRUE(matches( Code, cxxDependentScopeMemberExpr(hasMemberName("operator==")))); } // other named decl: { auto Code = "template struct S{ static int mem; }; struct " "mem{}; template " " void x() { S s; s.mem; }"; EXPECT_TRUE(matches( Code, translationUnitDecl(has(cxxRecordDecl(hasName("mem"))), hasDescendant(cxxDependentScopeMemberExpr())))); EXPECT_FALSE(matches( Code, translationUnitDecl(has(cxxRecordDecl(hasName("mem")).bind("templMem")), hasDescendant(cxxDependentScopeMemberExpr( memberHasSameNameAsBoundNode("templMem")))))); } } TEST(ASTMatchersTest, ArgumentCountIs_CXXUnresolvedConstructExpr) { const auto *Code = "template struct S{}; template void " "x() { auto s = S(); }"; EXPECT_TRUE(matches(Code, cxxUnresolvedConstructExpr(argumentCountIs(0)))); EXPECT_TRUE(notMatches(Code, cxxUnresolvedConstructExpr(argumentCountIs(1)))); } TEST(ASTMatchersTest, HasArgument_CXXUnresolvedConstructExpr) { const auto *Code = "template struct S{ S(int){} }; template void x() { int y; auto s = S(y); }"; EXPECT_TRUE(matches(Code, cxxUnresolvedConstructExpr(hasArgument( 0, declRefExpr(to(varDecl(hasName("y")))))))); EXPECT_TRUE( notMatches(Code, cxxUnresolvedConstructExpr(hasArgument( 0, declRefExpr(to(varDecl(hasName("x")))))))); } TEST_P(ASTMatchersTest, IsListInitialization) { if (!GetParam().isCXX11OrLater()) { return; } auto ConstructorListInit = traverse(TK_AsIs, varDecl(has(cxxConstructExpr(isListInitialization())))); EXPECT_TRUE(matches("class X { public: X(int); }; void x() { X x{0}; }", ConstructorListInit)); EXPECT_FALSE(matches("class X { public: X(int); }; void x() { X x(0); }", ConstructorListInit)); } TEST_P(ASTMatchersTest, IsImplicit_CXXConstructorDecl) { if (!GetParam().isCXX()) { return; } // This one doesn't match because the constructor is not added by the // compiler (it is not needed). EXPECT_TRUE(notMatches("class Foo { };", cxxConstructorDecl(isImplicit()))); // The compiler added the implicit default constructor. EXPECT_TRUE(matches("class Foo { }; Foo* f = new Foo();", cxxConstructorDecl(isImplicit()))); EXPECT_TRUE(matches("class Foo { Foo(){} };", cxxConstructorDecl(unless(isImplicit())))); // The compiler added an implicit assignment operator. EXPECT_TRUE(matches("struct A { int x; } a = {0}, b = a; void f() { a = b; }", cxxMethodDecl(isImplicit(), hasName("operator=")))); } TEST_P(ASTMatchersTest, IsExplicit_CXXConstructorDecl) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("struct S { explicit S(int); };", cxxConstructorDecl(isExplicit()))); EXPECT_TRUE( notMatches("struct S { S(int); };", cxxConstructorDecl(isExplicit()))); } TEST_P(ASTMatchersTest, IsExplicit_CXXConstructorDecl_CXX20) { if (!GetParam().isCXX20OrLater()) { return; } EXPECT_TRUE(notMatches("template struct S { explicit(b) S(int);};", cxxConstructorDecl(isExplicit()))); EXPECT_TRUE(matches("struct S { explicit(true) S(int);};", cxxConstructorDecl(isExplicit()))); EXPECT_TRUE(notMatches("struct S { explicit(false) S(int);};", cxxConstructorDecl(isExplicit()))); } TEST_P(ASTMatchersTest, IsExplicit_CXXDeductionGuideDecl) { if (!GetParam().isCXX17OrLater()) { return; } EXPECT_TRUE(notMatches("template struct S { S(int);};" "S(int) -> S;", cxxDeductionGuideDecl(isExplicit()))); EXPECT_TRUE(matches("template struct S { S(int);};" "explicit S(int) -> S;", cxxDeductionGuideDecl(isExplicit()))); } TEST_P(ASTMatchersTest, IsExplicit_CXXDeductionGuideDecl_CXX20) { if (!GetParam().isCXX20OrLater()) { return; } EXPECT_TRUE(matches("template struct S { S(int);};" "explicit(true) S(int) -> S;", cxxDeductionGuideDecl(isExplicit()))); EXPECT_TRUE(notMatches("template struct S { S(int);};" "explicit(false) S(int) -> S;", cxxDeductionGuideDecl(isExplicit()))); EXPECT_TRUE( notMatches("template struct S { S(int);};" "template explicit(b) S(int) -> S;", cxxDeductionGuideDecl(isExplicit()))); } TEST_P(ASTMatchersTest, CXXConstructorDecl_Kinds) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("struct S { S(); };", cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(); };", cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(); };", cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(const S&); };", cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); EXPECT_TRUE( matches("struct S { S(const S&); };", cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(const S&); };", cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(S&&); };", cxxConstructorDecl(isDefaultConstructor(), unless(isImplicit())))); EXPECT_TRUE(notMatches( "struct S { S(S&&); };", cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())))); EXPECT_TRUE( matches("struct S { S(S&&); };", cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())))); } TEST_P(ASTMatchersTest, IsUserProvided) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(notMatches("struct S { int X = 0; };", cxxConstructorDecl(isUserProvided()))); EXPECT_TRUE(notMatches("struct S { S() = default; };", cxxConstructorDecl(isUserProvided()))); EXPECT_TRUE(notMatches("struct S { S() = delete; };", cxxConstructorDecl(isUserProvided()))); EXPECT_TRUE( matches("struct S { S(); };", cxxConstructorDecl(isUserProvided()))); EXPECT_TRUE(matches("struct S { S(); }; S::S(){}", cxxConstructorDecl(isUserProvided()))); } TEST_P(ASTMatchersTest, IsDelegatingConstructor) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(notMatches("struct S { S(); S(int); int X; };", cxxConstructorDecl(isDelegatingConstructor()))); EXPECT_TRUE(notMatches("struct S { S(){} S(int X) : X(X) {} int X; };", cxxConstructorDecl(isDelegatingConstructor()))); EXPECT_TRUE(matches( "struct S { S() : S(0) {} S(int X) : X(X) {} int X; };", cxxConstructorDecl(isDelegatingConstructor(), parameterCountIs(0)))); EXPECT_TRUE(matches( "struct S { S(); S(int X); int X; }; S::S(int X) : S() {}", cxxConstructorDecl(isDelegatingConstructor(), parameterCountIs(1)))); } TEST_P(ASTMatchersTest, HasSize) { StatementMatcher Literal = stringLiteral(hasSize(4)); EXPECT_TRUE(matches("const char *s = \"abcd\";", Literal)); // with escaped characters EXPECT_TRUE(matches("const char *s = \"\x05\x06\x07\x08\";", Literal)); // no matching, too small EXPECT_TRUE(notMatches("const char *s = \"ab\";", Literal)); } TEST_P(ASTMatchersTest, HasSize_CXX) { if (!GetParam().isCXX()) { // FIXME: Fix this test to also work in non-C++ language modes. return; } StatementMatcher Literal = stringLiteral(hasSize(4)); // wide string EXPECT_TRUE(matches("const wchar_t *s = L\"abcd\";", Literal)); } TEST_P(ASTMatchersTest, HasName_MatchesNamespaces) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", recordDecl(hasName("a::b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", recordDecl(hasName("::a::b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", recordDecl(hasName("b::C")))); EXPECT_TRUE(matches("namespace a { namespace b { class C; } }", recordDecl(hasName("C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("c::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("a::c::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("a::b::A")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("z::a::b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class C; } }", recordDecl(hasName("a+b::C")))); EXPECT_TRUE(notMatches("namespace a { namespace b { class AC; } }", recordDecl(hasName("C")))); } TEST_P(ASTMatchersTest, HasName_MatchesOuterClasses) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class A { class B { class C; }; };", recordDecl(hasName("A::B::C")))); EXPECT_TRUE(matches("class A { class B { class C; }; };", recordDecl(hasName("::A::B::C")))); EXPECT_TRUE(matches("class A { class B { class C; }; };", recordDecl(hasName("B::C")))); EXPECT_TRUE( matches("class A { class B { class C; }; };", recordDecl(hasName("C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("c::B::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("A::c::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("A::B::A")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("::B::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("z::A::B::C")))); EXPECT_TRUE(notMatches("class A { class B { class C; }; };", recordDecl(hasName("A+B::C")))); } TEST_P(ASTMatchersTest, HasName_MatchesInlinedNamespaces) { if (!GetParam().isCXX()) { return; } StringRef code = "namespace a { inline namespace b { class C; } }"; EXPECT_TRUE(matches(code, recordDecl(hasName("a::b::C")))); EXPECT_TRUE(matches(code, recordDecl(hasName("a::C")))); EXPECT_TRUE(matches(code, recordDecl(hasName("::a::b::C")))); EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C")))); } TEST_P(ASTMatchersTest, HasName_MatchesAnonymousNamespaces) { if (!GetParam().isCXX()) { return; } StringRef code = "namespace a { namespace { class C; } }"; EXPECT_TRUE( matches(code, recordDecl(hasName("a::(anonymous namespace)::C")))); EXPECT_TRUE(matches(code, recordDecl(hasName("a::C")))); EXPECT_TRUE( matches(code, recordDecl(hasName("::a::(anonymous namespace)::C")))); EXPECT_TRUE(matches(code, recordDecl(hasName("::a::C")))); } TEST_P(ASTMatchersTest, HasName_MatchesAnonymousOuterClasses) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class A { class { class C; } x; };", recordDecl(hasName("A::(anonymous class)::C")))); EXPECT_TRUE(matches("class A { class { class C; } x; };", recordDecl(hasName("::A::(anonymous class)::C")))); EXPECT_FALSE(matches("class A { class { class C; } x; };", recordDecl(hasName("::A::C")))); EXPECT_TRUE(matches("class A { struct { class C; } x; };", recordDecl(hasName("A::(anonymous struct)::C")))); EXPECT_TRUE(matches("class A { struct { class C; } x; };", recordDecl(hasName("::A::(anonymous struct)::C")))); EXPECT_FALSE(matches("class A { struct { class C; } x; };", recordDecl(hasName("::A::C")))); } TEST_P(ASTMatchersTest, HasName_MatchesFunctionScope) { if (!GetParam().isCXX()) { return; } StringRef code = "namespace a { void F(int a) { struct S { int m; }; int i; } }"; EXPECT_TRUE(matches(code, varDecl(hasName("i")))); EXPECT_FALSE(matches(code, varDecl(hasName("F()::i")))); EXPECT_TRUE(matches(code, fieldDecl(hasName("m")))); EXPECT_TRUE(matches(code, fieldDecl(hasName("S::m")))); EXPECT_TRUE(matches(code, fieldDecl(hasName("F(int)::S::m")))); EXPECT_TRUE(matches(code, fieldDecl(hasName("a::F(int)::S::m")))); EXPECT_TRUE(matches(code, fieldDecl(hasName("::a::F(int)::S::m")))); } TEST_P(ASTMatchersTest, HasName_QualifiedStringMatchesThroughLinkage) { if (!GetParam().isCXX()) { return; } // https://bugs.llvm.org/show_bug.cgi?id=42193 StringRef code = R"cpp(namespace foo { extern "C" void test(); })cpp"; EXPECT_TRUE(matches(code, functionDecl(hasName("test")))); EXPECT_TRUE(matches(code, functionDecl(hasName("foo::test")))); EXPECT_TRUE(matches(code, functionDecl(hasName("::foo::test")))); EXPECT_TRUE(notMatches(code, functionDecl(hasName("::test")))); code = R"cpp(namespace foo { extern "C" { void test(); } })cpp"; EXPECT_TRUE(matches(code, functionDecl(hasName("test")))); EXPECT_TRUE(matches(code, functionDecl(hasName("foo::test")))); EXPECT_TRUE(matches(code, functionDecl(hasName("::foo::test")))); EXPECT_TRUE(notMatches(code, functionDecl(hasName("::test")))); } TEST_P(ASTMatchersTest, HasAnyName) { if (!GetParam().isCXX()) { // FIXME: Add a test for `hasAnyName()` that does not depend on C++. return; } StringRef Code = "namespace a { namespace b { class C; } }"; EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "a::b::C")))); EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("a::b::C", "XX")))); EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX::C", "a::b::C")))); EXPECT_TRUE(matches(Code, recordDecl(hasAnyName("XX", "C")))); EXPECT_TRUE(notMatches(Code, recordDecl(hasAnyName("::C", "::b::C")))); EXPECT_TRUE( matches(Code, recordDecl(hasAnyName("::C", "::b::C", "::a::b::C")))); std::vector Names = {"::C", "::b::C", "::a::b::C"}; EXPECT_TRUE(matches(Code, recordDecl(hasAnyName(Names)))); } TEST_P(ASTMatchersTest, IsDefinition) { DeclarationMatcher DefinitionOfClassA = recordDecl(hasName("A"), isDefinition()); EXPECT_TRUE(matches("struct A {};", DefinitionOfClassA)); EXPECT_TRUE(notMatches("struct A;", DefinitionOfClassA)); DeclarationMatcher DefinitionOfVariableA = varDecl(hasName("a"), isDefinition()); EXPECT_TRUE(matches("int a;", DefinitionOfVariableA)); EXPECT_TRUE(notMatches("extern int a;", DefinitionOfVariableA)); } TEST_P(ASTMatchersTest, IsDefinition_CXX) { if (!GetParam().isCXX()) { return; } DeclarationMatcher DefinitionOfMethodA = cxxMethodDecl(hasName("a"), isDefinition()); EXPECT_TRUE(matches("class A { void a() {} };", DefinitionOfMethodA)); EXPECT_TRUE(notMatches("class A { void a(); };", DefinitionOfMethodA)); DeclarationMatcher DefinitionOfObjCMethodA = objcMethodDecl(hasName("a"), isDefinition()); EXPECT_TRUE(matchesObjC("@interface A @end " "@implementation A; -(void)a {} @end", DefinitionOfObjCMethodA)); EXPECT_TRUE( notMatchesObjC("@interface A; - (void)a; @end", DefinitionOfObjCMethodA)); } TEST_P(ASTMatchersTest, HandlesNullQualTypes) { if (!GetParam().isCXX()) { // FIXME: Add an equivalent test that does not depend on C++. return; } // FIXME: Add a Type matcher so we can replace uses of this // variable with Type(True()) const TypeMatcher AnyType = anything(); // We don't really care whether this matcher succeeds; we're testing that // it completes without crashing. EXPECT_TRUE(matches( "struct A { };" "template " "void f(T t) {" " T local_t(t /* this becomes a null QualType in the AST */);" "}" "void g() {" " f(0);" "}", expr(hasType(TypeMatcher(anyOf(TypeMatcher(hasDeclaration(anything())), pointsTo(AnyType), references(AnyType) // Other QualType matchers should go here. )))))); } TEST_P(ASTMatchersTest, ObjCIvarRefExpr) { StringRef ObjCString = "@interface A @end " "@implementation A { A *x; } - (void) func { x = 0; } @end"; EXPECT_TRUE(matchesObjC(ObjCString, objcIvarRefExpr())); EXPECT_TRUE(matchesObjC( ObjCString, objcIvarRefExpr(hasDeclaration(namedDecl(hasName("x")))))); EXPECT_FALSE(matchesObjC( ObjCString, objcIvarRefExpr(hasDeclaration(namedDecl(hasName("y")))))); } TEST_P(ASTMatchersTest, BlockExpr) { EXPECT_TRUE(matchesObjC("void f() { ^{}(); }", blockExpr())); } TEST_P(ASTMatchersTest, StatementCountIs_FindsNoStatementsInAnEmptyCompoundStatement) { EXPECT_TRUE(matches("void f() { }", compoundStmt(statementCountIs(0)))); EXPECT_TRUE(notMatches("void f() {}", compoundStmt(statementCountIs(1)))); } TEST_P(ASTMatchersTest, StatementCountIs_AppearsToMatchOnlyOneCount) { EXPECT_TRUE(matches("void f() { 1; }", compoundStmt(statementCountIs(1)))); EXPECT_TRUE(notMatches("void f() { 1; }", compoundStmt(statementCountIs(0)))); EXPECT_TRUE(notMatches("void f() { 1; }", compoundStmt(statementCountIs(2)))); } TEST_P(ASTMatchersTest, StatementCountIs_WorksWithMultipleStatements) { EXPECT_TRUE( matches("void f() { 1; 2; 3; }", compoundStmt(statementCountIs(3)))); } TEST_P(ASTMatchersTest, StatementCountIs_WorksWithNestedCompoundStatements) { EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", compoundStmt(statementCountIs(1)))); EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", compoundStmt(statementCountIs(2)))); EXPECT_TRUE(notMatches("void f() { { 1; } { 1; 2; 3; 4; } }", compoundStmt(statementCountIs(3)))); EXPECT_TRUE(matches("void f() { { 1; } { 1; 2; 3; 4; } }", compoundStmt(statementCountIs(4)))); } TEST_P(ASTMatchersTest, Member_WorksInSimplestCase) { if (!GetParam().isCXX()) { // FIXME: Add a test for `member()` that does not depend on C++. return; } EXPECT_TRUE(matches("struct { int first; } s; int i(s.first);", memberExpr(member(hasName("first"))))); } TEST_P(ASTMatchersTest, Member_DoesNotMatchTheBaseExpression) { if (!GetParam().isCXX()) { // FIXME: Add a test for `member()` that does not depend on C++. return; } // Don't pick out the wrong part of the member expression, this should // be checking the member (name) only. EXPECT_TRUE(notMatches("struct { int i; } first; int i(first.i);", memberExpr(member(hasName("first"))))); } TEST_P(ASTMatchersTest, Member_MatchesInMemberFunctionCall) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("void f() {" " struct { void first() {}; } s;" " s.first();" "};", memberExpr(member(hasName("first"))))); } TEST_P(ASTMatchersTest, FieldDecl) { EXPECT_TRUE( matches("struct A { int i; }; void f() { struct A a; a.i = 2; }", memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); EXPECT_TRUE( notMatches("struct A { float f; }; void f() { struct A a; a.f = 2.0f; }", memberExpr(hasDeclaration(fieldDecl(hasType(isInteger())))))); } TEST_P(ASTMatchersTest, IsBitField) { EXPECT_TRUE(matches("struct C { int a : 2; int b; };", fieldDecl(isBitField(), hasName("a")))); EXPECT_TRUE(notMatches("struct C { int a : 2; int b; };", fieldDecl(isBitField(), hasName("b")))); EXPECT_TRUE(matches("struct C { int a : 2; int b : 4; };", fieldDecl(isBitField(), hasBitWidth(2), hasName("a")))); } TEST_P(ASTMatchersTest, HasInClassInitializer) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("class C { int a = 2; int b; };", fieldDecl(hasInClassInitializer(integerLiteral(equals(2))), hasName("a")))); EXPECT_TRUE( notMatches("class C { int a = 2; int b; };", fieldDecl(hasInClassInitializer(anything()), hasName("b")))); } TEST_P(ASTMatchersTest, IsPublic_IsProtected_IsPrivate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("struct A { int i; };", fieldDecl(isPublic(), hasName("i")))); EXPECT_TRUE(notMatches("struct A { int i; };", fieldDecl(isProtected(), hasName("i")))); EXPECT_TRUE( notMatches("struct A { int i; };", fieldDecl(isPrivate(), hasName("i")))); EXPECT_TRUE( notMatches("class A { int i; };", fieldDecl(isPublic(), hasName("i")))); EXPECT_TRUE(notMatches("class A { int i; };", fieldDecl(isProtected(), hasName("i")))); EXPECT_TRUE( matches("class A { int i; };", fieldDecl(isPrivate(), hasName("i")))); EXPECT_TRUE(notMatches("class A { protected: int i; };", fieldDecl(isPublic(), hasName("i")))); EXPECT_TRUE(matches("class A { protected: int i; };", fieldDecl(isProtected(), hasName("i")))); EXPECT_TRUE(notMatches("class A { protected: int i; };", fieldDecl(isPrivate(), hasName("i")))); // Non-member decls have the AccessSpecifier AS_none and thus aren't matched. EXPECT_TRUE(notMatches("int i;", varDecl(isPublic(), hasName("i")))); EXPECT_TRUE(notMatches("int i;", varDecl(isProtected(), hasName("i")))); EXPECT_TRUE(notMatches("int i;", varDecl(isPrivate(), hasName("i")))); } TEST_P(ASTMatchersTest, HasDynamicExceptionSpec_MatchesDynamicExceptionSpecifications) { if (!GetParam().supportsCXXDynamicExceptionSpecification()) { return; } EXPECT_TRUE(notMatches("void f();", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE( matches("void j() throw();", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE( matches("void k() throw(int);", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE( matches("void l() throw(...);", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE( notMatches("void f();", functionProtoType(hasDynamicExceptionSpec()))); EXPECT_TRUE(matches("void j() throw();", functionProtoType(hasDynamicExceptionSpec()))); EXPECT_TRUE(matches("void k() throw(int);", functionProtoType(hasDynamicExceptionSpec()))); EXPECT_TRUE(matches("void l() throw(...);", functionProtoType(hasDynamicExceptionSpec()))); } TEST_P(ASTMatchersTest, HasDynamicExceptionSpec_MatchesDynamicExceptionSpecifications_CXX11) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(notMatches("void g() noexcept;", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE(notMatches("void h() noexcept(true);", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE(notMatches("void i() noexcept(false);", functionDecl(hasDynamicExceptionSpec()))); EXPECT_TRUE(notMatches("void g() noexcept;", functionProtoType(hasDynamicExceptionSpec()))); EXPECT_TRUE(notMatches("void h() noexcept(true);", functionProtoType(hasDynamicExceptionSpec()))); EXPECT_TRUE(notMatches("void i() noexcept(false);", functionProtoType(hasDynamicExceptionSpec()))); } TEST_P(ASTMatchersTest, HasObjectExpression_DoesNotMatchMember) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches( "class X {}; struct Z { X m; }; void f(Z z) { z.m; }", memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); } TEST_P(ASTMatchersTest, HasObjectExpression_MatchesBaseOfVariable) { EXPECT_TRUE(matches( "struct X { int m; }; void f(struct X x) { x.m; }", memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(matches("struct X { int m; }; void f(struct X* x) { x->m; }", memberExpr(hasObjectExpression( hasType(pointsTo(recordDecl(hasName("X")))))))); } TEST_P(ASTMatchersTest, HasObjectExpression_MatchesBaseOfVariable_CXX) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template struct X { void f() { T t; t.m; } };", cxxDependentScopeMemberExpr(hasObjectExpression( declRefExpr(to(namedDecl(hasName("t")))))))); EXPECT_TRUE( matches("template struct X { void f() { T t; t->m; } };", cxxDependentScopeMemberExpr(hasObjectExpression( declRefExpr(to(namedDecl(hasName("t")))))))); } TEST_P(ASTMatchersTest, HasObjectExpression_MatchesBaseOfMemberFunc) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( "struct X { void f(); }; void g(X x) { x.f(); }", memberExpr(hasObjectExpression(hasType(recordDecl(hasName("X"))))))); } TEST_P(ASTMatchersTest, HasObjectExpression_MatchesBaseOfMemberFunc_Template) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("struct X { template void f(); };" "template void g(X x) { x.f(); }", unresolvedMemberExpr(hasObjectExpression( hasType(recordDecl(hasName("X"))))))); EXPECT_TRUE(matches("template void f(T t) { t.g(); }", cxxDependentScopeMemberExpr(hasObjectExpression( declRefExpr(to(namedDecl(hasName("t")))))))); } TEST_P(ASTMatchersTest, HasObjectExpression_ImplicitlyFormedMemberExpression) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class X {}; struct S { X m; void f() { this->m; } };", memberExpr(hasObjectExpression( hasType(pointsTo(recordDecl(hasName("S")))))))); EXPECT_TRUE(matches("class X {}; struct S { X m; void f() { m; } };", memberExpr(hasObjectExpression( hasType(pointsTo(recordDecl(hasName("S")))))))); } TEST_P(ASTMatchersTest, FieldDecl_DoesNotMatchNonFieldMembers) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class X { void m(); };", fieldDecl(hasName("m")))); EXPECT_TRUE(notMatches("class X { class m {}; };", fieldDecl(hasName("m")))); EXPECT_TRUE(notMatches("class X { enum { m }; };", fieldDecl(hasName("m")))); EXPECT_TRUE(notMatches("class X { enum m {}; };", fieldDecl(hasName("m")))); } TEST_P(ASTMatchersTest, FieldDecl_MatchesField) { EXPECT_TRUE(matches("struct X { int m; };", fieldDecl(hasName("m")))); } TEST_P(ASTMatchersTest, IsVolatileQualified) { EXPECT_TRUE( matches("volatile int i = 42;", varDecl(hasType(isVolatileQualified())))); EXPECT_TRUE( notMatches("volatile int *i;", varDecl(hasType(isVolatileQualified())))); EXPECT_TRUE(matches("typedef volatile int v_int; v_int i = 42;", varDecl(hasType(isVolatileQualified())))); } TEST_P(ASTMatchersTest, IsConstQualified_MatchesConstInt) { EXPECT_TRUE( matches("const int i = 42;", varDecl(hasType(isConstQualified())))); } TEST_P(ASTMatchersTest, IsConstQualified_MatchesConstPointer) { EXPECT_TRUE(matches("int i = 42; int* const p = &i;", varDecl(hasType(isConstQualified())))); } TEST_P(ASTMatchersTest, IsConstQualified_MatchesThroughTypedef) { EXPECT_TRUE(matches("typedef const int const_int; const_int i = 42;", varDecl(hasType(isConstQualified())))); EXPECT_TRUE(matches("typedef int* int_ptr; const int_ptr p = ((int*)0);", varDecl(hasType(isConstQualified())))); } TEST_P(ASTMatchersTest, IsConstQualified_DoesNotMatchInappropriately) { EXPECT_TRUE(notMatches("typedef int nonconst_int; nonconst_int i = 42;", varDecl(hasType(isConstQualified())))); EXPECT_TRUE( notMatches("int const* p;", varDecl(hasType(isConstQualified())))); } TEST_P(ASTMatchersTest, DeclCountIs_DeclCountIsCorrect) { EXPECT_TRUE(matches("void f() {int i,j;}", declStmt(declCountIs(2)))); EXPECT_TRUE( notMatches("void f() {int i,j; int k;}", declStmt(declCountIs(3)))); EXPECT_TRUE( notMatches("void f() {int i,j, k, l;}", declStmt(declCountIs(3)))); } TEST_P(ASTMatchersTest, EachOf_TriggersForEachMatch) { EXPECT_TRUE(matchAndVerifyResultTrue( "class A { int a; int b; };", recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), has(fieldDecl(hasName("b")).bind("v")))), std::make_unique>("v", 2))); } TEST_P(ASTMatchersTest, EachOf_BehavesLikeAnyOfUnlessBothMatch) { EXPECT_TRUE(matchAndVerifyResultTrue( "struct A { int a; int c; };", recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), has(fieldDecl(hasName("b")).bind("v")))), std::make_unique>("v", 1))); EXPECT_TRUE(matchAndVerifyResultTrue( "struct A { int c; int b; };", recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), has(fieldDecl(hasName("b")).bind("v")))), std::make_unique>("v", 1))); EXPECT_TRUE( notMatches("struct A { int c; int d; };", recordDecl(eachOf(has(fieldDecl(hasName("a")).bind("v")), has(fieldDecl(hasName("b")).bind("v")))))); } TEST_P(ASTMatchersTest, Optionally_SubmatchersDoNotMatch) { EXPECT_TRUE(matchAndVerifyResultFalse( "class A { int a; int b; };", recordDecl(optionally(has(fieldDecl(hasName("c")).bind("c")))), std::make_unique>("c"))); } // Regression test. TEST_P(ASTMatchersTest, Optionally_SubmatchersDoNotMatchButPreserveBindings) { StringRef Code = "class A { int a; int b; };"; auto Matcher = recordDecl(decl().bind("decl"), optionally(has(fieldDecl(hasName("c")).bind("v")))); // "decl" is still bound. EXPECT_TRUE(matchAndVerifyResultTrue( Code, Matcher, std::make_unique>("decl"))); // "v" is not bound, but the match still suceeded. EXPECT_TRUE(matchAndVerifyResultFalse( Code, Matcher, std::make_unique>("v"))); } TEST_P(ASTMatchersTest, Optionally_SubmatchersMatch) { EXPECT_TRUE(matchAndVerifyResultTrue( "class A { int a; int c; };", recordDecl(optionally(has(fieldDecl(hasName("a")).bind("v")))), std::make_unique>("v"))); } TEST_P(ASTMatchersTest, IsTemplateInstantiation_MatchesImplicitClassTemplateInstantiation) { if (!GetParam().isCXX()) { return; } // Make sure that we can both match the class by name (::X) and by the type // the template was instantiated with (via a field). EXPECT_TRUE( matches("template class X {}; class A {}; X x;", cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); EXPECT_TRUE(matches( "template class X { T t; }; class A {}; X x;", cxxRecordDecl( isTemplateInstantiation(), hasDescendant(fieldDecl(hasType(recordDecl(hasName("A")))))))); } TEST_P(ASTMatchersTest, IsTemplateInstantiation_MatchesImplicitFunctionTemplateInstantiation) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( "template void f(T t) {} class A {}; void g() { f(A()); }", functionDecl(hasParameter(0, hasType(recordDecl(hasName("A")))), isTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsTemplateInstantiation_MatchesExplicitClassTemplateInstantiation) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("template class X { T t; }; class A {};" "template class X;", cxxRecordDecl(isTemplateInstantiation(), hasDescendant(fieldDecl( hasType(recordDecl(hasName("A")))))))); // Make sure that we match the instantiation instead of the template // definition by checking whether the member function is present. EXPECT_TRUE( matches("template class X { void f() { T t; } };" "extern template class X;", cxxRecordDecl(isTemplateInstantiation(), unless(hasDescendant(varDecl(hasName("t"))))))); } TEST_P( ASTMatchersTest, IsTemplateInstantiation_MatchesInstantiationOfPartiallySpecializedClassTemplate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("template class X {};" "template class X {}; class A {}; X x;", cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); } TEST_P( ASTMatchersTest, IsTemplateInstantiation_MatchesInstantiationOfClassTemplateNestedInNonTemplate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("class A {};" "class X {" " template class Y { U u; };" " Y y;" "};", cxxRecordDecl(hasName("::X::Y"), isTemplateInstantiation()))); } TEST_P( ASTMatchersTest, IsTemplateInstantiation_DoesNotMatchInstantiationsInsideOfInstantiation) { if (!GetParam().isCXX()) { return; } // FIXME: Figure out whether this makes sense. It doesn't affect the // normal use case as long as the uppermost instantiation always is marked // as template instantiation, but it might be confusing as a predicate. EXPECT_TRUE(matches( "class A {};" "template class X {" " template class Y { U u; };" " Y y;" "}; X x;", cxxRecordDecl(hasName("::X::Y"), unless(isTemplateInstantiation())))); } TEST_P( ASTMatchersTest, IsTemplateInstantiation_DoesNotMatchExplicitClassTemplateSpecialization) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( notMatches("template class X {}; class A {};" "template <> class X {}; X x;", cxxRecordDecl(hasName("::X"), isTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsTemplateInstantiation_DoesNotMatchNonTemplate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class A {}; class Y { A a; };", cxxRecordDecl(isTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsInstantiated_MatchesInstantiation) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("template class A { T i; }; class Y { A a; };", cxxRecordDecl(isInstantiated()))); } TEST_P(ASTMatchersTest, IsInstantiated_NotMatchesDefinition) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template class A { T i; };", cxxRecordDecl(isInstantiated()))); } TEST_P(ASTMatchersTest, IsInTemplateInstantiation_MatchesInstantiationStmt) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("template struct A { A() { T i; } };" "class Y { A a; }; Y y;", declStmt(isInTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsInTemplateInstantiation_NotMatchesDefinitionStmt) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template struct A { void x() { T i; } };", declStmt(isInTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsInstantiated_MatchesFunctionInstantiation) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("template void A(T t) { T i; } void x() { A(0); }", functionDecl(isInstantiated()))); } TEST_P(ASTMatchersTest, IsInstantiated_NotMatchesFunctionDefinition) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template void A(T t) { T i; }", varDecl(isInstantiated()))); } TEST_P(ASTMatchersTest, IsInTemplateInstantiation_MatchesFunctionInstantiationStmt) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("template void A(T t) { T i; } void x() { A(0); }", declStmt(isInTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsInTemplateInstantiation_NotMatchesFunctionDefinitionStmt) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template void A(T t) { T i; }", declStmt(isInTemplateInstantiation()))); } TEST_P(ASTMatchersTest, IsInTemplateInstantiation_Sharing) { if (!GetParam().isCXX()) { return; } auto Matcher = binaryOperator(unless(isInTemplateInstantiation())); // FIXME: Node sharing is an implementation detail, exposing it is ugly // and makes the matcher behave in non-obvious ways. EXPECT_TRUE(notMatches( "int j; template void A(T t) { j += 42; } void x() { A(0); }", Matcher)); EXPECT_TRUE(matches( "int j; template void A(T t) { j += t; } void x() { A(0); }", Matcher)); } TEST_P(ASTMatchersTest, IsInstantiationDependent_MatchesNonValueTypeDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches( "template void f() { (void) sizeof(sizeof(T() + T())); }", expr(isInstantiationDependent()))); } TEST_P(ASTMatchersTest, IsInstantiationDependent_MatchesValueDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template int f() { return T; }", expr(isInstantiationDependent()))); } TEST_P(ASTMatchersTest, IsInstantiationDependent_MatchesTypeDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template T f() { return T(); }", expr(isInstantiationDependent()))); } TEST_P(ASTMatchersTest, IsTypeDependent_MatchesTypeDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template T f() { return T(); }", expr(isTypeDependent()))); } TEST_P(ASTMatchersTest, IsTypeDependent_NotMatchesValueDependent) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template int f() { return T; }", expr(isTypeDependent()))); } TEST_P(ASTMatchersTest, IsValueDependent_MatchesValueDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template int f() { return T; }", expr(isValueDependent()))); } TEST_P(ASTMatchersTest, IsValueDependent_MatchesTypeDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches("template T f() { return T(); }", expr(isValueDependent()))); } TEST_P(ASTMatchersTest, IsValueDependent_MatchesInstantiationDependent) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } EXPECT_TRUE(matches( "template void f() { (void) sizeof(sizeof(T() + T())); }", expr(isValueDependent()))); } TEST_P(ASTMatchersTest, IsExplicitTemplateSpecialization_DoesNotMatchPrimaryTemplate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template class X {};", cxxRecordDecl(isExplicitTemplateSpecialization()))); EXPECT_TRUE(notMatches("template void f(T t);", functionDecl(isExplicitTemplateSpecialization()))); } TEST_P( ASTMatchersTest, IsExplicitTemplateSpecialization_DoesNotMatchExplicitTemplateInstantiations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( notMatches("template class X {};" "template class X; extern template class X;", cxxRecordDecl(isExplicitTemplateSpecialization()))); EXPECT_TRUE( notMatches("template void f(T t) {}" "template void f(int t); extern template void f(long t);", functionDecl(isExplicitTemplateSpecialization()))); } TEST_P( ASTMatchersTest, IsExplicitTemplateSpecialization_DoesNotMatchImplicitTemplateInstantiations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("template class X {}; X x;", cxxRecordDecl(isExplicitTemplateSpecialization()))); EXPECT_TRUE( notMatches("template void f(T t); void g() { f(10); }", functionDecl(isExplicitTemplateSpecialization()))); } TEST_P( ASTMatchersTest, IsExplicitTemplateSpecialization_MatchesExplicitTemplateSpecializations) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("template class X {};" "template<> class X {};", cxxRecordDecl(isExplicitTemplateSpecialization()))); EXPECT_TRUE(matches("template void f(T t) {}" "template<> void f(int t) {}", functionDecl(isExplicitTemplateSpecialization()))); } TEST_P(ASTMatchersTest, IsNoReturn) { EXPECT_TRUE(notMatches("void func();", functionDecl(isNoReturn()))); EXPECT_TRUE(notMatches("void func() {}", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("__attribute__((noreturn)) void func();", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("__attribute__((noreturn)) void func() {}", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("_Noreturn void func();", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("_Noreturn void func() {}", functionDecl(isNoReturn()))); } TEST_P(ASTMatchersTest, IsNoReturn_CXX) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( notMatches("struct S { void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE( notMatches("struct S { void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE(notMatches("struct S { static void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(notMatches("struct S { static void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE(notMatches("struct S { S(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(notMatches("struct S { S() {} };", functionDecl(isNoReturn()))); // --- EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE( matches("struct S { __attribute__((noreturn)) static void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE( matches("struct S { __attribute__((noreturn)) static void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) S(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { __attribute__((noreturn)) S() {} };", functionDecl(isNoReturn()))); } TEST_P(ASTMatchersTest, IsNoReturn_CXX11Attribute) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("[[noreturn]] void func();", functionDecl(isNoReturn()))); EXPECT_TRUE( matches("[[noreturn]] void func() {}", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { [[noreturn]] void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { [[noreturn]] void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { [[noreturn]] static void func(); };", functionDecl(isNoReturn()))); EXPECT_TRUE(matches("struct S { [[noreturn]] static void func() {} };", functionDecl(isNoReturn()))); EXPECT_TRUE( matches("struct S { [[noreturn]] S(); };", functionDecl(isNoReturn()))); EXPECT_TRUE( matches("struct S { [[noreturn]] S() {} };", functionDecl(isNoReturn()))); } TEST_P(ASTMatchersTest, BooleanType) { if (!GetParam().isCXX()) { // FIXME: Add a test for `booleanType()` that does not depend on C++. return; } EXPECT_TRUE(matches("struct S { bool func(); };", cxxMethodDecl(returns(booleanType())))); EXPECT_TRUE(notMatches("struct S { void func(); };", cxxMethodDecl(returns(booleanType())))); } TEST_P(ASTMatchersTest, VoidType) { if (!GetParam().isCXX()) { // FIXME: Add a test for `voidType()` that does not depend on C++. return; } EXPECT_TRUE(matches("struct S { void func(); };", cxxMethodDecl(returns(voidType())))); } TEST_P(ASTMatchersTest, RealFloatingPointType) { if (!GetParam().isCXX()) { // FIXME: Add a test for `realFloatingPointType()` that does not depend on // C++. return; } EXPECT_TRUE(matches("struct S { float func(); };", cxxMethodDecl(returns(realFloatingPointType())))); EXPECT_TRUE(notMatches("struct S { int func(); };", cxxMethodDecl(returns(realFloatingPointType())))); EXPECT_TRUE(matches("struct S { long double func(); };", cxxMethodDecl(returns(realFloatingPointType())))); } TEST_P(ASTMatchersTest, ArrayType) { EXPECT_TRUE(matches("int a[] = {2,3};", arrayType())); EXPECT_TRUE(matches("int a[42];", arrayType())); EXPECT_TRUE(matches("void f(int b) { int a[b]; }", arrayType())); EXPECT_TRUE(notMatches("struct A {}; struct A a[7];", arrayType(hasElementType(builtinType())))); EXPECT_TRUE(matches("int const a[] = { 2, 3 };", qualType(arrayType(hasElementType(builtinType()))))); EXPECT_TRUE(matches( "int const a[] = { 2, 3 };", qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); EXPECT_TRUE(matches("typedef const int T; T x[] = { 1, 2 };", qualType(isConstQualified(), arrayType()))); EXPECT_TRUE(notMatches( "int a[] = { 2, 3 };", qualType(isConstQualified(), arrayType(hasElementType(builtinType()))))); EXPECT_TRUE(notMatches( "int a[] = { 2, 3 };", qualType(arrayType(hasElementType(isConstQualified(), builtinType()))))); EXPECT_TRUE(notMatches("int const a[] = { 2, 3 };", qualType(arrayType(hasElementType(builtinType())), unless(isConstQualified())))); EXPECT_TRUE( matches("int a[2];", constantArrayType(hasElementType(builtinType())))); EXPECT_TRUE(matches("const int a = 0;", qualType(isInteger()))); } TEST_P(ASTMatchersTest, DecayedType) { EXPECT_TRUE( matches("void f(int i[]);", valueDecl(hasType(decayedType(hasDecayedType(pointerType())))))); EXPECT_TRUE(notMatches("int i[7];", decayedType())); } TEST_P(ASTMatchersTest, ComplexType) { EXPECT_TRUE(matches("_Complex float f;", complexType())); EXPECT_TRUE( matches("_Complex float f;", complexType(hasElementType(builtinType())))); EXPECT_TRUE(notMatches("_Complex float f;", complexType(hasElementType(isInteger())))); } TEST_P(ASTMatchersTest, IsAnonymous) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("namespace N {}", namespaceDecl(isAnonymous()))); EXPECT_TRUE(matches("namespace {}", namespaceDecl(isAnonymous()))); } TEST_P(ASTMatchersTest, InStdNamespace) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class vector {};" "namespace foo {" " class vector {};" "}" "namespace foo {" " namespace std {" " class vector {};" " }" "}", cxxRecordDecl(hasName("vector"), isInStdNamespace()))); EXPECT_TRUE(matches("namespace std {" " class vector {};" "}", cxxRecordDecl(hasName("vector"), isInStdNamespace()))); } TEST_P(ASTMatchersTest, InStdNamespace_CXX11) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("namespace std {" " inline namespace __1 {" " class vector {};" " }" "}", cxxRecordDecl(hasName("vector"), isInStdNamespace()))); EXPECT_TRUE(notMatches("namespace std {" " inline namespace __1 {" " inline namespace __fs {" " namespace filesystem {" " inline namespace v1 {" " class path {};" " }" " }" " }" " }" "}", cxxRecordDecl(hasName("path"), isInStdNamespace()))); EXPECT_TRUE( matches("namespace std {" " inline namespace __1 {" " inline namespace __fs {" " namespace filesystem {" " inline namespace v1 {" " class path {};" " }" " }" " }" " }" "}", cxxRecordDecl(hasName("path"), hasAncestor(namespaceDecl(hasName("filesystem"), isInStdNamespace()))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_QualType) { EXPECT_TRUE(matches( "int i = 1;", varDecl(hasType(qualType().bind("type")), hasInitializer(ignoringParenImpCasts( hasType(qualType(equalsBoundNode("type")))))))); EXPECT_TRUE(notMatches("int i = 1.f;", varDecl(hasType(qualType().bind("type")), hasInitializer(ignoringParenImpCasts(hasType( qualType(equalsBoundNode("type")))))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_NonMatchingTypes) { EXPECT_TRUE(notMatches( "int i = 1;", varDecl(namedDecl(hasName("i")).bind("name"), hasInitializer(ignoringParenImpCasts( hasType(qualType(equalsBoundNode("type")))))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_Stmt) { EXPECT_TRUE( matches("void f() { if(1) {} }", stmt(allOf(ifStmt().bind("if"), hasParent(stmt(has(stmt(equalsBoundNode("if"))))))))); EXPECT_TRUE(notMatches( "void f() { if(1) { if (1) {} } }", stmt(allOf(ifStmt().bind("if"), has(stmt(equalsBoundNode("if"))))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_Decl) { if (!GetParam().isCXX()) { // FIXME: Add a test for `equalsBoundNode()` for declarations that does not // depend on C++. return; } EXPECT_TRUE(matches( "class X { class Y {}; };", decl(allOf(recordDecl(hasName("::X::Y")).bind("record"), hasParent(decl(has(decl(equalsBoundNode("record"))))))))); EXPECT_TRUE(notMatches("class X { class Y {}; };", decl(allOf(recordDecl(hasName("::X")).bind("record"), has(decl(equalsBoundNode("record"))))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_Type) { if (!GetParam().isCXX()) { // FIXME: Add a test for `equalsBoundNode()` for types that does not depend // on C++. return; } EXPECT_TRUE(matches( "class X { int a; int b; };", recordDecl( has(fieldDecl(hasName("a"), hasType(type().bind("t")))), has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t")))))))); EXPECT_TRUE(notMatches( "class X { int a; double b; };", recordDecl( has(fieldDecl(hasName("a"), hasType(type().bind("t")))), has(fieldDecl(hasName("b"), hasType(type(equalsBoundNode("t")))))))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_UsingForEachDescendant) { EXPECT_TRUE(matchAndVerifyResultTrue( "int f() {" " if (1) {" " int i = 9;" " }" " int j = 10;" " {" " float k = 9.0;" " }" " return 0;" "}", // Look for variable declarations within functions whose type is the same // as the function return type. functionDecl( returns(qualType().bind("type")), forEachDescendant(varDecl(hasType(qualType(equalsBoundNode("type")))) .bind("decl"))), // Only i and j should match, not k. std::make_unique>("decl", 2))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_FiltersMatchedCombinations) { EXPECT_TRUE(matchAndVerifyResultTrue( "void f() {" " int x;" " double d;" " x = d + x - d + x;" "}", functionDecl( hasName("f"), forEachDescendant(varDecl().bind("d")), forEachDescendant(declRefExpr(to(decl(equalsBoundNode("d")))))), std::make_unique>("d", 5))); } TEST_P(ASTMatchersTest, EqualsBoundNodeMatcher_UnlessDescendantsOfAncestorsMatch) { EXPECT_TRUE(matchAndVerifyResultTrue( "struct StringRef { int size() const; const char* data() const; };" "void f(StringRef v) {" " v.data();" "}", cxxMemberCallExpr( callee(cxxMethodDecl(hasName("data"))), on(declRefExpr(to( varDecl(hasType(recordDecl(hasName("StringRef")))).bind("var")))), unless(hasAncestor(stmt(hasDescendant(cxxMemberCallExpr( callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))), on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) .bind("data"), std::make_unique>("data", 1))); EXPECT_FALSE(matches( "struct StringRef { int size() const; const char* data() const; };" "void f(StringRef v) {" " v.data();" " v.size();" "}", cxxMemberCallExpr( callee(cxxMethodDecl(hasName("data"))), on(declRefExpr(to( varDecl(hasType(recordDecl(hasName("StringRef")))).bind("var")))), unless(hasAncestor(stmt(hasDescendant(cxxMemberCallExpr( callee(cxxMethodDecl(anyOf(hasName("size"), hasName("length")))), on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) .bind("data"))); } TEST_P(ASTMatchersTest, NullPointerConstant) { EXPECT_TRUE(matches("#define NULL ((void *)0)\n" "void *v1 = NULL;", expr(nullPointerConstant()))); EXPECT_TRUE(matches("char *cp = (char *)0;", expr(nullPointerConstant()))); EXPECT_TRUE(matches("int *ip = 0;", expr(nullPointerConstant()))); EXPECT_TRUE(matches("int i = 0;", expr(nullPointerConstant()))); } TEST_P(ASTMatchersTest, NullPointerConstant_GNUNull) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("void *p = __null;", expr(nullPointerConstant()))); } TEST_P(ASTMatchersTest, NullPointerConstant_GNUNullInTemplate) { if (!GetParam().isCXX() || GetParam().hasDelayedTemplateParsing()) { // FIXME: Fix this test to work with delayed template parsing. return; } const char kTest[] = R"( template struct MyTemplate { MyTemplate() : field_(__null) {} T* field_; }; )"; EXPECT_TRUE(matches(kTest, expr(nullPointerConstant()))); } TEST_P(ASTMatchersTest, NullPointerConstant_CXX11Nullptr) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("void *p = nullptr;", expr(nullPointerConstant()))); } TEST_P(ASTMatchersTest, HasExternalFormalLinkage) { EXPECT_TRUE(matches("int a = 0;", namedDecl(hasName("a"), hasExternalFormalLinkage()))); EXPECT_TRUE(notMatches("static int a = 0;", namedDecl(hasName("a"), hasExternalFormalLinkage()))); EXPECT_TRUE(notMatches("static void f(void) { int a = 0; }", namedDecl(hasName("a"), hasExternalFormalLinkage()))); EXPECT_TRUE(notMatches("void f(void) { int a = 0; }", namedDecl(hasName("a"), hasExternalFormalLinkage()))); } TEST_P(ASTMatchersTest, HasExternalFormalLinkage_CXX) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("namespace { int a = 0; }", namedDecl(hasName("a"), hasExternalFormalLinkage()))); } TEST_P(ASTMatchersTest, HasDefaultArgument) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("void x(int val = 0) {}", parmVarDecl(hasDefaultArgument()))); EXPECT_TRUE( notMatches("void x(int val) {}", parmVarDecl(hasDefaultArgument()))); } TEST_P(ASTMatchersTest, IsAtPosition) { EXPECT_TRUE(matches("void x(int a, int b) {}", parmVarDecl(isAtPosition(1)))); EXPECT_TRUE(matches("void x(int a, int b) {}", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matches("void x(int a, int b) {}", parmVarDecl(isAtPosition(1)))); EXPECT_TRUE(notMatches("void x(int val) {}", parmVarDecl(isAtPosition(1)))); } TEST_P(ASTMatchersTest, IsAtPosition_FunctionDecl) { EXPECT_TRUE(matches("void x(int a);", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matches("void x(int a, int b);", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matches("void x(int a, int b);", parmVarDecl(isAtPosition(1)))); EXPECT_TRUE(notMatches("void x(int val);", parmVarDecl(isAtPosition(1)))); } TEST_P(ASTMatchersTest, IsAtPosition_Lambda) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE( matches("void x() { [](int a) {}; }", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matches("void x() { [](int a, int b) {}; }", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matches("void x() { [](int a, int b) {}; }", parmVarDecl(isAtPosition(1)))); EXPECT_TRUE( notMatches("void x() { [](int val) {}; }", parmVarDecl(isAtPosition(1)))); } TEST_P(ASTMatchersTest, IsAtPosition_BlockDecl) { EXPECT_TRUE(matchesObjC( "void func() { void (^my_block)(int arg) = ^void(int arg) {}; } ", parmVarDecl(isAtPosition(0)))); EXPECT_TRUE(matchesObjC("void func() { void (^my_block)(int x, int y) = " "^void(int x, int y) {}; } ", parmVarDecl(isAtPosition(1)))); EXPECT_TRUE(notMatchesObjC( "void func() { void (^my_block)(int arg) = ^void(int arg) {}; } ", parmVarDecl(isAtPosition(1)))); } TEST_P(ASTMatchersTest, IsArray) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("struct MyClass {}; MyClass *p1 = new MyClass[10];", cxxNewExpr(isArray()))); } TEST_P(ASTMatchersTest, HasArraySize) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("struct MyClass {}; MyClass *p1 = new MyClass[10];", cxxNewExpr(hasArraySize( ignoringParenImpCasts(integerLiteral(equals(10))))))); } TEST_P(ASTMatchersTest, HasDefinition_MatchesStructDefinition) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("struct x {};", cxxRecordDecl(hasDefinition()))); EXPECT_TRUE(notMatches("struct x;", cxxRecordDecl(hasDefinition()))); } TEST_P(ASTMatchersTest, HasDefinition_MatchesClassDefinition) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class x {};", cxxRecordDecl(hasDefinition()))); EXPECT_TRUE(notMatches("class x;", cxxRecordDecl(hasDefinition()))); } TEST_P(ASTMatchersTest, HasDefinition_MatchesUnionDefinition) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("union x {};", cxxRecordDecl(hasDefinition()))); EXPECT_TRUE(notMatches("union x;", cxxRecordDecl(hasDefinition()))); } TEST_P(ASTMatchersTest, IsScoped_MatchesScopedEnum) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("enum class X {};", enumDecl(isScoped()))); } TEST_P(ASTMatchersTest, IsScoped_NotMatchesRegularEnum) { EXPECT_TRUE(notMatches("enum E { E1 };", enumDecl(isScoped()))); } TEST_P(ASTMatchersTest, IsStruct) { EXPECT_TRUE(matches("struct S {};", tagDecl(isStruct()))); } TEST_P(ASTMatchersTest, IsUnion) { EXPECT_TRUE(matches("union U {};", tagDecl(isUnion()))); } TEST_P(ASTMatchersTest, IsEnum) { EXPECT_TRUE(matches("enum E { E1 };", tagDecl(isEnum()))); } TEST_P(ASTMatchersTest, IsClass) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class C {};", tagDecl(isClass()))); } TEST_P(ASTMatchersTest, HasTrailingReturn_MatchesTrailingReturn) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches("auto Y() -> int { return 0; }", functionDecl(hasTrailingReturn()))); EXPECT_TRUE(matches("auto X() -> int;", functionDecl(hasTrailingReturn()))); EXPECT_TRUE( notMatches("int X() { return 0; }", functionDecl(hasTrailingReturn()))); EXPECT_TRUE(notMatches("int X();", functionDecl(hasTrailingReturn()))); EXPECT_TRUE(notMatches("void X();", functionDecl(hasTrailingReturn()))); } TEST_P(ASTMatchersTest, HasTrailingReturn_MatchesLambdaTrailingReturn) { if (!GetParam().isCXX11OrLater()) { return; } EXPECT_TRUE(matches( "auto lambda2 = [](double x, double y) -> double {return x + y;};", functionDecl(hasTrailingReturn()))); EXPECT_TRUE( notMatches("auto lambda2 = [](double x, double y) {return x + y;};", functionDecl(hasTrailingReturn()))); } TEST_P(ASTMatchersTest, IsAssignmentOperator) { if (!GetParam().isCXX()) { return; } StatementMatcher BinAsgmtOperator = binaryOperator(isAssignmentOperator()); StatementMatcher CXXAsgmtOperator = cxxOperatorCallExpr(isAssignmentOperator()); EXPECT_TRUE(matches("void x() { int a; a += 1; }", BinAsgmtOperator)); EXPECT_TRUE(matches("void x() { int a; a = 2; }", BinAsgmtOperator)); EXPECT_TRUE(matches("void x() { int a; a &= 3; }", BinAsgmtOperator)); EXPECT_TRUE(matches("struct S { S& operator=(const S&); };" "void x() { S s1, s2; s1 = s2; }", CXXAsgmtOperator)); EXPECT_TRUE( notMatches("void x() { int a; if(a == 0) return; }", BinAsgmtOperator)); } TEST_P(ASTMatchersTest, IsComparisonOperator) { if (!GetParam().isCXX()) { return; } StatementMatcher BinCompOperator = binaryOperator(isComparisonOperator()); StatementMatcher CXXCompOperator = cxxOperatorCallExpr(isComparisonOperator()); EXPECT_TRUE(matches("void x() { int a; a == 1; }", BinCompOperator)); EXPECT_TRUE(matches("void x() { int a; a > 2; }", BinCompOperator)); EXPECT_TRUE(matches("struct S { bool operator==(const S&); };" "void x() { S s1, s2; bool b1 = s1 == s2; }", CXXCompOperator)); EXPECT_TRUE( notMatches("void x() { int a; if(a = 0) return; }", BinCompOperator)); } TEST_P(ASTMatchersTest, HasInit) { if (!GetParam().isCXX11OrLater()) { // FIXME: Add a test for `hasInit()` that does not depend on C++. return; } EXPECT_TRUE(matches("int x{0};", initListExpr(hasInit(0, expr())))); EXPECT_FALSE(matches("int x{0};", initListExpr(hasInit(1, expr())))); EXPECT_FALSE(matches("int x;", initListExpr(hasInit(0, expr())))); } TEST_P(ASTMatchersTest, IsMain) { EXPECT_TRUE(matches("int main() {}", functionDecl(isMain()))); EXPECT_TRUE(notMatches("int main2() {}", functionDecl(isMain()))); } TEST_P(ASTMatchersTest, OMPExecutableDirective_IsStandaloneDirective) { auto Matcher = ompExecutableDirective(isStandaloneDirective()); StringRef Source0 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); StringRef Source1 = R"( void x() { #pragma omp taskyield })"; EXPECT_TRUE(matchesWithOpenMP(Source1, Matcher)); } TEST_P(ASTMatchersTest, OMPExecutableDirective_HasStructuredBlock) { StringRef Source0 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(matchesWithOpenMP( Source0, ompExecutableDirective(hasStructuredBlock(nullStmt())))); StringRef Source1 = R"( void x() { #pragma omp parallel {;} })"; EXPECT_TRUE(notMatchesWithOpenMP( Source1, ompExecutableDirective(hasStructuredBlock(nullStmt())))); EXPECT_TRUE(matchesWithOpenMP( Source1, ompExecutableDirective(hasStructuredBlock(compoundStmt())))); StringRef Source2 = R"( void x() { #pragma omp taskyield {;} })"; EXPECT_TRUE(notMatchesWithOpenMP( Source2, ompExecutableDirective(hasStructuredBlock(anything())))); } TEST_P(ASTMatchersTest, OMPExecutableDirective_HasClause) { auto Matcher = ompExecutableDirective(hasAnyClause(anything())); StringRef Source0 = R"( void x() { ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); StringRef Source1 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); StringRef Source2 = R"( void x() { #pragma omp parallel default(none) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); StringRef Source3 = R"( void x() { #pragma omp parallel default(shared) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source3, Matcher)); StringRef Source4 = R"( void x() { #pragma omp parallel default(firstprivate) ; })"; EXPECT_TRUE(matchesWithOpenMP51(Source4, Matcher)); StringRef Source5 = R"( void x(int x) { #pragma omp parallel num_threads(x) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source5, Matcher)); } TEST_P(ASTMatchersTest, OMPDefaultClause_IsNoneKind) { auto Matcher = ompExecutableDirective(hasAnyClause(ompDefaultClause(isNoneKind()))); StringRef Source0 = R"( void x() { ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); StringRef Source1 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); StringRef Source2 = R"( void x() { #pragma omp parallel default(none) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); StringRef Source3 = R"( void x() { #pragma omp parallel default(shared) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source3, Matcher)); StringRef Source4 = R"( void x(int x) { #pragma omp parallel default(firstprivate) ; })"; EXPECT_TRUE(notMatchesWithOpenMP51(Source4, Matcher)); const std::string Source5 = R"( void x(int x) { #pragma omp parallel num_threads(x) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source5, Matcher)); } TEST_P(ASTMatchersTest, OMPDefaultClause_IsSharedKind) { auto Matcher = ompExecutableDirective(hasAnyClause(ompDefaultClause(isSharedKind()))); StringRef Source0 = R"( void x() { ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); StringRef Source1 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); StringRef Source2 = R"( void x() { #pragma omp parallel default(shared) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); StringRef Source3 = R"( void x() { #pragma omp parallel default(none) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source3, Matcher)); StringRef Source4 = R"( void x(int x) { #pragma omp parallel default(firstprivate) ; })"; EXPECT_TRUE(notMatchesWithOpenMP51(Source4, Matcher)); const std::string Source5 = R"( void x(int x) { #pragma omp parallel num_threads(x) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source5, Matcher)); } TEST(OMPDefaultClause, isFirstPrivateKind) { auto Matcher = ompExecutableDirective( hasAnyClause(ompDefaultClause(isFirstPrivateKind()))); const std::string Source0 = R"( void x() { ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); const std::string Source1 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source1, Matcher)); const std::string Source2 = R"( void x() { #pragma omp parallel default(shared) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source2, Matcher)); const std::string Source3 = R"( void x() { #pragma omp parallel default(none) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source3, Matcher)); const std::string Source4 = R"( void x(int x) { #pragma omp parallel default(firstprivate) ; })"; EXPECT_TRUE(matchesWithOpenMP51(Source4, Matcher)); const std::string Source5 = R"( void x(int x) { #pragma omp parallel num_threads(x) ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source5, Matcher)); } TEST_P(ASTMatchersTest, OMPExecutableDirective_IsAllowedToContainClauseKind) { auto Matcher = ompExecutableDirective( isAllowedToContainClauseKind(llvm::omp::OMPC_default)); StringRef Source0 = R"( void x() { ; })"; EXPECT_TRUE(notMatchesWithOpenMP(Source0, Matcher)); StringRef Source1 = R"( void x() { #pragma omp parallel ; })"; EXPECT_TRUE(matchesWithOpenMP(Source1, Matcher)); StringRef Source2 = R"( void x() { #pragma omp parallel default(none) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source2, Matcher)); StringRef Source3 = R"( void x() { #pragma omp parallel default(shared) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source3, Matcher)); StringRef Source4 = R"( void x() { #pragma omp parallel default(firstprivate) ; })"; EXPECT_TRUE(matchesWithOpenMP51(Source4, Matcher)); StringRef Source5 = R"( void x(int x) { #pragma omp parallel num_threads(x) ; })"; EXPECT_TRUE(matchesWithOpenMP(Source5, Matcher)); StringRef Source6 = R"( void x() { #pragma omp taskyield })"; EXPECT_TRUE(notMatchesWithOpenMP(Source6, Matcher)); StringRef Source7 = R"( void x() { #pragma omp task ; })"; EXPECT_TRUE(matchesWithOpenMP(Source7, Matcher)); } TEST_P(ASTMatchersTest, HasAnyBase_DirectBase) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( "struct Base {};" "struct ExpectedMatch : Base {};", cxxRecordDecl(hasName("ExpectedMatch"), hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))); } TEST_P(ASTMatchersTest, HasAnyBase_IndirectBase) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( "struct Base {};" "struct Intermediate : Base {};" "struct ExpectedMatch : Intermediate {};", cxxRecordDecl(hasName("ExpectedMatch"), hasAnyBase(hasType(cxxRecordDecl(hasName("Base"))))))); } TEST_P(ASTMatchersTest, HasAnyBase_NoBase) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("struct Foo {};" "struct Bar {};", cxxRecordDecl(hasAnyBase(hasType(cxxRecordDecl()))))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPublic_Public) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class Base {};" "class Derived : public Base {};", cxxRecordDecl(hasAnyBase(isPublic())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPublic_DefaultAccessSpecifierPublic) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class Base {};" "struct Derived : Base {};", cxxRecordDecl(hasAnyBase(isPublic())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPublic_Private) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : private Base {};", cxxRecordDecl(hasAnyBase(isPublic())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPublic_DefaultAccessSpecifierPrivate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : Base {};", cxxRecordDecl(hasAnyBase(isPublic())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPublic_Protected) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : protected Base {};", cxxRecordDecl(hasAnyBase(isPublic())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPrivate_Private) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class Base {};" "class Derived : private Base {};", cxxRecordDecl(hasAnyBase(isPrivate())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPrivate_DefaultAccessSpecifierPrivate) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("struct Base {};" "class Derived : Base {};", cxxRecordDecl(hasAnyBase(isPrivate())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPrivate_Public) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : public Base {};", cxxRecordDecl(hasAnyBase(isPrivate())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPrivate_DefaultAccessSpecifierPublic) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "struct Derived : Base {};", cxxRecordDecl(hasAnyBase(isPrivate())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsPrivate_Protected) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : protected Base {};", cxxRecordDecl(hasAnyBase(isPrivate())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsProtected_Protected) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class Base {};" "class Derived : protected Base {};", cxxRecordDecl(hasAnyBase(isProtected())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsProtected_Public) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : public Base {};", cxxRecordDecl(hasAnyBase(isProtected())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsProtected_Private) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : private Base {};", cxxRecordDecl(hasAnyBase(isProtected())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsVirtual_Directly) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches("class Base {};" "class Derived : virtual Base {};", cxxRecordDecl(hasAnyBase(isVirtual())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsVirtual_Indirectly) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE( matches("class Base {};" "class Intermediate : virtual Base {};" "class Derived : Intermediate {};", cxxRecordDecl(hasName("Derived"), hasAnyBase(isVirtual())))); } TEST_P(ASTMatchersTest, HasAnyBase_IsVirtual_NoVirtualBase) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(notMatches("class Base {};" "class Derived : Base {};", cxxRecordDecl(hasAnyBase(isVirtual())))); } TEST_P(ASTMatchersTest, HasDirectBase) { if (!GetParam().isCXX()) { return; } EXPECT_TRUE(matches( R"cc( class Base {}; class Derived : Base{}; )cc", cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl(hasName("Base"))))))); StringRef MultiDerived = R"cc( class Base {}; class Base2 {}; class Derived : Base, Base2{}; )cc"; EXPECT_TRUE(matches( MultiDerived, cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl(hasName("Base"))))))); EXPECT_TRUE(matches( MultiDerived, cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl(hasName("Base2"))))))); StringRef Indirect = R"cc( class Base {}; class Intermediate : Base {}; class Derived : Intermediate{}; )cc"; EXPECT_TRUE( matches(Indirect, cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl( hasName("Intermediate"))))))); EXPECT_TRUE(notMatches( Indirect, cxxRecordDecl(hasName("Derived"), hasDirectBase(hasType(cxxRecordDecl(hasName("Base"))))))); } } // namespace ast_matchers } // namespace clang