diff options
author | Alex Lorenz <arphaman@gmail.com> | 2017-10-13 19:42:05 +0000 |
---|---|---|
committer | Alex Lorenz <arphaman@gmail.com> | 2017-10-13 19:42:05 +0000 |
commit | f5386a25ef684adc9fdb6278a106a250f3d9bac7 (patch) | |
tree | 2824511416ba9b6aa6d904ae005491226b92751d /tools | |
parent | 6ac84e99d98cd9d1cd9681f954f005e60f5a6193 (diff) |
[clang-refactor] Apply source replacements
This commit actually brings clang-refactor to a usable state as it can now
apply the refactoring changes to source files.
The -selection option is now also fully supported.
Differential Revision: https://reviews.llvm.org/D38402
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@315738 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools')
-rw-r--r-- | tools/clang-refactor/ClangRefactor.cpp | 90 |
1 files changed, 84 insertions, 6 deletions
diff --git a/tools/clang-refactor/ClangRefactor.cpp b/tools/clang-refactor/ClangRefactor.cpp index b1a1ebdf68..72b3dab892 100644 --- a/tools/clang-refactor/ClangRefactor.cpp +++ b/tools/clang-refactor/ClangRefactor.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "TestSupport.h" +#include "clang/Frontend/CommandLineSourceLoc.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Refactoring.h" @@ -54,7 +55,7 @@ public: /// Prints any additional state associated with the selection argument to /// the given output stream. - virtual void print(raw_ostream &OS) = 0; + virtual void print(raw_ostream &OS) {} /// Returns a replacement refactoring result consumer (if any) that should /// consume the results of a refactoring operation. @@ -99,6 +100,41 @@ private: TestSelectionRangesInFile TestSelections; }; +/// Stores the parsed -selection=filename:line:column[-line:column] option. +class SourceRangeSelectionArgument final : public SourceSelectionArgument { +public: + SourceRangeSelectionArgument(ParsedSourceRange Range) + : Range(std::move(Range)) {} + + bool forAllRanges(const SourceManager &SM, + llvm::function_ref<void(SourceRange R)> Callback) override { + const FileEntry *FE = SM.getFileManager().getFile(Range.FileName); + FileID FID = FE ? SM.translateFile(FE) : FileID(); + if (!FE || FID.isInvalid()) { + llvm::errs() << "error: -selection=" << Range.FileName + << ":... : given file is not in the target TU\n"; + return true; + } + + SourceLocation Start = SM.getMacroArgExpandedLocation( + SM.translateLineCol(FID, Range.Begin.first, Range.Begin.second)); + SourceLocation End = SM.getMacroArgExpandedLocation( + SM.translateLineCol(FID, Range.End.first, Range.End.second)); + if (Start.isInvalid() || End.isInvalid()) { + llvm::errs() << "error: -selection=" << Range.FileName << ':' + << Range.Begin.first << ':' << Range.Begin.second << '-' + << Range.End.first << ':' << Range.End.second + << " : invalid source location\n"; + return true; + } + Callback(SourceRange(Start, End)); + return false; + } + +private: + ParsedSourceRange Range; +}; + std::unique_ptr<SourceSelectionArgument> SourceSelectionArgument::fromString(StringRef Value) { if (Value.startswith("test:")) { @@ -110,10 +146,12 @@ SourceSelectionArgument::fromString(StringRef Value) { return llvm::make_unique<TestSourceSelectionArgument>( std::move(*ParsedTestSelection)); } - // FIXME: Support true selection ranges. + Optional<ParsedSourceRange> Range = ParsedSourceRange::fromString(Value); + if (Range) + return llvm::make_unique<SourceRangeSelectionArgument>(std::move(*Range)); llvm::errs() << "error: '-selection' option must be specified using " "<file>:<line>:<column> or " - "<file>:<line>:<column>-<line>:<column> format"; + "<file>:<line>:<column>-<line>:<column> format\n"; return nullptr; } @@ -268,11 +306,18 @@ private: class ClangRefactorConsumer : public RefactoringResultConsumer { public: - void handleError(llvm::Error Err) { + void handleError(llvm::Error Err) override { llvm::errs() << llvm::toString(std::move(Err)) << "\n"; } - // FIXME: Consume atomic changes and apply them to files. + void handle(AtomicChanges Changes) override { + SourceChanges.insert(SourceChanges.begin(), Changes.begin(), Changes.end()); + } + + const AtomicChanges &getSourceChanges() const { return SourceChanges; } + +private: + AtomicChanges SourceChanges; }; class ClangRefactorTool { @@ -352,6 +397,39 @@ public: } } + bool applySourceChanges(const AtomicChanges &Replacements) { + std::set<std::string> Files; + for (const auto &Change : Replacements) + Files.insert(Change.getFilePath()); + // FIXME: Add automatic formatting support as well. + tooling::ApplyChangesSpec Spec; + // FIXME: We should probably cleanup the result by default as well. + Spec.Cleanup = false; + for (const auto &File : Files) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferErr = + llvm::MemoryBuffer::getFile(File); + if (!BufferErr) { + llvm::errs() << "error: failed to open " << File << " for rewriting\n"; + return true; + } + auto Result = tooling::applyAtomicChanges(File, (*BufferErr)->getBuffer(), + Replacements, Spec); + if (!Result) { + llvm::errs() << toString(Result.takeError()); + return true; + } + + std::error_code EC; + llvm::raw_fd_ostream OS(File, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << EC.message() << "\n"; + return true; + } + OS << *Result; + } + return false; + } + bool invokeAction(RefactoringActionSubcommand &Subcommand, const CompilationDatabase &DB, ArrayRef<std::string> Sources) { @@ -423,7 +501,7 @@ public: // FIXME (Alex L): Implement non-selection based invocation path. })) return true; - return HasFailed; + return HasFailed || applySourceChanges(Consumer.getSourceChanges()); } }; |