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
llvm-svn: 172219
This commit is contained in:
Edwin Vane 2013-01-11 17:04:55 +00:00
parent bfbd10b329
commit 5038ac0fb8
4 changed files with 70 additions and 47 deletions

View File

@ -52,7 +52,11 @@ public:
iterator end() const { return Buffer.end(); } iterator end() const { return Buffer.end(); }
unsigned size() const { return Buffer.size(); } 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. /// RemoveText - Remove the specified text.
void RemoveText(unsigned OrigOffset, unsigned Size, void RemoveText(unsigned OrigOffset, unsigned Size,

View File

@ -105,35 +105,48 @@ public:
/// FIXME: Change to a vector and deduplicate in the RefactoringTool. /// FIXME: Change to a vector and deduplicate in the RefactoringTool.
typedef std::set<Replacement, Replacement::Less> Replacements; typedef std::set<Replacement, Replacement::Less> 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 /// Replacement applications happen independently of the success of
/// Apply will be executed independently of the result of other /// other applications.
/// Apply operations. ///
/// \returns true if all replacements apply. false otherwise.
bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite); bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite);
/// \brief A tool to run refactorings. /// \brief A tool to run refactorings.
/// ///
/// This is a refactoring specific version of \see ClangTool. /// This is a refactoring specific version of \see ClangTool. FrontendActions
/// All text replacements added to getReplacements() during the run of the /// passed to run() and runAndSave() should add replacements to
/// tool will be applied and saved after all translation units have been /// getReplacements().
/// processed. class RefactoringTool : public ClangTool {
class RefactoringTool {
public: public:
/// \see ClangTool::ClangTool. /// \see ClangTool::ClangTool.
RefactoringTool(const CompilationDatabase &Compilations, RefactoringTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths); ArrayRef<std::string> SourcePaths);
/// \brief Returns a set of replacements. All replacements added during the /// \brief Returns the set of replacements to which replacements should
/// run of the tool will be applied after all translation units have been /// be added during the run of the tool.
/// processed.
Replacements &getReplacements(); Replacements &getReplacements();
/// \see ClangTool::run. /// \brief Call run(), apply all generated replacements, and immediately save
int run(FrontendActionFactory *ActionFactory); /// 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: private:
ClangTool Tool;
Replacements Replace; Replacements Replace;
}; };
@ -149,4 +162,3 @@ Replacement::Replacement(SourceManager &Sources, const Node &NodeToReplace,
} // end namespace clang } // end namespace clang
#endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H #endif // end namespace LLVM_CLANG_TOOLING_REFACTORING_H

View File

@ -179,6 +179,8 @@ class ClangTool {
ClangTool(const CompilationDatabase &Compilations, ClangTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths); ArrayRef<std::string> SourcePaths);
virtual ~ClangTool() {}
/// \brief Map a virtual file to be used while running the tool. /// \brief Map a virtual file to be used while running the tool.
/// ///
/// \param FilePath The path at which the content will be mapped. /// \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 /// \param ActionFactory Factory generating the frontend actions. The function
/// takes ownership of this parameter. A new action is generated for every /// takes ownership of this parameter. A new action is generated for every
/// processed translation unit. /// processed translation unit.
int run(FrontendActionFactory *ActionFactory); virtual int run(FrontendActionFactory *ActionFactory);
/// \brief Returns the file manager used in the tool. /// \brief Returns the file manager used in the tool.
/// ///

View File

@ -135,7 +135,38 @@ bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) {
return Result; return Result;
} }
bool saveRewrittenFiles(Rewriter &Rewrite) { RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths)
: ClangTool(Compilations, SourcePaths) {}
Replacements &RefactoringTool::getReplacements() { return Replace; }
int RefactoringTool::runAndSave(FrontendActionFactory *ActionFactory) {
if (int Result = run(ActionFactory)) {
return Result;
}
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
&*DiagOpts, &DiagnosticPrinter, false);
SourceManager Sources(Diagnostics, getFiles());
Rewriter Rewrite(Sources, DefaultLangOptions);
if (!applyAllReplacements(Rewrite)) {
llvm::errs() << "Skipped some replacements.\n";
}
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(), for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
E = Rewrite.buffer_end(); E = Rewrite.buffer_end();
I != E; ++I) { I != E; ++I) {
@ -148,37 +179,11 @@ bool saveRewrittenFiles(Rewriter &Rewrite) {
llvm::raw_fd_ostream FileStream( llvm::raw_fd_ostream FileStream(
Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary);
if (!ErrorInfo.empty()) if (!ErrorInfo.empty())
return false; return 1;
I->second.write(FileStream); I->second.write(FileStream);
FileStream.flush(); FileStream.flush();
} }
return true; return 0;
}
RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths)
: Tool(Compilations, SourcePaths) {}
Replacements &RefactoringTool::getReplacements() { return Replace; }
int RefactoringTool::run(FrontendActionFactory *ActionFactory) {
int Result = Tool.run(ActionFactory);
LangOptions DefaultLangOptions;
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
TextDiagnosticPrinter DiagnosticPrinter(llvm::errs(), &*DiagOpts);
DiagnosticsEngine Diagnostics(
llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
&*DiagOpts, &DiagnosticPrinter, false);
SourceManager Sources(Diagnostics, Tool.getFiles());
Rewriter Rewrite(Sources, DefaultLangOptions);
if (!applyAllReplacements(Replace, Rewrite)) {
llvm::errs() << "Skipped some replacements.\n";
}
if (!saveRewrittenFiles(Rewrite)) {
llvm::errs() << "Could not save rewritten files.\n";
return 1;
}
return Result;
} }
} // end namespace tooling } // end namespace tooling