From d088a5f966c31462280d5ace29febc6889834611 Mon Sep 17 00:00:00 2001 From: Edwin Vane Date: Fri, 11 Jan 2013 17:04:55 +0000 Subject: [PATCH] Allow RefactoringTool to write to memory instead of always to disk RefactoringTool::run() always writes the result of rewrites to disk. Instead, make this optional and provide a method for getting the refactoring results in a memory buffer instead. Also made ClangTool polymorphic so RefactoringTool could inherit from it to properly express the IS-A relationship. This change also provides access to ClangTool's public interface, e.g. mapVirtualFile() which is important once refactored buffers start living in memory instead of on disk. Reviewers: klimek git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@172219 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Rewrite/Core/Rewriter.h | 6 ++- include/clang/Tooling/Refactoring.h | 44 ++++++++++++------- include/clang/Tooling/Tooling.h | 4 +- lib/Tooling/Refactoring.cpp | 63 +++++++++++++++------------ 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/include/clang/Rewrite/Core/Rewriter.h b/include/clang/Rewrite/Core/Rewriter.h index ae1c42996c..cb044aef23 100644 --- a/include/clang/Rewrite/Core/Rewriter.h +++ b/include/clang/Rewrite/Core/Rewriter.h @@ -52,7 +52,11 @@ public: iterator end() const { return Buffer.end(); } unsigned size() const { return Buffer.size(); } - raw_ostream &write(raw_ostream &) const; + /// \brief Write to \p Stream the result of applying all changes to the + /// original buffer. + /// + /// The original buffer is not actually changed. + raw_ostream &write(raw_ostream &Stream) const; /// RemoveText - Remove the specified text. void RemoveText(unsigned OrigOffset, unsigned Size, diff --git a/include/clang/Tooling/Refactoring.h b/include/clang/Tooling/Refactoring.h index 469793449d..5f2f8082f2 100644 --- a/include/clang/Tooling/Refactoring.h +++ b/include/clang/Tooling/Refactoring.h @@ -105,35 +105,48 @@ public: /// FIXME: Change to a vector and deduplicate in the RefactoringTool. typedef std::set Replacements; -/// \brief Apply all replacements on the Rewriter. +/// \brief Apply all replacements in \p Replaces to the Rewriter \p Rewrite. /// -/// If at least one Apply returns false, ApplyAll returns false. Every -/// Apply will be executed independently of the result of other -/// Apply operations. +/// Replacement applications happen independently of the success of +/// other applications. +/// +/// \returns true if all replacements apply. false otherwise. bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite); /// \brief A tool to run refactorings. /// -/// This is a refactoring specific version of \see ClangTool. -/// All text replacements added to getReplacements() during the run of the -/// tool will be applied and saved after all translation units have been -/// processed. -class RefactoringTool { +/// This is a refactoring specific version of \see ClangTool. FrontendActions +/// passed to run() and runAndSave() should add replacements to +/// getReplacements(). +class RefactoringTool : public ClangTool { public: /// \see ClangTool::ClangTool. RefactoringTool(const CompilationDatabase &Compilations, ArrayRef SourcePaths); - /// \brief Returns a set of replacements. All replacements added during the - /// run of the tool will be applied after all translation units have been - /// processed. + /// \brief Returns the set of replacements to which replacements should + /// be added during the run of the tool. Replacements &getReplacements(); - /// \see ClangTool::run. - int run(FrontendActionFactory *ActionFactory); + /// \brief Call run(), apply all generated replacements, and immediately save + /// the results to disk. + /// + /// \returns 0 upon success. Non-zero upon failure. + int runAndSave(FrontendActionFactory *ActionFactory); + + /// \brief Apply all stored replacements to the given Rewriter. + /// + /// Replacement applications happen independently of the success of other + /// applications. + /// + /// \returns true if all replacements apply. false otherwise. + bool applyAllReplacements(Rewriter &Rewrite); + +private: + /// \brief Write all refactored files to disk. + int saveRewrittenFiles(Rewriter &Rewrite); private: - ClangTool Tool; Replacements Replace; }; @@ -149,4 +162,3 @@ Replacement::Replacement(SourceManager &Sources, const Node &NodeToReplace, } // end namespace clang #endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H - diff --git a/include/clang/Tooling/Tooling.h b/include/clang/Tooling/Tooling.h index e17e8b965a..dae848dddf 100644 --- a/include/clang/Tooling/Tooling.h +++ b/include/clang/Tooling/Tooling.h @@ -179,6 +179,8 @@ class ClangTool { ClangTool(const CompilationDatabase &Compilations, ArrayRef SourcePaths); + virtual ~ClangTool() {} + /// \brief Map a virtual file to be used while running the tool. /// /// \param FilePath The path at which the content will be mapped. @@ -195,7 +197,7 @@ class ClangTool { /// \param ActionFactory Factory generating the frontend actions. The function /// takes ownership of this parameter. A new action is generated for every /// processed translation unit. - int run(FrontendActionFactory *ActionFactory); + virtual int run(FrontendActionFactory *ActionFactory); /// \brief Returns the file manager used in the tool. /// diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp index c5002ef9fc..9ed0999140 100644 --- a/lib/Tooling/Refactoring.cpp +++ b/lib/Tooling/Refactoring.cpp @@ -135,50 +135,55 @@ bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) { return Result; } -bool saveRewrittenFiles(Rewriter &Rewrite) { - for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), - E = Rewrite.buffer_end(); - I != E; ++I) { - // FIXME: This code is copied from the FixItRewriter.cpp - I think it should - // go into directly into Rewriter (there we also have the Diagnostics to - // handle the error cases better). - const FileEntry *Entry = - Rewrite.getSourceMgr().getFileEntryForID(I->first); - std::string ErrorInfo; - llvm::raw_fd_ostream FileStream( - Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); - if (!ErrorInfo.empty()) - return false; - I->second.write(FileStream); - FileStream.flush(); - } - return true; -} - RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, ArrayRef SourcePaths) - : Tool(Compilations, SourcePaths) {} + : ClangTool(Compilations, SourcePaths) {} Replacements &RefactoringTool::getReplacements() { return Replace; } -int RefactoringTool::run(FrontendActionFactory *ActionFactory) { - int Result = Tool.run(ActionFactory); +int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) { + if (int Result = run(ActionFactory)) { + return Result; + } + LangOptions DefaultLangOptions; IntrusiveRefCntPtr DiagOpts = new DiagnosticOptions(); TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts); DiagnosticsEngine Diagnostics( llvm::IntrusiveRefCntPtr(new DiagnosticIDs()), &*DiagOpts, &DiagnosticPrinter, false); - SourceManager Sources(Diagnostics, Tool.getFiles()); + SourceManager Sources(Diagnostics, getFiles()); Rewriter Rewrite(Sources, DefaultLangOptions); - if (!applyAllReplacements(Replace, Rewrite)) { + + if (!applyAllReplacements(Rewrite)) { llvm::errs() << "Skipped some replacements.\n"; } - if (!saveRewrittenFiles(Rewrite)) { - llvm::errs() << "Could not save rewritten files.\n"; - return 1; + + return saveRewrittenFiles(Rewrite); +} + +bool RefactoringTool::applyAllReplacements(Rewriter &Rewrite) { + return tooling::applyAllReplacements(Replace, Rewrite); +} + +int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) { + for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(), + E = Rewrite.buffer_end(); + I != E; ++I) { + // FIXME: This code is copied from the FixItRewriter.cpp - I think it should + // go into directly into Rewriter (there we also have the Diagnostics to + // handle the error cases better). + const FileEntry *Entry = + Rewrite.getSourceMgr().getFileEntryForID(I->first); + std::string ErrorInfo; + llvm::raw_fd_ostream FileStream( + Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); + if (!ErrorInfo.empty()) + return 1; + I->second.write(FileStream); + FileStream.flush(); } - return Result; + return 0; } } // end namespace tooling -- 2.40.0