ilya-biryukov updated this revision to Diff 100262.
ilya-biryukov added a comment.

- Fixed file comment of ClangdTests.cpp


https://reviews.llvm.org/D33416

Files:
  clangd/ClangdLSPServer.cpp
  clangd/ClangdServer.cpp
  clangd/ClangdServer.h
  clangd/ClangdUnit.cpp
  clangd/ClangdUnit.h
  clangd/ClangdUnitStore.h
  unittests/CMakeLists.txt
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ClangdTests.cpp

Index: unittests/clangd/ClangdTests.cpp
===================================================================
--- /dev/null
+++ unittests/clangd/ClangdTests.cpp
@@ -0,0 +1,364 @@
+//===-- ClangdTests.cpp - Clangd unit tests ---------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdServer.h"
+#include "clang/Basic/VirtualFileSystem.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Config/config.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "gtest/gtest.h"
+#include <algorithm>
+#include <iostream>
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace vfs {
+
+/// An implementation of vfs::FileSystem that only allows access to
+/// files and folders inside a set of whitelisted directories.
+///
+/// FIXME(ibiryukov): should it also emulate access to parents of whitelisted
+/// directories with only whitelisted contents?
+class FilteredFileSystem : public vfs::FileSystem {
+public:
+  /// The paths inside \p WhitelistedDirs should be absolute
+  FilteredFileSystem(std::vector<std::string> WhitelistedDirs,
+                     IntrusiveRefCntPtr<vfs::FileSystem> InnerFS)
+      : WhitelistedDirs(std::move(WhitelistedDirs)), InnerFS(InnerFS) {
+    assert(std::all_of(WhitelistedDirs.begin(), WhitelistedDirs.end(),
+                       [](const std::string &Path) -> bool {
+                         return llvm::sys::path::is_absolute(Path);
+                       }) &&
+           "Not all WhitelistedDirs are absolute");
+  }
+
+  virtual llvm::ErrorOr<Status> status(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->status(Path);
+  }
+
+  virtual llvm::ErrorOr<std::unique_ptr<File>>
+  openFileForRead(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->openFileForRead(Path);
+  }
+
+  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
+  getBufferForFile(const Twine &Name, int64_t FileSize = -1,
+                   bool RequiresNullTerminator = true,
+                   bool IsVolatile = false) {
+    if (!isInsideWhitelistedDir(Name))
+      return llvm::errc::no_such_file_or_directory;
+    return InnerFS->getBufferForFile(Name, FileSize, RequiresNullTerminator,
+                                     IsVolatile);
+  }
+
+  virtual directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) {
+    if (!isInsideWhitelistedDir(Dir)) {
+      EC = llvm::errc::no_such_file_or_directory;
+      return directory_iterator();
+    }
+    return InnerFS->dir_begin(Dir, EC);
+  }
+
+  virtual std::error_code setCurrentWorkingDirectory(const Twine &Path) {
+    return InnerFS->setCurrentWorkingDirectory(Path);
+  }
+
+  virtual llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const {
+    return InnerFS->getCurrentWorkingDirectory();
+  }
+
+  bool exists(const Twine &Path) {
+    if (!isInsideWhitelistedDir(Path))
+      return false;
+    return InnerFS->exists(Path);
+  }
+
+  std::error_code makeAbsolute(SmallVectorImpl<char> &Path) const {
+    return InnerFS->makeAbsolute(Path);
+  }
+
+private:
+  bool isInsideWhitelistedDir(const Twine &InputPath) const {
+    SmallString<128> Path;
+    InputPath.toVector(Path);
+
+    if (makeAbsolute(Path))
+      return false;
+
+    for (const auto &Dir : WhitelistedDirs) {
+      if (Path.startswith(Dir))
+        return true;
+    }
+    return false;
+  }
+
+  std::vector<std::string> WhitelistedDirs;
+  IntrusiveRefCntPtr<vfs::FileSystem> InnerFS;
+};
+
+/// Create a vfs::FileSystem that has access only to temporary directories
+/// (obtained by calling system_temp_directory).
+IntrusiveRefCntPtr<vfs::FileSystem> getTempOnlyFS() {
+  llvm::SmallString<128> TmpDir1;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, TmpDir1);
+  llvm::SmallString<128> TmpDir2;
+  llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true, TmpDir2);
+
+  std::vector<std::string> TmpDirs;
+  TmpDirs.push_back(TmpDir1.str());
+  if (TmpDir2 != TmpDir2)
+    TmpDirs.push_back(TmpDir2.str());
+  return new vfs::FilteredFileSystem(std::move(TmpDirs),
+                                     vfs::getRealFileSystem());
+}
+} // namespace vfs
+
+namespace clangd {
+namespace {
+
+class ErrorCheckingDiagConsumer : public DiagnosticsConsumer {
+public:
+  void onDiagnosticsReady(PathRef File,
+                          std::vector<DiagWithFixIts> Diagnostics) override {
+    bool HadError = false;
+    for (const auto &DiagAndFixIts : Diagnostics) {
+      // FIXME: severities returned by clangd should have a descriptive
+      // diagnostic severity enum
+      const int ErrorSeverity = 1;
+      HadError = DiagAndFixIts.Diag.severity == ErrorSeverity;
+    }
+
+    std::lock_guard<std::mutex> Lock(Mutex);
+    HadErrorInLastDiags = HadError;
+  }
+
+  bool hadErrorInLastDiags() {
+    std::lock_guard<std::mutex> Lock(Mutex);
+    return HadErrorInLastDiags;
+  }
+
+private:
+  std::mutex Mutex;
+  bool HadErrorInLastDiags = false;
+};
+
+class MockCompilationDatabase : public GlobalCompilationDatabase {
+public:
+  std::vector<tooling::CompileCommand>
+  getCompileCommands(PathRef File) override {
+    return {};
+  }
+};
+
+class MockFSProvider : public FileSystemProvider {
+public:
+  IntrusiveRefCntPtr<vfs::FileSystem> getFileSystem() override {
+    IntrusiveRefCntPtr<vfs::InMemoryFileSystem> MemFS(
+        new vfs::InMemoryFileSystem);
+    for (auto &FileAndContents : Files)
+      MemFS->addFile(FileAndContents.first(), time_t(),
+                     llvm::MemoryBuffer::getMemBuffer(FileAndContents.second,
+                                                      FileAndContents.first()));
+
+    auto OverlayFS = IntrusiveRefCntPtr<vfs::OverlayFileSystem>(
+        new vfs::OverlayFileSystem(vfs::getTempOnlyFS()));
+    OverlayFS->pushOverlay(std::move(MemFS));
+    return OverlayFS;
+  }
+
+  llvm::StringMap<std::string> Files;
+};
+
+/// Replaces all patterns of the form 0x123abc with spaces
+void replacePtrsInDump(std::string &Dump) {
+  llvm::Regex RE("0x[0-9a-fA-F]+");
+  llvm::SmallVector<StringRef, 1> Matches;
+  while (RE.match(Dump, &Matches)) {
+    assert(Matches.size() == 1 && "Exactly one match expected");
+    auto MatchPos = Matches[0].data() - Dump.data();
+    std::fill(Dump.begin() + MatchPos,
+              Dump.begin() + MatchPos + Matches[0].size(), ' ');
+  }
+}
+
+std::string dumpASTWithoutMemoryLocs(ClangdServer &Server, PathRef File) {
+  auto Dump = Server.dumpAST(File);
+  replacePtrsInDump(Dump);
+  return Dump;
+}
+
+template <class T>
+std::unique_ptr<T> getAndMove(std::unique_ptr<T> Ptr, T *&Output) {
+  Output = Ptr.get();
+  return Ptr;
+}
+} // namespace
+
+class ClangdVFSTest : public ::testing::Test {
+protected:
+  SmallString<16> getVirtualTestRoot() {
+#ifdef LLVM_ON_WIN32
+    return SmallString<16>("C:\\clangd-test");
+#else
+    return SmallString<16>("/clangd-test");
+#endif
+  }
+
+  llvm::SmallString<32> getVirtualTestFilePath(PathRef File) {
+    assert(llvm::sys::path::is_relative(File) && "FileName should be relative");
+
+    llvm::SmallString<32> Path;
+    llvm::sys::path::append(Path, getVirtualTestRoot(), File);
+    return Path;
+  }
+
+  std::string parseSourceAndDumpAST(
+      PathRef SourceFileRelPath, StringRef SourceContents,
+      std::vector<std::pair<PathRef, StringRef>> ExtraFiles = {},
+      bool ExpectErrors = false) {
+    MockFSProvider *FS;
+    ErrorCheckingDiagConsumer *DiagConsumer;
+    ClangdServer Server(
+        llvm::make_unique<MockCompilationDatabase>(),
+        getAndMove(llvm::make_unique<ErrorCheckingDiagConsumer>(),
+                   DiagConsumer),
+        getAndMove(llvm::make_unique<MockFSProvider>(), FS),
+        /*RunSynchronously=*/false);
+    for (const auto &FileWithContents : ExtraFiles)
+      FS->Files[getVirtualTestFilePath(FileWithContents.first)] =
+          FileWithContents.second;
+
+    auto SourceFilename = getVirtualTestFilePath(SourceFileRelPath);
+    Server.addDocument(SourceFilename, SourceContents);
+    auto Result = dumpASTWithoutMemoryLocs(Server, SourceFilename);
+    EXPECT_EQ(ExpectErrors, DiagConsumer->hadErrorInLastDiags());
+    return Result;
+  }
+};
+
+TEST_F(ClangdVFSTest, Parse) {
+  // FIXME: figure out a stable format for AST dumps, so that we can check the
+  // output of the dump itself is equal to the expected one, not just that it's
+  // different.
+  auto Empty = parseSourceAndDumpAST("foo.cpp", "", {});
+  auto OneDecl = parseSourceAndDumpAST("foo.cpp", "int a;", {});
+  auto SomeDecls = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;", {});
+  EXPECT_NE(Empty, OneDecl);
+  EXPECT_NE(Empty, SomeDecls);
+  EXPECT_NE(SomeDecls, OneDecl);
+
+  auto Empty2 = parseSourceAndDumpAST("foo.cpp", "");
+  auto OneDecl2 = parseSourceAndDumpAST("foo.cpp", "int a;");
+  auto SomeDecls2 = parseSourceAndDumpAST("foo.cpp", "int a; int b; int c;");
+  EXPECT_EQ(Empty, Empty2);
+  EXPECT_EQ(OneDecl, OneDecl2);
+  EXPECT_EQ(SomeDecls, SomeDecls2);
+}
+
+TEST_F(ClangdVFSTest, ParseWithHeader) {
+  parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {},
+                        /*ExpectErrors=*/true);
+  parseSourceAndDumpAST("foo.cpp", "#include \"foo.h\"", {{"foo.h", ""}},
+                        /*ExpectErrors=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+  parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", ""}},
+                        /*ExpectErrors=*/true);
+  parseSourceAndDumpAST("foo.cpp", SourceContents, {{"foo.h", "int a;"}},
+                        /*ExpectErrors=*/false);
+}
+
+TEST_F(ClangdVFSTest, Reparse) {
+  MockFSProvider *FS;
+  ErrorCheckingDiagConsumer *DiagConsumer;
+  ClangdServer Server(
+      llvm::make_unique<MockCompilationDatabase>(),
+      getAndMove(llvm::make_unique<ErrorCheckingDiagConsumer>(), DiagConsumer),
+      getAndMove(llvm::make_unique<MockFSProvider>(), FS),
+      /*RunSynchronously=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  auto FooH = getVirtualTestFilePath("foo.h");
+
+  FS->Files[FooH] = "int a;";
+  FS->Files[FooCpp] = SourceContents;
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer->hadErrorInLastDiags());
+
+  Server.addDocument(FooCpp, "");
+  auto DumpParseEmpty = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer->hadErrorInLastDiags());
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer->hadErrorInLastDiags());
+
+  EXPECT_EQ(DumpParse1, DumpParse2);
+  EXPECT_NE(DumpParse1, DumpParseEmpty);
+}
+
+TEST_F(ClangdVFSTest, ReparseOnHeaderChange) {
+  MockFSProvider *FS;
+  ErrorCheckingDiagConsumer *DiagConsumer;
+
+  ClangdServer Server(
+      llvm::make_unique<MockCompilationDatabase>(),
+      getAndMove(llvm::make_unique<ErrorCheckingDiagConsumer>(), DiagConsumer),
+      getAndMove(llvm::make_unique<MockFSProvider>(), FS),
+      /*RunSynchronously=*/false);
+
+  const auto SourceContents = R"cpp(
+#include "foo.h"
+int b = a;
+)cpp";
+
+  auto FooCpp = getVirtualTestFilePath("foo.cpp");
+  auto FooH = getVirtualTestFilePath("foo.h");
+
+  FS->Files[FooH] = "int a;";
+  FS->Files[FooCpp] = SourceContents;
+
+  Server.addDocument(FooCpp, SourceContents);
+  auto DumpParse1 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer->hadErrorInLastDiags());
+
+  FS->Files[FooH] = "";
+  Server.forceReparse(FooCpp);
+  auto DumpParseDifferent = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_TRUE(DiagConsumer->hadErrorInLastDiags());
+
+  FS->Files[FooH] = "int a;";
+  Server.forceReparse(FooCpp);
+  auto DumpParse2 = dumpASTWithoutMemoryLocs(Server, FooCpp);
+  EXPECT_FALSE(DiagConsumer->hadErrorInLastDiags());
+
+  EXPECT_EQ(DumpParse1, DumpParse2);
+  EXPECT_NE(DumpParse1, DumpParseDifferent);
+}
+
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/clangd/CMakeLists.txt
@@ -0,0 +1,24 @@
+set(LLVM_LINK_COMPONENTS
+  support
+  )
+
+get_filename_component(CLANGD_SOURCE_DIR
+  ${CMAKE_CURRENT_SOURCE_DIR}/../../clangd REALPATH)
+include_directories(
+  ${CLANGD_SOURCE_DIR}
+  )
+
+add_extra_unittest(ClangdTests
+  ClangdTests.cpp
+  )
+
+target_link_libraries(ClangdTests
+  clangBasic
+  clangDaemon
+  clangFormat
+  clangFrontend
+  clangSema
+  clangTooling
+  clangToolingCore
+  LLVMSupport
+  )
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -11,4 +11,5 @@
 add_subdirectory(clang-query)
 add_subdirectory(clang-tidy)
 add_subdirectory(clang-rename)
