//===-- TestFS.cpp ----------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "TestFS.h" #include "GlobalCompilationDatabase.h" #include "URI.h" #include "support/Path.h" #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Path.h" namespace clang { namespace clangd { llvm::IntrusiveRefCntPtr buildTestFS(llvm::StringMap const &Files, llvm::StringMap const &Timestamps) { llvm::IntrusiveRefCntPtr MemFS( new llvm::vfs::InMemoryFileSystem); MemFS->setCurrentWorkingDirectory(testRoot()); for (auto &FileAndContents : Files) { llvm::StringRef File = FileAndContents.first(); MemFS->addFile( File, Timestamps.lookup(File), llvm::MemoryBuffer::getMemBufferCopy(FileAndContents.second, File)); } return MemFS; } MockCompilationDatabase::MockCompilationDatabase(llvm::StringRef Directory, llvm::StringRef RelPathPrefix) : ExtraClangFlags({"-ffreestanding"}), Directory(Directory), RelPathPrefix(RelPathPrefix) { // -ffreestanding avoids implicit stdc-predef.h. } llvm::Optional MockCompilationDatabase::getProjectInfo(PathRef File) const { return ProjectInfo{std::string(Directory)}; } llvm::Optional MockCompilationDatabase::getCompileCommand(PathRef File) const { if (ExtraClangFlags.empty()) return None; auto FileName = llvm::sys::path::filename(File); // Build the compile command. auto CommandLine = ExtraClangFlags; CommandLine.insert(CommandLine.begin(), "clang"); if (RelPathPrefix.empty()) { // Use the absolute path in the compile command. CommandLine.push_back(std::string(File)); } else { // Build a relative path using RelPathPrefix. llvm::SmallString<32> RelativeFilePath(RelPathPrefix); llvm::sys::path::append(RelativeFilePath, FileName); CommandLine.push_back(std::string(RelativeFilePath.str())); } return {tooling::CompileCommand(Directory != llvm::StringRef() ? Directory : llvm::sys::path::parent_path(File), FileName, std::move(CommandLine), "")}; } const char *testRoot() { #ifdef _WIN32 return "C:\\clangd-test"; #else return "/clangd-test"; #endif } std::string testPath(PathRef File, llvm::sys::path::Style Style) { assert(llvm::sys::path::is_relative(File) && "FileName should be relative"); llvm::SmallString<32> NativeFile = File; llvm::sys::path::native(NativeFile, Style); llvm::SmallString<32> Path; llvm::sys::path::append(Path, Style, testRoot(), NativeFile); return std::string(Path.str()); } /// unittest: is a scheme that refers to files relative to testRoot(). /// URI body is a path relative to testRoot() e.g. unittest:///x.h for /// /clangd-test/x.h. class TestScheme : public URIScheme { public: static const char *Scheme; llvm::Expected getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, llvm::StringRef HintPath) const override { if (!HintPath.startswith(testRoot())) return error("Hint path doesn't start with test root: {0}", HintPath); if (!Body.consume_front("/")) return error("Body of an unittest: URI must start with '/'"); llvm::SmallString<16> Path(Body.begin(), Body.end()); llvm::sys::path::native(Path); return testPath(Path); } llvm::Expected uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { llvm::StringRef Body = AbsolutePath; if (!Body.consume_front(testRoot())) return error("{0} does not start with {1}", AbsolutePath, testRoot()); return URI(Scheme, /*Authority=*/"", llvm::sys::path::convert_to_slash(Body)); } }; const char *TestScheme::Scheme = "unittest"; static URISchemeRegistry::Add X(TestScheme::Scheme, "Test schema"); volatile int UnittestSchemeAnchorSource = 0; } // namespace clangd } // namespace clang