nathawes updated this revision to Diff 130496.
nathawes marked 6 inline comments as done.
nathawes added a comment.

- Applied the various refactorings suggested by @ioeric
- Extended c-index-test with a new option to print out the collected unit 
indexing data, and
- Added tests for the unit indexing functionality using the new option
- Fixed formatting


https://reviews.llvm.org/D39050

Files:
  include/clang/Basic/AllDiagnostics.h
  include/clang/Basic/CMakeLists.txt
  include/clang/Basic/Diagnostic.td
  include/clang/Basic/DiagnosticGroups.td
  include/clang/Basic/DiagnosticIDs.h
  include/clang/Basic/DiagnosticIndexKinds.td
  include/clang/Driver/Job.h
  include/clang/Driver/Options.td
  include/clang/Frontend/CompilerInstance.h
  include/clang/Frontend/FrontendOptions.h
  include/clang/Index/DeclOccurrence.h
  include/clang/Index/IndexDataConsumer.h
  include/clang/Index/IndexDiagnostic.h
  include/clang/Index/IndexingAction.h
  include/clang/Index/RecordingAction.h
  include/clang/Index/UnitIndexDataConsumer.h
  include/clang/Index/UnitIndexingAction.h
  include/clang/module.modulemap
  lib/Basic/DiagnosticIDs.cpp
  lib/Driver/Driver.cpp
  lib/Driver/Job.cpp
  lib/Driver/ToolChains/Clang.cpp
  lib/Driver/ToolChains/Darwin.cpp
  lib/Frontend/CompilerInstance.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/FrontendTool/CMakeLists.txt
  lib/FrontendTool/ExecuteCompilerInvocation.cpp
  lib/Index/CMakeLists.txt
  lib/Index/FileIndexData.cpp
  lib/Index/FileIndexData.h
  lib/Index/IndexingAction.cpp
  lib/Index/IndexingContext.cpp
  lib/Index/IndexingContext.h
  lib/Index/UnitIndexDataRecorder.cpp
  lib/Index/UnitIndexDataRecorder.h
  test/Index/Core/Inputs/module/ModDep.h
  test/Index/Core/Inputs/module/ModSystem.h
  test/Index/Core/Inputs/module/ModTop.h
  test/Index/Core/Inputs/module/ModTopSub1.h
  test/Index/Core/Inputs/module/ModTopSub2.h
  test/Index/Core/Inputs/module/module.modulemap
  test/Index/Core/Inputs/sys/system-head.h
  test/Index/Core/Inputs/transitive-include.h
  test/Index/Core/external-source-symbol-attr.m
  test/Index/Core/index-instantiated-source.cpp
  test/Index/Core/index-source.mm
  test/Index/Core/index-subkinds.m
  test/Index/Core/index-system.mm
  test/Index/Core/index-unit.mm
  test/Index/Store/assembly-invocation.c
  tools/c-index-test/core_main.cpp
  tools/diagtool/DiagnosticNames.cpp
  tools/libclang/CXIndexDataConsumer.cpp
  tools/libclang/CXIndexDataConsumer.h

Index: tools/libclang/CXIndexDataConsumer.h
===================================================================
--- tools/libclang/CXIndexDataConsumer.h
+++ tools/libclang/CXIndexDataConsumer.h
@@ -463,12 +463,12 @@
 private:
   bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles,
                            ArrayRef<index::SymbolRelation> Relations,
-                           FileID FID, unsigned Offset,
+                           FileID FID, unsigned Offset, bool IsInSystemFile,
                            ASTNodeInfo ASTNode) override;
 
   bool handleModuleOccurence(const ImportDecl *ImportD,
-                             index::SymbolRoleSet Roles,
-                             FileID FID, unsigned Offset) override;
+                             index::SymbolRoleSet Roles, FileID FID,
+                             unsigned Offset, bool IsInSystemFile) override;
 
   void finish() override;
 
Index: tools/libclang/CXIndexDataConsumer.cpp
===================================================================
--- tools/libclang/CXIndexDataConsumer.cpp
+++ tools/libclang/CXIndexDataConsumer.cpp
@@ -150,11 +150,9 @@
 };
 }
 
-bool CXIndexDataConsumer::handleDeclOccurence(const Decl *D,
-                                              SymbolRoleSet Roles,
-                                             ArrayRef<SymbolRelation> Relations,
-                                              FileID FID, unsigned Offset,
-                                              ASTNodeInfo ASTNode) {
+bool CXIndexDataConsumer::handleDeclOccurence(
+    const Decl *D, SymbolRoleSet Roles, ArrayRef<SymbolRelation> Relations,
+    FileID FID, unsigned Offset, bool IsInSystemFile, ASTNodeInfo ASTNode) {
   SourceLocation Loc = getASTContext().getSourceManager()
       .getLocForStartOfFile(FID).getLocWithOffset(Offset);
 
@@ -219,9 +217,9 @@
 }
 
 bool CXIndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD,
-                                                SymbolRoleSet Roles,
-                                                FileID FID,
-                                                unsigned Offset) {
+                                                SymbolRoleSet Roles, FileID FID,
+                                                unsigned Offset,
+                                                bool IsInSystemFile) {
   IndexingDeclVisitor(*this, SourceLocation(), nullptr).Visit(ImportD);
   return !shouldAbort();
 }
Index: tools/diagtool/DiagnosticNames.cpp
===================================================================
--- tools/diagtool/DiagnosticNames.cpp
+++ tools/diagtool/DiagnosticNames.cpp
@@ -43,6 +43,7 @@
 #include "clang/Basic/DiagnosticSemaKinds.inc"
 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
 #include "clang/Basic/DiagnosticRefactoringKinds.inc"
+#include "clang/Basic/DiagnosticIndexKinds.inc"
 #undef DIAG
 };
 
Index: tools/c-index-test/core_main.cpp
===================================================================
--- tools/c-index-test/core_main.cpp
+++ tools/c-index-test/core_main.cpp
@@ -12,15 +12,17 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendAction.h"
-#include "clang/Index/IndexingAction.h"
+#include "clang/Index/CodegenNameGenerator.h"
 #include "clang/Index/IndexDataConsumer.h"
 #include "clang/Index/USRGeneration.h"
-#include "clang/Index/CodegenNameGenerator.h"
+#include "clang/Index/UnitIndexDataConsumer.h"
+#include "clang/Index/UnitIndexingAction.h"
 #include "clang/Serialization/ASTReader.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/PrettyStackTrace.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
-#include "llvm/Support/PrettyStackTrace.h"
 
 using namespace clang;
 using namespace clang::index;
@@ -33,18 +35,20 @@
 enum class ActionType {
   None,
   PrintSourceSymbols,
+  PrintSourceUnit,
 };
 
 namespace options {
 
 static cl::OptionCategory IndexTestCoreCategory("index-test-core options");
 
-static cl::opt<ActionType>
-Action(cl::desc("Action:"), cl::init(ActionType::None),
-       cl::values(
-          clEnumValN(ActionType::PrintSourceSymbols,
-                     "print-source-symbols", "Print symbols from source")),
-       cl::cat(IndexTestCoreCategory));
+static cl::opt<ActionType> Action(
+    cl::desc("Action:"), cl::init(ActionType::None),
+    cl::values(clEnumValN(ActionType::PrintSourceSymbols,
+                          "print-source-symbols", "Print symbols from source"),
+               clEnumValN(ActionType::PrintSourceUnit, "print-source-unit",
+                          "Print unit info from source")),
+    cl::cat(IndexTestCoreCategory));
 
 static cl::extrahelp MoreHelp(
   "\nAdd \"-- <compiler arguments>\" at the end to setup the compiler "
@@ -72,6 +76,42 @@
 static void printSymbolNameAndUSR(const Decl *D, ASTContext &Ctx,
                                   raw_ostream &OS);
 
+static void printDeclOccurrence(const Decl *D, SymbolRoleSet Roles,
+                                ArrayRef<SymbolRelation> Relations, FileID FID,
+                                unsigned Offset, bool IsInSystemFile,
+                                CodegenNameGenerator &CGNameGen,
+                                raw_ostream &OS) {
+  ASTContext &Ctx = D->getASTContext();
+  SourceManager &SM = Ctx.getSourceManager();
+
+  unsigned Line = SM.getLineNumber(FID, Offset);
+  unsigned Col = SM.getColumnNumber(FID, Offset);
+  OS << Line << ':' << Col << " | ";
+
+  printSymbolInfo(getSymbolInfo(D), OS);
+  OS << " | ";
+
+  printSymbolNameAndUSR(D, Ctx, OS);
+  OS << " | ";
+
+  if (CGNameGen.writeName(D, OS))
+    OS << "<no-cgname>";
+  OS << " | ";
+
+  printSymbolRoles(Roles, OS);
+  OS << " | ";
+
+  OS << "rel: " << Relations.size() << '\n';
+
+  for (auto &SymRel : Relations) {
+    OS << '\t';
+    printSymbolRoles(SymRel.Roles, OS);
+    OS << " | ";
+    printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS);
+    OS << '\n';
+  }
+}
+
 namespace {
 
 class PrintIndexDataConsumer : public IndexDataConsumer {
@@ -87,44 +127,17 @@
   }
 
   bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
-                           ArrayRef<SymbolRelation> Relations,
-                           FileID FID, unsigned Offset,
+                           ArrayRef<SymbolRelation> Relations, FileID FID,
+                           unsigned Offset, bool IsInSystemFile,
                            ASTNodeInfo ASTNode) override {
-    ASTContext &Ctx = D->getASTContext();
-    SourceManager &SM = Ctx.getSourceManager();
-
-    unsigned Line = SM.getLineNumber(FID, Offset);
-    unsigned Col = SM.getColumnNumber(FID, Offset);
-    OS << Line << ':' << Col << " | ";
-
-    printSymbolInfo(getSymbolInfo(D), OS);
-    OS << " | ";
-
-    printSymbolNameAndUSR(D, Ctx, OS);
-    OS << " | ";
-
-    if (CGNameGen->writeName(D, OS))
-      OS << "<no-cgname>";
-    OS << " | ";
-
-    printSymbolRoles(Roles, OS);
-    OS << " | ";
-
-    OS << "rel: " << Relations.size() << '\n';
-
-    for (auto &SymRel : Relations) {
-      OS << '\t';
-      printSymbolRoles(SymRel.Roles, OS);
-      OS << " | ";
-      printSymbolNameAndUSR(SymRel.RelatedSymbol, Ctx, OS);
-      OS << '\n';
-    }
-
+    printDeclOccurrence(D, Roles, Relations, FID, Offset, IsInSystemFile,
+                        *CGNameGen, OS);
     return true;
   }
 
   bool handleModuleOccurence(const ImportDecl *ImportD, SymbolRoleSet Roles,
-                             FileID FID, unsigned Offset) override {
+                             FileID FID, unsigned Offset,
+                             bool IsInSystemFile) override {
     ASTContext &Ctx = ImportD->getASTContext();
     SourceManager &SM = Ctx.getSourceManager();
 
@@ -235,6 +248,156 @@
   return false;
 }
 
+class PrintUnitDataConsumer : public UnitIndexDataConsumer {
+  struct Dependency {
+    enum Kind { File, Module };
+
+    Kind Kind;
+    std::string Name;
+    bool IsSystem;
+
+    void print(raw_ostream &OS) const {
+      switch (Kind) {
+      case File:
+        OS << "File";
+        break;
+      case Module:
+        OS << "Module";
+        break;
+      }
+      OS << " | " << (IsSystem ? "system" : "user") << " | " << Name << '\n';
+    }
+  };
+
+  struct Include {
+    std::string Source;
+    std::string Target;
+    unsigned Line;
+
+    void print(raw_ostream &OS) const {
+      OS << Source << ':' << Line << " -> " << Target << '\n';
+    }
+  };
+
+  struct FileOccurrences {
+    SourceManager &SM;
+    FileID FID;
+    bool IsSystem;
+    std::vector<DeclOccurrence> Occurrences;
+
+    void print(raw_ostream &OS, CodegenNameGenerator &CGNameGen) const {
+      const FileEntry *FE = SM.getFileEntryForID(FID);
+      OS << '\n' << FE->getName() << "\n----------\n";
+      for (const DeclOccurrence &Occur : Occurrences) {
+        printDeclOccurrence(Occur.Dcl, Occur.Roles, Occur.Relations, FID,
+                            Occur.Offset, IsSystem, CGNameGen, OS);
+      }
+    }
+  };
+
+  raw_ostream &OS;
+  UnitDetails UnitInfo;
+  const CompilerInstance &CI;
+  CodegenNameGenerator CGNameGen;
+  bool IndexModDependencies = true;
+  std::vector<Dependency> Dependencies;
+  std::vector<Include> Includes;
+  std::vector<FileOccurrences> FileOccurInfos;
+
+public:
+  PrintUnitDataConsumer(raw_ostream &OS, UnitDetails UnitInfo,
+                        bool IndexModDeps)
+      : OS(OS), UnitInfo(UnitInfo), CI(UnitInfo.CI),
+        CGNameGen(CI.getASTContext()), IndexModDependencies(IndexModDeps) {}
+
+  void handleFileDependency(const FileEntry *FE, bool IsSystem) override {
+    Dependencies.push_back({Dependency::File, FE->getName(), IsSystem});
+  }
+
+  void handleModuleImport(const serialization::ModuleFile &Mod,
+                          bool IsSystem) override {
+    Dependencies.push_back({Dependency::Module, Mod.FileName, IsSystem});
+  }
+
+  void handleInclude(const FileEntry *Source, unsigned Line,
+                     const FileEntry *Target) override {
+    Includes.push_back({Source->getName(), Target->getName(), Line});
+  }
+
+  bool
+  shouldIndexModuleDependency(const serialization::ModuleFile &Mod) override {
+    return IndexModDependencies;
+  }
+
+  bool handleFileOccurrences(FileID FID,
+                             ArrayRef<DeclOccurrence> OccurrencesSortedByOffset,
+                             bool IsSystem) override {
+    SourceManager &SM = CI.getASTContext().getSourceManager();
+    FileOccurInfos.push_back({SM, FID, IsSystem, OccurrencesSortedByOffset});
+    return false;
+  }
+
+  void finish() override {
+    OS << sys::path::filename(UnitInfo.OutputFile) << '\n'
+       << "----------\n"
+       << "is-system: " << UnitInfo.IsSystemUnit << '\n'
+       << "is-module: " << UnitInfo.IsModuleUnit << '\n'
+       << "module-name: " << UnitInfo.ModuleName << '\n'
+       << "has-main: " << !!UnitInfo.RootFile << '\n'
+       << "main-path: "
+       << (UnitInfo.RootFile ? UnitInfo.RootFile->getName() : "") << '\n'
+       << "out-file: " << UnitInfo.OutputFile << '\n'
+       << "target: " << UnitInfo.CI.getTargetOpts().Triple << '\n'
+       << "is-debug: " << UnitInfo.IsDebugCompilation << '\n';
+
+    OS << "\nDEPEND START\n";
+    for (const Dependency &Dep : Dependencies) {
+      Dep.print(OS);
+    }
+    OS << "DEPEND END (" << Dependencies.size() << ")\n";
+    OS << "\nINCLUDE START\n";
+    for (const Include &Inc : Includes) {
+      Inc.print(OS);
+    }
+    OS << "INCLUDE END (" << Includes.size() << ")\n";
+    for (const FileOccurrences &FileInfo : FileOccurInfos) {
+      FileInfo.print(OS, CGNameGen);
+    }
+    OS << '\n';
+  }
+};
+
+static bool printSourceUnit(ArrayRef<const char *> Args, bool IndexLocals,
+                            bool IndexModDeps) {
+  SmallVector<const char *, 4> ArgsWithProgName;
+  ArgsWithProgName.push_back("clang");
+  ArgsWithProgName.append(Args.begin(), Args.end());
+  IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
+      CompilerInstance::createDiagnostics(new DiagnosticOptions));
+  auto CInvok = createInvocationFromCommandLine(ArgsWithProgName, Diags);
+  if (!CInvok)
+    return true;
+
+  raw_ostream &OS = outs();
+  UnitIndexingOptions IndexOpts;
+  IndexOpts.IndexFunctionLocals = IndexLocals;
+
+  auto ConsumerFactory = [&OS, IndexModDeps](UnitDetails UnitInfo) {
+    return llvm::make_unique<PrintUnitDataConsumer>(OS, std::move(UnitInfo),
+                                                    IndexModDeps);
+  };
+
+  std::unique_ptr<FrontendAction> IndexAction;
+  IndexAction = createUnitIndexingAction(ConsumerFactory, IndexOpts,
+                                         /*WrappedAction=*/nullptr);
+
+  auto PCHContainerOps = std::make_shared<PCHContainerOperations>();
+  std::unique_ptr<ASTUnit> Unit(ASTUnit::LoadFromCompilerInvocationAction(
+      std::move(CInvok), PCHContainerOps, Diags, IndexAction.get()));
+
+  return !Unit;
+}
+
 //===----------------------------------------------------------------------===//
 // Helper Utils
 //===----------------------------------------------------------------------===//