+add_subdirectory(clangd)
 add_subdirectory(include-fixer)
Index: clangd/ClangdUnitStore.h
===================================================================
--- clangd/ClangdUnitStore.h
+++ clangd/ClangdUnitStore.h
@@ -32,9 +32,10 @@
   template <class Func>
   void runOnUnit(PathRef File, StringRef FileContents,
                  GlobalCompilationDatabase &CDB,
-                 std::shared_ptr<PCHContainerOperations> PCHs, Func Action) {
+                 std::shared_ptr<PCHContainerOperations> PCHs,
+                 IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
     runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/true,
-                  std::forward<Func>(Action));
+                  VFS, std::forward<Func>(Action));
   }
 
   /// Run specified \p Action on the ClangdUnit for \p File.
@@ -45,9 +46,10 @@
   void runOnUnitWithoutReparse(PathRef File, StringRef FileContents,
                                GlobalCompilationDatabase &CDB,
                                std::shared_ptr<PCHContainerOperations> PCHs,
+                               IntrusiveRefCntPtr<vfs::FileSystem> VFS,
                                Func Action) {
     runOnUnitImpl(File, FileContents, CDB, PCHs, /*ReparseBeforeAction=*/false,
-                  std::forward<Func>(Action));
+                  VFS, std::forward<Func>(Action));
   }
 
   /// Run the specified \p Action on the ClangdUnit for \p File.
@@ -71,24 +73,23 @@
   void runOnUnitImpl(PathRef File, StringRef FileContents,
                      GlobalCompilationDatabase &CDB,
                      std::shared_ptr<PCHContainerOperations> PCHs,
-                     bool ReparseBeforeAction, Func Action) {
+                     bool ReparseBeforeAction,
+                     IntrusiveRefCntPtr<vfs::FileSystem> VFS, Func Action) {
     std::lock_guard<std::mutex> Lock(Mutex);
 
     auto Commands = getCompileCommands(CDB, File);
     assert(!Commands.empty() &&
            "getCompileCommands should add default command");
-    // chdir. This is thread hostile.
-    // FIXME(ibiryukov): get rid of this
-    llvm::sys::fs::set_current_path(Commands.front().Directory);
+    VFS->setCurrentWorkingDirectory(Commands.front().Directory);
 
     auto It = OpenedFiles.find(File);
     if (It == OpenedFiles.end()) {
       It = OpenedFiles
                .insert(std::make_pair(
-                   File, ClangdUnit(File, FileContents, PCHs, Commands)))
+                   File, ClangdUnit(File, FileContents, PCHs, Commands, VFS)))
                .first;
     } else if (ReparseBeforeAction) {
-      It->second.reparse(FileContents);
+      It->second.reparse(FileContents, VFS);
     }
     return Action(It->second);
   }
