Index: unittests/AST/ASTImporterTest.cpp =================================================================== --- unittests/AST/ASTImporterTest.cpp +++ unittests/AST/ASTImporterTest.cpp @@ -2241,6 +2241,217 @@ EXPECT_EQ(ImportedD1->getPreviousDecl(), ImportedD); } +TEST_P(ImportFriendFunctions, Lookup) { + auto Pattern = functionDecl(hasName("f")); + + Decl *FromTU = + getTuDecl("struct X { friend void f(); };", Lang_CXX, "input0.cc"); + auto FromD = FirstDeclMatcher().match(FromTU, Pattern); + ASSERT_TRUE(FromD->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(!FromD->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + auto FromName = FromD->getDeclName(); + { + CXXRecordDecl *Class = + FirstDeclMatcher().match(FromTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 0u); + lookup_res = cast(FromTU)->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 1u); + } + + auto ToD = cast(Import(FromD, Lang_CXX)); + auto ToName = ToD->getDeclName(); + + { + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + CXXRecordDecl *Class = + FirstDeclMatcher().match(ToTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 0u); + lookup_res = cast(ToTU)->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 1u); + } + + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 1u); + auto To0 = FirstDeclMatcher().match(ToTU, Pattern); + EXPECT_TRUE(To0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + EXPECT_TRUE(!To0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); +} + +TEST_P(ImportFriendFunctions, DISABLED_LookupWithProto) { + auto Pattern = functionDecl(hasName("f")); + + Decl *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, Pattern); + auto From1 = LastDeclMatcher().match(FromTU, Pattern); + ASSERT_TRUE(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 FromName = From0->getDeclName(); + { + CXXRecordDecl *Class = + FirstDeclMatcher().match(FromTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 0u); + lookup_res = cast(FromTU)->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 1u); + } + + auto To0 = cast(Import(From0, Lang_CXX)); + auto ToName = To0->getDeclName(); + + { + auto ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + CXXRecordDecl *Class = + FirstDeclMatcher().match(ToTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 0u); + lookup_res = ToTU->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 1u); + } + + auto ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); + To0 = FirstDeclMatcher().match(ToTU, Pattern); + auto To1 = LastDeclMatcher().match(ToTU, Pattern); + EXPECT_TRUE(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, LookupWithProtoFirst) { + auto Pattern = functionDecl(hasName("f")); + + Decl *FromTU = + getTuDecl("void f();" + "struct X { friend void f(); };", + Lang_CXX, "input0.cc"); + auto From0 = FirstDeclMatcher().match(FromTU, Pattern); + auto From1 = LastDeclMatcher().match(FromTU, Pattern); + ASSERT_TRUE(!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 FromName = From0->getDeclName(); + { + CXXRecordDecl *Class = + FirstDeclMatcher().match(FromTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 0u); + lookup_res = cast(FromTU)->noload_lookup(FromName); + ASSERT_EQ(lookup_res.size(), 1u); + } + + auto To0 = cast(Import(From0, Lang_CXX)); + auto ToName = To0->getDeclName(); + Decl *ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + + { + CXXRecordDecl *Class = + FirstDeclMatcher().match(ToTU, cxxRecordDecl()); + auto lookup_res = Class->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 0u); + lookup_res = cast(ToTU)->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 1u); + } + + ASSERT_EQ(DeclCounter().match(ToTU, Pattern), 2u); + To0 = FirstDeclMatcher().match(ToTU, Pattern); + auto To1 = LastDeclMatcher().match(ToTU, Pattern); + EXPECT_TRUE(!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")); + + Decl *FromTU0 = getTuDecl("void f();", Lang_CXX, "input0.cc"); + FunctionDecl *FromD0 = + FirstDeclMatcher().match(FromTU0, Pattern); + Decl *FromTU1 = + getTuDecl("class X { friend void f(); };", Lang_CXX, "input1.cc"); + FunctionDecl *FromD1 = + FirstDeclMatcher().match(FromTU1, Pattern); + auto FromName0 = FromD0->getDeclName(); + auto FromName1 = FromD1->getDeclName(); + + ASSERT_TRUE(FromD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(!FromD0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + ASSERT_TRUE(!FromD1->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + ASSERT_TRUE(FromD1->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + auto lookup_res = cast(FromTU0)->noload_lookup(FromName0); + ASSERT_EQ(lookup_res.size(), 1u); + lookup_res = cast(FromTU1)->noload_lookup(FromName1); + ASSERT_EQ(lookup_res.size(), 1u); + + FunctionDecl *ToD0 = cast(Import(FromD0, Lang_CXX)); + auto ToTU = ToAST->getASTContext().getTranslationUnitDecl(); + auto ToName = ToD0->getDeclName(); + EXPECT_TRUE(ToD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(!ToD0->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend)); + lookup_res = cast(ToTU)->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 1u); + + FunctionDecl *ToD1 = cast(Import(FromD1, Lang_CXX)); + lookup_res = cast(ToTU)->noload_lookup(ToName); + EXPECT_EQ(lookup_res.size(), 1u); + EXPECT_EQ(DeclCounter().match(ToTU, Pattern), 2u); + + EXPECT_TRUE(ToD0->isInIdentifierNamespace(Decl::IDNS_Ordinary)); + EXPECT_TRUE(!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")); + + Decl *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); + auto *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; + } + ASSERT_EQ(FrN, 1u); +} + AST_MATCHER_P(TagDecl, hasTypedefForAnonDecl, Matcher, InnerMatcher) { if (auto *Typedef = Node.getTypedefNameForAnonDecl())