[ASTImporter] Add importer specific lookup

Summary:
There are certain cases when normal C/C++ lookup (localUncachedLookup)
does not find AST nodes. E.g.:

Example 1:

  template <class T>
  struct X {
    friend void foo(); // this is never found in the DC of the TU.
  };

Example 2:

  // The fwd decl to Foo is not found in the lookupPtr of the DC of the
  // translation unit decl.
  struct A { struct Foo *p; };

In these cases we create a new node instead of returning with the old one.
To fix it we create a new lookup table which holds every node and we are
not interested in any C++ specific visibility considerations.
Simply, we must know if there is an existing Decl in a given DC.

Reviewers: a_sidorin, a.sidorin

Subscribers: mgorny, rnkovacs, dkrupp, Szelethus, cfe-commits

Differential Revision: https://reviews.llvm.org/D53708

llvm-svn: 349351
diff --git a/clang/unittests/AST/ASTImporterTest.cpp b/clang/unittests/AST/ASTImporterTest.cpp
index 3407f7d..aaadeb0 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -15,6 +15,8 @@
 #include "MatchVerifier.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclContextInternals.h"
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Tooling/Tooling.h"
@@ -308,24 +310,27 @@
       Unit->enableSourceFileDiagnostics();
     }
 
-    void lazyInitImporter(ASTUnit *ToAST) {
+    void lazyInitImporter(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST) {
       assert(ToAST);
       if (!Importer) {
-        Importer.reset(new ASTImporter(
-            ToAST->getASTContext(), ToAST->getFileManager(),
-            Unit->getASTContext(), Unit->getFileManager(), false));
+        Importer.reset(
+            new ASTImporter(ToAST->getASTContext(), ToAST->getFileManager(),
+                            Unit->getASTContext(), Unit->getFileManager(),
+                            false, &LookupTable));
       }
       assert(&ToAST->getASTContext() == &Importer->getToContext());
       createVirtualFileIfNeeded(ToAST, FileName, Code);
     }
 
-    Decl *import(ASTUnit *ToAST, Decl *FromDecl) {
-      lazyInitImporter(ToAST);
+    Decl *import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+                 Decl *FromDecl) {
+      lazyInitImporter(LookupTable, ToAST);
       return Importer->Import(FromDecl);
-     }
+    }
 
-    QualType import(ASTUnit *ToAST, QualType FromType) {
-      lazyInitImporter(ToAST);
+    QualType import(ASTImporterLookupTable &LookupTable, ASTUnit *ToAST,
+                    QualType FromType) {
+      lazyInitImporter(LookupTable, ToAST);
       return Importer->Import(FromType);
     }
   };
@@ -339,13 +344,23 @@
   // vector is expanding, with the list we won't have these issues.
   std::list<TU> FromTUs;
 
-  void lazyInitToAST(Language ToLang) {
+  // Initialize the lookup table if not initialized already.
+  void lazyInitLookupTable(TranslationUnitDecl *ToTU) {
+    assert(ToTU);
+    if (!LookupTablePtr)
+      LookupTablePtr = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+  }
+
+  void lazyInitToAST(Language ToLang, StringRef ToSrcCode, StringRef FileName) {
     if (ToAST)
       return;
     ArgVector ToArgs = getArgVectorForLanguage(ToLang);
+    // Source code must be a valid live buffer through the tests lifetime.
+    ToCode = ToSrcCode;
     // Build the AST from an empty file.
-    ToAST = tooling::buildASTFromCodeWithArgs(/*Code=*/"", ToArgs, "empty.cc");
+    ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, FileName);
     ToAST->enableSourceFileDiagnostics();
+    lazyInitLookupTable(ToAST->getASTContext().getTranslationUnitDecl());
   }
 
   TU *findFromTU(Decl *From) {
@@ -359,6 +374,10 @@
     return &*It;
   }
 
+protected:
+
+  std::unique_ptr<ASTImporterLookupTable> LookupTablePtr;
+
 public:
   // We may have several From context but only one To context.
   std::unique_ptr<ASTUnit> ToAST;