Index: clangd/ClangdUnit.h
===================================================================
--- clangd/ClangdUnit.h
+++ clangd/ClangdUnit.h
@@ -10,8 +10,8 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
 
-#include "Protocol.h"
 #include "Path.h"
+#include "Protocol.h"
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Tooling/Core/Replacement.h"
 #include <memory>
@@ -24,6 +24,10 @@
 class ASTUnit;
 class PCHContainerOperations;
 
+namespace vfs {
+class FileSystem;
+}
+
 namespace tooling {
 struct CompileCommand;
 }
@@ -42,16 +46,19 @@
 public:
   ClangdUnit(PathRef FileName, StringRef Contents,
              std::shared_ptr<PCHContainerOperations> PCHs,
-             std::vector<tooling::CompileCommand> Commands);
+             std::vector<tooling::CompileCommand> Commands,
+             IntrusiveRefCntPtr<vfs::FileSystem> VFS);
 
   /// Reparse with new contents.
-  void reparse(StringRef Contents);
+  void reparse(StringRef Contents, IntrusiveRefCntPtr<vfs::FileSystem> VFS);
 
   /// Get code completions at a specified \p Line and \p Column in \p File.
   ///
   /// This function is thread-safe and returns completion items that own the
   /// data they contain.
-  std::vector<CompletionItem> codeComplete(StringRef Contents, Position Pos);
+  std::vector<CompletionItem>
+  codeComplete(StringRef Contents, Position Pos,
+               IntrusiveRefCntPtr<vfs::FileSystem> VFS);
   /// Returns diagnostics and corresponding FixIts for each diagnostic that are
   /// located in the current file.
   std::vector<DiagWithFixIts> getLocalDiagnostics() const;
