- Addressed user comments. Ported unit tests.

Hi tareqsiraj, arielbernal, Sarcasm, klimek,

http://llvm-reviews.chandlerc.com/D1730

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1730?vs=4423&id=4446#toc

Files:
  clang-apply-replacements/CMakeLists.txt
  
clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h
  clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
  clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
  test/clang-apply-replacements/Inputs/format/no.cpp
  test/clang-apply-replacements/Inputs/format/no.yaml
  test/clang-apply-replacements/Inputs/format/yes.cpp
  test/clang-apply-replacements/Inputs/format/yes.yaml
  test/clang-apply-replacements/format.cpp
  unittests/CMakeLists.txt
  unittests/Makefile
  unittests/clang-apply-replacements/.ReformattingTest.cpp.swo
  unittests/clang-apply-replacements/CMakeLists.txt
  unittests/clang-apply-replacements/Makefile
  unittests/clang-modernize/Makefile
  unittests/clang-apply-replacements/ReformattingTest.cpp
  unittests/clang-modernize/CMakeLists.txt
  unittests/clang-modernize/FileOverridesTest.cpp
  unittests/clang-modernize/IncludeDirectivesTest.cpp
  unittests/clang-modernize/IncludeExcludeTest.cpp
  unittests/include/common/Utility.h
  unittests/clang-modernize/Utility.h
  unittests/include/common/VirtualFileHelper.h
  unittests/clang-modernize/VirtualFileHelper.h
Index: clang-apply-replacements/CMakeLists.txt
===================================================================
--- clang-apply-replacements/CMakeLists.txt
+++ clang-apply-replacements/CMakeLists.txt
@@ -13,6 +13,7 @@
   clangTooling
   clangBasic
   clangRewriteFrontend
