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 @@ -2256,7 +2256,8 @@ if (!FromUT->isIncompleteType() && !FoundUT->isIncompleteType()) return Importer.MapImported(D, FoundTypedef); } - // FIXME Handle redecl chain. + // FIXME Handle redecl chain. When you do that make consistent changes + // in ASTImporterLookupTable too. break; } diff --git a/clang/lib/AST/ASTImporterLookupTable.cpp b/clang/lib/AST/ASTImporterLookupTable.cpp --- a/clang/lib/AST/ASTImporterLookupTable.cpp +++ b/clang/lib/AST/ASTImporterLookupTable.cpp @@ -26,17 +26,30 @@ LT.add(D); return true; } + // In most cases the FriendDecl contains the declaration of the befriended + // class as a child node, so it is discovered during the recursive + // visitation. However, there are cases when the befriended class is not a + // child, thus it must be fetched explicitly from the FriendDecl, and only + // then can we add it to the lookup table. bool VisitFriendDecl(FriendDecl *D) { if (D->getFriendType()) { QualType Ty = D->getFriendType()->getType(); - // FIXME Can this be other than elaborated? - QualType NamedTy = cast(Ty)->getNamedType(); - if (!NamedTy->isDependentType()) { - if (const auto *RTy = dyn_cast(NamedTy)) + if (isa(Ty)) + Ty = cast(Ty)->getNamedType(); + // A FriendDecl with a dependent type (e.g. ClassTemplateSpecialization) + // always has that decl as child node. + // However, there are non-dependent cases which does not have the + // type as a child node. We have to dig up that type now. + if (!Ty->isDependentType()) { + if (const auto *RTy = dyn_cast(Ty)) LT.add(RTy->getAsCXXRecordDecl()); - else if (const auto *SpecTy = - dyn_cast(NamedTy)) { + else if (const auto *SpecTy = dyn_cast(Ty)) LT.add(SpecTy->getAsCXXRecordDecl()); + else if (isa(Ty)) { + // We do not put friend typedefs to the lookup table because + // ASTImporter does not organize typedefs into redecl chains. + } else { + llvm_unreachable("Unhandled type of friend class"); } } } 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 @@ -4246,13 +4246,27 @@ ASSERT_EQ(Res.size(), 0u); } +static QualType getUnderlyingType(const TypedefType *TDT) { + QualType T = TDT->getDecl()->getUnderlyingType(); + + if (const auto *Inner = dyn_cast(T.getTypePtr())) + return getUnderlyingType(Inner); + + return T; +} + static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) { QualType Ty = FD->getFriendType()->getType(); - QualType NamedTy = cast(Ty)->getNamedType(); - return cast(NamedTy)->getDecl(); + if (auto *Inner = dyn_cast(Ty.getTypePtr())) { + Ty = getUnderlyingType(Inner); + } + if (isa(Ty)) + Ty = cast(Ty)->getNamedType(); + return cast(Ty)->getDecl(); } -TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) { +TEST_P(ASTImporterLookupTableTest, + LookupFindsFwdFriendClassDeclWithElaboratedType) { TranslationUnitDecl *ToTU = getToTuDecl( R"( class Y { friend class F; }; @@ -4276,6 +4290,52 @@ EXPECT_EQ(Res.size(), 0u); } +TEST_P(ASTImporterLookupTableTest, + LookupFindsFwdFriendClassDeclWithUnelaboratedType) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class F; + class Y { friend F; }; + )", + Lang_CXX11); + + // In this case, the CXXRecordDecl is hidden, the FriendDecl is not a parent. + // So we must dig up the underlying CXXRecordDecl. + ASTImporterLookupTable LT(*ToTU); + auto *FriendD = FirstDeclMatcher().match(ToTU, friendDecl()); + const RecordDecl *RD = getRecordDeclOfFriend(FriendD); + auto *Y = FirstDeclMatcher().match(ToTU, cxxRecordDecl(hasName("Y"))); + + DeclarationName Name = RD->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.size(), 1u); + EXPECT_EQ(*Res.begin(), RD); + + Res = LT.lookup(Y, Name); + EXPECT_EQ(Res.size(), 0u); +} + +TEST_P(ASTImporterLookupTableTest, + LookupFindsFriendClassDeclWithTypeAliasDoesNotAssert) { + TranslationUnitDecl *ToTU = getToTuDecl( + R"( + class F; + using alias_of_f = F; + class Y { friend alias_of_f; }; + )", + Lang_CXX11); + + // ASTImporterLookupTable constructor handles using declarations correctly, + // no assert is expected. + ASTImporterLookupTable LT(*ToTU); + + auto *Alias = FirstDeclMatcher().match( + ToTU, typeAliasDecl(hasName("alias_of_f"))); + DeclarationName Name = Alias->getDeclName(); + auto Res = LT.lookup(ToTU, Name); + EXPECT_EQ(Res.count(Alias), 1u); +} + TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassTemplateDecl) { TranslationUnitDecl *ToTU = getToTuDecl( R"(