Index: clangd/ClangdUnit.cpp
===================================================================
--- clangd/ClangdUnit.cpp
+++ clangd/ClangdUnit.cpp
@@ -11,14 +11,16 @@
 #include "clang/Frontend/ASTUnit.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Tooling/CompilationDatabase.h"
 
 using namespace clang::clangd;
 using namespace clang;
 
 ClangdUnit::ClangdUnit(PathRef FileName, StringRef Contents,
                        std::shared_ptr<PCHContainerOperations> PCHs,
-                       std::vector<tooling::CompileCommand> Commands)
+                       std::vector<tooling::CompileCommand> Commands,
+                       IntrusiveRefCntPtr<vfs::FileSystem> VFS)
     : FileName(FileName), PCHs(PCHs) {
   assert(!Commands.empty() && "No compile commands provided");
 
@@ -48,18 +50,24 @@
       /*PrecompilePreambleAfterNParses=*/1, /*TUKind=*/TU_Prefix,
       /*CacheCodeCompletionResults=*/true,
       /*IncludeBriefCommentsInCodeCompletion=*/true,
-      /*AllowPCHWithCompilerErrors=*/true));
+      /*AllowPCHWithCompilerErrors=*/true,
+      /*SkipFunctionBodies=*/false,
+      /*UserFilesAreVolatile=*/false, /*ForSerialization=*/false,
+      /*ModuleFormat=*/llvm::None,
+      /*ErrAST=*/nullptr, VFS));
+  assert(Unit && "Unit wasn't created");
 }
 
