diff options
Diffstat (limited to 'clang-tools-extra/clang-modernize/Core')
20 files changed, 2350 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-modernize/Core/CMakeLists.txt b/clang-tools-extra/clang-modernize/Core/CMakeLists.txt new file mode 100644 index 00000000000..787388b69e2 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/CMakeLists.txt @@ -0,0 +1,19 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(modernizeCore + FileOverrides.cpp + SyntaxCheck.cpp + Transforms.cpp + Transform.cpp + IncludeExcludeInfo.cpp + PerfSupport.cpp + Reformatting.cpp + IncludeDirectives.cpp + ) +target_link_libraries(modernizeCore + clangFormat + clangTooling + clangBasic + clangASTMatchers + clangRewriteFrontend + ) diff --git a/clang-tools-extra/clang-modernize/Core/CustomMatchers.h b/clang-tools-extra/clang-modernize/Core/CustomMatchers.h new file mode 100644 index 00000000000..9af04977996 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/CustomMatchers.h @@ -0,0 +1,59 @@ +//===-- Core/CustomMatchers.h - Perf measurement helpers -----------*- 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 custom matchers to be used by different +/// transforms that requier the same matchers. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_CUSTOMMATCHERS_H +#define CPP11_MIGRATE_CUSTOMMATCHERS_H + +#include "clang/ASTMatchers/ASTMatchers.h" + +namespace clang { +namespace ast_matchers { + +/// \brief Matches declarations whose declaration context is the C++ standard +/// library namespace \c std. +/// +/// Note that inline namespaces are silently ignored during the lookup since +/// both libstdc++ and libc++ are known to use them for versioning purposes. +/// +/// Given +/// \code +/// namespace ns { +/// struct my_type {}; +/// using namespace std; +/// } +/// +/// using std::vector; +/// using ns::my_type; +/// using ns::list; +/// \endcode +/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace()))) +/// matches "using std::vector" and "using ns::list". +AST_MATCHER(Decl, isFromStdNamespace) { + const DeclContext *D = Node.getDeclContext(); + + while (D->isInlineNamespace()) + D = D->getParent(); + + if (!D->isNamespace() || !D->getParent()->isTranslationUnit()) + return false; + + const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier(); + + return Info && Info->isStr("std"); +} +} // namespace ast_matchers +} // namespace clang + +#endif // CPP11_MIGRATE_CUSTOMMATCHERS_H diff --git a/clang-tools-extra/clang-modernize/Core/FileOverrides.cpp b/clang-tools-extra/clang-modernize/Core/FileOverrides.cpp new file mode 100644 index 00000000000..7ab7e91c30c --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/FileOverrides.cpp @@ -0,0 +1,198 @@ +//===-- Core/FileOverrides.cpp --------------------------------------------===// +// +// 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 types and functionality for dealing with source +/// and header file content overrides. +/// +//===----------------------------------------------------------------------===// + +#include "Core/FileOverrides.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Tooling/Tooling.h" +#include "clang/Tooling/ReplacementsYaml.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +#include <algorithm> + +using namespace clang; +using namespace clang::tooling; + +bool generateReplacementsFileName(const llvm::StringRef MainSourceFile, + llvm::SmallVectorImpl<char> &Result, + llvm::SmallVectorImpl<char> &Error) { + using namespace llvm::sys; + + Error.clear(); + if (llvm::error_code EC = fs::createUniqueFile( + MainSourceFile + "_%%_%%_%%_%%_%%_%%.yaml", Result)) { + Error.append(EC.message().begin(), EC.message().end()); + return false; + } + + return true; +} + +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::ReplacementsVec &Replaces) { + // first adjust existing ranges in case they overlap with the replacements + for (ReplacementsVec::const_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 (ReplacementsVec::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(); + + 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()); +} + +void FileOverrides::applyOverrides(clang::SourceManager &SM) const { + FileManager &FM = SM.getFileManager(); + + for (FileStateMap::const_iterator I = FileStates.begin(), + E = FileStates.end(); + I != E; ++I) { + SM.overrideFileContents(FM.getFile(I->getKey()), + llvm::MemoryBuffer::getMemBuffer(I->getValue())); + } +} + +void FileOverrides::adjustChangedRanges( + const clang::replace::FileToReplacementsMap &Replaces) { + + for (replace::FileToReplacementsMap::const_iterator I = Replaces.begin(), + E = Replaces.end(); I != E; ++I) { + ChangeTracking[I->getKey()].adjustChangedRanges(I->getValue()); + } +} + +void FileOverrides::updateState(const clang::Rewriter &Rewrites) { + for (Rewriter::const_buffer_iterator BufferI = Rewrites.buffer_begin(), + BufferE = Rewrites.buffer_end(); + BufferI != BufferE; ++BufferI) { + const char *FileName = + Rewrites.getSourceMgr().getFileEntryForID(BufferI->first)->getName(); + const RewriteBuffer &RewriteBuf = BufferI->second; + FileStates[FileName].assign(RewriteBuf.begin(), RewriteBuf.end()); + } +} + +bool FileOverrides::writeToDisk(DiagnosticsEngine &Diagnostics) const { + bool Errors = false; + for (FileStateMap::const_iterator I = FileStates.begin(), + E = FileStates.end(); + I != E; ++I) { + std::string ErrorInfo; + // The extra transform through std::string is to ensure null-termination + // of the filename stored as the key of the StringMap. + llvm::raw_fd_ostream FileStream(I->getKey().str().c_str(), ErrorInfo); + if (!ErrorInfo.empty()) { + llvm::errs() << "Failed to write new state for " << I->getKey() << ".\n"; + Errors = true; + } + FileStream << I->getValue(); + } + return !Errors; +} diff --git a/clang-tools-extra/clang-modernize/Core/FileOverrides.h b/clang-tools-extra/clang-modernize/Core/FileOverrides.h new file mode 100644 index 00000000000..8ed2914f0d2 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/FileOverrides.h @@ -0,0 +1,129 @@ +//===-- Core/FileOverrides.h ------------------------------------*- 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 types and functionality for dealing with source +/// and header file content overrides. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_FILE_OVERRIDES_H +#define CPP11_MIGRATE_FILE_OVERRIDES_H + +#include "Core/Refactoring.h" +#include "clang-apply-replacements/Tooling/ApplyReplacements.h" +#include "clang/Tooling/ReplacementsYaml.h" +#include "llvm/ADT/StringMap.h" + +// Forward Declarations +namespace llvm { +template <typename T> +class SmallVectorImpl; +} // namespace llvm +namespace clang { +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::ReplacementsVec &Replaces); + + /// \brief Iterators. + /// \{ + const_iterator begin() const { return Ranges.begin(); } + const_iterator end() const { return Ranges.end(); } + /// \} + +private: + void coalesceRanges(); + + RangeVec Ranges; +}; + +/// \brief Maintains current state of transformed files and tracks source ranges +/// where changes have been made. +class FileOverrides { +public: + /// \brief Maps file names to file contents. + typedef llvm::StringMap<std::string> FileStateMap; + + /// \brief Maps file names to change tracking info for a file. + typedef llvm::StringMap<ChangedRanges> ChangeMap; + + + /// \brief Override file contents seen by \c SM for all files stored by this + /// object. + void applyOverrides(clang::SourceManager &SM) const; + + /// \brief Update change tracking information based on replacements stored in + /// \c Replaces. + void + adjustChangedRanges(const clang::replace::FileToReplacementsMap &Replaces); + + /// \brief Accessor for change tracking information. + const ChangeMap &getChangedRanges() const { + return ChangeTracking; + } + + /// \brief Coalesce changes stored in \c Rewrites and replace file contents + /// overrides stored in this object. + /// + /// \param Rewrites Rewriter containing changes to files. + void updateState(const clang::Rewriter &Rewrites); + + /// \brief Accessor for current file state. + const FileStateMap &getState() const { return FileStates; } + + /// \brief Write all file contents overrides to disk. + /// + /// \param Diagnostics DiagnosticsEngine for error output. + /// + /// \returns \li true if all files with overridden file contents were written + /// to disk successfully. + /// \li false if any failure occurred. + bool writeToDisk(clang::DiagnosticsEngine &Diagnostics) const; + +private: + FileStateMap FileStates; + ChangeMap ChangeTracking; +}; + +/// \brief Generate a unique filename to store the replacements. +/// +/// Generates a unique filename in the same directory as \c MainSourceFile. The +/// filename is generated following this pattern: +/// +/// MainSourceFile_%%_%%_%%_%%_%%_%%.yaml +/// +/// where all '%' will be replaced by a randomly chosen hex number. +/// +/// \param[in] MainSourceFile Full path to the source file. +/// \param[out] Result The resulting unique filename in the same directory as +/// the \c MainSourceFile. +/// \param[out] Error If an error occurs a description of that error is +/// placed in this string. +/// \returns true on success, false if a unique file name could not be created. +bool generateReplacementsFileName(const llvm::StringRef MainSourceFile, + llvm::SmallVectorImpl<char> &Result, + llvm::SmallVectorImpl<char> &Error); + +#endif // CPP11_MIGRATE_FILE_OVERRIDES_H diff --git a/clang-tools-extra/clang-modernize/Core/IncludeDirectives.cpp b/clang-tools-extra/clang-modernize/Core/IncludeDirectives.cpp new file mode 100644 index 00000000000..f240950790b --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/IncludeDirectives.cpp @@ -0,0 +1,474 @@ +//===-- Core/IncludeDirectives.cpp - Include directives handling ----------===// +// +// 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 defines the IncludeDirectives class that helps with +/// detecting and modifying \#include directives. +/// +//===----------------------------------------------------------------------===// + +#include "IncludeDirectives.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include <stack> + +using namespace clang; +using namespace clang::tooling; +using llvm::StringRef; + +/// \brief PPCallbacks that fills-in the include information in the given +/// \c IncludeDirectives. +class IncludeDirectivesPPCallback : public clang::PPCallbacks { + // Struct helping the detection of header guards in the various callbacks + struct GuardDetection { + GuardDetection(FileID FID) + : FID(FID), Count(0), TheMacro(0), CountAtEndif(0) {} + + FileID FID; + // count for relevant preprocessor directives + unsigned Count; + // the macro that is tested in the top most ifndef for the header guard + // (e.g: GUARD_H) + const IdentifierInfo *TheMacro; + // the hash locations of #ifndef, #define, #endif + SourceLocation IfndefLoc, DefineLoc, EndifLoc; + // the value of Count once the #endif is reached + unsigned CountAtEndif; + + /// \brief Check that with all the information gathered if this is a + /// potential header guard. + /// + /// Meaning a top-most \#ifndef has been found, followed by a define and the + /// last preprocessor directive was the terminating \#endif. + /// + /// FIXME: accept the \#if !defined identifier form too. + bool isPotentialHeaderGuard() const { + return Count == CountAtEndif && DefineLoc.isValid(); + } + }; + +public: + IncludeDirectivesPPCallback(IncludeDirectives *Self) : Self(Self), Guard(0) {} + +private: + virtual ~IncludeDirectivesPPCallback() {} + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported) LLVM_OVERRIDE { + SourceManager &SM = Self->Sources; + const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(HashLoc)); + assert(FE && "Valid file expected."); + + IncludeDirectives::Entry E(HashLoc, File, IsAngled); + Self->FileToEntries[FE].push_back(E); + Self->IncludeAsWrittenToLocationsMap[FileName].push_back(HashLoc); + } + + // Keep track of the current file in the stack + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + SourceManager &SM = Self->Sources; + switch (Reason) { + case EnterFile: + Files.push(GuardDetection(SM.getFileID(Loc))); + Guard = &Files.top(); + break; + + case ExitFile: + if (Guard->isPotentialHeaderGuard()) + handlePotentialHeaderGuard(*Guard); + Files.pop(); + Guard = &Files.top(); + break; + + default: + break; + } + } + + /// \brief Mark this header as guarded in the IncludeDirectives if it's a + /// proper header guard. + void handlePotentialHeaderGuard(const GuardDetection &Guard) { + SourceManager &SM = Self->Sources; + const FileEntry *File = SM.getFileEntryForID(Guard.FID); + const LangOptions &LangOpts = Self->CI.getLangOpts(); + + // Null file can happen for the <built-in> buffer for example. They + // shouldn't have header guards though... + if (!File) + return; + + // The #ifndef should be the next thing after the preamble. We aren't + // checking for equality because it can also be part of the preamble if the + // preamble is the whole file. + unsigned Preamble = + Lexer::ComputePreamble(SM.getBuffer(Guard.FID), LangOpts).first; + unsigned IfndefOffset = SM.getFileOffset(Guard.IfndefLoc); + if (IfndefOffset > (Preamble + 1)) + return; + + // No code is allowed in the code remaining after the #endif. + const llvm::MemoryBuffer *Buffer = SM.getBuffer(Guard.FID); + Lexer Lex(SM.getLocForStartOfFile(Guard.FID), LangOpts, + Buffer->getBufferStart(), + Buffer->getBufferStart() + SM.getFileOffset(Guard.EndifLoc), + Buffer->getBufferEnd()); + + // Find the first newline not part of a multi-line comment. + Token Tok; + Lex.LexFromRawLexer(Tok); // skip endif + Lex.LexFromRawLexer(Tok); + + // Not a proper header guard, the remainder of the file contains something + // else than comments or whitespaces. + if (Tok.isNot(tok::eof)) + return; + + // Add to the location of the define to the IncludeDirectives for this file. + Self->HeaderToGuard[File] = Guard.DefineLoc; + } + + virtual void Ifndef(SourceLocation Loc, const Token &MacroNameTok, + const MacroDirective *MD) { + Guard->Count++; + + // If this #ifndef is the top-most directive and the symbol isn't defined + // store those information in the guard detection, the next step will be to + // check for the define. + if (Guard->Count == 1 && MD == 0) { + IdentifierInfo *MII = MacroNameTok.getIdentifierInfo(); + + if (MII->hasMacroDefinition()) + return; + Guard->IfndefLoc = Loc; + Guard->TheMacro = MII; + } + } + + virtual void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + Guard->Count++; + + // If this #define is the second directive of the file and the symbol + // defined is the same as the one checked in the #ifndef then store the + // information about this define. + if (Guard->Count == 2 && Guard->TheMacro != 0) { + IdentifierInfo *MII = MacroNameTok.getIdentifierInfo(); + + // macro unrelated to the ifndef, doesn't look like a proper header guard + if (MII->getName() != Guard->TheMacro->getName()) + return; + + Guard->DefineLoc = MacroNameTok.getLocation(); + } + } + + virtual void Endif(SourceLocation Loc, SourceLocation IfLoc) { + Guard->Count++; + + // If it's the #endif corresponding to the top-most #ifndef + if (Self->Sources.getDecomposedLoc(Guard->IfndefLoc) != + Self->Sources.getDecomposedLoc(IfLoc)) + return; + + // And that the top-most #ifndef was followed by the right #define + if (Guard->DefineLoc.isInvalid()) + return; + + // Then save the information about this #endif. Once the file is exited we + // will check if it was the final preprocessor directive. + Guard->CountAtEndif = Guard->Count; + Guard->EndifLoc = Loc; + } + + virtual void MacroExpands(const Token &, const MacroDirective *, SourceRange, + const MacroArgs *) { + Guard->Count++; + } + virtual void MacroUndefined(const Token &, const MacroDirective *) { + Guard->Count++; + } + virtual void Defined(const Token &, const MacroDirective *, SourceRange) { + Guard->Count++; + } + virtual void If(SourceLocation, SourceRange, bool) { Guard->Count++; } + virtual void Elif(SourceLocation, SourceRange, bool, SourceLocation) { + Guard->Count++; + } + virtual void Ifdef(SourceLocation, const Token &, const MacroDirective *) { + Guard->Count++; + } + virtual void Else(SourceLocation, SourceLocation) { Guard->Count++; } + + IncludeDirectives *Self; + // keep track of the guard info through the include stack + std::stack<GuardDetection> Files; + // convenience field pointing to Files.top().second + GuardDetection *Guard; +}; + +// Flags that describes where to insert newlines. +enum NewLineFlags { + // Prepend a newline at the beginning of the insertion. + NL_Prepend = 0x1, + + // Prepend another newline at the end of the insertion. + NL_PrependAnother = 0x2, + + // Add two newlines at the end of the insertion. + NL_AppendTwice = 0x4, + + // Convenience value to enable both \c NL_Prepend and \c NL_PrependAnother. + NL_PrependTwice = NL_Prepend | NL_PrependAnother +}; + +/// \brief Guess the end-of-line sequence used in the given FileID. If the +/// sequence can't be guessed return an Unix-style newline. +static StringRef guessEOL(SourceManager &SM, FileID ID) { + StringRef Content = SM.getBufferData(ID); + StringRef Buffer = Content.substr(Content.find_first_of("\r\n")); + + return llvm::StringSwitch<StringRef>(Buffer) + .StartsWith("\r\n", "\r\n") + .StartsWith("\n\r", "\n\r") + .StartsWith("\r", "\r") + .Default("\n"); +} + +/// \brief Find the end of the end of the directive, either the beginning of a +/// newline or the end of file. +// +// \return The offset into the file where the directive ends along with a +// boolean value indicating whether the directive ends because the end of file +// was reached or not. +static std::pair<unsigned, bool> findDirectiveEnd(SourceLocation HashLoc, + SourceManager &SM, + const LangOptions &LangOpts) { + FileID FID = SM.getFileID(HashLoc); + unsigned Offset = SM.getFileOffset(HashLoc); + StringRef Content = SM.getBufferData(FID); + Lexer Lex(SM.getLocForStartOfFile(FID), LangOpts, Content.begin(), + Content.begin() + Offset, Content.end()); + Lex.SetCommentRetentionState(true); + Token Tok; + + // This loop look for the newline after our directive but avoids the ones part + // of a multi-line comments: + // + // #include <foo> /* long \n comment */\n + // ~~ no ~~ yes + for (;;) { + // find the beginning of the end-of-line sequence + StringRef::size_type EOLOffset = Content.find_first_of("\r\n", Offset); + // ends because EOF was reached + if (EOLOffset == StringRef::npos) + return std::make_pair(Content.size(), true); + + // find the token that contains our end-of-line + unsigned TokEnd = 0; + do { + Lex.LexFromRawLexer(Tok); + TokEnd = SM.getFileOffset(Tok.getLocation()) + Tok.getLength(); + + // happens when the whitespaces are eaten after a multiline comment + if (Tok.is(tok::eof)) + return std::make_pair(EOLOffset, false); + } while (TokEnd < EOLOffset); + + // the end-of-line is not part of a multi-line comment, return its location + if (Tok.isNot(tok::comment)) + return std::make_pair(EOLOffset, false); + + // for the next search to start after the end of this token + Offset = TokEnd; + } +} + +IncludeDirectives::IncludeDirectives(clang::CompilerInstance &CI) + : CI(CI), Sources(CI.getSourceManager()) { + // addPPCallbacks takes ownership of the callback + CI.getPreprocessor().addPPCallbacks(new IncludeDirectivesPPCallback(this)); +} + +bool IncludeDirectives::lookForInclude(const FileEntry *File, + const LocationVec &IncludeLocs, + SeenFilesSet &Seen) const { + // mark this file as visited + Seen.insert(File); + + // First check if included directly in this file + for (LocationVec::const_iterator I = IncludeLocs.begin(), + E = IncludeLocs.end(); + I != E; ++I) + if (Sources.getFileEntryForID(Sources.getFileID(*I)) == File) + return true; + + // Otherwise look recursively all the included files + FileToEntriesMap::const_iterator EntriesIt = FileToEntries.find(File); + if (EntriesIt == FileToEntries.end()) + return false; + for (EntryVec::const_iterator I = EntriesIt->second.begin(), + E = EntriesIt->second.end(); + I != E; ++I) { + // skip if this header has already been checked before + if (Seen.count(I->getIncludedFile())) + continue; + if (lookForInclude(I->getIncludedFile(), IncludeLocs, Seen)) + return true; + } + return false; +} + +bool IncludeDirectives::hasInclude(const FileEntry *File, + StringRef Include) const { + llvm::StringMap<LocationVec>::const_iterator It = + IncludeAsWrittenToLocationsMap.find(Include); + + // Include isn't included in any file + if (It == IncludeAsWrittenToLocationsMap.end()) + return false; + + SeenFilesSet Seen; + return lookForInclude(File, It->getValue(), Seen); +} + +Replacement IncludeDirectives::addAngledInclude(const clang::FileEntry *File, + llvm::StringRef Include) { + FileID FID = Sources.translateFile(File); + assert(!FID.isInvalid() && "Invalid file entry given!"); + + if (hasInclude(File, Include)) + return Replacement(); + + unsigned Offset, NLFlags; + llvm::tie(Offset, NLFlags) = angledIncludeInsertionOffset(FID); + + StringRef EOL = guessEOL(Sources, FID); + llvm::SmallString<32> InsertionText; + if (NLFlags & NL_Prepend) + InsertionText += EOL; + if (NLFlags & NL_PrependAnother) + InsertionText += EOL; + InsertionText += "#include <"; + InsertionText += Include; + InsertionText += ">"; + if (NLFlags & NL_AppendTwice) { + InsertionText += EOL; + InsertionText += EOL; + } + return Replacement(File->getName(), Offset, 0, InsertionText); +} + +Replacement IncludeDirectives::addAngledInclude(llvm::StringRef File, + llvm::StringRef Include) { + const FileEntry *Entry = Sources.getFileManager().getFile(File); + assert(Entry && "Invalid file given!"); + return addAngledInclude(Entry, Include); +} + +std::pair<unsigned, unsigned> +IncludeDirectives::findFileHeaderEndOffset(FileID FID) const { + unsigned NLFlags = NL_Prepend; + StringRef Content = Sources.getBufferData(FID); + Lexer Lex(Sources.getLocForStartOfFile(FID), CI.getLangOpts(), + Content.begin(), Content.begin(), Content.end()); + Lex.SetCommentRetentionState(true); + Lex.SetKeepWhitespaceMode(true); + + // find the first newline not part of a multi-line comment + Token Tok; + do { + Lex.LexFromRawLexer(Tok); + unsigned Offset = Sources.getFileOffset(Tok.getLocation()); + // allow one newline between the comments + if (Tok.is(tok::unknown) && isWhitespace(Content[Offset])) { + StringRef Whitespaces(Content.substr(Offset, Tok.getLength())); + if (Whitespaces.count('\n') == 1 || Whitespaces.count('\r') == 1) + Lex.LexFromRawLexer(Tok); + else { + // add an empty line to separate the file header and the inclusion + NLFlags = NL_PrependTwice; + } + } + } while (Tok.is(tok::comment)); + + // apparently there is no header, insertion point is the beginning of the file + if (Tok.isNot(tok::unknown)) + return std::make_pair(0, NL_AppendTwice); + return std::make_pair(Sources.getFileOffset(Tok.getLocation()), NLFlags); +} + +SourceLocation +IncludeDirectives::angledIncludeHintLoc(FileID FID) const { + FileToEntriesMap::const_iterator EntriesIt = + FileToEntries.find(Sources.getFileEntryForID(FID)); + + if (EntriesIt == FileToEntries.end()) + return SourceLocation(); + + HeaderSearch &HeaderInfo = CI.getPreprocessor().getHeaderSearchInfo(); + const EntryVec &Entries = EntriesIt->second; + EntryVec::const_reverse_iterator QuotedCandidate = Entries.rend(); + for (EntryVec::const_reverse_iterator I = Entries.rbegin(), + E = Entries.rend(); + I != E; ++I) { + // Headers meant for multiple inclusion can potentially appears in the + // middle of the code thus making them a poor choice for an insertion point. + if (!HeaderInfo.isFileMultipleIncludeGuarded(I->getIncludedFile())) + continue; + + // return preferably the last angled include + if (I->isAngled()) + return I->getHashLocation(); + + // keep track of the last quoted include that is guarded + if (QuotedCandidate == Entries.rend()) + QuotedCandidate = I; + } + + if (QuotedCandidate == Entries.rend()) + return SourceLocation(); + + // return the last quoted-include if we couldn't find an angled one + return QuotedCandidate->getHashLocation(); +} + +std::pair<unsigned, unsigned> +IncludeDirectives::angledIncludeInsertionOffset(FileID FID) const { + SourceLocation Hint = angledIncludeHintLoc(FID); + unsigned NL_Flags = NL_Prepend; + + // If we can't find a similar include and we are in a header check if it's a + // guarded header. If so the hint will be the location of the #define from the + // guard. + if (Hint.isInvalid()) { + const FileEntry *File = Sources.getFileEntryForID(FID); + HeaderToGuardMap::const_iterator GuardIt = HeaderToGuard.find(File); + if (GuardIt != HeaderToGuard.end()) { + // get the hash location from the #define + Hint = GuardIt->second; + // we want a blank line between the #define and the #include + NL_Flags = NL_PrependTwice; + } + } + + // no hints, insertion is done after the file header + if (Hint.isInvalid()) + return findFileHeaderEndOffset(FID); + + unsigned Offset = findDirectiveEnd(Hint, Sources, CI.getLangOpts()).first; + return std::make_pair(Offset, NL_Flags); +} diff --git a/clang-tools-extra/clang-modernize/Core/IncludeDirectives.h b/clang-tools-extra/clang-modernize/Core/IncludeDirectives.h new file mode 100644 index 00000000000..c1c5a7acf77 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/IncludeDirectives.h @@ -0,0 +1,141 @@ +//===-- Core/IncludeDirectives.h - Include directives handling --*- 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 declares the IncludeDirectives class that helps with +/// detecting and modifying \#include directives. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_INCLUDE_DIRECTIVES_H +#define CPP11_MIGRATE_INCLUDE_DIRECTIVES_H + +#include "clang/Basic/SourceLocation.h" +#include "clang/Tooling/Refactoring.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/SmallPtrSet.h" +#include <vector> + +namespace clang { +class Preprocessor; +} // namespace clang + +/// \brief Support for include directives handling. +/// +/// This class should be created with a \c clang::CompilerInstance before the +/// file is preprocessed in order to collect the inclusion information. It can +/// be queried as long as the compiler instance is valid. +class IncludeDirectives { +public: + IncludeDirectives(clang::CompilerInstance &CI); + + /// \brief Add an angled include to a the given file. + /// + /// \param File A file accessible by a SourceManager + /// \param Include The include file as it should be written in the code. + /// + /// \returns + /// \li A null Replacement (check using \c Replacement::isApplicable()), if + /// the \c Include is already visible from \c File. + /// \li Otherwise, a non-null Replacement that, when applied, inserts an + /// \c \#include into \c File. + clang::tooling::Replacement addAngledInclude(llvm::StringRef File, + llvm::StringRef Include); + clang::tooling::Replacement addAngledInclude(const clang::FileEntry *File, + llvm::StringRef Include); + + /// \brief Check if \p Include is included by \p File or any of the files + /// \p File includes. + bool hasInclude(const clang::FileEntry *File, llvm::StringRef Include) const; + +private: + friend class IncludeDirectivesPPCallback; + + /// \brief Contains information about an inclusion. + class Entry { + public: + Entry(clang::SourceLocation HashLoc, const clang::FileEntry *IncludedFile, + bool Angled) + : HashLoc(HashLoc), IncludedFile(IncludedFile), Angled(Angled) {} + + /// \brief The location of the '#'. + clang::SourceLocation getHashLocation() const { return HashLoc; } + + /// \brief The file included by this include directive. + const clang::FileEntry *getIncludedFile() const { return IncludedFile; } + + /// \brief \c true if the include use angle brackets, \c false otherwise + /// when using of quotes. + bool isAngled() const { return Angled; } + + private: + clang::SourceLocation HashLoc; + const clang::FileEntry *IncludedFile; + bool Angled; + }; + + // A list of entries. + typedef std::vector<Entry> EntryVec; + + // A list of source locations. + typedef std::vector<clang::SourceLocation> LocationVec; + + // Associates files to their includes. + typedef llvm::DenseMap<const clang::FileEntry *, EntryVec> FileToEntriesMap; + + // Associates headers to their include guards if any. The location is the + // location of the hash from the #define. + typedef llvm::DenseMap<const clang::FileEntry *, clang::SourceLocation> + HeaderToGuardMap; + + /// \brief Type used by \c lookForInclude() to keep track of the files that + /// have already been processed. + typedef llvm::SmallPtrSet<const clang::FileEntry *, 32> SeenFilesSet; + + /// \brief Recursively look if an include is included by \p File or any of the + /// headers \p File includes. + /// + /// \param File The file where to start the search. + /// \param IncludeLocs These are the hash locations of the \#include + /// directives we are looking for. + /// \param Seen Used to avoid visiting a same file more than once during the + /// recursion. + bool lookForInclude(const clang::FileEntry *File, + const LocationVec &IncludeLocs, SeenFilesSet &Seen) const; + + /// \brief Find the end of a file header and returns a pair (FileOffset, + /// NewLineFlags). + /// + /// Source files often contain a file header (copyright, license, explanation + /// of the file content). An \#include should preferrably be put after this. + std::pair<unsigned, unsigned> + findFileHeaderEndOffset(clang::FileID FID) const; + + /// \brief Finds the offset where an angled include should be added and + /// returns a pair (FileOffset, NewLineFlags). + std::pair<unsigned, unsigned> + angledIncludeInsertionOffset(clang::FileID FID) const; + + /// \brief Find the location of an include directive that can be used to + /// insert an inclusion after. + /// + /// If no such include exists returns a null SourceLocation. + clang::SourceLocation angledIncludeHintLoc(clang::FileID FID) const; + + clang::CompilerInstance &CI; + clang::SourceManager &Sources; + FileToEntriesMap FileToEntries; + // maps include filename as written in the source code to the source locations + // where it appears + llvm::StringMap<LocationVec> IncludeAsWrittenToLocationsMap; + HeaderToGuardMap HeaderToGuard; +}; + +#endif // CPP11_MIGRATE_INCLUDE_DIRECTIVES_H diff --git a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp new file mode 100644 index 00000000000..e3f07c5f0d3 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp @@ -0,0 +1,169 @@ +//===-- Core/IncludeExcludeInfo.cpp - IncludeExclude class impl -----------===// +// +// 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 implementation of the IncludeExcludeInfo class +/// to handle the include and exclude command line options. +/// +//===----------------------------------------------------------------------===// + +#include "IncludeExcludeInfo.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +/// A string type to represent paths. +typedef SmallString<64> PathString; + +namespace { +/// \brief Helper function to determine whether a file has the same path +/// prefix as \a Path. +/// +/// \a Path must be an absolute path. +bool fileHasPathPrefix(StringRef File, StringRef Path) { + // Converts File to its absolute path. + PathString AbsoluteFile = File; + sys::fs::make_absolute(AbsoluteFile); + + // Convert path strings to sys::path to iterate over each of its directories. + sys::path::const_iterator FileI = sys::path::begin(AbsoluteFile), + FileE = sys::path::end(AbsoluteFile), + PathI = sys::path::begin(Path), + PathE = sys::path::end(Path); + while (FileI != FileE && PathI != PathE) { + // If the strings aren't equal then the two paths aren't contained within + // each other. + bool IsSeparator = ((FileI->size() == 1) && (PathI->size() == 1) && + sys::path::is_separator((*FileI)[0]) && + sys::path::is_separator((*PathI)[0])); + if (!FileI->equals(*PathI) && !IsSeparator) + return false; + ++FileI; + ++PathI; + } + return true; +} + +/// \brief Helper function for removing relative operators from a given +/// path i.e. "..", ".". +/// \a Path must be a absolute path. +std::string removeRelativeOperators(StringRef Path) { + sys::path::const_iterator PathI = sys::path::begin(Path); + sys::path::const_iterator PathE = sys::path::end(Path); + SmallVector<StringRef, 16> PathT; + while (PathI != PathE) { + if (PathI->equals("..")) { + // Test if we have reached the root then Path is invalid. + if (PathT.empty()) + return ""; + PathT.pop_back(); + } else if (!PathI->equals(".")) + PathT.push_back(*PathI); + ++PathI; + } + // Rebuild the new path. + PathString NewPath; + for (SmallVectorImpl<StringRef>::iterator I = PathT.begin(), E = PathT.end(); + I != E; ++I) { + llvm::sys::path::append(NewPath, *I); + } + return NewPath.str(); +} + +/// \brief Helper function to tokenize a string of paths and populate +/// the vector. +error_code parseCLInput(StringRef Line, std::vector<std::string> &List, + StringRef Separator) { + SmallVector<StringRef, 32> Tokens; + Line.split(Tokens, Separator, /*MaxSplit=*/ -1, /*KeepEmpty=*/ false); + for (SmallVectorImpl<StringRef>::iterator I = Tokens.begin(), + E = Tokens.end(); + I != E; ++I) { + // Convert each path to its absolute path. + PathString Path = I->rtrim(); + if (error_code Err = sys::fs::make_absolute(Path)) + return Err; + // Remove relative operators from the path. + std::string AbsPath = removeRelativeOperators(Path); + // Add only non-empty paths to the list. + if (!AbsPath.empty()) + List.push_back(AbsPath); + else + llvm::errs() << "Unable to parse input path: " << *I << "\n"; + + llvm::errs() << "Parse: " <<List.back() << "\n"; + } + return error_code::success(); +} +} // end anonymous namespace + +error_code IncludeExcludeInfo::readListFromString(StringRef IncludeString, + StringRef ExcludeString) { + if (error_code Err = parseCLInput(IncludeString, IncludeList, + /*Separator=*/ ",")) + return Err; + if (error_code Err = parseCLInput(ExcludeString, ExcludeList, + /*Separator=*/ ",")) + return Err; + return error_code::success(); +} + +error_code IncludeExcludeInfo::readListFromFile(StringRef IncludeListFile, + StringRef ExcludeListFile) { + if (!IncludeListFile.empty()) { + OwningPtr<MemoryBuffer> FileBuf; + if (error_code Err = MemoryBuffer::getFile(IncludeListFile, FileBuf)) { + errs() << "Unable to read from include file.\n"; + return Err; + } + if (error_code Err = parseCLInput(FileBuf->getBuffer(), IncludeList, + /*Separator=*/ "\n")) + return Err; + } + if (!ExcludeListFile.empty()) { + OwningPtr<MemoryBuffer> FileBuf; + if (error_code Err = MemoryBuffer::getFile(ExcludeListFile, FileBuf)) { + errs() << "Unable to read from exclude file.\n"; + return Err; + } + if (error_code Err = parseCLInput(FileBuf->getBuffer(), ExcludeList, + /*Separator=*/ "\n")) + return Err; + } + return error_code::success(); +} + +bool IncludeExcludeInfo::isFileIncluded(StringRef FilePath) const { + bool InIncludeList = false; + + for (std::vector<std::string>::const_iterator I = IncludeList.begin(), + E = IncludeList.end(); + I != E; ++I) + if ((InIncludeList = fileHasPathPrefix(FilePath, *I))) + break; + + // If file is not in the list of included paths then it is not necessary + // to check the excluded path list. + if (!InIncludeList) + return false; + + for (std::vector<std::string>::const_iterator I = ExcludeList.begin(), + E = ExcludeList.end(); + I != E; ++I) + if (fileHasPathPrefix(FilePath, *I)) + return false; + + // If the file is in the included list but not in the excluded list, then + // it is safe to transform. + return true; +} diff --git a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h new file mode 100644 index 00000000000..a5e73efae0f --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h @@ -0,0 +1,56 @@ +//===-- Core/IncludeExcludeInfo.h - IncludeExclude class def'n --*- 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 definition for the IncludeExcludeInfo class +/// to handle the include and exclude command line options. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H +#define CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/system_error.h" +#include <vector> + +/// \brief Class encapsulating the handling of include and exclude paths +/// provided by the user through command line options. +class IncludeExcludeInfo { +public: + /// \brief Read and parse a comma-seperated lists of paths from + /// \a IncludeString and \a ExcludeString. + /// + /// Returns error_code::success() on successful parse of the strings or + /// an error_code indicating the encountered error. + llvm::error_code readListFromString(llvm::StringRef IncludeString, + llvm::StringRef ExcludeString); + + /// \brief Read and parse the lists of paths from \a IncludeListFile + /// and \a ExcludeListFile. Each file should contain one path per line. + /// + /// Returns error_code::success() on successful read and parse of both files + /// or an error_code indicating the encountered error. + llvm::error_code readListFromFile(llvm::StringRef IncludeListFile, + llvm::StringRef ExcludeListFile); + + /// \brief Determine if the given path is in the list of include paths but + /// not in the list of exclude paths. + /// + /// \a FilePath shouldn't contain relative operators i.e. ".." or "." since + /// Path comes from the include/exclude list of paths in which relative + /// operators were removed. + bool isFileIncluded(llvm::StringRef FilePath) const; + +private: + std::vector<std::string> IncludeList; + std::vector<std::string> ExcludeList; +}; + +#endif // CPP11_MIGRATE_INCLUDEEXCLUDEINFO_H diff --git a/clang-tools-extra/clang-modernize/Core/Makefile b/clang-tools-extra/clang-modernize/Core/Makefile new file mode 100644 index 00000000000..9ccffae2fa3 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Makefile @@ -0,0 +1,14 @@ +##===- clang-modernize/Core/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 := ../../../.. +LIBRARYNAME := migrateCore + +include $(CLANG_LEVEL)/Makefile + +CPP.Flags += -I$(PROJ_SRC_DIR)/.. -I$(PROJ_SRC_DIR)/../../clang-apply-replacements/include diff --git a/clang-tools-extra/clang-modernize/Core/PerfSupport.cpp b/clang-tools-extra/clang-modernize/Core/PerfSupport.cpp new file mode 100644 index 00000000000..e074bd123ef --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/PerfSupport.cpp @@ -0,0 +1,101 @@ +//===-- Core/PerfSupport.cpp - Perf measurement helpers -------------------===// +// +// 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 implementations for performance measuring helpers. +/// +//===----------------------------------------------------------------------===// + +#include "PerfSupport.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Path.h" + +void collectSourcePerfData(const Transform &T, SourcePerfData &Data) { + for (Transform::TimingVec::const_iterator I = T.timing_begin(), + E = T.timing_end(); + I != E; ++I) { + SourcePerfData::iterator DataI = Data.insert( + SourcePerfData::value_type(I->first, std::vector<PerfItem>())).first; + DataI->second + .push_back(PerfItem(T.getName(), I->second.getProcessTime() * 1000.0)); + } +} + +void writePerfDataJSON( + const llvm::StringRef DirectoryName, + const SourcePerfData &TimingResults) { + // Create directory path if it doesn't exist + llvm::sys::fs::create_directories(DirectoryName); + + // Get PID and current time. + // FIXME: id_type on Windows is NOT a process id despite the function name. + // Need to call GetProcessId() providing it what get_id() returns. For now + // disabling PID-based file names until this is fixed properly. + //llvm::sys::self_process *SP = llvm::sys::process::get_self(); + //id_type Pid = SP->get_id(); + unsigned Pid = 0; + llvm::TimeRecord T = llvm::TimeRecord::getCurrentTime(); + + std::string FileName; + llvm::raw_string_ostream SS(FileName); + SS << DirectoryName << "/" << static_cast<int>(T.getWallTime()) << "_" << Pid + << ".json"; + + std::string ErrorInfo; + llvm::raw_fd_ostream FileStream(SS.str().c_str(), ErrorInfo); + FileStream << "{\n"; + FileStream << " \"Sources\" : [\n"; + for (SourcePerfData::const_iterator I = TimingResults.begin(), + E = TimingResults.end(); + I != E; ++I) { + // Terminate the last source with a comma before continuing to the next one. + if (I != TimingResults.begin()) + FileStream << ",\n"; + + FileStream << " {\n"; + FileStream << " \"Source \" : \"" << I->first << "\",\n"; + FileStream << " \"Data\" : [\n"; + for (std::vector<PerfItem>::const_iterator IE = I->second.begin(), + EE = I->second.end(); + IE != EE; ++IE) { + // Terminate the last perf item with a comma before continuing to the next + // one. + if (IE != I->second.begin()) + FileStream << ",\n"; + + FileStream << " {\n"; + FileStream << " \"TimerId\" : \"" << IE->Label << "\",\n"; + FileStream << " \"Time\" : " << llvm::format("%.2f", IE->Duration) + << "\n"; + + FileStream << " }"; + + } + FileStream << "\n ]\n"; + FileStream << " }"; + } + FileStream << "\n ]\n"; + FileStream << "}"; +} + +void dumpPerfData(const SourcePerfData &Data) { + for (SourcePerfData::const_iterator I = Data.begin(), E = Data.end(); I != E; + ++I) { + llvm::errs() << I->first << ":\n"; + for (std::vector<PerfItem>::const_iterator VecI = I->second.begin(), + VecE = I->second.end(); + VecI != VecE; ++VecI) { + llvm::errs() << " " << VecI->Label << ": " + << llvm::format("%.1f", VecI->Duration) << "ms\n"; + } + } +} diff --git a/clang-tools-extra/clang-modernize/Core/PerfSupport.h b/clang-tools-extra/clang-modernize/Core/PerfSupport.h new file mode 100644 index 00000000000..58ddded2207 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/PerfSupport.h @@ -0,0 +1,57 @@ +//===-- Core/PerfSupport.h - Perf measurement helpers -----------*- 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 helper functionality for measuring performance and +/// recording data to file. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_PERFSUPPORT_H +#define CPP11_MIGRATE_PERFSUPPORT_H + +#include "Transform.h" +#include "llvm/ADT/StringRef.h" + +#include <map> +#include <vector> + +/// \brief A single piece of performance data: a duration in milliseconds and a +/// label for that duration. +struct PerfItem { + PerfItem(const llvm::StringRef Label, float Duration) + : Label(Label), Duration(Duration) {} + + /// Label for this performance measurement. + std::string Label; + + /// Duration in milliseconds. + float Duration; +}; + +/// Maps source file names to a vector of durations/labels. +typedef std::map<std::string, std::vector<PerfItem> > SourcePerfData; + +/// Extracts durations collected by a Transform for all sources and adds them +/// to a SourcePerfData map where data is organized by source file. +extern void collectSourcePerfData(const Transform &T, SourcePerfData &Data); + +/// Write timing results to a JSON formatted file. +/// +/// File is placed in the directory given by \p DirectoryName. File is named in +/// a unique way with time and process ID to avoid naming collisions with +/// existing files or files being generated by other migrator processes. +void writePerfDataJSON( + const llvm::StringRef DirectoryName, + const SourcePerfData &TimingResults); + +/// Dump a SourcePerfData map to llvm::errs(). +extern void dumpPerfData(const SourcePerfData &Data); + +#endif // CPP11_MIGRATE_PERFSUPPORT_H diff --git a/clang-tools-extra/clang-modernize/Core/Refactoring.h b/clang-tools-extra/clang-modernize/Core/Refactoring.h new file mode 100644 index 00000000000..a15634a3704 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Refactoring.h @@ -0,0 +1,31 @@ +//===-- Core/Refactoring.h - Stand-in for Tooling/Refactoring.h -*- 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 is meant to be used instead of clang/Tooling/Refactoring.h +/// until such time as clang::tooling::Replacements is re-implemented as a +/// vector instead of a set. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_REPLACEMENTS_VEC_H +#define CPP11_MIGRATE_REPLACEMENTS_VEC_H + +#include "clang/Tooling/Refactoring.h" + +// FIXME: Remove this file when clang::tooling::Replacements becomes a vector +// instead of a set. + +namespace clang { +namespace tooling { +typedef std::vector<clang::tooling::Replacement> ReplacementsVec; +} +} + +#endif // CPP11_MIGRATE_REPLACEMENTS_VEC_H diff --git a/clang-tools-extra/clang-modernize/Core/Reformatting.cpp b/clang-tools-extra/clang-modernize/Core/Reformatting.cpp new file mode 100644 index 00000000000..50ba1f19f7a --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Reformatting.cpp @@ -0,0 +1,62 @@ +//===-- 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(const FileOverrides &FileStates, + clang::SourceManager &SM, + clang::tooling::ReplacementsVec &Replaces) { + FileStates.applyOverrides(SM); + + for (FileOverrides::ChangeMap::const_iterator + I = FileStates.getChangedRanges().begin(), + E = FileStates.getChangedRanges().end(); + I != E; ++I) { + reformatSingleFile(I->getKey(), I->getValue(), SM, Replaces); + } +} + +void Reformatter::reformatSingleFile( + const llvm::StringRef FileName, const ChangedRanges &Changes, + SourceManager &SM, clang::tooling::ReplacementsVec &FormatReplacements) { + + 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)); + const tooling::Replacements &R = + format::reformat(Style, Lex, SM, ReformatRanges); + std::copy(R.begin(), R.end(), std::back_inserter(FormatReplacements)); +} diff --git a/clang-tools-extra/clang-modernize/Core/Reformatting.h b/clang-tools-extra/clang-modernize/Core/Reformatting.h new file mode 100644 index 00000000000..9a10171a742 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Reformatting.h @@ -0,0 +1,60 @@ +//===-- 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 "Core/Refactoring.h" +#include "clang/Format/Format.h" + +class FileOverrides; +class ChangedRanges; + +class Reformatter { +public: + Reformatter(const clang::format::FormatStyle &Style) : Style(Style) {} + + /// \brief Reformat the changes made to the file overrides. + /// + /// This function will apply the state of files stored in \c FileState to \c + /// SM. + /// + /// \param[in] FileState Files to reformat. + /// \param[in] SM SourceManager for access to source files. + /// \param[out] Replaces Container to store all reformatting replacements. + void reformatChanges(const FileOverrides &FileState, clang::SourceManager &SM, + clang::tooling::ReplacementsVec &Replaces); + + /// \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 + /// function applies to ranges also apply here. + /// + /// \param[in] FileName Name of file to reformat. + /// \param[in] Changes Description of where changes were made to the file. + /// \param[in] SM SourceManager required to create replacements. + /// \param[out] FormatReplacements New reformatting replacements are appended + /// to this container. + void reformatSingleFile(const llvm::StringRef FileName, + const ChangedRanges &Changes, + clang::SourceManager &SM, + clang::tooling::ReplacementsVec &FormatReplacements); + +private: + clang::format::FormatStyle Style; +}; + +#endif // CPP11_MIGRATE_REFORMATTING_H diff --git a/clang-tools-extra/clang-modernize/Core/SyntaxCheck.cpp b/clang-tools-extra/clang-modernize/Core/SyntaxCheck.cpp new file mode 100644 index 00000000000..42900e7d540 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/SyntaxCheck.cpp @@ -0,0 +1,73 @@ +//===-- Core/SyntaxCheck.cpp ----------------------------------------------===// +// +// 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 exposes functionaliy for doing a syntax-only check on +/// files with overridden contents. +/// +//===----------------------------------------------------------------------===// + +#include "Core/SyntaxCheck.h" +#include "Core/FileOverrides.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/Tooling.h" + +using namespace clang; +using namespace tooling; + +class SyntaxCheck : public SyntaxOnlyAction { +public: + SyntaxCheck(const FileOverrides &Overrides) : Overrides(Overrides) {} + + virtual bool BeginSourceFileAction(CompilerInstance &CI, StringRef Filename) { + if (!SyntaxOnlyAction::BeginSourceFileAction(CI, Filename)) + return false; + + Overrides.applyOverrides(CI.getSourceManager()); + + return true; + } + +private: + const FileOverrides &Overrides; +}; + +class SyntaxCheckFactory : public FrontendActionFactory { +public: + SyntaxCheckFactory(const FileOverrides &Overrides) + : Overrides(Overrides) {} + + virtual FrontendAction *create() { return new SyntaxCheck(Overrides); } + +private: + const FileOverrides &Overrides; +}; + +class SyntaxArgumentsAdjuster : public ArgumentsAdjuster { + CommandLineArguments Adjust(const CommandLineArguments &Args) { + CommandLineArguments AdjustedArgs = Args; + AdjustedArgs.push_back("-fsyntax-only"); + AdjustedArgs.push_back("-std=c++11"); + return AdjustedArgs; + } +}; + +bool doSyntaxCheck(const CompilationDatabase &Database, + const std::vector<std::string> &SourcePaths, + const FileOverrides &Overrides) { + ClangTool SyntaxTool(Database, SourcePaths); + + // Ensure C++11 support is enabled. + // FIXME: This isn't necessary anymore since the Modernizer requires C++11 + // to be enabled in the CompilationDatabase. Remove later. + SyntaxTool.setArgumentsAdjuster(new SyntaxArgumentsAdjuster); + + return SyntaxTool.run(new SyntaxCheckFactory(Overrides)) == 0; +} diff --git a/clang-tools-extra/clang-modernize/Core/SyntaxCheck.h b/clang-tools-extra/clang-modernize/Core/SyntaxCheck.h new file mode 100644 index 00000000000..1651a7e7acc --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/SyntaxCheck.h @@ -0,0 +1,38 @@ +//===-- Core/SyntaxCheck.h --------------------------------------*- 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 exposes functionaliy for doing a syntax-only check on +/// files with overridden contents. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_SYNTAX_CHECK_H +#define CPP11_MIGRATE_SYNTAX_CHECK_H + +#include <string> +#include <vector> + +// Forward Declarations +namespace clang { +namespace tooling { +class CompilationDatabase; +} // namespace tooling +} // namespace clang + +class FileOverrides; + +/// \brief Perform a syntax-only check over all files in \c SourcePaths using +/// options provided by \c Database using file contents from \c Overrides if +/// available. +extern bool doSyntaxCheck(const clang::tooling::CompilationDatabase &Database, + const std::vector<std::string> &SourcePaths, + const FileOverrides &Overrides); + +#endif // CPP11_MIGRATE_SYNTAX_CHECK_H diff --git a/clang-tools-extra/clang-modernize/Core/Transform.cpp b/clang-tools-extra/clang-modernize/Core/Transform.cpp new file mode 100644 index 00000000000..cd76723d316 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Transform.cpp @@ -0,0 +1,172 @@ +//===-- Core/Transform.cpp - Transform Base Class Def'n -------------------===// +// +// 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 definition for the base Transform class from +/// which all transforms must subclass. +/// +//===----------------------------------------------------------------------===// + +#include "Core/Transform.h" +#include "Core/FileOverrides.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" + +using namespace clang; + +llvm::cl::OptionCategory TransformsOptionsCategory("Transforms' options"); + +namespace { + +using namespace tooling; +using namespace ast_matchers; + +/// \brief Custom FrontendActionFactory to produce FrontendActions that simply +/// forward (Begin|End)SourceFileAction calls to a given Transform. +class ActionFactory : public clang::tooling::FrontendActionFactory { +public: + ActionFactory(MatchFinder &Finder, Transform &Owner) + : Finder(Finder), Owner(Owner) {} + + virtual FrontendAction *create() LLVM_OVERRIDE { + return new FactoryAdaptor(Finder, Owner); + } + +private: + class FactoryAdaptor : public ASTFrontendAction { + public: + FactoryAdaptor(MatchFinder &Finder, Transform &Owner) + : Finder(Finder), Owner(Owner) {} + + ASTConsumer *CreateASTConsumer(CompilerInstance &, StringRef) { + return Finder.newASTConsumer(); + } + + virtual bool BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) LLVM_OVERRIDE { + if (!ASTFrontendAction::BeginSourceFileAction(CI, Filename)) + return false; + + return Owner.handleBeginSource(CI, Filename); + } + + virtual void EndSourceFileAction() LLVM_OVERRIDE { + Owner.handleEndSource(); + return ASTFrontendAction::EndSourceFileAction(); + } + + private: + MatchFinder &Finder; + Transform &Owner; + }; + + MatchFinder &Finder; + Transform &Owner; +}; +} // namespace + +Transform::Transform(llvm::StringRef Name, const TransformOptions &Options) + : Name(Name), GlobalOptions(Options), Overrides(0) { + Reset(); +} + +Transform::~Transform() {} + +bool Transform::isFileModifiable(const SourceManager &SM, + const SourceLocation &Loc) const { + if (SM.isWrittenInMainFile(Loc)) + return true; + + if (!GlobalOptions.EnableHeaderModifications) + return false; + + const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Loc)); + if (!FE) + return false; + + return GlobalOptions.ModifiableHeaders.isFileIncluded(FE->getName()); +} + +bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) { + assert(Overrides != 0 && "Subclass transform didn't provide InputState"); + + Overrides->applyOverrides(CI.getSourceManager()); + CurrentSource = Filename; + + if (Options().EnableTiming) { + Timings.push_back(std::make_pair(Filename.str(), llvm::TimeRecord())); + Timings.back().second -= llvm::TimeRecord::getCurrentTime(true); + } + return true; +} + +void Transform::handleEndSource() { + CurrentSource.clear(); + if (Options().EnableTiming) + Timings.back().second += llvm::TimeRecord::getCurrentTime(false); +} + +void Transform::addTiming(llvm::StringRef Label, llvm::TimeRecord Duration) { + Timings.push_back(std::make_pair(Label.str(), Duration)); +} + +bool +Transform::addReplacementForCurrentTU(const clang::tooling::Replacement &R) { + if (CurrentSource.empty()) + return false; + + TranslationUnitReplacements &TU = Replacements[CurrentSource]; + if (TU.MainSourceFile.empty()) + TU.MainSourceFile = CurrentSource; + TU.Replacements.push_back(R); + + return true; +} + +FrontendActionFactory *Transform::createActionFactory(MatchFinder &Finder) { + return new ActionFactory(Finder, /*Owner=*/ *this); +} + +Version Version::getFromString(llvm::StringRef VersionStr) { + llvm::StringRef MajorStr, MinorStr; + Version V; + + llvm::tie(MajorStr, MinorStr) = VersionStr.split('.'); + if (!MinorStr.empty()) { + llvm::StringRef Ignore; + llvm::tie(MinorStr, Ignore) = MinorStr.split('.'); + if (MinorStr.getAsInteger(10, V.Minor)) + return Version(); + } + if (MajorStr.getAsInteger(10, V.Major)) + return Version(); + return V; +} + +TransformFactory::~TransformFactory() {} + +namespace { +bool versionSupported(Version Required, Version AvailableSince) { + // null version, means no requirements, means supported + if (Required.isNull()) + return true; + return Required >= AvailableSince; +} +} // end anonymous namespace + +bool TransformFactory::supportsCompilers(CompilerVersions Required) const { + return versionSupported(Required.Clang, Since.Clang) && + versionSupported(Required.Gcc, Since.Gcc) && + versionSupported(Required.Icc, Since.Icc) && + versionSupported(Required.Msvc, Since.Msvc); +} diff --git a/clang-tools-extra/clang-modernize/Core/Transform.h b/clang-tools-extra/clang-modernize/Core/Transform.h new file mode 100644 index 00000000000..846bb8a76b2 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Transform.h @@ -0,0 +1,344 @@ +//===-- Core/Transform.h - Transform Base Class Def'n -----------*- 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 declaration for the base Transform class from +/// which all transforms must subclass. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_TRANSFORM_H +#define CPP11_MIGRATE_TRANSFORM_H + +#include "Core/IncludeExcludeInfo.h" +#include "Core/Refactoring.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Registry.h" +#include "llvm/Support/Timer.h" +#include <string> +#include <vector> + +/// \brief Description of the riskiness of actions that can be taken by +/// transforms. +enum RiskLevel { + /// Transformations that will not change semantics. + RL_Safe, + + /// Transformations that might change semantics. + RL_Reasonable, + + /// Transformations that are likely to change semantics. + RL_Risky +}; + +// Forward declarations +namespace clang { +class CompilerInstance; +namespace tooling { +class CompilationDatabase; +class FrontendActionFactory; +} // namespace tooling +namespace ast_matchers { +class MatchFinder; +} // namespace ast_matchers +} // namespace clang + +class FileOverrides; + + +// \brief Maps main source file names to a TranslationUnitReplacements +// structure storing replacements for that translation unit. +typedef llvm::StringMap<clang::tooling::TranslationUnitReplacements> +TUReplacementsMap; + +/// \brief To group transforms' options together when printing the help. +extern llvm::cl::OptionCategory TransformsOptionsCategory; + +/// \brief Container for global options affecting all transforms. +struct TransformOptions { + /// \brief Enable the use of performance timers. + bool EnableTiming; + + /// \brief Allow changes to headers included from the main source file. + /// Transform sub-classes should use ModifiableHeaders to determine which + /// headers are modifiable and which are not. + bool EnableHeaderModifications; + + /// \brief Contains information on which headers are safe to transform and + /// which aren't. + IncludeExcludeInfo ModifiableHeaders; + + /// \brief Maximum allowed level of risk. + RiskLevel MaxRiskLevel; +}; + +/// \brief Abstract base class for all C++11 migration transforms. +/// +/// Subclasses must call createActionFactory() to create a +/// FrontendActionFactory to pass to ClangTool::run(). Subclasses are also +/// responsible for calling setOverrides() before calling ClangTool::run(). +/// +/// If timing is enabled (see TransformOptions), per-source performance timing +/// is recorded and stored in a TimingVec for later access with timing_begin() +/// and timing_end(). +class Transform { +public: + /// \brief Constructor + /// \param Name Name of the transform for human-readable purposes (e.g. -help + /// text) + /// \param Options Global options that affect all Transforms. + Transform(llvm::StringRef Name, const TransformOptions &Options); + + virtual ~Transform(); + + /// \brief Apply a transform to all files listed in \p SourcePaths. + /// + /// \p Database must contain information for how to compile all files in \p + /// SourcePaths. \p InputStates contains the file contents of files in \p + /// SourcePaths and should take precedence over content of files on disk. + /// Upon return, \p ResultStates shall contain the result of performing this + /// transform on the files listed in \p SourcePaths. + virtual int apply(const FileOverrides &InputStates, + const clang::tooling::CompilationDatabase &Database, + const std::vector<std::string> &SourcePaths) = 0; + + /// \brief Query if changes were made during the last call to apply(). + bool getChangesMade() const { return AcceptedChanges > 0; } + + /// \brief Query if changes were not made due to conflicts with other changes + /// made during the last call to apply() or if changes were too risky for the + /// requested risk level. + bool getChangesNotMade() const { + return RejectedChanges > 0 || DeferredChanges > 0; + } + + /// \brief Query the number of accepted changes. + unsigned getAcceptedChanges() const { return AcceptedChanges; } + /// \brief Query the number of changes considered too risky. + unsigned getRejectedChanges() const { return RejectedChanges; } + /// \brief Query the number of changes not made because they conflicted with + /// early changes. + unsigned getDeferredChanges() const { return DeferredChanges; } + + /// \brief Query transform name. + llvm::StringRef getName() const { return Name; } + + /// \brief Reset internal state of the transform. + /// + /// Useful if calling apply() several times with one instantiation of a + /// transform. + void Reset() { + AcceptedChanges = 0; + RejectedChanges = 0; + DeferredChanges = 0; + } + + /// \brief Tests if the file containing \a Loc is allowed to be modified by + /// the Modernizer. + bool isFileModifiable(const clang::SourceManager &SM, + const clang::SourceLocation &Loc) const; + + /// \brief Whether a transformation with a risk level of \p RiskLevel is + /// acceptable or not. + bool isAcceptableRiskLevel(RiskLevel RiskLevel) const { + return RiskLevel <= GlobalOptions.MaxRiskLevel; + } + + /// \brief Called before parsing a translation unit for a FrontendAction. + /// + /// Transform uses this function to apply file overrides and start + /// performance timers. Subclasses overriding this function must call it + /// before returning. + virtual bool handleBeginSource(clang::CompilerInstance &CI, + llvm::StringRef Filename); + + /// \brief Called after FrontendAction has been run over a translation unit. + /// + /// Transform uses this function to stop performance timers. Subclasses + /// overriding this function must call it before returning. A call to + /// handleEndSource() for a given translation unit is expected to be called + /// immediately after the corresponding handleBeginSource() call. + virtual void handleEndSource(); + + /// \brief Performance timing data is stored as a vector of pairs. Pairs are + /// formed of: + /// \li Name of source file. + /// \li Elapsed time. + typedef std::vector<std::pair<std::string, llvm::TimeRecord> > TimingVec; + + /// \brief Return an iterator to the start of collected timing data. + TimingVec::const_iterator timing_begin() const { return Timings.begin(); } + /// \brief Return an iterator to the start of collected timing data. + TimingVec::const_iterator timing_end() const { return Timings.end(); } + + /// \brief Add a Replacement to the list for the current translation unit. + /// + /// \returns \li true on success + /// \li false if there is no current translation unit + bool addReplacementForCurrentTU(const clang::tooling::Replacement &R); + + /// \brief Accessor to Replacements across all transformed translation units. + const TUReplacementsMap &getAllReplacements() const { + return Replacements; + } + +protected: + + void setAcceptedChanges(unsigned Changes) { + AcceptedChanges = Changes; + } + void setRejectedChanges(unsigned Changes) { + RejectedChanges = Changes; + } + void setDeferredChanges(unsigned Changes) { + DeferredChanges = Changes; + } + + /// \brief Allows subclasses to manually add performance timer data. + /// + /// \p Label should probably include the source file name somehow as the + /// duration info is simply added to the vector of timing data which holds + /// data for all sources processed by this transform. + void addTiming(llvm::StringRef Label, llvm::TimeRecord Duration); + + /// \brief Provide access for subclasses to the TransformOptions they were + /// created with. + const TransformOptions &Options() { return GlobalOptions; } + + /// \brief Affords a subclass to provide file contents overrides before + /// applying frontend actions. + /// + /// It is an error not to call this function before calling ClangTool::run() + /// with the factory provided by createActionFactory(). + void setOverrides(const FileOverrides &Overrides) { + this->Overrides = &Overrides; + } + + /// \brief Subclasses must call this function to create a + /// FrontendActionFactory to pass to ClangTool. + /// + /// The factory returned by this function is responsible for calling back to + /// Transform to call handleBeginSource() and handleEndSource(). + clang::tooling::FrontendActionFactory * + createActionFactory(clang::ast_matchers::MatchFinder &Finder); + +private: + const std::string Name; + const TransformOptions &GlobalOptions; + const FileOverrides *Overrides; + TUReplacementsMap Replacements; + std::string CurrentSource; + TimingVec Timings; + unsigned AcceptedChanges; + unsigned RejectedChanges; + unsigned DeferredChanges; +}; + +/// \brief Describes a version number of the form major[.minor] (minor being +/// optional). +struct Version { + explicit Version(unsigned Major = 0, unsigned Minor = 0) + : Major(Major), Minor(Minor) {} + + bool operator<(Version RHS) const { + if (Major < RHS.Major) + return true; + if (Major == RHS.Major) + return Minor < RHS.Minor; + return false; + } + + bool operator==(Version RHS) const { + return Major == RHS.Major && Minor == RHS.Minor; + } + + bool operator!=(Version RHS) const { return !(*this == RHS); } + bool operator>(Version RHS) const { return RHS < *this; } + bool operator<=(Version RHS) const { return !(*this > RHS); } + bool operator>=(Version RHS) const { return !(*this < RHS); } + + bool isNull() const { return Minor == 0 && Major == 0; } + unsigned getMajor() const { return Major; } + unsigned getMinor() const { return Minor; } + + /// \brief Creates a version from a string of the form \c major[.minor]. + /// + /// Note that any version component after \c minor is ignored. + /// + /// \return A null version is returned on error. + static Version getFromString(llvm::StringRef VersionStr); + +private: + unsigned Major; + unsigned Minor; +}; + +/// \brief Convenience structure to store the version of some compilers. +struct CompilerVersions { + Version Clang, Gcc, Icc, Msvc; +}; + +/// \brief A factory that can instantiate a specific transform. +/// +/// Each transform should subclass this class and implement +/// \c createTransform(). +/// +/// In the sub-classed factory constructor, specify the earliest versions since +/// the compilers in \c CompilerVersions support the feature introduced by the +/// transform. See the example below. +/// +/// Note that you should use \c TransformFactoryRegistry to register the +/// transform globally. +/// +/// Example: +/// \code +/// class MyTransform : public Transform { ... }; +/// +/// struct MyFactory : TransformFactory { +/// MyFactory() { +/// Since.Clang = Version(3, 0); +/// Since.Gcc = Version(4, 7); +/// Since.Icc = Version(12); +/// Since.Msvc = Version(10); +/// } +/// +/// Transform *createTransform(const TransformOptions &Opts) LLVM_OVERRIDE { +/// return new MyTransform(Opts); +/// } +/// }; +/// +/// // Register the factory using this statically initialized variable. +/// static TransformFactoryRegistry::Add<MyFactory> +/// X("my-transform", "<Short description of my transform>"); +/// +/// // This anchor is used to force the linker to link in the generated object +/// // file and thus register the factory. +/// volatile int MyTransformAnchorSource = 0; +/// \endcode +class TransformFactory { +public: + virtual ~TransformFactory(); + virtual Transform *createTransform(const TransformOptions &) = 0; + + /// \brief Whether the transform is supported by the required compilers or + /// not. + bool supportsCompilers(CompilerVersions Required) const; + +protected: + /// \brief Since when the C++11 feature introduced by this transform has been + /// available. + /// + /// Can be set by the sub-class in the constructor body. + CompilerVersions Since; +}; + +typedef llvm::Registry<TransformFactory> TransformFactoryRegistry; + +#endif // CPP11_MIGRATE_TRANSFORM_H diff --git a/clang-tools-extra/clang-modernize/Core/Transforms.cpp b/clang-tools-extra/clang-modernize/Core/Transforms.cpp new file mode 100644 index 00000000000..93701796e24 --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Transforms.cpp @@ -0,0 +1,71 @@ +//===-- Core/Transforms.cpp - class Transforms Impl -----------------------===// +// +// 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 implementation for class Transforms. +/// +//===----------------------------------------------------------------------===// + +#include "Core/Transforms.h" +#include "Core/Transform.h" + +namespace cl = llvm::cl; + +static cl::OptionCategory TransformCategory("Transforms"); + +Transforms::~Transforms() { + for (std::vector<Transform *>::iterator I = ChosenTransforms.begin(), + E = ChosenTransforms.end(); + I != E; ++I) + delete *I; + + for (OptionMap::iterator I = Options.begin(), E = Options.end(); I != E; ++I) + delete I->getValue(); +} + +void Transforms::registerTransforms() { + for (TransformFactoryRegistry::iterator I = TransformFactoryRegistry::begin(), + E = TransformFactoryRegistry::end(); + I != E; ++I) + Options[I->getName()] = new cl::opt<bool>( + I->getName(), cl::desc(I->getDesc()), cl::cat(TransformCategory)); +} + +bool Transforms::hasAnyExplicitOption() const { + for (OptionMap::const_iterator I = Options.begin(), E = Options.end(); I != E; + ++I) + if (*I->second) + return true; + return false; +} + +void +Transforms::createSelectedTransforms(const TransformOptions &GlobalOptions, + const CompilerVersions &RequiredVersions) { + // if at least one transform is set explicitly on the command line, do not + // enable non-explicit ones + bool EnableAllTransformsByDefault = !hasAnyExplicitOption(); + + for (TransformFactoryRegistry::iterator I = TransformFactoryRegistry::begin(), + E = TransformFactoryRegistry::end(); + I != E; ++I) { + bool ExplicitlyEnabled = *Options[I->getName()]; + bool OptionEnabled = EnableAllTransformsByDefault || ExplicitlyEnabled; + + if (!OptionEnabled) + continue; + + llvm::OwningPtr<TransformFactory> Factory(I->instantiate()); + if (Factory->supportsCompilers(RequiredVersions)) + ChosenTransforms.push_back(Factory->createTransform(GlobalOptions)); + else if (ExplicitlyEnabled) + llvm::errs() << "note: " << '-' << I->getName() + << ": transform not available for specified compilers\n"; + } +} diff --git a/clang-tools-extra/clang-modernize/Core/Transforms.h b/clang-tools-extra/clang-modernize/Core/Transforms.h new file mode 100644 index 00000000000..18369407dda --- /dev/null +++ b/clang-tools-extra/clang-modernize/Core/Transforms.h @@ -0,0 +1,82 @@ +//===-- Core/Transforms.h - class Transforms Def'n --------------*- 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 definition for class Transforms which is +/// responsible for defining the command-line arguments exposing +/// transformations to the user and applying requested transforms. +/// +//===----------------------------------------------------------------------===// + +#ifndef CPP11_MIGRATE_TRANSFORMS_H +#define CPP11_MIGRATE_TRANSFORMS_H + +#include "llvm/Support/CommandLine.h" +#include "llvm/ADT/StringRef.h" + +#include <vector> + +// Forward declarations +namespace llvm { +namespace cl { +class Option; +} // namespace cl +} // namespace llvm +class Transform; +struct TransformOptions; +struct CompilerVersions; + +typedef Transform *(*TransformCreator)(const TransformOptions &); +template <typename T> +Transform *ConstructTransform(const TransformOptions &Options) { + return new T(Options); +} + +/// \brief Class encapsulating the creation of command line bool options +/// for each transform and instantiating transforms chosen by the user. +class Transforms { +public: + typedef std::vector<Transform*> TransformVec; + typedef TransformVec::const_iterator const_iterator; + +public: + + ~Transforms(); + + /// \brief Registers all available transforms causing them to be made + /// available on the command line. + /// + /// Be sure to register all transforms *before* parsing command line options. + void registerTransforms(); + + /// \brief Instantiate all transforms that were selected on the command line. + /// + /// Call *after* parsing options. + void createSelectedTransforms(const TransformOptions &Options, + const CompilerVersions &RequiredVersions); + + /// \brief Return an iterator to the start of a container of instantiated + /// transforms. + const_iterator begin() const { return ChosenTransforms.begin(); } + + /// \brief Return an iterator to the end of a container of instantiated + /// transforms. + const_iterator end() const { return ChosenTransforms.end(); } + +private: + bool hasAnyExplicitOption() const; + + typedef llvm::StringMap<llvm::cl::opt<bool> *> OptionMap; + +private: + TransformVec ChosenTransforms; + OptionMap Options; +}; + +#endif // CPP11_MIGRATE_TRANSFORMS_H |