diff options
Diffstat (limited to 'unittests/ASTMatchers/ASTMatchersTest.cpp')
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 280 |
1 files changed, 232 insertions, 48 deletions
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index cc13c01fec39..bd7a5a6df8f3 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -12,6 +12,8 @@ #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 { @@ -42,14 +44,15 @@ TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) { 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)); + EXPECT_TRUE(Finder.addDynamicMatcher(decl(), nullptr)); + EXPECT_TRUE(Finder.addDynamicMatcher(callExpr(), nullptr)); + EXPECT_TRUE(Finder.addDynamicMatcher(constantArrayType(hasSize(42)), + nullptr)); // 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)); + EXPECT_FALSE(Finder.addDynamicMatcher(isArrow(), nullptr)); + EXPECT_FALSE(Finder.addDynamicMatcher(hasSize(2), nullptr)); + EXPECT_FALSE(Finder.addDynamicMatcher(hasName("x"), nullptr)); } TEST(Decl, MatchesDeclarations) { @@ -103,12 +106,13 @@ TEST(NameableDeclaration, REMatchesVariousDecls) { TEST(DeclarationMatcher, MatchClass) { DeclarationMatcher ClassMatcher(recordDecl()); -#if !defined(_MSC_VER) - EXPECT_FALSE(matches("", ClassMatcher)); -#else - // Matches class type_info. - EXPECT_TRUE(matches("", ClassMatcher)); -#endif + llvm::Triple Triple(llvm::sys::getDefaultTargetTriple()); + if (Triple.getOS() != llvm::Triple::Win32 || + Triple.getEnvironment() != llvm::Triple::MSVC) + EXPECT_FALSE(matches("", ClassMatcher)); + else + // Matches class type_info. + EXPECT_TRUE(matches("", ClassMatcher)); DeclarationMatcher ClassX = recordDecl(recordDecl(hasName("X"))); EXPECT_TRUE(matches("class X;", ClassX)); @@ -663,7 +667,7 @@ public: : Id(Id), ExpectedCount(ExpectedCount), Count(0), ExpectedName(ExpectedName) {} - void onEndOfTranslationUnit() LLVM_OVERRIDE { + void onEndOfTranslationUnit() override { if (ExpectedCount != -1) EXPECT_EQ(ExpectedCount, Count); if (!ExpectedName.empty()) @@ -694,7 +698,8 @@ public: 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); + EXPECT_TRUE(M.count(Id) == 0 || + M.find(Id)->second.template get<T>() == nullptr); return false; } @@ -1000,7 +1005,7 @@ TEST(Matcher, Call) { } TEST(Matcher, Lambda) { - EXPECT_TRUE(matches("auto f = [&] (int i) { return i; };", + EXPECT_TRUE(matches("auto f = [] (int i) { return i; };", lambdaExpr())); } @@ -1012,6 +1017,17 @@ TEST(Matcher, ForRange) { forRangeStmt())); } +TEST(Matcher, SubstNonTypeTemplateParm) { + EXPECT_FALSE(matches("template<int N>\n" + "struct A { static const int n = 0; };\n" + "struct B : public A<42> {};", + substNonTypeTemplateParmExpr())); + EXPECT_TRUE(matches("template<int N>\n" + "struct A { static const int n = N; };\n" + "struct B : public A<42> {};", + substNonTypeTemplateParmExpr())); +} + TEST(Matcher, UserDefinedLiteral) { EXPECT_TRUE(matches("constexpr char operator \"\" _inc (const char i) {" " return i + 1;" @@ -1038,7 +1054,7 @@ TEST(HasType, MatchesAsString) { 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>::A"))))); + fieldDecl(hasType(asString("struct (anonymous namespace)::A"))))); } TEST(Matcher, OverloadedOperatorCall) { @@ -1079,12 +1095,19 @@ TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) { "bool operator&&(Y x, Y y) { return true; }; " "Y a; Y b; bool c = a && b;", OpCallLessLess)); + StatementMatcher OpStarCall = + operatorCallExpr(hasOverloadedOperatorName("*")); + EXPECT_TRUE(matches("class Y; int operator*(Y &); void f(Y &y) { *y; }", + OpStarCall)); DeclarationMatcher ClassWithOpStar = recordDecl(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) { @@ -1162,6 +1185,18 @@ TEST(Matcher, VariableUsage) { "}", Reference)); } +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, FindsVarDeclInFunctionParameter) { EXPECT_TRUE(matches( "void f(int i) {}", @@ -1300,15 +1335,16 @@ TEST(Function, MatchesFunctionDeclarations) { EXPECT_TRUE(matches("void f() { f(); }", CallFunctionF)); EXPECT_TRUE(notMatches("void f() { }", CallFunctionF)); -#if !defined(_MSC_VER) - // FIXME: Make this work for MSVC. - // Dependent contexts, but a non-dependent call. - EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }", - CallFunctionF)); - EXPECT_TRUE( - matches("void f(); template <int N> struct S { void g() { f(); } };", - CallFunctionF)); -#endif + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() != + llvm::Triple::Win32) { + // FIXME: Make this work for MSVC. + // Dependent contexts, but a non-dependent call. + EXPECT_TRUE(matches("void f(); template <int N> void g() { f(); }", + CallFunctionF)); + EXPECT_TRUE( + matches("void f(); template <int N> struct S { void g() { f(); } };", + CallFunctionF)); + } // Depedent calls don't match. EXPECT_TRUE( @@ -1479,7 +1515,7 @@ TEST(HasAnyParameter, DoesNotMatchThisPointer) { recordDecl(hasName("X")))))))); } -TEST(HasName, MatchesParameterVariableDeclartions) { +TEST(HasName, MatchesParameterVariableDeclarations) { EXPECT_TRUE(matches("class Y {}; class X { void x(int x) {} };", methodDecl(hasAnyParameter(hasName("x"))))); EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", @@ -1527,6 +1563,19 @@ TEST(Matcher, MatchesDeclarationReferenceTemplateArgument) { "A<int> a;", classTemplateSpecializationDecl(hasAnyTemplateArgument( refersToDeclaration(decl()))))); + + EXPECT_TRUE(matches( + "struct B { int next; };" + "template<int(B::*next_ptr)> struct A {};" + "A<&B::next> a;", + templateSpecializationType(hasAnyTemplateArgument(isExpr( + hasDescendant(declRefExpr(to(fieldDecl(hasName("next")))))))))); + + EXPECT_TRUE(notMatches( + "template <typename T> struct A {};" + "A<int> a;", + templateSpecializationType(hasAnyTemplateArgument( + refersToDeclaration(decl()))))); } TEST(Matcher, MatchesSpecificArgument) { @@ -1540,6 +1589,17 @@ TEST(Matcher, MatchesSpecificArgument) { "A<int, bool> a;", classTemplateSpecializationDecl(hasTemplateArgument( 1, refersToType(asString("int")))))); + + EXPECT_TRUE(matches( + "template<typename T, typename U> class A {};" + "A<bool, int> a;", + templateSpecializationType(hasTemplateArgument( + 1, refersToType(asString("int")))))); + EXPECT_TRUE(notMatches( + "template<typename T, typename U> class A {};" + "A<int, bool> a;", + templateSpecializationType(hasTemplateArgument( + 1, refersToType(asString("int")))))); } TEST(Matcher, MatchesAccessSpecDecls) { @@ -1561,6 +1621,13 @@ TEST(Matcher, MatchesVirtualMethod) { methodDecl(isVirtual()))); } +TEST(Matcher, MatchesPureMethod) { + EXPECT_TRUE(matches("class X { virtual int f() = 0; };", + methodDecl(isPure(), hasName("::X::f")))); + EXPECT_TRUE(notMatches("class X { int f(); };", + methodDecl(isPure()))); +} + TEST(Matcher, MatchesConstMethod) { EXPECT_TRUE(matches("struct A { void foo() const; };", methodDecl(isConst()))); @@ -1637,6 +1704,17 @@ TEST(Matcher, ConstructorArgumentCount) { Constructor1Arg)); } +TEST(Matcher, ConstructorListInitialization) { + StatementMatcher ConstructorListInit = constructExpr(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(Matcher,ThisExpr) { EXPECT_TRUE( matches("struct X { int a; int f () { return a; } };", thisExpr())); @@ -1727,6 +1805,9 @@ TEST(ConstructorDeclaration, IsImplicit) { constructorDecl(isImplicit()))); EXPECT_TRUE(matches("class Foo { Foo(){} };", constructorDecl(unless(isImplicit())))); + // The compiler added an implicit assignment operator. + EXPECT_TRUE(matches("struct A { int x; } a = {0}, b = a; void f() { a = b; }", + methodDecl(isImplicit(), hasName("operator=")))); } TEST(DestructorDeclaration, MatchesVirtualDestructor) { @@ -1917,6 +1998,17 @@ TEST(Matcher, Conditions) { EXPECT_TRUE(notMatches("void x() { if (1) {} }", Condition)); } +TEST(IfStmt, ChildTraversalMatchers) { + EXPECT_TRUE(matches("void f() { if (false) true; else false; }", + ifStmt(hasThen(boolLiteral(equals(true)))))); + EXPECT_TRUE(notMatches("void f() { if (false) false; else true; }", + ifStmt(hasThen(boolLiteral(equals(true)))))); + EXPECT_TRUE(matches("void f() { if (false) false; else true; }", + ifStmt(hasElse(boolLiteral(equals(true)))))); + EXPECT_TRUE(notMatches("void f() { if (false) true; else false; }", + ifStmt(hasElse(boolLiteral(equals(true)))))); +} + TEST(MatchBinaryOperator, HasOperatorName) { StatementMatcher OperatorOr = binaryOperator(hasOperatorName("||")); @@ -2339,6 +2431,14 @@ TEST(For, ForLoopInternals) { forStmt(hasLoopInit(anything())))); } +TEST(For, ForRangeLoopInternals) { + EXPECT_TRUE(matches("void f(){ int a[] {1, 2}; for (int i : a); }", + forRangeStmt(hasLoopVariable(anything())))); + EXPECT_TRUE(matches( + "void f(){ int a[] {1, 2}; for (int i : a); }", + forRangeStmt(hasRangeInit(declRefExpr(to(varDecl(hasName("a")))))))); +} + TEST(For, NegativeForLoopInternals) { EXPECT_TRUE(notMatches("void f(){ for (int i = 0; ; ++i); }", forStmt(hasCondition(expr())))); @@ -2375,6 +2475,8 @@ TEST(HasBody, FindsBodyOfForWhileDoLoops) { whileStmt(hasBody(compoundStmt())))); EXPECT_TRUE(matches("void f() { do {} while(true); }", doStmt(hasBody(compoundStmt())))); + EXPECT_TRUE(matches("void f() { int p[2]; for (auto x : p) {} }", + forRangeStmt(hasBody(compoundStmt())))); } TEST(HasAnySubstatement, MatchesForTopLevelCompoundStatement) { @@ -2608,13 +2710,13 @@ TEST(ReinterpretCast, DoesNotMatchOtherCasts) { } TEST(FunctionalCast, MatchesSimpleCase) { - std::string foo_class = "class Foo { public: Foo(char*); };"; + std::string foo_class = "class Foo { public: Foo(const char*); };"; EXPECT_TRUE(matches(foo_class + "void r() { Foo f = Foo(\"hello world\"); }", functionalCastExpr())); } TEST(FunctionalCast, DoesNotMatchOtherCasts) { - std::string FooClass = "class Foo { public: Foo(char*); };"; + std::string FooClass = "class Foo { public: Foo(const char*); };"; EXPECT_TRUE( notMatches(FooClass + "void r() { Foo f = (Foo) \"hello world\"; }", functionalCastExpr())); @@ -2892,6 +2994,15 @@ TEST(DeclarationStatement, MatchesVariableDeclarationStatements) { EXPECT_TRUE(matches("void x() { int a; }", declStmt())); } +TEST(ExprWithCleanups, MatchesExprWithCleanups) { + EXPECT_TRUE(matches("struct Foo { ~Foo(); };" + "const Foo f = Foo();", + varDecl(hasInitializer(exprWithCleanups())))); + EXPECT_FALSE(matches("struct Foo { };" + "const Foo f = Foo();", + varDecl(hasInitializer(exprWithCleanups())))); +} + TEST(InitListExpression, MatchesInitListExpression) { EXPECT_TRUE(matches("int a[] = { 1, 2 };", initListExpr(hasType(asString("int [2]"))))); @@ -2927,6 +3038,13 @@ TEST(UsingDeclaration, ThroughUsingDeclaration) { declRefExpr(throughUsingDecl(anything())))); } +TEST(UsingDirectiveDeclaration, MatchesUsingNamespace) { + EXPECT_TRUE(matches("namespace X { int x; } using namespace X;", + usingDirectiveDecl())); + EXPECT_FALSE( + matches("namespace X { int x; } using X::x;", usingDirectiveDecl())); +} + TEST(SingleDecl, IsSingleDecl) { StatementMatcher SingleDeclStmt = declStmt(hasSingleDecl(varDecl(hasInitializer(anything())))); @@ -3530,6 +3648,27 @@ TEST(HasParent, MatchesAllParents) { compoundStmt(hasParent(recordDecl())))); } +TEST(HasParent, NoDuplicateParents) { + class HasDuplicateParents : public BoundNodesCallback { + public: + bool run(const BoundNodes *Nodes) override { return false; } + bool run(const BoundNodes *Nodes, ASTContext *Context) override { + const Stmt *Node = Nodes->getNodeAs<Stmt>("node"); + std::set<const void *> Parents; + for (const auto &Parent : Context->getParents(*Node)) { + if (!Parents.insert(Parent.getMemoizationData()).second) { + return true; + } + } + return false; + } + }; + EXPECT_FALSE(matchAndVerifyResultTrue( + "template <typename T> int Foo() { return 1 + 2; }\n" + "int x = Foo<int>() + Foo<unsigned>();", + stmt().bind("node"), new HasDuplicateParents())); +} + TEST(TypeMatching, MatchesTypes) { EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); } @@ -3617,12 +3756,16 @@ TEST(TypeMatching, MatchesVariableArrayType) { } TEST(TypeMatching, MatchesAtomicTypes) { - EXPECT_TRUE(matches("_Atomic(int) i;", atomicType())); - - EXPECT_TRUE(matches("_Atomic(int) i;", - atomicType(hasValueType(isInteger())))); - EXPECT_TRUE(notMatches("_Atomic(float) f;", - atomicType(hasValueType(isInteger())))); + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getOS() != + llvm::Triple::Win32) { + // FIXME: Make this work for MSVC. + EXPECT_TRUE(matches("_Atomic(int) i;", atomicType())); + + EXPECT_TRUE(matches("_Atomic(int) i;", + atomicType(hasValueType(isInteger())))); + EXPECT_TRUE(notMatches("_Atomic(float) f;", + atomicType(hasValueType(isInteger())))); + } } TEST(TypeMatching, MatchesAutoTypes) { @@ -4021,7 +4164,7 @@ public: virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { const T *Node = Nodes->getNodeAs<T>(Id); return selectFirst<const T>(InnerId, - match(InnerMatcher, *Node, *Context)) != NULL; + match(InnerMatcher, *Node, *Context)) !=nullptr; } private: std::string Id; @@ -4075,24 +4218,32 @@ public: } bool verify(const BoundNodes &Nodes, ASTContext &Context, const Stmt *Node) { + // Use the original typed pointer to verify we can pass pointers to subtypes + // to equalsNode. + const T *TypedNode = cast<T>(Node); return selectFirst<const T>( - "", match(stmt(hasParent(stmt(has(stmt(equalsNode(Node)))).bind(""))), - *Node, Context)) != NULL; + "", match(stmt(hasParent( + stmt(has(stmt(equalsNode(TypedNode)))).bind(""))), + *Node, Context)) != nullptr; } bool verify(const BoundNodes &Nodes, ASTContext &Context, const Decl *Node) { + // Use the original typed pointer to verify we can pass pointers to subtypes + // to equalsNode. + const T *TypedNode = cast<T>(Node); return selectFirst<const T>( - "", match(decl(hasParent(decl(has(decl(equalsNode(Node)))).bind(""))), - *Node, Context)) != NULL; + "", match(decl(hasParent( + decl(has(decl(equalsNode(TypedNode)))).bind(""))), + *Node, Context)) != nullptr; } }; TEST(IsEqualTo, MatchesNodesByIdentity) { EXPECT_TRUE(matchAndVerifyResultTrue( "class X { class Y {}; };", recordDecl(hasName("::X::Y")).bind(""), - new VerifyAncestorHasChildIsEqual<Decl>())); - EXPECT_TRUE( - matchAndVerifyResultTrue("void f() { if(true) {} }", ifStmt().bind(""), - new VerifyAncestorHasChildIsEqual<Stmt>())); + new VerifyAncestorHasChildIsEqual<CXXRecordDecl>())); + EXPECT_TRUE(matchAndVerifyResultTrue( + "void f() { if (true) if(true) {} }", ifStmt().bind(""), + new VerifyAncestorHasChildIsEqual<IfStmt>())); } class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { @@ -4111,12 +4262,13 @@ TEST(MatchFinder, InterceptsStartOfTranslationUnit) { MatchFinder Finder; VerifyStartOfTranslationUnit VerifyCallback; Finder.addMatcher(decl(), &VerifyCallback); - OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + std::unique_ptr<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;")); + std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;")); ASSERT_TRUE(AST.get()); Finder.matchAST(AST->getASTContext()); EXPECT_TRUE(VerifyCallback.Called); @@ -4138,12 +4290,13 @@ TEST(MatchFinder, InterceptsEndOfTranslationUnit) { MatchFinder Finder; VerifyEndOfTranslationUnit VerifyCallback; Finder.addMatcher(decl(), &VerifyCallback); - OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); + std::unique_ptr<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;")); + std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;")); ASSERT_TRUE(AST.get()); Finder.matchAST(AST->getASTContext()); EXPECT_TRUE(VerifyCallback.Called); @@ -4204,7 +4357,6 @@ TEST(EqualsBoundNodeMatcher, Type) { } TEST(EqualsBoundNodeMatcher, UsingForEachDescendant) { - EXPECT_TRUE(matchAndVerifyResultTrue( "int f() {" " if (1) {" @@ -4238,5 +4390,37 @@ TEST(EqualsBoundNodeMatcher, FiltersMatchedCombinations) { new 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();" + "}", + memberCallExpr( + callee(methodDecl(hasName("data"))), + on(declRefExpr(to(varDecl(hasType(recordDecl(hasName("StringRef")))) + .bind("var")))), + unless(hasAncestor(stmt(hasDescendant(memberCallExpr( + callee(methodDecl(anyOf(hasName("size"), hasName("length")))), + on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) + .bind("data"), + new VerifyIdIsBoundTo<Expr>("data", 1))); + + EXPECT_FALSE(matches( + "struct StringRef { int size() const; const char* data() const; };" + "void f(StringRef v) {" + " v.data();" + " v.size();" + "}", + memberCallExpr( + callee(methodDecl(hasName("data"))), + on(declRefExpr(to(varDecl(hasType(recordDecl(hasName("StringRef")))) + .bind("var")))), + unless(hasAncestor(stmt(hasDescendant(memberCallExpr( + callee(methodDecl(anyOf(hasName("size"), hasName("length")))), + on(declRefExpr(to(varDecl(equalsBoundNode("var"))))))))))) + .bind("data"))); +} + } // end namespace ast_matchers } // end namespace clang |