[clangd] Populate #include insertions as additional edits in completion items.

Summary:
o Remove IncludeInsertion LSP command.
o Populate include insertion edits synchromously in completion items.
o Share the code completion compiler instance and precompiled preamble to get existing inclusions in main file.
o Include insertion logic lives only in CodeComplete now.
o Use tooling::HeaderIncludes for inserting new includes.
o Refactored tests.

Reviewers: sammccall

Reviewed By: sammccall

Subscribers: klimek, ilya-biryukov, MaskRay, jkorous, cfe-commits

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

llvm-svn: 332363
diff --git a/clang-tools-extra/clangd/Headers.cpp b/clang-tools-extra/clangd/Headers.cpp
index 58a81dc..c6f18fa 100644
--- a/clang-tools-extra/clangd/Headers.cpp
+++ b/clang-tools-extra/clangd/Headers.cpp
@@ -15,8 +15,6 @@
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "clang/Lex/HeaderSearch.h"
-#include "clang/Lex/PreprocessorOptions.h"
-#include "clang/Tooling/CompilationDatabase.h"
 #include "llvm/Support/Path.h"
 
 namespace clang {
@@ -74,64 +72,13 @@
 
 /// FIXME(ioeric): we might not want to insert an absolute include path if the
 /// path is not shortened.
-llvm::Expected<std::string>
-calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,
-                     const HeaderFile &DeclaringHeader,
-                     const HeaderFile &InsertedHeader,
-                     const tooling::CompileCommand &CompileCommand,
-                     IntrusiveRefCntPtr<vfs::FileSystem> FS) {
-  assert(llvm::sys::path::is_absolute(File));
+llvm::Expected<std::string> calculateIncludePath(
+    PathRef File, StringRef BuildDir, HeaderSearch &HeaderSearchInfo,
+    const std::vector<Inclusion> &Inclusions, const HeaderFile &DeclaringHeader,
+    const HeaderFile &InsertedHeader) {
   assert(DeclaringHeader.valid() && InsertedHeader.valid());
   if (File == DeclaringHeader.File || File == InsertedHeader.File)
     return "";
-  FS->setCurrentWorkingDirectory(CompileCommand.Directory);
-
-  // Set up a CompilerInstance and create a preprocessor to collect existing
-  // #include headers in \p Code. Preprocesor also provides HeaderSearch with
-  // which we can calculate the shortest include path for \p Header.
-  std::vector<const char *> Argv;
-  for (const auto &S : CompileCommand.CommandLine)
-    Argv.push_back(S.c_str());
-  IgnoringDiagConsumer IgnoreDiags;
-  auto CI = clang::createInvocationFromCommandLine(
-      Argv,
-      CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,
-                                          false),
-      FS);
-  if (!CI)
-    return llvm::make_error<llvm::StringError>(
-        "Failed to create a compiler instance for " + File,
-        llvm::inconvertibleErrorCode());
-  CI->getFrontendOpts().DisableFree = false;
-  // Parse the main file to get all existing #includes in the file, and then we
-  // can make sure the same header (even with different include path) is not
-  // added more than once.
-  CI->getPreprocessorOpts().SingleFileParseMode = true;
-
-  // The diagnostic options must be set before creating a CompilerInstance.
-  CI->getDiagnosticOpts().IgnoreWarnings = true;
-  auto Clang = prepareCompilerInstance(
-      std::move(CI), /*Preamble=*/nullptr,
-      llvm::MemoryBuffer::getMemBuffer(Code, File),
-      std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);
-
-  if (Clang->getFrontendOpts().Inputs.empty())
-    return llvm::make_error<llvm::StringError>(
-        "Empty frontend action inputs empty for file " + File,
-        llvm::inconvertibleErrorCode());
-  PreprocessOnlyAction Action;
-  if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
-    return llvm::make_error<llvm::StringError>(
-        "Failed to begin preprocessor only action for file " + File,
-        llvm::inconvertibleErrorCode());
-  std::vector<Inclusion> Inclusions;
-  Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback(
-      Clang->getSourceManager(),
-      [&Inclusions](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); }));
-  if (!Action.Execute())
-    return llvm::make_error<llvm::StringError>(
-        "Failed to execute preprocessor only action for file " + File,
-        llvm::inconvertibleErrorCode());
   llvm::StringSet<> IncludedHeaders;
   for (const auto &Inc : Inclusions) {
     IncludedHeaders.insert(Inc.Written);
@@ -144,14 +91,13 @@
   if (Included(DeclaringHeader.File) || Included(InsertedHeader.File))
     return "";
 
-  auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();
   bool IsSystem = false;
 
   if (InsertedHeader.Verbatim)
     return InsertedHeader.File;
 
   std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(
-      InsertedHeader.File, CompileCommand.Directory, &IsSystem);
+      InsertedHeader.File, BuildDir, &IsSystem);
   if (IsSystem)
     Suggested = "<" + Suggested + ">";
   else
@@ -161,5 +107,35 @@
   return Suggested;
 }
 
+Expected<Optional<TextEdit>>
+IncludeInserter::insert(const HeaderFile &DeclaringHeader,
+                        const HeaderFile &InsertedHeader) const {
+  auto Validate = [](const HeaderFile &Header) {
+    return Header.valid()
+               ? llvm::Error::success()
+               : llvm::make_error<llvm::StringError>(
+                     "Invalid HeaderFile: " + Header.File +
+                         " (verbatim=" + std::to_string(Header.Verbatim) + ").",
+                     llvm::inconvertibleErrorCode());
+  };
+  if (auto Err = Validate(DeclaringHeader))
+    return std::move(Err);
+  if (auto Err = Validate(InsertedHeader))
+    return std::move(Err);
+  auto Include =
+      calculateIncludePath(FileName, BuildDir, HeaderSearchInfo, Inclusions,
+                           DeclaringHeader, InsertedHeader);
+  if (!Include)
+    return Include.takeError();
+  if (Include->empty())
+    return llvm::None;
+  StringRef IncludeRef = *Include;
+  auto Insertion =
+      Inserter.insert(IncludeRef.trim("\"<>"), IncludeRef.startswith("<"));
+  if (!Insertion)
+    return llvm::None;
+  return replacementToEdit(Code, *Insertion);
+}
+
 } // namespace clangd
 } // namespace clang