@@ -305,5 +468,15 @@
     return printSourceSymbols(CompArgs, options::DumpModuleImports, options::IncludeLocals);
   }
 
+  if (options::Action == ActionType::PrintSourceUnit) {
+    if (CompArgs.empty()) {
+      errs()
+          << "error: missing compiler args; pass '-- <compiler arguments>'\n";
+      return 1;
+    }
+    return printSourceUnit(CompArgs, options::IncludeLocals,
+                           /*IndexModDepedencies=*/true);
+  }
+
   return 0;
 }
Index: test/Index/Store/assembly-invocation.c
===================================================================
--- /dev/null
+++ test/Index/Store/assembly-invocation.c
@@ -0,0 +1,3 @@
+// Make sure it doesn't crash.
+// RUN: %clang -target x86_64-apple-macosx10.7 -S %s -o %t.s
+// RUN: %clang -target x86_64-apple-macosx10.7 -c %t.s -o %t.o -index-store-path %t.idx
Index: test/Index/Core/index-unit.mm
===================================================================
--- /dev/null
+++ test/Index/Core/index-unit.mm
@@ -0,0 +1,135 @@
+// RUN: rm -rf %t.mcp
+// RUN: c-index-test core -print-source-unit -- -arch x86_64 -mmacosx-version-min=10.7 -c %s -o %t.o -isystem %S/Inputs/sys -fmodules -fmodules-cache-path=%t.mcp -Xclang -fdisable-module-hash -I %S/Inputs/module -I %S/Inputs | FileCheck %s
+
+@import ModDep;
+@import ModSystem;
+
+
+
+// CHECK: ModTop.pcm
+
+// CHECK: is-system: 0
+// CHECK: is-module: 1
+// CHECK: module-name: ModTop
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModTop.pcm
+
+// CHECK: DEPEND START
+// CHECK: File | user | {{.*}}/Inputs/module/ModTopSub2.h
+// CHECK: File | user | {{.*}}/Inputs/module/ModTopSub1.h
+// CHECK: File | user | {{.*}}/Inputs/module/ModTop.h
+// CHECK: DEPEND END (3)
+
+// CHECK: INCLUDE START
+// CHECK: INCLUDE END (0)
+
+// CHECK: {{.*}}/Inputs/module/ModTop.h
+// CHECK: 2:9 | struct/C | <no-name> | c:{{.*}} | <no-cgname> | Def | rel: 0
+// CHECK: 2:19 | type-alias/C | ModTopStruct | [[ModTopStruct_USR:.*]] | <no-cgname> | Def | rel: 0
+// CHECK: 4:6 | function/C | ModTop_func | {{.*}} | __Z11ModTop_funcv | Decl | rel: 0
+
+// CHECK: {{.*}}/Inputs/module/ModTopSub1.h
+// CHECK: 1:6 | function/C | ModTopSub1_func | {{.*}} | __Z15ModTopSub1_funcv | Decl | rel: 0
+
+
+
+// CHECK: ModDep.pcm
+
+// CHECK: is-system: 0
+// CHECK: is-module: 1
+// CHECK: module-name: ModDep
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModDep.pcm
+
+// CHECK: DEPEND START
+// CHECK: File | user | {{.*}}/Inputs/module/ModDep.h
+// CHECK: Module | user | {{.*}}/ModTop.pcm
+// CHECK: DEPEND END (2)
+
+// CHECK: INCLUDE START
+// CHECK: INCLUDE END (0)
+
+// CHECK: {{.*}}/Inputs/module/ModDep.h
+// CHECK: 3:6 | function/C | ModDep_func | [[ModDep_func_USR:.*]] | __Z11ModDep_func12ModTopStruct | Decl | rel: 0
+// CHECK: 3:18 | type-alias/C | ModTopStruct | [[ModTopStruct_USR]] | <no-cgname> | Ref,RelCont | rel: 1
+// CHECK-NEXT: RelCont | ModDep_func | [[ModDep_func_USR]]
+
+
+
+// CHECK: ModSystem.pcm
+
+// CHECK: is-system: 1
+// CHECK: is-module: 1
+// CHECK: module-name: ModSystem
+// CHECK: has-main: 0
+// CHECK: main-path: {{$}}
+// CHECK: out-file: {{.*}}/ModSystem.pcm
+
+// CHECK: DEPEND START
+// CHECK: File | system | {{.*}}/Inputs/module/ModSystem.h
+// CHECK: DEPEND END (1)
+
+// CHECK: INCLUDE START
+// CHECK: INCLUDE END (0)
+
+// CHECK: {{.*}}/Inputs/module/ModSystem.h
+// CHECK: 2:9 | struct/C | <no-name> | {{.*}} | <no-cgname> | Def | rel: 0
+// CHECK: 2:19 | type-alias/C | ModSystemStruct | {{.*}} | <no-cgname> | Def | rel: 0
+// CHECK: 4:6 | function/C | ModSystem_func | {{.*}} | __Z14ModSystem_funcv | Decl | rel: 0
+
+
+
+// CHECK: index-unit.mm.o
+
+// CHECK: is-system: 0
+// CHECK: is-module: 0
+// CHECK: module-name: {{$}}
+// CHECK: has-main: 1
+// CHECK: main-path: {{.*}}/index-unit.mm
+// CHECK: out-file: {{.*}}/index-unit.mm.o
+
+// CHECK: DEPEND START
+// CHECK: File | user | {{.*}}/index-unit.mm
+// CHECK: File | user | {{.*}}/Inputs/module/module.modulemap
+// CHECK: File | user | {{.*}}/Inputs/transitive-include.h
+// CHECK: File | system | {{.*}}/Inputs/sys/system-head.h
+// CHECK: Module | user | {{.*}}/ModDep.pcm
+// CHECK: Module | system | {{.*}}/ModSystem.pcm
+// CHECK: DEPEND END (6)
+
+// CHECK: INCLUDE START
+// CHECK: {{.*}}index-unit.mm:[[@LINE+1]] -> {{.*}}/Inputs/transitive-include.h
+#include "transitive-include.h"
+// CHECK: {{.*}}/Inputs/transitive-include.h:1 -> {{.*}}/Inputs/sys/system-head.h
+// CHECK: INCLUDE END (2)
+
+// CHECK: {{.*}}/Inputs/transitive-include.h
+// CHECK: 3:8 | struct/C | Point | [[Point_USR:.*]] | <no-cgname> | Def | rel: 0
+// CHECK: 4:7 | field/C | x | {{.*}} | <no-cgname> | Def,RelChild | rel: 1
+// CHECK-NEXT: RelChild | Point | [[Point_USR]]
+// CHECK: 5:7 | field/C | y | {{.*}} | <no-cgname> | Def,RelChild | rel: 1
+// CHECK-NEXT: RelChild | Point | [[Point_USR]]
+
+// CHECK: {{.*}}/Inputs/sys/system-head.h
+// CHECK: 19:12 | class/ObjC | Base | [[Base_USR:.*]] | _OBJC_CLASS_$_Base | Decl | rel: 0
+// CHECK: 23:11 | protocol/ObjC | Prot1 | [[Prot1_USR:.*]] | <no-cgname> | Decl | rel: 0
+// CHECK: 29:11 | protocol/ObjC | Prot2 | [[Prot2_USR:.*]] | <no-cgname> | Decl | rel: 0
+// CHECK: 29:17 | protocol/ObjC | Prot1 | [[Prot1_USR]] | <no-cgname> | Ref,RelBase,RelCont | rel: 1
+// CHECK-NEXT: RelBase,RelCont | Prot2 | [[Prot2_USR]]
+// CHECK: 39:12 | class/ObjC | Sub | [[Sub_USR:.*]] | _OBJC_CLASS_$_Sub | Decl | rel: 0
+// CHECK: 39:18 | class/ObjC | Base | [[Base_USR]] | _OBJC_CLASS_$_Base | Ref,RelBase,RelCont | rel: 1
+// CHECK-NEXT: RelBase,RelCont | Sub | [[Sub_USR]]
+// CHECK: 39:23 | protocol/ObjC | Prot2 | [[Prot2_USR]] | <no-cgname> | Ref,RelBase,RelCont | rel: 1
+// CHECK-NEXT: RelBase,RelCont | Sub | [[Sub_USR]]
+// CHECK: 39:30 | protocol/ObjC | Prot1 | [[Prot1_USR]] | <no-cgname> | Ref,RelBase,RelCont | rel: 1
+// CHECK-NEXT: RelBase,RelCont | Sub | [[Sub_USR]]
+// CHECK: 41:8 | instance-method/ObjC | getit | {{.*}} | -[Sub getit] | Decl,Dyn,RelChild | rel: 1
+// CHECK-NEXT: RelChild | Sub | [[Sub_USR]]
+// CHECK: 45:7 | class/C++ | Cls | [[Cls_USR:.*]] | <no-cgname> | Def | rel: 0
+// CHECK: 50:7 | class/C++ | SubCls1 | [[SubCls1_USR:.*]] | <no-cgname> | Def | rel: 0
+// CHECK: 50:24 | class/C++ | Cls | [[Cls_USR]] | <no-cgname> | Ref,RelBase,RelCont | rel: 1
+// CHECK-NEXT: RelBase,RelCont | SubCls1 | [[SubCls1_USR]]
+// CHECK: 52:12 | field/C++ | f | {{.*}} | <no-cgname> | Def,RelChild | rel: 1
+// CHECK-NEXT: RelChild | SubCls1 | [[SubCls1_USR]]
Index: test/Index/Core/index-system.mm
===================================================================
--- test/Index/Core/index-system.mm
+++ test/Index/Core/index-system.mm
@@ -1,3 +1,4 @@
 // RUN: c-index-test core -print-source-symbols -- %s -isystem %S/Inputs/sys | FileCheck %S/Inputs/sys/system-head.h
+// RUN: c-index-test core -print-source-unit -- %s -isystem %S/Inputs/sys | FileCheck -check-prefixes=UNIT,CHECK %S/Inputs/sys/system-head.h
 
 #include "system-head.h"
Index: test/Index/Core/index-subkinds.m
===================================================================
--- test/Index/Core/index-subkinds.m
+++ test/Index/Core/index-subkinds.m
@@ -1,4 +1,5 @@
 // RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
+// RUN: c-index-test core -print-source-unit -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
 
 // CHECK: [[@LINE+1]]:12 | class/ObjC | XCTestCase | c:objc(cs)XCTestCase | _OBJC_CLASS_$_XCTestCase | Decl | rel: 0
 @interface XCTestCase
