Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -2274,6 +2274,211 @@ EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); } +TEST_P(ImportFriendFunctions, Lookup) { + auto FunctionPattern = functionDecl(hasName("f")); + auto ClassPattern = cxxRecordDecl(hasName("X")); + + TranslationUnitDecl *FromTU = + getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher().match(FromTU, FunctionPattern); + ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_FALSE(FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + { + auto FromName = FromD->getDeclName(); + auto *Class = FirstDeclMatcher().match(FromTU, ClassPattern); + auto LookupRes = Class->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 0u); + LookupRes = FromTU->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 1u); + } + + auto *ToD = cast(Import(FromD, Lang_CXX)); + auto ToName = ToD->getDeclName(); + + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto *Class = FirstDeclMatcher().match(ToTU, ClassPattern); + auto LookupRes = Class->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 0u); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + + EXPECT_EQ(DeclCounter().match(ToTU, FunctionPattern), 1u); + auto *To0 = FirstDeclMatcher().match(ToTU, FunctionPattern); + EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); +} + +TEST_P(ImportFriendFunctions, DISABLED_LookupWithProtoAfter) { + auto FunctionPattern = functionDecl(hasName("f")); + auto ClassPattern = cxxRecordDecl(hasName("X")); + + TranslationUnitDecl *FromTU = getTuDecl( + "struct X { friend void f(); };" + // This proto decl makes f available to normal + // lookup, otherwise it is hidden. + // Normal C++ lookup (implemented in + // `clang::Sema::CppLookupName()` and in `LookupDirect()`) + // returns the found `NamedDecl` only if the set IDNS is matched + "void f();", + Lang_CXX, "input0.cc"); + auto *FromFriend = + FirstDeclMatcher().match(FromTU, FunctionPattern); + auto *FromNormal = + LastDeclMatcher().match(FromTU, FunctionPattern); + ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_FALSE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + + auto FromName = FromFriend->getDeclName(); + auto *FromClass = + FirstDeclMatcher().match(FromTU, ClassPattern); + auto LookupRes = FromClass->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 0u); + LookupRes = FromTU->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 1u); + + auto *ToFriend = cast(Import(FromFriend, Lang_CXX)); + auto ToName = ToFriend->getDeclName(); + + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto *ToClass = FirstDeclMatcher().match(ToTU, ClassPattern); + LookupRes = ToClass->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 0u); + LookupRes = ToTU->noload_lookup(ToName); + // Test is disabled because this result is 2. + EXPECT_EQ(LookupRes.size(), 1u); + + ASSERT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); + ToFriend = FirstDeclMatcher().match(ToTU, FunctionPattern); + auto *ToNormal = LastDeclMatcher().match(ToTU, FunctionPattern); + EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_FALSE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); +} + +TEST_P(ImportFriendFunctions, LookupWithProtoBefore) { + auto FunctionPattern = functionDecl(hasName("f")); + auto ClassPattern = cxxRecordDecl(hasName("X")); + + TranslationUnitDecl *FromTU = getTuDecl( + "void f();" + "struct X { friend void f(); };", + Lang_CXX, "input0.cc"); + auto *FromNormal = + FirstDeclMatcher().match(FromTU, FunctionPattern); + auto *FromFriend = + LastDeclMatcher().match(FromTU, FunctionPattern); + ASSERT_FALSE(FromNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(FromNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(FromFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + + auto FromName = FromNormal->getDeclName(); + auto *FromClass = + FirstDeclMatcher().match(FromTU, ClassPattern); + auto LookupRes = FromClass->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 0u); + LookupRes = FromTU->noload_lookup(FromName); + ASSERT_EQ(LookupRes.size(), 1u); + + auto *ToNormal = cast(Import(FromNormal, Lang_CXX)); + auto ToName = ToNormal->getDeclName(); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + auto *ToClass = FirstDeclMatcher().match(ToTU, ClassPattern); + LookupRes = ToClass->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 0u); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + + EXPECT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); + ToNormal = FirstDeclMatcher().match(ToTU, FunctionPattern); + auto *ToFriend = LastDeclMatcher().match(ToTU, FunctionPattern); + EXPECT_FALSE(ToNormal->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(ToNormal->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(ToFriend->isInIdentifierNamespace(Decl::IDNS_Ordinary)); +} + +TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) { + auto Pattern = functionDecl(hasName("f")); + + TranslationUnitDecl *FromNormalTU = + getTuDecl("void f();", Lang_CXX, "input0.cc"); + auto *FromNormalF = + FirstDeclMatcher().match(FromNormalTU, Pattern); + TranslationUnitDecl *FromFriendTU = + getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc"); + auto *FromFriendF = + FirstDeclMatcher().match(FromFriendTU, Pattern); + auto FromNormalName = FromNormalF->getDeclName(); + auto FromFriendName = FromFriendF->getDeclName(); + + ASSERT_TRUE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_FALSE(FromNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_FALSE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(FromFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + auto LookupRes = FromNormalTU->noload_lookup(FromNormalName); + ASSERT_EQ(LookupRes.size(), 1u); + LookupRes = FromFriendTU->noload_lookup(FromFriendName); + ASSERT_EQ(LookupRes.size(), 1u); + + auto *ToNormalF = cast(Import(FromNormalF, Lang_CXX)); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto ToName = ToNormalF->getDeclName(); + EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); + + auto *ToFriendF = cast(Import(FromFriendF, Lang_CXX)); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); + + EXPECT_TRUE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(ToNormalF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + + EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(ToFriendF->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); +} + +TEST_P(ImportFriendFunctions, ImportFriendList) { + TranslationUnitDecl *FromTU = getTuDecl( + "struct X { friend void f(); };" + "void f();", + Lang_CXX, "input0.cc"); + auto *FromFriendF = FirstDeclMatcher().match( + FromTU, functionDecl(hasName("f"))); + + auto *FromClass = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("X"))); + auto *FromFriend = FirstDeclMatcher().match(FromTU, friendDecl()); + auto FromFriends = FromClass->friends(); + unsigned int FrN = 0; + for (auto Fr : FromFriends) { + ASSERT_EQ(Fr, FromFriend); + ++FrN; + } + ASSERT_EQ(FrN, 1u); + + Import(FromFriendF, Lang_CXX); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto *ToClass = FirstDeclMatcher().match( + ToTU, cxxRecordDecl(hasName("X"))); + auto *ToFriend = FirstDeclMatcher().match(ToTU, friendDecl()); + auto ToFriends = ToClass->friends(); + FrN = 0; + for (auto Fr : ToFriends) { + EXPECT_EQ(Fr, ToFriend); + ++FrN; + } + EXPECT_EQ(FrN, 1u); +} + AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher, InnerMatcher) { if (auto *Typedef = Node.getTypedefNameForAnonDecl())