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 @@ -183,7 +183,8 @@ Opts.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults(); // FIXME: call tidy options builder on the worker thread, it can do IO. if (GetClangTidyOptions) - Opts.ClangTidyOpts = GetClangTidyOptions(*FSProvider.getFileSystem(), File); + Opts.ClangTidyOpts = + GetClangTidyOptions(*FSProvider.getFileSystem("."), File); Opts.SuggestMissingIncludes = SuggestMissingIncludes; // Compile command is set asynchronously during update, as it can be slow. @@ -317,9 +318,9 @@ llvm::Expected CursorPos = positionToOffset(Code, Pos); if (!CursorPos) return CursorPos.takeError(); - auto FS = FSProvider.getFileSystem(); auto Style = format::getStyle(format::DefaultFormatStyle, File, - format::DefaultFallbackStyle, Code, FS.get()); + format::DefaultFallbackStyle, Code, + FSProvider.getFileSystem(".").get()); if (!Style) return Style.takeError(); @@ -397,7 +398,9 @@ if (Opts.WantFormat) { auto Style = getFormatStyleForFile( File, InpAST->Inputs.Contents, - InpAST->Inputs.FSProvider->getFileSystem().get()); + InpAST->Inputs.FSProvider + ->getFileSystem(InpAST->Inputs.CompileCommand.Directory) + .get()); llvm::Error Err = llvm::Error::success(); for (auto &E : *Edits) Err = @@ -473,38 +476,39 @@ static constexpr trace::Metric TweakAttempt( "tweak_attempt", trace::Metric::Counter, "tweak_id"); TweakAttempt.record(1, TweakID); - auto Action = - [File = File.str(), Sel, TweakID = TweakID.str(), CB = std::move(CB), - FS = FSProvider.getFileSystem()](Expected InpAST) mutable { - if (!InpAST) - return CB(InpAST.takeError()); - auto Selections = tweakSelection(Sel, *InpAST); - if (!Selections) - return CB(Selections.takeError()); - llvm::Optional> Effect; - // Try each selection, take the first one that prepare()s. - // If they all fail, Effect will hold get the last error. - for (const auto &Selection : *Selections) { - auto T = prepareTweak(TweakID, *Selection); - if (T) { - Effect = (*T)->apply(*Selection); - break; - } - Effect = T.takeError(); - } - assert(Effect.hasValue() && "Expected at least one selection"); - if (*Effect) { - // Tweaks don't apply clang-format, do that centrally here. - for (auto &It : (*Effect)->ApplyEdits) { - Edit &E = It.second; - format::FormatStyle Style = - getFormatStyleForFile(File, E.InitialCode, FS.get()); - if (llvm::Error Err = reformatEdit(E, Style)) - elog("Failed to format {0}: {1}", It.first(), std::move(Err)); - } - } - return CB(std::move(*Effect)); - }; + auto Action = [File = File.str(), Sel, TweakID = TweakID.str(), + CB = std::move(CB), + this](Expected InpAST) mutable { + if (!InpAST) + return CB(InpAST.takeError()); + auto Selections = tweakSelection(Sel, *InpAST); + if (!Selections) + return CB(Selections.takeError()); + llvm::Optional> Effect; + // Try each selection, take the first one that prepare()s. + // If they all fail, Effect will hold get the last error. + for (const auto &Selection : *Selections) { + auto T = prepareTweak(TweakID, *Selection); + if (T) { + Effect = (*T)->apply(*Selection); + break; + } + Effect = T.takeError(); + } + assert(Effect.hasValue() && "Expected at least one selection"); + auto FS = FSProvider.getFileSystem(InpAST->Inputs.CompileCommand.Directory); + if (*Effect) { + // Tweaks don't apply clang-format, do that centrally here. + for (auto &It : (*Effect)->ApplyEdits) { + Edit &E = It.second; + format::FormatStyle Style = + getFormatStyleForFile(File, E.InitialCode, FS.get()); + if (llvm::Error Err = reformatEdit(E, Style)) + elog("Failed to format {0}: {1}", It.first(), std::move(Err)); + } + } + return CB(std::move(*Effect)); + }; WorkScheduler.runWithAST("ApplyTweak", File, std::move(Action)); } @@ -549,7 +553,7 @@ // 2) if 1) fails, we use the AST&Index approach, it is slower but supports // different code layout. if (auto CorrespondingFile = getCorrespondingHeaderOrSource( - std::string(Path), FSProvider.getFileSystem())) + std::string(Path), FSProvider.getFileSystem("."))) return CB(std::move(CorrespondingFile)); auto Action = [Path = Path.str(), CB = std::move(CB), this](llvm::Expected InpAST) mutable { @@ -565,7 +569,7 @@ llvm::ArrayRef Ranges) { // Call clang-format. format::FormatStyle Style = - getFormatStyleForFile(File, Code, FSProvider.getFileSystem().get()); + getFormatStyleForFile(File, Code, FSProvider.getFileSystem(".").get()); tooling::Replacements IncludeReplaces = format::sortIncludes(Style, Code, Ranges, File); auto Changed = tooling::applyAllReplacements(Code, IncludeReplaces); @@ -597,9 +601,11 @@ this](llvm::Expected InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); - format::FormatStyle Style = - getFormatStyleForFile(File, InpAST->Inputs.Contents, - InpAST->Inputs.FSProvider->getFileSystem().get()); + format::FormatStyle Style = getFormatStyleForFile( + File, InpAST->Inputs.Contents, + InpAST->Inputs.FSProvider + ->getFileSystem(InpAST->Inputs.CompileCommand.Directory) + .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 @@ -1113,12 +1113,10 @@ // NOTE: we must call BeginSourceFile after prepareCompilerInstance. Otherwise // the remapped buffers do not get freed. llvm::IntrusiveRefCntPtr VFS = - Input.ParseInput.FSProvider->getFileSystem(); + Input.ParseInput.FSProvider->getFileSystem( + Input.ParseInput.CompileCommand.Directory); if (Input.Preamble.StatCache) VFS = Input.Preamble.StatCache->getConsumingFS(std::move(VFS)); - if (VFS->setCurrentWorkingDirectory( - Input.ParseInput.CompileCommand.Directory)) - elog("Couldn't set working directory during code completion"); auto Clang = prepareCompilerInstance( std::move(CI), !CompletingInPreamble ? &Input.Preamble.Preamble : nullptr, std::move(ContentsBuffer), std::move(VFS), IgnoreDiags); @@ -1294,7 +1292,9 @@ IsUsingDeclaration = Recorder->CCContext.isUsingDeclaration(); auto Style = getFormatStyleForFile( SemaCCInput.FileName, SemaCCInput.ParseInput.Contents, - SemaCCInput.ParseInput.FSProvider->getFileSystem().get()); + SemaCCInput.ParseInput.FSProvider + ->getFileSystem(SemaCCInput.ParseInput.CompileCommand.Directory) + .get()); const auto NextToken = Lexer::findNextToken( Recorder->CCSema->getPreprocessor().getCodeCompletionLoc(), Recorder->CCSema->getSourceManager(), Recorder->CCSema->LangOpts); @@ -1785,7 +1785,8 @@ return (!Preamble || Opts.RunParser == CodeCompleteOptions::NeverParse) ? std::move(Flow).runWithoutSema( ParseInput.Contents, *Offset, - ParseInput.FSProvider->getFileSystem()) + ParseInput.FSProvider->getFileSystem( + ParseInput.CompileCommand.Directory)) : 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.cpp b/clang-tools-extra/clangd/Compiler.cpp --- a/clang-tools-extra/clangd/Compiler.cpp +++ b/clang-tools-extra/clangd/Compiler.cpp @@ -47,13 +47,7 @@ for (const auto &S : Inputs.CompileCommand.CommandLine) ArgStrs.push_back(S.c_str()); - 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. - } - + auto VFS = Inputs.FSProvider->getFileSystem(Inputs.CompileCommand.Directory); llvm::IntrusiveRefCntPtr CommandLineDiagsEngine = CompilerInstance::createDiagnostics(new DiagnosticOptions, &D, false); std::unique_ptr CI = createInvocationFromCommandLine( 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,14 +248,9 @@ trace::Span Tracer("BuildAST"); SPAN_ATTACH(Tracer, "File", Filename); - auto VFS = Inputs.FSProvider->getFileSystem(); + auto VFS = Inputs.FSProvider->getFileSystem(Inputs.CompileCommand.Directory); if (Preamble && Preamble->StatCache) VFS = Preamble->StatCache->getConsumingFS(std::move(VFS)); - 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. - } assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want 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 @@ -236,7 +236,7 @@ VFSProvider(llvm::IntrusiveRefCntPtr FS) : VFS(std::move(FS)) {} llvm::IntrusiveRefCntPtr - getFileSystem() const override { + getFileSystem(PathRef) const override { return VFS; } @@ -354,13 +354,7 @@ CI.getPreprocessorOpts().WriteCommentListToPCH = false; CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback); - 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. - } - + auto VFS = Inputs.FSProvider->getFileSystem(Inputs.CompileCommand.Directory); llvm::SmallString<32> AbsFileName(FileName); VFS->makeAbsolute(AbsFileName); auto StatCache = std::make_unique(AbsFileName); @@ -397,8 +391,7 @@ llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName); auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0); - auto VFS = Inputs.FSProvider->getFileSystem(); - VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory); + auto VFS = Inputs.FSProvider->getFileSystem(Inputs.CompileCommand.Directory); return compileCommandsAreEqual(Inputs.CompileCommand, Preamble.CompileCommand) && Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds, @@ -425,8 +418,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()); + auto VFS = Baseline.StatCache->getConsumingFS( + Modified.FSProvider->getFileSystem(Modified.CompileCommand.Directory)); // 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: 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 @@ -244,8 +244,7 @@ SPAN_ATTACH(Tracer, "file", Cmd.Filename); auto AbsolutePath = getAbsolutePath(Cmd); - auto FS = FSProvider.getFileSystem(); - FS->setCurrentWorkingDirectory(Cmd.Directory); + auto FS = FSProvider.getFileSystem(Cmd.Directory); auto Buf = FS->getBufferForFile(AbsolutePath); if (!Buf) return llvm::errorCodeToError(Buf.getError()); @@ -382,7 +381,7 @@ Rebuilder.loadedShard(LoadedShards); Rebuilder.doneLoading(); - auto FS = FSProvider.getFileSystem(); + auto FS = FSProvider.getFileSystem("."); llvm::DenseSet TUsToIndex; // We'll accept data from stale shards, but ensure the files get reindexed // soon. 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 @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FSPROVIDER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FSPROVIDER_H +#include "Path.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/VirtualFileSystem.h" #include @@ -25,14 +26,15 @@ /// Context::current() will be the context passed to the clang entrypoint, /// such as addDocument(), and will also be propagated to result callbacks. /// Embedders may use this to isolate filesystem accesses. + /// Will try to set curret working directory to \p CWD. virtual llvm::IntrusiveRefCntPtr - getFileSystem() const = 0; + getFileSystem(PathRef CWD) const = 0; }; class RealFileSystemProvider : public FileSystemProvider { public: llvm::IntrusiveRefCntPtr - getFileSystem() const override; + getFileSystem(PathRef CWD) const override; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/support/FSProvider.cpp b/clang-tools-extra/clangd/support/FSProvider.cpp --- a/clang-tools-extra/clangd/support/FSProvider.cpp +++ b/clang-tools-extra/clangd/support/FSProvider.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "support/FSProvider.h" +#include "Logger.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" @@ -72,12 +73,14 @@ } // namespace llvm::IntrusiveRefCntPtr -clang::clangd::RealFileSystemProvider::getFileSystem() const { +clang::clangd::RealFileSystemProvider::getFileSystem(PathRef CWD) const { + auto FS = llvm::vfs::createPhysicalFileSystem(); + if (FS->setCurrentWorkingDirectory(CWD)) + elog("Failed to set CWD in RealFileSystemProvider"); // Avoid using memory-mapped files. // FIXME: Try to use a similar approach in Sema instead of relying on // propagation of the 'isVolatile' flag through all layers. - return new VolatileFileSystem( - llvm::vfs::createPhysicalFileSystem().release()); + return new VolatileFileSystem(FS.release()); } } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -21,6 +21,7 @@ #include "clang/Basic/Version.h" #include "clang/Format/Format.h" #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -717,7 +718,7 @@ ClangTidyOptProvider = std::make_unique( tidy::ClangTidyGlobalOptions(), /* Default */ EmptyDefaults, - /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem()); + /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem(".")); Opts.GetClangTidyOptions = [&](llvm::vfs::FileSystem &, llvm::StringRef File) { // This function must be thread-safe and tidy option providers are not. diff --git a/clang-tools-extra/clangd/unittests/ClangdTests.cpp b/clang-tools-extra/clangd/unittests/ClangdTests.cpp --- a/clang-tools-extra/clangd/unittests/ClangdTests.cpp +++ b/clang-tools-extra/clangd/unittests/ClangdTests.cpp @@ -15,6 +15,7 @@ #include "SyncAPI.h" #include "TestFS.h" #include "URI.h" +#include "support/Path.h" #include "support/Threading.h" #include "clang/Config/config.h" #include "clang/Sema/CodeCompleteConsumer.h" @@ -270,7 +271,8 @@ TEST_F(ClangdVFSTest, PropagatesContexts) { static Key Secret; struct FSProvider : public FileSystemProvider { - IntrusiveRefCntPtr getFileSystem() const override { + IntrusiveRefCntPtr + getFileSystem(PathRef) const override { Got = Context::current().getExisting(Secret); return buildTestFS({}); } @@ -925,7 +927,8 @@ ListenStatsFSProvider(llvm::StringMap &CountStats) : CountStats(CountStats) {} - IntrusiveRefCntPtr getFileSystem() const override { + IntrusiveRefCntPtr + getFileSystem(PathRef) const override { class ListenStatVFS : public llvm::vfs::ProxyFileSystem { public: ListenStatVFS(IntrusiveRefCntPtr FS, diff --git a/clang-tools-extra/clangd/unittests/FSTests.cpp b/clang-tools-extra/clangd/unittests/FSTests.cpp --- a/clang-tools-extra/clangd/unittests/FSTests.cpp +++ b/clang-tools-extra/clangd/unittests/FSTests.cpp @@ -21,7 +21,6 @@ Files["y"] = ""; Files["main"] = ""; auto FS = buildTestFS(Files); - FS->setCurrentWorkingDirectory(testRoot()); PreambleFileStatusCache StatCache(testPath("main")); auto ProduceFS = StatCache.getProducingFS(FS); 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 @@ -52,8 +52,7 @@ 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 VFS = FS.getFileSystem(Cmd->Directory); auto Clang = prepareCompilerInstance( std::move(CI), /*Preamble=*/nullptr, llvm::MemoryBuffer::getMemBuffer(FS.Files[MainFile], MainFile), 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 @@ -70,11 +70,11 @@ // We don't run PP directly over the patch cotents to test production // behaviour. auto Bounds = Lexer::ComputePreamble(ModifiedContents, *CI->getLangOpts()); - auto Clang = - prepareCompilerInstance(std::move(CI), &BaselinePreamble->Preamble, - llvm::MemoryBuffer::getMemBufferCopy( - ModifiedContents.slice(0, Bounds.Size).str()), - PI.FSProvider->getFileSystem(), Diags); + auto Clang = prepareCompilerInstance( + std::move(CI), &BaselinePreamble->Preamble, + llvm::MemoryBuffer::getMemBufferCopy( + ModifiedContents.slice(0, Bounds.Size).str()), + PI.FSProvider->getFileSystem(PI.CompileCommand.Directory), Diags); PreprocessOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { ADD_FAILURE() << "failed begin source file"; 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 @@ -30,10 +30,17 @@ // A VFS provider that returns TestFSes containing a provided set of files. class MockFSProvider : public FileSystemProvider { public: - IntrusiveRefCntPtr getFileSystem() const override { + IntrusiveRefCntPtr getFileSystem() const { return buildTestFS(Files, Timestamps); } + IntrusiveRefCntPtr + getFileSystem(PathRef CWD) const override { + auto FS = getFileSystem(); + FS->setCurrentWorkingDirectory(CWD); + return FS; + } + // If relative paths are used, they are resolved with testPath(). llvm::StringMap Files; llvm::StringMap Timestamps;