[ASTImporter] Fix redecl chain of classes and class templates
Summary:
The crux of the issue that is being fixed is that lookup could not find
previous decls of a friend class. The solution involves making the
friend declarations visible in their decl context (i.e. adding them to
the lookup table).
Also, we simplify `VisitRecordDecl` greatly.
This fix involves two other repairs (without these the unittests fail):
(1) We could not handle the addition of injected class types properly
when a redecl chain was involved, now this is fixed.
(2) DeclContext::removeDecl failed if the lookup table in Vector form
did not contain the to be removed element. This caused troubles in
ASTImporter::ImportDeclContext. This is also fixed.
Reviewers: a_sidorin, balazske, a.sidorin
Subscribers: rnkovacs, dkrupp, Szelethus, cfe-commits
Differential Revision: https://reviews.llvm.org/D53655
llvm-svn: 349349
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 0450cb4..3407f7d 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -11,9 +11,10 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTImporter.h"
#include "MatchVerifier.h"
#include "clang/AST/ASTContext.h"
-#include "clang/AST/ASTImporter.h"
+#include "clang/AST/DeclContextInternals.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
@@ -1808,6 +1809,65 @@
EXPECT_NE(To0->getCanonicalDecl(), To1->getCanonicalDecl());
}
+TEST_P(ASTImporterTestBase, AnonymousRecords) {
+ auto *Code =
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )";
+ Decl *FromTU0 = getTuDecl(Code, Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(Code, Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect no (ODR) warning during the import.
+ EXPECT_EQ(0u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(1u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
+TEST_P(ASTImporterTestBase, AnonymousRecordsReversed) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ struct X {
+ struct { int a; };
+ struct { int b; };
+ };
+ )",
+ Lang_C, "input0.c");
+
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ struct X { // reversed order
+ struct { int b; };
+ struct { int a; };
+ };
+ )",
+ Lang_C, "input1.c");
+
+ auto *X0 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU0, recordDecl(hasName("X")));
+ auto *X1 =
+ FirstDeclMatcher<RecordDecl>().match(FromTU1, recordDecl(hasName("X")));
+ Import(X0, Lang_C);
+ Import(X1, Lang_C);
+
+ auto *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ // We expect one (ODR) warning during the import.
+ EXPECT_EQ(1u, ToTU->getASTContext().getDiagnostics().getNumWarnings());
+ EXPECT_EQ(2u,
+ DeclCounter<RecordDecl>().match(ToTU, recordDecl(hasName("X"))));
+}
+
TEST_P(ASTImporterTestBase, ImportDoesUpdateUsedFlag) {
auto Pattern = varDecl(hasName("x"));
VarDecl *Imported1;
@@ -2930,93 +2990,6 @@
EXPECT_EQ(FromIndex, 3u);
}
-TEST_P(
- ASTImporterTestBase,
- ImportOfFriendRecordDoesNotMergeDefinition) {
- Decl *FromTU = getTuDecl(
- R"(
- class A {
- template <int I> class F {};
- class X {
- template <int I> friend class F;
- };
- };
- )",
- Lang_CXX, "input0.cc");
-
- auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
- FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
- auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
- FromTU, cxxRecordDecl(hasName("F")));
-
- ASSERT_TRUE(FromClass);
- ASSERT_TRUE(FromFriendClass);
- ASSERT_NE(FromClass, FromFriendClass);
- ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
- ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
- ASSERT_EQ(
- FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
- FromClass->getDescribedClassTemplate());
-
- auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
- auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
-
- EXPECT_TRUE(ToClass);
- EXPECT_TRUE(ToFriendClass);
- EXPECT_NE(ToClass, ToFriendClass);
- EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
- EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
- EXPECT_EQ(
- ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
- ToClass->getDescribedClassTemplate());
-}
-
-TEST_P(
- ASTImporterTestBase,
- ImportOfRecursiveFriendClass) {
- Decl *FromTu = getTuDecl(
- R"(
- class declToImport {
- friend class declToImport;
- };
- )",
- Lang_CXX, "input.cc");
-
- auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
- FromTu, cxxRecordDecl(hasName("declToImport")));
- auto *ToD = Import(FromD, Lang_CXX);
- auto Pattern = cxxRecordDecl(hasName("declToImport"), has(friendDecl()));
- ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
- EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-}
-
-TEST_P(
- ASTImporterTestBase,
- ImportOfRecursiveFriendClassTemplate) {
- Decl *FromTu = getTuDecl(
- R"(
- template <class A> class declToImport {
- template <class A1> friend class declToImport;
- };
- )",
- Lang_CXX, "input.cc");
-
- auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(
- FromTu, classTemplateDecl(hasName("declToImport")));
- auto *ToD = Import(FromD, Lang_CXX);
-
- auto Pattern = classTemplateDecl(
- has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
- ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
- EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
-
- auto *Class =
- FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
- auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
- EXPECT_NE(Friend->getFriendDecl(), Class);
- EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
-}
-
TEST_P(ASTImporterTestBase, MergeFieldDeclsOfClassTemplateSpecialization) {
std::string ClassTemplate =
R"(
@@ -3381,6 +3354,542 @@
EXPECT_TRUE(ImportedD->getDefinition());
}
+struct ImportClasses : ASTImporterTestBase {};
+
+TEST_P(ImportClasses,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+ Decl *FromTU = getTuDecl("class X;", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToD);
+ EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeAfterImportedPrototype) {
+ Decl *FromTU = getTuDecl("class X; class X;", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+ auto From1 = LastDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, DefinitionShouldBeImportedAsADefinition) {
+ Decl *FromTU = getTuDecl("class X {};", Lang_CXX);
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ EXPECT_TRUE(cast<CXXRecordDecl>(ImportedD)->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+ Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+}
+
+TEST_P(ImportClasses, ImportDefinitions) {
+ Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(Imported0, Imported1);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 1u);
+ auto To0 = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClasses, ImportDefinitionThenPrototype) {
+ Decl *FromTU0 = getTuDecl("class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X;", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto ToProto = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+}
+
+TEST_P(ImportClasses, ImportPrototypeThenDefinition) {
+ Decl *FromTU0 = getTuDecl("class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 = getTuDecl("class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = cxxRecordDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto = FirstDeclMatcher<CXXRecordDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<CXXRecordDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<CXXRecordDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<CXXRecordDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+struct ImportClassTemplates : ASTImporterTestBase {};
+
+TEST_P(ImportClassTemplates,
+ PrototypeShouldBeImportedAsAPrototypeWhenThereIsNoDefinition) {
+ Decl *FromTU = getTuDecl("template <class T> class X;", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedD == ToD);
+ ASSERT_TRUE(ToD->getTemplatedDecl());
+ EXPECT_FALSE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeAfterImportedPrototype) {
+ Decl *FromTU = getTuDecl(
+ "template <class T> class X; template <class T> class X;", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+ auto From1 = LastDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ ASSERT_TRUE(To1->getTemplatedDecl());
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+ EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+ To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, DefinitionShouldBeImportedAsADefinition) {
+ Decl *FromTU = getTuDecl("template <class T> class X {};", Lang_CXX);
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto *FromD = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU, Pattern);
+
+ Decl *ImportedD = Import(FromD, Lang_CXX);
+ Decl *ToTU = ImportedD->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto ToD = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ ASSERT_TRUE(ToD->getTemplatedDecl());
+ EXPECT_TRUE(ToD->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates,
+ ImportPrototypeFromDifferentTUAfterImportedPrototype) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto To1 = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ EXPECT_TRUE(Imported1 == To1);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ ASSERT_TRUE(To1->getTemplatedDecl());
+ EXPECT_FALSE(To0->isThisDeclarationADefinition());
+ EXPECT_FALSE(To1->isThisDeclarationADefinition());
+ EXPECT_EQ(To1->getPreviousDecl(), To0);
+ EXPECT_EQ(To1->getTemplatedDecl()->getPreviousDecl(),
+ To0->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitions) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto From0 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto From1 = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *Imported0 = Import(From0, Lang_CXX);
+ Decl *Imported1 = Import(From1, Lang_CXX);
+ Decl *ToTU = Imported0->getTranslationUnitDecl();
+
+ EXPECT_EQ(Imported0, Imported1);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 1u);
+ auto To0 = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(Imported0 == To0);
+ ASSERT_TRUE(To0->getTemplatedDecl());
+ EXPECT_TRUE(To0->isThisDeclarationADefinition());
+}
+
+TEST_P(ImportClassTemplates, ImportDefinitionThenPrototype) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto FromProto =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto ToDef = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto ToProto = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ ASSERT_TRUE(ToDef->getTemplatedDecl());
+ ASSERT_TRUE(ToProto->getTemplatedDecl());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToProto->getPreviousDecl(), ToDef);
+ EXPECT_EQ(ToProto->getTemplatedDecl()->getPreviousDecl(),
+ ToDef->getTemplatedDecl());
+}
+
+TEST_P(ImportClassTemplates, ImportPrototypeThenDefinition) {
+ Decl *FromTU0 =
+ getTuDecl("template <class T> class X;", Lang_CXX, "input0.cc");
+ Decl *FromTU1 =
+ getTuDecl("template <class T> class X {};", Lang_CXX, "input1.cc");
+ auto Pattern = classTemplateDecl(hasName("X"), unless(isImplicit()));
+ auto FromProto =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTU0, Pattern);
+ auto FromDef = FirstDeclMatcher<ClassTemplateDecl>().match(FromTU1, Pattern);
+
+ Decl *ImportedProto = Import(FromProto, Lang_CXX);
+ Decl *ImportedDef = Import(FromDef, Lang_CXX);
+ Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+ EXPECT_NE(ImportedDef, ImportedProto);
+ EXPECT_EQ(DeclCounter<ClassTemplateDecl>().match(ToTU, Pattern), 2u);
+ auto ToProto = FirstDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ auto ToDef = LastDeclMatcher<ClassTemplateDecl>().match(ToTU, Pattern);
+ EXPECT_TRUE(ImportedDef == ToDef);
+ EXPECT_TRUE(ImportedProto == ToProto);
+ ASSERT_TRUE(ToProto->getTemplatedDecl());
+ ASSERT_TRUE(ToDef->getTemplatedDecl());
+ EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+ EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+ EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+ EXPECT_EQ(ToDef->getTemplatedDecl()->getPreviousDecl(),
+ ToProto->getTemplatedDecl());
+}
+
+struct ImportFriendClasses : ASTImporterTestBase {};
+
+TEST_P(ImportFriendClasses, ImportOfFriendRecordDoesNotMergeDefinition) {
+ Decl *FromTU = getTuDecl(
+ R"(
+ class A {
+ template <int I> class F {};
+ class X {
+ template <int I> friend class F;
+ };
+ };
+ )",
+ Lang_CXX, "input0.cc");
+
+ auto *FromClass = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F"), isDefinition()));
+ auto *FromFriendClass = LastDeclMatcher<CXXRecordDecl>().match(
+ FromTU, cxxRecordDecl(hasName("F")));
+
+ ASSERT_TRUE(FromClass);
+ ASSERT_TRUE(FromFriendClass);
+ ASSERT_NE(FromClass, FromFriendClass);
+ ASSERT_EQ(FromFriendClass->getDefinition(), FromClass);
+ ASSERT_EQ(FromFriendClass->getPreviousDecl(), FromClass);
+ ASSERT_EQ(FromFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ FromClass->getDescribedClassTemplate());
+
+ auto *ToClass = cast<CXXRecordDecl>(Import(FromClass, Lang_CXX));
+ auto *ToFriendClass = cast<CXXRecordDecl>(Import(FromFriendClass, Lang_CXX));
+
+ EXPECT_TRUE(ToClass);
+ EXPECT_TRUE(ToFriendClass);
+ EXPECT_NE(ToClass, ToFriendClass);
+ EXPECT_EQ(ToFriendClass->getDefinition(), ToClass);
+ EXPECT_EQ(ToFriendClass->getPreviousDecl(), ToClass);
+ EXPECT_EQ(ToFriendClass->getDescribedClassTemplate()->getPreviousDecl(),
+ ToClass->getDescribedClassTemplate());
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClass) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ class declToImport {
+ friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTu, cxxRecordDecl(hasName("declToImport")));
+ auto *ToD = Import(FromD, Lang_CXX);
+ auto Pattern = cxxRecordDecl(has(friendDecl()));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+}
+
+TEST_P(ImportFriendClasses, ImportOfRecursiveFriendClassTemplate) {
+ Decl *FromTu = getTuDecl(
+ R"(
+ template<class A> class declToImport {
+ template<class A1> friend class declToImport;
+ };
+ )",
+ Lang_CXX, "input.cc");
+
+ auto *FromD =
+ FirstDeclMatcher<ClassTemplateDecl>().match(FromTu, classTemplateDecl());
+ auto *ToD = Import(FromD, Lang_CXX);
+
+ auto Pattern = classTemplateDecl(
+ has(cxxRecordDecl(has(friendDecl(has(classTemplateDecl()))))));
+ ASSERT_TRUE(MatchVerifier<Decl>{}.match(FromD, Pattern));
+ EXPECT_TRUE(MatchVerifier<Decl>{}.match(ToD, Pattern));
+
+ auto *Class =
+ FirstDeclMatcher<ClassTemplateDecl>().match(ToD, classTemplateDecl());
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(ToD, friendDecl());
+ EXPECT_NE(Friend->getFriendDecl(), Class);
+ EXPECT_EQ(Friend->getFriendDecl()->getPreviousDecl(), Class);
+}
+
+TEST_P(ImportFriendClasses, ProperPrevDeclForClassTemplateDecls) {
+ auto Pattern = classTemplateSpecializationDecl(hasName("X"));
+
+ ClassTemplateSpecializationDecl *Imported1;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "struct Y { friend class X<int>; };",
+ Lang_CXX, "input0.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported1 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+ ClassTemplateSpecializationDecl *Imported2;
+ {
+ Decl *FromTU = getTuDecl("template<class T> class X;"
+ "template<> class X<int>{};"
+ "struct Z { friend class X<int>; };",
+ Lang_CXX, "input1.cc");
+ auto *FromD = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+ FromTU, Pattern);
+
+ Imported2 = cast<ClassTemplateSpecializationDecl>(Import(FromD, Lang_CXX));
+ }
+
+ Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl();
+ EXPECT_EQ(DeclCounter<ClassTemplateSpecializationDecl>().match(ToTU, Pattern),
+ 2u);
+ ASSERT_TRUE(Imported2->getPreviousDecl());
+ EXPECT_EQ(Imported2->getPreviousDecl(), Imported1);
+}
+
+TEST_P(ImportFriendClasses, TypeForDeclShouldBeSetInTemplated) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *Imported0 = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *Imported1 = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_EQ(Imported0->getTemplatedDecl()->getTypeForDecl(),
+ Imported1->getTemplatedDecl()->getTypeForDecl());
+}
+
+TEST_P(ImportFriendClasses, DeclsFromFriendsShouldBeInRedeclChains) {
+ Decl *From, *To;
+ std::tie(From, To) =
+ getImportedDecl("class declToImport {};", Lang_CXX,
+ "class Y { friend class declToImport; };", Lang_CXX);
+ auto *Imported = cast<CXXRecordDecl>(To);
+
+ EXPECT_TRUE(Imported->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionShouldConnectToFwdFriend) {
+ Decl *ToTU = getToTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX);
+ auto *ToDecl = FirstDeclMatcher<ClassTemplateDecl>().match(
+ ToTU, classTemplateDecl(hasName("F")));
+ Decl *FromTU = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ToDecl->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses,
+ ImportOfClassTemplateDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ template <typename T>
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Fwd = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU0, classTemplateDecl(hasName("F")));
+ auto *ImportedFwd = cast<ClassTemplateDecl>(Import(Fwd, Lang_CXX));
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ template <typename T>
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<ClassTemplateDecl>().match(
+ FromTU1, classTemplateDecl(hasName("F")));
+ auto *ImportedDef = cast<ClassTemplateDecl>(Import(Definition, Lang_CXX));
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd->getTemplatedDecl(),
+ ImportedDef->getTemplatedDecl()->getPreviousDecl());
+}
+
+TEST_P(ImportFriendClasses, ImportOfClassDefinitionAndFwdFriendShouldBeLinked) {
+ Decl *FromTU0 = getTuDecl(
+ R"(
+ class X {
+ class Y;
+ };
+ class X::Y {
+ friend class F; // The decl context of F is the global namespace.
+ };
+ )",
+ Lang_CXX, "input0.cc");
+ auto *Friend = FirstDeclMatcher<FriendDecl>().match(FromTU0, friendDecl());
+ QualType FT = Friend->getFriendType()->getType();
+ FT = FromTU0->getASTContext().getCanonicalType(FT);
+ auto *Fwd = cast<TagType>(FT)->getDecl();
+ auto *ImportedFwd = Import(Fwd, Lang_CXX);
+ Decl *FromTU1 = getTuDecl(
+ R"(
+ class F {};
+ )",
+ Lang_CXX, "input1.cc");
+ auto *Definition = FirstDeclMatcher<CXXRecordDecl>().match(
+ FromTU1, cxxRecordDecl(hasName("F")));
+ auto *ImportedDef = Import(Definition, Lang_CXX);
+ EXPECT_TRUE(ImportedDef->getPreviousDecl());
+ EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
+}
+
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
@@ -3410,6 +3919,40 @@
EXPECT_FALSE(NS->containsDecl(Spec));
}
+TEST_P(DeclContextTest,
+ removeDeclShouldNotFailEvenIfWeHaveExternalVisibleStorage) {
+ Decl *TU = getTuDecl("extern int A; int A;", Lang_CXX);
+ auto *A0 = FirstDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+ auto *A1 = LastDeclMatcher<VarDecl>().match(TU, varDecl(hasName("A")));
+
+ // Investigate the list.
+ auto *DC = A0->getDeclContext();
+ ASSERT_TRUE(DC->containsDecl(A0));
+ ASSERT_TRUE(DC->containsDecl(A1));
+
+ // Investigate the lookup table.
+ auto *Map = DC->getLookupPtr();
+ ASSERT_TRUE(Map);
+ auto I = Map->find(A0->getDeclName());
+ ASSERT_NE(I, Map->end());
+ StoredDeclsList &L = I->second;
+ // The lookup table contains the most recent decl of A.
+ ASSERT_NE(L.getAsDecl(), A0);
+ ASSERT_EQ(L.getAsDecl(), A1);
+
+ ASSERT_TRUE(L.getAsDecl());
+ // Simulate the private function DeclContext::reconcileExternalVisibleStorage.
+ // The point here is to have a Vec with only one element, which is not the
+ // one we are going to delete from the DC later.
+ L.setHasExternalDecls();
+ ASSERT_TRUE(L.getAsVector());
+ ASSERT_EQ(1u, L.getAsVector()->size());
+
+ // This asserts in the old implementation.
+ DC->removeDecl(A0);
+ EXPECT_FALSE(DC->containsDecl(A0));
+}
+
struct ImportFunctionTemplateSpecializations : ASTImporterTestBase {};
TEST_P(ImportFunctionTemplateSpecializations,
@@ -3827,6 +4370,15 @@
INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
DefaultTestValuesForRunOptions, );
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,
+ DefaultTestValuesForRunOptions, );
+
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendClasses,
+ DefaultTestValuesForRunOptions, );
+
INSTANTIATE_TEST_CASE_P(ParameterizedTests,
ImportFunctionTemplateSpecializations,
DefaultTestValuesForRunOptions, );