aboutsummaryrefslogtreecommitdiff
path: root/unittests/ASTMatchers/ASTMatchersTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'unittests/ASTMatchers/ASTMatchersTest.cpp')
-rw-r--r--unittests/ASTMatchers/ASTMatchersTest.cpp360
1 files changed, 348 insertions, 12 deletions
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp
index cfa53868ed29..cc13c01fec39 100644
--- a/unittests/ASTMatchers/ASTMatchersTest.cpp
+++ b/unittests/ASTMatchers/ASTMatchersTest.cpp
@@ -40,6 +40,18 @@ TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
}
#endif
+TEST(Finder, DynamicOnlyAcceptsSomeMatchers) {
+ MatchFinder Finder;
+ EXPECT_TRUE(Finder.addDynamicMatcher(decl(), NULL));
+ EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), NULL));
+ EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), NULL));
+
+ // Do not accept non-toplevel matchers.
+ EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), NULL));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), NULL));
+ EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), NULL));
+}
+
TEST(Decl, MatchesDeclarations) {
EXPECT_TRUE(notMatches("", decl(usingDecl())));
EXPECT_TRUE(matches("namespace x { class X {}; } using x::X;",
@@ -311,6 +323,12 @@ TEST(DeclarationMatcher, ClassIsDerived) {
EXPECT_TRUE(matches(
"class X {}; class Y : public X {};",
recordDecl(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> {};",
+ recordDecl(isDerivedFrom(namedDecl(hasName("X"))))));
}
TEST(DeclarationMatcher, hasMethod) {
@@ -645,14 +663,22 @@ public:
: Id(Id), ExpectedCount(ExpectedCount), Count(0),
ExpectedName(ExpectedName) {}
- ~VerifyIdIsBoundTo() {
+ void onEndOfTranslationUnit() LLVM_OVERRIDE {
if (ExpectedCount != -1)
EXPECT_EQ(ExpectedCount, Count);
if (!ExpectedName.empty())
EXPECT_EQ(ExpectedName, Name);
+ Count = 0;
+ Name.clear();
+ }
+
+ ~VerifyIdIsBoundTo() {
+ EXPECT_EQ(0, Count);
+ EXPECT_EQ("", Name);
}
virtual bool run(const BoundNodes *Nodes) {
+ const BoundNodes::IDToNodeMap &M = Nodes->getMap();
if (Nodes->getNodeAs<T>(Id)) {
++Count;
if (const NamedDecl *Named = Nodes->getNodeAs<NamedDecl>(Id)) {
@@ -662,8 +688,13 @@ public:
llvm::raw_string_ostream OS(Name);
NNS->print(OS, PrintingPolicy(LangOptions()));
}
+ BoundNodes::IDToNodeMap::const_iterator I = M.find(Id);
+ EXPECT_NE(M.end(), I);
+ if (I != M.end())
+ EXPECT_EQ(Nodes->getNodeAs<T>(Id), I->second.get<T>());
return true;
}
+ EXPECT_TRUE(M.count(Id) == 0 || M.find(Id)->second.template get<T>() == 0);
return false;
}
@@ -912,6 +943,15 @@ TEST(HasType, TakesDeclMatcherAndMatchesValueDecl) {
notMatches("class X {}; void y() { X *x; }", varDecl(hasType(ClassX))));
}
+TEST(HasTypeLoc, MatchesDeclaratorDecls) {
+ EXPECT_TRUE(matches("int x;",
+ varDecl(hasName("x"), hasTypeLoc(loc(asString("int"))))));
+
+ // Make sure we don't crash on implicit constructors.
+ EXPECT_TRUE(notMatches("class X {}; X x;",
+ declaratorDecl(hasTypeLoc(loc(asString("int"))))));
+}
+
TEST(Matcher, Call) {
// FIXME: Do we want to overload Call() to directly take
// Matcher<Decl>, too?
@@ -1346,8 +1386,12 @@ TEST(Matcher, References) {
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 {}; void y(X y) { X x = y; }", ReferenceClassX));
+ notMatches("class X {}; extern X x;", ReferenceClassX));
EXPECT_TRUE(
notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX));
}
@@ -1452,6 +1496,16 @@ TEST(Matcher, MatchesClassTemplateSpecialization) {
classTemplateSpecializationDecl()));
}
+TEST(DeclaratorDecl, MatchesDeclaratorDecls) {
+ EXPECT_TRUE(matches("int x;", declaratorDecl()));
+ EXPECT_TRUE(notMatches("class A {};", declaratorDecl()));
+}
+
+TEST(ParmVarDecl, MatchesParmVars) {
+ EXPECT_TRUE(matches("void f(int x);", parmVarDecl()));
+ EXPECT_TRUE(notMatches("void f();", parmVarDecl()));
+}
+
TEST(Matcher, MatchesTypeTemplateArgument) {
EXPECT_TRUE(matches(
"template<typename T> struct B {};"
@@ -1507,6 +1561,13 @@ TEST(Matcher, MatchesVirtualMethod) {
methodDecl(isVirtual())));
}
+TEST(Matcher, MatchesConstMethod) {
+ EXPECT_TRUE(matches("struct A { void foo() const; };",
+ methodDecl(isConst())));
+ EXPECT_TRUE(notMatches("struct A { void foo(); };",
+ methodDecl(isConst())));
+}
+
TEST(Matcher, MatchesOverridingMethod) {
EXPECT_TRUE(matches("class X { virtual int f(); }; "
"class Y : public X { int f(); };",
@@ -1827,6 +1888,17 @@ TEST(Matcher, IntegerLiterals) {
EXPECT_TRUE(notMatches("int i = 10.0;", HasIntLiteral));
}
+TEST(Matcher, FloatLiterals) {
+ StatementMatcher HasFloatLiteral = floatLiteral();
+ EXPECT_TRUE(matches("float i = 10.0;", HasFloatLiteral));
+ EXPECT_TRUE(matches("float i = 10.0f;", HasFloatLiteral));
+ EXPECT_TRUE(matches("double i = 10.0;", HasFloatLiteral));
+ EXPECT_TRUE(matches("double i = 10.0L;", HasFloatLiteral));
+ EXPECT_TRUE(matches("double i = 1e10;", HasFloatLiteral));
+
+ EXPECT_TRUE(notMatches("float i = 10;", HasFloatLiteral));
+}
+
TEST(Matcher, NullPtrLiteral) {
EXPECT_TRUE(matches("int* i = nullptr;", nullPtrLiteralExpr()));
}
@@ -2224,10 +2296,9 @@ TEST(AstMatcherPMacro, Works) {
}
AST_POLYMORPHIC_MATCHER_P(
- polymorphicHas, internal::Matcher<Decl>, AMatcher) {
- TOOLING_COMPILE_ASSERT((llvm::is_same<NodeType, Decl>::value) ||
- (llvm::is_same<NodeType, Stmt>::value),
- assert_node_type_is_accessible);
+ polymorphicHas,
+ AST_POLYMORPHIC_SUPPORTED_TYPES_2(Decl, Stmt),
+ internal::Matcher<Decl>, AMatcher) {
return Finder->matchesChildOf(
Node, AMatcher, Builder,
ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses,
@@ -2917,6 +2988,37 @@ TEST(SwitchCase, MatchesSwitch) {
EXPECT_TRUE(notMatches("void x() {}", switchStmt()));
}
+TEST(SwitchCase, MatchesEachCase) {
+ EXPECT_TRUE(notMatches("void x() { switch(42); }",
+ switchStmt(forEachSwitchCase(caseStmt()))));
+ EXPECT_TRUE(matches("void x() { switch(42) case 42:; }",
+ switchStmt(forEachSwitchCase(caseStmt()))));
+ EXPECT_TRUE(matches("void x() { switch(42) { case 42:; } }",
+ switchStmt(forEachSwitchCase(caseStmt()))));
+ EXPECT_TRUE(notMatches(
+ "void x() { if (1) switch(42) { case 42: switch (42) { default:; } } }",
+ ifStmt(has(switchStmt(forEachSwitchCase(defaultStmt()))))));
+ EXPECT_TRUE(matches("void x() { switch(42) { case 1+1: case 4:; } }",
+ switchStmt(forEachSwitchCase(
+ caseStmt(hasCaseConstant(integerLiteral()))))));
+ EXPECT_TRUE(notMatches("void x() { switch(42) { case 1+1: case 2+2:; } }",
+ switchStmt(forEachSwitchCase(
+ caseStmt(hasCaseConstant(integerLiteral()))))));
+ EXPECT_TRUE(notMatches("void x() { switch(42) { case 1 ... 2:; } }",
+ switchStmt(forEachSwitchCase(
+ caseStmt(hasCaseConstant(integerLiteral()))))));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void x() { switch (42) { case 1: case 2: case 3: default:; } }",
+ switchStmt(forEachSwitchCase(caseStmt().bind("x"))),
+ new VerifyIdIsBoundTo<CaseStmt>("x", 3)));
+}
+
+TEST(ForEachConstructorInitializer, MatchesInitializers) {
+ EXPECT_TRUE(matches(
+ "struct X { X() : i(42), j(42) {} int i, j; };",
+ constructorDecl(forEachConstructorInitializer(ctorInitializer()))));
+}
+
TEST(ExceptionHandling, SimpleCases) {
EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", catchStmt()));
EXPECT_TRUE(matches("void foo() try { } catch(int X) { }", tryStmt()));
@@ -2977,12 +3079,13 @@ TEST(ForEachDescendant, NestedForEachDescendant) {
recordDecl(hasName("A"), anyOf(m, forEachDescendant(m))),
new VerifyIdIsBoundTo<Decl>("x", "C")));
- // FIXME: This is not really a useful matcher, but the result is still
- // surprising (currently binds "A").
- //EXPECT_TRUE(matchAndVerifyResultTrue(
- // "class A { class B { class C {}; }; };",
- // recordDecl(hasName("A"), allOf(hasDescendant(m), anyOf(m, anything()))),
- // new VerifyIdIsBoundTo<Decl>("x", "C")));
+ // Check that a partial match of 'm' that binds 'x' in the
+ // first part of anyOf(m, anything()) will not overwrite the
+ // binding created by the earlier binding in the hasDescendant.
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A { class B { class C {}; }; };",
+ recordDecl(hasName("A"), allOf(hasDescendant(m), anyOf(m, anything()))),
+ new VerifyIdIsBoundTo<Decl>("x", "C")));
}
TEST(ForEachDescendant, BindsMultipleNodes) {
@@ -3002,6 +3105,117 @@ TEST(ForEachDescendant, BindsRecursiveCombinations) {
new VerifyIdIsBoundTo<FieldDecl>("f", 8)));
}
+TEST(ForEachDescendant, BindsCombinations) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void f() { if(true) {} if (true) {} while (true) {} if (true) {} while "
+ "(true) {} }",
+ compoundStmt(forEachDescendant(ifStmt().bind("if")),
+ forEachDescendant(whileStmt().bind("while"))),
+ new VerifyIdIsBoundTo<IfStmt>("if", 6)));
+}
+
+TEST(Has, DoesNotDeleteBindings) {
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class X { int a; };", recordDecl(decl().bind("x"), has(fieldDecl())),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+}
+
+TEST(LoopingMatchers, DoNotOverwritePreviousMatchResultOnFailure) {
+ // Those matchers cover all the cases where an inner matcher is called
+ // and there is not a 1:1 relationship between the match of the outer
+ // matcher and the match of the inner matcher.
+ // The pattern to look for is:
+ // ... return InnerMatcher.matches(...); ...
+ // In which case no special handling is needed.
+ //
+ // On the other hand, if there are multiple alternative matches
+ // (for example forEach*) or matches might be discarded (for example has*)
+ // the implementation must make sure that the discarded matches do not
+ // affect the bindings.
+ // When new such matchers are added, add a test here that:
+ // - matches a simple node, and binds it as the first thing in the matcher:
+ // recordDecl(decl().bind("x"), hasName("X")))
+ // - uses the matcher under test afterwards in a way that not the first
+ // alternative is matched; for anyOf, that means the first branch
+ // would need to return false; for hasAncestor, it means that not
+ // the direct parent matches the inner matcher.
+
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class X { int y; };",
+ recordDecl(
+ recordDecl().bind("x"), hasName("::X"),
+ anyOf(forEachDescendant(recordDecl(hasName("Y"))), anything())),
+ new VerifyIdIsBoundTo<CXXRecordDecl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class X {};", recordDecl(recordDecl().bind("x"), hasName("::X"),
+ anyOf(unless(anything()), anything())),
+ new VerifyIdIsBoundTo<CXXRecordDecl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "template<typename T1, typename T2> class X {}; X<float, int> x;",
+ classTemplateSpecializationDecl(
+ decl().bind("x"),
+ hasAnyTemplateArgument(refersToType(asString("int")))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class X { void f(); void g(); };",
+ recordDecl(decl().bind("x"), hasMethod(hasName("g"))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class X { X() : a(1), b(2) {} double a; int b; };",
+ recordDecl(decl().bind("x"),
+ has(constructorDecl(
+ hasAnyConstructorInitializer(forField(hasName("b")))))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void x(int, int) { x(0, 42); }",
+ callExpr(expr().bind("x"), hasAnyArgument(integerLiteral(equals(42)))),
+ new VerifyIdIsBoundTo<Expr>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void x(int, int y) {}",
+ functionDecl(decl().bind("x"), hasAnyParameter(hasName("y"))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "void x() { return; if (true) {} }",
+ functionDecl(decl().bind("x"),
+ has(compoundStmt(hasAnySubstatement(ifStmt())))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "namespace X { void b(int); void b(); }"
+ "using X::b;",
+ usingDecl(decl().bind("x"), hasAnyUsingShadowDecl(hasTargetDecl(
+ functionDecl(parameterCountIs(1))))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A{}; class B{}; class C : B, A {};",
+ recordDecl(decl().bind("x"), isDerivedFrom("::A")),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A{}; typedef A B; typedef A C; typedef A D;"
+ "class E : A {};",
+ recordDecl(decl().bind("x"), isDerivedFrom("C")),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A { class B { void f() {} }; };",
+ functionDecl(decl().bind("x"), hasAncestor(recordDecl(hasName("::A")))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "template <typename T> struct A { struct B {"
+ " void f() { if(true) {} }"
+ "}; };"
+ "void t() { A<int>::B b; b.f(); }",
+ ifStmt(stmt().bind("x"), hasAncestor(recordDecl(hasName("::A")))),
+ new VerifyIdIsBoundTo<Stmt>("x", 2)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A {};",
+ recordDecl(hasName("::A"), decl().bind("x"), unless(hasName("fooble"))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+ EXPECT_TRUE(matchAndVerifyResultTrue(
+ "class A { A() : s(), i(42) {} const char *s; int i; };",
+ constructorDecl(hasName("::A::A"), decl().bind("x"),
+ forEachConstructorInitializer(forField(hasName("i")))),
+ new VerifyIdIsBoundTo<Decl>("x", 1)));
+}
+
TEST(ForEachDescendant, BindsCorrectNodes) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { void f(); int i; };",
@@ -3900,6 +4114,128 @@ TEST(MatchFinder, InterceptsStartOfTranslationUnit) {
OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
EXPECT_TRUE(VerifyCallback.Called);
+
+ VerifyCallback.Called = false;
+ OwningPtr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
+ ASSERT_TRUE(AST.get());
+ Finder.matchAST(AST->getASTContext());
+ EXPECT_TRUE(VerifyCallback.Called);
+}
+
+class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback {
+public:
+ VerifyEndOfTranslationUnit() : Called(false) {}
+ virtual void run(const MatchFinder::MatchResult &Result) {
+ EXPECT_FALSE(Called);
+ }
+ virtual void onEndOfTranslationUnit() {
+ Called = true;
+ }
+ bool Called;
+};
+
+TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
+ MatchFinder Finder;
+ VerifyEndOfTranslationUnit VerifyCallback;
+ Finder.addMatcher(decl(), &VerifyCallback);
+ OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder));
+ ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
+ EXPECT_TRUE(VerifyCallback.Called);
+
+ VerifyCallback.Called = false;
+ OwningPtr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
+ ASSERT_TRUE(AST.get());
+ Finder.matchAST(AST->getASTContext());
+ EXPECT_TRUE(VerifyCallback.Called);
+}
+
+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.
+ new 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")))))),
+ new VerifyIdIsBoundTo<VarDecl>("d", 5)));
}
} // end namespace ast_matchers