<div dir="ltr">r<span style="color:rgb(33,33,33)">332520 fixed the broken build in clangd. </span></div><br><div class="gmail_quote"><div dir="ltr">On Wed, May 16, 2018 at 9:48 PM Eric Liu <<a href="mailto:ioeric@google.com">ioeric@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Looking... sorry that I missed the email from this bot.</div><br><div class="gmail_quote"><div dir="ltr">On Wed, May 16, 2018 at 9:43 PM Galina Kistanova <<a href="mailto:gkistanova@gmail.com" target="_blank">gkistanova@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Hello Eric,<br><br>This commit broke one of our builders:<br><a href="http://lab.llvm.org:8011/builders/clang-x86_64-linux-abi-test/builds/26399" target="_blank">http://lab.llvm.org:8011/builders/clang-x86_64-linux-abi-test/builds/26399</a><br><br>. . .<br>387.403 [725/64/4163] Building CXX object tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o<br>FAILED: tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o <br>/usr/bin/c++   -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -Itools/clang/tools/extra/clangd -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/include -Itools/clang/include -Iinclude -I/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/include -fPIC -fvisibility-inlines-hidden -std=c++11 -Wall -W -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wno-maybe-uninitialized -Wdelete-non-virtual-dtor -Wno-comment -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -fno-strict-aliasing -O3    -UNDEBUG  -fno-exceptions -fno-rtti -MD -MT tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o -MF tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o.d -o tools/clang/tools/extra/clangd/CMakeFiles/clangDaemon.dir/CodeComplete.cpp.o -c /home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp<br>/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp: In function ‘clang::clangd::SignatureHelp clang::clangd::signatureHelp(clang::clangd::PathRef, const clang::tooling::CompileCommand&, const clang::PrecompiledPreamble*, llvm::StringRef, clang::clangd::Position, llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem>, std::shared_ptr<clang::PCHContainerOperations>)’:<br>/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp:1127:37: error: invalid initialization of reference of type ‘const clang::clangd::{anonymous}::SemaCompleteInput&’ from expression of type ‘<brace-enclosed initializer list>’<br>                     std::move(PCHs)});<br>                                     ^<br>/home/buildslave/buildslave1a/clang-x86_64-linux-abi-test/llvm/tools/clang/tools/extra/clangd/CodeComplete.cpp:710:6: error: in passing argument 3 of ‘bool clang::clangd::{anonymous}::semaCodeComplete(std::unique_ptr<clang::CodeCompleteConsumer>, const clang::CodeCompleteOptions&, const clang::clangd::{anonymous}::SemaCompleteInput&, std::unique_ptr<clang::clangd::IncludeInserter>*)’</div><div dir="ltr"><br> bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,<br></div><div dir="ltr">      ^<br>. . .<br><br>Please have a look?<br><br>It is not good idea to keep the bot red for too long. This hides new problem which later hard to track down.<br><br>Thanks</div><div dir="ltr"><br><br>Galina<br><br><br></div><div class="gmail_extra"><br><div class="gmail_quote">On Tue, May 15, 2018 at 8:29 AM, Eric Liu via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: ioeric<br>
Date: Tue May 15 08:29:32 2018<br>
New Revision: 332363<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=332363&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=332363&view=rev</a><br>
Log:<br>
[clangd] Populate #include insertions as additional edits in completion items.<br>
<br>
Summary:<br>
o Remove IncludeInsertion LSP command.<br>
o Populate include insertion edits synchromously in completion items.<br>
o Share the code completion compiler instance and precompiled preamble to get existing inclusions in main file.<br>
o Include insertion logic lives only in CodeComplete now.<br>
o Use tooling::HeaderIncludes for inserting new includes.<br>
o Refactored tests.<br>
<br>
Reviewers: sammccall<br>
<br>
Reviewed By: sammccall<br>
<br>
Subscribers: klimek, ilya-biryukov, MaskRay, jkorous, cfe-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D46497" rel="noreferrer" target="_blank">https://reviews.llvm.org/D46497</a><br>
<br>
Modified:<br>
    clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
    clang-tools-extra/trunk/clangd/CodeComplete.cpp<br>
    clang-tools-extra/trunk/clangd/CodeComplete.h<br>
    clang-tools-extra/trunk/clangd/Headers.cpp<br>
    clang-tools-extra/trunk/clangd/Headers.h<br>
    clang-tools-extra/trunk/clangd/Protocol.h<br>
    clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp<br>
    clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp<br>
<br>
Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Tue May 15 08:29:32 2018<br>
@@ -161,6 +161,7 @@ void ClangdServer::codeComplete(PathRef<br>
     // both the old and the new version in case only one of them matches.<br>
     CompletionList Result = clangd::codeComplete(<br>
         File, IP->Command, PreambleData ? &PreambleData->Preamble : nullptr,<br>
+        PreambleData ? PreambleData->Inclusions : std::vector<Inclusion>(),<br>
         IP->Contents, Pos, FS, PCHs, CodeCompleteOpts);<br>
     CB(std::move(Result));<br>
   };<br>
<br>
Modified: clang-tools-extra/trunk/clangd/CodeComplete.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.cpp?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/CodeComplete.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/CodeComplete.cpp Tue May 15 08:29:32 2018<br>
@@ -18,9 +18,11 @@<br>
 #include "CodeCompletionStrings.h"<br>
 #include "Compiler.h"<br>
 #include "FuzzyMatch.h"<br>
+#include "Headers.h"<br>
 #include "Logger.h"<br>
 #include "SourceCode.h"<br>
 #include "Trace.h"<br>
+#include "URI.h"<br>
 #include "index/Index.h"<br>
 #include "clang/Format/Format.h"<br>
 #include "clang/Frontend/CompilerInstance.h"<br>
@@ -219,6 +221,28 @@ std::string sortText(float Score, llvm::<br>
   return S;<br>
 }<br>