Index: test/Index/Core/index-source.mm
===================================================================
--- test/Index/Core/index-source.mm
+++ test/Index/Core/index-source.mm
@@ -1,4 +1,5 @@
 // RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
+// RUN: c-index-test core -print-source-unit -- %s -target x86_64-apple-macosx10.7 | FileCheck -check-prefixes=CHECK %s
 
 @interface MyCls
 @end
Index: test/Index/Core/index-instantiated-source.cpp
===================================================================
--- test/Index/Core/index-instantiated-source.cpp
+++ test/Index/Core/index-instantiated-source.cpp
@@ -1,4 +1,5 @@
 // RUN: c-index-test core -print-source-symbols -- %s -std=c++14 -target x86_64-apple-macosx10.7 | FileCheck %s
+// RUN: c-index-test core -print-source-unit -- %s -std=c++14 -target x86_64-apple-macosx10.7 | FileCheck %s
 // References to declarations in instantiations should be canonicalized:
 
 template<typename T>
Index: test/Index/Core/external-source-symbol-attr.m
===================================================================
--- test/Index/Core/external-source-symbol-attr.m
+++ test/Index/Core/external-source-symbol-attr.m
@@ -1,4 +1,5 @@
 // RUN: c-index-test core -print-source-symbols -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
+// RUN: c-index-test core -print-source-unit -- %s -target x86_64-apple-macosx10.7 | FileCheck %s
 
 #define EXT_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name)))
 #define GEN_DECL(mod_name) __attribute__((external_source_symbol(language="Swift", defined_in=mod_name, generated_declaration)))
Index: test/Index/Core/Inputs/transitive-include.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/transitive-include.h
@@ -0,0 +1,6 @@
+#include "system-head.h"
+
+struct Point {
+  int x;
+  int y;
+};
Index: test/Index/Core/Inputs/sys/system-head.h
===================================================================
--- test/Index/Core/Inputs/sys/system-head.h
+++ test/Index/Core/Inputs/sys/system-head.h
@@ -1,3 +1,20 @@
+// UNIT: index-system.mm.o
+// UNIT: is-system: 0
+// UNIT: is-module: 0
+// UNIT: has-main: 1
+// UNIT: main-path: {{.*}}index-system.mm
+// UNIT: out-file: {{.*}}index-system.mm.o
+// UNIT: is-debug: 1
+
+// UNIT: DEPEND START
+// UNIT: File | user | {{.*}}index-system.mm
+// UNIT: File | system | {{.*}}system-head.h
+// UNIT: DEPEND END (2)
+
+// UNIT: INCLUDE START
+// UNIT: {{.*}}index-system.mm:4 -> {{.*}}system-head.h
+// UNIT: INCLUDE END (1)
+
 // CHECK: [[@LINE+1]]:12 | class/ObjC | Base | [[Base_USR:.*]] | {{.*}} | Decl | rel: 0
 @interface Base
 @end
Index: test/Index/Core/Inputs/module/module.modulemap
===================================================================
--- test/Index/Core/Inputs/module/module.modulemap
+++ test/Index/Core/Inputs/module/module.modulemap
@@ -1 +1,13 @@
 module ModA { header "ModA.h" export * }
+module ModDep { header "ModDep.h" export * }
+module ModSystem [system] { header "ModSystem.h" export * }
+module ModTop {
+  header "ModTop.h"
+  export *
+  module Sub1 {
+    header "ModTopSub1.h"
+  }
+  module Sub2 {
+    header "ModTopSub2.h"
+  }
+}
Index: test/Index/Core/Inputs/module/ModTopSub2.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/module/ModTopSub2.h
@@ -0,0 +1 @@
+// This header has no symbols, intended to show up as file dependency.
Index: test/Index/Core/Inputs/module/ModTopSub1.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/module/ModTopSub1.h
@@ -0,0 +1 @@
+void ModTopSub1_func(void);
Index: test/Index/Core/Inputs/module/ModTop.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/module/ModTop.h
@@ -0,0 +1,4 @@
+
+typedef struct {} ModTopStruct;
+
+void ModTop_func(void);
Index: test/Index/Core/Inputs/module/ModSystem.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/module/ModSystem.h
@@ -0,0 +1,4 @@
+
+typedef struct {} ModSystemStruct;
+
+void ModSystem_func(void);
Index: test/Index/Core/Inputs/module/ModDep.h
===================================================================
--- /dev/null
+++ test/Index/Core/Inputs/module/ModDep.h
@@ -0,0 +1,3 @@
+#include "ModTop.h"
+
+void ModDep_func(ModTopStruct s);
Index: lib/Index/UnitIndexDataRecorder.h
===================================================================
--- /dev/null
+++ lib/Index/UnitIndexDataRecorder.h
@@ -0,0 +1,52 @@
+//===--- UnitIndexDataRecorder.h - Persist index data to the file system --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_UNITINDEXDATARECORDER_H
+#define LLVM_CLANG_LIB_INDEX_UNITINDEXDATARECORDER_H
+
+#include "clang/Index/RecordingAction.h"
+#include "clang/Index/UnitIndexDataConsumer.h"
+
+namespace clang {
+class DiagnosticsEngine;
+class FrontendOptions;
+
+namespace index {
+
+/// Persists the provided index data for a single translation unit out to the
+/// file system.
+class UnitIndexDataRecorder : public UnitIndexDataConsumer {
+protected:
+  UnitDetails UnitInfo;
+
+public:
+  UnitIndexDataRecorder(UnitDetails UnitInfo, RecordingOptions RecordOpts);
+
+  void handleFileDependency(const FileEntry *FE, bool IsSystem) override;
+
+  void handleInclude(const FileEntry *Source, unsigned Line,
+                     const FileEntry *Target) override;
+
+  void handleModuleImport(const serialization::ModuleFile &Mod,
+                          bool IsSystem) override;
+
+  bool
+  shouldIndexModuleDependency(const serialization::ModuleFile &Mod) override;
+
+  bool handleFileOccurrences(FileID FID,
+                             ArrayRef<DeclOccurrence> OccurrencesSortedByOffset,
+                             bool IsSystem) override;
+
+  void finish() override;
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
Index: lib/Index/UnitIndexDataRecorder.cpp
===================================================================
--- /dev/null
+++ lib/Index/UnitIndexDataRecorder.cpp
@@ -0,0 +1,52 @@
+//===--- UnitIndexDataRecorder.cpp - Persist index data to the file system ===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UnitIndexDataRecorder.h"
+#include "clang/Frontend/FrontendOptions.h"
+
+using namespace clang;
+using namespace clang::index;
+
+UnitIndexDataRecorder::UnitIndexDataRecorder(UnitDetails UnitDetails,
+                                             RecordingOptions RecordOpts)
+    : UnitInfo(UnitDetails) {
+  // TODO
+}
+
+void UnitIndexDataRecorder::handleFileDependency(const FileEntry *FE,
+                                                 bool IsSystem) {
+  // TODO
+}
+
+void UnitIndexDataRecorder::handleInclude(const FileEntry *Source,
+                                          unsigned int Line,
+                                          const FileEntry *Target) {
+  // TODO
+}
+
+void UnitIndexDataRecorder::handleModuleImport(
+    const serialization::ModuleFile &Mod, bool IsSystem) {
+  // TODO
+}
+
+bool UnitIndexDataRecorder::shouldIndexModuleDependency(
+    const serialization::ModuleFile &Mod) {
+  // TODO
+  return true;
+};
+
+bool UnitIndexDataRecorder::handleFileOccurrences(
+    FileID FID, ArrayRef<DeclOccurrence> Occurs, bool IsSystem) {
+  // TODO
+  return false;
+};
+
+void UnitIndexDataRecorder::finish(){
+    // TODO
+};
Index: lib/Index/IndexingContext.h
===================================================================
--- lib/Index/IndexingContext.h
+++ lib/Index/IndexingContext.h
@@ -11,9 +11,11 @@
 #define LLVM_CLANG_LIB_INDEX_INDEXINGCONTEXT_H
 
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
 #include "clang/Index/IndexSymbol.h"
 #include "clang/Index/IndexingAction.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 
 namespace clang {
   class ASTContext;
@@ -29,25 +31,51 @@
   class Stmt;
   class Expr;
   class TypeLoc;
-  class SourceLocation;
+  class DirectoryEntry;
 
 namespace index {
   class IndexDataConsumer;
 
+/// Tracks the current system root path and computes and caches whether a
+/// file is considered a system file or not
+class SystemFileCache {
+  std::string SysrootPath;
+  // Records whether a directory entry is system or not.
+  llvm::DenseMap<const DirectoryEntry *, bool> DirEntries;
+  // Keeps track of the last check for whether a FileID is system or
+  // not. This is used to speed up isSystemFile() call.
+  std::pair<FileID, bool> LastFileCheck;
+
+public:
+  SystemFileCache() = default;
+  SystemFileCache(std::string SysrootPath);
+
+  void setSysrootPath(StringRef path);
+  StringRef getSysrootPath() const { return SysrootPath; }
+  bool isSystem(FileID FID, SourceManager &SM);
+};
+
+/// Generates and reports indexing data to the provided \c IndexDataConsumer
+/// for any AST nodes passed to its various \c index* methods.
 class IndexingContext {
   IndexingOptions IndexOpts;
+  SystemFileCache SystemCache;
   IndexDataConsumer &DataConsumer;
   ASTContext *Ctx = nullptr;
 
 public:
   IndexingContext(IndexingOptions IndexOpts, IndexDataConsumer &DataConsumer)
-    : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}
+      : IndexOpts(IndexOpts), DataConsumer(DataConsumer) {}
 
   const IndexingOptions &getIndexOpts() const { return IndexOpts; }
+  SystemFileCache &getSystemCache() { return SystemCache; }
   IndexDataConsumer &getDataConsumer() { return DataConsumer; }
 
   void setASTContext(ASTContext &ctx) { Ctx = &ctx; }
 
+  void setSysrootPath(StringRef path) { SystemCache.setSysrootPath(path); }
+  StringRef getSysrootPath() const { return SystemCache.getSysrootPath(); }
+
   bool shouldIndex(const Decl *D);
 
   const LangOptions &getLangOpts() const;
Index: lib/Index/IndexingContext.cpp
===================================================================
--- lib/Index/IndexingContext.cpp
+++ lib/Index/IndexingContext.cpp
@@ -8,22 +8,77 @@
 //===----------------------------------------------------------------------===//
 
 #include "IndexingContext.h"
-#include "clang/Index/IndexDataConsumer.h"
 #include "clang/AST/ASTContext.h"
-#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Index/IndexDataConsumer.h"
+#include "llvm/Support/Path.h"
 
 using namespace clang;
 using namespace index;
+using namespace llvm;
 
 static bool isGeneratedDecl(const Decl *D) {
   if (auto *attr = D->getAttr<ExternalSourceSymbolAttr>()) {
     return attr->getGeneratedDeclaration();
   }
   return false;
 }
 
+void SystemFileCache::setSysrootPath(llvm::StringRef Path) {
+  // Ignore sysroot path if it points to root, otherwise every header will be
+  // treated as system one.
+  SysrootPath = sys::path::root_path(Path) == Path ? StringRef() : Path;
+
+  // Invalidate existing results
+  LastFileCheck = {FileID(), false};
+  DirEntries.clear();
+}
+
+SystemFileCache::SystemFileCache(std::string Path) { setSysrootPath(Path); }
+
+bool SystemFileCache::isSystem(clang::FileID FID, clang::SourceManager &SM) {
+  if (LastFileCheck.first == FID)
+    return LastFileCheck.second;
+
+  auto Result = [&](bool Res) -> bool {
+    LastFileCheck = {FID, Res};
+    return Res;
+  };
+
+  bool Invalid = false;
+  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
+  if (Invalid || !SEntry.isFile())
+    return Result(false);
+
+  const SrcMgr::FileInfo &FI = SEntry.getFile();
+  if (FI.getFileCharacteristic() != SrcMgr::C_User)
+    return Result(true);
+
+  auto *CC = FI.getContentCache();
+  if (!CC)
+    return Result(false);
+  auto *FE = CC->OrigEntry;
+  if (!FE)
+    return Result(false);
+
+  if (SysrootPath.empty())
+    return Result(false);
+
+  // Check if directory is in sysroot so that we can consider system headers
+  // even the headers found via a user framework search path, pointing inside
+  // sysroot.
+  auto DirEntry = FE->getDir();
+  auto Pair = DirEntries.insert(std::make_pair(DirEntry, false));
+  bool &IsSystemDir = Pair.first->second;
+  bool WasInserted = Pair.second;
+  if (WasInserted) {
+    IsSystemDir = StringRef(DirEntry->getName()).startswith(SysrootPath);
+  }
+  return Result(IsSystemDir);
+}
+
 bool IndexingContext::shouldIndex(const Decl *D) {
   return !isGeneratedDecl(D);
 }
@@ -93,12 +148,8 @@
   if (FID.isInvalid())
     return true;
 
-  bool Invalid = false;
-  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
-  if (Invalid || !SEntry.isFile())
-    return true;
-
-  if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
+  bool IsInSystemFile = SystemCache.isSystem(FID, SM);
+  if (IsInSystemFile) {
     switch (IndexOpts.SystemSymbolFilter) {
     case IndexingOptions::SystemSymbolFilterKind::None:
       return true;
@@ -112,7 +163,8 @@
   if (ImportD->isImplicit())
     Roles |= (unsigned)SymbolRole::Implicit;
 
-  return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset);
+  return DataConsumer.handleModuleOccurence(ImportD, Roles, FID, Offset,
+                                            IsInSystemFile);
 }
 
 bool IndexingContext::isTemplateImplicitInstantiation(const Decl *D) {
@@ -205,7 +257,8 @@
   return nullptr;
 }
 
-static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC, ASTContext &Ctx) {
+static bool isDeclADefinition(const Decl *D, const DeclContext *ContainerDC,
+                              ASTContext &Ctx) {
   if (auto VD = dyn_cast<VarDecl>(D))
     return VD->isThisDeclarationADefinition(Ctx);
 
@@ -321,7 +374,7 @@
                                            const Expr *OrigE,
                                            const Decl *OrigD,
                                            const DeclContext *ContainerDC) {
-  if (D->isImplicit() && !isa<ObjCMethodDecl>(D))
+  if (D->isImplicit() && !(isa<ObjCMethodDecl>(D) || isa<ObjCIvarDecl>(D)))
     return true;
   if (!isa<NamedDecl>(D) || shouldSkipNamelessDecl(cast<NamedDecl>(D)))
     return true;
@@ -337,12 +390,8 @@
   if (FID.isInvalid())
     return true;
 
-  bool Invalid = false;
-  const SrcMgr::SLocEntry &SEntry = SM.getSLocEntry(FID, &Invalid);
-  if (Invalid || !SEntry.isFile())
-    return true;
-
-  if (SEntry.getFile().getFileCharacteristic() != SrcMgr::C_User) {
+  bool IsInSystemFile = SystemCache.isSystem(FID, SM);
+  if (IsInSystemFile) {
     switch (IndexOpts.SystemSymbolFilter) {
     case IndexingOptions::SystemSymbolFilterKind::None:
       return true;
@@ -416,5 +465,5 @@
 
   IndexDataConsumer::ASTNodeInfo Node{ OrigE, OrigD, Parent, ContainerDC };
   return DataConsumer.handleDeclOccurence(D, Roles, FinalRelations, FID, Offset,
-                                          Node);
+                                          IsInSystemFile, Node);
 }
Index: lib/Index/IndexingAction.cpp
===================================================================
--- lib/Index/IndexingAction.cpp
+++ lib/Index/IndexingAction.cpp
@@ -8,35 +8,51 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Index/IndexingAction.h"
+#include "clang/Index/RecordingAction.h"
+#include "clang/Index/UnitIndexingAction.h"
+
+#include "FileIndexData.h"
 #include "IndexingContext.h"
+#include "UnitIndexDataRecorder.h"
+#include "clang/Basic/FileManager.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/MultiplexConsumer.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Index/IndexDataConsumer.h"
+#include "clang/Index/IndexDiagnostic.h"
+#include "clang/Index/UnitIndexDataConsumer.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Serialization/ASTReader.h"
+#include "llvm/Support/Path.h"
 
 using namespace clang;
 using namespace clang::index;
 
+void UnitIndexDataConsumer::_anchor() {}
 void IndexDataConsumer::_anchor() {}
 
 bool IndexDataConsumer::handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
                                             ArrayRef<SymbolRelation> Relations,
                                             FileID FID, unsigned Offset,
+                                            bool IsInSystemFile,
                                             ASTNodeInfo ASTNode) {
   return true;
 }
 
 bool IndexDataConsumer::handleMacroOccurence(const IdentifierInfo *Name,
-                                             const MacroInfo *MI, SymbolRoleSet Roles,
-                                             FileID FID, unsigned Offset) {
+                                             const MacroInfo *MI,
+                                             SymbolRoleSet Roles, FileID FID,
+                                             unsigned Offset,
+                                             bool IsInSystemFile) {
   return true;
 }
 
 bool IndexDataConsumer::handleModuleOccurence(const ImportDecl *ImportD,
-                                              SymbolRoleSet Roles,
-                                              FileID FID, unsigned Offset) {
+                                              SymbolRoleSet Roles, FileID FID,
+                                              unsigned Offset,
+                                              bool IsInSystemFile) {
   return true;
 }
 
@@ -73,96 +89,116 @@
   }
 };
 
-class IndexActionBase {
-protected:
-  std::shared_ptr<IndexDataConsumer> DataConsumer;
-  IndexingContext IndexCtx;
-
-  IndexActionBase(std::shared_ptr<IndexDataConsumer> dataConsumer,
-                  IndexingOptions Opts)
-    : DataConsumer(std::move(dataConsumer)),
-      IndexCtx(Opts, *DataConsumer) {}
-
-  std::unique_ptr<IndexASTConsumer>
-  createIndexASTConsumer(CompilerInstance &CI) {
-    return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(),
-                                               IndexCtx);
-  }
-
-  void finish() {
-    DataConsumer->finish();
-  }
+/// Abstracts the core logic shared between \c IndexAction and
+/// \c WrappingIndexAction frontend actions.
+class IndexActionImpl {
+public:
+  virtual ~IndexActionImpl() = default;
+
+  /// Called at the beginning of processing a single input, this creates the
+  /// IndexASTConsumer object to use.
+  ///
+  /// \param CI The compiler instance used to process the input
+  /// \returns the created IndexASTConsumer.
+  virtual std::unique_ptr<IndexASTConsumer>
+  createIndexASTConsumer(CompilerInstance &CI) = 0;
+
+  /// Callback at the end of processing a single input.
+  ///
+  /// \param CI The compiler instance used to process the input. It will be the
+  /// same instance as provided in \c createIndexASTConsumer.
+  virtual void finish(CompilerInstance &CI) = 0;
 };
 
-class IndexAction : public ASTFrontendAction, IndexActionBase {
+class IndexAction : public ASTFrontendAction {
+  std::unique_ptr<IndexActionImpl> Impl;
+
 public:
-  IndexAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
-              IndexingOptions Opts)
-    : IndexActionBase(std::move(DataConsumer), Opts) {}
+  IndexAction(std::unique_ptr<IndexActionImpl> Impl) : Impl(std::move(Impl)) {}
 
 protected:
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef InFile) override {
-    return createIndexASTConsumer(CI);
+    return Impl->createIndexASTConsumer(CI);
   }
 
   void EndSourceFileAction() override {
     FrontendAction::EndSourceFileAction();
-    finish();
+    Impl->finish(getCompilerInstance());
   }
 };
 
-class WrappingIndexAction : public WrapperFrontendAction, IndexActionBase {
-  bool IndexActionFailed = false;
+class WrappingIndexAction : public WrapperFrontendAction {
+  std::unique_ptr<IndexActionImpl> Impl;
+  bool CreatedASTConsumer = false;
 
 public:
   WrappingIndexAction(std::unique_ptr<FrontendAction> WrappedAction,
-                      std::shared_ptr<IndexDataConsumer> DataConsumer,
-                      IndexingOptions Opts)
-    : WrapperFrontendAction(std::move(WrappedAction)),
-      IndexActionBase(std::move(DataConsumer), Opts) {}
+                      std::unique_ptr<IndexActionImpl> Impl)
+      : WrapperFrontendAction(std::move(WrappedAction)), Impl(std::move(Impl)) {
+  }
 
 protected:
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
-                                                 StringRef InFile) override;
-  void EndSourceFileAction() override;
+                                                 StringRef InFile) override {
+    auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
+    if (!OtherConsumer)
+      return nullptr;
+
+    std::vector<std::unique_ptr<ASTConsumer>> Consumers;
+    Consumers.push_back(std::move(OtherConsumer));
+    Consumers.push_back(Impl->createIndexASTConsumer(CI));
+    CreatedASTConsumer = true;
+
+    return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
+  };
+
+  void EndSourceFileAction() override {
+    // Invoke wrapped action's method.
+    WrapperFrontendAction::EndSourceFileAction();
+    if (CreatedASTConsumer) {
+      CreatedASTConsumer = false;
+      Impl->finish(getCompilerInstance());
+    }
+  };
 };
 
-} // anonymous namespace
+/// An implementation for \c IndexAction or \c WrappingIndexAction that provides
+/// decl ocurrences information from the AST.
+class DataConsumerActionImpl : public IndexActionImpl {
+protected:
+  std::shared_ptr<IndexDataConsumer> DataConsumer;
+  IndexingContext IndexCtx;
 
-void WrappingIndexAction::EndSourceFileAction() {
-  // Invoke wrapped action's method.
-  WrapperFrontendAction::EndSourceFileAction();
-  if (!IndexActionFailed)
-    finish();
-}
+public:
+  DataConsumerActionImpl(std::shared_ptr<IndexDataConsumer> Consumer,
+                         IndexingOptions Opts)
+      : DataConsumer(std::move(Consumer)), IndexCtx(Opts, *DataConsumer) {}
 
-std::unique_ptr<ASTConsumer>
-WrappingIndexAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
-  auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
-  if (!OtherConsumer) {
-    IndexActionFailed = true;
-    return nullptr;
+  std::unique_ptr<IndexASTConsumer>
+  createIndexASTConsumer(CompilerInstance &CI) override {
+    IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot);
+    return llvm::make_unique<IndexASTConsumer>(CI.getPreprocessorPtr(),
+                                               IndexCtx);
   }
 
-  std::vector<std::unique_ptr<ASTConsumer>> Consumers;
-  Consumers.push_back(std::move(OtherConsumer));
-  Consumers.push_back(createIndexASTConsumer(CI));
-  return llvm::make_unique<MultiplexConsumer>(std::move(Consumers));
-}
+  void finish(CompilerInstance &CI) override { DataConsumer->finish(); }
+};
+
+} // anonymous namespace
 
 std::unique_ptr<FrontendAction>
 index::createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
                             IndexingOptions Opts,
                             std::unique_ptr<FrontendAction> WrappedAction) {
+  auto ActionImpl =
+      llvm::make_unique<DataConsumerActionImpl>(std::move(DataConsumer), Opts);
   if (WrappedAction)
     return llvm::make_unique<WrappingIndexAction>(std::move(WrappedAction),
-                                                  std::move(DataConsumer),
-                                                  Opts);
-  return llvm::make_unique<IndexAction>(std::move(DataConsumer), Opts);
+                                                  std::move(ActionImpl));
+  return llvm::make_unique<IndexAction>(std::move(ActionImpl));
 }
 