@@ -375,26 +394,23 @@
     FromTUs.emplace_back(FromSrcCode, InputFileName, FromArgs);
     TU &FromTU = FromTUs.back();
 
-    ToCode = ToSrcCode;
     assert(!ToAST);
-    ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
-    ToAST->enableSourceFileDiagnostics();
+    lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
 
     ASTContext &FromCtx = FromTU.Unit->getASTContext();
 
-    createVirtualFileIfNeeded(ToAST.get(), InputFileName, FromTU.Code);
-
     IdentifierInfo *ImportedII = &FromCtx.Idents.get(Identifier);
     assert(ImportedII && "Declaration with the given identifier "
                          "should be specified in test!");
     DeclarationName ImportDeclName(ImportedII);
-    SmallVector<NamedDecl *, 4> FoundDecls;
+    SmallVector<NamedDecl *, 1> FoundDecls;
     FromCtx.getTranslationUnitDecl()->localUncachedLookup(ImportDeclName,
                                                           FoundDecls);
 
     assert(FoundDecls.size() == 1);
 
-    Decl *Imported = FromTU.import(ToAST.get(), FoundDecls.front());
+    Decl *Imported =
+        FromTU.import(*LookupTablePtr, ToAST.get(), FoundDecls.front());
 
     assert(Imported);
     return std::make_tuple(*FoundDecls.begin(), Imported);
@@ -420,11 +436,8 @@
   // Creates the To context with the given source code and returns the TU decl.
   TranslationUnitDecl *getToTuDecl(StringRef ToSrcCode, Language ToLang) {
     ArgVector ToArgs = getArgVectorForLanguage(ToLang);
-    ToCode = ToSrcCode;
     assert(!ToAST);
-    ToAST = tooling::buildASTFromCodeWithArgs(ToCode, ToArgs, OutputFileName);
-    ToAST->enableSourceFileDiagnostics();
-
+    lazyInitToAST(ToLang, ToSrcCode, OutputFileName);
     return ToAST->getASTContext().getTranslationUnitDecl();
   }
 
@@ -432,15 +445,17 @@
   // May be called several times in a given test.
   // The different instances of the param From may have different ASTContext.
   Decl *Import(Decl *From, Language ToLang) {
-    lazyInitToAST(ToLang);
+    lazyInitToAST(ToLang, "", OutputFileName);
     TU *FromTU = findFromTU(From);
-    return FromTU->import(ToAST.get(), From);
+    assert(LookupTablePtr);
+    return FromTU->import(*LookupTablePtr, ToAST.get(), From);
   }
 
   QualType ImportType(QualType FromType, Decl *TUDecl, Language ToLang) {
-    lazyInitToAST(ToLang);
+    lazyInitToAST(ToLang, "", OutputFileName);
     TU *FromTU = findFromTU(TUDecl);
-    return FromTU->import(ToAST.get(), FromType);
+    assert(LookupTablePtr);
+    return FromTU->import(*LookupTablePtr, ToAST.get(), FromType);
    }
 
   ~ASTImporterTestBase() {
@@ -2727,6 +2742,7 @@
       CXXMethodDecl *Method =
           FirstDeclMatcher<CXXMethodDecl>().match(ToClass, MethodMatcher);
       ToClass->removeDecl(Method);
+      LookupTablePtr->remove(Method);
     }
 
     ASSERT_EQ(DeclCounter<CXXMethodDecl>().match(ToClass, MethodMatcher), 0u);
