diff options
Diffstat (limited to 'unittests/ASTMatchers/ASTMatchersTest.cpp')
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 360 |
1 files changed, 348 insertions, 12 deletions
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index cfa5386..cc13c01 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 |