-
 static bool topLevelDeclVisitor(void *context, const Decl *D) {
   IndexingContext &IndexCtx = *static_cast<IndexingContext*>(context);
   return IndexCtx.indexTopLevelDecl(D);
@@ -209,3 +245,545 @@
   }
   DataConsumer->finish();
 }
+
+//===----------------------------------------------------------------------===//
+// Index Data Recording
+//===----------------------------------------------------------------------===//
+
+/// Construct a \c UnitDetails for a translation unit with the provided root
+/// \c FileEntry or \c Module and with the provided sysroot path.
+static index::UnitDetails getUnitDetails(const CompilerInstance &CI,
+                                         std::string OutputFile,
+                                         const FileEntry *RootFile,
+                                         Module *UnitMod,
+                                         StringRef SysrootPath) {
+  std::string ModuleName =
+      UnitMod ? UnitMod->getFullModuleName() : std::string();
+  bool IsSystemUnit = UnitMod ? UnitMod->IsSystem : false;
+  bool IsModuleUnit = UnitMod != nullptr;
+  bool IsDebugCompilation = CI.getCodeGenOpts().OptimizationLevel == 0;
+
+  // Ignore sysroot path if it points to root, otherwise every header will be
+  // treated as system one.
+  if (llvm::sys::path::root_path(SysrootPath) == SysrootPath)
+    SysrootPath = "";
+
+  return {CI,           UnitMod,      ModuleName,
+          RootFile,     OutputFile,   SysrootPath,
+          IsSystemUnit, IsModuleUnit, IsDebugCompilation};
+}
+
+/// Construct a \c UnitDetails from the invocation associated with the provided
+/// \c CompilerInstance and the provided sysroot path.
+static index::UnitDetails getUnitDetails(const CompilerInstance &CI,
+                                         StringRef SysrootPath) {
+  SourceManager &SM = CI.getASTContext().getSourceManager();
+
+  std::string OutputFile = CI.getFrontendOpts().OutputFile;
+  if (OutputFile.empty()) {
+    OutputFile = CI.getFrontendOpts().Inputs[0].getFile();
+    OutputFile += ".o";
+  }
+
+  const FileEntry *RootFile = nullptr;
+  Module *UnitMod = nullptr;
+  bool IsModuleGeneration = CI.getLangOpts().isCompilingModule();
+  if (!IsModuleGeneration &&
+      CI.getFrontendOpts().ProgramAction != frontend::GeneratePCH)
+    RootFile = SM.getFileEntryForID(SM.getMainFileID());
+
+  if (IsModuleGeneration) {
+    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+    UnitMod = HS.lookupModule(CI.getLangOpts().CurrentModule,
+                              /*AllowSearch=*/false);
+    assert(UnitMod && "only loaded modules should be indexed");
+  }
+  return getUnitDetails(CI, std::move(OutputFile), RootFile, UnitMod,
+                        SysrootPath);
+}
+
+/// Construct a \c UnitDetails for the given module file.
+static index::UnitDetails getUnitDetails(serialization::ModuleFile &Mod,
+                                         const CompilerInstance &CI,
+                                         StringRef SysrootPath) {
+  HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+  Module *UnitMod = HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false);
+  assert(UnitMod && "only loaded modules should be indexed");
+
+  return getUnitDetails(CI, /*OutputFile=*/Mod.FileName, /*RootFile=*/nullptr,
+                        UnitMod, SysrootPath);
+}
+
+namespace {
+
+/// Collects and groups consumed index data by \c FileID.
+class FileIndexDataCollector : public IndexDataConsumer {
+  std::shared_ptr<Preprocessor> PP;
+  typedef llvm::DenseMap<FileID, std::unique_ptr<FileIndexData>>
+      IndexDataByFileTy;
+  IndexDataByFileTy IndexDataByFile;
+
+public:
+  void setPreprocessor(std::shared_ptr<Preprocessor> PreProc) override {
+    PP = PreProc;
+  }
+
+  IndexDataByFileTy::const_iterator begin() const {
+    return IndexDataByFile.begin();
+  }
+
+  IndexDataByFileTy::const_iterator end() const {
+    return IndexDataByFile.end();
+  }
+
+  bool empty() const { return IndexDataByFile.empty(); }
+
+  bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
+                           ArrayRef<SymbolRelation> Relations, FileID FID,
+                           unsigned Offset, bool IsInSystemFile,
+                           ASTNodeInfo ASTNode) override {
+    // Ignore occurrences in the predefines buffer
+    if (FID == PP->getPredefinesFileID())
+      return true;
+
+    FileIndexData &FileData = getFileIndexData(FID, IsInSystemFile);
+    FileData.addDeclOccurence(Roles, Offset, D, Relations);
+    return true;
+  }
+
+private:
+  FileIndexData &getFileIndexData(FileID FID, bool IsInSystemFile) {
+    auto &Entry = IndexDataByFile[FID];
+    if (!Entry) {
+      Entry.reset(new FileIndexData(FID, IsInSystemFile));
+    }
+    return *Entry;
+  }
+};
+
+struct IncludeLocation {
+  const FileEntry *Source;
+  const FileEntry *Target;
+  unsigned Line;
+};
+
+/// Preprocessor callbacks to collect file to file inclusion information
+class IncludePPCallbacks : public PPCallbacks {
+  SystemFileCache &SystemCache;
+  UnitIndexingOptions::FileIncludeFilterKind FileIncludeFilter;
+  std::vector<IncludeLocation> &Includes;
+  SourceManager &SourceMgr;
+
+public:
+  IncludePPCallbacks(SystemFileCache &SystemCache,
+                     UnitIndexingOptions::FileIncludeFilterKind IncludeFilter,
+                     std::vector<IncludeLocation> &IncludesForFile,
+                     SourceManager &SourceMgr)
+      : SystemCache(SystemCache), FileIncludeFilter(IncludeFilter),
+        Includes(IncludesForFile), SourceMgr(SourceMgr) {}
+
+  virtual void InclusionDirective(SourceLocation HashLoc,
+                                  const Token &IncludeTok, StringRef FileName,
+                                  bool IsAngled, CharSourceRange FilenameRange,
+                                  const FileEntry *File, StringRef SearchPath,
+                                  StringRef RelativePath,
+                                  const Module *Imported) override {
+    if (HashLoc.isFileID() && File && File->isValid())
+      addInclude(HashLoc, File);
+  }
+
+private:
+  void addInclude(SourceLocation From, const FileEntry *To) {
+    assert(To);
+    if (FileIncludeFilter == UnitIndexingOptions::FileIncludeFilterKind::None)
+      return;
+
+    std::pair<FileID, unsigned> LocInfo =
+        SourceMgr.getDecomposedExpansionLoc(From);
+
+    if (LocInfo.first.isInvalid())
+      return; // Ignore invalid locations.
+
+    if (FileIncludeFilter ==
+        UnitIndexingOptions::FileIncludeFilterKind::UserOnly)
+      if (SystemCache.isSystem(LocInfo.first, SourceMgr))
+        return; // Ignore includes of system headers.
+
+    if (auto *FE = SourceMgr.getFileEntryForID(LocInfo.first)) {
+      auto lineNo = SourceMgr.getLineNumber(LocInfo.first, LocInfo.second);
+      Includes.push_back({FE, To, lineNo});
+    }
+  }
+};
+
+/// Abstract interface for providing the file and module dependencies of a
+/// translation unit, as well as the set of file to file inclusions
+class IndexDependencyProvider {
+public:
+  virtual ~IndexDependencyProvider() {}
+
+  virtual void forEachFileDependency(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(const FileEntry *FE, bool IsSystem)> Callback)
+      const = 0;
+
+  virtual void
+  forEachInclude(llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+                                         const FileEntry *Target)>
+                     Callback) const = 0;
+  virtual void forEachModuleImport(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(serialization::ModuleFile &Mod, bool IsSystem)>
+          Callback) const = 0;
+};
+
+/// An IndexDependencyProvider for the index data collected by
+/// \c FileIndexDependencyCollector.
+class FileIndexDependencyProvider : public IndexDependencyProvider {
+  llvm::SetVector<const FileEntry *> Files;
+  llvm::BitVector IsSystemByUID;
+  std::vector<IncludeLocation> Includes;
+  bool IncludeSysModules;
+
+public:
+  FileIndexDependencyProvider(llvm::SetVector<const FileEntry *> Entries,
+                              llvm::BitVector IsSystemByUID,
+                              std::vector<IncludeLocation> Includes,
+                              bool IncludeSysDeps)
+      : Files(std::move(Entries)), IsSystemByUID(std::move(IsSystemByUID)),
+        Includes(std::move(Includes)), IncludeSysModules(IncludeSysDeps) {}
+
+  void forEachFileDependency(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(const FileEntry *FE, bool IsSystem)> Callback)
+      const override {
+    for (auto *FE : Files)
+      Callback(FE, isSystemFile(FE));
+  }
+
+  void
+  forEachInclude(llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+                                         const FileEntry *Target)>
+                     Callback) const override {
+    for (auto &Include : Includes)
+      Callback(Include.Source, Include.Line, Include.Target);
+  }
+
+  void forEachModuleImport(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(serialization::ModuleFile &Mod, bool IsSystem)>
+          Callback) const override {
+    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+
+    if (auto Reader = CI.getModuleManager()) {
+      Reader->getModuleManager().visit(
+          [&](serialization::ModuleFile &Mod) -> bool {
+            bool IsSystemMod = false;
+            if (Mod.isModule()) {
+              if (auto *M =
+                      HS.lookupModule(Mod.ModuleName, /*AllowSearch=*/false))
+                IsSystemMod = M->IsSystem;
+            }
+            if (!IsSystemMod || IncludeSysModules)
+              Callback(Mod, IsSystemMod);
+            return true; // skip module dependencies.
+          });
+    }
+  }
+
+private:
+  bool isSystemFile(const FileEntry *FE) const {
+    auto UID = FE->getUID();
+    return IsSystemByUID.size() > UID && IsSystemByUID[UID];
+  }
+};
+
+/// Collects file and module dependency information for a translation unit,
+/// including file to file inclusions.
+class FileIndexDependencyCollector : public DependencyCollector {
+  SystemFileCache &SystemCache;
+  UnitIndexingOptions IndexOpts;
+  llvm::SetVector<const FileEntry *> SeenFiles;
+  llvm::BitVector IsSystemByUID;
+  std::vector<IncludeLocation> Includes;
+  SourceManager *SourceMgr = nullptr;
+
+public:
+  FileIndexDependencyCollector(SystemFileCache &SystemCache,
+                               UnitIndexingOptions IndexOpts)
+      : SystemCache(SystemCache), IndexOpts(IndexOpts) {}
+
+  void attachToPreprocessor(Preprocessor &PP) override {
+    DependencyCollector::attachToPreprocessor(PP);
+    PP.addPPCallbacks(llvm::make_unique<IncludePPCallbacks>(
+        SystemCache, IndexOpts.FileIncludeFilter, Includes,
+        PP.getSourceManager()));
+  }
+
+  void setSourceManager(SourceManager *SourceMgr) {
+    this->SourceMgr = SourceMgr;
+  }
+
+  FileIndexDependencyProvider consume() {
+    return FileIndexDependencyProvider(
+        std::move(SeenFiles), std::move(IsSystemByUID), std::move(Includes),
+        IndexOpts.IncludeSystemDependencies);
+  }
+
+private:
+  bool needSystemDependencies() override {
+    return IndexOpts.IncludeSystemDependencies;
+  }
+
+  bool sawDependency(StringRef Filename, bool FromModule, bool IsSystem,
+                     bool IsModuleFile, bool IsMissing) override {
+    bool SawIt = DependencyCollector::sawDependency(
+        Filename, FromModule, IsSystem, IsModuleFile, IsMissing);
+    if (auto *FE = SourceMgr->getFileManager().getFile(Filename)) {
+      if (SawIt)
+        SeenFiles.insert(FE);
+
+      // Record system-ness for all files that we pass through.
+      if (IsSystemByUID.size() < FE->getUID() + 1)
+        IsSystemByUID.resize(FE->getUID() + 1);
+      IsSystemByUID[FE->getUID()] = IsSystem || isInSysroot(Filename);
+    }
+    return SawIt;
+  }
+
+  bool isInSysroot(StringRef Filename) {
+    StringRef SysrootPath = SystemCache.getSysrootPath();
+    return !SysrootPath.empty() && Filename.startswith(SysrootPath);
+  }
+};
+} // anonymous namespace
+
+static void reportData(const CompilerInstance &CI,
+                       const FileIndexDataCollector &Collector,
+                       const IndexDependencyProvider &DepProvider,
+                       UnitDetails UnitInfo,
+                       const IndexUnitDataConsumerFactory &UnitConsumerFactory,
+                       const UnitIndexingOptions &IndexOpts) {
+
+  std::unique_ptr<UnitIndexDataConsumer> Consumer =
+      UnitConsumerFactory(UnitInfo);
+  if (!Consumer)
+    return;
+
+  DepProvider.forEachFileDependency(
+      CI, [&](const FileEntry *FE, bool IsSystemFile) {
+        Consumer->handleFileDependency(FE, IsSystemFile);
+      });
+  DepProvider.forEachInclude(
+      [&](const FileEntry *Source, unsigned Line, const FileEntry *Target) {
+        Consumer->handleInclude(Source, Line, Target);
+      });
+  DepProvider.forEachModuleImport(
+      CI, [&](serialization::ModuleFile &Mod, bool IsSystemMod) {
+        Consumer->handleModuleImport(Mod, IsSystemMod);
+        if (Mod.isModule() && Consumer->shouldIndexModuleDependency(Mod))
+          indexModuleFile(Mod, CI, UnitConsumerFactory, IndexOpts);
+      });
+
+  for (auto I = Collector.begin(), E = Collector.end(); I != E; ++I) {
+    FileID FID = I->first;
+    const FileIndexData &FileData = *I->second;
+    if (Consumer->handleFileOccurrences(
+            FID, FileData.getDeclOccurrencesSortedByOffset(),
+            FileData.isSystem()))
+      return;
+  }
+
+  Consumer->finish();
+}
+
+namespace {
+
+/// An implementation for IndexAction or WrappingIndexAction that gathers decl
+/// occurrence, file inclusion and dependency information for the translation
+/// and, optionally, its module dependencies.
+class UnitDataConsumerActionImpl : public IndexActionImpl {
+  UnitIndexingOptions IndexOpts;
+  FileIndexDataCollector Collector;
+  IndexingContext IndexCtx;
+  FileIndexDependencyCollector DepCollector;
+  IndexUnitDataConsumerFactory UnitConsumerFactory;
+
+public:
+  UnitDataConsumerActionImpl(UnitIndexingOptions UnitIndexOpts,
+                             IndexUnitDataConsumerFactory UnitConsumerFactory)
+      : IndexOpts(UnitIndexOpts), IndexCtx(UnitIndexOpts, Collector),
+        DepCollector(IndexCtx.getSystemCache(), IndexOpts),
+        UnitConsumerFactory(std::move(UnitConsumerFactory)) {}
+
+  std::unique_ptr<IndexASTConsumer>
+  createIndexASTConsumer(CompilerInstance &CI) override {
+    IndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot);
+
+    std::shared_ptr<Preprocessor> PP = CI.getPreprocessorPtr();
+    Collector.setPreprocessor(PP);
+    DepCollector.setSourceManager(&CI.getSourceManager());
+    DepCollector.attachToPreprocessor(CI.getPreprocessor());
+
+    return llvm::make_unique<IndexASTConsumer>(PP, IndexCtx);
+  }
+
+  /// Provides the collected indexing info to the \c IndexUnitDataConsumer
+  void finish(CompilerInstance &CI) override {
+    // The consumer may emit more diagnostics so do the begin/end source file
+    // invocations on the diagnostic client.
+    // FIXME: FrontendAction::EndSourceFile() should probably not call
+    // CI.getDiagnosticClient().EndSourceFile()' until after it has called
+    // 'EndSourceFileAction()', so that code executing during
+    // EndSourceFileAction() can emit diagnostics. If this is fixed,
+    // DiagClientBeginEndRAII can go away.
+    struct DiagClientBeginEndRAII {
+      CompilerInstance &CI;
+      DiagClientBeginEndRAII(CompilerInstance &CI) : CI(CI) {
+        CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts());
+      }
+      ~DiagClientBeginEndRAII() { CI.getDiagnosticClient().EndSourceFile(); }
+    } diagClientBeginEndRAII(CI);
+
+    Collector.finish();
+    reportData(CI, Collector, DepCollector.consume(),
+               getUnitDetails(CI, IndexCtx.getSysrootPath()),
+               UnitConsumerFactory, IndexOpts);
+  }
+};
+
+/// Provides the file and module dependency information for a \c ModuleFile
+class ModuleFileIndexDependencyCollector : public IndexDependencyProvider {
+  serialization::ModuleFile &ModFile;
+  bool CollectSystemDependencies;
+
+public:
+  ModuleFileIndexDependencyCollector(serialization::ModuleFile &Mod,
+                                     bool CollectSystemDependencies)
+      : ModFile(Mod), CollectSystemDependencies(CollectSystemDependencies) {}
+
+  void forEachFileDependency(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(const FileEntry *FE, bool IsSystem)> Callback)
+      const override {
+    auto Reader = CI.getModuleManager();
+    Reader->visitInputFiles(
+        ModFile, CollectSystemDependencies, /*Complain=*/false,
+        [&](const serialization::InputFile &IF, bool IsSystem) {
+          auto *FE = IF.getFile();
+          if (!FE)
+            return;
+          // Ignore module map files, they are not as important to track as
+          // source files and they may be auto-generated which would create an
+          // undesirable dependency on an intermediate build byproduct.
+          if (FE->getName().endswith("module.modulemap"))
+            return;
+
+          Callback(FE, IsSystem);
+        });
+  }
+
+  void
+  forEachInclude(llvm::function_ref<void(const FileEntry *Source, unsigned Line,
+                                         const FileEntry *Target)>
+                     Callback) const override {
+    // FIXME: Module files without a preprocessing record do not have info about
+    // include locations. Serialize enough data to be able to retrieve such
+    // info.
+  }
+
+  void forEachModuleImport(
+      const CompilerInstance &CI,
+      llvm::function_ref<void(serialization::ModuleFile &Mod, bool IsSystem)>
+          Callback) const override {
+    HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
+    for (auto *Mod : ModFile.Imports) {
+      bool IsSystemMod = false;
+      if (auto *M = HS.lookupModule(Mod->ModuleName, /*AllowSearch=*/false))
+        IsSystemMod = M->IsSystem;
+      if (!IsSystemMod || CollectSystemDependencies)
+        Callback(*Mod, IsSystemMod);
+    }
+  }
+};
+} // anonymous namespace.
+
+void index::indexModuleFile(serialization::ModuleFile &Mod,
+                            const CompilerInstance &CI,
+                            IndexUnitDataConsumerFactory UnitConsumerFactory,
+                            UnitIndexingOptions IndexOpts) {
+
+  DiagnosticsEngine &Diag = CI.getDiagnostics();
+  Diag.Report(Mod.ImportLoc, diag::remark_index_producing_module_file_data)
+      << Mod.FileName;
+
+  FileIndexDataCollector Collector;
+  IndexingContext ModIndexCtx(IndexOpts, Collector);
+
+  auto &ASTCtx = CI.getASTContext();
+  Collector.initialize(ASTCtx);
+  Collector.setPreprocessor(CI.getPreprocessorPtr());
+  ModIndexCtx.setASTContext(ASTCtx);
+  ModIndexCtx.setSysrootPath(CI.getHeaderSearchOpts().Sysroot);
+
+  for (const Decl *D : CI.getModuleManager()->getModuleFileLevelDecls(Mod))
+    ModIndexCtx.indexTopLevelDecl(D);
+
+  Collector.finish();
+
+  ModuleFileIndexDependencyCollector DepCollector(
+      Mod, IndexOpts.IncludeSystemDependencies);
+
+  reportData(CI, Collector, DepCollector,
+             getUnitDetails(Mod, CI, ModIndexCtx.getSysrootPath()),
+             UnitConsumerFactory, IndexOpts);
+}
+
+std::unique_ptr<FrontendAction>
+index::createUnitIndexingAction(IndexUnitDataConsumerFactory ConsumerFactory,
+                                UnitIndexingOptions IndexOpts,
+                                std::unique_ptr<FrontendAction> WrappedAction) {
+  auto ActionImpl = llvm::make_unique<UnitDataConsumerActionImpl>(
+      std::move(IndexOpts), ConsumerFactory);
+  if (WrappedAction)
+    return llvm::make_unique<WrappingIndexAction>(std::move(WrappedAction),
+                                                  std::move(ActionImpl));
+  return llvm::make_unique<IndexAction>(std::move(ActionImpl));
+};
+
+std::unique_ptr<FrontendAction> index::createIndexDataRecordingAction(
+    RecordingOptions RecordOpts,
+    std::unique_ptr<FrontendAction> WrappedAction) {
+
+  auto ConsumerFactory =
+      [RecordOpts](
+          UnitDetails UnitInfo) -> std::unique_ptr<UnitIndexDataConsumer> {
+    return llvm::make_unique<UnitIndexDataRecorder>(std::move(UnitInfo),
+                                                    RecordOpts);
+  };
+  return createUnitIndexingAction(ConsumerFactory, std::move(RecordOpts),
+                                  std::move(WrappedAction));
+};
+
+RecordingOptions
+index::getRecordingOptionsFromFrontendOptions(const FrontendOptions &FEOpts) {
+  RecordingOptions RecordOpts;
+  RecordOpts.DataDirPath = FEOpts.IndexStorePath;
+  if (FEOpts.IndexIgnoreSystemSymbols) {
+    RecordOpts.SystemSymbolFilter =
+        index::IndexingOptions::SystemSymbolFilterKind::None;
+  }
+  RecordOpts.RecordSymbolCodeGenName = FEOpts.IndexRecordCodegenName;
+  return RecordOpts;
+}
+
+void index::recordIndexDataForModuleFile(serialization::ModuleFile *ModFile,
+                                         RecordingOptions RecordOpts,
+                                         const CompilerInstance &CI) {
+  auto UnitConsumerFactory = [RecordOpts](UnitDetails UnitInfo) {
+    return llvm::make_unique<UnitIndexDataRecorder>(std::move(UnitInfo),
+                                                    RecordOpts);
+  };
+  return indexModuleFile(*ModFile, CI, UnitConsumerFactory,
+                         std::move(RecordOpts));
+}
Index: lib/Index/FileIndexData.h
===================================================================
--- /dev/null
+++ lib/Index/FileIndexData.h
@@ -0,0 +1,56 @@
+//===--- FileIndexData.h - Index data per file --------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H
+#define LLVM_CLANG_LIB_INDEX_FILEINDEXRECORD_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Index/DeclOccurrence.h"
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include <vector>
+
+namespace clang {
+class IdentifierInfo;
+
+namespace index {
+
+/// Stores the declaration occurrences seen in a particular source or header
+/// file of a translation unit
+class FileIndexData {
+private:
+  FileID FID;
+  bool IsSystem;
+  std::vector<DeclOccurrence> Decls;
+
+public:
+  FileIndexData(FileID FID, bool IsSystem) : FID(FID), IsSystem(IsSystem) {}
+
+  std::vector<DeclOccurrence> getDeclOccurrencesSortedByOffset() const;
+
+  FileID getFileID() const { return FID; }
+  bool isSystem() const { return IsSystem; }
+
+  /// Adds an occurrence of the canonical declaration \c D at the supplied
+  /// \c Offset
+  ///
+  /// \param Roles the roles the occurrence fulfills in this position.
+  /// \param Offset the offset in the file of this occurrence.
+  /// \param D the canonical declaration this is an occurrence of.
+  /// \param Relations the set of symbols related to this occurrence.
+  void addDeclOccurence(SymbolRoleSet Roles, unsigned Offset, const Decl *D,
+                        ArrayRef<SymbolRelation> Relations);
+  void print(llvm::raw_ostream &OS) const;
+};
+
+} // end namespace index
+} // end namespace clang
+
+#endif
Index: lib/Index/FileIndexData.cpp
===================================================================
--- /dev/null
+++ lib/Index/FileIndexData.cpp
@@ -0,0 +1,52 @@
+//===--- FileIndexData.cpp - Index data per file ------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "FileIndexData.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclTemplate.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+using namespace clang::index;
+
+void FileIndexData::addDeclOccurence(SymbolRoleSet Roles, unsigned Offset,
+                                     const Decl *D,
+                                     ArrayRef<SymbolRelation> Relations) {
+  assert(D->isCanonicalDecl() &&
+         "Occurrences should be associated with their canonical decl");
+
+  Decls.emplace_back(Roles, Offset, D, Relations);
+}
+
+std::vector<DeclOccurrence>
+FileIndexData::getDeclOccurrencesSortedByOffset() const {
+  std::vector<DeclOccurrence> Sorted(Decls);
+  std::sort(Sorted.begin(), Sorted.end());
+  return Sorted;
+}
+
+void FileIndexData::print(llvm::raw_ostream &OS) const {
+  OS << "DECLS BEGIN ---\n";
+  for (auto &DclInfo : Decls) {
+    auto D = DclInfo.Dcl;
+    SourceManager &SM = D->getASTContext().getSourceManager();
+    SourceLocation Loc = SM.getFileLoc(D->getLocation());
+    PresumedLoc PLoc = SM.getPresumedLoc(Loc);
+    OS << llvm::sys::path::filename(PLoc.getFilename()) << ':' << PLoc.getLine()
+       << ':' << PLoc.getColumn();
+
+    if (auto ND = dyn_cast<NamedDecl>(D)) {
+      OS << ' ' << ND->getNameAsString();
+    }
+
+    OS << '\n';
+  }
+  OS << "DECLS END ---\n";
+}
Index: lib/Index/CMakeLists.txt
===================================================================
--- lib/Index/CMakeLists.txt
+++ lib/Index/CMakeLists.txt
@@ -6,12 +6,14 @@
 add_clang_library(clangIndex
   CodegenNameGenerator.cpp
   CommentToXML.cpp
+  FileIndexData.cpp
   IndexBody.cpp
   IndexDecl.cpp
   IndexingAction.cpp
   IndexingContext.cpp
   IndexSymbol.cpp
   IndexTypeSourceInfo.cpp
+  UnitIndexDataRecorder.cpp
   USRGeneration.cpp
 
   ADDITIONAL_HEADERS
@@ -23,6 +25,7 @@
   clangBasic
   clangFormat
   clangFrontend
+  clangLex
   clangRewrite
   clangSerialization
   clangToolingCore
Index: lib/FrontendTool/ExecuteCompilerInvocation.cpp
===================================================================
--- lib/FrontendTool/ExecuteCompilerInvocation.cpp
+++ lib/FrontendTool/ExecuteCompilerInvocation.cpp
@@ -23,6 +23,7 @@
 #include "clang/Frontend/FrontendDiagnostic.h"
 #include "clang/Frontend/FrontendPluginRegistry.h"
 #include "clang/Frontend/Utils.h"
+#include "clang/Index/RecordingAction.h"
 #include "clang/Rewrite/Frontend/FrontendActions.h"
 #include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
 #include "llvm/Option/OptTable.h"
@@ -164,6 +165,22 @@
   }
 #endif
 
