From 2e16060a8bbbd82f4637ddb5e58533a614ef529e Mon Sep 17 00:00:00 2001 From: Balazs Keri <1.int32@gmail.com> Date: Mon, 12 Aug 2019 10:07:38 +0000 Subject: [PATCH] [ASTImporter] Fix for import of friend class template with definition. Summary: If there is a friend class template "prototype" (forward declaration) and later a definition for it in the existing code, this existing definition may be not found by ASTImporter because it is not linked to the prototype (under the friend AST node). The problem is fixed by looping over all found matching decls instead of break after the first found one. Reviewers: martong, a.sidorin, shafik, a_sidorin Reviewed By: a_sidorin Subscribers: rnkovacs, dkrupp, Szelethus, gamesh411, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D65269 llvm-svn: 368551 --- clang/lib/AST/ASTImporter.cpp | 10 +++++--- 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 69a9f9d..e999e89 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 97c88b9..3cdad0d 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 + struct Base { + template + friend struct Class; + }; + template + struct Class { }; + )"; + + TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX); + TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc"); + + auto *ToClassProto = FirstDeclMatcher().match( + ToTU, classTemplateDecl(hasName("Class"))); + auto *ToClassDef = LastDeclMatcher().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().match( + FromTU, classTemplateDecl(hasName("Class"))); + auto *FromClassDef = LastDeclMatcher().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, -- 2.7.4