diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1549,10 +1549,15 @@ DEF_TRAVERSE_DECL(FriendDecl, { // Friend is either decl or a type. - if (D->getFriendType()) + if (D->getFriendType()) { TRY_TO(TraverseTypeLoc(D->getFriendType()->getTypeLoc())); - else + // Traverse any CXXRecordDecl owned by this type, since + // it will not be in the parent context: + if (auto *ET = D->getFriendType()->getType()->getAs()) + TRY_TO(TraverseDecl(ET->getOwnedTagDecl())); + } else { TRY_TO(TraverseDecl(D->getFriendDecl())); + } }) DEF_TRAVERSE_DECL(FriendTemplateDecl, { diff --git a/clang/unittests/AST/ASTContextParentMapTest.cpp b/clang/unittests/AST/ASTContextParentMapTest.cpp --- a/clang/unittests/AST/ASTContextParentMapTest.cpp +++ b/clang/unittests/AST/ASTContextParentMapTest.cpp @@ -119,5 +119,34 @@ Lang_CXX11)); } +TEST(GetParents, FriendTypeLoc) { + auto AST = tooling::buildASTFromCode("struct A { friend struct Fr; };" + "struct B { friend struct Fr; };" + "struct Fr;"); + auto &Ctx = AST->getASTContext(); + auto &TU = *Ctx.getTranslationUnitDecl(); + auto &A = *TU.lookup(&Ctx.Idents.get("A")).front(); + auto &B = *TU.lookup(&Ctx.Idents.get("B")).front(); + auto &FrA = *cast(*++(cast(A).decls_begin())); + auto &FrB = *cast(*++(cast(B).decls_begin())); + TypeLoc FrALoc = FrA.getFriendType()->getTypeLoc(); + TypeLoc FrBLoc = FrB.getFriendType()->getTypeLoc(); + TagDecl *FrATagDecl = + FrALoc.getTypePtr()->getAs()->getOwnedTagDecl(); + TagDecl *FrBTagDecl = + FrBLoc.getTypePtr()->getAs()->getOwnedTagDecl(); + + EXPECT_THAT(Ctx.getParents(A), ElementsAre(DynTypedNode::create(TU))); + EXPECT_THAT(Ctx.getParents(B), ElementsAre(DynTypedNode::create(TU))); + EXPECT_THAT(Ctx.getParents(FrA), ElementsAre(DynTypedNode::create(A))); + EXPECT_THAT(Ctx.getParents(FrB), ElementsAre(DynTypedNode::create(B))); + EXPECT_THAT(Ctx.getParents(FrALoc), ElementsAre(DynTypedNode::create(FrA))); + EXPECT_THAT(Ctx.getParents(FrBLoc), ElementsAre(DynTypedNode::create(FrB))); + EXPECT_TRUE(FrATagDecl); + EXPECT_FALSE(FrBTagDecl); + EXPECT_THAT(Ctx.getParents(*FrATagDecl), + ElementsAre(DynTypedNode::create(FrA))); +} + } // end namespace ast_matchers } // end namespace clang