+  if (!FEOpts.IndexStorePath.empty()) {
+    auto WrapWithIndexRecordAction =
+        [&](const FrontendOptions &opts,
+            std::unique_ptr<FrontendAction> WrappedAction) {
+          auto RecordOpts =
+              index::getRecordingOptionsFromFrontendOptions(FEOpts);
+          return index::createIndexDataRecordingAction(
+              RecordOpts, std::move(WrappedAction));
+        };
+
+    // Wrap the main action as well as any GenerateModuleActions created while
+    // loading modules
+    Act = WrapWithIndexRecordAction(FEOpts, std::move(Act));
+    CI.setGenModuleActionWrapper(WrapWithIndexRecordAction);
+  }
+
   // If there are any AST files to merge, create a frontend action
   // adaptor to perform the merge.
   if (!FEOpts.ASTMergeFiles.empty())
Index: lib/FrontendTool/CMakeLists.txt
===================================================================
--- lib/FrontendTool/CMakeLists.txt
+++ lib/FrontendTool/CMakeLists.txt
@@ -8,6 +8,7 @@
   clangCodeGen
   clangDriver
   clangFrontend
+  clangIndex
   clangRewriteFrontend
   )
 
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1513,6 +1513,10 @@
       << "ARC migration" << "ObjC migration";
   }
 