-void ClangdUnit::reparse(StringRef Contents) {
+void ClangdUnit::reparse(StringRef Contents,
+                         IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
   // Do a reparse if this wasn't the first parse.
   // FIXME: This might have the wrong working directory if it changed in the
   // meantime.
   ASTUnit::RemappedFile RemappedSource(
       FileName,
       llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
 
-  Unit->Reparse(PCHs, RemappedSource);
+  Unit->Reparse(PCHs, RemappedSource, VFS);
 }
 
 namespace {
@@ -146,8 +154,9 @@
 };
 } // namespace
 
-std::vector<CompletionItem> ClangdUnit::codeComplete(StringRef Contents,
-                                                     Position Pos) {
+std::vector<CompletionItem>
+ClangdUnit::codeComplete(StringRef Contents, Position Pos,
+                         IntrusiveRefCntPtr<vfs::FileSystem> VFS) {
   CodeCompleteOptions CCO;
   CCO.IncludeBriefComments = 1;
   // This is where code completion stores dirty buffers. Need to free after
@@ -163,17 +172,19 @@
       FileName,
       llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
 
+  IntrusiveRefCntPtr<FileManager> FileMgr(
+      new FileManager(Unit->getFileSystemOpts(), VFS));
   IntrusiveRefCntPtr<SourceManager> SourceMgr(
-      new SourceManager(*DiagEngine, Unit->getFileManager()));
+      new SourceManager(*DiagEngine, *FileMgr));
   // CodeComplete seems to require fresh LangOptions.
   LangOptions LangOpts = Unit->getLangOpts();
   // The language server protocol uses zero-based line and column numbers.
   // The clang code completion uses one-based numbers.
   Unit->CodeComplete(FileName, Pos.line + 1, Pos.character + 1, RemappedSource,
                      CCO.IncludeMacros, CCO.IncludeCodePatterns,
                      CCO.IncludeBriefComments, Collector, PCHs, *DiagEngine,
-                     LangOpts, *SourceMgr, Unit->getFileManager(),
-                     StoredDiagnostics, OwnedBuffers);
+                     LangOpts, *SourceMgr, *FileMgr, StoredDiagnostics,
+                     OwnedBuffers);
   for (const llvm::MemoryBuffer *Buffer : OwnedBuffers)
     delete Buffer;
   return Items;
Index: clangd/ClangdServer.h
===================================================================
--- clangd/ClangdServer.h
+++ clangd/ClangdServer.h
@@ -50,6 +50,17 @@
                                   std::vector<DiagWithFixIts> Diagnostics) = 0;
 };
 
+class FileSystemProvider {
+public:
+  virtual ~FileSystemProvider() = default;
+  virtual IntrusiveRefCntPtr<vfs::FileSystem> getFileSystem() = 0;
+};
+
+class RealFileSystemProvider : public FileSystemProvider {
+public:
+  IntrusiveRefCntPtr<vfs::FileSystem> getFileSystem() override;
+};
+
 class ClangdServer;
 
 /// Handles running WorkerRequests of ClangdServer on a separate threads.
@@ -94,6 +105,7 @@
 public:
   ClangdServer(std::unique_ptr<GlobalCompilationDatabase> CDB,
                std::unique_ptr<DiagnosticsConsumer> DiagConsumer,
+               std::unique_ptr<FileSystemProvider> FSProvider,
                bool RunSynchronously);
 
   /// Add a \p File to the list of tracked C++ files or update the contents if
@@ -104,6 +116,8 @@
   /// Remove \p File from list of tracked files, schedule a request to free
   /// resources associated with it.
   void removeDocument(PathRef File);
