- Re-use much stuff from libFormat and clang-format:
- Use format::reformat() instead of reformatRanges().
- Handling of format command-line options has has a major overhaul. Now
using some of clang-format's functionality now in libFormat.
- Range coalescing doesn't happen any more.
- pre/post-conditions made more explicit.
- Unit tests updated to reflect changes in code.
- Refactoring of code involving getNewDataFor().
Hi tareqsiraj, arielbernal, Sarcasm, klimek, djasper,
http://llvm-reviews.chandlerc.com/D1730
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D1730?vs=4496&id=4527#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/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/clang-modernize/ReformattingTest.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> RangeVector;
+
/// \brief Collection of TranslationUnitReplacements.
typedef std::vector<clang::tooling::TranslationUnitReplacements>
TUReplacements;
@@ -67,6 +74,9 @@
/// \brief Deduplicate, check for conflicts, and apply all Replacements stored
/// in \c TUs. If conflicts occur, no Replacements are applied.
///
+/// \post For all (key,value) in GroupedReplacements, value[i].getOffset() <=
+/// value[i+1].getOffset().
+///
/// \param[in] TUs Collection of TranslationUnitReplacements to merge,
/// deduplicate, and test for conflicts.
/// \param[out] GroupedReplacements Container grouping all Replacements by the
@@ -91,6 +101,18 @@
bool applyReplacements(const FileToReplacementsMap &GroupedReplacements,
clang::Rewriter &Rewrites);
+/// \brief Given a collection of Replacements for a single file, produces a list
+/// of source ranges that enclose those Replacements.
+///
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements from a single file.
+///
+/// \returns Collection of source ranges that enclose all given Replacements.
+/// One range is created for each replacement.
+RangeVector calculateChangedRanges(
+ const 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"
@@ -34,7 +36,6 @@
namespace clang {
namespace replace {
-
llvm::error_code
collectReplacementsFromDirectory(const llvm::StringRef Directory,
TUReplacements &TUs,
@@ -128,6 +129,8 @@
/// \brief Deduplicates and tests for conflicts among the replacements for each
/// file in \c Replacements. Any conflicts found are reported.
///
+/// \post Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
/// \param[in,out] Replacements Container of all replacements grouped by file
/// to be deduplicated and checked for conflicts.
/// \param[in] SM SourceManager required for conflict reporting.
@@ -212,6 +215,29 @@
return true;
}
+RangeVector calculateChangedRanges(
+ const std::vector<clang::tooling::Replacement> &Replaces) {
+ RangeVector ChangedRanges;
+
+ // Generate the new ranges from the replacements.
+ //
+ // NOTE: This is O(n^2) in the number of replacements. If this starts to
+ // become a problem inline shiftedCodePosition() here and do shifts in a
+ // single run through this loop.
+ 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));
+ }
+
+ return ChangedRanges;
+}
+
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"
@@ -27,12 +28,31 @@
static cl::opt<std::string> Directory(cl::Positional, cl::Required,
cl::desc("<Search Root Directory>"));
+static cl::OptionCategory FormattingCategory("Formatting Options");
+
static cl::opt<bool> RemoveTUReplacementFiles(
"remove-change-desc-files",
cl::desc("Remove the change description files regardless of successful\n"
"merging/replacing."),
cl::init(false));
+static cl::opt<bool> DoFormat(
+ "format",
+ cl::desc("Enable formatting of code changed by applying replacements.\n"
+ "Use -style to choose formatting style.\n"),
+ cl::cat(FormattingCategory));
+
+static cl::opt<std::string> FormatStyleConfig(
+ "style-config",
+ cl::desc("Path to a directory containing a .clang-format file\n"
+ "describing a formatting style to use for formatting\n"
+ "code when -style=file.\n"),
+ cl::init(""), cl::cat(FormattingCategory));
+
+static cl::opt<std::string>
+FormatStyleOpt("style", cl::desc(format::StyleOptionHelpDescription),
+ cl::init("LLVM"), cl::cat(FormattingCategory));
+
// Helper object to remove the TUReplacement files (triggered by
// "remove-change-desc-files" command line option) when exiting current scope.
class ScopedFileRemover {
@@ -50,14 +70,121 @@
clang::DiagnosticsEngine &Diag;
};
+/// \brief Convenience function to get rewritten content for \c Filename from
+/// \c Rewrites.
+///
+/// \pre !Replacements.empty().
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+///
+/// \param[in] Replacements Replacements to apply
+/// \param[in] Rewrites Rewriter to use to apply replacements.
+/// \param[out] Result New data is stored here.
+///
+/// \returns \li true if all replacements were applied successfully.
+/// \li false if at least one replacement failed to apply.
+static bool
+getRewrittenData(const std::vector<tooling::Replacement> &Replacements,
+ Rewriter &Rewrites, std::string &Result) {
+ assert(!Replacements.empty() && "Need at least one replacement");
+
+ if (!tooling::applyAllReplacements(Replacements, Rewrites))
+ return false;
+
+ SourceManager &SM = Rewrites.getSourceMgr();
+ FileManager &Files = SM.getFileManager();
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ const clang::FileEntry *Entry = Files.getFile(FileName);
+ assert(Entry && "Expected an existing file");
+ FileID ID = SM.translateFile(Entry);
+ assert(!ID.isInvalid() && "Expected a valid FileID");
+ const RewriteBuffer *Buffer = Rewrites.getRewriteBufferFor(ID);
+ Result = std::string(Buffer->begin(), Buffer->end());
+
+ return true;
+}
+
+/// \brief Apply \c Replacements and return the new file contents.
+///
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+///
+/// \param[in] Replacements Replacements to apply.
+/// \param[out] Result The contents of the file after applying replacements.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \li true if all replacements applied successfully.
+/// \li false if at least one replacement failed to apply.
+bool applyReplacements(const std::vector<tooling::Replacement> &Replacements,
+ std::string &Result,
+ DiagnosticsEngine &Diagnostics) {
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(Replacements, Rewrites, Result);
+}
+
+/// \brief Apply code formatting to all places where replacements were made.
+///
+/// \pre !Replacements.empty().
+/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath().
+/// \pre Replacements[i].getOffset() <= Replacements[i+1].getOffset().
+///
+/// \param[in] Replacements Replacements that were made to the file. Provided
+/// to indicate where changes were made.
+/// \param[in] FileData The contents of the file \b after \c Replacements have
+/// been applied.
+/// \param[out] FormattedFileData The contents of the file after reformatting.
+/// \param[in] Diagnostics For diagnostic output.
+///
+/// \returns \li true if reformatting replacements were all successfully
+/// applied.
+/// \li false if at least one reformatting replacement failed to apply.
+bool applyFormatting(const std::vector<tooling::Replacement> &Replacements,
+ const StringRef FileData,
+ std::string &FormattedFileData,
+ const format::FormatStyle &FormatStyle,
+ DiagnosticsEngine &Diagnostics) {
+ assert(!Replacements.empty() && "Need at least one replacement");
+
+ RangeVector Ranges = calculateChangedRanges(Replacements);
+
+ StringRef FileName = Replacements.begin()->getFilePath();
+ tooling::Replacements R =
+ format::reformat(FormatStyle, FileData, Ranges, FileName);
+
+ // FIXME: Remove this copy when tooling::Replacements is implemented as a
+ // vector instead of a set.
+ std::vector<tooling::Replacement> FormattingReplacements;
+ std::copy(R.begin(), R.end(), back_inserter(FormattingReplacements));
+
+ if (FormattingReplacements.empty()) {
+ FormattedFileData = FileData;
+ return true;
+ }
+
+ FileManager Files((FileSystemOptions()));
+ SourceManager SM(Diagnostics, Files);
+ SM.overrideFileContents(Files.getFile(FileName),
+ llvm::MemoryBuffer::getMemBufferCopy(FileData));
+ Rewriter Rewrites(SM, LangOptions());
+
+ return getRewrittenData(FormattingReplacements, Rewrites, FormattedFileData);
+}
+
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv);
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
DiagnosticsEngine Diagnostics(
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()),
DiagOpts.getPtr());
+ // Determine a formatting style from options.
+ format::FormatStyle FormatStyle;
+ if (DoFormat)
+ FormatStyle = format::getStyle(FormatStyleOpt, FormatStyleConfig);
+
TUReplacements TUs;
TUReplacementFiles TURFiles;
@@ -83,14 +210,42 @@
if (!mergeAndDeduplicate(TUs, GroupedReplacements, SM))
return 1;
- Rewriter DestRewriter(SM, LangOptions());
- if (!applyReplacements(GroupedReplacements, DestRewriter)) {
- errs() << "Failed to apply all replacements. No changes made.\n";
- return 1;
+ Rewriter ReplacementsRewriter(SM, LangOptions());
+
+ for (FileToReplacementsMap::const_iterator I = GroupedReplacements.begin(),
+ E = GroupedReplacements.end();
+ I != E; ++I) {
+
+ std::string NewFileData;
+
+ // This shouldn't happen but if a file somehow has no replacements skip to
+ // next file.
+ if (I->getValue().empty())
+ continue;
+
+ if (!applyReplacements(I->getValue(), NewFileData, Diagnostics)) {
+ errs() << "Failed to apply replacements to " << I->getKey() << "\n";
+ continue;
+ }
+
+ // Apply formatting if requested.
+ if (DoFormat && !applyFormatting(I->getValue(), NewFileData, NewFileData,
+ FormatStyle, Diagnostics)) {
+ errs() << "Failed to apply reformatting replacements for " << I->getKey()
+ << "\n";
+ continue;
+ }
+
+ // Write new file to disk
+ std::string ErrorInfo;
+ llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo);
+ if (!ErrorInfo.empty()) {
+ llvm::errs() << "Could not open " << I->getKey() << " for writing\n";
+ continue;
+ }
+
+ FileStream << NewFileData;
}
- if (!writeFiles(DestRewriter))
- return 1;
-
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,22 @@
+class MyType012345678901234567890123456789 {};
+
+void g(int, int*, int, int*, int, int*, int);
+
+void f() {
+ MyType012345678901234567890123456789 *a =
+ new MyType012345678901234567890123456789();
+ // CHECK: {{^\ \ auto\ a\ \=\ new\ MyType012345678901234567890123456789\(\);}}
+
+ int iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii;
+ int jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj;
+ int kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk;
+ int mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm;
+ g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, 0, jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj,
+ 0, kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, 0, mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+ // CHECK: g(iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii, nullptr,
+ // CHECK-NEXT: jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj, nullptr,
+ // CHECK-NEXT: kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk, nullptr,
+ // CHECK-NEXT: mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm);
+
+ delete a;
+}
Index: test/clang-apply-replacements/Inputs/format/yes.yaml
===================================================================
--- /dev/null
+++ test/clang-apply-replacements/Inputs/format/yes.yaml
@@ -0,0 +1,22 @@
+# These replacements are out of order on purpose to test that they get sorted
+# so that formatting happens correctly.
+---
+MainSourceFile: yes.cpp
+Replacements:
+ - FilePath: $(path)/yes.cpp
+ Offset: 494
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 410
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 454
+ Length: 1
+ ReplacementText: nullptr
+ - FilePath: $(path)/yes.cpp
+ Offset: 108
+ 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,69 @@
+//===- clang-apply-replacements/ReformattingTest.cpp ----------------------===//
+//
+// 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() << ")";
+}
+} // namespace tooling
+} // namespace clang
+
+// Ensure zero-length ranges are produced. Even lines where things are deleted
+// need reformatting.
+TEST(CalculateChangedRangesTest, producesZeroLengthRange) {
+ RangeVector Changes = calculateChangedRanges(makeReplacements(0, 4, 0));
+ EXPECT_EQ(Range(0, 0), Changes.front());
+}
+
+// Basic test to ensure replacements turn into ranges properly.
+TEST(CalculateChangedRangesTest, calculatesRanges) {
+ ReplacementsVec R;
+ R.push_back(makeReplacement(2, 0, 3));
+ R.push_back(makeReplacement(5, 2, 4));
+ RangeVector Changes = calculateChangedRanges(R);
+
+ Range ExpectedRanges[] = { Range(2, 3), Range(8, 4) };
+ EXPECT_TRUE(std::equal(Changes.begin(), Changes.end(), ExpectedRanges));
+}
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/ReformattingTest.cpp
===================================================================
--- unittests/clang-modernize/ReformattingTest.cpp
+++ unittests/clang-modernize/ReformattingTest.cpp
@@ -11,7 +11,7 @@
#include "Core/FileOverrides.h"
#include "Core/Refactoring.h"
#include "gtest/gtest.h"
-#include "VirtualFileHelper.h"
+#include "common/VirtualFileHelper.h"
using namespace clang;
using namespace clang::tooling;
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