+  Opts.IndexStorePath = Args.getLastArgValue(OPT_index_store_path);
+  Opts.IndexIgnoreSystemSymbols = Args.hasArg(OPT_index_ignore_system_symbols);
+  Opts.IndexRecordCodegenName = Args.hasArg(OPT_index_record_codegen_name);
+
   InputKind DashX(InputKind::Unknown);
   if (const Arg *A = Args.getLastArg(OPT_x)) {
     StringRef XValue = A->getValue();
Index: lib/Frontend/CompilerInstance.cpp
===================================================================
--- lib/Frontend/CompilerInstance.cpp
+++ lib/Frontend/CompilerInstance.cpp
@@ -28,6 +28,7 @@
 #include "clang/Frontend/TextDiagnosticPrinter.h"
 #include "clang/Frontend/Utils.h"
 #include "clang/Frontend/VerifyDiagnosticConsumer.h"
+#include "clang/Index/IndexingAction.h"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/PTHManager.h"
 #include "clang/Lex/Preprocessor.h"
@@ -1148,6 +1149,10 @@
   SourceMgr.pushModuleBuildStack(ModuleName,
     FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager()));
 
+  // Pass along the GenModuleActionWrapper callback
+  auto WrapGenModuleAction = ImportingInstance.getGenModuleActionWrapper();
+  Instance.setGenModuleActionWrapper(WrapGenModuleAction);
+
   // If we're collecting module dependencies, we need to share a collector
   // between all of the module CompilerInstances. Other than that, we don't
   // want to produce any dependency output from the module build.
@@ -1166,8 +1171,11 @@
   llvm::CrashRecoveryContext CRC;
   CRC.RunSafelyOnThread(
       [&]() {
-        GenerateModuleFromModuleMapAction Action;
-        Instance.ExecuteAction(Action);
+        std::unique_ptr<FrontendAction> Action(
+            new GenerateModuleFromModuleMapAction);
+        if (WrapGenModuleAction)
+          Action = WrapGenModuleAction(FrontendOpts, std::move(Action));
+        Instance.ExecuteAction(*Action);
       },
       ThreadStackSize);
 
Index: lib/Driver/ToolChains/Darwin.cpp
===================================================================
--- lib/Driver/ToolChains/Darwin.cpp
+++ lib/Driver/ToolChains/Darwin.cpp
@@ -436,6 +436,10 @@
   // more information.
   ArgStringList CmdArgs;
 
