Hi klimek,
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.
http://llvm-reviews.chandlerc.com/D273
Files:
include/clang/Rewrite/Core/Rewriter.h
include/clang/Tooling/Refactoring.h
include/clang/Tooling/Tooling.h
lib/Rewrite/Core/Rewriter.cpp
lib/Tooling/Refactoring.cpp
Index: include/clang/Rewrite/Core/Rewriter.h
===================================================================
--- include/clang/Rewrite/Core/Rewriter.h
+++ include/clang/Rewrite/Core/Rewriter.h
@@ -54,6 +54,12 @@
raw_ostream &write(raw_ostream &) const;
+ /// \brief Return the result of applying all changes to the original buffer.
+ ///
+ /// The original buffer is not actually changed. The buffer returned from
+ /// this function is the responsibility of the caller.
+ const char *getRewriteResult() const;
+
/// RemoveText - Remove the specified text.
void RemoveText(unsigned OrigOffset, unsigned Size,
bool removeLineIfEmpty = false);
@@ -278,6 +284,7 @@
// Iterators over rewrite buffers.
buffer_iterator buffer_begin() { return RewriteBuffers.begin(); }
buffer_iterator buffer_end() { return RewriteBuffers.end(); }
+ unsigned int getNumBuffers() { return RewriteBuffers.size(); }
/// overwriteChangedFiles - Save all changed files to disk.
///
Index: include/clang/Tooling/Refactoring.h
===================================================================
--- include/clang/Tooling/Refactoring.h
+++ include/clang/Tooling/Refactoring.h
@@ -112,29 +112,44 @@
/// Apply operations.
bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite);
+class RewriteHelper;
+class RefactoringResults;
+
+
/// \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 {
+class RefactoringTool : public ClangTool {
public:
/// \see ClangTool::ClangTool.
RefactoringTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> SourcePaths);
+ ~RefactoringTool();
+
/// \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.
Replacements &getReplacements();
/// \see ClangTool::run.
- int run(FrontendActionFactory *ActionFactory);
+ int run(FrontendActionFactory *ActionFactory) LLVM_OVERRIDE;
+
+ /// \brief Write all refactored files to disk.
+ int saveRewrittenFiles();
+
+ /// \brief Write all refactored files to buffers in memory, leaving files on
+ /// disk unchanged.
+ ///
+ /// Results are returned via \p Results.
+ void getResults(RefactoringResults &Results);
private:
- ClangTool Tool;
Replacements Replace;
+ llvm::OwningPtr<RewriteHelper> Impl;
};
template <typename Node>
@@ -145,6 +160,45 @@
setFromSourceRange(Sources, Range, ReplacementText);
}
+/// \brief Owns the result of refactoring one or more files.
+///
+/// Each refactoring result is represented by a pair: a filename for the
+/// original file, and a string buffer containing the result of refactoring on
+/// the original file. Both the filenames and the buffer contents become owned
+/// by this class.
+class RefactoringResults {
+public:
+ typedef std::vector<std::pair<const char*, const char*> > ResultVec;
+ typedef ResultVec::const_iterator const_iterator;
+
+public:
+ /// \brief Construct a container for holding \p NumResults refactoring
+ /// results.
+ RefactoringResults(unsigned int NumResults);
+ ~RefactoringResults();
+
+ /// \brief Add a new refactoring result.
+ ///
+ /// The memory pointed at by \p Filename and \p Content becomes owned by this
+ /// class.
+ void addResult(const char *Filename, const char *Content);
+
+ /// \brief Free all refactoring results.
+ void clear();
+
+ /// \brief Iterator to the first refactoring result.
+ const_iterator begin() const { return Data.begin(); }
+
+ /// \brief Iterator to the end of the container.
+ const_iterator end() const { return Data.end(); }
+
+ /// \brief Return how many results are actually stored by this class.
+ ResultVec::size_type size() const { return Data.size(); }
+
+private:
+ ResultVec Data;
+};
+
} // end namespace tooling
} // end namespace clang
Index: include/clang/Tooling/Tooling.h
===================================================================
--- include/clang/Tooling/Tooling.h
+++ include/clang/Tooling/Tooling.h
@@ -179,6 +179,8 @@
ClangTool(const CompilationDatabase &Compilations,
ArrayRef<std::string> 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 @@
/// \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.
///
Index: lib/Rewrite/Core/Rewriter.cpp
===================================================================
--- lib/Rewrite/Core/Rewriter.cpp
+++ lib/Rewrite/Core/Rewriter.cpp
@@ -31,6 +31,13 @@
return os;
}
+const char *RewriteBuffer::getRewriteResult() const {
+ char *Result = new char[size() + 1];
+ std::copy(begin(), end(), Result);
+ Result[size()] = 0;
+ return Result;
+}
+
/// \brief Return true if this character is non-new-line whitespace:
/// ' ', '\\t', '\\f', '\\v', '\\r'.
static inline bool isWhitespace(unsigned char c) {
Index: lib/Tooling/Refactoring.cpp
===================================================================
--- lib/Tooling/Refactoring.cpp
+++ lib/Tooling/Refactoring.cpp
@@ -135,7 +135,56 @@
return Result;
}
-bool saveRewrittenFiles(Rewriter &Rewrite) {
+// A class serving in a pseudo-pimpl idiom role for RefactoringTool. The main
+// goal is to make a Rewriter instance created by RefactoringTool::run()
+// available for calling in other member functions of RefactoringTool,
+// saveRewrittenFiles() and getResults(), without cluttering Refactoring.h's
+// header with includes that are really only needed by the implementation.
+class RewriteHelper {
+public:
+ RewriteHelper(ClangTool& Tool) : DiagOpts(new DiagnosticOptions()),
+ DiagnosticPrinter(llvm::errs(), &*DiagOpts),
+ Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
+ &*DiagOpts, &DiagnosticPrinter, false),
+ Sources(Diagnostics, Tool.getFiles()),
+ Rewrite(Sources, DefaultLangOptions) {}
+
+ Rewriter &getRewriter() { return Rewrite; }
+
+private:
+ LangOptions DefaultLangOptions;
+ IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
+ TextDiagnosticPrinter DiagnosticPrinter;
+ DiagnosticsEngine Diagnostics;
+ SourceManager Sources;
+ Rewriter Rewrite;
+};
+
+RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
+ ArrayRef<std::string> SourcePaths)
+ : ClangTool(Compilations, SourcePaths) {}
+
+// The destructor of RewriteHelper is present at this point but not in the
+// header since the class is a pImpl idiom to prevent having to add many
+// #includes.
+RefactoringTool::~RefactoringTool() {}
+
+Replacements &RefactoringTool::getReplacements() { return Replace; }
+
+int RefactoringTool::run(FrontendActionFactory *ActionFactory) {
+ int Result = ClangTool::run(ActionFactory);
+ Impl.reset(new RewriteHelper(*this));
+
+ if (!applyAllReplacements(Replace, Impl->getRewriter())) {
+ llvm::errs() << "Skipped some replacements.\n";
+ }
+ return Result;
+}
+
+int RefactoringTool::saveRewrittenFiles() {
+ assert(Impl.get() != 0 && "Calling saveRewrittenFiles before running tool");
+
+ Rewriter &Rewrite = Impl->getRewriter();
for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
E = Rewrite.buffer_end();
I != E; ++I) {
@@ -148,37 +197,52 @@
llvm::raw_fd_ostream FileStream(
Entry->getName(), ErrorInfo, llvm::raw_fd_ostream::F_Binary);
if (!ErrorInfo.empty())
- return false;
+ return 1;
I->second.write(FileStream);
FileStream.flush();
}
- return true;
+ return 0;
}
-RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations,
- ArrayRef<std::string> SourcePaths)
- : Tool(Compilations, SourcePaths) {}
-
-Replacements &RefactoringTool::getReplacements() { return Replace; }
+void RefactoringTool::getResults(RefactoringResults &Results) {
+ assert(Impl.get() != 0 && "Calling getResults before running tool");
-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";
+ Rewriter &Rewrite = Impl->getRewriter();
+ for (Rewriter::buffer_iterator I = Rewrite.buffer_begin(),
+ E = Rewrite.buffer_end();
+ I != E; ++I) {
+ const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
+ assert(Entry != 0 && "Expected a FileEntry");
+ assert(Entry->getName() != 0 &&
+ "Unexpected NULL return from FileEntry::getName()");
+ unsigned int FilenameLength = strlen(Entry->getName());
+ char *Filename = new char[FilenameLength + 1];
+ memcpy(Filename, Entry->getName(), FilenameLength);
+ Filename[FilenameLength] = 0;
+ Results.addResult(Filename, I->second.getRewriteResult());
}
- if (!saveRewrittenFiles(Rewrite)) {
- llvm::errs() << "Could not save rewritten files.\n";
- return 1;
+}
+
+RefactoringResults::RefactoringResults(unsigned int NumResults) {
+ Data.reserve(NumResults);
+}
+
+RefactoringResults::~RefactoringResults() {
+ clear();
+}
+
+void RefactoringResults::clear() {
+ for (ResultVec::iterator I = Data.begin(),
+ E = Data.end();
+ I != E; ++I) {
+ delete [] I->first;
+ delete [] I->second;
}
- return Result;
+ Data.clear();
+}
+
+void RefactoringResults::addResult(const char *Filename, const char *Content) {
+ Data.push_back(std::make_pair(Filename, Content));
}
} // end namespace tooling
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits