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 @@ -49,6 +49,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/VirtualFileSystem.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -429,7 +430,8 @@ // May generate several candidate selections, due to SelectionTree ambiguity. // vector of pointers because GCC doesn't like non-copyable Selection. static llvm::Expected>> -tweakSelection(const Range &Sel, const InputsAndAST &AST) { +tweakSelection(const Range &Sel, const InputsAndAST &AST, + llvm::vfs::FileSystem *VFS = nullptr) { auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start); if (!Begin) return Begin.takeError(); @@ -441,7 +443,7 @@ AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End, [&](SelectionTree T) { Result.push_back(std::make_unique( - AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T))); + AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T), VFS)); return false; }); assert(!Result.empty() && "Expected at least one SelectionTree"); @@ -494,16 +496,17 @@ TweakAttempt.record(1, TweakID); auto Action = [File = File.str(), Sel, TweakID = TweakID.str(), CB = std::move(CB), + FS(WorkScheduler.overlayFileContents(TFS.view(llvm::None))), this](Expected InpAST) mutable { if (!InpAST) return CB(InpAST.takeError()); - auto Selections = tweakSelection(Sel, *InpAST); + auto Selections = tweakSelection(Sel, *InpAST, FS.get()); 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) { + for (auto &Selection : *Selections) { auto T = prepareTweak(TweakID, *Selection); if (T) { Effect = (*T)->apply(*Selection); diff --git a/clang-tools-extra/clangd/TUScheduler.h b/clang-tools-extra/clangd/TUScheduler.h --- a/clang-tools-extra/clangd/TUScheduler.h +++ b/clang-tools-extra/clangd/TUScheduler.h @@ -239,6 +239,15 @@ /// Returns a snapshot of all file buffer contents, per last update(). llvm::StringMap getAllFileContents() const; + /// Returns a Filesystem snapshot of all file buffer contents, per last + /// update(). + llvm::IntrusiveRefCntPtr getAllFileContentsFS() const; + + /// Returns a Snapshot of all file buffer contents in a Filesystem overlayed + /// ontop of \p Base. + llvm::IntrusiveRefCntPtr overlayFileContents( + llvm::IntrusiveRefCntPtr Base) const; + /// Schedule an async task with no dependencies. /// Path may be empty (it is used only to set the Context). void run(llvm::StringRef Name, llvm::StringRef Path, diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -1456,5 +1456,26 @@ MT.detail(Elem.first()).child("ast").addUsage(Elem.second.UsedBytesAST); } } + +llvm::IntrusiveRefCntPtr +TUScheduler::getAllFileContentsFS() const { + // FIXME: Copying the files is needlessly expensive and grows according to how + // many files are being tracked, it may be worthwhile to create a Filesystem + // that references this and only copy the buffers lazily. + auto *InMemoryFS = new llvm::vfs::InMemoryFileSystem; + for (const auto &File : Files) + InMemoryFS->addFile( + File.first(), 0, + llvm::MemoryBuffer::getMemBufferCopy(File.getValue()->Contents)); + return InMemoryFS; +} + +llvm::IntrusiveRefCntPtr +TUScheduler::overlayFileContents( + llvm::IntrusiveRefCntPtr Base) const { + auto *OverlayFS = new llvm::vfs::OverlayFileSystem(std::move(Base)); + OverlayFS->pushOverlay(getAllFileContentsFS()); + return OverlayFS; +} } // namespace clangd } // namespace clang diff --git a/clang-tools-extra/clangd/refactor/Tweak.h b/clang-tools-extra/clangd/refactor/Tweak.h --- a/clang-tools-extra/clangd/refactor/Tweak.h +++ b/clang-tools-extra/clangd/refactor/Tweak.h @@ -30,6 +30,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" +#include "llvm/Support/VirtualFileSystem.h" #include namespace clang { @@ -48,7 +49,8 @@ /// Input to prepare and apply tweaks. struct Selection { Selection(const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin, - unsigned RangeEnd, SelectionTree ASTSelection); + unsigned RangeEnd, SelectionTree ASTSelection, + llvm::vfs::FileSystem *VFS); /// The text of the active document. llvm::StringRef Code; /// The Index for handling codebase related queries. @@ -64,6 +66,8 @@ unsigned SelectionEnd; /// The AST nodes that were selected. SelectionTree ASTSelection; + /// The file system that should be queried for cross file tweaks. + llvm::vfs::FileSystem *VFS = nullptr; // FIXME: provide a way to get sources and ASTs for other files. }; diff --git a/clang-tools-extra/clangd/refactor/Tweak.cpp b/clang-tools-extra/clangd/refactor/Tweak.cpp --- a/clang-tools-extra/clangd/refactor/Tweak.cpp +++ b/clang-tools-extra/clangd/refactor/Tweak.cpp @@ -47,9 +47,10 @@ Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST, unsigned RangeBegin, unsigned RangeEnd, - SelectionTree ASTSelection) + SelectionTree ASTSelection, + llvm::vfs::FileSystem *VFS) : Index(Index), AST(&AST), SelectionBegin(RangeBegin), - SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)) { + SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), VFS(VFS) { auto &SM = AST.getSourceManager(); Code = SM.getBufferData(SM.getMainFileID()); Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin); diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineOutline.cpp @@ -36,6 +36,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" +#include "llvm/Support/VirtualFileSystem.h" #include #include @@ -63,10 +64,9 @@ } llvm::Optional getSourceFile(llvm::StringRef FileName, - const Tweak::Selection &Sel) { - if (auto Source = getCorrespondingHeaderOrSource( - FileName, - &Sel.AST->getSourceManager().getFileManager().getVirtualFileSystem())) + const Tweak::Selection &Sel, + llvm::vfs::FileSystem *FS) { + if (auto Source = getCorrespondingHeaderOrSource(FileName, FS)) return *Source; return getCorrespondingHeaderOrSource(FileName, *Sel.AST, Sel.Index); } @@ -403,13 +403,18 @@ if (!MainFileName) return error("Couldn't get absolute path for main file."); - auto CCFile = getSourceFile(*MainFileName, Sel); + // If we have a Filesystem attached to the Selection, use that, otherwise + // fallback to the SourceManagar Filesystem. + auto *FS = Sel.VFS ? Sel.VFS + : &Sel.AST->getSourceManager() + .getFileManager() + .getVirtualFileSystem(); + + auto CCFile = getSourceFile(*MainFileName, Sel, FS); + if (!CCFile) return error("Couldn't find a suitable implementation file."); - - auto &FS = - Sel.AST->getSourceManager().getFileManager().getVirtualFileSystem(); - auto Buffer = FS.getBufferForFile(*CCFile); + auto Buffer = FS->getBufferForFile(*CCFile); // FIXME: Maybe we should consider creating the implementation file if it // doesn't exist? if (!Buffer) diff --git a/clang-tools-extra/clangd/tool/Check.cpp b/clang-tools-extra/clangd/tool/Check.cpp --- a/clang-tools-extra/clangd/tool/Check.cpp +++ b/clang-tools-extra/clangd/tool/Check.cpp @@ -202,7 +202,8 @@ vlog(" {0} {1}", Pos, Tok.text(AST->getSourceManager())); auto Tree = SelectionTree::createRight(AST->getASTContext(), AST->getTokens(), Start, End); - Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree)); + Tweak::Selection Selection(&Index, *AST, Start, End, std::move(Tree), + nullptr); for (const auto &T : prepareTweaks(Selection, Opts.TweakFilter)) { auto Result = T->apply(Selection); if (!Result) { diff --git a/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp b/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/TweakTesting.cpp @@ -74,7 +74,8 @@ SelectionTree::createEach(AST.getASTContext(), AST.getTokens(), Range.first, Range.second, [&](SelectionTree ST) { Tweak::Selection S(Index, AST, Range.first, - Range.second, std::move(ST)); + Range.second, std::move(ST), + nullptr); if (auto T = prepareTweak(TweakID, S)) { Result = (*T)->apply(S); return true;