diff options
Diffstat (limited to 'unittests/ASTMatchers')
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.cpp | 273 | ||||
-rw-r--r-- | unittests/ASTMatchers/ASTMatchersTest.h | 87 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/ParserTest.cpp | 75 | ||||
-rw-r--r-- | unittests/ASTMatchers/Dynamic/RegistryTest.cpp | 60 |
4 files changed, 444 insertions, 51 deletions
diff --git a/unittests/ASTMatchers/ASTMatchersTest.cpp b/unittests/ASTMatchers/ASTMatchersTest.cpp index bd7a5a6..d2e9ee1 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.cpp +++ b/unittests/ASTMatchers/ASTMatchersTest.cpp @@ -375,6 +375,13 @@ TEST(DeclarationMatcher, hasDeclContext) { "}", recordDecl(hasDeclContext(namespaceDecl( hasName("M"), hasDeclContext(namespaceDecl())))))); + + EXPECT_TRUE(matches("class D{};", decl(hasDeclContext(decl())))); +} + +TEST(DeclarationMatcher, LinkageSpecification) { + EXPECT_TRUE(matches("extern \"C\" { void foo() {}; }", linkageSpecDecl())); + EXPECT_TRUE(notMatches("void foo() {};", linkageSpecDecl())); } TEST(ClassTemplate, DoesNotMatchClass) { @@ -455,6 +462,11 @@ TEST(DeclarationMatcher, MatchAnyOf) { 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)); } TEST(DeclarationMatcher, MatchHas) { @@ -581,6 +593,11 @@ TEST(DeclarationMatcher, MatchNot) { 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(DeclarationMatcher, HasDescendant) { @@ -643,6 +660,45 @@ TEST(DeclarationMatcher, HasDescendant) { "};", 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, 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, MatchCudaDecl) { + EXPECT_TRUE(matchesWithCuda("__global__ void f() { }" + "void g() { f<<<1, 2>>>(); }", + CUDAKernelCallExpr())); + EXPECT_TRUE(matchesWithCuda("__attribute__((device)) void f() {}", + hasAttr(clang::attr::CUDADevice))); + EXPECT_TRUE(notMatchesWithCuda("void f() {}", + CUDAKernelCallExpr())); + EXPECT_FALSE(notMatchesWithCuda("__attribute__((global)) void f() {}", + hasAttr(clang::attr::CUDAGlobal))); +} + // Implements a run method that returns whether BoundNodes contains a // Decl bound to Id that can be dynamically cast to T. // Optionally checks that the check succeeded a specific number of times. @@ -681,7 +737,7 @@ public: EXPECT_EQ("", Name); } - virtual bool run(const BoundNodes *Nodes) { + virtual bool run(const BoundNodes *Nodes) override { const BoundNodes::IDToNodeMap &M = Nodes->getMap(); if (Nodes->getNodeAs<T>(Id)) { ++Count; @@ -703,7 +759,7 @@ public: return false; } - virtual bool run(const BoundNodes *Nodes, ASTContext *Context) { + virtual bool run(const BoundNodes *Nodes, ASTContext *Context) override { return run(Nodes); } @@ -771,6 +827,13 @@ TEST(Has, MatchesChildTypes) { varDecl(hasName("i"), hasType(qualType(has(pointerType())))))); } +TEST(ValueDecl, Matches) { + EXPECT_TRUE(matches("enum EnumType { EnumValue };", + valueDecl(hasType(asString("enum EnumType"))))); + EXPECT_TRUE(matches("void FunctionDecl();", + valueDecl(hasType(asString("void (void)"))))); +} + TEST(Enum, DoesNotMatchClasses) { EXPECT_TRUE(notMatches("class X {};", enumDecl(hasName("X")))); } @@ -1504,6 +1567,13 @@ TEST(IsExternC, MatchesExternCFunctionDeclarations) { EXPECT_TRUE(notMatches("void f() {}", functionDecl(isExternC()))); } +TEST(IsDeleted, MatchesDeletedFunctionDeclarations) { + EXPECT_TRUE( + notMatches("void Func();", functionDecl(hasName("Func"), isDeleted()))); + EXPECT_TRUE(matches("void Func() = delete;", + functionDecl(hasName("Func"), isDeleted()))); +} + TEST(HasAnyParameter, DoesntMatchIfInnerMatcherDoesntMatch) { EXPECT_TRUE(notMatches("class Y {}; class X { void x(int) {} };", methodDecl(hasAnyParameter(hasType(recordDecl(hasName("X"))))))); @@ -1602,6 +1672,64 @@ TEST(Matcher, MatchesSpecificArgument) { 1, refersToType(asString("int")))))); } +TEST(TemplateArgument, Matches) { + EXPECT_TRUE(matches("template<typename T> struct C {}; C<int> c;", + classTemplateSpecializationDecl( + hasAnyTemplateArgument(templateArgument())))); + EXPECT_TRUE(matches( + "template<typename T> struct C {}; C<int> c;", + templateSpecializationType(hasAnyTemplateArgument(templateArgument())))); +} + +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(RefersToIntegralType, Matches) { + EXPECT_TRUE(matches("template<int T> struct C {}; C<42> c;", + classTemplateSpecializationDecl( + hasAnyTemplateArgument(refersToIntegralType( + asString("int")))))); + EXPECT_TRUE(notMatches("template<unsigned T> struct C {}; C<42> c;", + classTemplateSpecializationDecl(hasAnyTemplateArgument( + refersToIntegralType(asString("int")))))); +} + +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( @@ -3472,6 +3600,62 @@ TEST(IsTemplateInstantiation, DoesNotMatchNonTemplate) { recordDecl(isTemplateInstantiation()))); } +TEST(IsInstantiated, MatchesInstantiation) { + EXPECT_TRUE( + matches("template<typename T> class A { T i; }; class Y { A<int> a; };", + recordDecl(isInstantiated()))); +} + +TEST(IsInstantiated, NotMatchesDefinition) { + EXPECT_TRUE(notMatches("template<typename T> class A { T i; };", + recordDecl(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( @@ -3673,6 +3857,11 @@ TEST(TypeMatching, MatchesTypes) { EXPECT_TRUE(matches("struct S {};", qualType().bind("loc"))); } +TEST(TypeMatching, MatchesVoid) { + EXPECT_TRUE( + matches("struct S { void func(); };", methodDecl(returns(voidType())))); +} + TEST(TypeMatching, MatchesArrayTypes) { EXPECT_TRUE(matches("int a[] = {2,3};", arrayType())); EXPECT_TRUE(matches("int a[42];", arrayType())); @@ -4163,8 +4352,8 @@ 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)) !=nullptr; + return selectFirst<T>(InnerId, match(InnerMatcher, *Node, *Context)) != + nullptr; } private: std::string Id; @@ -4221,7 +4410,7 @@ public: // 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>( + return selectFirst<T>( "", match(stmt(hasParent( stmt(has(stmt(equalsNode(TypedNode)))).bind(""))), *Node, Context)) != nullptr; @@ -4230,7 +4419,7 @@ public: // 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>( + return selectFirst<T>( "", match(decl(hasParent( decl(has(decl(equalsNode(TypedNode)))).bind(""))), *Node, Context)) != nullptr; @@ -4246,6 +4435,25 @@ TEST(IsEqualTo, MatchesNodesByIdentity) { new VerifyAncestorHasChildIsEqual<IfStmt>())); } +TEST(MatchFinder, CheckProfiling) { + MatchFinder::MatchFinderOptions Options; + llvm::StringMap<llvm::TimeRecord> Records; + Options.CheckProfiling.emplace(Records); + MatchFinder Finder(std::move(Options)); + + struct NamedCallback : public MatchFinder::MatchCallback { + void run(const MatchFinder::MatchResult &Result) override {} + StringRef getID() const override { return "MyID"; } + } Callback; + Finder.addMatcher(decl(), &Callback); + std::unique_ptr<FrontendActionFactory> Factory( + newFrontendActionFactory(&Finder)); + ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); + + EXPECT_EQ(1u, Records.size()); + EXPECT_EQ("MyID", Records.begin()->getKey()); +} + class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { public: VerifyStartOfTranslationUnit() : Called(false) {} @@ -4422,5 +4630,58 @@ TEST(EqualsBoundNodeMatcher, UnlessDescendantsOfAncestorsMatch) { .bind("data"))); } +TEST(TypeDefDeclMatcher, Match) { + EXPECT_TRUE(matches("typedef int typedefDeclTest;", + typedefDecl(hasName("typedefDeclTest")))); +} + +// FIXME: Figure out how to specify paths so the following tests pass on Windows. +#ifndef LLVM_ON_WIN32 + +TEST(Matcher, IsExpansionInMainFileMatcher) { + EXPECT_TRUE(matches("class X {};", + recordDecl(hasName("X"), isExpansionInMainFile()))); + EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile()))); + FileContentMappings M; + M.push_back(std::make_pair("/other", "class X {};")); + EXPECT_TRUE(matchesConditionally("#include <other>\n", + recordDecl(isExpansionInMainFile()), false, + "-isystem/", M)); +} + +TEST(Matcher, IsExpansionInSystemHeader) { + FileContentMappings M; + M.push_back(std::make_pair("/other", "class X {};")); + EXPECT_TRUE(matchesConditionally( + "#include \"other\"\n", recordDecl(isExpansionInSystemHeader()), true, + "-isystem/", M)); + EXPECT_TRUE(matchesConditionally("#include \"other\"\n", + recordDecl(isExpansionInSystemHeader()), + false, "-I/", M)); + EXPECT_TRUE(notMatches("class X {};", + recordDecl(isExpansionInSystemHeader()))); + EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader()))); +} + +TEST(Matcher, IsExpansionInFileMatching) { + FileContentMappings M; + M.push_back(std::make_pair("/foo", "class A {};")); + M.push_back(std::make_pair("/bar", "class B {};")); + EXPECT_TRUE(matchesConditionally( + "#include <foo>\n" + "#include <bar>\n" + "class X {};", + recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true, + "-isystem/", M)); + EXPECT_TRUE(matchesConditionally( + "#include <foo>\n" + "#include <bar>\n" + "class X {};", + recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false, + "-isystem/", M)); +} + +#endif // LLVM_ON_WIN32 + } // end namespace ast_matchers } // end namespace clang diff --git a/unittests/ASTMatchers/ASTMatchersTest.h b/unittests/ASTMatchers/ASTMatchersTest.h index 2e4ee2c..a2ab9fe 100644 --- a/unittests/ASTMatchers/ASTMatchersTest.h +++ b/unittests/ASTMatchers/ASTMatchersTest.h @@ -7,8 +7,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H -#define LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H +#ifndef LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H +#define LLVM_CLANG_UNITTESTS_ASTMATCHERS_ASTMATCHERSTEST_H #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Frontend/ASTUnit.h" @@ -22,6 +22,7 @@ using clang::tooling::buildASTFromCodeWithArgs; using clang::tooling::newFrontendActionFactory; using clang::tooling::runToolOnCodeWithArgs; using clang::tooling::FrontendActionFactory; +using clang::tooling::FileContentMappings; class BoundNodesCallback { public: @@ -39,7 +40,7 @@ public: VerifyMatch(BoundNodesCallback *FindResultVerifier, bool *Verified) : Verified(Verified), FindResultReviewer(FindResultVerifier) {} - virtual void run(const MatchFinder::MatchResult &Result) { + virtual void run(const MatchFinder::MatchResult &Result) override { if (FindResultReviewer != nullptr) { *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context); } else { @@ -58,10 +59,10 @@ private: }; template <typename T> -testing::AssertionResult matchesConditionally(const std::string &Code, - const T &AMatcher, - bool ExpectMatch, - llvm::StringRef CompileArg) { +testing::AssertionResult matchesConditionally( + const std::string &Code, const T &AMatcher, bool ExpectMatch, + llvm::StringRef CompileArg, + const FileContentMappings &VirtualMappedFiles = FileContentMappings()) { bool Found = false, DynamicFound = false; MatchFinder Finder; VerifyMatch VerifyFound(nullptr, &Found); @@ -73,7 +74,8 @@ testing::AssertionResult matchesConditionally(const std::string &Code, newFrontendActionFactory(&Finder)); // Some tests use typeof, which is a gnu extension. std::vector<std::string> Args(1, CompileArg); - if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) { + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, "input.cc", + VirtualMappedFiles)) { return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; } if (Found != DynamicFound) { @@ -103,6 +105,75 @@ testing::AssertionResult notMatches(const std::string &Code, return matchesConditionally(Code, AMatcher, false, "-std=c++11"); } +// Function based on matchesConditionally with "-x cuda" argument added and +// small CUDA header prepended to the code string. +template <typename T> +testing::AssertionResult matchesConditionallyWithCuda( + const std::string &Code, const T &AMatcher, bool ExpectMatch, + llvm::StringRef CompileArg) { + const std::string CudaHeader = + "typedef unsigned int size_t;\n" + "#define __constant__ __attribute__((constant))\n" + "#define __device__ __attribute__((device))\n" + "#define __global__ __attribute__((global))\n" + "#define __host__ __attribute__((host))\n" + "#define __shared__ __attribute__((shared))\n" + "struct dim3 {" + " unsigned x, y, z;" + " __host__ __device__ dim3(unsigned x, unsigned y = 1, unsigned z = 1)" + " : x(x), y(y), z(z) {}" + "};" + "typedef struct cudaStream *cudaStream_t;" + "int cudaConfigureCall(dim3 gridSize, dim3 blockSize," + " size_t sharedSize = 0," + " cudaStream_t stream = 0);"; + + bool Found = false, DynamicFound = false; + MatchFinder Finder; + VerifyMatch VerifyFound(nullptr, &Found); + Finder.addMatcher(AMatcher, &VerifyFound); + VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound); + if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound)) + return testing::AssertionFailure() << "Could not add dynamic matcher"; + std::unique_ptr<FrontendActionFactory> Factory( + newFrontendActionFactory(&Finder)); + // Some tests use typeof, which is a gnu extension. + std::vector<std::string> Args; + Args.push_back("-xcuda"); + Args.push_back("-fno-ms-extensions"); + Args.push_back(CompileArg); + if (!runToolOnCodeWithArgs(Factory->create(), + CudaHeader + Code, Args)) { + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + } + if (Found != DynamicFound) { + return testing::AssertionFailure() << "Dynamic match result (" + << DynamicFound + << ") does not match static result (" + << Found << ")"; + } + if (!Found && ExpectMatch) { + return testing::AssertionFailure() + << "Could not find match in \"" << Code << "\""; + } else if (Found && !ExpectMatch) { + return testing::AssertionFailure() + << "Found unexpected match in \"" << Code << "\""; + } + return testing::AssertionSuccess(); +} + +template <typename T> +testing::AssertionResult matchesWithCuda(const std::string &Code, + const T &AMatcher) { + return matchesConditionallyWithCuda(Code, AMatcher, true, "-std=c++11"); +} + +template <typename T> +testing::AssertionResult notMatchesWithCuda(const std::string &Code, + const T &AMatcher) { + return matchesConditionallyWithCuda(Code, AMatcher, false, "-std=c++11"); +} + template <typename T> testing::AssertionResult matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher, diff --git a/unittests/ASTMatchers/Dynamic/ParserTest.cpp b/unittests/ASTMatchers/Dynamic/ParserTest.cpp index 4e3239f..2a9a61b 100644 --- a/unittests/ASTMatchers/Dynamic/ParserTest.cpp +++ b/unittests/ASTMatchers/Dynamic/ParserTest.cpp @@ -26,9 +26,12 @@ public: virtual ~MockSema() {} uint64_t expectMatcher(StringRef MatcherName) { - ast_matchers::internal::Matcher<Stmt> M = stmt(); + // Optimizations on the matcher framework make simple matchers like + // 'stmt()' to be all the same matcher. + // Use a more complex expression to prevent that. + ast_matchers::internal::Matcher<Stmt> M = stmt(stmt(), stmt()); ExpectedMatchers.insert(std::make_pair(MatcherName, M)); - return M.getID(); + return M.getID().second; } void parse(StringRef Code) { @@ -125,8 +128,12 @@ TEST(ParserTest, ParseMatcher) { EXPECT_EQ("", Sema.Errors[i]); } + EXPECT_NE(ExpectedFoo, ExpectedBar); + EXPECT_NE(ExpectedFoo, ExpectedBaz); + EXPECT_NE(ExpectedBar, ExpectedBaz); + EXPECT_EQ(1ULL, Sema.Values.size()); - EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID()); + EXPECT_EQ(ExpectedFoo, getSingleMatcher(Sema.Values[0])->getID().second); EXPECT_EQ(3ULL, Sema.Matchers.size()); const MockSema::MatcherInfo Bar = Sema.Matchers[0]; @@ -145,13 +152,21 @@ TEST(ParserTest, ParseMatcher) { EXPECT_EQ("Foo", Foo.MatcherName); EXPECT_TRUE(matchesRange(Foo.NameRange, 1, 2, 2, 12)); EXPECT_EQ(2ULL, Foo.Args.size()); - EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID()); - EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID()); + EXPECT_EQ(ExpectedBar, getSingleMatcher(Foo.Args[0].Value)->getID().second); + EXPECT_EQ(ExpectedBaz, getSingleMatcher(Foo.Args[1].Value)->getID().second); EXPECT_EQ("Yo!", Foo.BoundID); } using ast_matchers::internal::Matcher; +Parser::NamedValueMap getTestNamedValues() { + Parser::NamedValueMap Values; + Values["nameX"] = std::string("x"); + Values["hasParamA"] = + VariantMatcher::SingleMatcher(hasParameter(0, hasName("a"))); + return Values; +} + TEST(ParserTest, FullParserTest) { Diagnostics Error; llvm::Optional<DynTypedMatcher> VarDecl(Parser::parseMatcherExpression( @@ -174,21 +189,11 @@ TEST(ParserTest, FullParserTest) { EXPECT_FALSE(matches("void f(int x, int a);", M)); // Test named values. - struct NamedSema : public Parser::RegistrySema { - public: - virtual VariantValue getNamedValue(StringRef Name) { - if (Name == "nameX") - return std::string("x"); - if (Name == "param0") - return VariantMatcher::SingleMatcher(hasParameter(0, hasName("a"))); - return VariantValue(); - } - }; - NamedSema Sema; + auto NamedValues = getTestNamedValues(); llvm::Optional<DynTypedMatcher> HasParameterWithNamedValues( Parser::parseMatcherExpression( - "functionDecl(param0, hasParameter(1, hasName(nameX)))", &Sema, - &Error)); + "functionDecl(hasParamA, hasParameter(1, hasName(nameX)))", + nullptr, &NamedValues, &Error)); EXPECT_EQ("", Error.toStringFull()); M = HasParameterWithNamedValues->unconditionalConvertTo<Decl>(); @@ -270,7 +275,7 @@ TEST(ParserTest, OverloadErrors) { ParseWithError("callee(\"A\")")); } -TEST(ParserTest, Completion) { +TEST(ParserTest, CompletionRegistry) { std::vector<MatcherCompletion> Comps = Parser::completeExpression("while", 5); ASSERT_EQ(1u, Comps.size()); @@ -284,6 +289,38 @@ TEST(ParserTest, Completion) { EXPECT_EQ("bind", Comps[0].MatcherDecl); } +TEST(ParserTest, CompletionNamedValues) { + // Can complete non-matcher types. + auto NamedValues = getTestNamedValues(); + StringRef Code = "functionDecl(hasName("; + std::vector<MatcherCompletion> Comps = + Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues); + ASSERT_EQ(1u, Comps.size()); + EXPECT_EQ("nameX", Comps[0].TypedText); + EXPECT_EQ("String nameX", Comps[0].MatcherDecl); + + // Can complete if there are names in the expression. + Code = "methodDecl(hasName(nameX), "; + Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues); + EXPECT_LT(0u, Comps.size()); + + // Can complete names and registry together. + Code = "methodDecl(hasP"; + Comps = Parser::completeExpression(Code, Code.size(), nullptr, &NamedValues); + ASSERT_EQ(3u, Comps.size()); + EXPECT_EQ("aramA", Comps[0].TypedText); + EXPECT_EQ("Matcher<FunctionDecl> hasParamA", Comps[0].MatcherDecl); + + EXPECT_EQ("arameter(", Comps[1].TypedText); + EXPECT_EQ( + "Matcher<FunctionDecl> hasParameter(unsigned, Matcher<ParmVarDecl>)", + Comps[1].MatcherDecl); + + EXPECT_EQ("arent(", Comps[2].TypedText); + EXPECT_EQ("Matcher<Decl> hasParent(Matcher<Decl|Stmt>)", + Comps[2].MatcherDecl); +} + } // end anonymous namespace } // end namespace dynamic } // end namespace ast_matchers diff --git a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp index e659b3a..5483f8f 100644 --- a/unittests/ASTMatchers/Dynamic/RegistryTest.cpp +++ b/unittests/ASTMatchers/Dynamic/RegistryTest.cpp @@ -82,8 +82,9 @@ public: typedef std::vector<MatcherCompletion> CompVector; CompVector getCompletions() { - return Registry::getCompletions( - ArrayRef<std::pair<MatcherCtor, unsigned> >()); + std::vector<std::pair<MatcherCtor, unsigned> > Context; + return Registry::getMatcherCompletions( + Registry::getAcceptedCompletionTypes(Context)); } CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) { @@ -92,7 +93,8 @@ public: if (!Ctor) return CompVector(); Context.push_back(std::make_pair(*Ctor, ArgNo1)); - return Registry::getCompletions(Context); + return Registry::getMatcherCompletions( + Registry::getAcceptedCompletionTypes(Context)); } CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1, @@ -106,18 +108,16 @@ public: if (!Ctor) return CompVector(); Context.push_back(std::make_pair(*Ctor, ArgNo2)); - return Registry::getCompletions(Context); + return Registry::getMatcherCompletions( + Registry::getAcceptedCompletionTypes(Context)); } bool hasCompletion(const CompVector &Comps, StringRef TypedText, - StringRef MatcherDecl = StringRef(), - unsigned *Index = nullptr) { + StringRef MatcherDecl = StringRef()) { for (CompVector::const_iterator I = Comps.begin(), E = Comps.end(); I != E; ++I) { if (I->TypedText == TypedText && (MatcherDecl.empty() || I->MatcherDecl == MatcherDecl)) { - if (Index) - *Index = I - Comps.begin(); return true; } } @@ -347,7 +347,7 @@ TEST_F(RegistryTest, VariadicOp) { "anyOf", constructMatcher("recordDecl", constructMatcher("hasName", std::string("Foo"))), - constructMatcher("namedDecl", + constructMatcher("functionDecl", constructMatcher("hasName", std::string("foo")))) .getTypedMatcher<Decl>(); @@ -380,6 +380,13 @@ TEST_F(RegistryTest, VariadicOp) { EXPECT_FALSE(matches("class Bar{ int Foo; };", D)); EXPECT_TRUE(matches("class OtherBar{ int Foo; };", D)); + + D = constructMatcher( + "namedDecl", constructMatcher("hasName", std::string("Foo")), + constructMatcher("unless", constructMatcher("recordDecl"))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("void Foo(){}", D)); + EXPECT_TRUE(notMatches("struct Foo {};", D)); } TEST_F(RegistryTest, Errors) { @@ -438,24 +445,27 @@ TEST_F(RegistryTest, Errors) { TEST_F(RegistryTest, Completion) { CompVector Comps = getCompletions(); + // Overloaded EXPECT_TRUE(hasCompletion( Comps, "hasParent(", "Matcher<Decl|Stmt> hasParent(Matcher<Decl|Stmt>)")); + // Variadic. EXPECT_TRUE(hasCompletion(Comps, "whileStmt(", "Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)")); + // Polymorphic. + EXPECT_TRUE(hasCompletion( + Comps, "hasDescendant(", + "Matcher<NestedNameSpecifier|NestedNameSpecifierLoc|QualType|...> " + "hasDescendant(Matcher<CXXCtorInitializer|NestedNameSpecifier|" + "NestedNameSpecifierLoc|...>)")); CompVector WhileComps = getCompletions("whileStmt", 0); - unsigned HasBodyIndex, HasParentIndex, AllOfIndex; EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(", - "Matcher<WhileStmt> hasBody(Matcher<Stmt>)", - &HasBodyIndex)); + "Matcher<WhileStmt> hasBody(Matcher<Stmt>)")); EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(", - "Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)", - &HasParentIndex)); - EXPECT_TRUE(hasCompletion(WhileComps, "allOf(", - "Matcher<T> allOf(Matcher<T>...)", &AllOfIndex)); - EXPECT_GT(HasParentIndex, HasBodyIndex); - EXPECT_GT(AllOfIndex, HasParentIndex); + "Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)")); + EXPECT_TRUE( + hasCompletion(WhileComps, "allOf(", "Matcher<T> allOf(Matcher<T>...)")); EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt(")); EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt(")); @@ -475,6 +485,20 @@ TEST_F(RegistryTest, Completion) { hasCompletion(NamedDeclComps, "isPublic()", "Matcher<Decl> isPublic()")); EXPECT_TRUE(hasCompletion(NamedDeclComps, "hasName(\"", "Matcher<NamedDecl> hasName(string)")); + + // Heterogeneous overloads. + Comps = getCompletions("classTemplateSpecializationDecl", 0); + EXPECT_TRUE(hasCompletion( + Comps, "isSameOrDerivedFrom(", + "Matcher<CXXRecordDecl> isSameOrDerivedFrom(string|Matcher<NamedDecl>)")); +} + +TEST_F(RegistryTest, HasArgs) { + Matcher<Decl> Value = constructMatcher( + "decl", constructMatcher("hasAttr", std::string("attr::WarnUnused"))) + .getTypedMatcher<Decl>(); + EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};", Value)); + EXPECT_FALSE(matches("struct X {};", Value)); } } // end anonymous namespace |