2019-05-07 15:11:56 +08:00
|
|
|
#include "refactor/Rename.h"
|
|
|
|
#include "clang/Tooling/Refactoring/RefactoringResultConsumer.h"
|
|
|
|
#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class RefactoringResultCollector final
|
|
|
|
: public tooling::RefactoringResultConsumer {
|
|
|
|
public:
|
|
|
|
void handleError(llvm::Error Err) override {
|
|
|
|
assert(!Result.hasValue());
|
|
|
|
Result = std::move(Err);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Using the handle(SymbolOccurrences) from parent class.
|
|
|
|
using tooling::RefactoringResultConsumer::handle;
|
|
|
|
|
|
|
|
void handle(tooling::AtomicChanges SourceReplacements) override {
|
|
|
|
assert(!Result.hasValue());
|
|
|
|
Result = std::move(SourceReplacements);
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Optional<llvm::Expected<tooling::AtomicChanges>> Result;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Expand a DiagnosticError to make it print-friendly (print the detailed
|
|
|
|
// message, rather than "clang diagnostic").
|
|
|
|
llvm::Error expandDiagnostics(llvm::Error Err, DiagnosticsEngine &DE) {
|
|
|
|
if (auto Diag = DiagnosticError::take(Err)) {
|
|
|
|
llvm::cantFail(std::move(Err));
|
|
|
|
SmallVector<char, 128> DiagMessage;
|
|
|
|
Diag->second.EmitToString(DE, DiagMessage);
|
|
|
|
return llvm::make_error<llvm::StringError>(DiagMessage,
|
|
|
|
llvm::inconvertibleErrorCode());
|
|
|
|
}
|
|
|
|
return Err;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
llvm::Expected<tooling::Replacements>
|
|
|
|
renameWithinFile(ParsedAST &AST, llvm::StringRef File, Position Pos,
|
|
|
|
llvm::StringRef NewName) {
|
|
|
|
RefactoringResultCollector ResultCollector;
|
|
|
|
ASTContext &ASTCtx = AST.getASTContext();
|
|
|
|
const SourceManager &SourceMgr = ASTCtx.getSourceManager();
|
|
|
|
SourceLocation SourceLocationBeg =
|
|
|
|
clangd::getBeginningOfIdentifier(AST, Pos, SourceMgr.getMainFileID());
|
|
|
|
tooling::RefactoringRuleContext Context(ASTCtx.getSourceManager());
|
|
|
|
Context.setASTContext(ASTCtx);
|
|
|
|
auto Rename = clang::tooling::RenameOccurrences::initiate(
|
|
|
|
Context, SourceRange(SourceLocationBeg), NewName);
|
|
|
|
if (!Rename)
|
|
|
|
return expandDiagnostics(Rename.takeError(), ASTCtx.getDiagnostics());
|
|
|
|
|
|
|
|
Rename->invoke(ResultCollector, Context);
|
|
|
|
|
|
|
|
assert(ResultCollector.Result.hasValue());
|
|
|
|
if (!ResultCollector.Result.getValue())
|
|
|
|
return expandDiagnostics(ResultCollector.Result->takeError(),
|
|
|
|
ASTCtx.getDiagnostics());
|
|
|
|
|
|
|
|
tooling::Replacements FilteredChanges;
|
|
|
|
// Right now we only support renaming the main file, so we
|
|
|
|
// drop replacements not for the main file. In the future, we might
|
|
|
|
// also support rename with wider scope.
|
2019-05-07 15:45:41 +08:00
|
|
|
// Rename sometimes returns duplicate edits (which is a bug). A side-effect of
|
|
|
|
// adding them to a single Replacements object is these are deduplicated.
|
2019-05-07 15:11:56 +08:00
|
|
|
for (const tooling::AtomicChange &Change : ResultCollector.Result->get()) {
|
|
|
|
for (const auto &Rep : Change.getReplacements()) {
|
|
|
|
if (Rep.getFilePath() == File)
|
|
|
|
cantFail(FilteredChanges.add(Rep));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return FilteredChanges;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|