+  /// Force \p File to be reparsed using the latest contents.
+  void forceReparse(PathRef File);
 
   /// Run code completion for \p File at \p Pos.
   std::vector<CompletionItem> codeComplete(PathRef File, Position Pos);
@@ -129,6 +143,7 @@
 private:
   std::unique_ptr<GlobalCompilationDatabase> CDB;
   std::unique_ptr<DiagnosticsConsumer> DiagConsumer;
+  std::unique_ptr<FileSystemProvider> FSProvider;
   DraftStore DraftMgr;
   ClangdUnitStore Units;
   std::shared_ptr<PCHContainerOperations> PCHs;
Index: clangd/ClangdServer.cpp
===================================================================
--- clangd/ClangdServer.cpp
+++ clangd/ClangdServer.cpp
@@ -58,6 +58,10 @@
   return {Lines, Cols};
 }
 
+IntrusiveRefCntPtr<vfs::FileSystem> RealFileSystemProvider::getFileSystem() {
+  return vfs::getRealFileSystem();
+}
+
 ClangdScheduler::ClangdScheduler(bool RunSynchronously)
     : RunSynchronously(RunSynchronously) {
   if (RunSynchronously) {
@@ -135,8 +139,10 @@
 
 ClangdServer::ClangdServer(std::unique_ptr<GlobalCompilationDatabase> CDB,
                            std::unique_ptr<DiagnosticsConsumer> DiagConsumer,
+                           std::unique_ptr<FileSystemProvider> FSProvider,
                            bool RunSynchronously)
     : CDB(std::move(CDB)), DiagConsumer(std::move(DiagConsumer)),
+      FSProvider(std::move(FSProvider)),
       PCHs(std::make_shared<PCHContainerOperations>()),
       WorkScheduler(RunSynchronously) {}
 
@@ -150,10 +156,11 @@
 
     assert(FileContents.Draft &&
            "No contents inside a file that was scheduled for reparse");
-    Units.runOnUnit(
-        FileStr, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit const &Unit) {
-          DiagConsumer->onDiagnosticsReady(FileStr, Unit.getLocalDiagnostics());
-        });
+    Units.runOnUnit(FileStr, *FileContents.Draft, *CDB, PCHs,
+                    FSProvider->getFileSystem(), [&](ClangdUnit const &Unit) {
+                      DiagConsumer->onDiagnosticsReady(
+                          FileStr, Unit.getLocalDiagnostics());
+                    });
   });
 }
 
@@ -168,15 +175,22 @@
   });
 }
 
+void ClangdServer::forceReparse(PathRef File) {
+  // The addDocument schedules the reparse even if the contents of the file
+  // never changed, so we just call it here.
+  addDocument(File, getDocument(File));
+}
+
 std::vector<CompletionItem> ClangdServer::codeComplete(PathRef File,
                                                        Position Pos) {
   auto FileContents = DraftMgr.getDraft(File);
   assert(FileContents.Draft && "codeComplete is called for non-added document");
 
   std::vector<CompletionItem> Result;
+  auto VFS = FSProvider->getFileSystem();
   Units.runOnUnitWithoutReparse(
-      File, *FileContents.Draft, *CDB, PCHs, [&](ClangdUnit &Unit) {
-        Result = Unit.codeComplete(*FileContents.Draft, Pos);
+      File, *FileContents.Draft, *CDB, PCHs, VFS, [&](ClangdUnit &Unit) {
+        Result = Unit.codeComplete(*FileContents.Draft, Pos, VFS);
       });
   return Result;
 }
Index: clangd/ClangdLSPServer.cpp
===================================================================
--- clangd/ClangdLSPServer.cpp
+++ clangd/ClangdLSPServer.cpp
@@ -199,6 +199,7 @@
     : Out(Out),
       Server(llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(),
              llvm::make_unique<LSPDiagnosticsConsumer>(*this),
+             llvm::make_unique<RealFileSystemProvider>(),
              RunSynchronously) {}
 
 void ClangdLSPServer::run(std::istream &In) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to