+  clangFormat
   )
 
 include_directories(
Index: clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h
===================================================================
--- clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h
+++ clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h
@@ -28,8 +28,15 @@
 class DiagnosticsEngine;
 class Rewriter;
 
+namespace format {
+struct FormatStyle;
+} // end namespace format
+
 namespace replace {
 
+/// \brief Collection of source ranges.
+typedef std::vector<clang::tooling::Range> RangeVec;
+
 /// \brief Collection of TranslationUnitReplacements.
 typedef std::vector<clang::tooling::TranslationUnitReplacements>
 TUReplacements;
@@ -91,6 +98,39 @@
 bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
                        clang::Rewriter &Rewrites);
 
+/// \brief Given a collection of Replacements and existing changed source ranges
+/// for a single file, updates the list of source ranges to enclose \c
+/// Replacements.
+///
+/// \param[in] Replacements Replacements from a single file.
+/// \param[in,out] ChangedRanges Upon input, this vector may contain a list of
+/// source ranges describe where changes were previously made to the file. Upon
+/// return, any existing ranges are updated to enclose the new Replacements.
+/// Ranges do not overlap and will not be directly adjacent
+/// as such ranges are combined into one.
+void calculateChangedRanges(
+    const std::vector<clang::tooling::Replacement> &Replacements,
+    RangeVec &ChangedRanges);
+
+/// \brief Applies source-code formatting to the given source ranges of the
+/// given file.
+///
+/// This function uses \c clang::format::reformat() so the pre/post conditions
+/// of that function apply here as well. In particular, code outside of the
+/// given ranges may be formatted if it's part of the syntactic unit being
+/// formatted.
+///
+/// \param[in] FileName Name of the file being formatted.
+/// \param[in] Ranges Sequence of source ranges to format. 
+/// \param[in] SM SourceManager for interpreting source ranges.
+/// \param[in] Style Coding style to apply.
+/// \param[out] Replacements New replacements that will reformat code when
+/// applied.
+void reformatRanges(const StringRef FileName, const RangeVec &Ranges,
+                    clang::SourceManager &SM,
+                    const clang::format::FormatStyle &Style,
+                    std::vector<clang::tooling::Replacement> &Replacements);
+
 /// \brief Write the contents of \c FileContents to disk. Keys of the map are
 /// filenames and values are the new contents for those files.
 ///
Index: clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
===================================================================
--- clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
+++ clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp
@@ -17,6 +17,8 @@
 #include "clang-apply-replacements/Tooling/ApplyReplacements.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
+#include "clang/Lex/Lexer.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Tooling/ReplacementsYaml.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -35,6 +37,52 @@
 namespace replace {
 
 
+/// \brief Comparator to be able to order tooling::Range based on their offset.
+static bool rangeLess(clang::tooling::Range A, clang::tooling::Range B) {
+  if (A.getOffset() == B.getOffset())
+    return A.getLength() < B.getLength();
+  return A.getOffset() < B.getOffset();
+}
+
+namespace {
+
+/// \brief Functor that returns the given range without its overlaps with the
+/// replacement given in the constructor.
+struct RangeReplacedAdjuster {
+  RangeReplacedAdjuster(const tooling::Replacement &Replace)
+      : Replace(Replace.getOffset(), Replace.getLength()),
+        ReplaceNewSize(Replace.getReplacementText().size()) {}
+
+  tooling::Range operator()(tooling::Range Range) const {
+    if (!Range.overlapsWith(Replace))
+      return Range;
+    // range inside replacement -> make the range length null
+    if (Replace.contains(Range))
+      return tooling::Range(Range.getOffset(), 0);
+    // replacement inside range -> resize the range
+    if (Range.contains(Replace)) {
+      int Difference = ReplaceNewSize - Replace.getLength();
+      return tooling::Range(Range.getOffset(), Range.getLength() + Difference);
+    }
+    // beginning of the range replaced -> truncate range beginning
+    if (Range.getOffset() > Replace.getOffset()) {
+      unsigned ReplaceEnd = Replace.getOffset() + Replace.getLength();
+      unsigned RangeEnd = Range.getOffset() + Range.getLength();
+      return tooling::Range(ReplaceEnd, RangeEnd - ReplaceEnd);
+    }
+    // end of the range replaced -> truncate range end
+    if (Range.getOffset() < Replace.getOffset())
+      return tooling::Range(Range.getOffset(),
+                            Replace.getOffset() - Range.getOffset());
+    llvm_unreachable("conditions not handled properly");
+  }
+
+  const tooling::Range Replace;
+  const unsigned ReplaceNewSize;
+};
+
+} // end anonymous namespace
+
 llvm::error_code
 collectReplacementsFromDirectory(const llvm::StringRef Directory,
                                  TUReplacements &TUs,
@@ -212,6 +260,96 @@
   return true;
 }
 
+static void coalesceRanges(RangeVec &Ranges) {
+  // Sort the ranges by offset and then for each group of adjacent/overlapping
+  // ranges the first one in the group is extended to cover the whole group.
+  std::sort(Ranges.begin(), Ranges.end(), &rangeLess);
+  RangeVec::iterator FirstInGroup = Ranges.begin();
+  assert(!Ranges.empty() && "unexpected empty vector");
+  for (RangeVec::iterator I = Ranges.begin() + 1, E = Ranges.end(); I != E;
+       ++I) {
+    unsigned GroupEnd = FirstInGroup->getOffset() + FirstInGroup->getLength();
+
+    // no contact
+    if (I->getOffset() > GroupEnd)
+      FirstInGroup = I;
+    else {
+      unsigned GrpBegin = FirstInGroup->getOffset();
+      unsigned GrpEnd = std::max(GroupEnd, I->getOffset() + I->getLength());
+      *FirstInGroup = tooling::Range(GrpBegin, GrpEnd - GrpBegin);
+    }
+  }
+
+  // Remove the ranges that are covered by the first member of the group.
+  Ranges.erase(std::unique(Ranges.begin(), Ranges.end(),
+                           std::mem_fun_ref(&tooling::Range::contains)),
+               Ranges.end());
+}
+
+void calculateChangedRanges(
+    const std::vector<clang::tooling::Replacement> &Replaces,
+    RangeVec &ChangedRanges) {
+
+  // First adjust existing ranges in case they overlap with the replacements.
+  for (std::vector<clang::tooling::Replacement>::const_iterator
+           I = Replaces.begin(),
+           E = Replaces.end();
+       I != E; ++I) {
+    const tooling::Replacement &Replace = *I;
+
+    std::transform(ChangedRanges.begin(), ChangedRanges.end(),
+                   ChangedRanges.begin(), RangeReplacedAdjuster(Replace));
+  }
+
+  // Then shift existing ranges to reflect the new positions.
+  for (RangeVec::iterator I = ChangedRanges.begin(), E = ChangedRanges.end();
+       I != E; ++I) {
+    unsigned ShiftedOffset =
+        tooling::shiftedCodePosition(Replaces, I->getOffset());
+    *I = tooling::Range(ShiftedOffset, I->getLength());
+  }
+
+  // Then generate the new ranges from the replacements.
+  for (std::vector<clang::tooling::Replacement>::const_iterator
+           I = Replaces.begin(),
+           E = Replaces.end();
+       I != E; ++I) {
+    const tooling::Replacement &R = *I;
+    unsigned Offset = tooling::shiftedCodePosition(Replaces, R.getOffset());
+    unsigned Length = R.getReplacementText().size();
+
+    ChangedRanges.push_back(tooling::Range(Offset, Length));
+  }
+
+  coalesceRanges(ChangedRanges);
+}
+
+void reformatRanges(const llvm::StringRef FileName, const RangeVec &Ranges,
+                    clang::SourceManager &SM,
+                    const clang::format::FormatStyle &Style,
+                    std::vector<tooling::Replacement> &Replacements) {
+  const FileEntry *Entry = SM.getFileManager().getFile(FileName);
+  assert(Entry && "expected an existing file");
+
+  FileID ID = SM.translateFile(Entry);
+  if (ID.isInvalid())
+    ID = SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
+
+  std::vector<CharSourceRange> ReformatRanges;
+  SourceLocation StartOfFile = SM.getLocForStartOfFile(ID);
+  for (RangeVec::const_iterator I = Ranges.begin(), E = Ranges.end(); I != E;
+       ++I) {
+    SourceLocation Start = StartOfFile.getLocWithOffset(I->getOffset());
+    SourceLocation End = Start.getLocWithOffset(I->getLength());
+    ReformatRanges.push_back(CharSourceRange::getCharRange(Start, End));
+  }
+
+  Lexer Lex(ID, SM.getBuffer(ID), SM, getFormattingLangOpts(Style.Standard));
+  const tooling::Replacements &R =
+      format::reformat(Style, Lex, SM, ReformatRanges);
+  std::copy(R.begin(), R.end(), std::back_inserter(Replacements));
+}
+
 bool writeFiles(const clang::Rewriter &Rewrites) {
 
   for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(),
Index: clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
===================================================================
--- clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
+++ clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp
@@ -17,6 +17,7 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticOptions.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Format/Format.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "llvm/Support/CommandLine.h"
 
@@ -33,6 +34,57 @@
              "merging/replacing."),
     cl::init(false));
 
+static cl::opt<std::string> FormatStyleOpt(
+    "format",
+    cl::desc(
+        "Enable formatting of code changed by applying replacements.\n"
+        "<style> can be either a builtin style supported by clang-format,\n"
+        "or a YAML config file (see clang-format -dump-config).\n"
+        "The default style is 'llvm'"),
+    cl::ValueOptional, cl::value_desc("style"));
+
+/// \brief Fills in \c Style with information from the command-line option
+/// "format-style".
+///
+/// Upon error, print a diagnostic error message.
+///
+/// \param[in] ProgName The name of the program, \c argv[0], used to print
+/// errors.
+/// \param[in] OptionValue The value of the -format option.
+/// \param[out] Style Structure describing the style reformatting should follow.
+/// \param[in] Diagnostics For error output.
+///
+/// \returns \li true if the command-line option is present and well formed.
+///          \li false if the command-line option is present but describes an
+///          unknown built-in style or the custom style description file
+///          couldn't be parsed.
+static bool handleFormatStyle(const char *ProgName, const StringRef OptionValue,
+                              format::FormatStyle &Style,
+                              DiagnosticsEngine &Diagnostics) {
+  StringRef OptValue = OptionValue.empty() ? "llvm" : OptionValue;
+
+  if (!format::getPredefinedStyle(OptValue, &Style)) {
+    llvm::StringRef ConfigFilePath = FormatStyleOpt;
+    llvm::OwningPtr<llvm::MemoryBuffer> Text;
+    llvm::error_code ec;
+
+    ec = llvm::MemoryBuffer::getFile(ConfigFilePath, Text);
+    if (!ec)
+      ec = parseConfiguration(Text->getBuffer(), &Style);
+
+    if (ec) {
+      // FIXME: Use Diagnostics instead of llvm::errs().
+      llvm::errs() << ProgName << ": invalid format style " << FormatStyleOpt
+                   << ": " << ec.message() << "\n";
+      return false;
+    }
+  }
+
+  Style.Standard = clang::format::FormatStyle::LS_Auto;
+
+  return true;
+}
+
 // Helper object to remove the TUReplacement files (triggered by
 // "remove-change-desc-files" command line option) when exiting current scope.
 class ScopedFileRemover {
@@ -50,14 +102,71 @@
   clang::DiagnosticsEngine &Diag;
 };
 
+/// \brief Code changed by replacements in \c GroupedReplacements is reformatted
+/// according to \c Style.
+///
+/// \param[in] AppliedReplacements Rewriter with Replacements applied.
+/// \param[in] GroupedReplacements Replacements used for calculating changed
+/// ranges.
+/// \param[in] Diagnostics For diagnostics.
+/// \param[in] Style Formatting style to follow.
+/// \param[in,out] FormattingRewriter Assumed to be a Rewriter backed by a
+/// SourceManager where files can be overridden with new content from \c
+/// AppliedReplacements. Upon output, contains buffers containing formatted
+/// code.
+static void formatReplacements(const Rewriter &AppliedReplacements,
+                               const FileToReplacementsMap &GroupedReplacements,
+                               DiagnosticsEngine &Diagnostics,
+                               format::FormatStyle Style,
+                               Rewriter &FormattingRewriter) {
+  SourceManager &FormattingSM = FormattingRewriter.getSourceMgr();
+
+  // Update FormattingRewriter's SourceManager with new content from
+  // AppliedReplacements.
+  for (Rewriter::const_buffer_iterator
+           BufferI = AppliedReplacements.buffer_begin(),
+           BufferE = AppliedReplacements.buffer_end();
+       BufferI != BufferE; ++BufferI) {
+    const char *FileName = AppliedReplacements.getSourceMgr()
+                               .getFileEntryForID(BufferI->first)
+                               ->getName();
+
+    std::string NewData(BufferI->second.begin(), BufferI->second.end());
+    FormattingSM.overrideFileContents(
+        FormattingSM.getFileManager().getFile(FileName),
+        llvm::MemoryBuffer::getMemBufferCopy(NewData));
+
+    RangeVec Ranges;
+    calculateChangedRanges(GroupedReplacements.lookup(FileName), Ranges);
+
+    std::vector<clang::tooling::Replacement> FormattingReplacements;
+    reformatRanges(FileName, Ranges, FormattingSM, Style,
+                   FormattingReplacements);
+
+    if (!tooling::applyAllReplacements(FormattingReplacements,
+                                       FormattingRewriter))
+      errs() << "Failed to apply reformatting replacements for " << FileName
+             << "\n";
+  }
+}
+
 int main(int argc, char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
 
   IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
   DiagnosticsEngine Diagnostics(
       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
       DiagOpts.getPtr());
 
+  // Handle the -format option if present.
+  format::FormatStyle FormatStyle;
+  bool DoFormat = false;
+  if (FormatStyleOpt.getNumOccurrences() > 0) {
+    if (!handleFormatStyle(argv[0], FormatStyleOpt, FormatStyle, Diagnostics))
+      return 1;
+    DoFormat = true;
+  }
+
   TUReplacements TUs;
   TUReplacementFiles TURFiles;
 
@@ -83,14 +192,53 @@
   if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
     return 1;
 
-  Rewriter DestRewriter(SM, LangOptions());
-  if (!applyReplacements(GroupedReplacements, DestRewriter)) {
+  Rewriter ReplacementsRewriter(SM, LangOptions());
+
+  if (!applyReplacements(GroupedReplacements, ReplacementsRewriter)) {
     errs() << "Failed to apply all replacements. No changes made.\n";
     return 1;
   }
 
-  if (!writeFiles(DestRewriter))
-    return 1;
+  // Separate File/SourceManager required since we can't update the content of
+  // a file in an existing SourceManager once it's been used by that
+  // SourceManager.
+  FileManager FormattingFiles((FileSystemOptions()));
+  SourceManager FormattingSM(Diagnostics, FormattingFiles);
+  Rewriter FormattingRewriter(FormattingSM, LangOptions());
+  if (DoFormat)
+    formatReplacements(ReplacementsRewriter, GroupedReplacements, Diagnostics,
+                       FormatStyle, FormattingRewriter);
+
+  // Write modified file to disk.
+  for (Rewriter::const_buffer_iterator
+           BufferI = ReplacementsRewriter.buffer_begin(),
+           BufferE = ReplacementsRewriter.buffer_end();
+       BufferI != BufferE; ++BufferI) {
+    const char *FileName = SM.getFileEntryForID(BufferI->first)->getName();
+
+    std::string ErrorInfo;
+    llvm::raw_fd_ostream FileStream(FileName, ErrorInfo);
+    if (!ErrorInfo.empty()) {
+      llvm::errs() << "Could not open " << FileName << " for writing\n";
+      continue;
+    }
+
+    // If this file was formatted, write the formatted buffer to disk instead
+    // of the unformatted buffer. Note the use of FormattingFiles and
+    // FormattignSM here to get FID. This is necessary since these are the
+    // objects FormattingRewriter was constructed with.
+    const clang::FileEntry *Entry = FormattingFiles.getFile(FileName);
+    assert(Entry && "expected an existing file");
+    FileID ID = FormattingSM.translateFile(Entry);
+    const RewriteBuffer *FormattedBuffer = 0;
+    if (!ID.isInvalid())
+      FormattedBuffer = FormattingRewriter.getRewriteBufferFor(ID);
+
+    if (FormattedBuffer)
+      FormattedBuffer->write(FileStream);
+    else
+      BufferI->second.write(FileStream);
+  }
 
   return 0;
 }
Index: test/clang-apply-replacements/Inputs/format/no.cpp
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/Inputs/format/no.cpp
@@ -0,0 +1,6 @@
+class C {};
+
+void f() { // This comment necessary to prevent formatting as void f() { ... }
+  C *a = new C();
+  // CHECK: {{^\ \ auto\ a\ \=\ new\ C\(\);}}
+}
Index: test/clang-apply-replacements/Inputs/format/no.yaml
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/Inputs/format/no.yaml
@@ -0,0 +1,8 @@
+---
+MainSourceFile:  no.cpp
+Replacements:    
+  - FilePath:        $(path)/no.cpp
+    Offset:          94
+    Length:          3
+    ReplacementText: 'auto '
+...
Index: test/clang-apply-replacements/Inputs/format/yes.cpp
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/Inputs/format/yes.cpp
@@ -0,0 +1,9 @@
+class MyType012345678901234567890123456789 {};
+
+void f() {
+  MyType012345678901234567890123456789 *a =
+      new MyType012345678901234567890123456789();
+  // CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
+
+  delete a;
+}
Index: test/clang-apply-replacements/Inputs/format/yes.yaml
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/Inputs/format/yes.yaml
@@ -0,0 +1,8 @@
+---
+MainSourceFile:  yes.cpp
+Replacements:    
+  - FilePath:        $(path)/yes.cpp
+    Offset:          61
+    Length:          38
+    ReplacementText: 'auto '
+...
Index: test/clang-apply-replacements/format.cpp
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/format.cpp
@@ -0,0 +1,15 @@
+// RUN: mkdir -p %T/Inputs/format
+//
+// yes.cpp requires formatting after replacements are applied. no.cpp does not.
+// The presence of no.cpp ensures that files that don't need formatting still
+// have their new state written to disk after applying replacements.
+//
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/yes.cpp > %T/Inputs/format/yes.cpp
+// RUN: grep -Ev "// *[A-Z-]+:" %S/Inputs/format/no.cpp > %T/Inputs/format/no.cpp
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/yes.yaml > %T/Inputs/format/yes.yaml
+// RUN: sed "s#\$(path)#%/T/Inputs/format#" %S/Inputs/format/no.yaml > %T/Inputs/format/no.yaml
+// RUN: clang-apply-replacements -format %T/Inputs/format
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/yes.cpp %S/Inputs/format/yes.cpp
+// RUN: FileCheck --strict-whitespace -input-file=%T/Inputs/format/no.cpp %S/Inputs/format/no.cpp
+//
+// RUN not clang-apply-replacements -format=blah %T/Inputs/format
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -5,5 +5,6 @@
   add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN})
 endfunction()
 
