| // 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<VarDecl>(Name).dynCastTo<Decl>(); |
| auto RD = internal::Matcher<RecordDecl>(Name).dynCastTo<Decl>(); |
| // Matching VD first should not make a cache hit for RD. |
| EXPECT_TRUE(notMatches("void f() { int i; }", |
| decl(hasDescendant(VD), hasDescendant(RD)))); |
| EXPECT_TRUE(notMatches("void f() { int i; }", |
| decl(hasDescendant(RD), hasDescendant(VD)))); |
| // Not matching RD first should not make a cache hit for VD either. |
| EXPECT_TRUE(matches("void f() { int i; }", |
| decl(anyOf(hasDescendant(RD), hasDescendant(VD))))); |
| } |
| |
| TEST_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 <typename T> struct S;" |
| "template <> struct S<int>{ 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<typename T> |
| 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"))))))); |
| |
| Code = R"cpp( |
| struct ConstructorTakesInt |
| { |
| ConstructorTakesInt(int i) {} |
| }; |
| |
| void callTakesInt(int i) |
| { |
| |
| } |
| |
| void doCall() |
| { |
| callTakesInt(42); |
| } |
| |
| void doConstruct() |
| { |
| ConstructorTakesInt cti(42); |
| } |
| )cpp"; |
| |
| EXPECT_TRUE(matches( |
| Code, traverse(TK_IgnoreUnlessSpelledInSource, |
| invocation(forFunction(functionDecl(hasName("doCall"))), |
| hasArgument(0, integerLiteral(equals(42))), |
| hasAnyArgument(integerLiteral(equals(42))), |
| forEachArgumentWithParam( |
| integerLiteral(equals(42)), |
| parmVarDecl(hasName("i"))))))); |
| |
| EXPECT_TRUE(matches( |
| Code, |
| traverse( |
| TK_IgnoreUnlessSpelledInSource, |
| invocation(forFunction(functionDecl(hasName("doConstruct"))), |
| hasArgument(0, integerLiteral(equals(42))), |
| hasAnyArgument(integerLiteral(equals(42))), |
| forEachArgumentWithParam(integerLiteral(equals(42)), |
| parmVarDecl(hasName("i"))))))); |
| } |
| |
| 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<int N> class Y : Y<N-1>, X {};", |
| IsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; template<int N> class Y : X, Y<N-1> {};", |
| IsDerivedFromX)); |
| |
| DeclarationMatcher IsZDerivedFromX = |
| cxxRecordDecl(hasName("Z"), isDerivedFrom("X")); |
| EXPECT_TRUE(matches("class X {};" |
| "template<int N> class Y : Y<N-1> {};" |
| "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 T> class Y : public X {};" |
| "class Z : public Y<int> {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class X {};" |
| "template<class T> class Y : public X {};" |
| "class Z : public Y<int> {};", |
| ZIsDirectlyDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; template<class T> class Z : public X {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("template<class T> class X {}; " |
| "template<class T> class Z : public X<T> {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("template<class T, class U=T> class X {}; " |
| "template<class T> class Z : public X<T> {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class X> class A { public: class Z : public X {}; }; " |
| "class X{}; void y() { A<X>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template <class T> class X {}; " |
| "template<class Y> class A { class Z : public X<Y> {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(notMatches("template<template<class T> class X> class A { " |
| " class Z : public X<int> {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("template<template<class T> class X> class A { " |
| " public: class Z : public X<int> {}; }; " |
| "template<class T> class X {}; void y() { A<X>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X::D {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("template<class X> class A { public: " |
| " class Z : public X::D {}; }; " |
| "class Y { public: class X {}; typedef X D; }; " |
| "void y() { A<Y>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; typedef X Y; class Z : public Y {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("template<class T> class Y { typedef typename T::U X; " |
| " class Z : public X {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; class Z : public ::X {};", ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class T> class X {}; " |
| "template<class T> class A { class Z : public X<T>::D {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T> class X { public: typedef X<T> D; }; " |
| "template<class T> class A { public: " |
| " class Z : public X<T>::D {}; }; void y() { A<int>::Z z; }", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class A { class Z : public X::D::E {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {}; typedef X V; typedef V W; class Z : public W {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; class Y : public X {}; " |
| "typedef Y V; typedef V W; class Z : public W {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class X {}; class Y : public X {}; " |
| "typedef Y V; typedef V W; class Z : public W {};", |
| ZIsDirectlyDerivedFromX)); |
| EXPECT_TRUE( |
| matches("template<class T, class U> class X {}; " |
| "template<class T> class A { class Z : public X<T, int> {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<class X> class D { typedef X A; typedef A B; " |
| " typedef B C; class Z : public C {}; };", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; " |
| "class Z : public B {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class X {}; typedef X A; typedef A B; typedef B C; " |
| "class Z : public C {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class U {}; typedef U X; typedef X V; " |
| "class Z : public V {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class Base {}; typedef Base X; " |
| "class Z : public Base {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class Base {}; typedef Base Base2; typedef Base2 X; " |
| "class Z : public Base {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(notMatches("class Base {}; class Base2 {}; typedef Base2 X; " |
| "class Z : public Base {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(matches("class A {}; typedef A X; typedef A Y; " |
| "class Z : public Y {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE(notMatches("template <typename T> class Z;" |
| "template <> class Z<void> {};" |
| "template <typename T> class Z : public Z<void> {};", |
| IsDerivedFromX)); |
| EXPECT_TRUE(matches("template <typename T> class X;" |
| "template <> class X<void> {};" |
| "template <typename T> class X : public X<void> {};", |
| IsDerivedFromX)); |
| EXPECT_TRUE( |
| matches("class X {};" |
| "template <typename T> class Z;" |
| "template <> class Z<void> {};" |
| "template <typename T> class Z : public Z<void>, public X {};", |
| ZIsDerivedFromX)); |
| EXPECT_TRUE( |
| notMatches("template<int> struct X;" |
| "template<int i> struct X : public X<i-1> {};", |
| cxxRecordDecl(isDerivedFrom(recordDecl(hasName("Some")))))); |
| EXPECT_TRUE(matches( |
| "struct A {};" |
| "template<int> struct X;" |
| "template<int i> struct X : public X<i-1> {};" |
| "template<> struct X<0> : public A {};" |
| "struct B : public X<42> {};", |
| cxxRecordDecl(hasName("B"), isDerivedFrom(recordDecl(hasName("A")))))); |
| EXPECT_TRUE(notMatches( |
| "struct A {};" |
| "template<int> struct X;" |
| "template<int i> struct X : public X<i-1> {};" |
| "template<> struct X<0> : public A {};" |
| "struct B : public X<42> {};", |
| cxxRecordDecl(hasName("B"), |
| 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 <typename T> class Z;" |
| "template <> class Z<void> : public Base1 {};" |
| "template <> class Z<int> : public Base2 {};" |
| "template <> class Z<float> : public Z<void> {};" |
| "template <> class Z<double> : public Z<int> {};" |
| "template <typename T> class Z : public Z<float>, public Z<double> {};" |
| "void f() { Z<float> z_float; Z<double> z_double; Z<char> z_char; }"; |
| EXPECT_TRUE(matches( |
| RecursiveTemplateOneParameter, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); |
| EXPECT_TRUE(notMatches( |
| RecursiveTemplateOneParameter, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE( |
| matches(RecursiveTemplateOneParameter, |
| varDecl(hasName("z_char"), |
| hasInitializer(hasType(cxxRecordDecl( |
| isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); |
| |
| const char *RecursiveTemplateTwoParameters = |
| "class Base1 {}; class Base2 {};" |
| "template <typename T1, typename T2> class Z;" |
| "template <typename T> class Z<void, T> : public Base1 {};" |
| "template <typename T> class Z<int, T> : public Base2 {};" |
| "template <typename T> class Z<float, T> : public Z<void, T> {};" |
| "template <typename T> class Z<double, T> : public Z<int, T> {};" |
| "template <typename T1, typename T2> class Z : " |
| " public Z<float, T2>, public Z<double, T2> {};" |
| "void f() { Z<float, void> z_float; Z<double, void> z_double; " |
| " Z<char, void> z_char; }"; |
| EXPECT_TRUE(matches( |
| RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base1"))))))); |
| EXPECT_TRUE(notMatches( |
| RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_float"), |
| hasInitializer(hasType(cxxRecordDecl(isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE( |
| matches(RecursiveTemplateTwoParameters, |
| varDecl(hasName("z_char"), |
| hasInitializer(hasType(cxxRecordDecl( |
| isDerivedFrom("Base1"), isDerivedFrom("Base2"))))))); |
| EXPECT_TRUE(matches("namespace ns { class X {}; class Y : public X {}; }", |
| cxxRecordDecl(isDerivedFrom("::ns::X")))); |
| EXPECT_TRUE(notMatches("class X {}; class Y : public X {};", |
| cxxRecordDecl(isDerivedFrom("::ns::X")))); |
| |
| EXPECT_TRUE(matches( |
| "class X {}; class Y : public X {};", |
| cxxRecordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test"))))); |
| |
| EXPECT_TRUE(matches("template<typename T> class X {};" |
| "template<typename T> using Z = X<T>;" |
| "template <typename T> class Y : Z<T> {};", |
| cxxRecordDecl(isDerivedFrom(namedDecl(hasName("X")))))); |
| } |
| |
| TEST_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<VerifyIdIsBoundTo<CXXRecordDecl>>("x"))); |
| |
| EXPECT_TRUE(matchAndVerifyResultFalse( |
| "class X {};", ClassX, |
| std::make_unique<VerifyIdIsBoundTo<CXXRecordDecl>>("other-id"))); |
| |
| TypeMatcher TypeAHasClassB = hasDeclaration( |
| recordDecl(hasName("A"), has(recordDecl(hasName("B")).bind("b")))); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { public: A *a; class B {}; };", TypeAHasClassB, |
| std::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); |
| |
| StatementMatcher MethodX = |
| callExpr(callee(cxxMethodDecl(hasName("x")))).bind("x"); |
| |
| EXPECT_TRUE(matchAndVerifyResultTrue( |
| "class A { void x() { x(); } };", MethodX, |
| std::make_unique<VerifyIdIsBoundTo<CXXMemberCallExpr>>("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<VerifyIdIsBoundTo<CallExpr>>("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<VerifyIdIsBoundTo<Decl>>("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<VerifyIdIsBoundTo<CXXOperatorCallExpr>>("x", 2))); |
| EXPECT_TRUE(matches("class Y { }; " |
| "Y& operator&&(Y& x, Y& y) { return x; }; " |
| "Y a; Y b; Y c; Y d = a && b && c;", |
| cxxOperatorCallExpr(hasParent(cxxOperatorCallExpr())))); |
| EXPECT_TRUE( |
| matches("class Y { }; " |
| "Y& operator&&(Y& x, Y& y) { return x; }; " |
| "Y a; Y b; Y c; Y d = a && b && c;", |
| cxxOperatorCallExpr(hasDescendant(cxxOperatorCallExpr())))); |
| } |
| |
| TEST_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, IsInitCapture) { |
| if (!GetParam().isCXX11OrLater()) { |
| return; |
| } |
| auto M = varDecl(hasName("vd"), isInitCapture()); |
| EXPECT_TRUE(notMatches( |
| "int main() { int vd = 3; auto f = [vd]() { return vd; }; }", M)); |
| |
| if (!GetParam().isCXX14OrLater()) { |
| return; |
| } |
| EXPECT_TRUE(matches("int main() { auto f = [vd=3]() { return vd; }; }", M)); |
| EXPECT_TRUE(matches( |
| "int main() { int x = 3; auto f = [vd=x]() { return vd; }; }", 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 T> class Y { void x() { this->m; } };", |
| cxxDependentScopeMemberExpr(isArrow()))); |
| EXPECT_TRUE( |
| notMatches("template <class T> 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 <class T> void x() { this->x<T>(); } };", |
| unresolvedMemberExpr(isArrow()))); |
| EXPECT_TRUE(matches("class Y { template <class T> void x() { x<T>(); } };", |
| unresolvedMemberExpr(isArrow()))); |
| EXPECT_TRUE( |
| notMatches("class Y { template <class T> void x() { (*this).x<T>(); } };", |
| 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<bool b> 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<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(templateArgumentCountIs(1)))); |
| EXPECT_TRUE( |
| notMatches("template<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(templateArgumentCountIs(2)))); |
| |
| EXPECT_TRUE(matches("template<typename T> struct C {}; C<int> c;", |
| templateSpecializationType(templateArgumentCountIs(1)))); |
| EXPECT_TRUE( |
| notMatches("template<typename T> struct C {}; C<int> c;", |
| templateSpecializationType(templateArgumentCountIs(2)))); |
| } |
| |
| TEST_P(ASTMatchersTest, IsIntegral) { |
| if (!GetParam().isCXX()) { |
| return; |
| } |
| |
| EXPECT_TRUE(matches( |
| "template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl(hasAnyTemplateArgument(isIntegral())))); |
| EXPECT_TRUE(notMatches("template<typename T> struct C {}; C<int> c;", |
| classTemplateSpecializationDecl(hasAnyTemplateArgument( |
| templateArgument(isIntegral()))))); |
| } |
| |
| TEST_P(ASTMatchersTest, EqualsIntegralValue) { |
| if (!GetParam().isCXX()) { |
| return; |
| } |
| |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("42"))))); |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<-42> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("-42"))))); |
| EXPECT_TRUE(matches("template<int T> struct C {}; C<-0042> c;", |
| classTemplateSpecializationDecl( |
| hasAnyTemplateArgument(equalsIntegralValue("-34"))))); |
| EXPECT_TRUE(notMatches("template<int T> struct C {}; C<42> c;", |
| classTemplateSpecializationDecl(hasAnyTemplateArgument( |
| equalsIntegralValue("0042"))))); |
| } |
| |
| TEST_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 <typename Base> 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 <typename T> struct S{ void mem(); }; template " |
| "<typename T> void x() { S<T> 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 <typename T> struct S{ int mem; }; template " |
| "<typename T> void x() { S<T> 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 <typename T> struct S{ static int mem; }; template " |
| "<typename T> void x() { S<T> 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 <typename T> |
| struct S { |
| bool operator==(int) const { return true; } |
| }; |
| |
| template <typename T> |
| void func(T t) { |
| S<T> s; |
| s.operator==(1); |
| } |
| )cpp"; |
| |
| EXPECT_TRUE(matches( |
| Code, cxxDependentScopeMemberExpr(hasMemberName("operator==")))); |
| } |
| |
| // other named decl: |
| { |
| auto Code = "template <typename T> struct S{ static int mem; }; struct " |
| "mem{}; template " |
| "<typename T> void x() { S<T> 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 <typename T> struct S{}; template <typename T> void " |
| "x() { auto s = S<T>(); }"; |
| |
| EXPECT_TRUE(matches(Code, cxxUnresolvedConstructExpr(argumentCountIs(0)))); |
| EXPECT_TRUE(notMatches(Code, cxxUnresolvedConstructExpr(argumentCountIs(1)))); |
| } |
| |
| TEST(ASTMatchersTest, HasArgument_CXXUnresolvedConstructExpr) { |
| const auto *Code = |
| "template <typename T> struct S{ S(int){} }; template <typename " |
| "T> void x() { int y; auto s = S<T>(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<bool b> 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<typename T> struct S { S(int);};" |
| "S(int) -> S<int>;", |
| cxxDeductionGuideDecl(isExplicit()))); |
| EXPECT_TRUE(matches("template<typename T> struct S { S(int);};" |
| "explicit S(int) -> S<int>;", |
| cxxDeductionGuideDecl(isExplicit()))); |
| } |
| |
| TEST_P(ASTMatchersTest, IsExplicit_CXXDeductionGuideDecl_CXX20) { |
| if (!GetParam().isCXX20OrLater()) { |
| return; |
| } |
| |
| EXPECT_TRUE(matches("template<typename T> struct S { S(int);};" |
| "explicit(true) S(int) -> S<int>;", |
| cxxDeductionGuideDecl(isExplicit()))); |
| EXPECT_TRUE(notMatches("template<typename T> struct S { S(int);};" |
| "explicit(false) S(int) -> S<int>;", |
| cxxDeductionGuideDecl(isExplicit()))); |
| EXPECT_TRUE( |
| notMatches("template<typename T> struct S { S(int);};" |
| "template<bool b = true> explicit(b) S(int) -> S<int>;", |
| 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<StringRef> 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 <typename T>" |
| "void f(T t) {" |
| " T local_t(t /* this becomes a null QualType in the AST */);" |
| "}" |
| "void g() {" |
| " f(0);" |
| "}", |
| expr(hasType(TypeMatcher(anyOf(TypeMatcher(hasDeclaration(anything())), |
| pointsTo(AnyType), references(AnyType) |
| // Other QualType matchers should go here. |
| )))))); |
| } |
| |
| TEST_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 <class T> struct X { void f() { T t; t.m; } };", |
| cxxDependentScopeMemberExpr(hasObjectExpression( |
| declRefExpr(to(namedDecl(hasName("t")))))))); |
| EXPECT_TRUE( |
| matches("template <class T> 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 <class T> void f(); };" |
| "template <class T> void g(X x) { x.f<T>(); }", |
| unresolvedMemberExpr(hasObjectExpression( |
| hasType(recordDecl(hasName("X"))))))); |
| EXPECT_TRUE(matches("template <class T> 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<VerifyIdIsBoundTo<FieldDecl>>("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<VerifyIdIsBoundTo<FieldDecl>>("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<VerifyIdIsBoundTo<FieldDecl>>("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<VerifyIdIsBoundTo<FieldDecl>>("c"))); |
| } |
| |
| // Regression test. |
| TEST_P(ASTMatchersTest, Optionally_SubmatchersDoNotMatchButPreserveBindings) { |
| StringRef Code = "class A { int a; int b; };"; |
| auto Matcher = recordDecl(decl().bind("decl"), |
| optionally(has
|