r349351 - [ASTImporter] Add importer specific lookup

Gabor Marton via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 17 05:53:12 PST 2018


Author: martong
Date: Mon Dec 17 05:53:12 2018
New Revision: 349351

URL: http://llvm.org/viewvc/llvm-project?rev=349351&view=rev
Log:
[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

Added:
    cfe/trunk/include/clang/AST/ASTImporterLookupTable.h
    cfe/trunk/lib/AST/ASTImporterLookupTable.cpp
Modified:
    cfe/trunk/include/clang/AST/ASTImporter.h
    cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h
    cfe/trunk/lib/AST/ASTImporter.cpp
    cfe/trunk/lib/AST/CMakeLists.txt
    cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp
    cfe/trunk/lib/Frontend/ASTMerge.cpp
    cfe/trunk/unittests/AST/ASTImporterTest.cpp

Modified: cfe/trunk/include/clang/AST/ASTImporter.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTImporter.h?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTImporter.h (original)
+++ cfe/trunk/include/clang/AST/ASTImporter.h Mon Dec 17 05:53:12 2018
@@ -33,6 +33,7 @@
 namespace clang {
 
 class ASTContext;
+class ASTImporterLookupTable;
 class CXXBaseSpecifier;
 class CXXCtorInitializer;
 class Decl;
@@ -80,12 +81,21 @@ class Attr;
   /// Imports selected nodes from one AST context into another context,
   /// merging AST nodes where appropriate.
   class ASTImporter {
+    friend class ASTNodeImporter;
   public:
     using NonEquivalentDeclSet = llvm::DenseSet<std::pair<Decl *, Decl *>>;
     using ImportedCXXBaseSpecifierMap =
         llvm::DenseMap<const CXXBaseSpecifier *, CXXBaseSpecifier *>;
 
   private:
+
+    /// Pointer to the import specific lookup table, which may be shared
+    /// amongst several ASTImporter objects.
+    /// This is an externally managed resource (and should exist during the
+    /// lifetime of the ASTImporter object)
+    /// If not set then the original C/C++ lookup is used.
+    ASTImporterLookupTable *LookupTable = nullptr;
+
     /// The contexts we're importing to and from.
     ASTContext &ToContext, &FromContext;
 
@@ -123,9 +133,13 @@ class Attr;
     /// (which we have already complained about).
     NonEquivalentDeclSet NonEquivalentDecls;
 
+    using FoundDeclsTy = SmallVector<NamedDecl *, 2>;
+    FoundDeclsTy findDeclsInToCtx(DeclContext *DC, DeclarationName Name);
+
+    void AddToLookupTable(Decl *ToD);
+
   public:
-    /// Create a new AST importer.
-    ///
+
     /// \param ToContext The context we'll be importing into.
     ///
     /// \param ToFileManager The file manager we'll be importing into.
@@ -137,9 +151,14 @@ class Attr;
     /// \param MinimalImport If true, the importer will attempt to import
     /// as little as it can, e.g., by importing declarations as forward
     /// declarations that can be completed at a later point.
+    ///
+    /// \param LookupTable The importer specific lookup table which may be
+    /// shared amongst several ASTImporter objects.
+    /// If not set then the original C/C++ lookup is used.
     ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
                 ASTContext &FromContext, FileManager &FromFileManager,
-                bool MinimalImport);
+                bool MinimalImport,
+                ASTImporterLookupTable *LookupTable = nullptr);
 
     virtual ~ASTImporter();
 

Added: cfe/trunk/include/clang/AST/ASTImporterLookupTable.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTImporterLookupTable.h?rev=349351&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/ASTImporterLookupTable.h (added)
+++ cfe/trunk/include/clang/AST/ASTImporterLookupTable.h Mon Dec 17 05:53:12 2018
@@ -0,0 +1,75 @@
+//===- ASTImporterLookupTable.h - ASTImporter specific lookup--*- C++ -*---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the ASTImporterLookupTable class which implements a
+//  lookup procedure for the import mechanism.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_ASTIMPORTERLOOKUPTABLE_H
+#define LLVM_CLANG_AST_ASTIMPORTERLOOKUPTABLE_H
+
+#include "clang/AST/DeclBase.h" // lookup_result
+#include "clang/AST/DeclarationName.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SetVector.h"
+
+namespace clang {
+
+class ASTContext;
+class NamedDecl;
+class DeclContext;
+
+// 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.
+//   // Here we could find the node by doing a traverse throught the list of
+//   // the Decls in the DC, but that would not scale.
+//   struct A { struct Foo *p; };
+// This is a severe problem because the importer decides if it has to create a
+// new Decl or not based on the lookup results.
+// To overcome these cases we need an importer specific lookup table which
+// holds every node and we are not interested in any C/C++ specific visibility
+// considerations. Simply, we must know if there is an existing Decl in a
+// given DC. Once we found it then we can handle any visibility related tasks.
+class ASTImporterLookupTable {
+
+  // We store a list of declarations for each name.
+  // And we collect these lists for each DeclContext.
+  // We could have a flat map with (DeclContext, Name) tuple as key, but a two
+  // level map seems easier to handle.
+  using DeclList = llvm::SmallSetVector<NamedDecl *, 2>;
+  using NameMap = llvm::SmallDenseMap<DeclarationName, DeclList, 4>;
+  using DCMap = llvm::DenseMap<DeclContext *, NameMap>;
+
+  void add(DeclContext *DC, NamedDecl *ND);
+  void remove(DeclContext *DC, NamedDecl *ND);
+
+  DCMap LookupTable;
+
+public:
+  ASTImporterLookupTable(TranslationUnitDecl &TU);
+  void add(NamedDecl *ND);
+  void remove(NamedDecl *ND);
+  using LookupResult = DeclList;
+  LookupResult lookup(DeclContext *DC, DeclarationName Name) const;
+  void dump(DeclContext *DC) const;
+  void dump() const;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_ASTIMPORTERLOOKUPTABLE_H

Modified: cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h (original)
+++ cfe/trunk/include/clang/CrossTU/CrossTranslationUnit.h Mon Dec 17 05:53:12 2018
@@ -15,6 +15,7 @@
 #ifndef LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
 #define LLVM_CLANG_CROSSTU_CROSSTRANSLATIONUNIT_H
 
+#include "clang/AST/ASTImporterLookupTable.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallPtrSet.h"
@@ -152,6 +153,7 @@ public:
   void emitCrossTUDiagnostics(const IndexError &IE);
 
 private:
+  void lazyInitLookupTable(TranslationUnitDecl *ToTU);
   ASTImporter &getOrCreateASTImporter(ASTContext &From);
   const FunctionDecl *findFunctionInDeclContext(const DeclContext *DC,
                                                 StringRef LookupFnName);
@@ -163,6 +165,7 @@ private:
       ASTUnitImporterMap;
   CompilerInstance &CI;
   ASTContext &Context;
+  std::unique_ptr<ASTImporterLookupTable> LookupTable;
 };
 
 } // namespace cross_tu

Modified: cfe/trunk/lib/AST/ASTImporter.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporter.cpp?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTImporter.cpp (original)
+++ cfe/trunk/lib/AST/ASTImporter.cpp Mon Dec 17 05:53:12 2018
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/ASTStructuralEquivalence.h"
@@ -134,28 +135,6 @@ namespace clang {
       To->setIsUsed();
   }
 
-  Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) {
-    assert(F && (isa<FieldDecl>(*F) || isa<IndirectFieldDecl>(*F)) &&
-        "Try to get field index for non-field.");
-
-    auto *Owner = dyn_cast<RecordDecl>(F->getDeclContext());
-    if (!Owner)
-      return None;
-
-    unsigned Index = 0;
-    for (const auto *D : Owner->decls()) {
-      if (D == F)
-        return Index;
-
-      if (isa<FieldDecl>(*D) || isa<IndirectFieldDecl>(*D))
-        ++Index;
-    }
-
-    llvm_unreachable("Field was not found in its parent context.");
-
-    return None;
-  }
-
   // FIXME: Temporary until every import returns Expected.
   template <>
   LLVM_NODISCARD Error