+  Args.ClaimAllArgs(options::OPT_index_store_path);
+  Args.ClaimAllArgs(options::OPT_index_ignore_system_symbols);
+  Args.ClaimAllArgs(options::OPT_index_record_codegen_name);
+
   /// Hack(tm) to ignore linking errors when we are doing ARC migration.
   if (Args.hasArg(options::OPT_ccc_arcmt_check,
                   options::OPT_ccc_arcmt_migrate)) {
Index: lib/Driver/ToolChains/Clang.cpp
===================================================================
--- lib/Driver/ToolChains/Clang.cpp
+++ lib/Driver/ToolChains/Clang.cpp
@@ -3653,6 +3653,18 @@
 
   RenderARCMigrateToolOptions(D, Args, CmdArgs);
 
+  if (Args.hasArg(options::OPT_index_store_path)) {
+    Args.AddLastArg(CmdArgs, options::OPT_index_store_path);
+    Args.AddLastArg(CmdArgs, options::OPT_index_ignore_system_symbols);
+    Args.AddLastArg(CmdArgs, options::OPT_index_record_codegen_name);
+
+    // If '-o' is passed along with '-fsyntax-only' pass it along the cc1
+    // invocation so that the index action knows what the out file is.
+    if (isa<CompileJobAction>(JA) && JA.getType() == types::TY_Nothing) {
+      Args.AddLastArg(CmdArgs, options::OPT_o);
+    }
+  }
+
   // Add preprocessing options like -I, -D, etc. if we are using the
   // preprocessor.
   //
Index: lib/Driver/Job.cpp
===================================================================
--- lib/Driver/Job.cpp
+++ lib/Driver/Job.cpp
@@ -68,6 +68,8 @@
     .Default(false);
   if (IsInclude)
     return HaveCrashVFS ? false : true;
+  if (StringRef(Flag).startswith("-index-store-path"))
+    return true;
 
   // The remaining flags are treated as a single argument.
 
@@ -206,6 +208,18 @@
   IncFlags.push_back(std::move(NewInc));
 }
 
+/// Returns a path to a directory named \c DirName adjacent to the module
+/// cache directory:
+///  <...>.cache/vfs/<DirName>
+static llvm::SmallString<128>
+getDirAdjacentToModCache(StringRef DirName, CrashReportInfo *CrashInfo) {
+  llvm::SmallString<128> RelModCacheDir = llvm::sys::path::parent_path(
+      llvm::sys::path::parent_path(CrashInfo->VFSPath));
+  llvm::sys::path::append(RelModCacheDir, DirName);
+
+  return RelModCacheDir;
+}
+
 void Command::Print(raw_ostream &OS, const char *Terminator, bool Quote,
                     CrashReportInfo *CrashInfo) const {
   // Always quote the exe.
@@ -220,6 +234,7 @@
   }
 
   bool HaveCrashVFS = CrashInfo && !CrashInfo->VFSPath.empty();
+  bool HaveIndexStorePath = CrashInfo && !CrashInfo->IndexStorePath.empty();
   for (size_t i = 0, e = Args.size(); i < e; ++i) {
     const char *const Arg = Args[i];
 
@@ -267,22 +282,34 @@
     OS << ' ';
     printArg(OS, CrashInfo->VFSPath.str(), Quote);
 
-    // The leftover modules from the crash are stored in
-    //  <name>.cache/vfs/modules
-    // Leave it untouched for pcm inspection and provide a clean/empty dir
-    // path to contain the future generated module cache:
-    //  <name>.cache/vfs/repro-modules
-    SmallString<128> RelModCacheDir = llvm::sys::path::parent_path(
-        llvm::sys::path::parent_path(CrashInfo->VFSPath));
-    llvm::sys::path::append(RelModCacheDir, "repro-modules");
+    // Provide an empty dir path for the future generated module cache to
+    // leave the leftover modules from the crash untouched for pcm inspection
+    SmallString<128> RelModCacheDir =
+        getDirAdjacentToModCache("repro-modules", CrashInfo);
 
     std::string ModCachePath = "-fmodules-cache-path=";
     ModCachePath.append(RelModCacheDir.c_str());
 
     OS << ' ';
     printArg(OS, ModCachePath, Quote);
   }
 
+  if (CrashInfo && HaveIndexStorePath) {
+    SmallString<128> IndexStoreDir;
+
+    if (HaveCrashVFS) {
+      // Provide a new index store, leaving the old one from the crash untouched
+      IndexStoreDir = getDirAdjacentToModCache("index-store", CrashInfo);
+    } else {
+      IndexStoreDir = "index-store";
+    }
+
+    OS << ' ';
+    printArg(OS, "-index-store-path", Quote);
+    OS << ' ';
+    printArg(OS, IndexStoreDir.c_str(), Quote);
+  }
+
   if (ResponseFile != nullptr) {
     OS << "\n Arguments passed via response file:\n";
     writeResponseFile(OS);
Index: lib/Driver/Driver.cpp
===================================================================
--- lib/Driver/Driver.cpp
+++ lib/Driver/Driver.cpp
@@ -1253,7 +1253,9 @@
   }
 
   // Assume associated files are based off of the first temporary file.
-  CrashReportInfo CrashInfo(TempFiles[0], VFS);
+  CrashReportInfo CrashInfo(
+      TempFiles[0], VFS,
+      C.getArgs().getLastArgValue(options::OPT_index_store_path));
 
   std::string Script = CrashInfo.Filename.rsplit('.').first.str() + ".sh";
   std::error_code EC;
Index: lib/Basic/DiagnosticIDs.cpp
===================================================================
--- lib/Basic/DiagnosticIDs.cpp
+++ lib/Basic/DiagnosticIDs.cpp
@@ -89,6 +89,7 @@
 VALIDATE_DIAG_SIZE(SEMA)
 VALIDATE_DIAG_SIZE(ANALYSIS)
 VALIDATE_DIAG_SIZE(REFACTORING)
+VALIDATE_DIAG_SIZE(INDEX)
 #undef VALIDATE_DIAG_SIZE
 #undef STRINGIFY_NAME
 
@@ -114,6 +115,7 @@
 #include "clang/Basic/DiagnosticSemaKinds.inc"
 #include "clang/Basic/DiagnosticAnalysisKinds.inc"
 #include "clang/Basic/DiagnosticRefactoringKinds.inc"
+#include "clang/Basic/DiagnosticIndexKinds.inc"
 #undef DIAG
 };
 
@@ -153,6 +155,7 @@
 CATEGORY(SEMA, CROSSTU)
 CATEGORY(ANALYSIS, SEMA)
 CATEGORY(REFACTORING, ANALYSIS)
+CATEGORY(INDEX, REFACTORING)
 #undef CATEGORY
 
   // Avoid out of bounds reads.
Index: include/clang/module.modulemap
===================================================================
--- include/clang/module.modulemap
+++ include/clang/module.modulemap
@@ -68,6 +68,7 @@
   module Comment { header "AST/CommentDiagnostic.h" export * }
   module Driver { header "Driver/DriverDiagnostic.h" export * }
   module Frontend { header "Frontend/FrontendDiagnostic.h" export * }
+  module Index { header "Index/IndexDiagnostic.h" export * }
   module Lex { header "Lex/LexDiagnostic.h" export * }
   module Parse { header "Parse/ParseDiagnostic.h" export * }
   module Sema { header "Sema/SemaDiagnostic.h" export * }
Index: include/clang/Index/UnitIndexingAction.h
===================================================================
--- /dev/null
+++ include/clang/Index/UnitIndexingAction.h
@@ -0,0 +1,87 @@
+//===--- UnitIndexingAction.h - Frontend unit indexing action -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_UNITINDEXINGACTION_H
+#define LLVM_CLANG_INDEX_UNITINDEXINGACTION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Index/IndexingAction.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+class CompilerInstance;
+class FileEntry;
+class FrontendAction;
+class Module;
+
+namespace serialization {
+class ModuleFile;
+}
+
+namespace index {
+class UnitIndexDataConsumer;
+
+struct UnitIndexingOptions : IndexingOptions {
+  enum class FileIncludeFilterKind {
+    None,
+    UserOnly, // only record includes inside non-system files.
+    All,
+  };
+
+  bool IncludeSystemDependencies = true;
+  FileIncludeFilterKind FileIncludeFilter = FileIncludeFilterKind::UserOnly;
+};
+
+/// \brief Information about a translation unit useful for indexing.
+///
+struct UnitDetails {
+  const CompilerInstance &CI; ///< The owning compiler instance.
+
+  Module *UnitModule;     ///< The corresponding \c Module (module units only).
+  std::string ModuleName; ///< The \c Module name (module units only).
+  const FileEntry *RootFile; ///< The root \c FileEntry (non-module units only).
+
+  std::string OutputFile; ///< The output file path.
+  StringRef SysrootPath;  ///< The "virtual system root" path.
+  bool IsSystemUnit;
+  bool IsModuleUnit;
+  bool IsDebugCompilation;
+};
+
+/// Factory function type for producing UnitIndexDataConsumers for a given
+/// translation unit
+typedef std::function<std::unique_ptr<UnitIndexDataConsumer>(
+    UnitDetails UnitInfo)>
+    IndexUnitDataConsumerFactory;
+
+/// \brief Creates a frontend action that provides dependency, file inclusion
+/// and decl ocurrence information for the translation unit, and optionally its
+/// module dependencies.
+///
+/// Decl occurrence information is provided per-file, sorted by offset.
+///
+/// \param ConsumerFactory provides an \c IndexUnitDataConsumer to use for a
+/// translation unit.
+/// \param WrappedAction another frontend action to wrap over or null.
+std::unique_ptr<FrontendAction>
+createUnitIndexingAction(IndexUnitDataConsumerFactory ConsumerFactory,
+                         UnitIndexingOptions UnitIndexOpts,
+                         std::unique_ptr<FrontendAction> WrappedAction);
+
+/// Collects and provides dependency, file inclusion and decl occurrence
+/// information for a \c ModuleFile to an \c IndexUnitDataConsumer constructed
+/// from the provided \c IndexUnitDataConsumerFactory.
+void indexModuleFile(serialization::ModuleFile &Mod, const CompilerInstance &CI,
+                     IndexUnitDataConsumerFactory UnitConsumerFactory,
+                     UnitIndexingOptions Opts);
+
+} // namespace index
+} // namespace clang
+
+#endif
Index: include/clang/Index/UnitIndexDataConsumer.h
===================================================================
--- /dev/null
+++ include/clang/Index/UnitIndexDataConsumer.h
@@ -0,0 +1,74 @@
+//===--- UnitIndexDataConsumer.h - Abstract unit index data consumer ------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_UNITINDEXDATACONSUMER_H
+#define LLVM_CLANG_INDEX_UNITINDEXDATACONSUMER_H
+
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Index/DeclOccurrence.h"
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+namespace serialization {
+class ModuleFile;
+}
+
+namespace index {
+
+/// Consumer for the index data associated with a translation unit.
+class UnitIndexDataConsumer {
+public:
+  virtual ~UnitIndexDataConsumer() = default;
+
+  /// Called for each file dependency of the translation unit.
+  virtual void handleFileDependency(const FileEntry *FE, bool IsSystem) {}
+
+  /// Called for each file include in the translation unit.
+  virtual void handleInclude(const FileEntry *Source, unsigned Line,
+                             const FileEntry *Target) {}
+
+  /// Called for each each module imported by the translation unit.
+  virtual void handleModuleImport(const serialization::ModuleFile &Mod,
+                                  bool IsSystem) {}
+
+  /// Determines whether to collect the index data associated with the given
+  /// dependency of this translation unit or not.
+  ///
+  /// \param OutFilePath the output file path of the dependency.
+  /// \returns true to collect index data for \c Mod.
+  virtual bool
+  shouldIndexModuleDependency(const serialization::ModuleFile &Mod) {
+    return false;
+  }
+
+  /// Called with the decl occurrences in each file and AST file dependency,
+  /// sorted by offset.
+  ///
+  /// \returns true to cancel consuming data for this translation unit. Finish
+  /// will not be called.
+  virtual bool
+  handleFileOccurrences(FileID FID,
+                        ArrayRef<DeclOccurrence> OccurrencesSortedByOffset,
+                        bool IsSystem) {
+    return false;
+  }
+
+  /// Called when there is no more data to handle.
+  virtual void finish() {}
+
+private:
+  // avoid duplicate vtables
+  virtual void _anchor();
+};
+
+} // namespace index
+} // namespace clang
+
+#endif
Index: include/clang/Index/RecordingAction.h
===================================================================
--- /dev/null
+++ include/clang/Index/RecordingAction.h
@@ -0,0 +1,55 @@
+//===--- RecordingAction.h - Frontend index recording action --------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXRECORDINGACTION_H
+#define LLVM_CLANG_INDEX_INDEXRECORDINGACTION_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Index/UnitIndexingAction.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace clang {
+class CompilerInstance;
+class FrontendAction;
+class FrontendOptions;
+
+namespace serialization {
+class ModuleFile;
+}
+
+namespace index {
+
+struct RecordingOptions : UnitIndexingOptions {
+  std::string DataDirPath;
+  bool RecordSymbolCodeGenName = false;
+};
+
+RecordingOptions
+getRecordingOptionsFromFrontendOptions(const FrontendOptions &FEOpts);
+
+/// \brief Creates a frontend action that collects dependency, file inclusion
+/// and decl ocurrence information for the translation unit and persists it to
+/// an index store.
+///
+/// \param WrappedAction another frontend action to wrap over or null.
+std::unique_ptr<FrontendAction>
+createIndexDataRecordingAction(RecordingOptions RecordOpts,
+                               std::unique_ptr<FrontendAction> WrappedAction);
+
+/// Collects dependency, file inclusion and decl occurrence information for a
+/// \c ModuleFile and persists it to an index store. Does \b not check if
+/// the store already has up-to-date information for the provided module file.
+void recordIndexDataForModuleFile(serialization::ModuleFile *ModFile,
+                                  RecordingOptions RecordOpts,
+                                  const CompilerInstance &CI);
+
+} // namespace index
+} // namespace clang
+
+#endif
Index: include/clang/Index/IndexingAction.h
===================================================================
--- include/clang/Index/IndexingAction.h
+++ include/clang/Index/IndexingAction.h
@@ -1,4 +1,4 @@
-//===--- IndexingAction.h - Frontend index action -------------------------===//
+//===--- IndexingAction.h - Frontend AST indexing action ------------------===//
 //
 //                     The LLVM Compiler Infrastructure
 //