@@ -3486,6 +3502,82 @@
   EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
 }
 
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInToContext) {
+  Decl *ToTU = getToTuDecl("struct X;", Lang_C);
+  Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedDef = Import(FromDef, Lang_C);
+
+  EXPECT_NE(ImportedDef, ToProto);
+  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContext) {
+  Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_C);
+  Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedDef = Import(FromDef, Lang_C);
+
+  EXPECT_NE(ImportedDef, ToProto);
+  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportDefinitionWhenProtoIsInNestedToContextCXX) {
+  Decl *ToTU = getToTuDecl("struct A { struct X *Xp; };", Lang_CXX);
+  Decl *FromTU1 = getTuDecl("struct X {};", Lang_CXX, "input1.cc");
+  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedDef = Import(FromDef, Lang_CXX);
+
+  EXPECT_NE(ImportedDef, ToProto);
+  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
+TEST_P(ImportClasses, ImportNestedPrototypeThenDefinition) {
+  Decl *FromTU0 = getTuDecl("struct A { struct X *Xp; };", Lang_C, "input0.cc");
+  Decl *FromTU1 = getTuDecl("struct X {};", Lang_C, "input1.cc");
+  auto Pattern = recordDecl(hasName("X"), unless(isImplicit()));
+  auto FromProto = FirstDeclMatcher<RecordDecl>().match(FromTU0, Pattern);
+  auto FromDef = FirstDeclMatcher<RecordDecl>().match(FromTU1, Pattern);
+
+  Decl *ImportedProto = Import(FromProto, Lang_C);
+  Decl *ImportedDef = Import(FromDef, Lang_C);
+  Decl *ToTU = ImportedDef->getTranslationUnitDecl();
+
+  EXPECT_NE(ImportedDef, ImportedProto);
+  EXPECT_EQ(DeclCounter<RecordDecl>().match(ToTU, Pattern), 2u);
+  auto ToProto = FirstDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  auto ToDef = LastDeclMatcher<RecordDecl>().match(ToTU, Pattern);
+  EXPECT_TRUE(ImportedDef == ToDef);
+  EXPECT_TRUE(ImportedProto == ToProto);
+  EXPECT_TRUE(ToDef->isThisDeclarationADefinition());
+  EXPECT_FALSE(ToProto->isThisDeclarationADefinition());
+  EXPECT_EQ(ToDef->getPreviousDecl(), ToProto);
+}
+
 struct ImportClassTemplates : ASTImporterTestBase {};
 
 TEST_P(ImportClassTemplates,
@@ -3890,6 +3982,24 @@
   EXPECT_EQ(ImportedFwd, ImportedDef->getPreviousDecl());
 }
 
+TEST_P(ASTImporterTestBase, FriendFunInClassTemplate) {
+  auto *Code = R"(
+  template <class T>
+  struct X {
+    friend void foo(){}
+  };
+      )";
+  TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+  auto *ToFoo = FirstDeclMatcher<FunctionDecl>().match(
+      ToTU, functionDecl(hasName("foo")));
+
+  TranslationUnitDecl *FromTU = getTuDecl(Code, Lang_CXX, "input.cc");
+  auto *FromFoo = FirstDeclMatcher<FunctionDecl>().match(
+      FromTU, functionDecl(hasName("foo")));
+  auto *ImportedFoo = Import(FromFoo, Lang_CXX);
+  EXPECT_EQ(ImportedFoo, ToFoo);
+}
+
 struct DeclContextTest : ASTImporterTestBase {};
 
 TEST_P(DeclContextTest, removeDeclOfClassTemplateSpecialization) {
@@ -4339,6 +4449,416 @@
   EXPECT_FALSE(ImportedD->getUnderlyingType()->isIncompleteType());
 }
 
