diff options
Diffstat (limited to 'clang-tools-extra/cpp11-migrate')
7 files changed, 390 insertions, 15 deletions
diff --git a/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt b/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt index 562ae65e1eb..d89d86cc545 100644 --- a/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt +++ b/clang-tools-extra/cpp11-migrate/Core/CMakeLists.txt @@ -7,9 +7,12 @@ add_clang_library(migrateCore Transform.cpp IncludeExcludeInfo.cpp PerfSupport.cpp + Reformatting.cpp ) target_link_libraries(migrateCore + clangFormat clangTooling clangBasic clangASTMatchers + clangRewriteFrontend ) diff --git a/clang-tools-extra/cpp11-migrate/Core/FileOverrides.cpp b/clang-tools-extra/cpp11-migrate/Core/FileOverrides.cpp index 200b7fa2d9e..ae90b113bdb 100644 --- a/clang-tools-extra/cpp11-migrate/Core/FileOverrides.cpp +++ b/clang-tools-extra/cpp11-migrate/Core/FileOverrides.cpp @@ -17,8 +17,6 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticOptions.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/Tooling.h" #include "llvm/Support/FileSystem.h" @@ -29,10 +27,11 @@ using namespace clang; using namespace clang::tooling; -SourceOverrides::SourceOverrides(llvm::StringRef MainFileName) - : MainFileName(MainFileName) {} +SourceOverrides::SourceOverrides(llvm::StringRef MainFileName, + bool TrackChanges) + : MainFileName(MainFileName), TrackChanges(TrackChanges) {} -void SourceOverrides::applyReplacements(tooling::Replacements &Replaces) { +void SourceOverrides::applyReplacements(Replacements &Replaces) { llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts( new DiagnosticOptions()); DiagnosticsEngine Diagnostics( @@ -57,6 +56,8 @@ void SourceOverrides::applyReplacements(tooling::Replacements &Replaces, llvm::errs() << "error: failed to apply some replacements."; applyRewrites(Rewrites); + if (TrackChanges) + adjustChangedRanges(Replaces); } void SourceOverrides::applyRewrites(Rewriter &Rewrites) { @@ -96,6 +97,30 @@ void SourceOverrides::applyRewrites(Rewriter &Rewrites) { } } +void SourceOverrides::adjustChangedRanges(const Replacements &Replaces) { + // Start by grouping replacements by file name + Replacements MainFileReplaces; + llvm::StringMap<Replacements> HeadersReplaces; + + for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; + ++I) { + llvm::StringRef ReplacementFileName = I->getFilePath(); + + if (ReplacementFileName == MainFileName) + MainFileReplaces.insert(*I); + else + HeadersReplaces[ReplacementFileName].insert(*I); + } + + // Then adjust the changed ranges for each individual file + MainFileChanges.adjustChangedRanges(Replaces); + for (llvm::StringMap<Replacements>::iterator I = HeadersReplaces.begin(), + E = HeadersReplaces.end(); + I != E; ++I) { + Headers[I->getKey()].Changes.adjustChangedRanges(I->getValue()); + } +} + void SourceOverrides::applyOverrides(SourceManager &SM) const { FileManager &FM = SM.getFileManager(); @@ -154,6 +179,109 @@ SourceOverrides &FileOverrides::getOrCreate(llvm::StringRef Filename) { SourceOverrides *&Override = Overrides[Filename]; if (Override == NULL) - Override = new SourceOverrides(Filename); + Override = new SourceOverrides(Filename, TrackChanges); return *Override; } + +namespace { + +/// \brief Comparator to be able to order tooling::Range based on their offset. +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(); +} + +/// \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()(clang::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 + +void ChangedRanges::adjustChangedRanges(const tooling::Replacements &Replaces) { + // first adjust existing ranges in case they overlap with the replacements + for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; + ++I) { + const tooling::Replacement &Replace = *I; + + std::transform(Ranges.begin(), Ranges.end(), Ranges.begin(), + RangeReplacedAdjuster(Replace)); + } + + // then shift existing ranges to reflect the new positions + for (RangeVec::iterator I = Ranges.begin(), E = Ranges.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 (Replacements::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(); + + Ranges.push_back(tooling::Range(Offset, Length)); + } + + // cleanups unecessary ranges to finish + coalesceRanges(); +} + +void ChangedRanges::coalesceRanges() { + // 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(&Range::contains)), + Ranges.end()); +} diff --git a/clang-tools-extra/cpp11-migrate/Core/FileOverrides.h b/clang-tools-extra/cpp11-migrate/Core/FileOverrides.h index 7f8c8d20011..2bb3daea7b0 100644 --- a/clang-tools-extra/cpp11-migrate/Core/FileOverrides.h +++ b/clang-tools-extra/cpp11-migrate/Core/FileOverrides.h @@ -29,6 +29,34 @@ class SourceManager; class Rewriter; } // namespace clang +/// \brief Class encapsulating a list of \c tooling::Range with some +/// convenience methods. +/// +/// The ranges stored are used to keep track of the overriden parts of a file. +class ChangedRanges { + typedef std::vector<clang::tooling::Range> RangeVec; + +public: + typedef RangeVec::const_iterator const_iterator; + + /// \brief Create new ranges from the replacements and adjust existing one + /// to remove replaced parts. + /// + /// Note that all replacements should come from the same file. + void adjustChangedRanges(const clang::tooling::Replacements &Replaces); + + /// \brief Iterators. + /// @{ + const_iterator begin() const { return Ranges.begin(); } + const_iterator end() const { return Ranges.end(); } + /// @} + +private: + void coalesceRanges(); + + RangeVec Ranges; +}; + /// \brief Container for storing override information for a single headers. struct HeaderOverride { HeaderOverride() {} @@ -36,6 +64,7 @@ struct HeaderOverride { std::string FileName; std::string FileOverride; + ChangedRanges Changes; }; /// \brief Container mapping header file names to override information. @@ -46,12 +75,18 @@ typedef llvm::StringMap<HeaderOverride> HeaderOverrides; /// which changes have been made. class SourceOverrides { public: - SourceOverrides(llvm::StringRef MainFileName); + SourceOverrides(llvm::StringRef MainFileName, bool TrackChanges); /// \brief Accessors. /// @{ llvm::StringRef getMainFileName() const { return MainFileName; } llvm::StringRef getMainFileContent() const { return MainFileOverride; } + const ChangedRanges &getChangedRanges() const { return MainFileChanges; } + + /// \brief Is file change tracking enabled? + /// + /// Tracking file changes can be useful to reformat the code for example. + bool isTrackingFileChanges() const { return TrackChanges; } /// @} /// \brief Indicates if the source file has been overridden. @@ -87,8 +122,14 @@ private: /// file content overrides. void applyRewrites(clang::Rewriter &Rewrite); + /// \brief Adjust the changed ranges to reflect the parts of the files that + /// have been replaced. + void adjustChangedRanges(const clang::tooling::Replacements &Replaces); + const std::string MainFileName; std::string MainFileOverride; + const bool TrackChanges; + ChangedRanges MainFileChanges; HeaderOverrides Headers; }; @@ -98,7 +139,11 @@ public: typedef llvm::StringMap<SourceOverrides *> SourceOverridesMap; typedef SourceOverridesMap::const_iterator const_iterator; - FileOverrides() {} + /// \brief Construct the SourceOverrides manager. + /// + /// \param TrackChanges Wether or not the \c SourceOverrides should keep track + /// of changes. See \c SourceOverrides::isTrackingFileChanges(). + FileOverrides(bool TrackChanges) : TrackChanges(TrackChanges) {} ~FileOverrides(); const_iterator find(llvm::StringRef Filename) const { @@ -120,6 +165,7 @@ private: FileOverrides &operator=(const FileOverrides &) LLVM_DELETED_FUNCTION; SourceOverridesMap Overrides; + const bool TrackChanges; }; /// \brief Generate a unique filename to store the replacements. diff --git a/clang-tools-extra/cpp11-migrate/Core/Reformatting.cpp b/clang-tools-extra/cpp11-migrate/Core/Reformatting.cpp new file mode 100644 index 00000000000..3fd77612bbf --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/Core/Reformatting.cpp @@ -0,0 +1,76 @@ +//===-- Core/Reformatting.cpp - LibFormat integration ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides the LibFormat integration used to reformat +/// migrated code. +/// +//===----------------------------------------------------------------------===// + +#include "Core/Reformatting.h" +#include "Core/FileOverrides.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; + +void Reformatter::reformatChanges(SourceOverrides &Overrides) { + llvm::IntrusiveRefCntPtr<clang::DiagnosticOptions> DiagOpts( + new DiagnosticOptions()); + DiagnosticsEngine Diagnostics( + llvm::IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs()), + DiagOpts.getPtr()); + FileManager Files((FileSystemOptions())); + SourceManager SM(Diagnostics, Files); + + reformatChanges(Overrides, SM); +} + +void Reformatter::reformatChanges(SourceOverrides &Overrides, + clang::SourceManager &SM) { + tooling::Replacements Replaces; + Overrides.applyOverrides(SM); + if (Overrides.isSourceOverriden()) + Replaces = reformatSingleFile(Overrides.getMainFileName(), + Overrides.getChangedRanges(), SM); + + for (HeaderOverrides::const_iterator I = Overrides.headers_begin(), + E = Overrides.headers_end(); + I != E; ++I) { + const HeaderOverride &Header = I->getValue(); + const tooling::Replacements &HeaderReplaces = + reformatSingleFile(Header.FileName, Header.Changes, SM); + Replaces.insert(HeaderReplaces.begin(), HeaderReplaces.end()); + } + Overrides.applyReplacements(Replaces, SM); +} + +tooling::Replacements Reformatter::reformatSingleFile( + llvm::StringRef FileName, const ChangedRanges &Changes, SourceManager &SM) { + const clang::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(), clang::SrcMgr::C_User); + + std::vector<CharSourceRange> ReformatRanges; + SourceLocation StartOfFile = SM.getLocForStartOfFile(ID); + for (ChangedRanges::const_iterator I = Changes.begin(), E = Changes.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)); + return format::reformat(Style, Lex, SM, ReformatRanges); +} diff --git a/clang-tools-extra/cpp11-migrate/Core/Reformatting.h b/clang-tools-extra/cpp11-migrate/Core/Reformatting.h new file mode 100644 index 00000000000..ac9fef381c6 --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/Core/Reformatting.h @@ -0,0 +1,59 @@ +//===-- Core/Reformatting.h - LibFormat integration -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides the LibFormat integration used to reformat +/// migrated code. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_REFORMATTING_H +#define CPP11_MIGRATE_REFORMATTING_H + +#include "clang/Format/Format.h" + +class SourceOverrides; +class ChangedRanges; + +class Reformatter { +public: + Reformatter(const clang::format::FormatStyle &Style) : Style(Style) {} + + /// \brief Reformat the changes made to the file overrides. + /// + /// \param Overrides Overriden source files to reformat. Note that since only + /// the changes are reformatted, file change tracking has to be enabled. + /// \param SM A SourceManager where the overridens files can be found. + /// + /// \sa \c SourceOverrides::isTrackingFileChanges() + void reformatChanges(SourceOverrides &Overrides, clang::SourceManager &SM); + + /// \brief Overload of \c reformatChanges() providing it's own + /// \c SourceManager. + void reformatChanges(SourceOverrides &Overrides); + + /// \brief Produce a list of replacements to apply on \p FileName, only the + /// ranges in \p Changes are replaced. + /// + /// Since this routine use \c clang::format::reformat() the rules that applies + /// on the ranges are identical: + /// + /// <blockquote> Each range is extended on either end to its next bigger logic + /// unit, i.e. everything that might influence its formatting or might be + /// influenced by its formatting. + /// -- \c clang::format::reformat()</blockquote> + clang::tooling::Replacements reformatSingleFile(llvm::StringRef FileName, + const ChangedRanges &Changes, + clang::SourceManager &SM); + +private: + clang::format::FormatStyle Style; +}; + +#endif // CPP11_MIGRATE_REFORMATTING_H diff --git a/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp b/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp index 66839fdb1a9..b5b6a410ab0 100644 --- a/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp +++ b/clang-tools-extra/cpp11-migrate/tool/Cpp11Migrate.cpp @@ -20,6 +20,7 @@ #include "Core/SyntaxCheck.h" #include "Core/Transform.h" #include "Core/Transforms.h" +#include "Core/Reformatting.h" #include "LoopConvert/LoopConvert.h" #include "UseNullptr/UseNullptr.h" #include "UseAuto/UseAuto.h" @@ -28,6 +29,7 @@ #include "clang/Frontend/FrontendActions.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Signals.h" namespace cl = llvm::cl; @@ -43,9 +45,9 @@ static cl::extrahelp MoreHelp( " cpp11-migrate -use-auto path/to/file.cpp -- -Ipath/to/include/\n" "\n" "Convert for loops to the new ranged-based for loops on all files in a " - "subtree:\n\n" + "subtree\nand reformat the code automatically using the LLVM style:\n\n" " find path/in/subtree -name '*.cpp' -exec \\\n" - " cpp11-migrate -p build/path -loop-convert {} ';'\n" + " cpp11-migrate -p build/path -format-style=LLVM -loop-convert {} ';'\n" "\n" "Make use of both nullptr and the override specifier, using git ls-files:\n" "\n" @@ -70,6 +72,14 @@ static cl::opt<bool> FinalSyntaxCheck( cl::desc("Check for correct syntax after applying transformations"), cl::init(false)); +static cl::opt<std::string> FormatStyleOpt( + "format-style", + cl::desc("Coding style to use on the replacements, either a builtin style\n" + "or a YAML config file (see: clang-format -dump-config).\n" + "Currently supports 4 builtins style:\n" + " LLVM, Google, Chromium, Mozilla.\n"), + cl::value_desc("string")); + static cl::opt<bool> SummaryMode("summary", cl::desc("Print transform summary"), cl::init(false)); @@ -108,6 +118,40 @@ static cl::opt<bool, /*ExternalStorage=*/true> EnableHeaderModifications( cl::location(GlobalOptions.EnableHeaderModifications), cl::init(false)); +/// \brief Creates the Reformatter if the format style option is provided, +/// return a null pointer otherwise. +/// +/// \param ProgName The name of the program, \c argv[0], used to print errors. +/// \param Error If the \c -format-style is provided but with wrong parameters +/// this is parameter is set to \c true, left untouched otherwise. An error +/// message is printed with an explanation. +static Reformatter *handleFormatStyle(const char *ProgName, bool &Error) { + if (FormatStyleOpt.getNumOccurrences() > 0) { + format::FormatStyle Style; + if (!format::getPredefinedStyle(FormatStyleOpt, &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) { + llvm::errs() << ProgName << ": invalid format style " << FormatStyleOpt + << ": " << ec.message() << "\n"; + Error = true; + return 0; + } + } + + // force mode to C++11 + Style.Standard = clang::format::FormatStyle::LS_Cpp11; + return new Reformatter(Style); + } + return 0; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); Transforms TransformManager; @@ -137,6 +181,13 @@ int main(int argc, const char **argv) { // against the default value when the command line option is not specified. GlobalOptions.EnableTiming = (TimingDirectoryName != NoTiming); + // Check the reformatting style option + bool BadStyle = false; + llvm::OwningPtr<Reformatter> ChangesReformatter( + handleFormatStyle(argv[0], BadStyle)); + if (BadStyle) + return 1; + // Populate the ModifiableHeaders structure if header modifications are // enabled. if (GlobalOptions.EnableHeaderModifications) { @@ -153,7 +204,10 @@ int main(int argc, const char **argv) { return 1; } - FileOverrides FileStates; + // if reformatting is enabled we wants to track file changes so that it's + // possible to reformat them. + bool TrackReplacements = static_cast<bool>(ChangesReformatter); + FileOverrides FileStates(TrackReplacements); SourcePerfData PerfData; // Apply transforms. @@ -183,6 +237,15 @@ int main(int argc, const char **argv) { } } + // Reformat changes if a reformatter is provided. + if (ChangesReformatter) + for (FileOverrides::const_iterator I = FileStates.begin(), + E = FileStates.end(); + I != E; ++I) { + SourceOverrides &Overrides = *I->second; + ChangesReformatter->reformatChanges(Overrides); + } + if (FinalSyntaxCheck) if (!doSyntaxCheck(OptionsParser.getCompilations(), OptionsParser.getSourcePathList(), FileStates)) diff --git a/clang-tools-extra/cpp11-migrate/tool/Makefile b/clang-tools-extra/cpp11-migrate/tool/Makefile index 5185163d008..d59fa14062c 100644 --- a/clang-tools-extra/cpp11-migrate/tool/Makefile +++ b/clang-tools-extra/cpp11-migrate/tool/Makefile @@ -34,10 +34,10 @@ SOURCES += $(addprefix ../ReplaceAutoPtr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/.. BUILT_SOURCES += $(ObjDir)/../ReplaceAutoPtr/.objdir LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc mcparser option -USEDLIBS = migrateCore.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 +USEDLIBS = migrateCore.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 |