summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-modernize/Core
diff options
context:
space:
mode:
authorChandler Carruth <chandlerc@gmail.com>2013-09-04 17:35:07 +0000
committerChandler Carruth <chandlerc@gmail.com>2013-09-04 17:35:07 +0000
commitd9063c46f59f4bec47bcbeddca8ca2f789348c03 (patch)
tree76505542df7a05016dc71ffe44ed3ba264fb54be /clang-tools-extra/clang-modernize/Core
parent6a23d212897d5402035cfaea82260f6dae1c8f2a (diff)
downloadbcm5719-llvm-d9063c46f59f4bec47bcbeddca8ca2f789348c03.tar.gz
bcm5719-llvm-d9063c46f59f4bec47bcbeddca8ca2f789348c03.zip
Rename cpp11-migrate to clang-modernize.
There is no reason to expect this tool to be limited to C++11, it seems very likely to be of on-going interest. It seems likely to be useful for modernizing even as new libraries come out in TSes and other formats than a complete standard. Fundamentally, we need something a bit more general. After some discussion on the list, going with 'clang-modernize'. I've tried to do a reasonably comprehensive job of fixing up the names, but I may still have missed some. Feel free to poke me if you spot any fallout here. Things I've tried reasonably hard to find and fix: - cpp11-migrate -> clang-modernize - Migrator -> Modernizer - Clean up the introductory documentation that was C++11 specific. I'll also point out that this tool continues to delight me. =] Also, a huge thanks to those who have so carefully, thoroughly documented the tool. The docs here are simply phenomenal. Every tool should be this well documented. I hope I have updated the documentation reasonably well, but I'm not very good at documentation, so review much appreciated. llvm-svn: 189960
Diffstat (limited to 'clang-tools-extra/clang-modernize/Core')
-rw-r--r--clang-tools-extra/clang-modernize/Core/CMakeLists.txt19
-rw-r--r--clang-tools-extra/clang-modernize/Core/CustomMatchers.h59
-rw-r--r--clang-tools-extra/clang-modernize/Core/FileOverrides.cpp198
-rw-r--r--clang-tools-extra/clang-modernize/Core/FileOverrides.h129
-rw-r--r--clang-tools-extra/clang-modernize/Core/IncludeDirectives.cpp474
-rw-r--r--clang-tools-extra/clang-modernize/Core/IncludeDirectives.h141
-rw-r--r--clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp169
-rw-r--r--clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h56
-rw-r--r--clang-tools-extra/clang-modernize/Core/Makefile14
-rw-r--r--clang-tools-extra/clang-modernize/Core/PerfSupport.cpp101
-rw-r--r--clang-tools-extra/clang-modernize/Core/PerfSupport.h57
-rw-r--r--clang-tools-extra/clang-modernize/Core/Refactoring.h31
-rw-r--r--clang-tools-extra/clang-modernize/Core/Reformatting.cpp62
-rw-r--r--clang-tools-extra/clang-modernize/Core/Reformatting.h60
-rw-r--r--clang-tools-extra/clang-modernize/Core/SyntaxCheck.cpp73
-rw-r--r--clang-tools-extra/clang-modernize/Core/SyntaxCheck.h38
-rw-r--r--clang-tools-extra/clang-modernize/Core/Transform.cpp172
-rw-r--r--clang-tools-extra/clang-modernize/Core/Transform.h344
-rw-r--r--clang-tools-extra/clang-modernize/Core/Transforms.cpp71
-rw-r--r--clang-tools-extra/clang-modernize/Core/Transforms.h82
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
OpenPOWER on IntegriCloud