diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -3632,6 +3632,30 @@ return ToIndirectField; } +// Returns the DeclContext of the underlying friend declaration/type. If that +// is a dependent type then the returned optional does not have a value. +static Optional getDCOfUnderlyingDecl(FriendDecl *FrD) { + if (NamedDecl *ND = FrD->getFriendDecl()) + return ND->getDeclContext(); + if (FrD->getFriendType()) { + QualType Ty = FrD->getFriendType()->getType(); + if (isa(Ty)) + Ty = cast(Ty)->getNamedType(); + if (!Ty->isDependentType()) { + if (const auto *RTy = dyn_cast(Ty)) + return RTy->getAsCXXRecordDecl()->getDeclContext(); + else if (const auto *SpecTy = dyn_cast(Ty)) + return SpecTy->getAsCXXRecordDecl()->getDeclContext(); + else if (const auto TypedefTy = dyn_cast(Ty)) + return TypedefTy->getDecl()->getDeclContext(); + else + llvm_unreachable("Unhandled type of friend"); + } + } + // DependentType + return Optional(); +} + ExpectedDecl ASTNodeImporter::VisitFriendDecl(FriendDecl *D) { // Import the major distinguishing characteristics of a declaration. DeclContext *DC, *LexicalDC; @@ -3641,9 +3665,25 @@ // Determine whether we've already imported this decl. // FriendDecl is not a NamedDecl so we cannot use lookup. auto *RD = cast(DC); - FriendDecl *ImportedFriend = RD->getFirstFriend(); - while (ImportedFriend) { + for (FriendDecl *ImportedFriend = RD->getFirstFriend(); ImportedFriend; + ImportedFriend = ImportedFriend->getNextFriend()) { + + // Compare the semantic DeclContext of the underlying declarations of the + // existing and the to be imported friend. + // Normally, lookup ensures this, but with friends we cannot use the lookup. + Optional ImportedFriendDC = + getDCOfUnderlyingDecl(ImportedFriend); + Optional FromFriendDC = getDCOfUnderlyingDecl(D); + if (FromFriendDC) { // The underlying friend type is not dependent. + ExpectedDecl FriendDCDeclOrErr = import(cast(*FromFriendDC)); + if (!FriendDCDeclOrErr) + return FriendDCDeclOrErr.takeError(); + DeclContext *FriendDC = cast(*FriendDCDeclOrErr); + if (ImportedFriendDC != FriendDC) + continue; + } + if (D->getFriendDecl() && ImportedFriend->getFriendDecl()) { if (IsStructuralMatch(D->getFriendDecl(), ImportedFriend->getFriendDecl(), /*Complain=*/false)) @@ -3655,7 +3695,6 @@ ImportedFriend->getFriendType()->getType(), true)) return Importer.MapImported(D, ImportedFriend); } - ImportedFriend = ImportedFriend->getNextFriend(); } // Not found. Create it. diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp --- a/clang/unittests/AST/ASTImporterTest.cpp +++ b/clang/unittests/AST/ASTImporterTest.cpp @@ -4023,6 +4023,35 @@ EXPECT_EQ(ImportedFoo, ToFoo); } +TEST_P(ImportFriendClasses, ImportOfInlineFriendClassAfterFwdWithSameName) { + auto Code = + R"( + class Container { + friend class X; // A friend class ::X + class X{ }; + friend class X; // Friend class Container::X + }; + )"; + Decl *FromTu = getTuDecl(Code, Lang_CXX, "from.cc"); + + auto *To = Import(FirstDeclMatcher().match( + FromTu, cxxRecordDecl(hasName("Container"))), + Lang_CXX); + ASSERT_TRUE(To); + + Decl *ToTu = To->getTranslationUnitDecl(); + auto *ToFriend1 = FirstDeclMatcher().match(ToTu, friendDecl()); + auto *ToFriend2 = LastDeclMatcher().match(ToTu, friendDecl()); + auto *ToX = FirstDeclMatcher().match( + ToTu, cxxRecordDecl(hasName("X"), unless(isImplicit()))); + + EXPECT_NE(ToFriend1, ToFriend2); + const RecordDecl *RecordOfFriend1 = getRecordDeclOfFriend(ToFriend1); + const RecordDecl *RecordOfFriend2 = getRecordDeclOfFriend(ToFriend2); + EXPECT_NE(RecordOfFriend1, ToX); + EXPECT_EQ(RecordOfFriend2, ToX); +} + struct DeclContextTest : ASTImporterOptionSpecificTestBase {}; TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {