Summary: This ensures ParseInputs provides a read-only access to FS.
Reviewers: sammccall
Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, usaxena95, cfe-commits
Tags: #clang
Differential Revision: https://reviews.llvm.org/D81173
void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents,
llvm::StringRef Version,
WantDiagnostics WantDiags, bool ForceRebuild) {
- auto FS = FSProvider.getFileSystem();
-
ParseOptions Opts;
Opts.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults();
// FIXME: call tidy options builder on the worker thread, it can do IO.
if (GetClangTidyOptions)
- Opts.ClangTidyOpts = GetClangTidyOptions(*FS, File);
+ Opts.ClangTidyOpts = GetClangTidyOptions(*FSProvider.getFileSystem(), File);
Opts.SuggestMissingIncludes = SuggestMissingIncludes;
// Compile command is set asynchronously during update, as it can be slow.
ParseInputs Inputs;
- Inputs.FS = FS;
+ Inputs.FSProvider = &FSProvider;
Inputs.Contents = std::string(Contents);
Inputs.Version = Version.str();
Inputs.ForceRebuild = ForceRebuild;
if (!CodeCompleteOpts.Index) // Respect overridden index.
CodeCompleteOpts.Index = Index;
- auto Task = [Pos, FS = FSProvider.getFileSystem(), CodeCompleteOpts,
- File = File.str(), CB = std::move(CB),
+ auto Task = [Pos, CodeCompleteOpts, File = File.str(), CB = std::move(CB),
this](llvm::Expected<InputsAndPreamble> IP) mutable {
if (!IP)
return CB(IP.takeError());
}
}
}
- ParseInputs ParseInput{IP->Command, FS, IP->Contents.str()};
+ ParseInputs ParseInput{IP->Command, &FSProvider, IP->Contents.str()};
ParseInput.Index = Index;
ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST;
ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType;
void ClangdServer::signatureHelp(PathRef File, Position Pos,
Callback<SignatureHelp> CB) {
- auto Action = [Pos, FS = FSProvider.getFileSystem(), File = File.str(),
- CB = std::move(CB),
+ auto Action = [Pos, File = File.str(), CB = std::move(CB),
this](llvm::Expected<InputsAndPreamble> IP) mutable {
if (!IP)
return CB(IP.takeError());
return CB(llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to parse includes"));
- ParseInputs ParseInput{IP->Command, FS, IP->Contents.str()};
+ ParseInputs ParseInput{IP->Command, &FSProvider, IP->Contents.str()};
ParseInput.Index = Index;
ParseInput.Opts.BuildRecoveryAST = BuildRecoveryAST;
ParseInput.Opts.PreserveRecoveryASTType = PreserveRecoveryASTType;
return CB(Edits.takeError());
if (Opts.WantFormat) {
- auto Style = getFormatStyleForFile(File, InpAST->Inputs.Contents,
- InpAST->Inputs.FS.get());
+ auto Style = getFormatStyleForFile(
+ File, InpAST->Inputs.Contents,
+ InpAST->Inputs.FSProvider->getFileSystem().get());
llvm::Error Err = llvm::Error::success();
for (auto &E : *Edits)
Err =
this](llvm::Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
- format::FormatStyle Style = getFormatStyleForFile(
- File, InpAST->Inputs.Contents, InpAST->Inputs.FS.get());
+ format::FormatStyle Style =
+ getFormatStyleForFile(File, InpAST->Inputs.Contents,
+ InpAST->Inputs.FSProvider->getFileSystem().get());
CB(clangd::getHover(InpAST->AST, Pos, std::move(Style), Index));
};
#include "index/Index.h"
#include "index/Symbol.h"
#include "index/SymbolOrigin.h"
+#include "support/FSProvider.h"
#include "support/Logger.h"
#include "support/Threading.h"
#include "support/Trace.h"
const SemaCompleteInput &Input,
IncludeStructure *Includes = nullptr) {
trace::Span Tracer("Sema completion");
- llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = Input.ParseInput.FS;
- if (Input.Preamble.StatCache)
- VFS = Input.Preamble.StatCache->getConsumingFS(std::move(VFS));
IgnoreDiagnostics IgnoreDiags;
auto CI = buildCompilerInvocation(Input.ParseInput, IgnoreDiags);
Input.Patch->apply(*CI);
// NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise
// the remapped buffers do not get freed.
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
+ Input.ParseInput.FSProvider->getFileSystem();
+ if (Input.Preamble.StatCache)
+ VFS = Input.Preamble.StatCache->getConsumingFS(std::move(VFS));
auto Clang = prepareCompilerInstance(
std::move(CI), !CompletingInPreamble ? &Input.Preamble.Preamble : nullptr,
std::move(ContentsBuffer), std::move(VFS), IgnoreDiags);
assert(Recorder && "Recorder is not set");
CCContextKind = Recorder->CCContext.getKind();
IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration();
- auto Style = getFormatStyleForFile(SemaCCInput.FileName,
- SemaCCInput.ParseInput.Contents,
- SemaCCInput.ParseInput.FS.get());
+ auto Style = getFormatStyleForFile(
+ SemaCCInput.FileName, SemaCCInput.ParseInput.Contents,
+ SemaCCInput.ParseInput.FSProvider->getFileSystem().get());
// If preprocessor was run, inclusions from preprocessor callback should
// already be added to Includes.
Inserter.emplace(
FileName, Preamble ? Preamble->Includes : IncludeStructure(),
SpecFuzzyFind, Opts);
return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse)
- ? std::move(Flow).runWithoutSema(ParseInput.Contents, *Offset,
- ParseInput.FS)
+ ? std::move(Flow).runWithoutSema(
+ ParseInput.Contents, *Offset,
+ ParseInput.FSProvider->getFileSystem())
: std::move(Flow).run({FileName, *Offset, *Preamble,
// We want to serve code completions with
// low latency, so don't bother patching.
for (const auto &S : Inputs.CompileCommand.CommandLine)
ArgStrs.push_back(S.c_str());
- if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
+ auto VFS = Inputs.FSProvider->getFileSystem();
+ if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
log("Couldn't set working directory when creating compiler invocation.");
// We proceed anyway, our lit-tests rely on results for non-existing working
// dirs.
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> CommandLineDiagsEngine =
CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false);
std::unique_ptr<CompilerInvocation> CI = createInvocationFromCommandLine(
- ArgStrs, CommandLineDiagsEngine, Inputs.FS,
+ ArgStrs, CommandLineDiagsEngine, std::move(VFS),
/*ShouldRecoverOnErrors=*/true, CC1Args);
if (!CI)
return nullptr;
#include "../clang-tidy/ClangTidyOptions.h"
#include "GlobalCompilationDatabase.h"
#include "index/Index.h"
+#include "support/FSProvider.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/Tooling/CompilationDatabase.h"
/// Information required to run clang, e.g. to parse AST or do code completion.
struct ParseInputs {
tooling::CompileCommand CompileCommand;
- IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS;
+ const FileSystemProvider *FSProvider;
std::string Contents;
// Version identifier for Contents, provided by the client and opaque to us.
std::string Version = "null";
trace::Span Tracer("BuildAST");
SPAN_ATTACH(Tracer, "File", Filename);
- auto VFS = Inputs.FS;
+ auto VFS = Inputs.FSProvider->getFileSystem();
if (Preamble && Preamble->StatCache)
VFS = Preamble->StatCache->getConsumingFS(std::move(VFS));
if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
#include "Compiler.h"
#include "Headers.h"
#include "SourceCode.h"
+#include "support/FSProvider.h"
#include "support/Logger.h"
#include "support/Trace.h"
#include "clang/Basic/Diagnostic.h"
scanPreamble(llvm::StringRef Contents,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
const tooling::CompileCommand &Cmd) {
+ // FIXME: Change PreambleStatCache to operate on FileSystemProvider rather
+ // than vfs::FileSystem, that way we can just use ParseInputs without this
+ // hack.
+ auto GetFSProvider = [](llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
+ class VFSProvider : public FileSystemProvider {
+ public:
+ VFSProvider(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
+ : VFS(std::move(FS)) {}
+ llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
+ getFileSystem() const override {
+ return VFS;
+ }
+
+ private:
+ const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
+ };
+ return std::make_unique<VFSProvider>(std::move(FS));
+ };
+ auto FSProvider = GetFSProvider(std::move(VFS));
// Build and run Preprocessor over the preamble.
ParseInputs PI;
PI.Contents = Contents.str();
- PI.FS = std::move(VFS);
+ PI.FSProvider = FSProvider.get();
PI.CompileCommand = Cmd;
IgnoringDiagConsumer IgnoreDiags;
auto CI = buildCompilerInvocation(PI, IgnoreDiags);
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
- if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
+ auto VFS = Inputs.FSProvider->getFileSystem();
+ if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
log("Couldn't set working directory when building the preamble.");
// We proceed anyway, our lit-tests rely on results for non-existing working
// dirs.
}
llvm::SmallString<32> AbsFileName(FileName);
- Inputs.FS->makeAbsolute(AbsFileName);
+ VFS->makeAbsolute(AbsFileName);
auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
auto BuiltPreamble = PrecompiledPreamble::Build(
CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
- StatCache->getProducingFS(Inputs.FS),
+ StatCache->getProducingFS(VFS),
std::make_shared<PCHContainerOperations>(), StoreInMemory,
SerializedDeclsCollector);
return compileCommandsAreEqual(Inputs.CompileCommand,
Preamble.CompileCommand) &&
Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
- Inputs.FS.get());
+ Inputs.FSProvider->getFileSystem().get());
}
void escapeBackslashAndQuotes(llvm::StringRef Text, llvm::raw_ostream &OS) {
trace::Span Tracer("CreatePreamblePatch");
SPAN_ATTACH(Tracer, "File", FileName);
assert(llvm::sys::path::is_absolute(FileName) && "relative FileName!");
+ auto VFS =
+ Baseline.StatCache->getConsumingFS(Modified.FSProvider->getFileSystem());
// First scan preprocessor directives in Baseline and Modified. These will be
// used to figure out newly added directives in Modified. Scanning can fail,
// the code just bails out and creates an empty patch in such cases, as:
// there's nothing to do but generate an empty patch.
auto BaselineScan = scanPreamble(
// Contents needs to be null-terminated.
- Baseline.Preamble.getContents().str(),
- Baseline.StatCache->getConsumingFS(Modified.FS), Modified.CompileCommand);
+ Baseline.Preamble.getContents().str(), VFS, Modified.CompileCommand);
if (!BaselineScan) {
elog("Failed to scan baseline of {0}: {1}", FileName,
BaselineScan.takeError());
return PreamblePatch::unmodified(Baseline);
}
- auto ModifiedScan = scanPreamble(
- Modified.Contents, Baseline.StatCache->getConsumingFS(Modified.FS),
- Modified.CompileCommand);
+ auto ModifiedScan =
+ scanPreamble(Modified.Contents, std::move(VFS), Modified.CompileCommand);
if (!ModifiedScan) {
elog("Failed to scan modified contents of {0}: {1}", FileName,
ModifiedScan.takeError());
auto AbsolutePath = getAbsolutePath(Cmd);
auto FS = FSProvider.getFileSystem();
+ FS->setCurrentWorkingDirectory(Cmd.Directory);
auto Buf = FS->getBufferForFile(AbsolutePath);
if (!Buf)
return llvm::errorCodeToError(Buf.getError());
vlog("Indexing {0} (digest:={1})", Cmd.Filename, llvm::toHex(Hash));
ParseInputs Inputs;
- Inputs.FS = std::move(FS);
- Inputs.FS->setCurrentWorkingDirectory(Cmd.Directory);
+ Inputs.FSProvider = &FSProvider;
Inputs.CompileCommand = std::move(Cmd);
IgnoreDiagnostics IgnoreDiags;
auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
if (!CI)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Couldn't build compiler invocation");
- auto Clang = prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
- std::move(*Buf), Inputs.FS, IgnoreDiags);
+
+ auto Clang =
+ prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
+ std::move(*Buf), std::move(FS), IgnoreDiags);
if (!Clang)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Couldn't build compiler instance");
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/VirtualFileSystem.h"
+#include <memory>
namespace clang {
namespace clangd {
Opts.Index = OverrideIndex.get();
}
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
+ Inputs.Opts.BuildRecoveryAST = true;
+ Inputs.Opts.PreserveRecoveryASTType = true;
IgnoreDiagnostics Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
if (!CI) {
}
auto Preamble = buildPreamble(testPath(TU.Filename), *CI, Inputs,
/*InMemory=*/true, /*Callback=*/nullptr);
- ParseInputs ParseInput{Inputs.CompileCommand, Inputs.FS, TU.Code};
- ParseInput.Opts.BuildRecoveryAST = true;
- ParseInput.Opts.PreserveRecoveryASTType = true;
- return codeComplete(testPath(TU.Filename), Point, Preamble.get(), ParseInput,
+ return codeComplete(testPath(TU.Filename), Point, Preamble.get(), Inputs,
Opts);
}
MockFSProvider FS;
Annotations Test(Text);
- ParseInputs ParseInput{tooling::CompileCommand(), FS.getFileSystem(),
- Test.code().str()};
+ ParseInputs ParseInput{tooling::CompileCommand(), &FS, Test.code().str()};
return codeComplete(FilePath, Test.point(), /*Preamble=*/nullptr, ParseInput,
Opts);
}
Index = memIndex(IndexSymbols);
auto TU = TestTU::withCode(Text);
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
+ Inputs.Index = Index.get();
+ Inputs.Opts.BuildRecoveryAST = true;
+ Inputs.Opts.PreserveRecoveryASTType = true;
IgnoreDiagnostics Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
if (!CI) {
ADD_FAILURE() << "Couldn't build Preamble";
return {};
}
- ParseInputs ParseInput{Inputs.CompileCommand, Inputs.FS, Text.str()};
- ParseInput.Index = Index.get();
- ParseInput.Opts.BuildRecoveryAST = true;
- ParseInput.Opts.PreserveRecoveryASTType = true;
- return signatureHelp(testPath(TU.Filename), Point, *Preamble, ParseInput);
+ return signatureHelp(testPath(TU.Filename), Point, *Preamble, Inputs);
}
SignatureHelp signatures(llvm::StringRef Text,
TestTU TU;
TU.Code = "";
IgnoreDiagnostics Diags;
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
auto CI = buildCompilerInvocation(Inputs, Diags);
ASSERT_TRUE(CI);
auto EmptyPreamble = buildPreamble(testPath(TU.Filename), *CI, Inputs,
#include "a.h"
void bar() { foo(^2); })cpp");
TU.Code = Test.code().str();
- Inputs = TU.inputs();
-
- ParseInputs ParseInput{Inputs.CompileCommand, Inputs.FS, TU.Code};
auto Results = signatureHelp(testPath(TU.Filename), Test.point(),
- *EmptyPreamble, ParseInput);
+ *EmptyPreamble, TU.inputs(FS));
EXPECT_THAT(Results.signatures, ElementsAre(Sig("foo([[int x]]) -> int")));
EXPECT_EQ(0, Results.activeSignature);
EXPECT_EQ(0, Results.activeParameter);
using testing::IsEmpty;
TEST(BuildCompilerInvocation, DropsPCH) {
+ MockFSProvider FS;
IgnoreDiagnostics Diags;
TestTU TU;
TU.AdditionalFiles["test.h.pch"] = "";
TU.ExtraArgs = {"-include-pch", "test.h.pch"};
- EXPECT_THAT(buildCompilerInvocation(TU.inputs(), Diags)
+ EXPECT_THAT(buildCompilerInvocation(TU.inputs(FS), Diags)
->getPreprocessorOpts()
.ImplicitPCHInclude,
IsEmpty());
// Transparent include translation
TU.ExtraArgs = {"-include", "test.h"};
- EXPECT_THAT(buildCompilerInvocation(TU.inputs(), Diags)
+ EXPECT_THAT(buildCompilerInvocation(TU.inputs(FS), Diags)
->getPreprocessorOpts()
.ImplicitPCHInclude,
IsEmpty());
TU.AdditionalFiles["test.pch"] = "";
TU.ExtraArgs = {"--driver-mode=cl"};
TU.ExtraArgs.push_back("/Yutest.h");
- EXPECT_THAT(buildCompilerInvocation(TU.inputs(), Diags)
+ EXPECT_THAT(buildCompilerInvocation(TU.inputs(FS), Diags)
->getPreprocessorOpts()
.ImplicitPCHInclude,
IsEmpty());
- EXPECT_THAT(buildCompilerInvocation(TU.inputs(), Diags)
+ EXPECT_THAT(buildCompilerInvocation(TU.inputs(FS), Diags)
->getPreprocessorOpts()
.PCHThroughHeader,
IsEmpty());
PI.CompileCommand.Filename = FooCpp;
PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp};
- llvm::StringMap<std::string> Files;
- Files[FooCpp] = "";
- Files[FooH] = R"cpp(
+ MockFSProvider FSProvider;
+ FSProvider.Files[FooCpp] = "";
+ FSProvider.Files[FooH] = R"cpp(
namespace ns_in_header {
int func_in_header();
}
)cpp";
- PI.FS = buildTestFS(std::move(Files));
+ PI.FSProvider = &FSProvider;
PI.Contents = R"cpp(
#include "foo.h"
std::unique_ptr<CompilerInstance> setupClang() {
auto Cmd = CDB.getCompileCommand(MainFile);
assert(static_cast<bool>(Cmd));
- auto VFS = FS.getFileSystem();
- VFS->setCurrentWorkingDirectory(Cmd->Directory);
ParseInputs PI;
PI.CompileCommand = *Cmd;
- PI.FS = VFS;
+ PI.FSProvider = &FS;
auto CI = buildCompilerInvocation(PI, IgnoreDiags);
EXPECT_TRUE(static_cast<bool>(CI));
// The diagnostic options must be set before creating a CompilerInstance.
CI->getDiagnosticOpts().IgnoreWarnings = true;
+ auto VFS = FS.getFileSystem();
+ VFS->setCurrentWorkingDirectory(Cmd->Directory);
auto Clang = prepareCompilerInstance(
std::move(CI), /*Preamble=*/nullptr,
- llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile), VFS,
- IgnoreDiags);
+ llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile),
+ std::move(VFS), IgnoreDiags);
EXPECT_FALSE(Clang->getFrontendOpts().Inputs.empty());
return Clang;
}
TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
+ MockFSProvider FSProvider;
+ FSProvider.Files = {{testPath("foo.cpp"), "void test() {}"}};
// Unknown flags should not prevent a build of compiler invocation.
ParseInputs Inputs;
- Inputs.FS = buildTestFS({{testPath("foo.cpp"), "void test() {}"}});
+ Inputs.FSProvider = &FSProvider;
Inputs.CompileCommand.CommandLine = {"clang", "-fsome-unknown-flag",
testPath("foo.cpp")};
IgnoreDiagnostics IgnoreDiags;
// Make sure replay logic works with patched preambles.
TU.Code = "";
StoreDiags Diags;
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
auto CI = buildCompilerInvocation(Inputs, Diags);
auto EmptyPreamble =
buildPreamble(testPath(TU.Filename), *CI, Inputs, true, nullptr);
ASSERT_TRUE(EmptyPreamble);
TU.Code = "#include <a.h>";
Includes.clear();
- auto PatchedAST = ParsedAST::build(testPath(TU.Filename), TU.inputs(),
+ auto PatchedAST = ParsedAST::build(testPath(TU.Filename), TU.inputs(FS),
std::move(CI), {}, EmptyPreamble);
ASSERT_TRUE(PatchedAST);
// Make sure includes were seen only once.
// Build preamble with no includes.
TU.Code = "";
StoreDiags Diags;
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
auto CI = buildCompilerInvocation(Inputs, Diags);
auto EmptyPreamble =
buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
// Now build an AST using empty preamble and ensure patched includes worked.
TU.Code = ModifiedContents.str();
- Inputs = TU.inputs();
+ Inputs = TU.inputs(FS);
auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
{}, EmptyPreamble);
ASSERT_TRUE(PatchedAST);
// Build preamble with no includes.
TU.Code = R"cpp(#include <foo.h>)cpp";
StoreDiags Diags;
- auto Inputs = TU.inputs();
+ MockFSProvider FS;
+ auto Inputs = TU.inputs(FS);
auto CI = buildCompilerInvocation(Inputs, Diags);
auto BaselinePreamble =
buildPreamble(testPath("foo.cpp"), *CI, Inputs, true, nullptr);
// Now build an AST using additional includes and check that locations are
// correctly parsed.
TU.Code = "";
- Inputs = TU.inputs();
+ Inputs = TU.inputs(FS);
auto PatchedAST = ParsedAST::build(testPath("foo.cpp"), Inputs, std::move(CI),
{}, BaselinePreamble);
ASSERT_TRUE(PatchedAST);
collectPatchedIncludes(llvm::StringRef ModifiedContents,
llvm::StringRef BaselineContents,
llvm::StringRef MainFileName = "main.cpp") {
+ MockFSProvider FS;
auto TU = TestTU::withCode(BaselineContents);
TU.Filename = MainFileName.str();
// ms-compatibility changes meaning of #import, make sure it is turned off.
auto BaselinePreamble = TU.preamble();
// Create the patch.
TU.Code = ModifiedContents.str();
- auto PI = TU.inputs();
+ auto PI = TU.inputs(FS);
auto PP = PreamblePatch::create(testPath(TU.Filename), PI, *BaselinePreamble);
// Collect patch contents.
IgnoreDiagnostics Diags;
prepareCompilerInstance(std::move(CI), &BaselinePreamble->Preamble,
llvm::MemoryBuffer::getMemBufferCopy(
ModifiedContents.slice(0, Bounds.Size).str()),
- PI.FS, Diags);
+ PI.FSProvider->getFileSystem(), Diags);
PreprocessOnlyAction Action;
if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) {
ADD_FAILURE() << "failed begin source file";
}
TEST(PreamblePatchTest, PatchesPreambleIncludes) {
+ MockFSProvider FS;
IgnoreDiagnostics Diags;
auto TU = TestTU::withCode(R"cpp(
#include "a.h"
TU.AdditionalFiles["a.h"] = "#include \"b.h\"";
TU.AdditionalFiles["b.h"] = "";
TU.AdditionalFiles["c.h"] = "";
- auto PI = TU.inputs();
+ auto PI = TU.inputs(FS);
auto BaselinePreamble = buildPreamble(
TU.Filename, *buildCompilerInvocation(PI, Diags), PI, true, nullptr);
// We drop c.h from modified and add a new header. Since the latter is patched
#include "a.h"
#include "b.h"
)cpp";
- auto PP = PreamblePatch::create(testPath(TU.Filename), TU.inputs(),
+ auto PP = PreamblePatch::create(testPath(TU.Filename), TU.inputs(FS),
*BaselinePreamble);
// Only a.h should exists in the preamble, as c.h has been dropped and b.h was
// newly introduced.
}
IgnoreDiagnostics Diags;
+ MockFSProvider FS;
auto TU = TestTU::withCode(Modified);
- auto CI = buildCompilerInvocation(TU.inputs(), Diags);
+ auto CI = buildCompilerInvocation(TU.inputs(FS), Diags);
if (!CI) {
ADD_FAILURE() << "Failed to build compiler invocation";
return llvm::None;
}
- return ParsedAST::build(testPath(TU.Filename), TU.inputs(), std::move(CI), {},
- BaselinePreamble);
+ return ParsedAST::build(testPath(TU.Filename), TU.inputs(FS), std::move(CI),
+ {}, BaselinePreamble);
}
std::string getPreamblePatch(llvm::StringRef Baseline,
ADD_FAILURE() << "Failed to build baseline preamble";
return "";
}
+ MockFSProvider FS;
auto TU = TestTU::withCode(Modified);
- return PreamblePatch::create(testPath("main.cpp"), TU.inputs(),
+ return PreamblePatch::create(testPath("main.cpp"), TU.inputs(FS),
*BaselinePreamble)
.text()
.str();
#include "TestFS.h"
#include "support/Cancellation.h"
#include "support/Context.h"
+#include "support/FSProvider.h"
#include "support/Path.h"
#include "support/TestTracer.h"
#include "support/Threading.h"
ParseInputs getInputs(PathRef File, std::string Contents) {
ParseInputs Inputs;
Inputs.CompileCommand = *CDB.getCompileCommand(File);
- Inputs.FS = buildTestFS(Files, Timestamps);
+ Inputs.FSProvider = &FSProvider;
Inputs.Contents = std::move(Contents);
Inputs.Opts = ParseOptions();
return Inputs;
std::move(CB));
}
- llvm::StringMap<std::string> Files;
- llvm::StringMap<time_t> Timestamps;
+ MockFSProvider FSProvider;
MockCompilationDatabase CDB;
};
TUScheduler S(CDB, optsForTest());
auto Added = testPath("added.cpp");
- Files[Added] = "x";
+ FSProvider.Files[Added] = "x";
auto Missing = testPath("missing.cpp");
- Files[Missing] = "";
+ FSProvider.Files[Missing] = "";
S.update(Added, getInputs(Added, "x"), WantDiagnostics::No);
for (int I = 0; I < FilesCount; ++I) {
std::string Name = "foo" + std::to_string(I) + ".cpp";
Files.push_back(testPath(Name));
- this->Files[Files.back()] = "";
+ this->FSProvider.Files[Files.back()] = "";
}
StringRef Contents1 = R"cpp(int a;)cpp";
EXPECT_THAT(Context::current().get(NonceKey), Pointee(Nonce));
ASSERT_TRUE((bool)AST);
- EXPECT_EQ(AST->Inputs.FS, Inputs.FS);
EXPECT_EQ(AST->Inputs.Contents, Inputs.Contents);
EXPECT_EQ(AST->Inputs.Version, Inputs.Version);
EXPECT_EQ(AST->AST.version(), Inputs.Version);
auto Foo = testPath("foo.cpp");
auto Header = testPath("foo.h");
- Files[Header] = "void foo()";
- Timestamps[Header] = time_t(0);
+ FSProvider.Files[Header] = "void foo()";
+ FSProvider.Timestamps[Header] = time_t(0);
auto WithPreamble = R"cpp(
#include "foo.h"
int main() {}
auto Source = testPath("foo.cpp");
auto Header = testPath("foo.h");
- Files[Header] = "int a;";
- Timestamps[Header] = time_t(0);
+ FSProvider.Files[Header] = "int a;";
+ FSProvider.Timestamps[Header] = time_t(0);
std::string SourceContents = R"cpp(
#include "foo.h"
ASSERT_EQ(S.fileStats().lookup(Source).PreambleBuilds, 1u);
// Update to a header should cause a rebuild, though.
- Timestamps[Header] = time_t(1);
+ FSProvider.Timestamps[Header] = time_t(1);
ASSERT_TRUE(DoUpdate(SourceContents));
ASSERT_FALSE(DoUpdate(SourceContents));
ASSERT_EQ(S.fileStats().lookup(Source).ASTBuilds, 2u);
Field(&Diag::Message,
"use of undeclared identifier 'a'")));
});
+ S.blockUntilIdle(timeoutSeconds(10));
// Add the header file. We need to recreate the inputs since we changed a
// file from underneath the test FS.
- Files[Header] = "int a;";
- Timestamps[Header] = time_t(1);
+ FSProvider.Files[Header] = "int a;";
+ FSProvider.Timestamps[Header] = time_t(1);
Inputs = getInputs(Source, SourceContents);
// The addition of the missing header file shouldn't trigger a rebuild since
class MockFSProvider : public FileSystemProvider {
public:
IntrusiveRefCntPtr<llvm::vfs::FileSystem> getFileSystem() const override {
- return buildTestFS(Files);
+ return buildTestFS(Files, Timestamps);
}
// If relative paths are used, they are resolved with testPath().
llvm::StringMap<std::string> Files;
+ llvm::StringMap<time_t> Timestamps;
};
// A Compilation database that returns a fixed set of compile flags.
namespace clang {
namespace clangd {
-ParseInputs TestTU::inputs() const {
+ParseInputs TestTU::inputs(MockFSProvider &FSProvider) const {
std::string FullFilename = testPath(Filename),
FullHeaderName = testPath(HeaderFilename),
ImportThunk = testPath("import_thunk.h");
// guard without messing up offsets). In this case, use an intermediate file.
std::string ThunkContents = "#import \"" + FullHeaderName + "\"\n";
- llvm::StringMap<std::string> Files(AdditionalFiles);
- Files[FullFilename] = Code;
- Files[FullHeaderName] = HeaderCode;
- Files[ImportThunk] = ThunkContents;
+ FSProvider.Files = AdditionalFiles;
+ FSProvider.Files[FullFilename] = Code;
+ FSProvider.Files[FullHeaderName] = HeaderCode;
+ FSProvider.Files[ImportThunk] = ThunkContents;
ParseInputs Inputs;
auto &Argv = Inputs.CompileCommand.CommandLine;
Inputs.CompileCommand.Filename = FullFilename;
Inputs.CompileCommand.Directory = testRoot();
Inputs.Contents = Code;
- Inputs.FS = buildTestFS(Files);
+ Inputs.FSProvider = &FSProvider;
Inputs.Opts = ParseOptions();
Inputs.Opts.BuildRecoveryAST = true;
Inputs.Opts.PreserveRecoveryASTType = true;
}
std::shared_ptr<const PreambleData> TestTU::preamble() const {
- auto Inputs = inputs();
+ MockFSProvider FSProvider;
+ auto Inputs = inputs(FSProvider);
IgnoreDiagnostics Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
assert(CI && "Failed to build compilation invocation.");
}
ParsedAST TestTU::build() const {
- auto Inputs = inputs();
+ MockFSProvider FSProvider;
+ auto Inputs = inputs(FSProvider);
StoreDiags Diags;
auto CI = buildCompilerInvocation(Inputs, Diags);
assert(CI && "Failed to build compilation invocation.");
#include "Compiler.h"
#include "ParsedAST.h"
+#include "TestFS.h"
#include "index/Index.h"
#include "support/Path.h"
#include "llvm/ADT/StringMap.h"
// Suppress this behavior by adding an 'error-ok' comment to the code.
ParsedAST build() const;
std::shared_ptr<const PreambleData> preamble() const;
- ParseInputs inputs() const;
+ ParseInputs inputs(MockFSProvider &FSProvider) const;
SymbolSlab headerSymbols() const;
RefSlab headerRefs() const;
std::unique_ptr<SymbolIndex> index() const;