Index: lib/AST/ASTStructuralEquivalence.cpp =================================================================== --- lib/AST/ASTStructuralEquivalence.cpp +++ lib/AST/ASTStructuralEquivalence.cpp @@ -250,6 +250,9 @@ if (T1.isNull() || T2.isNull()) return T1.isNull() && T2.isNull(); + QualType OrigT1 = T1; + QualType OrigT2 = T2; + if (!Context.StrictTypeSpelling) { // We aren't being strict about token-to-token equivalence of types, // so map down to the canonical type. @@ -422,6 +425,7 @@ case Type::FunctionProto: { const auto *Proto1 = cast(T1); const auto *Proto2 = cast(T2); + if (Proto1->getNumParams() != Proto2->getNumParams()) return false; for (unsigned I = 0, N = Proto1->getNumParams(); I != N; ++I) { @@ -431,23 +435,33 @@ } if (Proto1->isVariadic() != Proto2->isVariadic()) return false; - if (Proto1->getExceptionSpecType() != Proto2->getExceptionSpecType()) + + if (Proto1->getTypeQuals() != Proto2->getTypeQuals()) return false; - if (Proto1->getExceptionSpecType() == EST_Dynamic) { - if (Proto1->getNumExceptions() != Proto2->getNumExceptions()) + + // Check exceptions, this information is lost in canonical type. + const auto *OrigProto1 = + cast(OrigT1.getDesugaredType(Context.FromCtx)); + const auto *OrigProto2 = + cast(OrigT2.getDesugaredType(Context.ToCtx)); + auto Spec1 = OrigProto1->getExceptionSpecType(); + auto Spec2 = OrigProto2->getExceptionSpecType(); + + if (Spec1 != Spec2) + return false; + if (Spec1 == EST_Dynamic) { + if (OrigProto1->getNumExceptions() != OrigProto2->getNumExceptions()) return false; - for (unsigned I = 0, N = Proto1->getNumExceptions(); I != N; ++I) { - if (!IsStructurallyEquivalent(Context, Proto1->getExceptionType(I), - Proto2->getExceptionType(I))) + for (unsigned I = 0, N = OrigProto1->getNumExceptions(); I != N; ++I) { + if (!IsStructurallyEquivalent(Context, OrigProto1->getExceptionType(I), + OrigProto2->getExceptionType(I))) return false; } - } else if (isComputedNoexcept(Proto1->getExceptionSpecType())) { - if (!IsStructurallyEquivalent(Context, Proto1->getNoexceptExpr(), - Proto2->getNoexceptExpr())) + } else if (isComputedNoexcept(Spec1)) { + if (!IsStructurallyEquivalent(Context, OrigProto1->getNoexceptExpr(), + OrigProto2->getNoexceptExpr())) return false; } - if (Proto1->getTypeQuals() != Proto2->getTypeQuals()) - return false; // Fall through to check the bits common with FunctionNoProtoType. LLVM_FALLTHROUGH; @@ -830,6 +844,75 @@ return true; } +/// Determine structural equivalence of two methodss. +static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, + CXXMethodDecl *Method1, + CXXMethodDecl *Method2) { + if (Method1->isStatic() != Method2->isStatic()) + return false; + if (Method1->isConst() != Method2->isConst()) + return false; + if (Method1->isVolatile() != Method2->isVolatile()) + return false; + if (Method1->isVirtual() != Method2->isVirtual()) + return false; + if (Method1->isPure() != Method2->isPure()) + return false; + if (Method1->isDefaulted() != Method2->isDefaulted()) + return false; + if (Method1->isDeleted() != Method2->isDeleted()) + return false; + // FIXME: Check for 'final'. + + if (Method1->getRefQualifier() != Method2->getRefQualifier()) + return false; + + if (Method1->getAccess() != Method2->getAccess()) + return false; + + if (auto *Constructor1 = dyn_cast(Method1)) { + if (auto *Constructor2 = dyn_cast(Method2)) { + if (Constructor1->isExplicit() != Constructor2->isExplicit()) + return false; + } else + return false; + } + + if (auto *Conversion1 = dyn_cast(Method1)) { + if (auto *Conversion2 = dyn_cast(Method2)) { + if (Conversion1->isExplicit() != Conversion2->isExplicit()) + return false; + if (!IsStructurallyEquivalent(Context, Conversion1->getConversionType(), + Conversion2->getConversionType())) + return false; + } else + return false; + } + + if (Method1->isOverloadedOperator() && Method2->isOverloadedOperator()) { + if (Method1->getOverloadedOperator() != Method2->getOverloadedOperator()) + return false; + const IdentifierInfo *Literal1 = Method1->getLiteralIdentifier(); + const IdentifierInfo *Literal2 = Method2->getLiteralIdentifier(); + if (!::IsStructurallyEquivalent(Literal1, Literal2)) + return false; + } + + // Check method names. + const IdentifierInfo *Name1 = Method1->getIdentifier(); + const IdentifierInfo *Name2 = Method2->getIdentifier(); + if (!::IsStructurallyEquivalent(Name1, Name2)) { + return false; + // TODO: add warning + } + + if (!::IsStructurallyEquivalent(Context, + Method1->getType(), Method2->getType())) + return false; + + return true; +} + /// Determine structural equivalence of two records. static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, RecordDecl *D1, RecordDecl *D2) { @@ -1445,6 +1528,14 @@ // Kind mismatch. Equivalent = false; } + } else if (auto *MD1 = dyn_cast(D1)) { + if (auto *MD2 = dyn_cast(D2)) { + if (!::IsStructurallyEquivalent(*this, MD1, MD2)) + Equivalent = false; + } else { + // Kind mismatch. + Equivalent = false; + } } else if (FunctionDecl *FD1 = dyn_cast(D1)) { if (FunctionDecl *FD2 = dyn_cast(D2)) { if (!::IsStructurallyEquivalent(FD1->getIdentifier(), Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -1555,6 +1555,37 @@ } } +TEST_P(ASTImporterTestBase, ImportOfDeclAfterFwdDecl) { + Decl *ToD1; + { + Decl *FromTU = getTuDecl( + R"( + struct A; + )", + Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + ToD1 = Import(FromD, Lang_CXX); + } + + Decl *ToD2; + { + Decl *FromTU = getTuDecl( + R"( + struct A { int x; }; + )", + Lang_CXX, "input1.cc"); + auto *FromD = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + ToD2 = Import(FromD, Lang_CXX); + } + + EXPECT_NE(ToD1, ToD2); + EXPECT_EQ(ToD1, ToD2->getPreviousDecl()); +} + TEST_P(ASTImporterTestBase, ImportDefinitionOfClassTemplateIfThereIsAnExistingFwdDeclAndDefinition) { Decl *ToTU = getToTuDecl( @@ -1995,6 +2026,132 @@ compoundStmt(has(callExpr(has(unresolvedMemberExpr()))))))))); } +TEST_P(ASTImporterTestBase, ImportOfEquivalentRecord) { + Decl *ToR1; + { + Decl *FromTU = getTuDecl( + "struct A { };", Lang_CXX, "input0.cc"); + auto *FromR = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + ToR1 = Import(FromR, Lang_CXX); + } + + Decl *ToR2; + { + Decl *FromTU = getTuDecl( + "struct A { };", Lang_CXX, "input1.cc"); + auto *FromR = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + + ToR2 = Import(FromR, Lang_CXX); + } + + EXPECT_EQ(ToR1, ToR2); +} + +TEST_P(ASTImporterTestBase, ImportOfNonEquivalentRecord) { + Decl *ToR1; + { + Decl *FromTU = getTuDecl( + "struct A { int x; };", Lang_CXX, "input0.cc"); + auto *FromR = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + ToR1 = Import(FromR, Lang_CXX); + } + Decl *ToR2; + { + Decl *FromTU = getTuDecl( + "struct A { unsigned x; };", Lang_CXX, "input1.cc"); + auto *FromR = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("A"))); + ToR2 = Import(FromR, Lang_CXX); + } + EXPECT_NE(ToR1, ToR2); +} + +TEST_P(ASTImporterTestBase, ImportOfEquivalentField) { + Decl *ToF1; + { + Decl *FromTU = getTuDecl( + "struct A { int x; };", Lang_CXX, "input0.cc"); + auto *FromF = FirstDeclMatcher().match( + FromTU, fieldDecl(hasName("x"))); + ToF1 = Import(FromF, Lang_CXX); + } + Decl *ToF2; + { + Decl *FromTU = getTuDecl( + "struct A { int x; };", Lang_CXX, "input1.cc"); + auto *FromF = FirstDeclMatcher().match( + FromTU, fieldDecl(hasName("x"))); + ToF2 = Import(FromF, Lang_CXX); + } + EXPECT_EQ(ToF1, ToF2); +} + +TEST_P(ASTImporterTestBase, ImportOfNonEquivalentField) { + Decl *ToF1; + { + Decl *FromTU = getTuDecl( + "struct A { int x; };", Lang_CXX, "input0.cc"); + auto *FromF = FirstDeclMatcher().match( + FromTU, fieldDecl(hasName("x"))); + ToF1 = Import(FromF, Lang_CXX); + } + Decl *ToF2; + { + Decl *FromTU = getTuDecl( + "struct A { unsigned x; };", Lang_CXX, "input1.cc"); + auto *FromF = FirstDeclMatcher().match( + FromTU, fieldDecl(hasName("x"))); + ToF2 = Import(FromF, Lang_CXX); + } + EXPECT_NE(ToF1, ToF2); +} + +TEST_P(ASTImporterTestBase, ImportOfEquivalentMethod) { + Decl *ToM1; + { + Decl *FromTU = getTuDecl( + "struct A { void x(); }; void A::x() { }", Lang_CXX, "input0.cc"); + auto *FromM = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("x"), isDefinition())); + ToM1 = Import(FromM, Lang_CXX); + } + Decl *ToM2; + { + Decl *FromTU = getTuDecl( + "struct A { void x(); }; void A::x() { }", Lang_CXX, "input1.cc"); + auto *FromM = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("x"), isDefinition())); + ToM2 = Import(FromM, Lang_CXX); + } + EXPECT_EQ(ToM1, ToM2); +} + +TEST_P(ASTImporterTestBase, ImportOfNonEquivalentMethod) { + Decl *ToM1; + { + Decl *FromTU = getTuDecl( + "struct A { void x(); }; void A::x() { }", + Lang_CXX, "input0.cc"); + auto *FromM = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("x"), isDefinition())); + ToM1 = Import(FromM, Lang_CXX); + } + Decl *ToM2; + { + Decl *FromTU = getTuDecl( + "struct A { void x() const; }; void A::x() const { }", + Lang_CXX, "input1.cc"); + auto *FromM = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("x"), isDefinition())); + ToM2 = Import(FromM, Lang_CXX); + } + EXPECT_NE(ToM1, ToM2); +} + struct DeclContextTest : ASTImporterTestBase {}; TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) { Index: unittests/AST/StructuralEquivalenceTest.cpp =================================================================== --- unittests/AST/StructuralEquivalenceTest.cpp +++ unittests/AST/StructuralEquivalenceTest.cpp @@ -18,13 +18,13 @@ std::unique_ptr AST0, AST1; std::string Code0, Code1; // Buffers for SourceManager - // Get a pair of Decl pointers to the synthetised declarations from the given - // code snipets. By default we search for the unique Decl with name 'foo' in - // both snippets. - std::tuple - makeNamedDecls(const std::string &SrcCode0, const std::string &SrcCode1, - Language Lang, const char *const Identifier = "foo") { - + // Get a pair of node pointers into the synthesized AST from the given code + // snippets. To determine the returned node, a separate matcher is specified + // for both snippets. The first matching node is returned. + template + std::tuple makeDecls( + const std::string &SrcCode0, const std::string &SrcCode1, Language Lang, + const MatcherType &Matcher0, const MatcherType &Matcher1) { this->Code0 = SrcCode0; this->Code1 = SrcCode1; ArgVector Args = getBasicRunOptionsForLanguage(Lang); @@ -34,28 +34,32 @@ AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName); AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName); - ASTContext &Ctx0 = AST0->getASTContext(), &Ctx1 = AST1->getASTContext(); - - auto getDecl = [](ASTContext &Ctx, const std::string &Name) -> NamedDecl * { - IdentifierInfo *SearchedII = &Ctx.Idents.get(Name); - assert(SearchedII && "Declaration with the identifier " - "should be specified in test!"); - DeclarationName SearchDeclName(SearchedII); - SmallVector FoundDecls; - Ctx.getTranslationUnitDecl()->localUncachedLookup(SearchDeclName, - FoundDecls); + NodeType *D0 = FirstDeclMatcher().match( + AST0->getASTContext().getTranslationUnitDecl(), Matcher0); + NodeType *D1 = FirstDeclMatcher().match( + AST1->getASTContext().getTranslationUnitDecl(), Matcher1); - // We should find one Decl but one only. - assert(FoundDecls.size() == 1); + return std::make_tuple(D0, D1); + } - return FoundDecls[0]; - }; + // Get a pair of node pointers into the synthesized AST from the given code + // snippets. The same matcher is used for both snippets. + template + std::tuple makeDecls( + const std::string &SrcCode0, const std::string &SrcCode1, Language Lang, + const MatcherType &AMatcher) { + return makeDecls( + SrcCode0, SrcCode1, Lang, AMatcher, AMatcher); + } - NamedDecl *D0 = getDecl(Ctx0, Identifier); - NamedDecl *D1 = getDecl(Ctx1, Identifier); - assert(D0); - assert(D1); - return std::make_tuple(D0, D1); + // Get a pair of Decl pointers to the synthesized declarations from the given + // code snippets. We search for the first NamedDecl with given name in both + // snippets. + std::tuple makeNamedDecls( + const std::string &SrcCode0, const std::string &SrcCode1, + Language Lang, const char *const Identifier = "foo") { + auto Matcher = namedDecl(hasName(Identifier)); + return makeDecls(SrcCode0, SrcCode1, Lang, Matcher); } bool testStructuralMatch(NamedDecl *D0, NamedDecl *D1) { @@ -110,35 +114,29 @@ } TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) { - auto Decls = makeNamedDecls( - "template struct foo; template<> struct foo{};", - "template struct foo; template<> struct foo{};", - Lang_CXX); - ClassTemplateSpecializationDecl *Spec0 = - *cast(get<0>(Decls))->spec_begin(); - ClassTemplateSpecializationDecl *Spec1 = - *cast(get<1>(Decls))->spec_begin(); - ASSERT_TRUE(Spec0 != nullptr); - ASSERT_TRUE(Spec1 != nullptr); + auto Decls = makeDecls( + R"(template struct foo; template<> struct foo{};)", + R"(template struct foo; template<> struct foo{};)", + Lang_CXX, + classTemplateSpecializationDecl()); + auto Spec0 = get<0>(Decls); + auto Spec1 = get<1>(Decls); EXPECT_TRUE(testStructuralMatch(Spec0, Spec1)); } TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) { - auto Decls = makeNamedDecls( - "template struct foo; template<> struct foo{};", - "template struct foo; template<> struct foo{};", - Lang_CXX); - ClassTemplateSpecializationDecl *Spec0 = - *cast(get<0>(Decls))->spec_begin(); - ClassTemplateSpecializationDecl *Spec1 = - *cast(get<1>(Decls))->spec_begin(); - ASSERT_TRUE(Spec0 != nullptr); - ASSERT_TRUE(Spec1 != nullptr); + auto Decls = makeDecls( + R"(template struct foo; template<> struct foo{};)", + R"(template struct foo; template<> struct foo{};)", + Lang_CXX, + classTemplateSpecializationDecl()); + auto Spec0 = get<0>(Decls); + auto Spec1 = get<1>(Decls); EXPECT_FALSE(testStructuralMatch(Spec0, Spec1)); } TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) { - auto Decls = makeNamedDecls( + auto Decls = makeDecls( R"( struct true_type{}; template struct foo; @@ -149,14 +147,9 @@ template struct foo; template<> struct foo : true_type {}; )", - Lang_CXX); - ClassTemplateSpecializationDecl *Spec0 = - *cast(get<0>(Decls))->spec_begin(); - ClassTemplateSpecializationDecl *Spec1 = - *cast(get<1>(Decls))->spec_begin(); - ASSERT_TRUE(Spec0 != nullptr); - ASSERT_TRUE(Spec1 != nullptr); - EXPECT_FALSE(testStructuralMatch(Spec0, Spec1)); + Lang_CXX, + classTemplateSpecializationDecl()); + EXPECT_FALSE(testStructuralMatch(Decls)); } // This test is disabled for now. @@ -203,5 +196,349 @@ EXPECT_FALSE(testStructuralMatch(Decls)); } +struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest { +}; + +TEST_F(StructuralEquivalenceFunctionTest, ParamConstWithRef) { + auto t = makeNamedDecls("void foo(int&);", + "void foo(const int&);", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ParamConstSimple) { + auto t = makeNamedDecls("void foo(int);", + "void foo(const int);", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); + // consider this OK +} + +TEST_F(StructuralEquivalenceFunctionTest, Throw) { + auto t = makeNamedDecls("void foo();", + "void foo() throw();", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, Noexcept) { + auto t = makeNamedDecls("void foo();", + "void foo() noexcept;", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexcept) { + auto t = makeNamedDecls("void foo() throw();", + "void foo() noexcept;", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptFalse) { + auto t = makeNamedDecls("void foo() throw();", + "void foo() noexcept(false);", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ThrowVsNoexceptTrue) { + auto t = makeNamedDecls("void foo() throw();", + "void foo() noexcept(true);", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, DISABLED_NoexceptNonMatch) { + // The expression is not checked yet. + auto t = makeNamedDecls("void foo() noexcept(false);", + "void foo() noexcept(true);", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NoexceptMatch) { + auto t = makeNamedDecls("void foo() noexcept(false);", + "void foo() noexcept(false);", Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptFalse) { + auto t = makeNamedDecls("void foo() noexcept;", + "void foo() noexcept(false);", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NoexceptVsNoexceptTrue) { + auto t = makeNamedDecls("void foo() noexcept;", + "void foo() noexcept(true);", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ReturnType) { + auto t = makeNamedDecls("char foo();", + "int foo();", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ReturnConst) { + auto t = makeNamedDecls("char foo();", + "const char foo();", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ReturnRef) { + auto t = makeNamedDecls("char &foo();", + "char &&foo();", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ParamCount) { + auto t = makeNamedDecls("void foo(int);", + "void foo(int, int);", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ParamType) { + auto t = makeNamedDecls("void foo(int);", + "void foo(char);", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ParamName) { + auto t = makeNamedDecls("void foo(int a);", + "void foo(int b);", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, Variadic) { + auto t = makeNamedDecls("void foo(int x...);", + "void foo(int x);", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, ParamPtr) { + auto t = makeNamedDecls("void foo(int *);", + "void foo(int);", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NameInParen) { + auto t = makeNamedDecls( + "void ((foo))();", + "void foo();", + Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithExceptionSpec) { + auto t = makeNamedDecls( + "void (foo)() throw(int);", + "void (foo)() noexcept;", + Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, NameInParenWithConst) { + auto t = makeNamedDecls( + "struct A { void (foo)() const; };", + "struct A { void (foo)(); };", + Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest { +}; + +TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) { + auto t = makeDecls( + "struct X { void foo(); };", + "struct X { virtual void foo(); };", Lang_CXX, + cxxMethodDecl(hasName("foo"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Pure) { + auto t = makeNamedDecls("struct X { virtual void foo(); };", + "struct X { virtual void foo() = 0; };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, DISABLED_Final) { + // The final-ness is not checked yet. + auto t = makeNamedDecls("struct X { virtual void foo(); };", + "struct X { virtual void foo() final; };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Const) { + auto t = makeNamedDecls("struct X { void foo(); };", + "struct X { void foo() const; };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Static) { + auto t = makeNamedDecls("struct X { void foo(); };", + "struct X { static void foo(); };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Ref1) { + auto t = makeNamedDecls("struct X { void foo(); };", + "struct X { void foo() &&; };", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Ref2) { + auto t = makeNamedDecls("struct X { void foo() &; };", + "struct X { void foo() &&; };", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, AccessSpecifier) { + auto t = makeDecls( + "struct X { public: void foo(); };", + "struct X { private: void foo(); };", Lang_CXX, + cxxMethodDecl(hasName("foo"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Delete) { + auto t = makeNamedDecls("struct X { void foo(); };", + "struct X { void foo() = delete; };", Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Constructor) { + auto t = makeDecls( + "void foo();", "struct foo { foo(); };", Lang_CXX, + functionDecl(), cxxConstructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) { + auto t = makeDecls("struct X { X(); };", + "struct X { X(int); };", Lang_CXX, + cxxConstructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) { + auto t = makeDecls("struct X { X(int); };", + "struct X { explicit X(int); };", + Lang_CXX11, + cxxConstructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) { + auto t = makeDecls("struct X { X(); };", + "struct X { X() = default; };", + Lang_CXX11, + cxxConstructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Conversion) { + auto t = makeDecls("struct X { operator bool(); };", + "struct X { operator char(); };", + Lang_CXX11, + cxxConversionDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Operator) { + auto t = makeDecls( + "struct X { int operator +(int); };", + "struct X { int operator -(int); };", Lang_CXX, + functionDecl(hasOverloadedOperatorName("+")), + functionDecl(hasOverloadedOperatorName("-"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass1) { + auto t = makeDecls( + "struct X { virtual void f(); }; void X::f() { }", + "struct X { virtual void f() { }; };", + Lang_CXX, + functionDecl(allOf(hasName("f"), isDefinition()))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, OutOfClass2) { + auto t = makeDecls( + "struct X { virtual void f(); }; void X::f() { }", + "struct X { void f(); }; void X::f() { }", + Lang_CXX, + functionDecl(allOf(hasName("f"), isDefinition()))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceRecordTest : StructuralEquivalenceTest { +}; + +TEST_F(StructuralEquivalenceRecordTest, Name) { + auto t = makeDecls( + "struct A{ };", + "struct B{ };", + Lang_CXX, + cxxRecordDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, Fields) { + auto t = makeNamedDecls( + "struct foo{ int x; };", + "struct foo{ char x; };", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, DISABLED_Methods) { + auto t = makeNamedDecls( + "struct foo{ int x(); };", + "struct foo{ char x(); };", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, Bases) { + auto t = makeNamedDecls( + "struct A{ }; struct foo: A { };", + "struct B{ }; struct foo: B { };", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, InheritanceVirtual) { + auto t = makeNamedDecls( + "struct A{ }; struct foo: A { };", + "struct A{ }; struct foo: virtual A { };", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, DISABLED_InheritanceType) { + // Access specifier in inheritance is not checked yet. + auto t = makeNamedDecls( + "struct A{ }; struct foo: public A { };", + "struct A{ }; struct foo: private A { };", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, Match) { + auto Code = R"( + struct A{ }; + struct B{ }; + struct foo: A, virtual B { + void x(); + int a; + }; + )"; + auto t = makeNamedDecls(Code, Code, Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTest, CompareSameDeclWithMultiple) { + auto t = makeNamedDecls( + "struct A{ }; struct B{ }; void foo(A a, A b);", + "struct A{ }; struct B{ }; void foo(A a, B b);", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + } // end namespace ast_matchers } // end namespace clang