@@ -13,6 +13,7 @@
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/ArrayRef.h"
 #include <memory>
+#include <string>
 
 namespace clang {
   class ASTContext;
@@ -35,11 +36,14 @@
     All,
   };
 
-  SystemSymbolFilterKind SystemSymbolFilter
-    = SystemSymbolFilterKind::DeclarationsOnly;
+  SystemSymbolFilterKind SystemSymbolFilter =
+      SystemSymbolFilterKind::DeclarationsOnly;
   bool IndexFunctionLocals = false;
 };
 
+/// Creates a frontend action that provides decl occurrence information from the
+/// AST to the given \c IndexDataConsumer.
+///
 /// \param WrappedAction another frontend action to wrap over or null.
 std::unique_ptr<FrontendAction>
 createIndexingAction(std::shared_ptr<IndexDataConsumer> DataConsumer,
Index: include/clang/Index/IndexDiagnostic.h
===================================================================
--- /dev/null
+++ include/clang/Index/IndexDiagnostic.h
@@ -0,0 +1,29 @@
+//===--- IndexDiagnostic.h - ------------------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_INDEXDIAGNOSTIC_H
+#define LLVM_CLANG_INDEX_INDEXDIAGNOSTIC_H
+
+#include "clang/Basic/Diagnostic.h"
+
+namespace clang {
+namespace diag {
+enum {
+#define DIAG(ENUM, FLAGS, DEFAULT_MAPPING, DESC, GROUP, SFINAE, NOWERROR,      \
+             SHOWINSYSHEADER, CATEGORY)                                        \
+  ENUM,
+#define INDEXSTART
+#include "clang/Basic/DiagnosticIndexKinds.inc"
+#undef DIAG
+  NUM_BUILTIN_INDEX_DIAGNOSTICS
+};
+} // end namespace diag
+} // end namespace clang
+
+#endif // LLVM_CLANG_INDEX_INDEXDIAGNOSTIC_H
Index: include/clang/Index/IndexDataConsumer.h
===================================================================
--- include/clang/Index/IndexDataConsumer.h
+++ include/clang/Index/IndexDataConsumer.h
@@ -43,17 +43,18 @@
   virtual bool handleDeclOccurence(const Decl *D, SymbolRoleSet Roles,
                                    ArrayRef<SymbolRelation> Relations,
                                    FileID FID, unsigned Offset,
-                                   ASTNodeInfo ASTNode);
+                                   bool IsInSystemFile, ASTNodeInfo ASTNode);
 
   /// \returns true to continue indexing, or false to abort.
   virtual bool handleMacroOccurence(const IdentifierInfo *Name,
                                     const MacroInfo *MI, SymbolRoleSet Roles,
-                                    FileID FID, unsigned Offset);
+                                    FileID FID, unsigned Offset,
+                                    bool IsInSystemFile);
 
   /// \returns true to continue indexing, or false to abort.
   virtual bool handleModuleOccurence(const ImportDecl *ImportD,
-                                     SymbolRoleSet Roles,
-                                     FileID FID, unsigned Offset);
+                                     SymbolRoleSet Roles, FileID FID,
+                                     unsigned Offset, bool IsInSystemFile);
 
   virtual void finish() {}
 
Index: include/clang/Index/DeclOccurrence.h
===================================================================
--- /dev/null
+++ include/clang/Index/DeclOccurrence.h
@@ -0,0 +1,42 @@
+//===--- DeclOccurrence.h - An occurrence of a decl within a file ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_INDEX_DECLOCCURRENCE_H
+#define LLVM_CLANG_INDEX_DECLOCCURRENCE_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Index/IndexSymbol.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang {
+class Decl;
+
+namespace index {
+
+struct DeclOccurrence {
+  SymbolRoleSet Roles;
+  unsigned Offset;
+  const Decl *Dcl;
+  SmallVector<SymbolRelation, 3> Relations;
+
+  DeclOccurrence(SymbolRoleSet R, unsigned Offset, const Decl *D,
+                 ArrayRef<SymbolRelation> Relations)
+      : Roles(R), Offset(Offset), Dcl(D),
+        Relations(Relations.begin(), Relations.end()) {}
+
+  friend bool operator<(const DeclOccurrence &LHS, const DeclOccurrence &RHS) {
+    return LHS.Offset < RHS.Offset;
+  }
+};
+
+} // namespace index
+} // namespace clang
+
+#endif
Index: include/clang/Frontend/FrontendOptions.h
===================================================================
--- include/clang/Frontend/FrontendOptions.h
+++ include/clang/Frontend/FrontendOptions.h
@@ -259,6 +259,13 @@
   std::string MTMigrateDir;
   std::string ARCMTMigrateReportOut;
 
+  /// The path to write index data to
+  std::string IndexStorePath;
+  /// Whether to ignore system files when writing out index data
+  unsigned IndexIgnoreSystemSymbols : 1;
+  /// Whether to include the codegen name of symbols in the index data
+  unsigned IndexRecordCodegenName : 1;
+
   /// The input files and their types.
   std::vector<FrontendInputFile> Inputs;
 
@@ -336,8 +343,9 @@
     SkipFunctionBodies(false), UseGlobalModuleIndex(true),
     GenerateGlobalModuleIndex(true), ASTDumpDecls(false), ASTDumpLookups(false),
     BuildingImplicitModule(false), ModulesEmbedAllFiles(false),
-    IncludeTimestamps(true), ARCMTAction(ARCMT_None),
-    ObjCMTAction(ObjCMT_None), ProgramAction(frontend::ParseSyntaxOnly)
+    IncludeTimestamps(true), ARCMTAction(ARCMT_None), ObjCMTAction(ObjCMT_None),
+    IndexIgnoreSystemSymbols(false), IndexRecordCodegenName(false),
+    ProgramAction(frontend::ParseSyntaxOnly)
   {}
 
   /// getInputKindForExtension - Return the appropriate input kind for a file
Index: include/clang/Frontend/CompilerInstance.h
===================================================================
--- include/clang/Frontend/CompilerInstance.h
+++ include/clang/Frontend/CompilerInstance.h
@@ -183,6 +183,14 @@
   /// The list of active output files.
   std::list<OutputFile> OutputFiles;
 
+  typedef std::function<std::unique_ptr<FrontendAction>(
+      const FrontendOptions &Opts, std::unique_ptr<FrontendAction> Action)>
+      ActionWrapperTy;
+
+  /// \brief An optional callback function used to wrap any
+  /// GenerateModuleActions created and executed when loading modules.
+  ActionWrapperTy GenModuleActionWrapper;
+
   CompilerInstance(const CompilerInstance &) = delete;
   void operator=(const CompilerInstance &) = delete;
 public:
@@ -447,7 +455,7 @@
     return *PP;
   }
 
-  std::shared_ptr<Preprocessor> getPreprocessorPtr() { return PP; }
+  std::shared_ptr<Preprocessor> getPreprocessorPtr() const { return PP; }
 
   void resetAndLeakPreprocessor() {
     BuryPointer(new std::shared_ptr<Preprocessor>(PP));
@@ -796,6 +804,14 @@
 
   bool lookupMissingImports(StringRef Name, SourceLocation TriggerLoc) override;
 
+  void setGenModuleActionWrapper(ActionWrapperTy Wrapper) {
+    GenModuleActionWrapper = Wrapper;
+  };
+
+  ActionWrapperTy getGenModuleActionWrapper() const {
+    return GenModuleActionWrapper;
+  }
+
   void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) {
     DependencyCollectors.push_back(std::move(Listener));
   }
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -324,6 +324,13 @@
 def : Joined<["-"], "objcmt-white-list-dir-path=">, Flags<[CC1Option]>,
     Alias<objcmt_whitelist_dir_path>;
 
+def index_store_path : Separate<["-"], "index-store-path">, Flags<[CC1Option]>,
+  HelpText<"Enable indexing with the specified data store path">;
+def index_ignore_system_symbols : Flag<["-"], "index-ignore-system-symbols">, Flags<[CC1Option]>,
+  HelpText<"Ignore symbols from system headers">;
+def index_record_codegen_name : Flag<["-"], "index-record-codegen-name">, Flags<[CC1Option]>,
+  HelpText<"Record the codegen name for symbols">;
+
 // Make sure all other -ccc- options are rejected.
 def ccc_ : Joined<["-"], "ccc-">, Group<internal_Group>, Flags<[Unsupported]>;
 
Index: include/clang/Driver/Job.h
===================================================================
--- include/clang/Driver/Job.h
+++ include/clang/Driver/Job.h
@@ -34,9 +34,11 @@
 struct CrashReportInfo {
   StringRef Filename;
   StringRef VFSPath;
+  StringRef IndexStorePath;
 
-  CrashReportInfo(StringRef Filename, StringRef VFSPath)
-      : Filename(Filename), VFSPath(VFSPath) {}
+  CrashReportInfo(StringRef Filename, StringRef VFSPath,
+                  StringRef IndexStorePath)
+      : Filename(Filename), VFSPath(VFSPath), IndexStorePath(IndexStorePath) {}
 };
 
 /// Command - An executable path/name and argument vector to
Index: include/clang/Basic/DiagnosticIndexKinds.td
===================================================================
--- /dev/null
+++ include/clang/Basic/DiagnosticIndexKinds.td
@@ -0,0 +1,31 @@
+//==--- DiagnosticIndexKinds.td - indexing diagnostics --------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Indexing Diagnostics
+//===----------------------------------------------------------------------===//
+
+let Component = "Index" in {
+
+let CategoryName = "Index Store Issue" in {
+
+def err_index_store_dir_create_failed : Error<"failed creating the index store "
+  "directory: %0">;
+def err_index_store_file_status_failed : Error<"failed file status check: %0">;
+def err_index_store_record_write_failed : Error<"failed writing record '%0': "
+  "%1">;
+def err_index_store_unit_write_failed : Error<"failed writing unit data: %0">;
+
+def remark_index_producing_module_file_data : Remark<"producing index data for "
+  "module file '%0'">,
+  InGroup<IndexStore>;
+
+}
+
+} // end of Indexing diagnostics
Index: include/clang/Basic/DiagnosticIDs.h
===================================================================
--- include/clang/Basic/DiagnosticIDs.h
+++ include/clang/Basic/DiagnosticIDs.h
@@ -40,6 +40,7 @@
       DIAG_SIZE_SEMA          = 3500,
       DIAG_SIZE_ANALYSIS      =  100,
       DIAG_SIZE_REFACTORING   = 1000,
+      DIAG_SIZE_INDEX         =  100,
     };
     // Start position for diagnostics.
     enum {
@@ -55,7 +56,8 @@
       DIAG_START_SEMA          = DIAG_START_CROSSTU       + DIAG_SIZE_COMMENT,
       DIAG_START_ANALYSIS      = DIAG_START_SEMA          + DIAG_SIZE_SEMA,
       DIAG_START_REFACTORING   = DIAG_START_ANALYSIS      + DIAG_SIZE_ANALYSIS,
-      DIAG_UPPER_LIMIT         = DIAG_START_REFACTORING   + DIAG_SIZE_REFACTORING
+      DIAG_START_INDEX         = DIAG_START_REFACTORING   + DIAG_SIZE_REFACTORING,
+      DIAG_UPPER_LIMIT         = DIAG_START_INDEX         + DIAG_SIZE_INDEX,
     };
 
     class CustomDiagInfo;
Index: include/clang/Basic/DiagnosticGroups.td
===================================================================
--- include/clang/Basic/DiagnosticGroups.td
+++ include/clang/Basic/DiagnosticGroups.td
@@ -327,6 +327,7 @@
 def ModuleBuild : DiagGroup<"module-build">;
 def ModuleConflict : DiagGroup<"module-conflict">;
 def ModuleFileExtension : DiagGroup<"module-file-extension">;
+def IndexStore : DiagGroup<"index-store">;
 def NewlineEOF : DiagGroup<"newline-eof">;
 def Nullability : DiagGroup<"nullability">;
 def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
Index: include/clang/Basic/Diagnostic.td
===================================================================
--- include/clang/Basic/Diagnostic.td
+++ include/clang/Basic/Diagnostic.td
@@ -136,6 +136,7 @@
 include "DiagnosticCrossTUKinds.td"
 include "DiagnosticDriverKinds.td"
 include "DiagnosticFrontendKinds.td"
+include "DiagnosticIndexKinds.td"
 include "DiagnosticLexKinds.td"
 include "DiagnosticParseKinds.td"
 include "DiagnosticRefactoringKinds.td"
Index: include/clang/Basic/CMakeLists.txt
===================================================================
--- include/clang/Basic/CMakeLists.txt
+++ include/clang/Basic/CMakeLists.txt
@@ -12,6 +12,7 @@
 clang_diag_gen(CrossTU)
 clang_diag_gen(Driver)
 clang_diag_gen(Frontend)
+clang_diag_gen(Index)
 clang_diag_gen(Lex)
 clang_diag_gen(Parse)
 clang_diag_gen(Refactoring)
Index: include/clang/Basic/AllDiagnostics.h
===================================================================
--- include/clang/Basic/AllDiagnostics.h
+++ include/clang/Basic/AllDiagnostics.h
@@ -21,6 +21,7 @@
 #include "clang/CrossTU/CrossTUDiagnostic.h"
 #include "clang/Driver/DriverDiagnostic.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Index/IndexDiagnostic.h"
 #include "clang/Lex/LexDiagnostic.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Sema/SemaDiagnostic.h"
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to