[clangd] Use dirty filesystem when performing cross file tweaks

Cross file tweaks can now use the dirty buffer contents easily when performing cross file effects.
This can be noted on the DefineOutline tweak, now working when the target file is unsaved

Reviewed By: sammccall

Differential Revision: https://reviews.llvm.org/D93978
This commit is contained in:
Nathan James 2021-04-20 17:13:44 +01:00
parent b2e72cd38d
commit 6b4e8f82a3
No known key found for this signature in database
GPG Key ID: CC007AFCDA90AA5F
7 changed files with 32 additions and 20 deletions

View File

@ -549,7 +549,8 @@ void ClangdServer::rename(PathRef File, Position Pos, llvm::StringRef NewName,
// May generate several candidate selections, due to SelectionTree ambiguity.
// vector of pointers because GCC doesn't like non-copyable Selection.
static llvm::Expected<std::vector<std::unique_ptr<Tweak::Selection>>>
tweakSelection(const Range &Sel, const InputsAndAST &AST) {
tweakSelection(const Range &Sel, const InputsAndAST &AST,
llvm::vfs::FileSystem *FS) {
auto Begin = positionToOffset(AST.Inputs.Contents, Sel.start);
if (!Begin)
return Begin.takeError();
@ -561,7 +562,7 @@ tweakSelection(const Range &Sel, const InputsAndAST &AST) {
AST.AST.getASTContext(), AST.AST.getTokens(), *Begin, *End,
[&](SelectionTree T) {
Result.push_back(std::make_unique<Tweak::Selection>(
AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T)));
AST.Inputs.Index, AST.AST, *Begin, *End, std::move(T), FS));
return false;
});
assert(!Result.empty() && "Expected at least one SelectionTree");
@ -580,7 +581,7 @@ void ClangdServer::enumerateTweaks(
Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
auto Selections = tweakSelection(Sel, *InpAST);
auto Selections = tweakSelection(Sel, *InpAST, /*FS=*/nullptr);
if (!Selections)
return CB(Selections.takeError());
std::vector<TweakRef> Res;
@ -618,7 +619,8 @@ void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
this](Expected<InputsAndAST> InpAST) mutable {
if (!InpAST)
return CB(InpAST.takeError());
auto Selections = tweakSelection(Sel, *InpAST);
auto FS = DirtyFS->view(llvm::None);
auto Selections = tweakSelection(Sel, *InpAST, FS.get());
if (!Selections)
return CB(Selections.takeError());
llvm::Optional<llvm::Expected<Tweak::Effect>> Effect;

View File

@ -61,9 +61,10 @@ getAllTweaks(const FeatureModuleSet *Modules) {
Tweak::Selection::Selection(const SymbolIndex *Index, ParsedAST &AST,
unsigned RangeBegin, unsigned RangeEnd,
SelectionTree ASTSelection)
SelectionTree ASTSelection,
llvm::vfs::FileSystem *FS)
: Index(Index), AST(&AST), SelectionBegin(RangeBegin),
SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)) {
SelectionEnd(RangeEnd), ASTSelection(std::move(ASTSelection)), FS(FS) {
auto &SM = AST.getSourceManager();
Code = SM.getBufferData(SM.getMainFileID());
Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin);

View File

@ -51,7 +51,8 @@ public:
/// 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.
@ -67,6 +68,9 @@ public:
unsigned SelectionEnd;
/// The AST nodes that were selected.
SelectionTree ASTSelection;
/// File system used to access source code (for cross-file tweaks).
/// This is only populated when applying a tweak, not during prepare.
llvm::vfs::FileSystem *FS = nullptr;
// FIXME: provide a way to get sources and ASTs for other files.
};

View File

@ -64,9 +64,8 @@ const FunctionDecl *getSelectedFunction(const SelectionTree::Node *SelNode) {
llvm::Optional<Path> getSourceFile(llvm::StringRef FileName,
const Tweak::Selection &Sel) {
if (auto Source = getCorrespondingHeaderOrSource(
FileName,
&Sel.AST->getSourceManager().getFileManager().getVirtualFileSystem()))
assert(Sel.FS);
if (auto Source = getCorrespondingHeaderOrSource(FileName, Sel.FS))
return *Source;
return getCorrespondingHeaderOrSource(FileName, *Sel.AST, Sel.Index);
}
@ -414,12 +413,11 @@ public:
return error("Couldn't get absolute path for main file.");
auto CCFile = getSourceFile(*MainFileName, Sel);
if (!CCFile)
return error("Couldn't find a suitable implementation file.");
auto &FS =
Sel.AST->getSourceManager().getFileManager().getVirtualFileSystem();
auto Buffer = FS.getBufferForFile(*CCFile);
assert(Sel.FS && "FS Must be set in apply");
auto Buffer = Sel.FS->getBufferForFile(*CCFile);
// FIXME: Maybe we should consider creating the implementation file if it
// doesn't exist?
if (!Buffer)

View File

@ -210,7 +210,8 @@ public:
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, Opts.FeatureModules)) {
auto Result = T->apply(Selection);

View File

@ -47,7 +47,8 @@ TEST(FeatureModulesTest, ContributesTweak) {
auto Tree =
SelectionTree::createRight(AST.getASTContext(), AST.getTokens(), 0, 0);
auto Actual = prepareTweak(
TweakID, Tweak::Selection(nullptr, AST, 0, 0, std::move(Tree)), &Set);
TweakID, Tweak::Selection(nullptr, AST, 0, 0, std::move(Tree), nullptr),
&Set);
ASSERT_TRUE(bool(Actual));
EXPECT_EQ(Actual->get()->id(), TweakID);
}

View File

@ -68,13 +68,14 @@ std::pair<unsigned, unsigned> rangeOrPoint(const Annotations &A) {
// Returns None if and only if prepare() failed.
llvm::Optional<llvm::Expected<Tweak::Effect>>
applyTweak(ParsedAST &AST, const Annotations &Input, StringRef TweakID,
const SymbolIndex *Index) {
const SymbolIndex *Index, llvm::vfs::FileSystem *FS) {
auto Range = rangeOrPoint(Input);
llvm::Optional<llvm::Expected<Tweak::Effect>> Result;
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),
FS);
if (auto T = prepareTweak(TweakID, S, nullptr)) {
Result = (*T)->apply(S);
return true;
@ -98,7 +99,9 @@ MATCHER_P7(TweakIsAvailable, TweakID, Ctx, Header, ExtraArgs, ExtraFiles, Index,
TU.ExtraArgs = ExtraArgs;
TU.AdditionalFiles = std::move(ExtraFiles);
ParsedAST AST = TU.build();
auto Result = applyTweak(AST, Input, TweakID, Index);
auto Result = applyTweak(
AST, Input, TweakID, Index,
&AST.getSourceManager().getFileManager().getVirtualFileSystem());
// We only care if prepare() succeeded, but must handle Errors.
if (Result && !*Result)
consumeError(Result->takeError());
@ -119,7 +122,9 @@ std::string TweakTest::apply(llvm::StringRef MarkedCode,
TU.ExtraArgs = ExtraArgs;
ParsedAST AST = TU.build();
auto Result = applyTweak(AST, Input, TweakID, Index.get());
auto Result = applyTweak(
AST, Input, TweakID, Index.get(),
&AST.getSourceManager().getFileManager().getVirtualFileSystem());
if (!Result)
return "unavailable";
if (!*Result)