diff options
22 files changed, 422 insertions, 15 deletions
diff --git a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt index c786f25325b..7ab29723045 100644 --- a/clang-tools-extra/clang-apply-replacements/CMakeLists.txt +++ b/clang-tools-extra/clang-apply-replacements/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(clangApplyReplacements clangTooling clangBasic clangRewriteFrontend + clangFormat ) include_directories( diff --git a/clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h b/clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h index b1cae5d05ff..e90b3b30b4e 100644 --- a/clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h +++ b/clang-tools-extra/clang-apply-replacements/include/clang-apply-replacements/Tooling/ApplyReplacements.h @@ -28,8 +28,15 @@ namespace clang { 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 @@ collectReplacementsFromDirectory(const llvm::StringRef Directory, /// \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 mergeAndDeduplicate(const TUReplacements &TUs, 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. /// diff --git a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp b/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp index 660c58bdbe1..037caf36254 100644 --- a/clang-tools-extra/clang-apply-replacements/lib/Tooling/ApplyReplacements.cpp +++ b/clang-tools-extra/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 @@ static void eatDiagnostics(const SMDiagnostic &, void *) {} namespace clang { namespace replace { - llvm::error_code collectReplacementsFromDirectory(const llvm::StringRef Directory, TUReplacements &TUs, @@ -128,6 +129,8 @@ static void reportConflict( /// \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 @@ bool applyReplacements(const FileToReplacementsMap &GroupedReplacements, 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(), diff --git a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp index c714b221c62..15fa0e8da45 100644 --- a/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp +++ b/clang-tools-extra/clang-apply-replacements/tool/ClangApplyReplacementsMain.cpp @@ -18,6 +18,7 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" +#include "clang/Format/Format.h" #include "clang/Rewrite/Core/Rewriter.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringSet.h" @@ -30,6 +31,8 @@ using namespace clang::replace; 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" @@ -39,7 +42,31 @@ static cl::opt<bool> RemoveTUReplacementFiles( // Update this list of options to show in -help as new options are added. // Should add even those options marked as 'Hidden'. Any option not listed // here will get marked 'ReallyHidden' so they don't appear in any -help text. -const char *OptionsToShow[] = { "help", "version", "remove-change-desc-files" }; +const char *OptionsToShow[] = { "help", "version", + "remove-change-desc-files", "format", + "style-config", "style" }; + +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)); + +// FIXME: Consider making the default behaviour for finding a style +// configuration file to start the search anew for every file being changed to +// handle situations where the style is different for different parts of a +// project. + +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. @@ -62,6 +89,111 @@ void printVersion() { outs() << "clang-apply-replacements version " CLANG_VERSION_STRING << "\n"; } +/// \brief Convenience function to get rewritten content for \c Filename from +/// \c Rewrites. +/// +/// \pre Replacements[i].getFilePath() == Replacements[i+1].getFilePath(). +/// \post Replacements.empty() -> Result.empty() +/// +/// \param[in] Replacements Replacements to apply +/// \param[in] Rewrites Rewriter to use to apply replacements. +/// \param[out] Result Contents of the file after applying replacements if +/// replacements were provided. +/// +/// \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) { + if (Replacements.empty()) return true; + + 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(). +/// \post Replacements.empty() -> Result.empty() +/// +/// \param[in] Replacements Replacements to apply. +/// \param[out] Result Contents of the file after applying replacements if +/// replacements were provided. +/// \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) { // Only include our options in -help output. StringMap<cl::Option*> OptMap; @@ -81,6 +213,11 @@ int main(int argc, char **argv) { 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; @@ -106,14 +243,42 @@ int main(int argc, char **argv) { 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()); - if (!writeFiles(DestRewriter)) - return 1; + 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; + } return 0; } diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/format/no.cpp b/clang-tools-extra/test/clang-apply-replacements/Inputs/format/no.cpp new file mode 100644 index 00000000000..5bc90811dfb --- /dev/null +++ b/clang-tools-extra/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\(\);}} +} diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/format/no.yaml b/clang-tools-extra/test/clang-apply-replacements/Inputs/format/no.yaml new file mode 100644 index 00000000000..ee85843f8a0 --- /dev/null +++ b/clang-tools-extra/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 ' +... diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/format/yes.cpp b/clang-tools-extra/test/clang-apply-replacements/Inputs/format/yes.cpp new file mode 100644 index 00000000000..8f600c38f27 --- /dev/null +++ b/clang-tools-extra/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; +} diff --git a/clang-tools-extra/test/clang-apply-replacements/Inputs/format/yes.yaml b/clang-tools-extra/test/clang-apply-replacements/Inputs/format/yes.yaml new file mode 100644 index 00000000000..e87f067b5ad --- /dev/null +++ b/clang-tools-extra/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 ' +... diff --git a/clang-tools-extra/test/clang-apply-replacements/format.cpp b/clang-tools-extra/test/clang-apply-replacements/format.cpp new file mode 100644 index 00000000000..7de320d34ae --- /dev/null +++ b/clang-tools-extra/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 diff --git a/clang-tools-extra/unittests/CMakeLists.txt b/clang-tools-extra/unittests/CMakeLists.txt index 44d61d68fd1..b86a7d9ca11 100644 --- a/clang-tools-extra/unittests/CMakeLists.txt +++ b/clang-tools-extra/unittests/CMakeLists.txt @@ -5,5 +5,6 @@ function(add_extra_unittest test_dirname) add_unittest(ExtraToolsUnitTests ${test_dirname} ${ARGN}) endfunction() +add_subdirectory(clang-apply-replacements) add_subdirectory(clang-modernize) add_subdirectory(clang-tidy) diff --git a/clang-tools-extra/unittests/Makefile b/clang-tools-extra/unittests/Makefile index 6a9ae081bee..c4c1f3b2bca 100644 --- a/clang-tools-extra/unittests/Makefile +++ b/clang-tools-extra/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 diff --git a/clang-tools-extra/unittests/clang-apply-replacements/CMakeLists.txt b/clang-tools-extra/unittests/clang-apply-replacements/CMakeLists.txt new file mode 100644 index 00000000000..9da1410178c --- /dev/null +++ b/clang-tools-extra/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 + ) diff --git a/clang-tools-extra/unittests/clang-apply-replacements/Makefile b/clang-tools-extra/unittests/clang-apply-replacements/Makefile new file mode 100644 index 00000000000..6e6c2fc5c59 --- /dev/null +++ b/clang-tools-extra/unittests/clang-apply-replacements/Makefile @@ -0,0 +1,23 @@ +##===- unittests/clang-apply-replacements/Makefile ---------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../../../.. +include $(CLANG_LEVEL)/../../Makefile.config + +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 + +include $(CLANG_LEVEL)/Makefile +MAKEFILE_UNITTEST_NO_INCLUDE_COMMON := 1 +CPP.Flags += -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include -I$(PROJ_SRC_DIR)/../include +include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/clang-tools-extra/unittests/clang-apply-replacements/ReformattingTest.cpp b/clang-tools-extra/unittests/clang-apply-replacements/ReformattingTest.cpp new file mode 100644 index 00000000000..e16308223c9 --- /dev/null +++ b/clang-tools-extra/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)); +} diff --git a/clang-tools-extra/unittests/clang-modernize/CMakeLists.txt b/clang-tools-extra/unittests/clang-modernize/CMakeLists.txt index fb01a445d85..3022a610549 100644 --- a/clang-tools-extra/unittests/clang-modernize/CMakeLists.txt +++ b/clang-tools-extra/unittests/clang-modernize/CMakeLists.txt @@ -6,9 +6,12 @@ get_filename_component(CLANG_MODERNIZE_SOURCE_DIR ${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( ${CLANG_MODERNIZE_SOURCE_DIR} ${ClangReplaceLocation} + ${CommonIncLocation} ) add_extra_unittest(ClangModernizeTests diff --git a/clang-tools-extra/unittests/clang-modernize/FileOverridesTest.cpp b/clang-tools-extra/unittests/clang-modernize/FileOverridesTest.cpp index 55f7f5dfced..94776bc2c10 100644 --- a/clang-tools-extra/unittests/clang-modernize/FileOverridesTest.cpp +++ b/clang-tools-extra/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; diff --git a/clang-tools-extra/unittests/clang-modernize/IncludeDirectivesTest.cpp b/clang-tools-extra/unittests/clang-modernize/IncludeDirectivesTest.cpp index 9e2add09734..b6ad4af6876 100644 --- a/clang-tools-extra/unittests/clang-modernize/IncludeDirectivesTest.cpp +++ b/clang-tools-extra/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" diff --git a/clang-tools-extra/unittests/clang-modernize/IncludeExcludeTest.cpp b/clang-tools-extra/unittests/clang-modernize/IncludeExcludeTest.cpp index a7df1da634e..dd41cdfce1a 100644 --- a/clang-tools-extra/unittests/clang-modernize/IncludeExcludeTest.cpp +++ b/clang-tools-extra/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" diff --git a/clang-tools-extra/unittests/clang-modernize/Makefile b/clang-tools-extra/unittests/clang-modernize/Makefile index 6b6e638c778..71c36793e63 100644 --- a/clang-tools-extra/unittests/clang-modernize/Makefile +++ b/clang-tools-extra/unittests/clang-modernize/Makefile @@ -21,5 +21,6 @@ USEDLIBS = modernizeCore.a clangFormat.a clangApplyReplacements.a clangTooling.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-modernize -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include \ + -I$(PROJ_SRC_DIR)/../include include $(LLVM_SRC_ROOT)/unittests/Makefile.unittest diff --git a/clang-tools-extra/unittests/clang-modernize/ReformattingTest.cpp b/clang-tools-extra/unittests/clang-modernize/ReformattingTest.cpp index d81e20bac5a..1359129714c 100644 --- a/clang-tools-extra/unittests/clang-modernize/ReformattingTest.cpp +++ b/clang-tools-extra/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; diff --git a/clang-tools-extra/unittests/clang-modernize/Utility.h b/clang-tools-extra/unittests/include/common/Utility.h index c934d4806e0..c934d4806e0 100644 --- a/clang-tools-extra/unittests/clang-modernize/Utility.h +++ b/clang-tools-extra/unittests/include/common/Utility.h diff --git a/clang-tools-extra/unittests/clang-modernize/VirtualFileHelper.h b/clang-tools-extra/unittests/include/common/VirtualFileHelper.h index 2960c72ab0d..2960c72ab0d 100644 --- a/clang-tools-extra/unittests/clang-modernize/VirtualFileHelper.h +++ b/clang-tools-extra/unittests/include/common/VirtualFileHelper.h |