<br>
+/// Creates a `HeaderFile` from \p Header which can be either a URI or a literal<br>
+/// include.<br>
+static llvm::Expected<HeaderFile> toHeaderFile(StringRef Header,<br>
+                                               llvm::StringRef HintPath) {<br>
+  if (isLiteralInclude(Header))<br>
+    return HeaderFile{Header.str(), /*Verbatim=*/true};<br>
+  auto U = URI::parse(Header);<br>
+  if (!U)<br>
+    return U.takeError();<br>
+<br>
+  auto IncludePath = URI::includeSpelling(*U);<br>
+  if (!IncludePath)<br>
+    return IncludePath.takeError();<br>
+  if (!IncludePath->empty())<br>
+    return HeaderFile{std::move(*IncludePath), /*Verbatim=*/true};<br>
+<br>
+  auto Resolved = URI::resolve(*U, HintPath);<br>
+  if (!Resolved)<br>
+    return Resolved.takeError();<br>
+  return HeaderFile{std::move(*Resolved), /*Verbatim=*/false};<br>
+}<br>
+<br>
 /// A code completion result, in clang-native form.<br>
 /// It may be promoted to a CompletionItem if it's among the top-ranked results.<br>
 struct CompletionCandidate {<br>
@@ -255,10 +279,10 @@ struct CompletionCandidate {<br>
   }<br>
<br>
   // Builds an LSP completion item.<br>
-  CompletionItem build(llvm::StringRef FileName,<br>
-                       const CompletionItemScores &Scores,<br>
+  CompletionItem build(StringRef FileName, const CompletionItemScores &Scores,<br>
                        const CodeCompleteOptions &Opts,<br>
-                       CodeCompletionString *SemaCCS) const {<br>
+                       CodeCompletionString *SemaCCS,<br>
+                       const IncludeInserter *Includes) const {<br>
     assert(bool(SemaResult) == bool(SemaCCS));<br>
     CompletionItem I;<br>
     if (SemaResult) {<br>
@@ -289,6 +313,30 @@ struct CompletionCandidate {<br>
           I.documentation = D->Documentation;<br>
         if (I.detail.empty())<br>
           I.detail = D->CompletionDetail;<br>
+        if (Includes && !D->IncludeHeader.empty()) {<br>
+          auto Edit = [&]() -> Expected<Optional<TextEdit>> {<br>
+            auto ResolvedDeclaring = toHeaderFile(<br>
+                IndexResult->CanonicalDeclaration.FileURI, FileName);<br>
+            if (!ResolvedDeclaring)<br>
+              return ResolvedDeclaring.takeError();<br>
+            auto ResolvedInserted = toHeaderFile(D->IncludeHeader, FileName);<br>
+            if (!ResolvedInserted)<br>
+              return ResolvedInserted.takeError();<br>
+            return Includes->insert(*ResolvedDeclaring, *ResolvedInserted);<br>
+          }();<br>
+          if (!Edit) {<br>
+            std::string ErrMsg =<br>
+                ("Failed to generate include insertion edits for adding header "<br>
+                 "(FileURI=\"" +<br>
+                 IndexResult->CanonicalDeclaration.FileURI +<br>
+                 "\", IncludeHeader=\"" + D->IncludeHeader + "\") into " +<br>
+                 FileName)<br>
+                    .str();<br>
+            log(ErrMsg + ":" + llvm::toString(Edit.takeError()));<br>
+          } else if (*Edit) {<br>
+            I.additionalTextEdits = {std::move(**Edit)};<br>
+          }<br>
+        }<br>
       }<br>
     }<br>
     I.scoreInfo = Scores;<br>
@@ -649,6 +697,7 @@ struct SemaCompleteInput {<br>
   PathRef FileName;<br>
   const tooling::CompileCommand &Command;<br>
   PrecompiledPreamble const *Preamble;<br>
+  const std::vector<Inclusion> &PreambleInclusions;<br>
   StringRef Contents;<br>
   Position Pos;<br>
   IntrusiveRefCntPtr<vfs::FileSystem> VFS;<br>
@@ -656,9 +705,12 @@ struct SemaCompleteInput {<br>
 };<br>
<br>
 // Invokes Sema code completion on a file.<br>
+// If \p Includes is set, it will be initialized after a compiler instance has<br>
+// been set up.<br>
 bool semaCodeComplete(std::unique_ptr<CodeCompleteConsumer> Consumer,<br>
                       const clang::CodeCompleteOptions &Options,<br>
-                      const SemaCompleteInput &Input) {<br>
+                      const SemaCompleteInput &Input,<br>
+                      std::unique_ptr<IncludeInserter> *Includes = nullptr) {<br>
   trace::Span Tracer("Sema completion");<br>
   std::vector<const char *> ArgStrs;<br>
   for (const auto &S : Input.Command.CommandLine)<br>
@@ -729,6 +781,28 @@ bool semaCodeComplete(std::unique_ptr<Co<br>
         Input.FileName);<br>
     return false;<br>
   }<br>
+  if (Includes) {<br>
+    // Initialize Includes if provided.<br>
+<br>
+    // FIXME(ioeric): needs more consistent style support in clangd server.<br>
+    auto Style = format::getStyle("file", Input.FileName, "LLVM",<br>
+                                  Input.Contents, Input.VFS.get());<br>
+    if (!Style) {<br>
+      log("Failed to get FormatStyle for file" + Input.FileName +<br>
+          ". Fall back to use LLVM style. Error: " +<br>
+          llvm::toString(Style.takeError()));<br>
+      Style = format::getLLVMStyle();<br>
+    }<br>
+    *Includes = llvm::make_unique<IncludeInserter>(<br>
+        Input.FileName, Input.Contents, *Style, Input.Command.Directory,<br>
+        Clang->getPreprocessor().getHeaderSearchInfo());<br>
+    for (const auto &Inc : Input.PreambleInclusions)<br>
+      Includes->get()->addExisting(Inc);<br>
+    Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback(<br>
+        Clang->getSourceManager(), [Includes](Inclusion Inc) {<br>
+          Includes->get()->addExisting(std::move(Inc));<br>
+        }));<br>
+  }<br>
   if (!Action.Execute()) {<br>
     log("Execute() failed when running codeComplete for " + Input.FileName);<br>
     return false;<br>
@@ -863,7 +937,8 @@ class CodeCompleteFlow {<br>
   CompletionRecorder *Recorder = nullptr;<br>
   int NSema = 0, NIndex = 0, NBoth = 0; // Counters for logging.<br>
   bool Incomplete = false; // Would more be available with a higher limit?<br>
-  llvm::Optional<FuzzyMatcher> Filter; // Initialized once Sema runs.<br>
+  llvm::Optional<FuzzyMatcher> Filter;       // Initialized once Sema runs.<br>
+  std::unique_ptr<IncludeInserter> Includes; // Initialized once compiler runs.<br>
<br>
 public:<br>
   // A CodeCompleteFlow object is only useful for calling run() exactly once.<br>
@@ -872,20 +947,25 @@ public:<br>
<br>
   CompletionList run(const SemaCompleteInput &SemaCCInput) && {<br>
     trace::Span Tracer("CodeCompleteFlow");<br>
+<br>
     // We run Sema code completion first. It builds an AST and calculates:<br>
     //   - completion results based on the AST.<br>
     //   - partial identifier and context. We need these for the index query.<br>
     CompletionList Output;<br>
     auto RecorderOwner = llvm::make_unique<CompletionRecorder>(Opts, [&]() {<br>
       assert(Recorder && "Recorder is not set");<br>
+      assert(Includes && "Includes is not set");<br>
+      // If preprocessor was run, inclusions from preprocessor callback should<br>
+      // already be added to Inclusions.<br>
       Output = runWithSema();<br>
+      Includes.reset(); // Make sure this doesn't out-live Clang.<br>
       SPAN_ATTACH(Tracer, "sema_completion_kind",<br>
                   getCompletionKindString(Recorder->CCContext.getKind()));<br>
     });<br>
<br>
     Recorder = RecorderOwner.get();<br>
     semaCodeComplete(std::move(RecorderOwner), Opts.getClangCompleteOpts(),<br>
-                     SemaCCInput);<br>
+                     SemaCCInput, &Includes);<br>
<br>
     SPAN_ATTACH(Tracer, "sema_results", NSema);<br>
     SPAN_ATTACH(Tracer, "index_results", NIndex);<br>
@@ -1011,19 +1091,21 @@ private:<br>
     CodeCompletionString *SemaCCS = nullptr;<br>
     if (auto *SR = Candidate.SemaResult)<br>
       SemaCCS = Recorder->codeCompletionString(*SR, Opts.IncludeBriefComments);<br>
-    return Candidate.build(FileName, Scores, Opts, SemaCCS);<br>
+    return Candidate.build(FileName, Scores, Opts, SemaCCS, Includes.get());<br>
   }<br>
 };<br>
<br>
 CompletionList codeComplete(PathRef FileName,<br>
                             const tooling::CompileCommand &Command,<br>
                             PrecompiledPreamble const *Preamble,<br>
+                            const std::vector<Inclusion> &PreambleInclusions,<br>
                             StringRef Contents, Position Pos,<br>
                             IntrusiveRefCntPtr<vfs::FileSystem> VFS,<br>
                             std::shared_ptr<PCHContainerOperations> PCHs,<br>
                             CodeCompleteOptions Opts) {<br>
   return CodeCompleteFlow(FileName, Opts)<br>
-      .run({FileName, Command, Preamble, Contents, Pos, VFS, PCHs});<br>
+      .run({FileName, Command, Preamble, PreambleInclusions, Contents, Pos, VFS,<br>
+            PCHs});<br>
 }<br>
<br>
 SignatureHelp signatureHelp(PathRef FileName,<br>
@@ -1040,7 +1122,8 @@ SignatureHelp signatureHelp(PathRef File<br>
   Options.IncludeBriefComments = true;<br>
   semaCodeComplete(llvm::make_unique<SignatureHelpCollector>(Options, Result),<br>
                    Options,<br>
-                   {FileName, Command, Preamble, Contents, Pos, std::move(VFS),<br>
+                   {FileName, Command, Preamble,<br>
+                    /*PreambleInclusions=*/{}, Contents, Pos, std::move(VFS),<br>
                     std::move(PCHs)});<br>
   return Result;<br>
 }<br>
<br>
Modified: clang-tools-extra/trunk/clangd/CodeComplete.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.h?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/CodeComplete.h?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/CodeComplete.h (original)<br>
+++ clang-tools-extra/trunk/clangd/CodeComplete.h Tue May 15 08:29:32 2018<br>
@@ -15,6 +15,7 @@<br>
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H<br>
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H<br>
<br>
+#include "Headers.h"<br>
 #include "Logger.h"<br>
 #include "Path.h"<br>
 #include "Protocol.h"<br>
@@ -69,6 +70,7 @@ struct CodeCompleteOptions {<br>
 CompletionList codeComplete(PathRef FileName,<br>
                             const tooling::CompileCommand &Command,<br>
                             PrecompiledPreamble const *Preamble,<br>
+                            const std::vector<Inclusion> &PreambleInclusions,<br>
                             StringRef Contents, Position Pos,<br>
                             IntrusiveRefCntPtr<vfs::FileSystem> VFS,<br>
                             std::shared_ptr<PCHContainerOperations> PCHs,<br>
<br>
Modified: clang-tools-extra/trunk/clangd/Headers.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.cpp?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.cpp?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/Headers.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/Headers.cpp Tue May 15 08:29:32 2018<br>
@@ -15,8 +15,6 @@<br>
 #include "clang/Frontend/CompilerInvocation.h"<br>
 #include "clang/Frontend/FrontendActions.h"<br>
 #include "clang/Lex/HeaderSearch.h"<br>
-#include "clang/Lex/PreprocessorOptions.h"<br>
-#include "clang/Tooling/CompilationDatabase.h"<br>
 #include "llvm/Support/Path.h"<br>
<br>
 namespace clang {<br>
@@ -74,64 +72,13 @@ collectInclusionsInMainFileCallback(cons<br>
<br>
 /// FIXME(ioeric): we might not want to insert an absolute include path if the<br>
 /// path is not shortened.<br>
-llvm::Expected<std::string><br>
-calculateIncludePath(llvm::StringRef File, llvm::StringRef Code,<br>
-                     const HeaderFile &DeclaringHeader,<br>
-                     const HeaderFile &InsertedHeader,<br>
-                     const tooling::CompileCommand &CompileCommand,<br>
-                     IntrusiveRefCntPtr<vfs::FileSystem> FS) {<br>
-  assert(llvm::sys::path::is_absolute(File));<br>
+llvm::Expected<std::string> calculateIncludePath(<br>
+    PathRef File, StringRef BuildDir, HeaderSearch &HeaderSearchInfo,<br>
+    const std::vector<Inclusion> &Inclusions, const HeaderFile &DeclaringHeader,<br>
+    const HeaderFile &InsertedHeader) {<br>
   assert(DeclaringHeader.valid() && InsertedHeader.valid());<br>
   if (File == DeclaringHeader.File || File == InsertedHeader.File)<br>
     return "";<br>
-  FS->setCurrentWorkingDirectory(CompileCommand.Directory);<br>
-<br>
-  // Set up a CompilerInstance and create a preprocessor to collect existing<br>
-  // #include headers in \p Code. Preprocesor also provides HeaderSearch with<br>
-  // which we can calculate the shortest include path for \p Header.<br>
-  std::vector<const char *> Argv;<br>
-  for (const auto &S : CompileCommand.CommandLine)<br>
-    Argv.push_back(S.c_str());<br>
-  IgnoringDiagConsumer IgnoreDiags;<br>
-  auto CI = clang::createInvocationFromCommandLine(<br>
-      Argv,<br>
-      CompilerInstance::createDiagnostics(new DiagnosticOptions(), &IgnoreDiags,<br>
-                                          false),<br>
-      FS);<br>
-  if (!CI)<br>
-    return llvm::make_error<llvm::StringError>(<br>
-        "Failed to create a compiler instance for " + File,<br>
-        llvm::inconvertibleErrorCode());<br>
-  CI->getFrontendOpts().DisableFree = false;<br>
-  // Parse the main file to get all existing #includes in the file, and then we<br>
-  // can make sure the same header (even with different include path) is not<br>
-  // added more than once.<br>
-  CI->getPreprocessorOpts().SingleFileParseMode = true;<br>
-<br>
-  // The diagnostic options must be set before creating a CompilerInstance.<br>
-  CI->getDiagnosticOpts().IgnoreWarnings = true;<br>
-  auto Clang = prepareCompilerInstance(<br>
-      std::move(CI), /*Preamble=*/nullptr,<br>
-      llvm::MemoryBuffer::getMemBuffer(Code, File),<br>
-      std::make_shared<PCHContainerOperations>(), FS, IgnoreDiags);<br>
-<br>
-  if (Clang->getFrontendOpts().Inputs.empty())<br>
-    return llvm::make_error<llvm::StringError>(<br>
-        "Empty frontend action inputs empty for file " + File,<br>
-        llvm::inconvertibleErrorCode());<br>
-  PreprocessOnlyAction Action;<br>
-  if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))<br>
-    return llvm::make_error<llvm::StringError>(<br>
-        "Failed to begin preprocessor only action for file " + File,<br>
-        llvm::inconvertibleErrorCode());<br>
-  std::vector<Inclusion> Inclusions;<br>
-  Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback(<br>
-      Clang->getSourceManager(),<br>
-      [&Inclusions](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); }));<br>
-  if (!Action.Execute())<br>
-    return llvm::make_error<llvm::StringError>(<br>
-        "Failed to execute preprocessor only action for file " + File,<br>
-        llvm::inconvertibleErrorCode());<br>
   llvm::StringSet<> IncludedHeaders;<br>
   for (const auto &Inc : Inclusions) {<br>
     IncludedHeaders.insert(Inc.Written);<br>
@@ -144,14 +91,13 @@ calculateIncludePath(llvm::StringRef Fil<br>
   if (Included(DeclaringHeader.File) || Included(InsertedHeader.File))<br>
     return "";<br>
<br>
-  auto &HeaderSearchInfo = Clang->getPreprocessor().getHeaderSearchInfo();<br>
   bool IsSystem = false;<br>
<br>
   if (InsertedHeader.Verbatim)<br>
     return InsertedHeader.File;<br>
<br>
   std::string Suggested = HeaderSearchInfo.suggestPathToFileForDiagnostics(<br>
-      InsertedHeader.File, CompileCommand.Directory, &IsSystem);<br>
+      InsertedHeader.File, BuildDir, &IsSystem);<br>
   if (IsSystem)<br>
     Suggested = "<" + Suggested + ">";<br>
   else<br>
@@ -161,5 +107,35 @@ calculateIncludePath(llvm::StringRef Fil<br>
   return Suggested;<br>
 }<br>
<br>
+Expected<Optional<TextEdit>><br>
+IncludeInserter::insert(const HeaderFile &DeclaringHeader,<br>
+                        const HeaderFile &InsertedHeader) const {<br>
+  auto Validate = [](const HeaderFile &Header) {<br>
+    return Header.valid()<br>
+               ? llvm::Error::success()<br>
+               : llvm::make_error<llvm::StringError>(<br>
+                     "Invalid HeaderFile: " + Header.File +<br>
+                         " (verbatim=" + std::to_string(Header.Verbatim) + ").",<br>
+                     llvm::inconvertibleErrorCode());<br>
+  };<br>
+  if (auto Err = Validate(DeclaringHeader))<br>
+    return std::move(Err);<br>
+  if (auto Err = Validate(InsertedHeader))<br>
+    return std::move(Err);<br>
+  auto Include =<br>
+      calculateIncludePath(FileName, BuildDir, HeaderSearchInfo, Inclusions,<br>
+                           DeclaringHeader, InsertedHeader);<br>
+  if (!Include)<br>
+    return Include.takeError();<br>
+  if (Include->empty())<br>
+    return llvm::None;<br>
+  StringRef IncludeRef = *Include;<br>
+  auto Insertion =<br>
+      Inserter.insert(IncludeRef.trim("\"<>"), IncludeRef.startswith("<"));<br>
+  if (!Insertion)<br>
+    return llvm::None;<br>
+  return replacementToEdit(Code, *Insertion);<br>
+}<br>
+<br>
 } // namespace clangd<br>
 } // namespace clang<br>
<br>
Modified: clang-tools-extra/trunk/clangd/Headers.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.h?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Headers.h?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/Headers.h (original)<br>
+++ clang-tools-extra/trunk/clangd/Headers.h Tue May 15 08:29:32 2018<br>
@@ -12,10 +12,14 @@<br>
<br>
 #include "Path.h"<br>
 #include "Protocol.h"<br>
+#include "SourceCode.h"<br>
 #include "clang/Basic/VirtualFileSystem.h"<br>
+#include "clang/Format/Format.h"<br>
+#include "clang/Lex/HeaderSearch.h"<br>
 #include "clang/Lex/PPCallbacks.h"<br>
-#include "clang/Tooling/CompilationDatabase.h"<br>
+#include "clang/Tooling/Core/HeaderIncludes.h"<br>
 #include "llvm/ADT/StringRef.h"<br>
+#include "llvm/ADT/StringSet.h"<br>
 #include "llvm/Support/Error.h"<br>
<br>
 namespace clang {<br>
@@ -51,20 +55,51 @@ collectInclusionsInMainFileCallback(cons<br>
 /// 'Foo/Bar.h' over a longer one like 'Baz/include/Foo/Bar.h'.<br>
 ///<br>
 /// \param File is an absolute file path.<br>
+/// \param Inclusions Existing inclusions in the main file.<br>
 /// \param DeclaringHeader is the original header corresponding to \p<br>
 /// InsertedHeader e.g. the header that declares a symbol.<br>
 /// \param InsertedHeader The preferred header to be inserted. This could be the<br>
 /// same as DeclaringHeader but must be provided.<br>
 //  \return A quoted "path" or <path>. This returns an empty string if:<br>
 ///   - Either \p DeclaringHeader or \p InsertedHeader is already (directly)<br>
-///   included in the file (including those included via different paths).<br>
+///   in \p Inclusions (including those included via different paths).<br>
 ///   - \p DeclaringHeader or \p InsertedHeader is the same as \p File.<br>
-llvm::Expected<std::string><br>
-calculateIncludePath(PathRef File, llvm::StringRef Code,<br>
-                     const HeaderFile &DeclaringHeader,<br>
-                     const HeaderFile &InsertedHeader,<br>
-                     const tooling::CompileCommand &CompileCommand,<br>
-                     IntrusiveRefCntPtr<vfs::FileSystem> FS);<br>
+llvm::Expected<std::string> calculateIncludePath(<br>
+    PathRef File, StringRef BuildDir, HeaderSearch &HeaderSearchInfo,<br>
+    const std::vector<Inclusion> &Inclusions, const HeaderFile &DeclaringHeader,<br>
+    const HeaderFile &InsertedHeader);<br>
+<br>
+// Calculates insertion edit for including a new header in a file.<br>
+class IncludeInserter {<br>
+public:<br>
+  IncludeInserter(StringRef FileName, StringRef Code,<br>
+                  const format::FormatStyle &Style, StringRef BuildDir,<br>
+                  HeaderSearch &HeaderSearchInfo)<br>
+      : FileName(FileName), Code(Code), BuildDir(BuildDir),<br>
+        HeaderSearchInfo(HeaderSearchInfo),<br>
+        Inserter(FileName, Code, Style.IncludeStyle) {}<br>
+<br>
+  void addExisting(Inclusion Inc) { Inclusions.push_back(std::move(Inc)); }<br>
+<br>
+  /// Returns a TextEdit that inserts a new header; if the header is not<br>
+  /// inserted e.g. it's an existing header, this returns None. If any header is<br>
+  /// invalid, this returns error.<br>
+  ///<br>
+  /// \param DeclaringHeader is the original header corresponding to \p<br>
+  /// InsertedHeader e.g. the header that declares a symbol.<br>
+  /// \param InsertedHeader The preferred header to be inserted. This could be<br>
+  /// the same as DeclaringHeader but must be provided.<br>
+  Expected<Optional<TextEdit>> insert(const HeaderFile &DeclaringHeader,<br>
+                                      const HeaderFile &InsertedHeader) const;<br>
+<br>
+private:<br>
+  StringRef FileName;<br>
+  StringRef Code;<br>
+  StringRef BuildDir;<br>
+  HeaderSearch &HeaderSearchInfo;<br>
+  std::vector<Inclusion> Inclusions;<br>
+  tooling::HeaderIncludes Inserter; // Computers insertion replacement.<br>
+};<br>
<br>
 } // namespace clangd<br>
 } // namespace clang<br>
<br>
Modified: clang-tools-extra/trunk/clangd/Protocol.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/Protocol.h (original)<br>
+++ clang-tools-extra/trunk/clangd/Protocol.h Tue May 15 08:29:32 2018<br>
@@ -99,6 +99,9 @@ struct Position {<br>
     return std::tie(LHS.line, LHS.character) ==<br>
            std::tie(RHS.line, RHS.character);<br>
   }<br>
+  friend bool operator!=(const Position &LHS, const Position &RHS) {<br>
+    return !(LHS == RHS);<br>
+  }<br>
   friend bool operator<(const Position &LHS, const Position &RHS) {<br>
     return std::tie(LHS.line, LHS.character) <<br>
            std::tie(RHS.line, RHS.character);<br>
<br>
Modified: clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp (original)<br>
+++ clang-tools-extra/trunk/unittests/clangd/CodeCompleteTests.cpp Tue May 15 08:29:32 2018<br>
@@ -45,6 +45,16 @@ MATCHER_P(Kind, K, "") { return arg.kind<br>
 MATCHER_P(Filter, F, "") { return arg.filterText == F; }<br>
 MATCHER_P(Doc, D, "") { return arg.documentation == D; }<br>
 MATCHER_P(Detail, D, "") { return arg.detail == D; }<br>
+MATCHER_P(InsertInclude, IncludeHeader, "") {<br>
+  if (arg.additionalTextEdits.size() != 1)<br>
+    return false;<br>
+  const auto &Edit = arg.additionalTextEdits[0];<br>
+  if (Edit.range.start != Edit.range.end)<br>
+    return false;<br>
+  SmallVector<StringRef, 2> Matches;<br>
+  llvm::Regex RE(R"(#include[ ]*(["<][^">]*[">]))");<br>
+  return RE.match(Edit.newText, &Matches) && Matches[1] == IncludeHeader;<br>
+}<br>
 MATCHER_P(PlainText, Text, "") {<br>
   return arg.insertTextFormat == clangd::InsertTextFormat::PlainText &&<br>
          arg.insertText == Text;<br>
@@ -58,6 +68,8 @@ MATCHER(NameContainsFilter, "") {<br>
     return true;<br>
   return llvm::StringRef(arg.insertText).contains(arg.filterText);<br>
 }<br>
+MATCHER(HasAdditionalEdits, "") { return !arg.additionalTextEdits.empty(); }<br>
+<br>
 // Shorthand for Contains(Named(Name)).<br>
 Matcher<const std::vector<CompletionItem> &> Has(std::string Name) {<br>
   return Contains(Named(std::move(Name)));<br>
@@ -75,9 +87,7 @@ std::unique_ptr<SymbolIndex> memIndex(st<br>
   return MemIndex::build(std::move(Slab).build());<br>
 }<br>
<br>
-// Builds a server and runs code completion.<br>
-// If IndexSymbols is non-empty, an index will be built and passed to opts.<br>
-CompletionList completions(StringRef Text,<br>
+CompletionList completions(ClangdServer &Server, StringRef Text,<br>
                            std::vector<Symbol> IndexSymbols = {},<br>
                            clangd::CodeCompleteOptions Opts = {}) {<br>
   std::unique_ptr<SymbolIndex> OverrideIndex;<br>
@@ -87,10 +97,6 @@ CompletionList completions(StringRef Tex<br>
     Opts.Index = OverrideIndex.get();<br>
   }<br>
<br>
-  MockFSProvider FS;<br>
-  MockCompilationDatabase CDB;<br>
-  IgnoreDiagnostics DiagConsumer;<br>
-  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());<br>
   auto File = testPath("foo.cpp");<br>
   Annotations Test(Text);<br>
   runAddDocument(Server, File, Test.code());<br>
@@ -101,6 +107,18 @@ CompletionList completions(StringRef Tex<br>
   return CompletionList;<br>
 }<br>
<br>
+// Builds a server and runs code completion.<br>
+// If IndexSymbols is non-empty, an index will be built and passed to opts.<br>
+CompletionList completions(StringRef Text,<br>
+                           std::vector<Symbol> IndexSymbols = {},<br>
+                           clangd::CodeCompleteOptions Opts = {}) {<br>
+  MockFSProvider FS;<br>
+  MockCompilationDatabase CDB;<br>
+  IgnoreDiagnostics DiagConsumer;<br>
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());<br>
+  return completions(Server, Text, std::move(IndexSymbols), std::move(Opts));<br>
+}<br>
+<br>
 std::string replace(StringRef Haystack, StringRef Needle, StringRef Repl) {<br>
   std::string Result;<br>
   raw_string_ostream OS(Result);<br>
@@ -505,6 +523,42 @@ TEST(CompletionTest, SemaIndexMergeWithL<br>
   EXPECT_TRUE(Results.isIncomplete);<br>
 }<br>
<br>
+TEST(CompletionTest, IncludeInsertionPreprocessorIntegrationTests) {<br>
+  MockFSProvider FS;<br>
+  MockCompilationDatabase CDB;<br>
+  std::string Subdir = testPath("sub");<br>
+  std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str();<br>
+  CDB.ExtraClangFlags = {SearchDirArg.c_str()};<br>
+  std::string BarHeader = testPath("sub/bar.h");<br>
+  FS.Files[BarHeader] = "";<br>
+<br>
+  IgnoreDiagnostics DiagConsumer;<br>
+  ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());<br>
+  Symbol::Details Scratch;<br>
+  auto BarURI = URI::createFile(BarHeader).toString();<br>
+  Symbol Sym = cls("ns::X");<br>
+  Sym.CanonicalDeclaration.FileURI = BarURI;<br>
+  Scratch.IncludeHeader = BarURI;<br>
+  Sym.Detail = &Scratch;<br>
+  // Shoten include path based on search dirctory and insert.<br>
+  auto Results = completions(Server,<br>
+                             R"cpp(<br>
+          int main() { ns::^ }<br>
+      )cpp",<br>
+                             {Sym});<br>
+  EXPECT_THAT(Results.items,<br>
+              ElementsAre(AllOf(Named("X"), InsertInclude("\"bar.h\""))));<br>
+  // Duplicate based on inclusions in preamble.<br>
+  Results = completions(Server,<br>
+                        R"cpp(<br>
+          #include "sub/bar.h"  // not shortest, so should only match resolved.<br>
+          int main() { ns::^ }<br>
+      )cpp",<br>
+                        {Sym});<br>
+  EXPECT_THAT(Results.items,<br>
+              ElementsAre(AllOf(Named("X"), Not(HasAdditionalEdits()))));<br>
+}<br>
+<br>
 TEST(CompletionTest, IndexSuppressesPreambleCompletions) {<br>
   MockFSProvider FS;<br>
   MockCompilationDatabase CDB;<br>
<br>
Modified: clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp?rev=332363&r1=332362&r2=332363&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp?rev=332363&r1=332362&r2=332363&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp (original)<br>
+++ clang-tools-extra/trunk/unittests/clangd/HeadersTests.cpp Tue May 15 08:29:32 2018<br>
@@ -8,7 +8,13 @@<br>
 //===----------------------------------------------------------------------===//<br>
<br>
 #include "Headers.h"<br>
+<br>
+#include "Compiler.h"<br>
 #include "TestFS.h"<br>
+#include "clang/Frontend/CompilerInstance.h"<br>
+#include "clang/Frontend/CompilerInvocation.h"<br>
+#include "clang/Frontend/FrontendActions.h"<br>
+#include "clang/Lex/PreprocessorOptions.h"<br>
 #include "gmock/gmock.h"<br>
 #include "gtest/gtest.h"<br>
<br>
@@ -16,30 +22,83 @@ namespace clang {<br>
 namespace clangd {<br>
 namespace {<br>
<br>
+using ::testing::AllOf;<br>
+using ::testing::UnorderedElementsAre;<br>
+<br>
 class HeadersTest : public ::testing::Test {<br>
 public:<br>
   HeadersTest() {<br>
     CDB.ExtraClangFlags = {SearchDirArg.c_str()};<br>
     FS.Files[MainFile] = "";<br>
+    // Make sure directory sub/ exists.<br>
+    FS.Files[testPath("sub/EMPTY")] = "";<br>
+  }<br>
+<br>
+private:<br>
+  std::unique_ptr<CompilerInstance> setupClang() {<br>
+    auto Cmd = CDB.getCompileCommand(MainFile);<br>
+    assert(static_cast<bool>(Cmd));<br>
+    auto VFS = FS.getFileSystem();<br>
+    VFS->setCurrentWorkingDirectory(Cmd->Directory);<br>
+<br>
+    std::vector<const char *> Argv;<br>
+    for (const auto &S : Cmd->CommandLine)<br>
+      Argv.push_back(S.c_str());<br>
+    auto CI = clang::createInvocationFromCommandLine(<br>
+        Argv,<br>
+        CompilerInstance::createDiagnostics(new DiagnosticOptions(),<br>
+                                            &IgnoreDiags, false),<br>
+        VFS);<br>
+    EXPECT_TRUE(static_cast<bool>(CI));<br>
+    CI->getFrontendOpts().DisableFree = false;<br>
+<br>
+    // The diagnostic options must be set before creating a CompilerInstance.<br>
+    CI->getDiagnosticOpts().IgnoreWarnings = true;<br>
+    auto Clang = prepareCompilerInstance(<br>
+        std::move(CI), /*Preamble=*/nullptr,<br>
+        llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),<br>
+        std::make_shared<PCHContainerOperations>(), VFS, IgnoreDiags);<br>
+<br>
+    EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());<br>
+    return Clang;<br>
   }<br>
<br>
 protected:<br>
+  std::vector<Inclusion> collectIncludes() {<br>
+    auto Clang = setupClang();<br>
+    PreprocessOnlyAction Action;<br>
+    EXPECT_TRUE(<br>
+        Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));<br>
+    std::vector<Inclusion> Inclusions;<br>
+    Clang->getPreprocessor().addPPCallbacks(collectInclusionsInMainFileCallback(<br>
+        Clang->getSourceManager(),<br>
+        [&](Inclusion Inc) { Inclusions.push_back(std::move(Inc)); }));<br>
+    EXPECT_TRUE(Action.Execute());<br>
+    Action.EndSourceFile();<br>
+    return Inclusions;<br>
+  }<br>
+<br>
   // Calculates the include path, or returns "" on error.<br>
   std::string calculate(PathRef Original, PathRef Preferred = "",<br>
+                        const std::vector<Inclusion> &Inclusions = {},<br>
                         bool ExpectError = false) {<br>
+    auto Clang = setupClang();<br>
+    PreprocessOnlyAction Action;<br>
+    EXPECT_TRUE(<br>
+        Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));<br>
+<br>
     if (Preferred.empty())<br>
       Preferred = Original;<br>
-    auto VFS = FS.getFileSystem();<br>
-    auto Cmd = CDB.getCompileCommand(MainFile);<br>
-    assert(static_cast<bool>(Cmd));<br>
-    VFS->setCurrentWorkingDirectory(Cmd->Directory);<br>
     auto ToHeaderFile = [](llvm::StringRef Header) {<br>
       return HeaderFile{Header,<br>
                         /*Verbatim=*/!llvm::sys::path::is_absolute(Header)};<br>
     };<br>
-    auto Path = calculateIncludePath(MainFile, FS.Files[MainFile],<br>
-                                     ToHeaderFile(Original),<br>
-                                     ToHeaderFile(Preferred), *Cmd, VFS);<br>
+<br>
+    auto Path = calculateIncludePath(<br>
+        MainFile, CDB.getCompileCommand(MainFile)->Directory,<br>
+        Clang->getPreprocessor().getHeaderSearchInfo(), Inclusions,<br>
+        ToHeaderFile(Original), ToHeaderFile(Preferred));<br>
+    Action.EndSourceFile();<br>
     if (!Path) {<br>
       llvm::consumeError(Path.takeError());<br>
       EXPECT_TRUE(ExpectError);<br>
@@ -49,52 +108,50 @@ protected:<br>
     }<br>
     return std::move(*Path);<br>
   }<br>
+<br>
+  Expected<Optional<TextEdit>><br>
+  insert(const HeaderFile &DeclaringHeader, const HeaderFile &InsertedHeader,<br>
+         const std::vector<Inclusion> &ExistingInclusions = {}) {<br>
+    auto Clang = setupClang();<br>
+    PreprocessOnlyAction Action;<br>
+    EXPECT_TRUE(<br>
+        Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]));<br>
+<br>
+    IncludeInserter Inserter(MainFile, /*Code=*/"", format::getLLVMStyle(),<br>
+                             CDB.getCompileCommand(MainFile)->Directory,<br>
+                             Clang->getPreprocessor().getHeaderSearchInfo());<br>
+    for (const auto &Inc : ExistingInclusions)<br>
+      Inserter.addExisting(Inc);<br>
+<br>
+    auto Edit = Inserter.insert(DeclaringHeader, InsertedHeader);<br>
+    Action.EndSourceFile();<br>
+    return Edit;<br>
+  }<br>
+<br>
   MockFSProvider FS;<br>
   MockCompilationDatabase CDB;<br>
   std::string MainFile = testPath("main.cpp");<br>
   std::string Subdir = testPath("sub");<br>
   std::string SearchDirArg = (llvm::Twine("-I") + Subdir).str();<br>
+  IgnoringDiagConsumer IgnoreDiags;<br>
 };<br>
<br>
-TEST_F(HeadersTest, InsertInclude) {<br>
-  std::string Path = testPath("sub/bar.h");<br>
-  FS.Files[Path] = "";<br>
-  EXPECT_EQ(calculate(Path), "\"bar.h\"");<br>
-}<br>
+MATCHER_P(Written, Name, "") { return arg.Written == Name; }<br>
+MATCHER_P(Resolved, Name, "") { return arg.Resolved == Name; }<br>
<br>
-TEST_F(HeadersTest, DontInsertDuplicateSameName) {<br>
+TEST_F(HeadersTest, CollectRewrittenAndResolved) {<br>
   FS.Files[MainFile] = R"cpp(<br>
-#include "bar.h"<br>
+#include "sub/bar.h" // not shortest<br>
 )cpp";<br>
   std::string BarHeader = testPath("sub/bar.h");<br>
   FS.Files[BarHeader] = "";<br>
-  EXPECT_EQ(calculate(BarHeader), "");<br>
-}<br>
-<br>
-TEST_F(HeadersTest, DontInsertDuplicateDifferentName) {<br>
-  std::string BarHeader = testPath("sub/bar.h");<br>
-  FS.Files[BarHeader] = "";<br>
-  FS.Files[MainFile] = R"cpp(<br>
-#include "sub/bar.h"  // not shortest<br>
-)cpp";<br>
-  EXPECT_EQ(calculate("\"sub/bar.h\""), ""); // Duplicate rewritten.<br>
-  EXPECT_EQ(calculate(BarHeader), "");       // Duplicate resolved.<br>
-  EXPECT_EQ(calculate(BarHeader, "\"BAR.h\""), ""); // Do not insert preferred.<br>
-}<br>
<br>
-TEST_F(HeadersTest, DontInsertDuplicatePreferred) {<br>
-  std::string BarHeader = testPath("sub/bar.h");<br>
-  FS.Files[BarHeader] = "";<br>
-  FS.Files[MainFile] = R"cpp(<br>
-#include "sub/bar.h"  // not shortest<br>
-)cpp";<br>
-  // Duplicate written.<br>
-  EXPECT_EQ(calculate("\"original.h\"", "\"sub/bar.h\""), "");<br>
-  // Duplicate resolved.<br>
-  EXPECT_EQ(calculate("\"original.h\"", BarHeader), "");<br>
+  EXPECT_THAT(collectIncludes(),<br>
+              UnorderedElementsAre(<br>
+                  AllOf(Written("\"sub/bar.h\""), Resolved(BarHeader))));<br>
 }<br>
<br>
-TEST_F(HeadersTest, StillInsertIfTrasitivelyIncluded) {<br>
+TEST_F(HeadersTest, OnlyCollectInclusionsInMain) {<br>
   std::string BazHeader = testPath("sub/baz.h");<br>
   FS.Files[BazHeader] = "";<br>
   std::string BarHeader = testPath("sub/bar.h");<br>
@@ -104,27 +161,92 @@ TEST_F(HeadersTest, StillInsertIfTrasiti<br>
   FS.Files[MainFile] = R"cpp(<br>
 #include "bar.h"<br>
 )cpp";<br>
-  EXPECT_EQ(calculate(BazHeader), "\"baz.h\"");<br>
+  EXPECT_THAT(<br>
+      collectIncludes(),<br>
+      UnorderedElementsAre(AllOf(Written("\"bar.h\""), Resolved(BarHeader))));<br>
+}<br>
+<br>
+TEST_F(HeadersTest, UnResolvedInclusion) {<br>
+  FS.Files[MainFile] = R"cpp(<br>
+#include "foo.h"<br>
+)cpp";<br>
+<br>
+  EXPECT_THAT(collectIncludes(),<br>
+              UnorderedElementsAre(AllOf(Written("\"foo.h\""), Resolved(""))));<br>
+}<br>
+<br>
+TEST_F(HeadersTest, InsertInclude) {<br>
+  std::string Path = testPath("sub/bar.h");<br>
+  FS.Files[Path] = "";<br>
+  EXPECT_EQ(calculate(Path), "\"bar.h\"");<br>
 }<br>
<br>
 TEST_F(HeadersTest, DoNotInsertIfInSameFile) {<br>
   MainFile = testPath("main.h");<br>
-  FS.Files[MainFile] = "";<br>
   EXPECT_EQ(calculate(MainFile), "");<br>
 }<br>
<br>
+TEST_F(HeadersTest, ShortenedInclude) {<br>
+  std::string BarHeader = testPath("sub/bar.h");<br>
+  EXPECT_EQ(calculate(BarHeader), "\"bar.h\"");<br>
+}<br>
+<br>
+TEST_F(HeadersTest, NotShortenedInclude) {<br>
+  std::string BarHeader = testPath("sub-2/bar.h");<br>
+  EXPECT_EQ(calculate(BarHeader, ""), "\"" + BarHeader + "\"");<br>
+}<br>
+<br>
 TEST_F(HeadersTest, PreferredHeader) {<br>
-  FS.Files[MainFile] = "";<br>
   std::string BarHeader = testPath("sub/bar.h");<br>
-  FS.Files[BarHeader] = "";<br>
   EXPECT_EQ(calculate(BarHeader, "<bar>"), "<bar>");<br>
<br>
   std::string BazHeader = testPath("sub/baz.h");<br>
-  FS.Files[BazHeader] = "";<br>
   EXPECT_EQ(calculate(BarHeader, BazHeader), "\"baz.h\"");<br>
 }<br>
<br>
+TEST_F(HeadersTest, DontInsertDuplicatePreferred) {<br>
+  std::vector<Inclusion> Inclusions = {<br>
+      {Range(), /*Written*/ "\"bar.h\"", /*Resolved*/ ""}};<br>
+  EXPECT_EQ(calculate(testPath("sub/bar.h"), "\"bar.h\"", Inclusions), "");<br>
+  EXPECT_EQ(calculate("\"x.h\"", "\"bar.h\"", Inclusions), "");<br>
+}<br>
+<br>
+TEST_F(HeadersTest, DontInsertDuplicateResolved) {<br>
+  std::string BarHeader = testPath("sub/bar.h");<br>
+  std::vector<Inclusion> Inclusions = {<br>
+      {Range(), /*Written*/ "fake-bar.h", /*Resolved*/ BarHeader}};<br>
+  EXPECT_EQ(calculate(BarHeader, "", Inclusions), "");<br>
+  // Do not insert preferred.<br>
+  EXPECT_EQ(calculate(BarHeader, "\"BAR.h\"", Inclusions), "");<br>
+}<br>
+<br>
+HeaderFile literal(StringRef Include) {<br>
+  HeaderFile H{Include, /*Verbatim=*/true};<br>
+  assert(H.valid());<br>
+  return H;<br>
+}<br>
+<br>
+TEST_F(HeadersTest, PreferInserted) {<br>
+  auto Edit = insert(literal("<x>"), literal("<y>"));<br>
+  EXPECT_TRUE(static_cast<bool>(Edit));<br>
+  EXPECT_TRUE(llvm::StringRef((*Edit)->newText).contains("<y>"));<br>
+}<br>
+<br>
+TEST_F(HeadersTest, ExistingInclusion) {<br>
+  Inclusion Existing{Range(), /*Written=*/"<c.h>", /*Resolved=*/""};<br>
+  auto Edit = insert(literal("<c.h>"), literal("<c.h>"), {Existing});<br>
+  EXPECT_TRUE(static_cast<bool>(Edit));<br>
+  EXPECT_FALSE(static_cast<bool>(*Edit));<br>
+}<br>
+<br>
+TEST_F(HeadersTest, ValidateHeaders) {<br>
+  HeaderFile InvalidHeader{"a.h", /*Verbatim=*/true};<br>
+  assert(!InvalidHeader.valid());<br>
+  auto Edit = insert(InvalidHeader, literal("\"c.h\""));<br>
+  EXPECT_FALSE(static_cast<bool>(Edit));<br>
+  llvm::consumeError(Edit.takeError());<br>
+}<br>
+<br>
 } // namespace<br>
 } // namespace clangd<br>
 } // namespace clang<br>
-<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div>
</blockquote></div></blockquote></div>