+add_subdirectory(clang-apply-replacements)
 add_subdirectory(clang-modernize)
 add_subdirectory(clang-tidy)
Index: unittests/Makefile
===================================================================
--- unittests/Makefile
+++ unittests/Makefile
@@ -10,6 +10,6 @@
 CLANG_LEVEL := ../../..
 include $(CLANG_LEVEL)/../../Makefile.config
 
-PARALLEL_DIRS := clang-modernize clang-tidy
+PARALLEL_DIRS := clang-apply-replacements clang-modernize clang-tidy
 
 include $(CLANG_LEVEL)/Makefile
Index: unittests/clang-apply-replacements/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/clang-apply-replacements/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(LLVM_LINK_COMPONENTS
+  asmparser
+  bitreader
+  support
+  mc
+  )
+
+get_filename_component(ClangApplyReplacementsLocation
+  "${CMAKE_CURRENT_SOURCE_DIR}/../../clang-apply-replacements/include" REALPATH)
+get_filename_component(CommonIncLocation
+  "${CMAKE_CURRENT_SOURCE_DIR}/../include" REALPATH)
+include_directories(
+  ${ClangApplyReplacementsLocation}
+  ${CommonIncLocation}
+  )
+
+add_extra_unittest(ClangApplyReplacementsTests
+  ReformattingTest.cpp
+  )
+
+target_link_libraries(ClangApplyReplacementsTests
+  clangApplyReplacements
+  )
Index: unittests/clang-apply-replacements/Makefile
===================================================================
--- unittests/clang-apply-replacements/Makefile
+++ unittests/clang-apply-replacements/Makefile
@@ -1,4 +1,4 @@
-##===- unittests/clang-modernize/Makefile ------------------*- Makefile -*-===##
+##===- unittests/clang-apply-replacements/Makefile ---------*- Makefile -*-===##
 #
 #                     The LLVM Compiler Infrastructure
 #
