diff options
author | 2020-08-03 14:31:31 +0000 | |
---|---|---|
committer | 2020-08-03 14:31:31 +0000 | |
commit | e5dd70708596ae51455a0ffa086a00c5b29f8583 (patch) | |
tree | 5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp | |
parent | Import LLVM 10.0.0 release including clang, lld and lldb. (diff) | |
download | wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip |
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom
tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp')
-rw-r--r-- | gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp | 1400 |
1 files changed, 1400 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp b/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp new file mode 100644 index 00000000000..8e467527ead --- /dev/null +++ b/gnu/llvm/clang/unittests/AST/StructuralEquivalenceTest.cpp @@ -0,0 +1,1400 @@ +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/AST/ASTStructuralEquivalence.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Tooling/Tooling.h" + +#include "Language.h" +#include "DeclMatcher.h" + +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { + +using std::get; + +struct StructuralEquivalenceTest : ::testing::Test { + std::unique_ptr<ASTUnit> AST0, AST1; + std::string Code0, Code1; // Buffers for SourceManager + + // 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 <typename NodeType, typename MatcherType> + std::tuple<NodeType *, NodeType *> 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); + + const char *const InputFileName = "input.cc"; + + AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName); + AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName); + + NodeType *D0 = FirstDeclMatcher<NodeType>().match( + AST0->getASTContext().getTranslationUnitDecl(), Matcher0); + NodeType *D1 = FirstDeclMatcher<NodeType>().match( + AST1->getASTContext().getTranslationUnitDecl(), Matcher1); + + return std::make_tuple(D0, D1); + } + + std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> makeTuDecls( + const std::string &SrcCode0, const std::string &SrcCode1, Language Lang) { + this->Code0 = SrcCode0; + this->Code1 = SrcCode1; + ArgVector Args = getBasicRunOptionsForLanguage(Lang); + + const char *const InputFileName = "input.cc"; + + AST0 = tooling::buildASTFromCodeWithArgs(Code0, Args, InputFileName); + AST1 = tooling::buildASTFromCodeWithArgs(Code1, Args, InputFileName); + + return std::make_tuple(AST0->getASTContext().getTranslationUnitDecl(), + AST1->getASTContext().getTranslationUnitDecl()); + } + + // Get a pair of node pointers into the synthesized AST from the given code + // snippets. The same matcher is used for both snippets. + template <typename NodeType, typename MatcherType> + std::tuple<NodeType *, NodeType *> makeDecls( + const std::string &SrcCode0, const std::string &SrcCode1, Language Lang, + const MatcherType &AMatcher) { + return makeDecls<NodeType, MatcherType>( + SrcCode0, SrcCode1, Lang, AMatcher, AMatcher); + } + + // 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<NamedDecl *, NamedDecl *> makeNamedDecls( + const std::string &SrcCode0, const std::string &SrcCode1, + Language Lang, const char *const Identifier = "foo") { + auto Matcher = namedDecl(hasName(Identifier)); + return makeDecls<NamedDecl>(SrcCode0, SrcCode1, Lang, Matcher); + } + + bool testStructuralMatch(Decl *D0, Decl *D1) { + llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls01; + llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls10; + StructuralEquivalenceContext Ctx01( + D0->getASTContext(), D1->getASTContext(), + NonEquivalentDecls01, StructuralEquivalenceKind::Default, false, false); + StructuralEquivalenceContext Ctx10( + D1->getASTContext(), D0->getASTContext(), + NonEquivalentDecls10, StructuralEquivalenceKind::Default, false, false); + bool Eq01 = Ctx01.IsEquivalent(D0, D1); + bool Eq10 = Ctx10.IsEquivalent(D1, D0); + EXPECT_EQ(Eq01, Eq10); + return Eq01; + } + + bool testStructuralMatch(std::tuple<Decl *, Decl *> t) { + return testStructuralMatch(get<0>(t), get<1>(t)); + } +}; + +TEST_F(StructuralEquivalenceTest, Int) { + auto Decls = makeNamedDecls("int foo;", "int foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedInt) { + auto Decls = makeNamedDecls("int foo;", "signed int foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, Char) { + auto Decls = makeNamedDecls("char foo;", "char foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +// This test is disabled for now. +// FIXME Whether this is equivalent is dependendant on the target. +TEST_F(StructuralEquivalenceTest, DISABLED_CharVsSignedChar) { + auto Decls = makeNamedDecls("char foo;", "signed char foo;", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, ForwardRecordDecl) { + auto Decls = makeNamedDecls("struct foo;", "struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedIntInStruct) { + auto Decls = makeNamedDecls("struct foo { int x; };", + "struct foo { signed int x; };", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharInStruct) { + auto Decls = makeNamedDecls("struct foo { char x; };", + "struct foo { signed char x; };", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, IntVsSignedIntTemplateSpec) { + auto Decls = makeDecls<ClassTemplateSpecializationDecl>( + R"(template <class T> struct foo; template<> struct foo<int>{};)", + R"(template <class T> struct foo; template<> struct foo<signed int>{};)", + Lang_CXX, + classTemplateSpecializationDecl()); + auto Spec0 = get<0>(Decls); + auto Spec1 = get<1>(Decls); + EXPECT_TRUE(testStructuralMatch(Spec0, Spec1)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpec) { + auto Decls = makeDecls<ClassTemplateSpecializationDecl>( + R"(template <class T> struct foo; template<> struct foo<char>{};)", + R"(template <class T> struct foo; template<> struct foo<signed char>{};)", + Lang_CXX, + classTemplateSpecializationDecl()); + auto Spec0 = get<0>(Decls); + auto Spec1 = get<1>(Decls); + EXPECT_FALSE(testStructuralMatch(Spec0, Spec1)); +} + +TEST_F(StructuralEquivalenceTest, CharVsSignedCharTemplateSpecWithInheritance) { + auto Decls = makeDecls<ClassTemplateSpecializationDecl>( + R"( + struct true_type{}; + template <class T> struct foo; + template<> struct foo<char> : true_type {}; + )", + R"( + struct true_type{}; + template <class T> struct foo; + template<> struct foo<signed char> : true_type {}; + )", + Lang_CXX, + classTemplateSpecializationDecl()); + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +// This test is disabled for now. +// FIXME Enable it, once the check is implemented. +TEST_F(StructuralEquivalenceTest, DISABLED_WrongOrderInNamespace) { + auto Code = + R"( + namespace NS { + template <class T> class Base { + int a; + }; + class Derived : Base<Derived> { + }; + } + void foo(NS::Derived &); + )"; + auto Decls = makeNamedDecls(Code, Code, Lang_CXX); + + NamespaceDecl *NS = + LastDeclMatcher<NamespaceDecl>().match(get<1>(Decls), namespaceDecl()); + ClassTemplateDecl *TD = LastDeclMatcher<ClassTemplateDecl>().match( + get<1>(Decls), classTemplateDecl(hasName("Base"))); + + // Reorder the decls, move the TD to the last place in the DC. + NS->removeDecl(TD); + NS->addDeclInternal(TD); + + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +TEST_F(StructuralEquivalenceTest, WrongOrderOfFieldsInClass) { + auto Code = "class X { int a; int b; };"; + auto Decls = makeNamedDecls(Code, Code, Lang_CXX, "X"); + + CXXRecordDecl *RD = FirstDeclMatcher<CXXRecordDecl>().match( + get<1>(Decls), cxxRecordDecl(hasName("X"))); + FieldDecl *FD = + FirstDeclMatcher<FieldDecl>().match(get<1>(Decls), fieldDecl(hasName("a"))); + + // Reorder the FieldDecls + RD->removeDecl(FD); + RD->addDeclInternal(FD); + + EXPECT_FALSE(testStructuralMatch(Decls)); +} + +struct StructuralEquivalenceFunctionTest : StructuralEquivalenceTest { +}; + +TEST_F(StructuralEquivalenceFunctionTest, TemplateVsNonTemplate) { + auto t = makeNamedDecls( + "void foo();", + "template<class T> void foo();", + Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, DifferentOperators) { + auto t = makeDecls<FunctionDecl>( + "struct X{}; bool operator<(X, X);", + "struct X{}; bool operator==(X, X);", Lang_CXX, + functionDecl(hasOverloadedOperatorName("<")), + functionDecl(hasOverloadedOperatorName("=="))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, SameOperators) { + auto t = makeDecls<FunctionDecl>( + "struct X{}; bool operator<(X, X);", + "struct X{}; bool operator<(X, X);", Lang_CXX, + functionDecl(hasOverloadedOperatorName("<")), + functionDecl(hasOverloadedOperatorName("<"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, CtorVsDtor) { + auto t = makeDecls<FunctionDecl>( + "struct X{ X(); };", + "struct X{ ~X(); };", Lang_CXX, + cxxConstructorDecl(), + cxxDestructorDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +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, NoexceptNonMatch) { + 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)); +} + +TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentNoreturnAttr) { + auto t = makeNamedDecls( + "__attribute__((noreturn)) void foo();", + " void foo();", + Lang_C); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, + FunctionsWithDifferentCallingConventions) { + // These attributes may not be available on certain platforms. + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() != + llvm::Triple::x86_64) + return; + auto t = makeNamedDecls( + "__attribute__((preserve_all)) void foo();", + "__attribute__((ms_abi)) void foo();", + Lang_C); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceFunctionTest, FunctionsWithDifferentSavedRegsAttr) { + if (llvm::Triple(llvm::sys::getDefaultTargetTriple()).getArch() != + llvm::Triple::x86_64) + return; + auto t = makeNamedDecls( + "__attribute__((no_caller_saved_registers)) void foo();", + " void foo();", + Lang_C); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceCXXMethodTest : StructuralEquivalenceTest { +}; + +TEST_F(StructuralEquivalenceCXXMethodTest, Virtual) { + auto t = makeDecls<CXXMethodDecl>( + "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<CXXMethodDecl>( + "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<FunctionDecl>( + "void foo();", "struct foo { foo(); };", Lang_CXX, + functionDecl(hasName("foo")), cxxConstructorDecl(hasName("foo"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorParam) { + auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };", + "struct X { X(int); };", Lang_CXX, + cxxConstructorDecl(hasName("X"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorExplicit) { + auto t = makeDecls<CXXConstructorDecl>("struct X { X(int); };", + "struct X { explicit X(int); };", + Lang_CXX11, + cxxConstructorDecl(hasName("X"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, ConstructorDefault) { + auto t = makeDecls<CXXConstructorDecl>("struct X { X(); };", + "struct X { X() = default; };", + Lang_CXX11, + cxxConstructorDecl(hasName("X"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Conversion) { + auto t = makeDecls<CXXConversionDecl>("struct X { operator bool(); };", + "struct X { operator char(); };", + Lang_CXX11, + cxxConversionDecl()); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceCXXMethodTest, Operator) { + auto t = makeDecls<FunctionDecl>( + "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<FunctionDecl>( + "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<FunctionDecl>( + "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 { + // FIXME Use a common getRecordDecl with ASTImporterTest.cpp! + RecordDecl *getRecordDecl(FieldDecl *FD) { + auto *ET = cast<ElaboratedType>(FD->getType().getTypePtr()); + return cast<RecordType>(ET->getNamedType().getTypePtr())->getDecl(); + }; +}; + +TEST_F(StructuralEquivalenceRecordTest, Name) { + auto t = makeDecls<CXXRecordDecl>( + "struct A{ };", + "struct B{ };", + Lang_CXX, + cxxRecordDecl(hasName("A")), + cxxRecordDecl(hasName("B"))); + 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) { + // Currently, methods of a class are not checked at class equivalence. + 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(StructuralEquivalenceRecordTest, UnnamedRecordsShouldBeInequivalent) { + auto t = makeTuDecls( + R"( + struct A { + struct { + struct A *next; + } entry0; + struct { + struct A *next; + } entry1; + }; + )", + "", Lang_C); + auto *TU = get<0>(t); + auto *Entry0 = + FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry0"))); + auto *Entry1 = + FirstDeclMatcher<FieldDecl>().match(TU, fieldDecl(hasName("entry1"))); + auto *R0 = getRecordDecl(Entry0); + auto *R1 = getRecordDecl(Entry1); + + ASSERT_NE(R0, R1); + EXPECT_TRUE(testStructuralMatch(R0, R0)); + EXPECT_TRUE(testStructuralMatch(R1, R1)); + EXPECT_FALSE(testStructuralMatch(R0, R1)); +} + +TEST_F(StructuralEquivalenceRecordTest, AnonymousRecordsShouldBeInequivalent) { + auto t = makeTuDecls( + R"( + struct X { + struct { + int a; + }; + struct { + int b; + }; + }; + )", + "", Lang_C); + auto *TU = get<0>(t); + auto *A = FirstDeclMatcher<IndirectFieldDecl>().match( + TU, indirectFieldDecl(hasName("a"))); + auto *FA = cast<FieldDecl>(A->chain().front()); + RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl(); + auto *B = FirstDeclMatcher<IndirectFieldDecl>().match( + TU, indirectFieldDecl(hasName("b"))); + auto *FB = cast<FieldDecl>(B->chain().front()); + RecordDecl *RB = cast<RecordType>(FB->getType().getTypePtr())->getDecl(); + + ASSERT_NE(RA, RB); + EXPECT_TRUE(testStructuralMatch(RA, RA)); + EXPECT_TRUE(testStructuralMatch(RB, RB)); + EXPECT_FALSE(testStructuralMatch(RA, RB)); +} + +TEST_F(StructuralEquivalenceRecordTest, + RecordsAreInequivalentIfOrderOfAnonRecordsIsDifferent) { + auto t = makeTuDecls( + R"( + struct X { + struct { int a; }; + struct { int b; }; + }; + )", + R"( + struct X { // The order is reversed. + struct { int b; }; + struct { int a; }; + }; + )", + Lang_C); + + auto *TU = get<0>(t); + auto *A = FirstDeclMatcher<IndirectFieldDecl>().match( + TU, indirectFieldDecl(hasName("a"))); + auto *FA = cast<FieldDecl>(A->chain().front()); + RecordDecl *RA = cast<RecordType>(FA->getType().getTypePtr())->getDecl(); + + auto *TU1 = get<1>(t); + auto *A1 = FirstDeclMatcher<IndirectFieldDecl>().match( + TU1, indirectFieldDecl(hasName("a"))); + auto *FA1 = cast<FieldDecl>(A1->chain().front()); + RecordDecl *RA1 = cast<RecordType>(FA1->getType().getTypePtr())->getDecl(); + + RecordDecl *X = + FirstDeclMatcher<RecordDecl>().match(TU, recordDecl(hasName("X"))); + RecordDecl *X1 = + FirstDeclMatcher<RecordDecl>().match(TU1, recordDecl(hasName("X"))); + ASSERT_NE(X, X1); + EXPECT_FALSE(testStructuralMatch(X, X1)); + + ASSERT_NE(RA, RA1); + EXPECT_TRUE(testStructuralMatch(RA, RA)); + EXPECT_TRUE(testStructuralMatch(RA1, RA1)); + EXPECT_FALSE(testStructuralMatch(RA1, RA)); +} + +TEST_F(StructuralEquivalenceRecordTest, + UnnamedRecordsShouldBeInequivalentEvenIfTheSecondIsBeingDefined) { + auto Code = + R"( + struct A { + struct { + struct A *next; + } entry0; + struct { + struct A *next; + } entry1; + }; + )"; + auto t = makeTuDecls(Code, Code, Lang_C); + + auto *FromTU = get<0>(t); + auto *Entry1 = + FirstDeclMatcher<FieldDecl>().match(FromTU, fieldDecl(hasName("entry1"))); + + auto *ToTU = get<1>(t); + auto *Entry0 = + FirstDeclMatcher<FieldDecl>().match(ToTU, fieldDecl(hasName("entry0"))); + auto *A = + FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A"))); + A->startDefinition(); // Set isBeingDefined, getDefinition() will return a + // nullptr. This may be the case during ASTImport. + + auto *R0 = getRecordDecl(Entry0); + auto *R1 = getRecordDecl(Entry1); + + ASSERT_NE(R0, R1); + EXPECT_TRUE(testStructuralMatch(R0, R0)); + EXPECT_TRUE(testStructuralMatch(R1, R1)); + EXPECT_FALSE(testStructuralMatch(R0, R1)); +} + +TEST_F(StructuralEquivalenceRecordTest, TemplateVsNonTemplate) { + auto t = makeDecls<CXXRecordDecl>( + "struct A { };", + "template<class T> struct A { };", + Lang_CXX, + cxxRecordDecl(hasName("A"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, + FwdDeclRecordShouldBeEqualWithFwdDeclRecord) { + auto t = makeNamedDecls("class foo;", "class foo;", Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, + FwdDeclRecordShouldBeEqualWithRecordWhichHasDefinition) { + auto t = + makeNamedDecls("class foo;", "class foo { int A; };", Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, + RecordShouldBeEqualWithRecordWhichHasDefinition) { + auto t = makeNamedDecls("class foo { int A; };", "class foo { int A; };", + Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceRecordTest, RecordsWithDifferentBody) { + auto t = makeNamedDecls("class foo { int B; };", "class foo { int A; };", + Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceLambdaTest : StructuralEquivalenceTest {}; + +TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentMethods) { + // Get the LambdaExprs, unfortunately we can't match directly the underlying + // implicit CXXRecordDecl of the Lambda classes. + auto t = makeDecls<LambdaExpr>( + "void f() { auto L0 = [](int){}; }", + "void f() { auto L1 = [](){}; }", + Lang_CXX11, + lambdaExpr(), + lambdaExpr()); + CXXRecordDecl *L0 = get<0>(t)->getLambdaClass(); + CXXRecordDecl *L1 = get<1>(t)->getLambdaClass(); + EXPECT_FALSE(testStructuralMatch(L0, L1)); +} + +TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqMethods) { + auto t = makeDecls<LambdaExpr>( + "void f() { auto L0 = [](int){}; }", + "void f() { auto L1 = [](int){}; }", + Lang_CXX11, + lambdaExpr(), + lambdaExpr()); + CXXRecordDecl *L0 = get<0>(t)->getLambdaClass(); + CXXRecordDecl *L1 = get<1>(t)->getLambdaClass(); + EXPECT_TRUE(testStructuralMatch(L0, L1)); +} + +TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithDifferentFields) { + auto t = makeDecls<LambdaExpr>( + "void f() { char* X; auto L0 = [X](){}; }", + "void f() { float X; auto L1 = [X](){}; }", + Lang_CXX11, + lambdaExpr(), + lambdaExpr()); + CXXRecordDecl *L0 = get<0>(t)->getLambdaClass(); + CXXRecordDecl *L1 = get<1>(t)->getLambdaClass(); + EXPECT_FALSE(testStructuralMatch(L0, L1)); +} + +TEST_F(StructuralEquivalenceLambdaTest, LambdaClassesWithEqFields) { + auto t = makeDecls<LambdaExpr>( + "void f() { float X; auto L0 = [X](){}; }", + "void f() { float X; auto L1 = [X](){}; }", + Lang_CXX11, + lambdaExpr(), + lambdaExpr()); + CXXRecordDecl *L0 = get<0>(t)->getLambdaClass(); + CXXRecordDecl *L1 = get<1>(t)->getLambdaClass(); + EXPECT_TRUE(testStructuralMatch(L0, L1)); +} + +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)); +} + +TEST_F(StructuralEquivalenceTest, ExplicitBoolDifferent) { + auto Decls = makeNamedDecls("struct foo {explicit(false) foo(int);};", + "struct foo {explicit(true) foo(int);};", Lang_CXX2a); + CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match( + get<0>(Decls), cxxConstructorDecl(hasName("foo"))); + CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match( + get<1>(Decls), cxxConstructorDecl(hasName("foo"))); + EXPECT_FALSE(testStructuralMatch(First, Second)); +} + +TEST_F(StructuralEquivalenceTest, ExplicitBoolSame) { + auto Decls = makeNamedDecls("struct foo {explicit(true) foo(int);};", + "struct foo {explicit(true) foo(int);};", Lang_CXX2a); + CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match( + get<0>(Decls), cxxConstructorDecl(hasName("foo"))); + CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match( + get<1>(Decls), cxxConstructorDecl(hasName("foo"))); + EXPECT_TRUE(testStructuralMatch(First, Second)); +} + +struct StructuralEquivalenceEnumTest : StructuralEquivalenceTest {}; + +TEST_F(StructuralEquivalenceEnumTest, FwdDeclEnumShouldBeEqualWithFwdDeclEnum) { + auto t = makeNamedDecls("enum class foo;", "enum class foo;", Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceEnumTest, + FwdDeclEnumShouldBeEqualWithEnumWhichHasDefinition) { + auto t = + makeNamedDecls("enum class foo;", "enum class foo { A };", Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceEnumTest, + EnumShouldBeEqualWithEnumWhichHasDefinition) { + auto t = makeNamedDecls("enum class foo { A };", "enum class foo { A };", + Lang_CXX11); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceEnumTest, EnumsWithDifferentBody) { + auto t = makeNamedDecls("enum class foo { B };", "enum class foo { A };", + Lang_CXX11); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceTemplateTest : StructuralEquivalenceTest {}; + +TEST_F(StructuralEquivalenceTemplateTest, ExactlySameTemplates) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <class T> struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgName) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <class U> struct foo;", Lang_CXX); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, DifferentTemplateArgKind) { + auto t = makeNamedDecls("template <class T> struct foo;", + "template <int T> struct foo;", Lang_CXX); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolSame) { + auto Decls = makeNamedDecls("template <bool b> struct foo {explicit(b) foo(int);};", + "template <bool b> struct foo {explicit(b) foo(int);};", Lang_CXX2a); + CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match( + get<0>(Decls), cxxConstructorDecl(hasName("foo<b>"))); + CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match( + get<1>(Decls), cxxConstructorDecl(hasName("foo<b>"))); + EXPECT_TRUE(testStructuralMatch(First, Second)); +} + +TEST_F(StructuralEquivalenceTemplateTest, ExplicitBoolDifference) { + auto Decls = makeNamedDecls("template <bool b> struct foo {explicit(b) foo(int);};", + "template <bool b> struct foo {explicit(!b) foo(int);};", Lang_CXX2a); + CXXConstructorDecl *First = FirstDeclMatcher<CXXConstructorDecl>().match( + get<0>(Decls), cxxConstructorDecl(hasName("foo<b>"))); + CXXConstructorDecl *Second = FirstDeclMatcher<CXXConstructorDecl>().match( + get<1>(Decls), cxxConstructorDecl(hasName("foo<b>"))); + EXPECT_FALSE(testStructuralMatch(First, Second)); +} + +TEST_F(StructuralEquivalenceTemplateTest, + TemplateVsSubstTemplateTemplateParmInArgEq) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( +template <typename P1> class Arg { }; +template <template <typename PP1> class P1> class Primary { }; + +void f() { + // Make specialization with simple template. + Primary <Arg> A; +} + )", + R"( +template <typename P1> class Arg { }; +template <template <typename PP1> class P1> class Primary { }; + +template <template <typename PP1> class P1> class Templ { + void f() { + // Make specialization with substituted template template param. + Primary <P1> A; + }; +}; + +// Instantiate with substitution Arg into P1. +template class Templ <Arg>; + )", + Lang_CXX, classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceTemplateTest, + TemplateVsSubstTemplateTemplateParmInArgNotEq) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( +template <typename P1> class Arg { }; +template <template <typename PP1> class P1> class Primary { }; + +void f() { + // Make specialization with simple template. + Primary <Arg> A; +} + )", + R"( +// Arg is different from the other, this should cause non-equivalence. +template <typename P1> class Arg { int X; }; +template <template <typename PP1> class P1> class Primary { }; + +template <template <typename PP1> class P1> class Templ { + void f() { + // Make specialization with substituted template template param. + Primary <P1> A; + }; +}; + +// Instantiate with substitution Arg into P1. +template class Templ <Arg>; + )", + Lang_CXX, classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +struct StructuralEquivalenceDependentTemplateArgsTest + : StructuralEquivalenceTemplateTest {}; + +TEST_F(StructuralEquivalenceDependentTemplateArgsTest, + SameStructsInDependentArgs) { + std::string Code = + R"( + template <typename> + struct S1; + + template <typename> + struct enable_if; + + struct S + { + template <typename T, typename enable_if<S1<T>>::type> + void f(); + }; + )"; + auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11, + functionTemplateDecl(hasName("f"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceDependentTemplateArgsTest, + DifferentStructsInDependentArgs) { + std::string Code = + R"( + template <typename> + struct S1; + + template <typename> + struct S2; + + template <typename> + struct enable_if; + )"; + auto t = makeDecls<FunctionTemplateDecl>(Code + R"( + struct S + { + template <typename T, typename enable_if<S1<T>>::type> + void f(); + }; + )", + Code + R"( + struct S + { + template <typename T, typename enable_if<S2<T>>::type> + void f(); + }; + )", + Lang_CXX11, + functionTemplateDecl(hasName("f"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceDependentTemplateArgsTest, + SameStructsInDependentScopeDeclRefExpr) { + std::string Code = + R"( + template <typename> + struct S1; + + template <bool> + struct enable_if; + + struct S + { + template <typename T, typename enable_if<S1<T>::value>::type> + void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^ + }; + )"; + auto t = makeDecls<FunctionTemplateDecl>(Code, Code, Lang_CXX11, + functionTemplateDecl(hasName("f"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceDependentTemplateArgsTest, + DifferentStructsInDependentScopeDeclRefExpr) { + std::string Code = + R"( + template <typename> + struct S1; + + template <typename> + struct S2; + + template <bool> + struct enable_if; + )"; + auto t = makeDecls<FunctionTemplateDecl>(Code + R"( + struct S + { + template <typename T, typename enable_if<S1<T>::value>::type> + void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^ + }; + )", + Code + R"( + struct S + { + template <typename T, typename enable_if<S2<T>::value>::type> + void f(); + }; + )", + Lang_CXX, + functionTemplateDecl(hasName("f"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F(StructuralEquivalenceDependentTemplateArgsTest, + DifferentValueInDependentScopeDeclRefExpr) { + std::string Code = + R"( + template <typename> + struct S1; + + template <bool> + struct enable_if; + )"; + auto t = makeDecls<FunctionTemplateDecl>(Code + R"( + struct S + { + template <typename T, typename enable_if<S1<T>::value1>::type> + void f(); // DependentScopeDeclRefExpr:^^^^^^^^^^^^ + }; + )", + Code + R"( + struct S + { + template <typename T, typename enable_if<S1<T>::value2>::type> + void f(); + }; + )", + Lang_CXX, + functionTemplateDecl(hasName("f"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F( + StructuralEquivalenceTemplateTest, + ClassTemplSpecWithQualifiedAndNonQualifiedTypeArgsShouldBeEqual) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( + template <class T> struct Primary {}; + namespace N { + struct Arg; + } + // Explicit instantiation with qualified name. + template struct Primary<N::Arg>; + )", + R"( + template <class T> struct Primary {}; + namespace N { + struct Arg; + } + using namespace N; + // Explicit instantiation with UNqualified name. + template struct Primary<Arg>; + )", + Lang_CXX, + classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F( + StructuralEquivalenceTemplateTest, + ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTypeArgs) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( + template <class T> struct Primary {}; + namespace N { + struct Arg { int a; }; + } + // Explicit instantiation with qualified name. + template struct Primary<N::Arg>; + )", + R"( + template <class T> struct Primary {}; + namespace N { + // This struct is not equivalent with the other in the prev TU. + struct Arg { double b; }; // -- Field mismatch. + } + using namespace N; + // Explicit instantiation with UNqualified name. + template struct Primary<Arg>; + )", + Lang_CXX, + classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F( + StructuralEquivalenceTemplateTest, + ClassTemplSpecWithQualifiedAndNonQualifiedTemplArgsShouldBeEqual) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( + template <template <class> class T> struct Primary {}; + namespace N { + template <class T> struct Arg; + } + // Explicit instantiation with qualified name. + template struct Primary<N::Arg>; + )", + R"( + template <template <class> class T> struct Primary {}; + namespace N { + template <class T> struct Arg; + } + using namespace N; + // Explicit instantiation with UNqualified name. + template struct Primary<Arg>; + )", + Lang_CXX, + classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_TRUE(testStructuralMatch(t)); +} + +TEST_F( + StructuralEquivalenceTemplateTest, + ClassTemplSpecWithInequivalentQualifiedAndNonQualifiedTemplArgs) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( + template <template <class> class T> struct Primary {}; + namespace N { + template <class T> struct Arg { int a; }; + } + // Explicit instantiation with qualified name. + template struct Primary<N::Arg>; + )", + R"( + template <template <class> class T> struct Primary {}; + namespace N { + // This template is not equivalent with the other in the prev TU. + template <class T> struct Arg { double b; }; // -- Field mismatch. + } + using namespace N; + // Explicit instantiation with UNqualified name. + template struct Primary<Arg>; + )", + Lang_CXX, + classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_FALSE(testStructuralMatch(t)); +} + +TEST_F( + StructuralEquivalenceTemplateTest, + ClassTemplSpecWithInequivalentShadowedTemplArg) { + auto t = makeDecls<ClassTemplateSpecializationDecl>( + R"( + template <template <class> class T> struct Primary {}; + template <class T> struct Arg { int a; }; + // Explicit instantiation with ::Arg + template struct Primary<Arg>; + )", + R"( + template <template <class> class T> struct Primary {}; + template <class T> struct Arg { int a; }; + namespace N { + // This template is not equivalent with the other in the global scope. + template <class T> struct Arg { double b; }; // -- Field mismatch. + // Explicit instantiation with N::Arg which shadows ::Arg + template struct Primary<Arg>; + } + )", + Lang_CXX, + classTemplateSpecializationDecl(hasName("Primary"))); + EXPECT_FALSE(testStructuralMatch(t)); +} +struct StructuralEquivalenceCacheTest : public StructuralEquivalenceTest { + llvm::DenseSet<std::pair<Decl *, Decl *>> NonEquivalentDecls; + + template <typename NodeType, typename MatcherType> + std::pair<NodeType *, NodeType *> + findDeclPair(std::tuple<TranslationUnitDecl *, TranslationUnitDecl *> TU, + MatcherType M) { + NodeType *D0 = FirstDeclMatcher<NodeType>().match(get<0>(TU), M); + NodeType *D1 = FirstDeclMatcher<NodeType>().match(get<1>(TU), M); + return {D0, D1}; + } + + template <typename NodeType> + bool isInNonEqCache(std::pair<NodeType *, NodeType *> D) { + return NonEquivalentDecls.count(D) > 0; + } +}; + +TEST_F(StructuralEquivalenceCacheTest, SimpleNonEq) { + auto TU = makeTuDecls( + R"( + class A {}; + class B {}; + void x(A, A); + )", + R"( + class A {}; + class B {}; + void x(A, B); + )", + Lang_CXX); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto X = findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x"))); + EXPECT_FALSE(Ctx.IsEquivalent(X.first, X.second)); + + EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("A"), unless(isImplicit()))))); + EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("B"), unless(isImplicit()))))); +} + +TEST_F(StructuralEquivalenceCacheTest, SpecialNonEq) { + auto TU = makeTuDecls( + R"( + class A {}; + class B { int i; }; + void x(A *); + void y(A *); + class C { + friend void x(A *); + friend void y(A *); + }; + )", + R"( + class A {}; + class B { int i; }; + void x(A *); + void y(B *); + class C { + friend void x(A *); + friend void y(B *); + }; + )", + Lang_CXX); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto C = findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("C"), unless(isImplicit()))); + EXPECT_FALSE(Ctx.IsEquivalent(C.first, C.second)); + + EXPECT_FALSE(isInNonEqCache(C)); + EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("A"), unless(isImplicit()))))); + EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("B"), unless(isImplicit()))))); + EXPECT_FALSE(isInNonEqCache( + findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x"))))); + EXPECT_FALSE(isInNonEqCache( + findDeclPair<FunctionDecl>(TU, functionDecl(hasName("y"))))); +} + +TEST_F(StructuralEquivalenceCacheTest, Cycle) { + auto TU = makeTuDecls( + R"( + class C; + class A { C *c; }; + void x(A *); + class C { + friend void x(A *); + }; + )", + R"( + class C; + class A { C *c; }; + void x(A *); + class C { + friend void x(A *); + }; + )", + Lang_CXX); + + StructuralEquivalenceContext Ctx( + get<0>(TU)->getASTContext(), get<1>(TU)->getASTContext(), + NonEquivalentDecls, StructuralEquivalenceKind::Default, false, false); + + auto C = findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("C"), unless(isImplicit()))); + EXPECT_TRUE(Ctx.IsEquivalent(C.first, C.second)); + + EXPECT_FALSE(isInNonEqCache(C)); + EXPECT_FALSE(isInNonEqCache(findDeclPair<CXXRecordDecl>( + TU, cxxRecordDecl(hasName("A"), unless(isImplicit()))))); + EXPECT_FALSE(isInNonEqCache( + findDeclPair<FunctionDecl>(TU, functionDecl(hasName("x"))))); +} + +} // end namespace ast_matchers +} // end namespace clang |