}
Decl *ASTNodeImporter::VisitRecordDecl(RecordDecl *D) {
+ bool IsFriendTemplate = false;
+ if (auto *DCXX = dyn_cast<CXXRecordDecl>(D)) {
+ IsFriendTemplate =
+ DCXX->getDescribedClassTemplate() &&
+ DCXX->getDescribedClassTemplate()->getFriendObjectKind() !=
+ Decl::FOK_None;
+ }
+
// If this record has a definition in the translation unit we're coming from,
// but this particular declaration is not that definition, import the
// definition and map to that.
TagDecl *Definition = D->getDefinition();
if (Definition && Definition != D &&
+ // Friend template declaration must be imported on its own.
+ !IsFriendTemplate &&
// In contrast to a normal CXXRecordDecl, the implicit
// CXXRecordDecl of ClassTemplateSpecializationDecl is its redeclaration.
// The definition of the implicit CXXRecordDecl in this case is the
PrevDecl = FoundRecord;
if (RecordDecl *FoundDef = FoundRecord->getDefinition()) {
- if ((SearchName && !D->isCompleteDefinition())
+ if ((SearchName && !D->isCompleteDefinition() && !IsFriendTemplate)
|| (D->isCompleteDefinition() &&
D->isAnonymousStructOrUnion()
== FoundDef->isAnonymousStructOrUnion() &&
!IsStructuralMatch(D, FoundRecord))
continue;
+ if (IsFriendTemplate)
+ continue;
+
AdoptDecl = FoundRecord;
continue;
} else if (!SearchName) {
if (!ToDescribed)
return nullptr;
D2CXX->setDescribedClassTemplate(ToDescribed);
- if (!DCXX->isInjectedClassName()) {
+ if (!DCXX->isInjectedClassName() && !IsFriendTemplate) {
// In a record describing a template the type should be an
// InjectedClassNameType (see Sema::CheckClassTemplate). Update the
// previously set type to the correct value here (ToDescribed is not
}
Decl *ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) {
+ bool IsFriend = D->getFriendObjectKind() != Decl::FOK_None;
+
// If this record has a definition in the translation unit we're coming from,
// but this particular declaration is not that definition, import the
// definition and map to that.
auto *Definition =
cast_or_null<CXXRecordDecl>(D->getTemplatedDecl()->getDefinition());
- if (Definition && Definition != D->getTemplatedDecl()) {
+ if (Definition && Definition != D->getTemplatedDecl() && !IsFriend) {
Decl *ImportedDef
= Importer.Import(Definition->getDescribedClassTemplate());
if (!ImportedDef)
// definition. So, try to get the definition if that is available in
// the redecl chain.
ClassTemplateDecl *TemplateWithDef = getDefinition(FoundTemplate);
- if (!TemplateWithDef)
+ if (TemplateWithDef)
+ FoundTemplate = TemplateWithDef;
+ else
continue;
- FoundTemplate = TemplateWithDef; // Continue with the definition.
}
if (IsStructuralMatch(D, FoundTemplate)) {
- // The class templates structurally match; call it the same template.
+ if (!IsFriend) {
+ Importer.MapImported(D->getTemplatedDecl(),
+ FoundTemplate->getTemplatedDecl());
+ return Importer.MapImported(D, FoundTemplate);
+ }
- Importer.MapImported(D->getTemplatedDecl(),
- FoundTemplate->getTemplatedDecl());
- return Importer.MapImported(D, FoundTemplate);
+ continue;
}
}
ToTemplated->setDescribedClassTemplate(D2);
+ if (ToTemplated->getPreviousDecl()) {
+ assert(
+ ToTemplated->getPreviousDecl()->getDescribedClassTemplate() &&
+ "Missing described template");
+ D2->setPreviousDecl(
+ ToTemplated->getPreviousDecl()->getDescribedClassTemplate());
+ }
D2->setAccess(D->getAccess());
D2->setLexicalDeclContext(LexicalDC);
- LexicalDC->addDeclInternal(D2);
+ if (!IsFriend)
+ LexicalDC->addDeclInternal(D2);
if (FromTemplated->isCompleteDefinition() &&
!ToTemplated->isCompleteDefinition()) {
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);
+}
+
struct DeclContextTest : ASTImporterTestBase {};
TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {