From 3975e67148c7382405ddd5b9004cdad6836cf443 Mon Sep 17 00:00:00 2001
From: Sean Callanan <scallanan@apple.com>
Date: Tue, 11 Apr 2017 19:33:35 +0000
Subject: [PATCH] [clang-import-test] Lookup inside contexts

clang-import-test has until now been only able to report top-level Decls.
This is clearly insufficient; we should be able to look inside structs
and namespaces also.  This patch adds new test cases for a variety of
lookups inside existing ASTContexts, and adds the functionality necessar
to make most of these testcases work.  (One testcase is known to fail
because of ASTImporter limitations when importing templates; I'll look
into that separately.)

This patch also separates the core functionality out into
ExternalASTMerger, an interface that allows clients like LLDB to make
use of it.  clang-import-test now only has the machinery necessary to
set up the tests.

Differential revision: https://reviews.llvm.org/D30435


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@299976 91177308-0d34-0410-b5e6-96231b3b80d8
---
 include/clang/AST/ExternalASTMerger.h         |  51 +++++
 lib/AST/CMakeLists.txt                        |   1 +
 lib/AST/ExternalASTMerger.cpp                 | 183 ++++++++++++++++++
 .../forward-declared-struct/Inputs/S1.c       |   1 +
 .../forward-declared-struct/Inputs/S2.c       |   3 +
 test/Import/forward-declared-struct/test.c    |   5 +
 test/Import/member-in-struct/Inputs/S.c       |   3 +
 test/Import/member-in-struct/test.c           |   5 +
 .../multiple-forward-declarations/Inputs/S1.c |   1 +
 .../multiple-forward-declarations/Inputs/S2.c |   1 +
 .../multiple-forward-declarations/test.c      |   4 +
 test/Import/overloaded-function/Inputs/F1.c   |   1 +
 test/Import/overloaded-function/Inputs/F2.c   |   4 +
 test/Import/overloaded-function/test.c        |   7 +
 test/Import/struct-in-namespace/Inputs/N1.cpp |  11 ++
 test/Import/struct-in-namespace/Inputs/N2.cpp |   5 +
 test/Import/struct-in-namespace/Inputs/N3.cpp |   5 +
 test/Import/struct-in-namespace/test.cpp      |   7 +
 .../template-specialization/Inputs/T.cpp      |  14 ++
 test/Import/template-specialization/test.cpp  |   7 +
 tools/clang-import-test/clang-import-test.cpp |  64 ++----
 21 files changed, 331 insertions(+), 52 deletions(-)
 create mode 100644 include/clang/AST/ExternalASTMerger.h
 create mode 100644 lib/AST/ExternalASTMerger.cpp
 create mode 100644 test/Import/forward-declared-struct/Inputs/S1.c
 create mode 100644 test/Import/forward-declared-struct/Inputs/S2.c
 create mode 100644 test/Import/forward-declared-struct/test.c
 create mode 100644 test/Import/member-in-struct/Inputs/S.c
 create mode 100644 test/Import/member-in-struct/test.c
 create mode 100644 test/Import/multiple-forward-declarations/Inputs/S1.c
 create mode 100644 test/Import/multiple-forward-declarations/Inputs/S2.c
 create mode 100644 test/Import/multiple-forward-declarations/test.c
 create mode 100644 test/Import/overloaded-function/Inputs/F1.c
 create mode 100644 test/Import/overloaded-function/Inputs/F2.c
 create mode 100644 test/Import/overloaded-function/test.c
 create mode 100644 test/Import/struct-in-namespace/Inputs/N1.cpp
 create mode 100644 test/Import/struct-in-namespace/Inputs/N2.cpp
 create mode 100644 test/Import/struct-in-namespace/Inputs/N3.cpp
 create mode 100644 test/Import/struct-in-namespace/test.cpp
 create mode 100644 test/Import/template-specialization/Inputs/T.cpp
 create mode 100644 test/Import/template-specialization/test.cpp

diff --git a/include/clang/AST/ExternalASTMerger.h b/include/clang/AST/ExternalASTMerger.h
new file mode 100644
index 00000000000..51d0c30ad23
--- /dev/null
+++ b/include/clang/AST/ExternalASTMerger.h
@@ -0,0 +1,51 @@
+//===--- ExternalASTMerger.h - Merging External AST Interface ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file declares the ExternalASTMerger, which vends a combination of ASTs
+//  from several different ASTContext/FileManager pairs
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_AST_EXTERNALASTMERGER_H
+#define LLVM_CLANG_AST_EXTERNALASTMERGER_H
+
+#include "clang/AST/ASTImporter.h"
+#include "clang/AST/ExternalASTSource.h"
+
+namespace clang {
+
+class ExternalASTMerger : public ExternalASTSource {
+public:
+  struct ImporterPair {
+    std::unique_ptr<ASTImporter> Forward;
+    std::unique_ptr<ASTImporter> Reverse;
+  };
+
+private:
+  std::vector<ImporterPair> Importers;
+
+public:
+  struct ImporterEndpoint {
+    ASTContext &AST;
+    FileManager &FM;
+  };
+  ExternalASTMerger(const ImporterEndpoint &Target,
+                    llvm::ArrayRef<ImporterEndpoint> Sources);
+
+  bool FindExternalVisibleDeclsByName(const DeclContext *DC,
+                                      DeclarationName Name) override;
+
+  void
+  FindExternalLexicalDecls(const DeclContext *DC,
+                           llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
+                           SmallVectorImpl<Decl *> &Result) override;
+};
+
+} // end namespace clang
+
+#endif
diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt
index 2e98f524da8..13bf352c2f2 100644
--- a/lib/AST/CMakeLists.txt
+++ b/lib/AST/CMakeLists.txt
@@ -31,6 +31,7 @@ add_clang_library(clangAST
   ExprConstant.cpp
   ExprCXX.cpp
   ExprObjC.cpp
+  ExternalASTMerger.cpp
   ExternalASTSource.cpp
   InheritViz.cpp
   ItaniumCXXABI.cpp
diff --git a/lib/AST/ExternalASTMerger.cpp b/lib/AST/ExternalASTMerger.cpp
new file mode 100644
index 00000000000..1d144047bd1
--- /dev/null
+++ b/lib/AST/ExternalASTMerger.cpp
@@ -0,0 +1,183 @@
+//===- ExternalASTMerger.cpp - Merging External AST Interface ---*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file implements the ExternalASTMerger, which vends a combination of
+//  ASTs from several different ASTContext/FileManager pairs
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ExternalASTMerger.h"
+
+using namespace clang;
+
+namespace {
+
+template <typename T> struct Source {
+  T t;
+  Source(T &&t) : t(std::move(t)) {}
+  operator T() { return t; }
+  template <typename U = T> U &get() { return t; }
+  template <typename U = T> const U &get() const { return t; }
+  template <typename U> operator Source<U>() { return Source<U>(t); }
+};
+
+typedef std::pair<Source<NamedDecl *>, ASTImporter *> Candidate;
+
+class LazyASTImporter : public ASTImporter {
+public:
+  LazyASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
+                  ASTContext &FromContext, FileManager &FromFileManager)
+      : ASTImporter(ToContext, ToFileManager, FromContext, FromFileManager,
+                    /*MinimalImport=*/true) {}
+  Decl *Imported(Decl *From, Decl *To) override {
+    if (auto ToTag = dyn_cast<TagDecl>(To)) {
+      ToTag->setHasExternalLexicalStorage();
+    } else if (auto ToNamespace = dyn_cast<NamespaceDecl>(To)) {
+      ToNamespace->setHasExternalVisibleStorage();
+    }
+    return ASTImporter::Imported(From, To);
+  }
+};
+
+Source<const DeclContext *>
+LookupSameContext(Source<TranslationUnitDecl *> SourceTU, const DeclContext *DC,
+                  ASTImporter &ReverseImporter) {
+  if (DC->isTranslationUnit()) {
+    return SourceTU;
+  }
+  Source<const DeclContext *> SourceParentDC =
+      LookupSameContext(SourceTU, DC->getParent(), ReverseImporter);
+  if (!SourceParentDC) {
+    // If we couldn't find the parent DC in this TranslationUnit, give up.
+    return nullptr;
+  }
+  auto ND = cast<NamedDecl>(DC);
+  DeclarationName Name = ND->getDeclName();
+  Source<DeclarationName> SourceName = ReverseImporter.Import(Name);
+  DeclContext::lookup_result SearchResult =
+      SourceParentDC.get()->lookup(SourceName.get());
+  size_t SearchResultSize = SearchResult.size();
+  // Handle multiple candidates once we have a test for it.
+  // This may turn up when we import template specializations correctly.
+  assert(SearchResultSize < 2);
+  if (SearchResultSize == 0) {
+    // couldn't find the name, so we have to give up
+    return nullptr;
+  } else {
+    NamedDecl *SearchResultDecl = SearchResult[0];
+    return dyn_cast<DeclContext>(SearchResultDecl);
+  }
+}
+
+bool IsForwardDeclaration(Decl *D) {
+  assert(!isa<ObjCInterfaceDecl>(D)); // TODO handle this case
+  if (auto TD = dyn_cast<TagDecl>(D)) {
+    return !TD->isThisDeclarationADefinition();
+  } else if (auto FD = dyn_cast<FunctionDecl>(D)) {
+    return !FD->isThisDeclarationADefinition();
+  } else {
+    return false;
+  }
+}
+
+void ForEachMatchingDC(
+    const DeclContext *DC,
+    llvm::ArrayRef<ExternalASTMerger::ImporterPair> Importers,
+    std::function<void(const ExternalASTMerger::ImporterPair &IP,
+                       Source<const DeclContext *> SourceDC)>
+        Callback) {
+  for (const ExternalASTMerger::ImporterPair &IP : Importers) {
+    Source<TranslationUnitDecl *> SourceTU(
+        IP.Forward->getFromContext().getTranslationUnitDecl());
+    Source<const DeclContext *> SourceDC =
+        LookupSameContext(SourceTU, DC, *IP.Reverse);
+    if (SourceDC.get()) {
+      Callback(IP, SourceDC);
+    }
+  }
+}
+
+bool HasDeclOfSameType(llvm::ArrayRef<Candidate> Decls, const Candidate &C) {
+  return std::any_of(Decls.begin(), Decls.end(), [&C](const Candidate &D) {
+    return C.first.get()->getKind() == D.first.get()->getKind();
+  });
+}
+} // end namespace
+
+ExternalASTMerger::ExternalASTMerger(const ImporterEndpoint &Target,
+                                     llvm::ArrayRef<ImporterEndpoint> Sources) {
+  for (const ImporterEndpoint &S : Sources) {
+    Importers.push_back(
+        {llvm::make_unique<LazyASTImporter>(Target.AST, Target.FM, S.AST, S.FM),
+         llvm::make_unique<ASTImporter>(S.AST, S.FM, Target.AST, Target.FM,
+                                        /*MinimalImport=*/true)});
+  }
+}
+
+bool ExternalASTMerger::FindExternalVisibleDeclsByName(const DeclContext *DC,
+                                                       DeclarationName Name) {
+  llvm::SmallVector<NamedDecl *, 1> Decls;
+  llvm::SmallVector<Candidate, 4> CompleteDecls;
+  llvm::SmallVector<Candidate, 4> ForwardDecls;
+
+  auto FilterFoundDecl = [&CompleteDecls, &ForwardDecls](const Candidate &C) {
+    if (IsForwardDeclaration(C.first.get())) {
+      if (!HasDeclOfSameType(ForwardDecls, C)) {
+        ForwardDecls.push_back(C);
+      }
+    } else {
+      CompleteDecls.push_back(C);
+    }
+  };
+
+  ForEachMatchingDC(DC, Importers, [Name, &FilterFoundDecl](
+                                       const ImporterPair &IP,
+                                       Source<const DeclContext *> SourceDC) {
+    DeclarationName FromName = IP.Reverse->Import(Name);
+    DeclContextLookupResult Result = SourceDC.get()->lookup(FromName);
+    for (NamedDecl *FromD : Result) {
+      FilterFoundDecl(std::make_pair(FromD, IP.Forward.get()));
+    }
+  });
+
+  llvm::ArrayRef<Candidate> DeclsToReport =
+      CompleteDecls.empty() ? ForwardDecls : CompleteDecls;
+
+  if (DeclsToReport.empty()) {
+    return false;
+  }
+
+  Decls.reserve(DeclsToReport.size());
+  for (const Candidate &C : DeclsToReport) {
+    NamedDecl *d = cast<NamedDecl>(C.second->Import(C.first.get()));
+    assert(d);
+    Decls.push_back(d);
+  }
+  SetExternalVisibleDeclsForName(DC, Name, Decls);
+  return true;
+}
+
+void ExternalASTMerger::FindExternalLexicalDecls(
+    const DeclContext *DC, llvm::function_ref<bool(Decl::Kind)> IsKindWeWant,
+    SmallVectorImpl<Decl *> &Result) {
+  ForEachMatchingDC(
+      DC, Importers, [DC, IsKindWeWant](const ImporterPair &IP,
+                                        Source<const DeclContext *> SourceDC) {
+        for (Source<const Decl *> SourceDecl : SourceDC.get()->decls()) {
+          if (IsKindWeWant(SourceDecl.get()->getKind())) {
+            Decl *ImportedDecl =
+                IP.Forward->Import(const_cast<Decl *>(SourceDecl.get()));
+            assert(ImportedDecl->getDeclContext() == DC);
+          }
+        }
+      });
+}
diff --git a/test/Import/forward-declared-struct/Inputs/S1.c b/test/Import/forward-declared-struct/Inputs/S1.c
new file mode 100644
index 00000000000..28377c2760b
--- /dev/null
+++ b/test/Import/forward-declared-struct/Inputs/S1.c
@@ -0,0 +1 @@
+struct S;
diff --git a/test/Import/forward-declared-struct/Inputs/S2.c b/test/Import/forward-declared-struct/Inputs/S2.c
new file mode 100644
index 00000000000..b0876d27df4
--- /dev/null
+++ b/test/Import/forward-declared-struct/Inputs/S2.c
@@ -0,0 +1,3 @@
+struct S {
+  int a;
+};
diff --git a/test/Import/forward-declared-struct/test.c b/test/Import/forward-declared-struct/test.c
new file mode 100644
index 00000000000..7ccdcf9e97d
--- /dev/null
+++ b/test/Import/forward-declared-struct/test.c
@@ -0,0 +1,5 @@
+// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
+void expr() {
+  struct S MyS;
+  MyS.a = 3;
+}
diff --git a/test/Import/member-in-struct/Inputs/S.c b/test/Import/member-in-struct/Inputs/S.c
new file mode 100644
index 00000000000..b0876d27df4
--- /dev/null
+++ b/test/Import/member-in-struct/Inputs/S.c
@@ -0,0 +1,3 @@
+struct S {
+  int a;
+};
diff --git a/test/Import/member-in-struct/test.c b/test/Import/member-in-struct/test.c
new file mode 100644
index 00000000000..bde2b60d275
--- /dev/null
+++ b/test/Import/member-in-struct/test.c
@@ -0,0 +1,5 @@
+// RUN: clang-import-test -import %S/Inputs/S.c -expression %s
+void expr() {
+  struct S MyS;
+  MyS.a = 3;
+}
diff --git a/test/Import/multiple-forward-declarations/Inputs/S1.c b/test/Import/multiple-forward-declarations/Inputs/S1.c
new file mode 100644
index 00000000000..28377c2760b
--- /dev/null
+++ b/test/Import/multiple-forward-declarations/Inputs/S1.c
@@ -0,0 +1 @@
+struct S;
diff --git a/test/Import/multiple-forward-declarations/Inputs/S2.c b/test/Import/multiple-forward-declarations/Inputs/S2.c
new file mode 100644
index 00000000000..28377c2760b
--- /dev/null
+++ b/test/Import/multiple-forward-declarations/Inputs/S2.c
@@ -0,0 +1 @@
+struct S;
diff --git a/test/Import/multiple-forward-declarations/test.c b/test/Import/multiple-forward-declarations/test.c
new file mode 100644
index 00000000000..fdaaaf61143
--- /dev/null
+++ b/test/Import/multiple-forward-declarations/test.c
@@ -0,0 +1,4 @@
+// RUN: clang-import-test -import %S/Inputs/S1.c --import %S/Inputs/S2.c -expression %s
+void expr() {
+  struct S *MySPtr;
+}
diff --git a/test/Import/overloaded-function/Inputs/F1.c b/test/Import/overloaded-function/Inputs/F1.c
new file mode 100644
index 00000000000..fb548b464f2
--- /dev/null
+++ b/test/Import/overloaded-function/Inputs/F1.c
@@ -0,0 +1 @@
+void f(int arg) { }
diff --git a/test/Import/overloaded-function/Inputs/F2.c b/test/Import/overloaded-function/Inputs/F2.c
new file mode 100644
index 00000000000..937efe54d84
--- /dev/null
+++ b/test/Import/overloaded-function/Inputs/F2.c
@@ -0,0 +1,4 @@
+struct S { int a; };
+
+void f(const char *arg) { }
+void f(S arg) { }
diff --git a/test/Import/overloaded-function/test.c b/test/Import/overloaded-function/test.c
new file mode 100644
index 00000000000..4f7781bc8e6
--- /dev/null
+++ b/test/Import/overloaded-function/test.c
@@ -0,0 +1,7 @@
+// RUN: clang-import-test -import %S/Inputs/F1.c -import %S/Inputs/F2.c -expression %s
+void expr() {
+  f(2);
+  f("world");
+  S s;
+  f(s);
+}
diff --git a/test/Import/struct-in-namespace/Inputs/N1.cpp b/test/Import/struct-in-namespace/Inputs/N1.cpp
new file mode 100644
index 00000000000..ddb67a51621
--- /dev/null
+++ b/test/Import/struct-in-namespace/Inputs/N1.cpp
@@ -0,0 +1,11 @@
+namespace N {
+  struct S {
+    int a;
+  };
+}
+
+namespace N {
+  struct T {
+    int b;
+  };
+}
diff --git a/test/Import/struct-in-namespace/Inputs/N2.cpp b/test/Import/struct-in-namespace/Inputs/N2.cpp
new file mode 100644
index 00000000000..ad97d5dd52e
--- /dev/null
+++ b/test/Import/struct-in-namespace/Inputs/N2.cpp
@@ -0,0 +1,5 @@
+namespace N {
+  struct U {
+    int c;
+  };
+}
diff --git a/test/Import/struct-in-namespace/Inputs/N3.cpp b/test/Import/struct-in-namespace/Inputs/N3.cpp
new file mode 100644
index 00000000000..e0ec4146747
--- /dev/null
+++ b/test/Import/struct-in-namespace/Inputs/N3.cpp
@@ -0,0 +1,5 @@
+namespace M {
+  struct V {
+    int d;
+  };
+}
diff --git a/test/Import/struct-in-namespace/test.cpp b/test/Import/struct-in-namespace/test.cpp
new file mode 100644
index 00000000000..fd14d82d178
--- /dev/null
+++ b/test/Import/struct-in-namespace/test.cpp
@@ -0,0 +1,7 @@
+// RUN: clang-import-test -import %S/Inputs/N1.cpp -import %S/Inputs/N2.cpp -import %S/Inputs/N3.cpp -expression %s
+void expr() {
+  N::S s;
+  N::T t;
+  N::U u;
+  int d = s.a + t.b + u.c;
+}
diff --git a/test/Import/template-specialization/Inputs/T.cpp b/test/Import/template-specialization/Inputs/T.cpp
new file mode 100644
index 00000000000..b31e2439efe
--- /dev/null
+++ b/test/Import/template-specialization/Inputs/T.cpp
@@ -0,0 +1,14 @@
+template <typename T> struct A {
+};
+
+template <> struct A<int> {
+  struct B {
+    int f;
+  };
+};
+
+template <> struct A<bool> {
+  struct B {
+    int g;
+  };
+};
diff --git a/test/Import/template-specialization/test.cpp b/test/Import/template-specialization/test.cpp
new file mode 100644
index 00000000000..43996c53a77
--- /dev/null
+++ b/test/Import/template-specialization/test.cpp
@@ -0,0 +1,7 @@
+// RUN: clang-import-test -import %S/Inputs/T.cpp -expression %s
+// XFAIL: *
+void expr() {
+  A<int>::B b1;
+  A<bool>::B b2;
+  b1.f + b2.g;
+}
diff --git a/tools/clang-import-test/clang-import-test.cpp b/tools/clang-import-test/clang-import-test.cpp
index 33190af4bf4..d7ab18478c3 100644
--- a/tools/clang-import-test/clang-import-test.cpp
+++ b/tools/clang-import-test/clang-import-test.cpp
@@ -9,6 +9,8 @@
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTImporter.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ExternalASTMerger.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceLocation.h"
@@ -189,61 +191,18 @@ std::unique_ptr<CodeGenerator> BuildCodeGen(CompilerInstance &CI,
 } // end namespace
 
 namespace {
-class TestExternalASTSource : public ExternalASTSource {
-private:
-  llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs;
-  std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ForwardImporters;
-  std::map<CompilerInstance *, std::unique_ptr<ASTImporter>> ReverseImporters;
-
-public:
-  TestExternalASTSource(
-      CompilerInstance &ExpressionCI,
-      llvm::ArrayRef<std::unique_ptr<CompilerInstance>> ImportCIs)
-      : ImportCIs(ImportCIs) {
-    for (const std::unique_ptr<CompilerInstance> &ImportCI : ImportCIs) {
-      ForwardImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
-          ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
-          ImportCI->getASTContext(), ImportCI->getFileManager(),
-          /*MinimalImport=*/true);
-      ReverseImporters[ImportCI.get()] = llvm::make_unique<ASTImporter>(
-          ImportCI->getASTContext(), ImportCI->getFileManager(),
-          ExpressionCI.getASTContext(), ExpressionCI.getFileManager(),
-          /*MinimalImport=*/true);
-    }
-  }
-
-  bool FindExternalVisibleDeclsByName(const DeclContext *DC,
-                                      DeclarationName Name) override {
-    llvm::SmallVector<NamedDecl *, 1> Decls;
-
-    if (isa<TranslationUnitDecl>(DC)) {
-      for (const std::unique_ptr<CompilerInstance> &I : ImportCIs) {
-        DeclarationName FromName = ReverseImporters[I.get()]->Import(Name);
-        DeclContextLookupResult Result =
-            I->getASTContext().getTranslationUnitDecl()->lookup(FromName);
-        for (NamedDecl *FromD : Result) {
-          NamedDecl *D =
-              llvm::cast<NamedDecl>(ForwardImporters[I.get()]->Import(FromD));
-          Decls.push_back(D);
-        }
-      }
-    }
-    if (Decls.empty()) {
-      return false;
-    } else {
-      SetExternalVisibleDeclsForName(DC, Name, Decls);
-      return true;
-    }
-  }
-};
-
+ 
 void AddExternalSource(
     CompilerInstance &CI,
     llvm::ArrayRef<std::unique_ptr<CompilerInstance>> Imports) {
-  ASTContext &AST = CI.getASTContext();
-  auto ES = llvm::make_unique<TestExternalASTSource>(CI, Imports);
-  AST.setExternalSource(ES.release());
-  AST.getTranslationUnitDecl()->setHasExternalVisibleStorage();
+  ExternalASTMerger::ImporterEndpoint Target({CI.getASTContext(), CI.getFileManager()});
+  llvm::SmallVector<ExternalASTMerger::ImporterEndpoint, 3> Sources;
+  for (const std::unique_ptr<CompilerInstance> &CI : Imports) {
+    Sources.push_back({CI->getASTContext(), CI->getFileManager()});
+  }
+  auto ES = llvm::make_unique<ExternalASTMerger>(Target, Sources);
+  CI.getASTContext().setExternalSource(ES.release());
+  CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
 }
 
 llvm::Error ParseSource(const std::string &Path, CompilerInstance &CI,
@@ -292,6 +251,7 @@ Parse(const std::string &Path,
     return std::move(CI);
   }
 }
+
 } // end namespace
 
 int main(int argc, const char **argv) {
-- 
GitLab