diff --git a/clang-tools-extra/clangd/ClangdServer.cpp b/clang-tools-extra/clangd/ClangdServer.cpp --- a/clang-tools-extra/clangd/ClangdServer.cpp +++ b/clang-tools-extra/clangd/ClangdServer.cpp @@ -179,18 +179,16 @@ 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; @@ -214,8 +212,7 @@ 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 IP) mutable { if (!IP) return CB(IP.takeError()); @@ -238,7 +235,7 @@ } } } - 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; @@ -275,8 +272,7 @@ void ClangdServer::signatureHelp(PathRef File, Position Pos, Callback 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 IP) mutable { if (!IP) return CB(IP.takeError()); @@ -286,7 +282,7 @@ 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; @@ -399,8 +395,9 @@ 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 = @@ -600,8 +597,9 @@ this](llvm::Expected 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)); }; diff --git a/clang-tools-extra/clangd/CodeComplete.cpp b/clang-tools-extra/clangd/CodeComplete.cpp --- a/clang-tools-extra/clangd/CodeComplete.cpp +++ b/clang-tools-extra/clangd/CodeComplete.cpp @@ -36,6 +36,7 @@ #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" @@ -1060,9 +1061,6 @@ const SemaCompleteInput &Input, IncludeStructure *Includes = nullptr) { trace::Span Tracer("Sema completion"); - llvm::IntrusiveRefCntPtr VFS = Input.ParseInput.FS; - if (Input.Preamble.StatCache) - VFS = Input.Preamble.StatCache->getConsumingFS(std::move(VFS)); IgnoreDiagnostics IgnoreDiags; auto CI = buildCompilerInvocation(Input.ParseInput, IgnoreDiags); @@ -1103,6 +1101,10 @@ Input.Patch->apply(*CI); // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise // the remapped buffers do not get freed. + llvm::IntrusiveRefCntPtr 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); @@ -1272,9 +1274,9 @@ 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( @@ -1759,8 +1761,9 @@ 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. diff --git a/clang-tools-extra/clangd/Compiler.h b/clang-tools-extra/clangd/Compiler.h --- a/clang-tools-extra/clangd/Compiler.h +++ b/clang-tools-extra/clangd/Compiler.h @@ -18,6 +18,7 @@ #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" @@ -45,7 +46,7 @@ /// Information required to run clang, e.g. to parse AST or do code completion. struct ParseInputs { tooling::CompileCommand CompileCommand; - IntrusiveRefCntPtr FS; + const FileSystemProvider *FSProvider; std::string Contents; // Version identifier for Contents, provided by the client and opaque to us. std::string Version = "null"; diff --git a/clang-tools-extra/clangd/Compiler.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -47,7 +47,8 @@ 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. @@ -56,7 +57,7 @@ llvm::IntrusiveRefCntPtr CommandLineDiagsEngine = CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false); std::unique_ptr CI = createInvocationFromCommandLine( - ArgStrs, CommandLineDiagsEngine, Inputs.FS, + ArgStrs, CommandLineDiagsEngine, std::move(VFS), /*ShouldRecoverOnErrors=*/true, CC1Args); if (!CI) return nullptr; diff --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp --- a/clang-tools-extra/clangd/ParsedAST.cpp +++ b/clang-tools-extra/clangd/ParsedAST.cpp @@ -248,7 +248,7 @@ 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)) { diff --git a/clang-tools-extra/clangd/Preamble.cpp b/clang-tools-extra/clangd/Preamble.cpp --- a/clang-tools-extra/clangd/Preamble.cpp +++ b/clang-tools-extra/clangd/Preamble.cpp @@ -10,6 +10,7 @@ #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" @@ -212,10 +213,29 @@ scanPreamble(llvm::StringRef Contents, llvm::IntrusiveRefCntPtr 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 FS) { + class VFSProvider : public FileSystemProvider { + public: + VFSProvider(llvm::IntrusiveRefCntPtr FS) + : VFS(std::move(FS)) {} + llvm::IntrusiveRefCntPtr + getFileSystem() const override { + return VFS; + } + + private: + const llvm::IntrusiveRefCntPtr VFS; + }; + return std::make_unique(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); @@ -320,18 +340,19 @@ 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(AbsFileName); auto BuiltPreamble = PrecompiledPreamble::Build( CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine, - StatCache->getProducingFS(Inputs.FS), + StatCache->getProducingFS(VFS), std::make_shared(), StoreInMemory, SerializedDeclsCollector); @@ -365,7 +386,7 @@ 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) { @@ -388,6 +409,8 @@ 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: @@ -398,16 +421,14 @@ // 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()); diff --git a/clang-tools-extra/clangd/index/Background.cpp b/clang-tools-extra/clangd/index/Background.cpp --- a/clang-tools-extra/clangd/index/Background.cpp +++ b/clang-tools-extra/clangd/index/Background.cpp @@ -245,6 +245,7 @@ auto AbsolutePath = getAbsolutePath(Cmd); auto FS = FSProvider.getFileSystem(); + FS->setCurrentWorkingDirectory(Cmd.Directory); auto Buf = FS->getBufferForFile(AbsolutePath); if (!Buf) return llvm::errorCodeToError(Buf.getError()); @@ -259,16 +260,17 @@ 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"); diff --git a/clang-tools-extra/clangd/support/FSProvider.h b/clang-tools-extra/clangd/support/FSProvider.h --- a/clang-tools-extra/clangd/support/FSProvider.h +++ b/clang-tools-extra/clangd/support/FSProvider.h @@ -11,6 +11,7 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" +#include namespace clang { namespace clangd { diff --git a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp --- a/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp +++ b/clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp @@ -108,7 +108,10 @@ 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) { @@ -117,10 +120,7 @@ } 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); } @@ -151,8 +151,7 @@ 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); } @@ -1058,7 +1057,11 @@ 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) { @@ -1071,11 +1074,7 @@ 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, @@ -1213,7 +1212,8 @@ 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, @@ -1225,11 +1225,8 @@ #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); diff --git a/clang-tools-extra/clangd/unittests/CompilerTests.cpp b/clang-tools-extra/clangd/unittests/CompilerTests.cpp --- a/clang-tools-extra/clangd/unittests/CompilerTests.cpp +++ b/clang-tools-extra/clangd/unittests/CompilerTests.cpp @@ -19,19 +19,20 @@ 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()); @@ -40,11 +41,11 @@ 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()); diff --git a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp --- a/clang-tools-extra/clangd/unittests/FileIndexTests.cpp +++ b/clang-tools-extra/clangd/unittests/FileIndexTests.cpp @@ -272,14 +272,14 @@ PI.CompileCommand.Filename = FooCpp; PI.CompileCommand.CommandLine = {"clang", "-xc++", FooCpp}; - llvm::StringMap 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" diff --git a/clang-tools-extra/clangd/unittests/HeadersTests.cpp b/clang-tools-extra/clangd/unittests/HeadersTests.cpp --- a/clang-tools-extra/clangd/unittests/HeadersTests.cpp +++ b/clang-tools-extra/clangd/unittests/HeadersTests.cpp @@ -44,20 +44,20 @@ std::unique_ptr setupClang() { auto Cmd = CDB.getCompileCommand(MainFile); assert(static_cast(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(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; diff --git a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp --- a/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp +++ b/clang-tools-extra/clangd/unittests/ParsedASTTests.cpp @@ -249,9 +249,11 @@ } 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; @@ -445,14 +447,15 @@ // 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 "; 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. @@ -484,7 +487,8 @@ // 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); @@ -493,7 +497,7 @@ // 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); @@ -526,7 +530,8 @@ // Build preamble with no includes. TU.Code = R"cpp(#include )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); @@ -537,7 +542,7 @@ // 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); diff --git a/clang-tools-extra/clangd/unittests/PreambleTests.cpp b/clang-tools-extra/clangd/unittests/PreambleTests.cpp --- a/clang-tools-extra/clangd/unittests/PreambleTests.cpp +++ b/clang-tools-extra/clangd/unittests/PreambleTests.cpp @@ -50,6 +50,7 @@ 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. @@ -57,7 +58,7 @@ 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; @@ -73,7 +74,7 @@ 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"; @@ -164,6 +165,7 @@ } TEST(PreamblePatchTest, PatchesPreambleIncludes) { + MockFSProvider FS; IgnoreDiagnostics Diags; auto TU = TestTU::withCode(R"cpp( #include "a.h" @@ -172,7 +174,7 @@ 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 @@ -181,7 +183,7 @@ #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. @@ -199,14 +201,15 @@ } 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, @@ -216,8 +219,9 @@ 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(); diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp @@ -16,6 +16,7 @@ #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" @@ -73,7 +74,7 @@ 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; @@ -149,8 +150,7 @@ std::move(CB)); } - llvm::StringMap Files; - llvm::StringMap Timestamps; + MockFSProvider FSProvider; MockCompilationDatabase CDB; }; @@ -161,10 +161,10 @@ 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); @@ -425,7 +425,7 @@ 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"; @@ -476,7 +476,6 @@ 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); @@ -617,8 +616,8 @@ 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() {} @@ -686,8 +685,8 @@ 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" @@ -715,7 +714,7 @@ 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); @@ -761,11 +760,12 @@ 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 diff --git a/clang-tools-extra/clangd/unittests/TestFS.h b/clang-tools-extra/clangd/unittests/TestFS.h --- a/clang-tools-extra/clangd/unittests/TestFS.h +++ b/clang-tools-extra/clangd/unittests/TestFS.h @@ -31,11 +31,12 @@ class MockFSProvider : public FileSystemProvider { public: IntrusiveRefCntPtr getFileSystem() const override { - return buildTestFS(Files); + return buildTestFS(Files, Timestamps); } // If relative paths are used, they are resolved with testPath(). llvm::StringMap Files; + llvm::StringMap Timestamps; }; // A Compilation database that returns a fixed set of compile flags. diff --git a/clang-tools-extra/clangd/unittests/TestTU.h b/clang-tools-extra/clangd/unittests/TestTU.h --- a/clang-tools-extra/clangd/unittests/TestTU.h +++ b/clang-tools-extra/clangd/unittests/TestTU.h @@ -19,6 +19,7 @@ #include "Compiler.h" #include "ParsedAST.h" +#include "TestFS.h" #include "index/Index.h" #include "support/Path.h" #include "llvm/ADT/StringMap.h" @@ -69,7 +70,7 @@ // Suppress this behavior by adding an 'error-ok' comment to the code. ParsedAST build() const; std::shared_ptr preamble() const; - ParseInputs inputs() const; + ParseInputs inputs(MockFSProvider &FSProvider) const; SymbolSlab headerSymbols() const; RefSlab headerRefs() const; std::unique_ptr index() const; diff --git a/clang-tools-extra/clangd/unittests/TestTU.cpp b/clang-tools-extra/clangd/unittests/TestTU.cpp --- a/clang-tools-extra/clangd/unittests/TestTU.cpp +++ b/clang-tools-extra/clangd/unittests/TestTU.cpp @@ -20,7 +20,7 @@ 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"); @@ -29,10 +29,10 @@ // guard without messing up offsets). In this case, use an intermediate file. std::string ThunkContents = "#import \"" + FullHeaderName + "\"\n"; - llvm::StringMap 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; @@ -54,7 +54,7 @@ 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; @@ -67,7 +67,8 @@ } std::shared_ptr 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."); @@ -77,7 +78,8 @@ } 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.");