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