Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -2275,6 +2275,205 @@ EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); } +TEST_P(ImportFriendFunctions, Lookup) { + auto FunctionPattern = functionDecl(hasName("f")); + auto ClassPattern = cxxRecordDecl(hasName("X")); + + Decl *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 = cast(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 *From0 = FirstDeclMatcher().match(FromTU, FunctionPattern); + auto *From1 = LastDeclMatcher().match(FromTU, FunctionPattern); + ASSERT_TRUE(From0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_FALSE(From0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_FALSE(From1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(From1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + { + auto Name = From0->getDeclName(); + auto *Class = FirstDeclMatcher().match(FromTU, ClassPattern); + auto LookupRes = Class->noload_lookup(Name); + ASSERT_EQ(LookupRes.size(), 0u); + LookupRes = FromTU->noload_lookup(Name); + ASSERT_EQ(LookupRes.size(), 1u); + } + + auto *To0 = cast(Import(From0, Lang_CXX)); + auto Name = To0->getDeclName(); + + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto *Class = FirstDeclMatcher().match(ToTU, ClassPattern); + auto LookupRes = Class->noload_lookup(Name); + EXPECT_EQ(LookupRes.size(), 0u); + LookupRes = ToTU->noload_lookup(Name); + // Test is disabled because this result is 2. + EXPECT_EQ(LookupRes.size(), 1u); + + ASSERT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); + To0 = FirstDeclMatcher().match(ToTU, FunctionPattern); + auto *To1 = LastDeclMatcher().match(ToTU, FunctionPattern); + EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(To1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(To1->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 *From0 = FirstDeclMatcher().match(FromTU, FunctionPattern); + auto *From1 = LastDeclMatcher().match(FromTU, FunctionPattern); + ASSERT_FALSE(From0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(From0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(From1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(From1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + { + auto Name = From0->getDeclName(); + auto *Class = FirstDeclMatcher().match(FromTU, ClassPattern); + auto LookupRes = Class->noload_lookup(Name); + ASSERT_EQ(LookupRes.size(), 0u); + LookupRes = FromTU->noload_lookup(Name); + ASSERT_EQ(LookupRes.size(), 1u); + } + + auto *To0 = cast(Import(From0, Lang_CXX)); + auto Name = To0->getDeclName(); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + auto *Class = FirstDeclMatcher().match(ToTU, ClassPattern); + auto LookupRes = Class->noload_lookup(Name); + EXPECT_EQ(LookupRes.size(), 0u); + LookupRes = ToTU->noload_lookup(Name); + EXPECT_EQ(LookupRes.size(), 1u); + + EXPECT_EQ(DeclCounter().match(ToTU, FunctionPattern), 2u); + To0 = FirstDeclMatcher().match(ToTU, FunctionPattern); + auto *To1 = LastDeclMatcher().match(ToTU, FunctionPattern); + EXPECT_FALSE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(To1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(To1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); +} + +TEST_P(ImportFriendFunctions, ImportFriendChangesLookup) { + auto Pattern = functionDecl(hasName("f")); + + TranslationUnitDecl *FromTU0 = getTuDecl("void f();", Lang_CXX, "input0.cc"); + auto *FromD0 = FirstDeclMatcher().match(FromTU0, Pattern); + TranslationUnitDecl *FromTU1 = + getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc"); + auto *FromD1 = FirstDeclMatcher().match(FromTU1, Pattern); + auto FromName0 = FromD0->getDeclName(); + auto FromName1 = FromD1->getDeclName(); + + ASSERT_TRUE(FromD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_FALSE(FromD0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_FALSE(FromD1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(FromD1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + auto LookupRes = FromTU0->noload_lookup(FromName0); + ASSERT_EQ(LookupRes.size(), 1u); + LookupRes = FromTU1->noload_lookup(FromName1); + ASSERT_EQ(LookupRes.size(), 1u); + + auto *ToD0 = cast(Import(FromD0, Lang_CXX)); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto ToName = ToD0->getDeclName(); + EXPECT_TRUE(ToD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(ToD0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); + + auto *ToD1 = cast(Import(FromD1, Lang_CXX)); + LookupRes = ToTU->noload_lookup(ToName); + EXPECT_EQ(LookupRes.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); + + EXPECT_TRUE(ToD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_FALSE(ToD0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + + EXPECT_TRUE(ToD1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(ToD1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); +} + +TEST_P(ImportFriendFunctions, ImportFriendList) { + auto Pattern = functionDecl(hasName("f")); + + TranslationUnitDecl *FromTU = getTuDecl( + "struct X { friend void f(); };" + "void f();", + Lang_CXX, "input0.cc"); + auto *FromD = FirstDeclMatcher().match(FromTU, Pattern); + { + auto *Class = FirstDeclMatcher().match( + FromTU, cxxRecordDecl(hasName("X"))); + auto *Friend = FirstDeclMatcher().match(FromTU, friendDecl()); + auto Friends = Class->friends(); + unsigned int FrN = 0; + for (auto Fr : Friends) { + ASSERT_EQ(Fr, Friend); + ++FrN; + } + ASSERT_EQ(FrN, 1u); + } + Import(FromD, Lang_CXX); + TranslationUnitDecl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto *Class = FirstDeclMatcher().match( + ToTU, cxxRecordDecl(hasName("X"))); + auto *Friend = FirstDeclMatcher().match(ToTU, friendDecl()); + auto Friends = Class->friends(); + unsigned int FrN = 0; + for (auto Fr : Friends) { + EXPECT_EQ(Fr, Friend); + ++FrN; + } + EXPECT_EQ(FrN, 1u); +} + AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher, InnerMatcher) { if (auto *Typedef = Node.getTypedefNameForAnonDecl())