@@ -313,12 +292,14 @@ namespace clang {
       if (ToD)
         return true; // Already imported.
       ToD = CreateFun(std::forward<Args>(args)...);
+      // Keep track of imported Decls.
+      Importer.MapImported(FromD, ToD);
+      Importer.AddToLookupTable(ToD);
       InitializeImportedDecl(FromD, ToD);
       return false; // A new Decl is created.
     }
 
     void InitializeImportedDecl(Decl *FromD, Decl *ToD) {
-      Importer.MapImported(FromD, ToD);
       ToD->IdentifierNamespace = FromD->IdentifierNamespace;
       if (FromD->hasAttrs())
         for (const Attr *FromAttr : FromD->getAttrs())
@@ -2198,8 +2179,7 @@ ExpectedDecl ASTNodeImporter::VisitNames
       MergeWithNamespace = cast<NamespaceDecl>(DC)->getAnonymousNamespace();
   } else {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Namespace))
         continue;
@@ -2310,8 +2290,7 @@ ASTNodeImporter::VisitTypedefNameDecl(Ty
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -2399,8 +2378,7 @@ ASTNodeImporter::VisitTypeAliasTemplateD
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -2502,8 +2480,8 @@ ExpectedDecl ASTNodeImporter::VisitEnumD
   // We may already have an enum of the same name; try to find and match it.
   if (!DC->isFunctionOrMethod() && SearchName) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(SearchName, FoundDecls);
+    auto FoundDecls =
+        Importer.findDeclsInToCtx(DC, SearchName);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -2615,9 +2593,8 @@ ExpectedDecl ASTNodeImporter::VisitRecor
   RecordDecl *PrevDecl = nullptr;
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(SearchName, FoundDecls);
-
+    auto FoundDecls =
+        Importer.findDeclsInToCtx(DC, SearchName);
     if (!FoundDecls.empty()) {
       // We're going to have to compare D against potentially conflicting Decls, so complete it.
       if (D->hasExternalLexicalStorage() && !D->isCompleteDefinition())
@@ -2634,15 +2611,6 @@ ExpectedDecl ASTNodeImporter::VisitRecor
           Found = Tag->getDecl();
       }
 
-      if (D->getDescribedTemplate()) {
-        if (auto *Template = dyn_cast<ClassTemplateDecl>(Found)) {
-          Found = Template->getTemplatedDecl();
-        } else {
-          ConflictingDecls.push_back(FoundDecl);
-          continue;
-        }
-      }
-
       if (auto *FoundRecord = dyn_cast<RecordDecl>(Found)) {
         // Do not emit false positive diagnostic in case of unnamed
         // struct/union and in case of anonymous structs.  Would be false
@@ -2838,8 +2806,7 @@ ExpectedDecl ASTNodeImporter::VisitEnumC
   if (!LexicalDC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -3005,9 +2972,9 @@ ExpectedDecl ASTNodeImporter::VisitFunct
   FunctionTemplateDecl *FromFT = D->getDescribedFunctionTemplate();
 
   // If this is a function template specialization, then try to find the same
-  // existing specialization in the "to" context. The localUncachedLookup
-  // below will not find any specialization, but would find the primary
-  // template; thus, we have to skip normal lookup in case of specializations.
+  // existing specialization in the "to" context. The lookup below will not
+  // find any specialization, but would find the primary template; thus, we
+  // have to skip normal lookup in case of specializations.
   // FIXME handle member function templates (TK_MemberSpecialization) similarly?
   if (D->getTemplatedKind() ==
       FunctionDecl::TK_FunctionTemplateSpecialization) {
@@ -3025,20 +2992,11 @@ ExpectedDecl ASTNodeImporter::VisitFunct
   else if (!LexicalDC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
 
-      // If template was found, look at the templated function.
-      if (FromFT) {
-        if (auto *Template = dyn_cast<FunctionTemplateDecl>(FoundDecl))
-          FoundDecl = Template->getTemplatedDecl();
-        else
-          continue;
-      }
-
       if (auto *FoundFunction = dyn_cast<FunctionDecl>(FoundDecl)) {
         if (FoundFunction->hasExternalFormalLinkage() &&
             D->hasExternalFormalLinkage()) {
@@ -3299,8 +3257,7 @@ ExpectedDecl ASTNodeImporter::VisitField
     return ToD;
 
   // Determine whether we've already imported this field.
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (FieldDecl *FoundField = dyn_cast<FieldDecl>(FoundDecl)) {
       // For anonymous fields, match up by index.
@@ -3385,8 +3342,7 @@ ExpectedDecl ASTNodeImporter::VisitIndir
     return ToD;
 
   // Determine whether we've already imported this field.
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (unsigned I = 0, N = FoundDecls.size(); I != N; ++I) {
     if (auto *FoundField = dyn_cast<IndirectFieldDecl>(FoundDecls[I])) {
       // For anonymous indirect fields, match up by index.
@@ -3454,7 +3410,7 @@ ExpectedDecl ASTNodeImporter::VisitFrien
     return std::move(Err);
 
   // Determine whether we've already imported this decl.
-  // FriendDecl is not a NamedDecl so we cannot use localUncachedLookup.
+  // FriendDecl is not a NamedDecl so we cannot use lookup.
   auto *RD = cast<CXXRecordDecl>(DC);
   FriendDecl *ImportedFriend = RD->getFirstFriend();
 
@@ -3532,8 +3488,7 @@ ExpectedDecl ASTNodeImporter::VisitObjCI
     return ToD;
 
   // Determine whether we've already imported this ivar
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (ObjCIvarDecl *FoundIvar = dyn_cast<ObjCIvarDecl>(FoundDecl)) {
       if (Importer.IsStructurallyEquivalent(D->getType(),
@@ -3603,8 +3558,7 @@ ExpectedDecl ASTNodeImporter::VisitVarDe
   if (D->isFileVarDecl()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
     unsigned IDNS = Decl::IDNS_Ordinary;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -3814,8 +3768,7 @@ ExpectedDecl ASTNodeImporter::VisitObjCM
   if (ToD)
     return ToD;
 
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (auto *FoundMethod = dyn_cast<ObjCMethodDecl>(FoundDecl)) {
       if (FoundMethod->isInstanceMethod() != D->isInstanceMethod())
@@ -4122,8 +4075,7 @@ ExpectedDecl ASTNodeImporter::VisitObjCP
     return ToD;
 
   ObjCProtocolDecl *MergeWithProtocol = nullptr;
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_ObjCProtocol))
       continue;
@@ -4548,8 +4500,7 @@ ExpectedDecl ASTNodeImporter::VisitObjCI
 
   // Look for an existing interface with the same name.
   ObjCInterfaceDecl *MergeWithIface = nullptr;
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
       continue;
@@ -4724,8 +4675,7 @@ ExpectedDecl ASTNodeImporter::VisitObjCP
     return ToD;
 
   // Check whether we have already imported this property.
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (auto *FoundProp = dyn_cast<ObjCPropertyDecl>(FoundDecl)) {
       // Check property types.
@@ -4995,8 +4945,7 @@ ExpectedDecl ASTNodeImporter::VisitClass
   // We may already have a template of the same name; try to find and match it.
   if (!DC->isFunctionOrMethod()) {
     SmallVector<NamedDecl *, 4> ConflictingDecls;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary |
                                               Decl::IDNS_TagFriend))
@@ -5304,8 +5253,7 @@ ExpectedDecl ASTNodeImporter::VisitVarTe
   assert(!DC->isFunctionOrMethod() &&
          "Variable templates cannot be declared at function scope");
   SmallVector<NamedDecl *, 4> ConflictingDecls;
-  SmallVector<NamedDecl *, 2> FoundDecls;
-  DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+  auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
   for (auto *FoundDecl : FoundDecls) {
     if (!FoundDecl->isInIdentifierNamespace(Decl::IDNS_Ordinary))
       continue;
@@ -5537,8 +5485,7 @@ ASTNodeImporter::VisitFunctionTemplateDe
   // type, and in the same context as the function we're importing.
   if (!LexicalDC->isFunctionOrMethod()) {
     unsigned IDNS = Decl::IDNS_Ordinary;
-    SmallVector<NamedDecl *, 2> FoundDecls;
-    DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
+    auto FoundDecls = Importer.findDeclsInToCtx(DC, Name);
     for (auto *FoundDecl : FoundDecls) {
       if (!FoundDecl->isInIdentifierNamespace(IDNS))
         continue;
@@ -7666,12 +7613,14 @@ void ASTNodeImporter::ImportOverrides(CX
 
 ASTImporter::ASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
                          ASTContext &FromContext, FileManager &FromFileManager,
-                         bool MinimalImport)
-    : ToContext(ToContext), FromContext(FromContext),
+                         bool MinimalImport,
+                         ASTImporterLookupTable *LookupTable)
+    : LookupTable(LookupTable), ToContext(ToContext), FromContext(FromContext),
       ToFileManager(ToFileManager), FromFileManager(FromFileManager),
       Minimal(MinimalImport) {
-  ImportedDecls[FromContext.getTranslationUnitDecl()]
-    = ToContext.getTranslationUnitDecl();
+
+  ImportedDecls[FromContext.getTranslationUnitDecl()] =
+      ToContext.getTranslationUnitDecl();
 }
 
 ASTImporter::~ASTImporter() = default;
@@ -7682,6 +7631,58 @@ Expected<QualType> ASTImporter::Import_N
     return make_error<ImportError>();
   return ToT;
 }
+
+Optional<unsigned> ASTImporter::getFieldIndex(Decl *F) {
+  assert(F && (isa<FieldDecl>(*F) || isa<IndirectFieldDecl>(*F)) &&
+      "Try to get field index for non-field.");
+
+  auto *Owner = dyn_cast<RecordDecl>(F->getDeclContext());
+  if (!Owner)
+    return None;
+
+  unsigned Index = 0;
+  for (const auto *D : Owner->decls()) {
+    if (D == F)
+      return Index;
+
+    if (isa<FieldDecl>(*D) || isa<IndirectFieldDecl>(*D))
+      ++Index;
+  }
+
+  llvm_unreachable("Field was not found in its parent context.");
+
+  return None;
+}
+
+ASTImporter::FoundDeclsTy
+ASTImporter::findDeclsInToCtx(DeclContext *DC, DeclarationName Name) {
+  // We search in the redecl context because of transparent contexts.
+  // E.g. a simple C language enum is a transparent context:
+  //   enum E { A, B };
+  // Now if we had a global variable in the TU
+  //   int A;
+  // then the enum constant 'A' and the variable 'A' violates ODR.
+  // We can diagnose this only if we search in the redecl context.
+  DeclContext *ReDC = DC->getRedeclContext();
+  if (LookupTable) {
+    ASTImporterLookupTable::LookupResult LookupResult =
+        LookupTable->lookup(ReDC, Name);
+    return FoundDeclsTy(LookupResult.begin(), LookupResult.end());
+  } else {
+    // FIXME Can we remove this kind of lookup?
+    // Or lldb really needs this C/C++ lookup?
+    FoundDeclsTy Result;
+    ReDC->localUncachedLookup(Name, Result);
+    return Result;
+  }
+}
+
+void ASTImporter::AddToLookupTable(Decl *ToD) {
+  if (LookupTable)
+    if (auto *ToND = dyn_cast<NamedDecl>(ToD))
+      LookupTable->add(ToND);
+}
+
 QualType ASTImporter::Import(QualType FromT) {
   if (FromT.isNull())
     return {};
@@ -7774,6 +7775,11 @@ Decl *ASTImporter::Import(Decl *FromD) {
   }
   ToD = *ToDOrErr;
 
+  // Once the decl is connected to the existing declarations, i.e. when the
+  // redecl chain is properly set then we populate the lookup again.
+  // This way the primary context will be able to find all decls.
+  AddToLookupTable(ToD);
+
   // Notify subclasses.
   Imported(FromD, ToD);
 

Added: cfe/trunk/lib/AST/ASTImporterLookupTable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTImporterLookupTable.cpp?rev=349351&view=auto
==============================================================================
--- cfe/trunk/lib/AST/ASTImporterLookupTable.cpp (added)
+++ cfe/trunk/lib/AST/ASTImporterLookupTable.cpp Mon Dec 17 05:53:12 2018
@@ -0,0 +1,129 @@
+//===- ASTImporterLookupTable.cpp - ASTImporter specific lookup -----------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the ASTImporterLookupTable class which implements a
+//  lookup procedure for the import mechanism.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTImporterLookupTable.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+
+namespace clang {
+
+namespace {
+
+struct Builder : RecursiveASTVisitor<Builder> {
+  ASTImporterLookupTable <
+  Builder(ASTImporterLookupTable &LT) : LT(LT) {}
+  bool VisitNamedDecl(NamedDecl *D) {
+    LT.add(D);
+    return true;
+  }
+  bool VisitFriendDecl(FriendDecl *D) {
+    if (D->getFriendType()) {
+      QualType Ty = D->getFriendType()->getType();
+      // FIXME Can this be other than elaborated?
+      QualType NamedTy = cast<ElaboratedType>(Ty)->getNamedType();
+      if (!NamedTy->isDependentType()) {
+        if (const auto *RTy = dyn_cast<RecordType>(NamedTy))
+          LT.add(RTy->getAsCXXRecordDecl());
+        else if (const auto *SpecTy =
+                     dyn_cast<TemplateSpecializationType>(NamedTy)) {
+          LT.add(SpecTy->getAsCXXRecordDecl());
+        }
+      }
+    }
+    return true;
+  }
+
+  // Override default settings of base.
+  bool shouldVisitTemplateInstantiations() const { return true; }
+  bool shouldVisitImplicitCode() const { return true; }
+};
+
+} // anonymous namespace
+
+ASTImporterLookupTable::ASTImporterLookupTable(TranslationUnitDecl &TU) {
+  Builder B(*this);
+  B.TraverseDecl(&TU);
+}
+
+void ASTImporterLookupTable::add(DeclContext *DC, NamedDecl *ND) {
+  DeclList &Decls = LookupTable[DC][ND->getDeclName()];
+  // Inserts if and only if there is no element in the container equal to it.
+  Decls.insert(ND);
+}
+
+void ASTImporterLookupTable::remove(DeclContext *DC, NamedDecl *ND) {
+  DeclList &Decls = LookupTable[DC][ND->getDeclName()];
+  bool EraseResult = Decls.remove(ND);
+  (void)EraseResult;
+  assert(EraseResult == true && "Trying to remove not contained Decl");
+}
+
+void ASTImporterLookupTable::add(NamedDecl *ND) {
+  assert(ND);
+  DeclContext *DC = ND->getDeclContext()->getPrimaryContext();
+  add(DC, ND);
+  DeclContext *ReDC = DC->getRedeclContext()->getPrimaryContext();
+  if (DC != ReDC)
+    add(ReDC, ND);
+}
+
+void ASTImporterLookupTable::remove(NamedDecl *ND) {
+  assert(ND);
+  DeclContext *DC = ND->getDeclContext()->getPrimaryContext();
+  remove(DC, ND);
+  DeclContext *ReDC = DC->getRedeclContext()->getPrimaryContext();
+  if (DC != ReDC)
+    remove(ReDC, ND);
+}
+
+ASTImporterLookupTable::LookupResult
+ASTImporterLookupTable::lookup(DeclContext *DC, DeclarationName Name) const {
+  auto DCI = LookupTable.find(DC->getPrimaryContext());
+  if (DCI == LookupTable.end())
+    return {};
+
+  const auto &FoundNameMap = DCI->second;
+  auto NamesI = FoundNameMap.find(Name);
+  if (NamesI == FoundNameMap.end())
+    return {};
+
+  return NamesI->second;
+}
+
+void ASTImporterLookupTable::dump(DeclContext *DC) const {
+  auto DCI = LookupTable.find(DC->getPrimaryContext());
+  if (DCI == LookupTable.end())
+    llvm::errs() << "empty\n";
+  const auto &FoundNameMap = DCI->second;
+  for (const auto &Entry : FoundNameMap) {
+    DeclarationName Name = Entry.first;
+    llvm::errs() << "==== Name: ";
+    Name.dump();
+    const DeclList& List = Entry.second;
+    for (NamedDecl *ND : List) {
+      ND->dump();
+    }
+  }
+}
+
+void ASTImporterLookupTable::dump() const {
+  for (const auto &Entry : LookupTable) {
+    DeclContext *DC = Entry.first;
+    StringRef Primary = DC->getPrimaryContext() ? " primary" : "";
+    llvm::errs() << "== DC:" << cast<Decl>(DC) << Primary << "\n";
+    dump(DC);
+  }
+}
+
+} // namespace clang

Modified: cfe/trunk/lib/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Mon Dec 17 05:53:12 2018
@@ -10,6 +10,7 @@ add_clang_library(clangAST
   ASTDiagnostic.cpp
   ASTDumper.cpp
   ASTImporter.cpp
+  ASTImporterLookupTable.cpp
   ASTStructuralEquivalence.cpp
   ASTTypeTraits.cpp
   AttrImpl.cpp

Modified: cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp (original)
+++ cfe/trunk/lib/CrossTU/CrossTranslationUnit.cpp Mon Dec 17 05:53:12 2018
@@ -342,14 +342,21 @@ CrossTranslationUnitContext::importDefin
   return ToDecl;
 }
 
+void CrossTranslationUnitContext::lazyInitLookupTable(
+    TranslationUnitDecl *ToTU) {
+  if (!LookupTable)
+    LookupTable = llvm::make_unique<ASTImporterLookupTable>(*ToTU);
+}
+
 ASTImporter &
 CrossTranslationUnitContext::getOrCreateASTImporter(ASTContext &From) {
   auto I = ASTUnitImporterMap.find(From.getTranslationUnitDecl());
   if (I != ASTUnitImporterMap.end())
     return *I->second;
-  ASTImporter *NewImporter =
-      new ASTImporter(Context, Context.getSourceManager().getFileManager(),
-                      From, From.getSourceManager().getFileManager(), false);
+  lazyInitLookupTable(Context.getTranslationUnitDecl());
+  ASTImporter *NewImporter = new ASTImporter(
+      Context, Context.getSourceManager().getFileManager(), From,
+      From.getSourceManager().getFileManager(), false, LookupTable.get());
   ASTUnitImporterMap[From.getTranslationUnitDecl()].reset(NewImporter);
   return *NewImporter;
 }

Modified: cfe/trunk/lib/Frontend/ASTMerge.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/ASTMerge.cpp?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/ASTMerge.cpp (original)
+++ cfe/trunk/lib/Frontend/ASTMerge.cpp Mon Dec 17 05:53:12 2018
@@ -10,6 +10,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTDiagnostic.h"
 #include "clang/AST/ASTImporter.h"
+#include "clang/AST/ASTImporterLookupTable.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
@@ -38,6 +39,8 @@ void ASTMergeAction::ExecuteAction() {
                                        &CI.getASTContext());
   IntrusiveRefCntPtr<DiagnosticIDs>
       DiagIDs(CI.getDiagnostics().getDiagnosticIDs());
+  ASTImporterLookupTable LookupTable(
+      *CI.getASTContext().getTranslationUnitDecl());
   for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) {
     IntrusiveRefCntPtr<DiagnosticsEngine>
         Diags(new DiagnosticsEngine(DiagIDs, &CI.getDiagnosticOpts(),
@@ -51,11 +54,9 @@ void ASTMergeAction::ExecuteAction() {
     if (!Unit)
       continue;
 
-    ASTImporter Importer(CI.getASTContext(),
-                         CI.getFileManager(),
-                         Unit->getASTContext(),
-                         Unit->getFileManager(),
-                         /*MinimalImport=*/false);
+    ASTImporter Importer(CI.getASTContext(), CI.getFileManager(),
+                         Unit->getASTContext(), Unit->getFileManager(),
+                         /*MinimalImport=*/false, &LookupTable);
 
     TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl();
     for (auto *D : TU->decls()) {

Modified: cfe/trunk/unittests/AST/ASTImporterTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/AST/ASTImporterTest.cpp?rev=349351&r1=349350&r2=349351&view=diff
==============================================================================
--- cfe/trunk/unittests/AST/ASTImporterTest.cpp (original)
+++ cfe/trunk/unittests/AST/ASTImporterTest.cpp Mon Dec 17 05:53:12 2018
@@ -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 @@ class ASTImporterTestBase : public Param
       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 @@ class ASTImporterTestBase : public Param
   // 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 @@ class ASTImporterTestBase : public Param
     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 @@ public:
     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 @@ public:
   // 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 @@ public:
   // 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 @@ private:
       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 @@ TEST_P(ImportClasses, ImportPrototypeThe
   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 @@ TEST_P(ImportFriendClasses, ImportOfClas
   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 @@ TEST_P(ASTImporterTestBase, ImportingTyp
   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 @@ auto DefaultTestValuesForRunOptions = ::
     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(ParameterizedTes
 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,




More information about the cfe-commits mailing list