1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
//===-- 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/Logger.h"
#include "support/Path.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Path.h"
#include <optional>
namespace clang {
namespace clangd {
namespace {
// Tries to strip \p Prefix from beginning of \p Path. Returns true on success.
// If \p Prefix doesn't match, leaves \p Path untouched and returns false.
bool pathConsumeFront(PathRef &Path, PathRef Prefix) {
if (!pathStartsWith(Prefix, Path))
return false;
Path = Path.drop_front(Prefix.size());
return true;
}
} // namespace
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
buildTestFS(llvm::StringMap<std::string> const &Files,
llvm::StringMap<time_t> const &Timestamps) {
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> 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.
}
std::optional<ProjectInfo>
MockCompilationDatabase::getProjectInfo(PathRef File) const {
return ProjectInfo{std::string(Directory)};
}
std::optional<tooling::CompileCommand>
MockCompilationDatabase::getCompileCommand(PathRef File) const {
if (ExtraClangFlags.empty())
return std::nullopt;
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<std::string>
getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
llvm::StringRef HintPath) const override {
if (!HintPath.empty() && !pathStartsWith(testRoot(), HintPath))
return error("Hint path is not empty and doesn't start with {0}: {1}",
testRoot(), 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<URI>
uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
if (!pathConsumeFront(AbsolutePath, testRoot()))
return error("{0} does not start with {1}", AbsolutePath, testRoot());
return URI(Scheme, /*Authority=*/"",
llvm::sys::path::convert_to_slash(AbsolutePath));
}
};
const char *TestScheme::Scheme = "unittest";
static URISchemeRegistry::Add<TestScheme> X(TestScheme::Scheme, "Test schema");
volatile int UnittestSchemeAnchorSource = 0;
} // namespace clangd
} // namespace clang
|