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

Reply via email to