//===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry 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/ASTMatchers/Dynamic/Registry.h" #include "gtest/gtest.h" #include namespace clang { namespace ast_matchers { namespace dynamic { namespace { using ast_matchers::internal::Matcher; class RegistryTest : public ::testing::Test { public: std::vector Args() { return std::vector(); } std::vector Args(const VariantValue &Arg1) { std::vector Out(1); Out[0].Value = Arg1; return Out; } std::vector Args(const VariantValue &Arg1, const VariantValue &Arg2) { std::vector Out(2); Out[0].Value = Arg1; Out[1].Value = Arg2; return Out; } llvm::Optional lookupMatcherCtor(StringRef MatcherName) { return Registry::lookupMatcherCtor(MatcherName); } VariantMatcher constructMatcher(StringRef MatcherName, Diagnostics *Error = nullptr) { Diagnostics DummyError; if (!Error) Error = &DummyError; llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(), Error); EXPECT_EQ("", DummyError.toStringFull()); return Out; } VariantMatcher constructMatcher(StringRef MatcherName, const VariantValue &Arg1, Diagnostics *Error = nullptr) { Diagnostics DummyError; if (!Error) Error = &DummyError; llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1), Error); EXPECT_EQ("", DummyError.toStringFull()) << MatcherName; return Out; } VariantMatcher constructMatcher(StringRef MatcherName, const VariantValue &Arg1, const VariantValue &Arg2, Diagnostics *Error = nullptr) { Diagnostics DummyError; if (!Error) Error = &DummyError; llvm::Optional Ctor = lookupMatcherCtor(MatcherName); VariantMatcher Out; if (Ctor) Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1, Arg2), Error); EXPECT_EQ("", DummyError.toStringFull()); return Out; } typedef std::vector CompVector; CompVector getCompletions() { return Registry::getCompletions( llvm::ArrayRef >()); } CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) { std::vector > Context; llvm::Optional Ctor = lookupMatcherCtor(MatcherName1); if (!Ctor) return CompVector(); Context.push_back(std::make_pair(*Ctor, ArgNo1)); return Registry::getCompletions(Context); } CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1, StringRef MatcherName2, unsigned ArgNo2) { std::vector > Context; llvm::Optional Ctor = lookupMatcherCtor(MatcherName1); if (!Ctor) return CompVector(); Context.push_back(std::make_pair(*Ctor, ArgNo1)); Ctor = lookupMatcherCtor(MatcherName2); if (!Ctor) return CompVector(); Context.push_back(std::make_pair(*Ctor, ArgNo2)); return Registry::getCompletions(Context); } bool hasCompletion(const CompVector &Comps, StringRef TypedText, StringRef MatcherDecl = StringRef(), unsigned *Index = nullptr) { 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; } } return false; } }; TEST_F(RegistryTest, CanConstructNoArgs) { Matcher IsArrowValue = constructMatcher( "memberExpr", constructMatcher("isArrow")).getTypedMatcher(); Matcher BoolValue = constructMatcher("boolLiteral").getTypedMatcher(); const std::string ClassSnippet = "struct Foo { int x; };\n" "Foo *foo = new Foo;\n" "int i = foo->x;\n"; const std::string BoolSnippet = "bool Foo = true;\n"; EXPECT_TRUE(matches(ClassSnippet, IsArrowValue)); EXPECT_TRUE(matches(BoolSnippet, BoolValue)); EXPECT_FALSE(matches(ClassSnippet, BoolValue)); EXPECT_FALSE(matches(BoolSnippet, IsArrowValue)); } TEST_F(RegistryTest, ConstructWithSimpleArgs) { Matcher Value = constructMatcher( "namedDecl", constructMatcher("hasName", std::string("X"))) .getTypedMatcher(); EXPECT_TRUE(matches("class X {};", Value)); EXPECT_FALSE(matches("int x;", Value)); Value = functionDecl(constructMatcher("parameterCountIs", 2) .getTypedMatcher()); EXPECT_TRUE(matches("void foo(int,int);", Value)); EXPECT_FALSE(matches("void foo(int);", Value)); } TEST_F(RegistryTest, ConstructWithMatcherArgs) { Matcher HasInitializerSimple = constructMatcher( "varDecl", constructMatcher("hasInitializer", constructMatcher("stmt"))) .getTypedMatcher(); Matcher HasInitializerComplex = constructMatcher( "varDecl", constructMatcher("hasInitializer", constructMatcher("callExpr"))) .getTypedMatcher(); std::string code = "int i;"; EXPECT_FALSE(matches(code, HasInitializerSimple)); EXPECT_FALSE(matches(code, HasInitializerComplex)); code = "int i = 1;"; EXPECT_TRUE(matches(code, HasInitializerSimple)); EXPECT_FALSE(matches(code, HasInitializerComplex)); code = "int y(); int i = y();"; EXPECT_TRUE(matches(code, HasInitializerSimple)); EXPECT_TRUE(matches(code, HasInitializerComplex)); Matcher HasParameter = functionDecl(constructMatcher( "hasParameter", 1, constructMatcher("hasName", std::string("x"))) .getTypedMatcher()); EXPECT_TRUE(matches("void f(int a, int x);", HasParameter)); EXPECT_FALSE(matches("void f(int x, int a);", HasParameter)); } TEST_F(RegistryTest, OverloadedMatchers) { Matcher CallExpr0 = constructMatcher( "callExpr", constructMatcher("callee", constructMatcher("memberExpr", constructMatcher("isArrow")))) .getTypedMatcher(); Matcher CallExpr1 = constructMatcher( "callExpr", constructMatcher( "callee", constructMatcher("methodDecl", constructMatcher("hasName", std::string("x"))))) .getTypedMatcher(); std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }"; EXPECT_FALSE(matches(Code, CallExpr0)); EXPECT_TRUE(matches(Code, CallExpr1)); Code = "class Z { public: void z() { this->z(); } };"; EXPECT_TRUE(matches(Code, CallExpr0)); EXPECT_FALSE(matches(Code, CallExpr1)); Matcher DeclDecl = declaratorDecl(hasTypeLoc( constructMatcher( "loc", constructMatcher("asString", std::string("const double *"))) .getTypedMatcher())); Matcher NNSL = constructMatcher( "loc", VariantMatcher::SingleMatcher(nestedNameSpecifier( specifiesType(hasDeclaration(recordDecl(hasName("A"))))))) .getTypedMatcher(); Code = "const double * x = 0;"; EXPECT_TRUE(matches(Code, DeclDecl)); EXPECT_FALSE(matches(Code, NNSL)); Code = "struct A { struct B {}; }; A::B a_b;"; EXPECT_FALSE(matches(Code, DeclDecl)); EXPECT_TRUE(matches(Code, NNSL)); } TEST_F(RegistryTest, PolymorphicMatchers) { const VariantMatcher IsDefinition = constructMatcher("isDefinition"); Matcher Var = constructMatcher("varDecl", IsDefinition).getTypedMatcher(); Matcher Class = constructMatcher("recordDecl", IsDefinition).getTypedMatcher(); Matcher Func = constructMatcher("functionDecl", IsDefinition).getTypedMatcher(); EXPECT_TRUE(matches("int a;", Var)); EXPECT_FALSE(matches("extern int a;", Var)); EXPECT_TRUE(matches("class A {};", Class)); EXPECT_FALSE(matches("class A;", Class)); EXPECT_TRUE(matches("void f(){};", Func)); EXPECT_FALSE(matches("void f();", Func)); Matcher Anything = constructMatcher("anything").getTypedMatcher(); Matcher RecordDecl = constructMatcher( "recordDecl", constructMatcher("hasName", std::string("Foo")), VariantMatcher::SingleMatcher(Anything)).getTypedMatcher(); EXPECT_TRUE(matches("int Foo;", Anything)); EXPECT_TRUE(matches("class Foo {};", Anything)); EXPECT_TRUE(matches("void Foo(){};", Anything)); EXPECT_FALSE(matches("int Foo;", RecordDecl)); EXPECT_TRUE(matches("class Foo {};", RecordDecl)); EXPECT_FALSE(matches("void Foo(){};", RecordDecl)); Matcher ConstructExpr = constructMatcher( "constructExpr", constructMatcher( "hasDeclaration", constructMatcher( "methodDecl", constructMatcher( "ofClass", constructMatcher("hasName", std::string("Foo")))))) .getTypedMatcher(); EXPECT_FALSE(matches("class Foo { public: Foo(); };", ConstructExpr)); EXPECT_TRUE( matches("class Foo { public: Foo(); }; Foo foo = Foo();", ConstructExpr)); } TEST_F(RegistryTest, TemplateArgument) { Matcher HasTemplateArgument = constructMatcher( "classTemplateSpecializationDecl", constructMatcher( "hasAnyTemplateArgument", constructMatcher("refersToType", constructMatcher("asString", std::string("int"))))) .getTypedMatcher(); EXPECT_TRUE(matches("template class A {}; A a;", HasTemplateArgument)); EXPECT_FALSE(matches("template class A {}; A a;", HasTemplateArgument)); } TEST_F(RegistryTest, TypeTraversal) { Matcher M = constructMatcher( "pointerType", constructMatcher("pointee", constructMatcher("isConstQualified"), constructMatcher("isInteger"))).getTypedMatcher(); EXPECT_FALSE(matches("int *a;", M)); EXPECT_TRUE(matches("int const *b;", M)); M = constructMatcher( "arrayType", constructMatcher("hasElementType", constructMatcher("builtinType"))) .getTypedMatcher(); EXPECT_FALSE(matches("struct A{}; A a[7];;", M)); EXPECT_TRUE(matches("int b[7];", M)); } TEST_F(RegistryTest, CXXCtorInitializer) { Matcher CtorDecl = constructMatcher( "constructorDecl", constructMatcher( "hasAnyConstructorInitializer", constructMatcher("forField", constructMatcher("hasName", std::string("foo"))))) .getTypedMatcher(); EXPECT_TRUE(matches("struct Foo { Foo() : foo(1) {} int foo; };", CtorDecl)); EXPECT_FALSE(matches("struct Foo { Foo() {} int foo; };", CtorDecl)); EXPECT_FALSE(matches("struct Foo { Foo() : bar(1) {} int bar; };", CtorDecl)); } TEST_F(RegistryTest, Adaptative) { Matcher D = constructMatcher( "recordDecl", constructMatcher( "has", constructMatcher("recordDecl", constructMatcher("hasName", std::string("X"))))) .getTypedMatcher(); EXPECT_TRUE(matches("class X {};", D)); EXPECT_TRUE(matches("class Y { class X {}; };", D)); EXPECT_FALSE(matches("class Y { class Z {}; };", D)); Matcher S = constructMatcher( "forStmt", constructMatcher( "hasDescendant", constructMatcher("varDecl", constructMatcher("hasName", std::string("X"))))) .getTypedMatcher(); EXPECT_TRUE(matches("void foo() { for(int X;;); }", S)); EXPECT_TRUE(matches("void foo() { for(;;) { int X; } }", S)); EXPECT_FALSE(matches("void foo() { for(;;); }", S)); EXPECT_FALSE(matches("void foo() { if (int X = 0){} }", S)); S = constructMatcher( "compoundStmt", constructMatcher("hasParent", constructMatcher("ifStmt"))) .getTypedMatcher(); EXPECT_TRUE(matches("void foo() { if (true) { int x = 42; } }", S)); EXPECT_FALSE(matches("void foo() { if (true) return; }", S)); } TEST_F(RegistryTest, VariadicOp) { Matcher D = constructMatcher( "anyOf", constructMatcher("recordDecl", constructMatcher("hasName", std::string("Foo"))), constructMatcher("namedDecl", constructMatcher("hasName", std::string("foo")))) .getTypedMatcher(); EXPECT_TRUE(matches("void foo(){}", D)); EXPECT_TRUE(matches("struct Foo{};", D)); EXPECT_FALSE(matches("int i = 0;", D)); D = constructMatcher( "allOf", constructMatcher("recordDecl"), constructMatcher( "namedDecl", constructMatcher("anyOf", constructMatcher("hasName", std::string("Foo")), constructMatcher("hasName", std::string("Bar"))))) .getTypedMatcher(); EXPECT_FALSE(matches("void foo(){}", D)); EXPECT_TRUE(matches("struct Foo{};", D)); EXPECT_FALSE(matches("int i = 0;", D)); EXPECT_TRUE(matches("class Bar{};", D)); EXPECT_FALSE(matches("class OtherBar{};", D)); D = recordDecl( has(fieldDecl(hasName("Foo"))), constructMatcher( "unless", constructMatcher("namedDecl", constructMatcher("hasName", std::string("Bar")))) .getTypedMatcher()); EXPECT_FALSE(matches("class Bar{ int Foo; };", D)); EXPECT_TRUE(matches("class OtherBar{ int Foo; };", D)); } TEST_F(RegistryTest, Errors) { // Incorrect argument count. std::unique_ptr Error(new Diagnostics()); EXPECT_TRUE(constructMatcher("hasInitializer", Error.get()).isNull()); EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)", Error->toString()); Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("isArrow", std::string(), Error.get()).isNull()); EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)", Error->toString()); Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("anyOf", Error.get()).isNull()); EXPECT_EQ("Incorrect argument count. (Expected = (2, )) != (Actual = 0)", Error->toString()); Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("unless", std::string(), std::string(), Error.get()).isNull()); EXPECT_EQ("Incorrect argument count. (Expected = (1, 1)) != (Actual = 2)", Error->toString()); // Bad argument type Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("ofClass", std::string(), Error.get()).isNull()); EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher) != " "(Actual = String)", Error->toString()); Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("recordDecl", constructMatcher("recordDecl"), constructMatcher("parameterCountIs", 3), Error.get()).isNull()); EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher) != " "(Actual = Matcher)", Error->toString()); // Bad argument type with variadic. Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher("anyOf", std::string(), std::string(), Error.get()).isNull()); EXPECT_EQ( "Incorrect type for arg 1. (Expected = Matcher<>) != (Actual = String)", Error->toString()); Error.reset(new Diagnostics()); EXPECT_TRUE(constructMatcher( "recordDecl", constructMatcher("allOf", constructMatcher("isDerivedFrom", std::string("FOO")), constructMatcher("isArrow")), Error.get()).isNull()); EXPECT_EQ("Incorrect type for arg 1. " "(Expected = Matcher) != " "(Actual = Matcher&Matcher)", Error->toString()); } TEST_F(RegistryTest, Completion) { CompVector Comps = getCompletions(); EXPECT_TRUE(hasCompletion( Comps, "hasParent(", "Matcher hasParent(Matcher)")); EXPECT_TRUE(hasCompletion(Comps, "whileStmt(", "Matcher whileStmt(Matcher...)")); CompVector WhileComps = getCompletions("whileStmt", 0); unsigned HasBodyIndex, HasParentIndex, AllOfIndex; EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(", "Matcher hasBody(Matcher)", &HasBodyIndex)); EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(", "Matcher hasParent(Matcher)", &HasParentIndex)); EXPECT_TRUE(hasCompletion(WhileComps, "allOf(", "Matcher allOf(Matcher...)", &AllOfIndex)); EXPECT_GT(HasParentIndex, HasBodyIndex); EXPECT_GT(AllOfIndex, HasParentIndex); EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt(")); EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt(")); CompVector AllOfWhileComps = getCompletions("allOf", 0, "whileStmt", 0); ASSERT_EQ(AllOfWhileComps.size(), WhileComps.size()); EXPECT_TRUE(std::equal(WhileComps.begin(), WhileComps.end(), AllOfWhileComps.begin())); CompVector DeclWhileComps = getCompletions("decl", 0, "whileStmt", 0); EXPECT_EQ(0u, DeclWhileComps.size()); CompVector NamedDeclComps = getCompletions("namedDecl", 0); EXPECT_TRUE( hasCompletion(NamedDeclComps, "isPublic()", "Matcher isPublic()")); EXPECT_TRUE(hasCompletion(NamedDeclComps, "hasName(\"", "Matcher hasName(string)")); } } // end anonymous namespace } // end namespace dynamic } // end namespace ast_matchers } // end namespace clang