[clang-tools-extra] r303977 - [clangd] Allow to use vfs::FileSystem for file accesses.

Vedant Kumar via cfe-commits cfe-commits at lists.llvm.org
Fri May 26 14:11:57 PDT 2017


Hi Ilya,

The unit test introduced by this commit hits an assertion failure on our bots. Could you please take a look at the issue?

    lab.llvm.org:8080/green/job/clang-stage1-cmake-RA-expensive/6733 <http://lab.llvm.org:8080/green/job/clang-stage1-cmake-RA-expensive/6733>

I see:

[ RUN      ] ClangdVFSTest.Reparse

Assertion failed: (Unit && "Unit wasn't created"), function ClangdUnit, file /Users/buildslave/jenkins/sharedspace/clang-stage1-cmake-RA_workspace/llvm/tools/clang/tools/extra/clangd/ClangdUnit.cpp, line 58.

0  ClangdTests              0x0000000102fec2f8 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  ClangdTests              0x0000000102fec9e6 SignalHandler(int) + 454
2  libsystem_platform.dylib 0x00007fff97ed352a _sigtramp + 26
3  libsystem_platform.dylib 000000000000000000 _sigtramp + 1746062064
4  libsystem_c.dylib        0x00007fff97d776df abort + 129
5  libsystem_c.dylib        0x00007fff97d3edd8 basename + 0
6  ClangdTests              0x00000001030c95dd clang::clangd::ClangdUnit::ClangdUnit(llvm::StringRef, llvm::StringRef, std::__1::shared_ptr<clang::PCHContainerOperations>, std::__1::vector<clang::tooling::CompileCommand, std::__1::allocator<clang::tooling::CompileCommand> >, llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem>) + 1837
7  ClangdTests              0x00000001030c824f std::__1::__function::__func<clang::clangd::ClangdServer::addDocument(llvm::StringRef, llvm::StringRef)::$_1, std::__1::allocator<clang::clangd::ClangdServer::addDocument(llvm::StringRef, llvm::StringRef)::$_1>, void ()>::operator()() + 975
8  ClangdTests              0x00000001030c6e03 void* std::__1::__thread_proxy<std::__1::tuple<clang::clangd::ClangdScheduler::ClangdScheduler(bool)::$_0> >(void*) + 579
9  libsystem_pthread.dylib  0x00007fff8e26099d _pthread_body + 131
10 libsystem_pthread.dylib  0x00007fff8e26091a _pthread_body + 0
11 libsystem_pthread.dylib  0x00007fff8e25e351 thread_start + 13

********************
FAIL: Extra Tools Unit Tests :: clangd/ClangdTests/ClangdVFSTest.ReparseOnHeaderChange (13778 of 37955)
******************** TEST 'Extra Tools Unit Tests :: clangd/ClangdTests/ClangdVFSTest.ReparseOnHeaderChange' FAILED ********************
Note: Google Test filter = ClangdVFSTest.ReparseOnHeaderChange
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from ClangdVFSTest
[ RUN      ] ClangdVFSTest.ReparseOnHeaderChange

Assertion failed: (Unit && "Unit wasn't created"), function ClangdUnit, file /Users/buildslave/jenkins/sharedspace/clang-stage1-cmake-RA_workspace/llvm/tools/clang/tools/extra/clangd/ClangdUnit.cpp, line 58.

0  ClangdTests              0x00000001005b72f8 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40
1  ClangdTests              0x00000001005b79e6 SignalHandler(int) + 454
2  libsystem_platform.dylib 0x00007fff97ed352a _sigtramp + 26
3  libsystem_platform.dylib 000000000000000000 _sigtramp + 1746062064
4  libsystem_c.dylib        0x00007fff97d776df abort + 129
5  libsystem_c.dylib        0x00007fff97d3edd8 basename + 0
6  ClangdTests              0x00000001006945dd clang::clangd::ClangdUnit::ClangdUnit(llvm::StringRef, llvm::StringRef, std::__1::shared_ptr<clang::PCHContainerOperations>, std::__1::vector<clang::tooling::CompileCommand, std::__1::allocator<clang::tooling::CompileCommand> >, llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem>) + 1837
7  ClangdTests              0x000000010069324f std::__1::__function::__func<clang::clangd::ClangdServer::addDocument(llvm::StringRef, llvm::StringRef)::$_1, std::__1::allocator<clang::clangd::ClangdServer::addDocument(llvm::StringRef, llvm::StringRef)::$_1>, void ()>::operator()() + 975
8  ClangdTests              0x0000000100691e03 void* std::__1::__thread_proxy<std::__1::tuple<clang::clangd::ClangdScheduler::ClangdScheduler(bool)::$_0> >(void*) + 579
9  libsystem_pthread.dylib  0x00007fff8e26099d _pthread_body + 131
10 libsystem_pthread.dylib  0x00007fff8e26091a _pthread_body + 0
11 libsystem_pthread.dylib  0x00007fff8e25e351 thread_start + 13

thanks,
vedant

