diff options
-rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 10 | ||||
-rw-r--r-- | clang/unittests/AST/ASTImporterTest.cpp | 44 |
2 files changed, 50 insertions, 4 deletions
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index 69a9f9ddb6f..e999e89edad 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -5082,11 +5082,13 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateDecl(ClassTemplateDecl *D) { if (IsStructuralMatch(D, FoundTemplate)) { ClassTemplateDecl *TemplateWithDef = getTemplateDefinition(FoundTemplate); - if (D->isThisDeclarationADefinition() && TemplateWithDef) { + if (D->isThisDeclarationADefinition() && TemplateWithDef) return Importer.MapImported(D, TemplateWithDef); - } - FoundByLookup = FoundTemplate; - break; + if (!FoundByLookup) + FoundByLookup = FoundTemplate; + // Search in all matches because there may be multiple decl chains, + // see ASTTests test ImportExistingFriendClassTemplateDef. + continue; } } diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp index 97c88b95be3..3cdad0dfd5c 100644 --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -5191,6 +5191,50 @@ TEST_P(ASTImporterOptionSpecificTestBase, LambdaInGlobalScope) { EXPECT_TRUE(ToF); } +TEST_P(ASTImporterOptionSpecificTestBase, + ImportExistingFriendClassTemplateDef) { + auto Code = + R"( + template <class T1, class T2> + struct Base { + template <class U1, class U2> + friend struct Class; + }; + template <class T1, class T2> + struct Class { }; + )"; + + TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX); + TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc"); + + auto *ToClassProto = FirstDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("Class"))); + auto *ToClassDef = LastDeclMatcher<ClassTemplateDecl>().match( + ToTU, classTemplateDecl(hasName("Class"))); + ASSERT_FALSE(ToClassProto->isThisDeclarationADefinition()); + ASSERT_TRUE(ToClassDef->isThisDeclarationADefinition()); + // Previous friend decl is not linked to it! + ASSERT_FALSE(ToClassDef->getPreviousDecl()); + ASSERT_EQ(ToClassDef->getMostRecentDecl(), ToClassDef); + ASSERT_EQ(ToClassProto->getMostRecentDecl(), ToClassProto); + + auto *FromClassProto = FirstDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("Class"))); + auto *FromClassDef = LastDeclMatcher<ClassTemplateDecl>().match( + FromTU, classTemplateDecl(hasName("Class"))); + ASSERT_FALSE(FromClassProto->isThisDeclarationADefinition()); + ASSERT_TRUE(FromClassDef->isThisDeclarationADefinition()); + ASSERT_FALSE(FromClassDef->getPreviousDecl()); + ASSERT_EQ(FromClassDef->getMostRecentDecl(), FromClassDef); + ASSERT_EQ(FromClassProto->getMostRecentDecl(), FromClassProto); + + auto *ImportedDef = Import(FromClassDef, Lang_CXX); + // At import we should find the definition for 'Class' even if the + // prototype (inside 'friend') for it comes first in the AST and is not + // linked to the definition. + EXPECT_EQ(ImportedDef, ToClassDef); +} + struct LLDBLookupTest : ASTImporterOptionSpecificTestBase { LLDBLookupTest() { Creator = [](ASTContext &ToContext, FileManager &ToFileManager, |