@@ -10,16 +10,14 @@
 CLANG_LEVEL = ../../../..
 include $(CLANG_LEVEL)/../../Makefile.config
 
-TESTNAME = Cpp11MigrateTests
-LINK_COMPONENTS := asmparser bitreader support MC MCParser option \
-		TransformUtils
-USEDLIBS = modernizeCore.a clangFormat.a clangApplyReplacements.a clangTooling.a clangFrontend.a \
+TESTNAME = ClangApplyReplacementsTests
+LINK_COMPONENTS := asmparser bitreader support mc mcparser option
+USEDLIBS = clangApplyReplacements.a clangFormat.a clangTooling.a clangFrontend.a \
 		clangSerialization.a clangDriver.a clangRewriteFrontend.a \
 		clangRewriteCore.a clangParse.a clangSema.a clangAnalysis.a \
-		clangAST.a clangASTMatchers.a clangEdit.a clangLex.a \
-		clangBasic.a
+		clangAST.a clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
 
 include $(CLANG_LEVEL)/Makefile
 MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1
-CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-modernize -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include
+CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include -I$(PROJ_SRC_DIR)/../include
 include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
Index: unittests/clang-modernize/Makefile
===================================================================
--- unittests/clang-modernize/Makefile
+++ unittests/clang-modernize/Makefile
@@ -21,5 +21,6 @@
 
 include $(CLANG_LEVEL)/Makefile
 MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1
-CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-modernize -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include
+CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-modernize -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include \
+	           -I$(PROJ_SRC_DIR)/../include
 include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest
Index: unittests/clang-apply-replacements/ReformattingTest.cpp
===================================================================
--- /dev/null
+++ unittests/clang-apply-replacements/ReformattingTest.cpp
@@ -0,0 +1,164 @@
+//===- clang-modernize/ReformattingTest.cpp - Reformatting unit tests -----===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang-apply-replacements/Tooling/ApplyReplacements.h"
+#include "clang/Format/Format.h"
+#include "clang/Tooling/Refactoring.h"
+#include "common/VirtualFileHelper.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::tooling;
+using namespace clang::replace;
+
+typedef std::vector<clang::tooling::Replacement> ReplacementsVec;
+
+static Replacement makeReplacement(unsigned Offset, unsigned Length,
+                                   unsigned ReplacementLength,
+                                   llvm::StringRef FilePath) {
+  return Replacement(FilePath, Offset, Length,
+                     std::string(ReplacementLength, '~'));
+}
+
+// generate a set of replacements containing one element
+static ReplacementsVec makeReplacements(unsigned Offset, unsigned Length,
+                                        unsigned ReplacementLength,
+                                        llvm::StringRef FilePath = "~") {
+  ReplacementsVec Replaces;
+  Replaces.push_back(
+      makeReplacement(Offset, Length, ReplacementLength, FilePath));
+  return Replaces;
+}
+
+// Put these functions in the clang::tooling namespace so arg-dependent name
+// lookup finds these functions for the EXPECT_EQ macros below.
+namespace clang {
+namespace tooling {
+bool operator==(const Range &A, const Range &B) {
+  return A.getOffset() == B.getOffset() && A.getLength() == B.getLength();
+}
+
+std::ostream &operator<<(std::ostream &os, const Range &R) {
+  return os << "Range(" << R.getOffset() << ", " << R.getLength() << ")";
+}
+
+}
+}
+
+TEST(CalculateChangedRangesTest, shiftsExisting) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(2, 2, 1), Changes);
+  Range ExpectedChanges[] = { Range(2, 1), Range(4, 3) };
+  EXPECT_TRUE(std::equal(Changes.begin(), Changes.end(), ExpectedChanges));
+}
+
+TEST(CalculateChangedRangesTest, truncatesRight) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(7, 1, 0), Changes);
+  EXPECT_EQ(Range(5, 2), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, growsRight) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(7, 4, 4), Changes);
+  EXPECT_EQ(Range(5, 6), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, truncatesLeft) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(5, 1, 0), Changes);
+  EXPECT_EQ(Range(5, 2), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, growsLeft) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(2, 4, 4), Changes);
+  EXPECT_EQ(Range(2, 6), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, handlesRemovedRange) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(4, 5, 0), Changes);
+  EXPECT_EQ(Range(4, 0), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, handlesEnvelopedRange) {
+  RangeVec Changes(1, Range(5, 3));
+  calculateChangedRanges(makeReplacements(4, 5, 6), Changes);
+  EXPECT_EQ(Range(4, 6), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, handlesEnvelopedReplacement) {
+  RangeVec Changes(1, Range(5, 5));
+  calculateChangedRanges(makeReplacements(6, 2, 0), Changes);
+  EXPECT_EQ(Range(5, 3), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, handlesNoOverlap) {
+  RangeVec Changes;
+  calculateChangedRanges(makeReplacements(0, 0, 4), Changes);
+  calculateChangedRanges(makeReplacements(6, 0, 4), Changes);
+  Range ExpectedChanges[] = { Range(0, 4), Range(6, 4) };
+  EXPECT_TRUE(
+      std::equal(Changes.begin(), Changes.end(), ExpectedChanges));
+}
+
+TEST(CalculateChangedRangesTest, handlesNullRange) {
+  RangeVec Changes;
+  calculateChangedRanges(makeReplacements(0, 4, 0), Changes);
+  EXPECT_EQ(Range(0, 0), Changes.front());
+}
+
+TEST(CalculateChangedRangesTest, coalescesAdjacentRight) {
+  RangeVec Changes;
+  calculateChangedRanges(makeReplacements(3, 0, 2), Changes);
+  calculateChangedRanges(makeReplacements(5, 0, 2), Changes);
+  EXPECT_EQ(Range(3, 4), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(CalculateChangedRangesTest, coalescesAdjacentLeft) {
+  RangeVec Changes;
+  calculateChangedRanges(makeReplacements(5, 0, 2), Changes);
+  calculateChangedRanges(makeReplacements(3, 2, 2), Changes);
+  EXPECT_EQ(Range(3, 4), Changes.front());
+  EXPECT_EQ(1u, Changes.size());
+}
+
+TEST(Reformatter, SingleReformat) {
+  VirtualFileHelper VFHelper;
+  llvm::StringRef FileName = "<test>";
+  VFHelper.mapFile(FileName, "int  a;\n"
+                             "int  b;\n");
+
+  RangeVec Changes(1, Range(0, 6));
+  ReplacementsVec Replaces;
+  reformatRanges(FileName, Changes, VFHelper.getNewSourceManager(),
+                 format::getLLVMStyle(), Replaces);
+
+  // We expect the code above to reformatted like so:
+  //
+  // int a;
+  // int  b;
+  //
+  // This test is slightly fragile since there's more than one Replacement that
+  // results in the above change. However, testing the result of applying the
+  // replacement is more trouble than it's worth in this context.
+  ASSERT_EQ(1u, Replaces.size());
+  EXPECT_EQ(3u, Replaces[0].getOffset());
+  EXPECT_EQ(2u, Replaces[0].getLength());
+  EXPECT_EQ(" ", Replaces[0].getReplacementText());
+}
Index: unittests/clang-modernize/CMakeLists.txt
===================================================================
--- unittests/clang-modernize/CMakeLists.txt
+++ unittests/clang-modernize/CMakeLists.txt
@@ -6,9 +6,12 @@
   ${CMAKE_CURRENT_SOURCE_DIR}/../../clang-modernize REALPATH)
 get_filename_component(ClangReplaceLocation
   "${CMAKE_CURRENT_SOURCE_DIR}/../../clang-apply-replacements/include" REALPATH)
+get_filename_component(CommonIncLocation
+  "${CMAKE_CURRENT_SOURCE_DIR}/../include" REALPATH)
 include_directories(
   ${CPP11_MIGRATE_SOURCE_DIR}
   ${ClangReplaceLocation}
+  ${CommonIncLocation}
   )
 
 add_extra_unittest(Cpp11MigrateTests
Index: unittests/clang-modernize/FileOverridesTest.cpp
===================================================================
--- unittests/clang-modernize/FileOverridesTest.cpp
+++ unittests/clang-modernize/FileOverridesTest.cpp
@@ -10,7 +10,7 @@
 #include "Core/FileOverrides.h"
 #include "Core/Refactoring.h"
 #include "gtest/gtest.h"
-#include "VirtualFileHelper.h"
+#include "common/VirtualFileHelper.h"
 #include "clang/Rewrite/Core/Rewriter.h"
 
 using namespace clang;
Index: unittests/clang-modernize/IncludeDirectivesTest.cpp
===================================================================
--- unittests/clang-modernize/IncludeDirectivesTest.cpp
+++ unittests/clang-modernize/IncludeDirectivesTest.cpp
@@ -9,7 +9,7 @@
 
 #include "Core/IncludeDirectives.h"
 #include "gtest/gtest.h"
-#include "VirtualFileHelper.h"
+#include "common/VirtualFileHelper.h"
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/FrontendActions.h"
 #include "llvm/Support/Path.h"
Index: unittests/clang-modernize/IncludeExcludeTest.cpp
===================================================================
--- unittests/clang-modernize/IncludeExcludeTest.cpp
+++ unittests/clang-modernize/IncludeExcludeTest.cpp
@@ -7,7 +7,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "Utility.h"
+#include "common/Utility.h"
 #include "Core/IncludeExcludeInfo.h"
 #include "gtest/gtest.h"
 #include "llvm/Support/FileSystem.h"
Index: unittests/clang-modernize/Utility.h
===================================================================
--- /dev/null
+++ unittests/clang-modernize/Utility.h
@@ -1,25 +0,0 @@
-//=-- clang-modernize/Utility.h - Utility functions and macros---*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_MODERNIZE_UNITTESTS_UTILITY_H
-#define CLANG_MODERNIZE_UNITTESTS_UTILITY_H
-
-// FIXME: copied from unittests/Support/Path.cpp
-#define ASSERT_NO_ERROR(x)                                                     \
-  if (llvm::error_code ASSERT_NO_ERROR_ec = x) {                               \
-    llvm::SmallString<128> MessageStorage;                                     \
-    llvm::raw_svector_ostream Message(MessageStorage);                         \
-    Message << #x ": did not return errc::success.\n"                          \
-            << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n"          \
-            << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n";      \
-    GTEST_FATAL_FAILURE_(MessageStorage.c_str());                              \
-  } else {                                                                     \
-  }
-
-#endif // CLANG_MODERNIZE_UNITTESTS_UTILITY_H
Index: unittests/clang-modernize/VirtualFileHelper.h
===================================================================
--- /dev/null
+++ unittests/clang-modernize/VirtualFileHelper.h
@@ -1,81 +0,0 @@
-//===--- VirtualFileHelper.h ------------------------------------*- C++ -*-===//
-//
-//                     The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \brief This file defines an utility class for tests that needs a source
-/// manager for a virtual file with customizable content.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
-#define CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
-
-#include "clang/Basic/Diagnostic.h"
-#include "clang/Basic/DiagnosticOptions.h"
-#include "clang/Basic/FileManager.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Frontend/TextDiagnosticPrinter.h"
-
-namespace clang {
-
-/// \brief Class that provides easy access to a SourceManager and that allows to
-/// map virtual files conveniently.
-class VirtualFileHelper {
-  struct VirtualFile {
-    std::string FileName;
-    std::string Code;
-  };
-
-public:
-  VirtualFileHelper()
-      : DiagOpts(new DiagnosticOptions()),
-        Diagnostics(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
-                    &*DiagOpts),
-        DiagnosticPrinter(llvm::outs(), &*DiagOpts),
-        Files((FileSystemOptions())) {}
-
-  /// \brief Create a virtual file \p FileName, with content \p Code.
-  void mapFile(llvm::StringRef FileName, llvm::StringRef Code) {
-    VirtualFile VF = { FileName, Code };
-    VirtualFiles.push_back(VF);
-  }
-
-  /// \brief Create a new \c SourceManager with the virtual files and contents
-  /// mapped to it.
-  SourceManager &getNewSourceManager() {
-    Sources.reset(new SourceManager(Diagnostics, Files));
-    mapVirtualFiles(*Sources);
-    return *Sources;
-  }
-
-  /// \brief Map the virtual file contents in the given \c SourceManager.
-  void mapVirtualFiles(SourceManager &SM) const {
-    for (llvm::SmallVectorImpl<VirtualFile>::const_iterator
-             I = VirtualFiles.begin(),
-             E = VirtualFiles.end();
-         I != E; ++I) {
-      llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(I->Code);
-      const FileEntry *Entry = SM.getFileManager().getVirtualFile(
-          I->FileName, Buf->getBufferSize(), /*ModificationTime=*/0);
-      SM.overrideFileContents(Entry, Buf);
-    }
-  }
-
-private:
-  IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts;
-  DiagnosticsEngine Diagnostics;
-  TextDiagnosticPrinter DiagnosticPrinter;
-  FileManager Files;
-  // most tests don't need more than one file
-  llvm::SmallVector<VirtualFile, 1> VirtualFiles;
-  llvm::OwningPtr<SourceManager> Sources;
-};
-
-} // end namespace clang
-
-#endif // CLANG_MODERNIZE_VIRTUAL_FILE_HELPER_H
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to