> On May 26, 2017, at 5:26 AM, Ilya Biryukov via cfe-commits <cfe-commits at lists.llvm.org> wrote:
> 
> Author: ibiryukov
> Date: Fri May 26 07:26:51 2017
> New Revision: 303977
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=303977&view=rev
> Log:
> [clangd] Allow to use vfs::FileSystem for file accesses.
> 
> Summary:
> Custom vfs::FileSystem is currently used for unit tests.
> This revision depends on https://reviews.llvm.org/D33397.
> 
> Reviewers: bkramer, krasimir
> 
> Reviewed By: bkramer, krasimir
> 
> Subscribers: klimek, cfe-commits, mgorny
> 
> Differential Revision: https://reviews.llvm.org/D33416
> 
> Added:
>    clang-tools-extra/trunk/unittests/clangd/
>    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
>    clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp
> Modified:
>    clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
>    clang-tools-extra/trunk/clangd/ClangdServer.cpp
>    clang-tools-extra/trunk/clangd/ClangdServer.h
>    clang-tools-extra/trunk/clangd/ClangdUnit.cpp
>    clang-tools-extra/trunk/clangd/ClangdUnit.h
>    clang-tools-extra/trunk/clangd/ClangdUnitStore.h
>    clang-tools-extra/trunk/unittests/CMakeLists.txt
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)
> +++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri May 26 07:26:51 2017
> @@ -199,6 +199,7 @@ ClangdLSPServer::ClangdLSPServer(JSONOut
>     : Out(Out),
>       Server(llvm::make_unique<DirectoryBasedGlobalCompilationDatabase>(),
>              llvm::make_unique<LSPDiagnosticsConsumer>(*this),
> +             llvm::make_unique<RealFileSystemProvider>(),
>              RunSynchronously) {}
> 
> void ClangdLSPServer::run(std::istream &In) {
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)
> +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri May 26 07:26:51 2017
> @@ -58,6 +58,10 @@ Position clangd::offsetToPosition(String
>   return {Lines, Cols};
> }
> 
> +IntrusiveRefCntPtr<vfs::FileSystem> RealFileSystemProvider::getFileSystem() {
> +  return vfs::getRealFileSystem();
> +}
> +
> ClangdScheduler::ClangdScheduler(bool RunSynchronously)
>     : RunSynchronously(RunSynchronously) {
>   if (RunSynchronously) {
> @@ -135,8 +139,10 @@ void ClangdScheduler::addToEnd(std::func
> 
> 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 @@ void ClangdServer::addDocument(PathRef F
> 
>     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::removeDocument(PathRe
>   });
> }
> 
> +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;
> }
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdServer.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdServer.h (original)
> +++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri May 26 07:26:51 2017
> @@ -50,6 +50,17 @@ public:
>                                   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 @@ class ClangdServer {
> 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 @@ public:
>   /// 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 @@ public:
> 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;
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdUnit.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.cpp?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdUnit.cpp (original)
> +++ clang-tools-extra/trunk/clangd/ClangdUnit.cpp Fri May 26 07:26:51 2017
> @@ -11,6 +11,7 @@
> #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;
> @@ -18,7 +19,8 @@ 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,10 +50,16 @@ ClangdUnit::ClangdUnit(PathRef FileName,
>       /*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.
> @@ -59,7 +67,7 @@ void ClangdUnit::reparse(StringRef Conte
>       FileName,
>       llvm::MemoryBuffer::getMemBufferCopy(Contents, FileName).release());
> 
> -  Unit->Reparse(PCHs, RemappedSource);
> +  Unit->Reparse(PCHs, RemappedSource, VFS);
> }
> 
> namespace {
> @@ -146,8 +154,9 @@ public:
> };
> } // 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,8 +172,10 @@ std::vector<CompletionItem> ClangdUnit::
>       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.
> @@ -172,8 +183,8 @@ std::vector<CompletionItem> ClangdUnit::
>   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;
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdUnit.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnit.h?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdUnit.h (original)
> +++ clang-tools-extra/trunk/clangd/ClangdUnit.h Fri May 26 07:26:51 2017
> @@ -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 @@ namespace clang {
> class ASTUnit;
> class PCHContainerOperations;
> 
> +namespace vfs {
> +class FileSystem;
> +}
> +
> namespace tooling {
> struct CompileCommand;
> }
> @@ -42,16 +46,19 @@ class ClangdUnit {
> 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;
> 
> Modified: clang-tools-extra/trunk/clangd/ClangdUnitStore.h
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdUnitStore.h?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/clangd/ClangdUnitStore.h (original)
> +++ clang-tools-extra/trunk/clangd/ClangdUnitStore.h Fri May 26 07:26:51 2017
> @@ -32,9 +32,10 @@ public:
>   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 @@ public:
>   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 @@ private:
>   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);
>   }
> 
> Modified: clang-tools-extra/trunk/unittests/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/CMakeLists.txt?rev=303977&r1=303976&r2=303977&view=diff
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/CMakeLists.txt (original)
> +++ clang-tools-extra/trunk/unittests/CMakeLists.txt Fri May 26 07:26:51 2017
> @@ -11,4 +11,5 @@ add_subdirectory(clang-move)
> add_subdirectory(clang-query)
> add_subdirectory(clang-tidy)
> add_subdirectory(clang-rename)
> +add_subdirectory(clangd)
> add_subdirectory(include-fixer)
> 
> Added: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=303977&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (added)
> +++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Fri May 26 07:26:51 2017
> @@ -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
> +  )
> 
> Added: clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp
> URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp?rev=303977&view=auto
> ==============================================================================
> --- clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp (added)
> +++ clang-tools-extra/trunk/unittests/clangd/ClangdTests.cpp Fri May 26 07:26:51 2017
> @@ -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
> 
> 
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20170526/8da90783/attachment-0001.html>


More information about the cfe-commits mailing list