+struct ASTImporterLookupTableTest : ASTImporterTestBase {};
+
+TEST_P(ASTImporterLookupTableTest, OneDecl) {
+  auto *ToTU = getToTuDecl("int a;", Lang_CXX);
+  auto *D = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("a")));
+  ASTImporterLookupTable LT(*ToTU);
+  auto Res = LT.lookup(ToTU, D->getDeclName());
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), D);
+}
+
+static Decl *findInDeclListOfDC(DeclContext *DC, DeclarationName Name) {
+  for (Decl *D : DC->decls()) {
+    if (auto *ND = dyn_cast<NamedDecl>(D))
+      if (ND->getDeclName() == Name)
+        return ND;
+  }
+  return nullptr;
+};
+
+TEST_P(ASTImporterLookupTableTest,
+    FriendWhichIsnotFoundByNormalLookupShouldBeFoundByImporterSpecificLookup) {
+  auto *Code = R"(
+  template <class T>
+  struct X {
+    friend void foo(){}
+  };
+      )";
+  TranslationUnitDecl *ToTU = getToTuDecl(Code, Lang_CXX);
+  auto *X = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("X")));
+  auto *Foo = FirstDeclMatcher<FunctionDecl>().match(
+      ToTU, functionDecl(hasName("foo")));
+  DeclContext *FooDC = Foo->getDeclContext();
+  DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+  ASSERT_EQ(cast<Decl>(FooLexicalDC), X->getTemplatedDecl());
+  ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+  DeclarationName FooName = Foo->getDeclName();
+
+  // Cannot find in the LookupTable of its DC (TUDecl)
+  SmallVector<NamedDecl *, 2> FoundDecls;
+  FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 0u);
+
+  // Cannot find in the LookupTable of its LexicalDC (X)
+  FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 0u);
+
+  // Can't find in the list of Decls of the DC.
+  EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+  // Can't find in the list of Decls of the LexicalDC
+  EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), nullptr);
+
+  // ASTImporter specific lookup finds it.
+  ASTImporterLookupTable LT(*ToTU);
+  auto Res = LT.lookup(FooDC, Foo->getDeclName());
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+       FwdDeclStructShouldBeFoundByImporterSpecificLookup) {
+  TranslationUnitDecl *ToTU =
+      getToTuDecl("struct A { struct Foo *p; };", Lang_C);
+  auto *Foo =
+      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("Foo")));
+  auto *A =
+      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+  DeclContext *FooDC = Foo->getDeclContext();
+  DeclContext *FooLexicalDC = Foo->getLexicalDeclContext();
+  ASSERT_EQ(cast<Decl>(FooLexicalDC), A);
+  ASSERT_EQ(cast<Decl>(FooDC), ToTU);
+  DeclarationName FooName = Foo->getDeclName();
+
+  // Cannot find in the LookupTable of its DC (TUDecl).
+  SmallVector<NamedDecl *, 2> FoundDecls;
+  FooDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 0u);
+
+  // Cannot find in the LookupTable of its LexicalDC (A).
+  FooLexicalDC->getRedeclContext()->localUncachedLookup(FooName, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 0u);
+
+  // Can't find in the list of Decls of the DC.
+  EXPECT_EQ(findInDeclListOfDC(FooDC, FooName), nullptr);
+
+  // Can find in the list of Decls of the LexicalDC.
+  EXPECT_EQ(findInDeclListOfDC(FooLexicalDC, FooName), Foo);
+
+  // ASTImporter specific lookup finds it.
+  ASTImporterLookupTable LT(*ToTU);
+  auto Res = LT.lookup(FooDC, Foo->getDeclName());
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), Foo);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsNamesInDifferentDC) {
+  TranslationUnitDecl *ToTU =
+      getToTuDecl("int V; struct A { int V; }; struct B { int V; };", Lang_C);
+  DeclarationName VName = FirstDeclMatcher<VarDecl>()
+                              .match(ToTU, varDecl(hasName("V")))
+                              ->getDeclName();
+  auto *A =
+      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("A")));
+  auto *B =
+      FirstDeclMatcher<RecordDecl>().match(ToTU, recordDecl(hasName("B")));
+
+  ASTImporterLookupTable LT(*ToTU);
+
+  auto Res = LT.lookup(cast<DeclContext>(A), VName);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+                        ToTU, fieldDecl(hasName("V"),
+                                        hasParent(recordDecl(hasName("A"))))));
+  Res = LT.lookup(cast<DeclContext>(B), VName);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<FieldDecl>().match(
+                        ToTU, fieldDecl(hasName("V"),
+                                        hasParent(recordDecl(hasName("B"))))));
+  Res = LT.lookup(ToTU, VName);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), FirstDeclMatcher<VarDecl>().match(
+                        ToTU, varDecl(hasName("V"),
+                                        hasParent(translationUnitDecl()))));
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsOverloadedNames) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      void foo();
+      void foo(int);
+      void foo(int, int);
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F0 = FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+  auto *F2 = LastDeclMatcher<FunctionDecl>().match(ToTU, functionDecl());
+  DeclarationName Name = F0->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 3u);
+  EXPECT_EQ(Res.count(F0), 1u);
+  EXPECT_EQ(Res.count(F2), 1u);
+}
+
+static const RecordDecl * getRecordDeclOfFriend(FriendDecl *FD) {
+  QualType Ty = FD->getFriendType()->getType();
+  QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType();
+  return cast<RecordType>(NamedTy)->getDecl();
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendClassDecl) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      class Y { friend class F; };
+      )",
+      Lang_CXX);
+
+  // 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<FriendDecl>().match(ToTU, friendDecl());
+  const RecordDecl *RD = getRecordDeclOfFriend(FriendD);
+  auto *Y = FirstDeclMatcher<CXXRecordDecl>().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, LookupFindsFwdFriendClassTemplateDecl) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      class Y { template <class T> friend class F; };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("F")));
+  DeclarationName Name = F->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 2u);
+  EXPECT_EQ(Res.count(F), 1u);
+  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, DependentFriendClass) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      template <typename T>
+      class F;
+
+      template <typename T>
+      class Y {
+        friend class F<T>;
+      };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("F")));
+  DeclarationName Name = F->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 2u);
+  EXPECT_EQ(Res.count(F), 1u);
+  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, FriendClassTemplateSpecialization) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      template <typename T>
+      class F;
+
+      class Y {
+        friend class F<int>;
+      };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("F")));
+  DeclarationName Name = F->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  ASSERT_EQ(Res.size(), 3u);
+  EXPECT_EQ(Res.count(F), 1u);
+  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+  EXPECT_EQ(Res.count(*F->spec_begin()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionDecl) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      class Y { friend void F(); };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F =
+      FirstDeclMatcher<FunctionDecl>().match(ToTU, functionDecl(hasName("F")));
+  DeclarationName Name = F->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), F);
+}
+
+TEST_P(ASTImporterLookupTableTest,
+       LookupFindsDeclsInClassTemplateSpecialization) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      template <typename T>
+      struct X {
+        int F;
+      };
+      void foo() {
+        X<char> xc;
+      }
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+
+  auto *Template = FirstDeclMatcher<ClassTemplateDecl>().match(
+      ToTU, classTemplateDecl(hasName("X")));
+  auto *FieldInTemplate = FirstDeclMatcher<FieldDecl>().match(
+      ToTU,
+      fieldDecl(hasParent(cxxRecordDecl(hasParent(classTemplateDecl())))));
+
+  auto *Spec = FirstDeclMatcher<ClassTemplateSpecializationDecl>().match(
+      ToTU, classTemplateSpecializationDecl(hasName("X")));
+  FieldDecl *FieldInSpec = *Spec->field_begin();
+  ASSERT_TRUE(FieldInSpec);
+
+  DeclarationName Name = FieldInSpec->getDeclName();
+  auto TemplateDC = cast<DeclContext>(Template->getTemplatedDecl());
+
+  SmallVector<NamedDecl *, 2> FoundDecls;
+  TemplateDC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 1u);
+  EXPECT_EQ(FoundDecls[0], FieldInTemplate);
+
+  auto Res = LT.lookup(TemplateDC, Name);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), FieldInTemplate);
+
+  cast<DeclContext>(Spec)->getRedeclContext()->localUncachedLookup(Name,
+                                                                   FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 1u);
+  EXPECT_EQ(FoundDecls[0], FieldInSpec);
+
+  Res = LT.lookup(cast<DeclContext>(Spec), Name);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), FieldInSpec);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupFindsFwdFriendFunctionTemplateDecl) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      class Y { template <class T> friend void F(); };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *F = FirstDeclMatcher<FunctionTemplateDecl>().match(
+      ToTU, functionTemplateDecl(hasName("F")));
+  DeclarationName Name = F->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 2u);
+  EXPECT_EQ(Res.count(F), 1u);
+  EXPECT_EQ(Res.count(F->getTemplatedDecl()), 1u);
+}
+
+TEST_P(ASTImporterLookupTableTest, MultipleBefriendingClasses) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      struct X;
+      struct A {
+        friend struct X;
+      };
+      struct B {
+        friend struct X;
+      };
+      )",
+      Lang_CXX);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *X = FirstDeclMatcher<CXXRecordDecl>().match(
+      ToTU, cxxRecordDecl(hasName("X")));
+  auto *FriendD0 = FirstDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+  auto *FriendD1 = LastDeclMatcher<FriendDecl>().match(ToTU, friendDecl());
+  const RecordDecl *RD0 = getRecordDeclOfFriend(FriendD0);
+  const RecordDecl *RD1 = getRecordDeclOfFriend(FriendD1);
+  ASSERT_EQ(RD0, RD1);
+  ASSERT_EQ(RD1, X);
+
+  DeclarationName Name = X->getDeclName();
+  auto Res = LT.lookup(ToTU, Name);
+  EXPECT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), X);
+}
+
+TEST_P(ASTImporterLookupTableTest, EnumConstantDecl) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      enum E {
+        A,
+        B
+      };
+      )",
+      Lang_C);
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto *E = FirstDeclMatcher<EnumDecl>().match(ToTU, enumDecl(hasName("E")));
+  auto *A = FirstDeclMatcher<EnumConstantDecl>().match(
+      ToTU, enumConstantDecl(hasName("A")));
+
+  DeclarationName Name = A->getDeclName();
+  // Redecl context is the TU.
+  ASSERT_EQ(E->getRedeclContext(), ToTU);
+
+  SmallVector<NamedDecl *, 2> FoundDecls;
+  // Normal lookup finds in the DC.
+  E->localUncachedLookup(Name, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 1u);
+
+  // Normal lookup finds in the Redecl context.
+  ToTU->localUncachedLookup(Name, FoundDecls);
+  EXPECT_EQ(FoundDecls.size(), 1u);
+
+  // Import specific lookup finds in the DC.
+  auto Res = LT.lookup(E, Name);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), A);
+
+  // Import specific lookup finds in the Redecl context.
+  Res = LT.lookup(ToTU, Name);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), A);
+}
+
+TEST_P(ASTImporterLookupTableTest, LookupSearchesInTheWholeRedeclChain) {
+  TranslationUnitDecl *ToTU = getToTuDecl(
+      R"(
+      namespace N {
+        int A;
+      }
+      namespace N {
+      }
+      )",
+      Lang_CXX);
+  auto *N1 =
+      LastDeclMatcher<NamespaceDecl>().match(ToTU, namespaceDecl(hasName("N")));
+  auto *A = FirstDeclMatcher<VarDecl>().match(ToTU, varDecl(hasName("A")));
+  DeclarationName Name = A->getDeclName();
+
+  ASTImporterLookupTable LT(*ToTU);
+  auto Res = LT.lookup(N1, Name);
+  ASSERT_EQ(Res.size(), 1u);
+  EXPECT_EQ(*Res.begin(), A);
+}
+
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, DeclContextTest,
                         ::testing::Values(ArgVector()), );
 
@@ -4352,6 +4872,9 @@
     ArgVector{"-fms-compatibility"},
     ArgVector{"-fdelayed-template-parsing", "-fms-compatibility"});
 
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ASTImporterLookupTableTest,
+                        DefaultTestValuesForRunOptions, );
+
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportExpr,
                         DefaultTestValuesForRunOptions, );
 
@@ -4367,10 +4890,10 @@
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFunctions,
                         DefaultTestValuesForRunOptions, );
 
-INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
                         DefaultTestValuesForRunOptions, );
 
-INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClasses,
+INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportFriendFunctions,
                         DefaultTestValuesForRunOptions, );
 
 INSTANTIATE_TEST_CASE_P(ParameterizedTests, ImportClassTemplates,